2019-11-02 03:16:47 +00:00
//
// I n A p p W e b V i e w . s w i f t
2019-11-29 15:59:18 +00:00
// f l u t t e r _ i n a p p w e b v i e w
2019-11-02 03:16:47 +00:00
//
// C r e a t e d b y L o r e n z o o n 2 1 / 1 0 / 1 8 .
//
import Flutter
import Foundation
import WebKit
func currentTimeInMilliSeconds ( ) -> Int64 {
let currentDate = Date ( )
let since1970 = currentDate . timeIntervalSince1970
return Int64 ( since1970 * 1000 )
}
func convertToDictionary ( text : String ) -> [ String : Any ] ? {
if let data = text . data ( using : . utf8 ) {
do {
return try JSONSerialization . jsonObject ( with : data , options : [ ] ) as ? [ String : Any ]
} catch {
print ( error . localizedDescription )
}
}
return nil
}
2019-11-07 23:32:29 +00:00
func JSONStringify ( value : Any , prettyPrinted : Bool = false ) -> String {
let options : JSONSerialization . WritingOptions = prettyPrinted ? . prettyPrinted : . init ( rawValue : 0 )
if JSONSerialization . isValidJSONObject ( value ) {
let data = try ? JSONSerialization . data ( withJSONObject : value , options : options )
if data != nil {
if let string = String ( data : data ! , encoding : . utf8 ) {
return string
}
}
}
return " "
}
2019-11-29 15:59:18 +00:00
let JAVASCRIPT_BRIDGE_NAME = " flutter_inappwebview "
2019-11-05 02:44:22 +00:00
2020-05-09 02:36:07 +00:00
// h t t p s : / / g i t h u b . c o m / t i l d e i o / r s v p . j s
2019-12-09 23:29:09 +00:00
let promisePolyfillJS = " " "
if ( window . Promise = = null ) {
2020-05-09 02:36:07 +00:00
! function ( t , e ) { " object " = = typeof exports && " undefined " != typeof module ? e ( exports ) : " function " = = typeof define && define . amd ? define ( [ " exports " ] , e ) : e ( t . RSVP = { } ) } ( this , function ( t ) { " use strict " ; function e ( t ) { var e = t . _promiseCallbacks ; return e || ( e = t . _promiseCallbacks = { } ) , e } var r = { mixin : function ( t ) { return t . on = this . on , t . off = this . off , t . trigger = this . trigger , t . _promiseCallbacks = void 0 , t } , on : function ( t , r ) { if ( " function " != typeof r ) throw new TypeError ( " Callback must be a function " ) ; var n = e ( this ) , o = n [ t ] ; o || ( o = n [ t ] = [ ] ) , - 1 = = = o . indexOf ( r ) && o . push ( r ) } , off : function ( t , r ) { var n = e ( this ) ; if ( r ) { var o = n [ t ] , i = o . indexOf ( r ) ; - 1 ! = = i && o . splice ( i , 1 ) } else n [ t ] = [ ] } , trigger : function ( t , r , n ) { var o = e ( this ) [ t ] ; if ( o ) for ( var i = 0 ; i < o . length ; i ++ ) ( 0 , o [ i ] ) ( r , n ) } } , n = { instrument : ! 1 } ; function o ( t , e ) { if ( 2 ! = = arguments . length ) return n [ t ] ; n [ t ] = e } r . mixin ( n ) ; var i = [ ] ; function s ( t , e , r ) { 1 = = = i . push ( { name : t , payload : { key : e . _guidKey , id : e . _id , eventName : t , detail : e . _result , childId : r && r . _id , label : e . _label , timeStamp : Date . now ( ) , error : n [ " instrument-with-stack " ] ? new Error ( e . _label ) : null } } ) && setTimeout ( function ( ) { for ( var t = 0 ; t < i . length ; t ++ ) { var e = i [ t ] , r = e . payload ; r . guid = r . key + r . id , r . childGuid = r . key + r . childId , r . error && ( r . stack = r . error . stack ) , n . trigger ( e . name , e . payload ) } i . length = 0 } , 50 ) } function u ( t , e ) { if ( t && " object " = = typeof t && t . constructor = = = this ) return t ; var r = new this ( c , e ) ; return m ( r , t ) , r } function c ( ) { } var a = void 0 , f = 1 , l = 2 , h = { error : null } ; function p ( t ) { try { return t . then } catch ( t ) { return h . error = t , h } } var y = void 0 ; function _ ( ) { try { var t = y ; return y = null , t . apply ( this , arguments ) } catch ( t ) { return h . error = t , h } } function v ( t ) { return y = t , _ } function d ( t , e , r ) { if ( e . constructor = = = t . constructor && r = = = A && t . constructor . resolve = = = u ) ! function ( t , e ) { e . _state = = = f ? b ( t , e . _result ) : e . _state = = = l ? ( e . _onError = null , g ( t , e . _result ) ) : j ( e , void 0 , function ( r ) { e = = = r ? b ( t , r ) : m ( t , r ) } , function ( e ) { return g ( t , e ) } ) } ( t , e ) ; else if ( r = = = h ) { var o = h . error ; h . error = null , g ( t , o ) } else " function " = = typeof r ? function ( t , e , r ) { n . async ( function ( t ) { var n = ! 1 , o = v ( r ) . call ( e , function ( r ) { n || ( n = ! 0 , e = = = r ? b ( t , r ) : m ( t , r ) ) } , function ( e ) { n || ( n = ! 0 , g ( t , e ) ) } , " Settle: " + ( t . _label || " unknown promise " ) ) ; if ( ! n && o = = = h ) { n = ! 0 ; var i = h . error ; h . error = null , g ( t , i ) } } , t ) } ( t , e , r ) : b ( t , e ) } function m ( t , e ) { var r , n ; t = = = e ? b ( t , e ) : ( n = typeof ( r = e ) , null = = = r || " object " !== n && " function " !== n ? b ( t , e ) : d ( t , e , p ( e ) ) ) } function w ( t ) { t . _onError && t . _onError ( t . _result ) , O ( t ) } function b ( t , e ) { t . _state = = = a && ( t . _result = e , t . _state = f , 0 = = = t . _subscribers . length ? n . instrument && s ( " fulfilled " , t ) : n . async ( O , t ) ) } function g ( t , e ) { t . _state = = = a && ( t . _state = l , t . _result = e , n . async ( w , t ) ) } function j ( t , e , r , o ) { var i = t . _subscribers , s = i . length ; t . _onError = null , i [ s ] = e , i [ s + f ] = r , i [ s + l ] = o , 0 = = = s && t . _state && n . async ( O , t ) } function O ( t ) { var e = t . _subscribers , r = t . _state ; if ( n . instrument && s ( r = = = f ? " fulfilled " : " rejected " , t ) , 0 ! = = e . length ) { for ( var o = void 0 , i = void 0 , u = t . _result , c = 0 ; c < e . length ; c += 3 ) o = e [ c ] , i = e [ c + r ] , o ? E ( r , o , i , u ) : i ( u ) ; t . _subscribers . length = 0 } } function E ( t , e , r , n ) { var o = " function " = = typeof r , i = void 0 ; if ( i = o ? v ( r ) ( n ) : n , e . _state ! = = a ) ; else if ( i = = = e ) g ( e , new TypeError ( " A promises callback cannot return that same promise. " ) ) ; else if ( i = = = h ) { var s = h . error ; h . error = null , g ( e , s ) } else o ? m ( e , i ) : t = = = f ? b ( e , i ) : t = = = l && g ( e , i ) } function A ( t , e , r ) { var o = this . _state ; if ( o = = = f &&! t || o = = = l &&! e ) return n . instrument && s ( " chained " , this , this ) , this ; this . _onError = null ; var i = new this . constructor ( c , r ) , u = this . _result ; if ( n . instrument && s ( " chained " , this , i ) , o = = = a ) j ( this , i , t , e ) ; else { var h = o = = = f ? t : e ; n . async ( function ( ) { return E ( o , i , h , u ) } ) } return i } var T = function ( ) { function t ( t , e , r , n ) { this . _instanceConstructor = t , this . promise = new t ( c , n ) , this . _abortOnReject = r , this . _isUsingOwnPromise = t = = = k , this . _isUsingOwnResolve = t . resolve = = = u , this . _init . apply ( this , arguments ) } return t . prototype . _init = function ( t , e ) { var r = e . length || 0 ; this . length = r , this . _remaining = r , this . _result = new Array ( r ) , this . _enumerate ( e ) } , t . prototype . _enumerate = function ( t ) { for ( var e = this . length , r = this . promise , n = 0 ; r . _state = = = a && n < e ; n ++ ) this . _eachEntry ( t [ n ] , n , ! 0 ) ; this . _checkFullfillment ( ) } , t . prototype . _checkFullfillment = function ( ) { if ( 0 = = = this . _remaining ) { var t = this . _result ; b ( this . promise , t ) , this . _result = null } } , t . prototype . _settleMaybeThenable = functio
window . Promise = RSVP . Promise ;
2019-12-09 23:29:09 +00:00
}
" " "
let javaScriptBridgeJS = " " "
window . \ ( JAVASCRIPT_BRIDGE_NAME ) = { } ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler = function ( ) {
2020-07-03 18:12:52 +00:00
var _windowId = window . _flutter_inappwebview_windowId ;
2019-12-09 23:29:09 +00:00
var _callHandlerID = setTimeout ( function ( ) { } ) ;
2020-07-03 18:12:52 +00:00
window . webkit . messageHandlers [ ' callHandler ' ] . postMessage ( { ' handlerName ' : arguments [ 0 ] , ' _callHandlerID ' : _callHandlerID , ' args ' : JSON . stringify ( Array . prototype . slice . call ( arguments , 1 ) ) , ' _windowId ' : _windowId } ) ;
2019-12-09 23:29:09 +00:00
return new Promise ( function ( resolve , reject ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) [ _callHandlerID ] = resolve ;
} ) ;
2021-02-06 01:30:15 +00:00
} ;
2019-12-09 23:29:09 +00:00
" " "
2019-11-02 03:16:47 +00:00
// t h e m e s s a g e n e e d s t o b e c o n c a t e n a t e d w i t h ' ' i n o r d e r t o h a v e t h e s a m e b e h a v i o r l i k e o n A n d r o i d
let consoleLogJS = " " "
2019-11-05 02:44:22 +00:00
( function ( console ) {
2020-07-03 18:12:52 +00:00
2019-11-02 03:16:47 +00:00
var oldLogs = {
' consoleLog ' : console . log ,
' consoleDebug ' : console . debug ,
' consoleError ' : console . error ,
' consoleInfo ' : console . info ,
' consoleWarn ' : console . warn
} ;
for ( var k in oldLogs ) {
( function ( oldLog ) {
console [ oldLog . replace ( ' console ' , ' ' ) . toLowerCase ( ) ] = function ( ) {
var message = ' ' ;
for ( var i in arguments ) {
if ( message = = ' ' ) {
message += arguments [ i ] ;
}
else {
message += ' ' + arguments [ i ] ;
}
}
2020-07-03 18:12:52 +00:00
var _windowId = window . _flutter_inappwebview_windowId ;
window . webkit . messageHandlers [ oldLog ] . postMessage ( { ' message ' : message , ' _windowId ' : _windowId } ) ;
2020-05-09 02:36:07 +00:00
oldLogs [ oldLog ] . apply ( null , arguments ) ;
2019-11-02 03:16:47 +00:00
}
} ) ( k ) ;
}
2019-11-05 02:44:22 +00:00
} ) ( window . console ) ;
2019-11-02 03:16:47 +00:00
" " "
2019-12-09 23:29:09 +00:00
let printJS = " " "
window . print = function ( ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( " onPrint " , window . location . href ) ;
2019-11-02 03:16:47 +00:00
}
" " "
2019-11-29 15:59:18 +00:00
let platformReadyJS = " window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady')); " ;
2019-11-16 11:41:30 +00:00
2019-11-02 03:16:47 +00:00
let findTextHighlightJS = " " "
var wkwebview_SearchResultCount = 0 ;
var wkwebview_CurrentHighlight = 0 ;
var wkwebview_IsDoneCounting = false ;
function wkwebview_FindAllAsyncForElement ( element , keyword ) {
if ( element ) {
if ( element . nodeType = = 3 ) {
// T e x t n o d e
var elementTmp = element ;
while ( true ) {
var value = elementTmp . nodeValue ; // S e a r c h f o r k e y w o r d i n t e x t n o d e
var idx = value . toLowerCase ( ) . indexOf ( keyword ) ;
if ( idx < 0 ) break ;
var span = document . createElement ( " span " ) ;
var text = document . createTextNode ( value . substr ( idx , keyword . length ) ) ;
span . appendChild ( text ) ;
span . setAttribute (
" id " ,
" WKWEBVIEW_SEARCH_WORD_ " + wkwebview_SearchResultCount
) ;
span . setAttribute ( " class " , " wkwebview_Highlight " ) ;
var backgroundColor = wkwebview_SearchResultCount = = 0 ? " #FF9732 " : " #FFFF00 " ;
span . setAttribute ( " style " , " color: #000 !important; background: " + backgroundColor + " !important; padding: 0px !important; margin: 0px !important; border: 0px !important; " ) ;
text = document . createTextNode ( value . substr ( idx + keyword . length ) ) ;
element . deleteData ( idx , value . length - idx ) ;
var next = element . nextSibling ;
element . parentNode . insertBefore ( span , next ) ;
element . parentNode . insertBefore ( text , next ) ;
element = text ;
wkwebview_SearchResultCount ++ ;
elementTmp = document . createTextNode (
value . substr ( idx + keyword . length )
) ;
2020-07-03 18:12:52 +00:00
var _windowId = window . _flutter_inappwebview_windowId ;
2019-11-05 23:23:24 +00:00
window . webkit . messageHandlers [ " onFindResultReceived " ] . postMessage (
2020-07-03 18:12:52 +00:00
{
' findResult ' : {
' activeMatchOrdinal ' : wkwebview_CurrentHighlight ,
' numberOfMatches ' : wkwebview_SearchResultCount ,
' isDoneCounting ' : wkwebview_IsDoneCounting
} ,
' _windowId ' : _windowId
}
2019-11-02 03:16:47 +00:00
) ;
}
} else if ( element . nodeType = = 1 ) {
// E l e m e n t n o d e
if (
element . style . display != " none " &&
element . nodeName . toLowerCase ( ) != " select "
) {
for ( var i = element . childNodes . length - 1 ; i >= 0 ; i -- ) {
wkwebview_FindAllAsyncForElement (
element . childNodes [ element . childNodes . length - 1 - i ] ,
keyword
) ;
}
}
}
}
}
// t h e m a i n e n t r y p o i n t t o s t a r t t h e s e a r c h
function wkwebview_FindAllAsync ( keyword ) {
wkwebview_ClearMatches ( ) ;
wkwebview_FindAllAsyncForElement ( document . body , keyword . toLowerCase ( ) ) ;
wkwebview_IsDoneCounting = true ;
2020-07-03 18:12:52 +00:00
var _windowId = window . _flutter_inappwebview_windowId ;
2019-11-05 23:23:24 +00:00
window . webkit . messageHandlers [ " onFindResultReceived " ] . postMessage (
2020-07-03 18:12:52 +00:00
{
' findResult ' : {
' activeMatchOrdinal ' : wkwebview_CurrentHighlight ,
' numberOfMatches ' : wkwebview_SearchResultCount ,
' isDoneCounting ' : wkwebview_IsDoneCounting
} ,
' _windowId ' : _windowId
}
2019-11-02 03:16:47 +00:00
) ;
}
// h e l p e r f u n c t i o n , r e c u r s i v e l y r e m o v e s t h e h i g h l i g h t s i n e l e m e n t s a n d t h e i r c h i l d s
function wkwebview_ClearMatchesForElement ( element ) {
if ( element ) {
if ( element . nodeType = = 1 ) {
if ( element . getAttribute ( " class " ) = = " wkwebview_Highlight " ) {
var text = element . removeChild ( element . firstChild ) ;
element . parentNode . insertBefore ( text , element ) ;
element . parentNode . removeChild ( element ) ;
return true ;
} else {
var normalize = false ;
for ( var i = element . childNodes . length - 1 ; i >= 0 ; i -- ) {
if ( wkwebview_ClearMatchesForElement ( element . childNodes [ i ] ) ) {
normalize = true ;
}
}
if ( normalize ) {
element . normalize ( ) ;
}
}
}
}
return false ;
}
// t h e m a i n e n t r y p o i n t t o r e m o v e t h e h i g h l i g h t s
function wkwebview_ClearMatches ( ) {
wkwebview_SearchResultCount = 0 ;
wkwebview_CurrentHighlight = 0 ;
wkwebview_ClearMatchesForElement ( document . body ) ;
}
function wkwebview_FindNext ( forward ) {
if ( wkwebview_SearchResultCount <= 0 ) return ;
var idx = wkwebview_CurrentHighlight + ( forward ? + 1 : - 1 ) ;
idx =
idx < 0
? wkwebview_SearchResultCount - 1
: idx >= wkwebview_SearchResultCount
? 0
: idx ;
wkwebview_CurrentHighlight = idx ;
var scrollTo = document . getElementById ( " WKWEBVIEW_SEARCH_WORD_ " + idx ) ;
if ( scrollTo ) {
var highlights = document . getElementsByClassName ( " wkwebview_Highlight " ) ;
for ( var i = 0 ; i < highlights . length ; i ++ ) {
var span = highlights [ i ] ;
span . style . backgroundColor = " #FFFF00 " ;
}
scrollTo . style . backgroundColor = " #FF9732 " ;
scrollTo . scrollIntoView ( {
behavior : " auto " ,
block : " center "
} ) ;
2020-07-03 18:12:52 +00:00
var _windowId = window . _flutter_inappwebview_windowId ;
2019-11-05 23:23:24 +00:00
window . webkit . messageHandlers [ " onFindResultReceived " ] . postMessage (
2020-07-03 18:12:52 +00:00
{
' findResult ' : {
' activeMatchOrdinal ' : wkwebview_CurrentHighlight ,
' numberOfMatches ' : wkwebview_SearchResultCount ,
' isDoneCounting ' : wkwebview_IsDoneCounting
} ,
' _windowId ' : _windowId
}
2019-11-02 03:16:47 +00:00
) ;
}
}
" " "
2021-02-06 01:30:15 +00:00
let variableForOnLoadResourceJS = " _flutter_inappwebview_useOnLoadResource "
let enableVariableForOnLoadResourceJS = " window. \( variableForOnLoadResourceJS ) = $PLACEHOLDER_VALUE; "
2019-11-06 21:55:54 +00:00
let resourceObserverJS = " " "
( function ( ) {
var observer = new PerformanceObserver ( function ( list ) {
list . getEntries ( ) . forEach ( function ( entry ) {
if ( window . \ ( variableForOnLoadResourceJS ) = = null || window . \ ( variableForOnLoadResourceJS ) = = true ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( " onLoadResource " , entry ) ;
}
} ) ;
} ) ;
observer . observe ( { entryTypes : [ ' resource ' ] } ) ;
} ) ( ) ;
" " "
2021-02-04 00:43:55 +00:00
let variableForShouldInterceptAjaxRequestJS = " _flutter_inappwebview_useShouldInterceptAjaxRequest "
let enableVariableForShouldInterceptAjaxRequestJS = " window. \( variableForShouldInterceptAjaxRequestJS ) = $PLACEHOLDER_VALUE; "
2019-11-06 21:55:54 +00:00
2019-11-05 02:44:22 +00:00
let interceptAjaxRequestsJS = " " "
( function ( ajax ) {
var send = ajax . prototype . send ;
var open = ajax . prototype . open ;
var setRequestHeader = ajax . prototype . setRequestHeader ;
2019-11-29 15:59:18 +00:00
ajax . prototype . _flutter_inappwebview_url = null ;
ajax . prototype . _flutter_inappwebview_method = null ;
ajax . prototype . _flutter_inappwebview_isAsync = null ;
ajax . prototype . _flutter_inappwebview_user = null ;
ajax . prototype . _flutter_inappwebview_password = null ;
ajax . prototype . _flutter_inappwebview_password = null ;
ajax . prototype . _flutter_inappwebview_already_onreadystatechange_wrapped = false ;
ajax . prototype . _flutter_inappwebview_request_headers = { } ;
2019-11-10 23:16:38 +00:00
function convertRequestResponse ( request , callback ) {
if ( request . response != null && request . responseType != null ) {
switch ( request . responseType ) {
case ' arraybuffer ' :
callback ( new Uint8Array ( request . response ) ) ;
return ;
case ' blob ' :
const reader = new FileReader ( ) ;
reader . addEventListener ( ' loadend ' , function ( ) {
callback ( new Uint8Array ( reader . result ) ) ;
} ) ;
reader . readAsArrayBuffer ( blob ) ;
return ;
case ' document ' :
callback ( request . response . documentElement . outerHTML ) ;
return ;
case ' json ' :
callback ( request . response ) ;
return ;
} ;
}
callback ( null ) ;
} ;
2019-11-05 02:44:22 +00:00
ajax . prototype . open = function ( method , url , isAsync , user , password ) {
isAsync = ( isAsync != null ) ? isAsync : true ;
2019-11-29 15:59:18 +00:00
this . _flutter_inappwebview_url = url ;
this . _flutter_inappwebview_method = method ;
this . _flutter_inappwebview_isAsync = isAsync ;
this . _flutter_inappwebview_user = user ;
this . _flutter_inappwebview_password = password ;
this . _flutter_inappwebview_request_headers = { } ;
2019-11-05 02:44:22 +00:00
open . call ( this , method , url , isAsync , user , password ) ;
} ;
ajax . prototype . setRequestHeader = function ( header , value ) {
2019-11-29 15:59:18 +00:00
this . _flutter_inappwebview_request_headers [ header ] = value ;
2019-11-18 01:50:56 +00:00
setRequestHeader . call ( this , header , value ) ;
2019-11-05 02:44:22 +00:00
} ;
function handleEvent ( e ) {
var self = this ;
2021-02-04 00:43:55 +00:00
if ( window . \ ( variableForShouldInterceptAjaxRequestJS ) = = null || window . \ ( variableForShouldInterceptAjaxRequestJS ) = = true ) {
2019-11-05 02:44:22 +00:00
var headers = this . getAllResponseHeaders ( ) ;
var responseHeaders = { } ;
if ( headers != null ) {
var arr = headers . trim ( ) . split ( / [ \ \ r \ \ n ] +/ ) ;
arr . forEach ( function ( line ) {
var parts = line . split ( ' : ' ) ;
var header = parts . shift ( ) ;
var value = parts . join ( ' : ' ) ;
responseHeaders [ header ] = value ;
} ) ;
}
2019-11-10 23:16:38 +00:00
convertRequestResponse ( this , function ( response ) {
var ajaxRequest = {
2019-11-29 15:59:18 +00:00
method : self . _flutter_inappwebview_method ,
url : self . _flutter_inappwebview_url ,
isAsync : self . _flutter_inappwebview_isAsync ,
user : self . _flutter_inappwebview_user ,
password : self . _flutter_inappwebview_password ,
2019-11-10 23:16:38 +00:00
withCredentials : self . withCredentials ,
2019-11-29 15:59:18 +00:00
headers : self . _flutter_inappwebview_request_headers ,
2019-11-10 23:16:38 +00:00
readyState : self . readyState ,
status : self . status ,
responseURL : self . responseURL ,
responseType : self . responseType ,
response : response ,
responseText : ( self . responseType = = ' text ' || self . responseType = = ' ' ) ? self . responseText : null ,
responseXML : ( self . responseType = = ' document ' && self . responseXML != null ) ? self . responseXML . documentElement . outerHTML : null ,
statusText : self . statusText ,
responseHeaders , responseHeaders ,
event : {
type : e . type ,
loaded : e . loaded ,
lengthComputable : e . lengthComputable ,
total : e . total
}
} ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' onAjaxProgress ' , ajaxRequest ) . then ( function ( result ) {
if ( result != null ) {
switch ( result ) {
case 0 :
self . abort ( ) ;
return ;
} ;
}
} ) ;
2019-11-05 02:44:22 +00:00
} ) ;
2019-11-06 21:55:54 +00:00
}
} ;
ajax . prototype . send = function ( data ) {
var self = this ;
2021-02-04 00:43:55 +00:00
if ( window . \ ( variableForShouldInterceptAjaxRequestJS ) = = null || window . \ ( variableForShouldInterceptAjaxRequestJS ) = = true ) {
2019-11-29 15:59:18 +00:00
if ( ! this . _flutter_inappwebview_already_onreadystatechange_wrapped ) {
this . _flutter_inappwebview_already_onreadystatechange_wrapped = true ;
2019-11-06 21:55:54 +00:00
var onreadystatechange = this . onreadystatechange ;
this . onreadystatechange = function ( ) {
2021-02-04 00:43:55 +00:00
if ( window . \ ( variableForShouldInterceptAjaxRequestJS ) = = null || window . \ ( variableForShouldInterceptAjaxRequestJS ) = = true ) {
2019-11-06 21:55:54 +00:00
var headers = this . getAllResponseHeaders ( ) ;
var responseHeaders = { } ;
if ( headers != null ) {
var arr = headers . trim ( ) . split ( / [ \ \ r \ \ n ] +/ ) ;
arr . forEach ( function ( line ) {
var parts = line . split ( ' : ' ) ;
var header = parts . shift ( ) ;
var value = parts . join ( ' : ' ) ;
responseHeaders [ header ] = value ;
} ) ;
}
2019-11-10 23:16:38 +00:00
convertRequestResponse ( this , function ( response ) {
var ajaxRequest = {
2019-11-29 15:59:18 +00:00
method : self . _flutter_inappwebview_method ,
url : self . _flutter_inappwebview_url ,
isAsync : self . _flutter_inappwebview_isAsync ,
user : self . _flutter_inappwebview_user ,
password : self . _flutter_inappwebview_password ,
2019-11-10 23:16:38 +00:00
withCredentials : self . withCredentials ,
2019-11-29 15:59:18 +00:00
headers : self . _flutter_inappwebview_request_headers ,
2019-11-10 23:16:38 +00:00
readyState : self . readyState ,
status : self . status ,
responseURL : self . responseURL ,
responseType : self . responseType ,
response : response ,
responseText : ( self . responseType = = ' text ' || self . responseType = = ' ' ) ? self . responseText : null ,
responseXML : ( self . responseType = = ' document ' && self . responseXML != null ) ? self . responseXML . documentElement . outerHTML : null ,
statusText : self . statusText ,
responseHeaders : responseHeaders
} ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' onAjaxReadyStateChange ' , ajaxRequest ) . then ( function ( result ) {
if ( result != null ) {
switch ( result ) {
case 0 :
self . abort ( ) ;
return ;
} ;
}
if ( onreadystatechange != null ) {
onreadystatechange ( ) ;
}
} ) ;
2019-11-06 21:55:54 +00:00
} ) ;
} else if ( onreadystatechange != null ) {
onreadystatechange ( ) ;
}
} ;
}
this . addEventListener ( ' loadstart ' , handleEvent ) ;
this . addEventListener ( ' load ' , handleEvent ) ;
this . addEventListener ( ' loadend ' , handleEvent ) ;
this . addEventListener ( ' progress ' , handleEvent ) ;
this . addEventListener ( ' error ' , handleEvent ) ;
this . addEventListener ( ' abort ' , handleEvent ) ;
2019-11-09 22:35:18 +00:00
this . addEventListener ( ' timeout ' , handleEvent ) ;
2019-11-06 21:55:54 +00:00
var ajaxRequest = {
data : data ,
2019-11-29 15:59:18 +00:00
method : this . _flutter_inappwebview_method ,
url : this . _flutter_inappwebview_url ,
isAsync : this . _flutter_inappwebview_isAsync ,
user : this . _flutter_inappwebview_user ,
password : this . _flutter_inappwebview_password ,
2019-11-06 21:55:54 +00:00
withCredentials : this . withCredentials ,
2019-11-29 15:59:18 +00:00
headers : this . _flutter_inappwebview_request_headers ,
2019-11-10 23:16:38 +00:00
responseType : this . responseType
2019-11-06 21:55:54 +00:00
} ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' shouldInterceptAjaxRequest ' , ajaxRequest ) . then ( function ( result ) {
if ( result != null ) {
switch ( result . action ) {
case 0 :
self . abort ( ) ;
return ;
} ;
data = result . data ;
self . withCredentials = result . withCredentials ;
2019-11-10 23:16:38 +00:00
if ( result . responseType != null ) {
self . responseType = result . responseType ;
} ;
2019-11-06 21:55:54 +00:00
for ( var header in result . headers ) {
var value = result . headers [ header ] ;
2019-11-29 15:59:18 +00:00
var flutter_inappwebview_value = self . _flutter_inappwebview_request_headers [ header ] ;
if ( flutter_inappwebview_value = = null ) {
self . _flutter_inappwebview_request_headers [ header ] = value ;
2019-11-18 01:50:56 +00:00
} else {
2019-11-29 15:59:18 +00:00
self . _flutter_inappwebview_request_headers [ header ] += ' , ' + value ;
2019-11-18 01:50:56 +00:00
}
2019-11-16 11:41:30 +00:00
setRequestHeader . call ( self , header , value ) ;
2019-11-06 21:55:54 +00:00
} ;
2019-11-29 15:59:18 +00:00
if ( ( self . _flutter_inappwebview_method != result . method && result . method != null ) || ( self . _flutter_inappwebview_url != result . url && result . url != null ) ) {
2019-11-05 02:44:22 +00:00
self . abort ( ) ;
2019-11-06 21:55:54 +00:00
self . open ( result . method , result . url , result . isAsync , result . user , result . password ) ;
2019-11-05 02:44:22 +00:00
return ;
2019-11-06 21:55:54 +00:00
}
2019-11-05 02:44:22 +00:00
}
2019-11-06 21:55:54 +00:00
send . call ( self , data ) ;
} ) ;
} else {
send . call ( this , data ) ;
}
2019-11-05 02:44:22 +00:00
} ;
} ) ( window . XMLHttpRequest ) ;
" " "
2021-02-04 00:43:55 +00:00
let variableForShouldInterceptFetchRequestsJS = " _flutter_inappwebview_useShouldInterceptFetchRequest "
let enableVariableForShouldInterceptFetchRequestsJS = " window. \( variableForShouldInterceptFetchRequestsJS ) = $PLACEHOLDER_VALUE; "
2019-11-06 21:55:54 +00:00
2019-11-05 02:44:22 +00:00
let interceptFetchRequestsJS = " " "
( function ( fetch ) {
if ( fetch = = null ) {
return ;
}
2019-11-06 21:55:54 +00:00
function convertHeadersToJson ( headers ) {
var headersObj = { } ;
for ( var header of headers . keys ( ) ) {
var value = headers . get ( header ) ;
headersObj [ header ] = value ;
}
return headersObj ;
}
function convertJsonToHeaders ( headersJson ) {
return new Headers ( headersJson ) ;
}
function convertBodyToArray ( body ) {
return new Response ( body ) . arrayBuffer ( ) . then ( function ( arrayBuffer ) {
var arr = Array . from ( new Uint8Array ( arrayBuffer ) ) ;
return arr ;
} )
}
function convertArrayIntBodyToUint8Array ( arrayIntBody ) {
return new Uint8Array ( arrayIntBody ) ;
}
function convertCredentialsToJson ( credentials ) {
var credentialsObj = { } ;
if ( window . FederatedCredential != null && credentials instanceof FederatedCredential ) {
credentialsObj . type = credentials . type ;
credentialsObj . id = credentials . id ;
credentialsObj . name = credentials . name ;
credentialsObj . protocol = credentials . protocol ;
credentialsObj . provider = credentials . provider ;
credentialsObj . iconURL = credentials . iconURL ;
} else if ( window . PasswordCredential != null && credentials instanceof PasswordCredential ) {
credentialsObj . type = credentials . type ;
credentialsObj . id = credentials . id ;
credentialsObj . name = credentials . name ;
credentialsObj . password = credentials . password ;
credentialsObj . iconURL = credentials . iconURL ;
2019-11-05 02:44:22 +00:00
} else {
2019-11-06 21:55:54 +00:00
credentialsObj . type = ' default ' ;
credentialsObj . value = credentials ;
2019-11-05 02:44:22 +00:00
}
2019-11-06 21:55:54 +00:00
}
function convertJsonToCredential ( credentialsJson ) {
var credentials ;
if ( window . FederatedCredential != null && credentialsJson . type = = = ' federated ' ) {
credentials = new FederatedCredential ( {
id : credentialsJson . id ,
name : credentialsJson . name ,
protocol : credentialsJson . protocol ,
provider : credentialsJson . provider ,
iconURL : credentialsJson . iconURL
} ) ;
} else if ( window . PasswordCredential != null && credentialsJson . type = = = ' password ' ) {
credentials = new PasswordCredential ( {
id : credentialsJson . id ,
name : credentialsJson . name ,
password : credentialsJson . password ,
iconURL : credentialsJson . iconURL
} ) ;
} else {
credentials = credentialsJson ;
}
return credentials ;
}
window . fetch = async function ( resource , init ) {
if ( window . \ ( variableForShouldInterceptFetchRequestsJS ) = = null || window . \ ( variableForShouldInterceptFetchRequestsJS ) = = true ) {
var fetchRequest = {
url : null ,
method : null ,
headers : null ,
body : null ,
mode : null ,
credentials : null ,
cache : null ,
redirect : null ,
referrer : null ,
referrerPolicy : null ,
integrity : null ,
keepalive : null
} ;
if ( resource instanceof Request ) {
fetchRequest . url = resource . url ;
fetchRequest . method = resource . method ;
fetchRequest . headers = resource . headers ;
fetchRequest . body = resource . body ;
fetchRequest . mode = resource . mode ;
fetchRequest . credentials = resource . credentials ;
fetchRequest . cache = resource . cache ;
fetchRequest . redirect = resource . redirect ;
fetchRequest . referrer = resource . referrer ;
fetchRequest . referrerPolicy = resource . referrerPolicy ;
fetchRequest . integrity = resource . integrity ;
fetchRequest . keepalive = resource . keepalive ;
} else {
fetchRequest . url = resource ;
if ( init != null ) {
fetchRequest . method = init . method ;
fetchRequest . headers = init . headers ;
fetchRequest . body = init . body ;
fetchRequest . mode = init . mode ;
fetchRequest . credentials = init . credentials ;
fetchRequest . cache = init . cache ;
fetchRequest . redirect = init . redirect ;
fetchRequest . referrer = init . referrer ;
fetchRequest . referrerPolicy = init . referrerPolicy ;
fetchRequest . integrity = init . integrity ;
fetchRequest . keepalive = init . keepalive ;
2019-11-05 02:44:22 +00:00
}
}
2019-11-06 21:55:54 +00:00
if ( fetchRequest . headers instanceof Headers ) {
fetchRequest . headers = convertHeadersToJson ( fetchRequest . headers ) ;
}
fetchRequest . credentials = convertCredentialsToJson ( fetchRequest . credentials ) ;
return convertBodyToArray ( fetchRequest . body ) . then ( function ( body ) {
fetchRequest . body = body ;
return window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' shouldInterceptFetchRequest ' , fetchRequest ) . then ( function ( result ) {
if ( result != null ) {
switch ( result . action ) {
case 0 :
var controller = new AbortController ( ) ;
if ( init != null ) {
init . signal = controller . signal ;
} else {
init = {
signal : controller . signal
} ;
}
controller . abort ( ) ;
break ;
}
2019-11-16 18:54:03 +00:00
resource = ( result . url != null ) ? result . url : resource ;
if ( init = = null ) {
init = { } ;
2019-11-06 21:55:54 +00:00
}
2019-11-16 18:54:03 +00:00
if ( result . method != null && result . method . length > 0 ) {
init . method = result . method ;
}
if ( result . headers != null && Object . keys ( result . headers ) . length > 0 ) {
init . headers = convertJsonToHeaders ( result . headers ) ;
}
if ( result . body != null && result . body . length > 0 ) {
init . body = convertArrayIntBodyToUint8Array ( result . body ) ;
}
if ( result . mode != null && result . mode . length > 0 ) {
init . mode = result . mode ;
}
if ( result . credentials != null ) {
init . credentials = convertJsonToCredential ( result . credentials ) ;
}
if ( result . cache != null && result . cache . length > 0 ) {
init . cache = result . cache ;
}
if ( result . redirect != null && result . redirect . length > 0 ) {
init . redirect = result . redirect ;
}
if ( result . referrer != null && result . referrer . length > 0 ) {
init . referrer = result . referrer ;
}
if ( result . referrerPolicy != null && result . referrerPolicy . length > 0 ) {
init . referrerPolicy = result . referrerPolicy ;
}
if ( result . integrity != null && result . integrity . length > 0 ) {
init . integrity = result . integrity ;
}
if ( result . keepalive != null ) {
init . keepalive = result . keepalive ;
}
return fetch ( resource , init ) ;
2019-11-06 21:55:54 +00:00
}
return fetch ( resource , init ) ;
} ) ;
} ) ;
} else {
2019-11-05 02:44:22 +00:00
return fetch ( resource , init ) ;
2019-11-06 21:55:54 +00:00
}
2019-11-05 02:44:22 +00:00
} ;
} ) ( window . fetch ) ;
" " "
2020-05-09 02:36:07 +00:00
/* *
https : // d e v e l o p e r . a n d r o i d . c o m / r e f e r e n c e / a n d r o i d / w e b k i t / W e b V i e w . H i t T e s t R e s u l t
*/
let findElementsAtPointJS = " " "
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _findElementsAtPoint = function ( x , y ) {
var hitTestResultType = {
UNKNOWN_TYPE : 0 ,
PHONE_TYPE : 2 ,
GEO_TYPE : 3 ,
EMAIL_TYPE : 4 ,
IMAGE_TYPE : 5 ,
SRC_ANCHOR_TYPE : 7 ,
SRC_IMAGE_ANCHOR_TYPE : 8 ,
EDIT_TEXT_TYPE : 9
} ;
var element = document . elementFromPoint ( x , y ) ;
var data = {
type : 0 ,
extra : null
} ;
while ( element ) {
if ( element . tagName = = = ' IMG ' && element . src ) {
if ( element . parentNode && element . parentNode . tagName = = = ' A ' && element . parentNode . href ) {
data . type = hitTestResultType . SRC_IMAGE_ANCHOR_TYPE ;
} else {
data . type = hitTestResultType . IMAGE_TYPE ;
}
data . extra = element . src ;
break ;
} else if ( element . tagName = = = ' A ' && element . href ) {
if ( element . href . indexOf ( ' mailto : ' ) = = = 0 ) {
data . type = hitTestResultType . EMAIL_TYPE ;
data . extra = element . href . replace ( ' mailto : ' , ' ' ) ;
} else if ( element . href . indexOf ( ' tel : ' ) = = = 0 ) {
data . type = hitTestResultType . PHONE_TYPE ;
data . extra = element . href . replace ( ' tel : ' , ' ' ) ;
} else if ( element . href . indexOf ( ' geo : ' ) = = = 0 ) {
data . type = hitTestResultType . GEO_TYPE ;
data . extra = element . href . replace ( ' geo : ' , ' ' ) ;
} else {
data . type = hitTestResultType . SRC_ANCHOR_TYPE ;
data . extra = element . href ;
}
break ;
} else if (
( element . tagName = = = ' INPUT ' && [ ' text ' , ' email ' , ' password ' , ' number ' , ' search ' , ' tel ' , ' url ' ] . indexOf ( element . type ) >= 0 ) ||
element . tagName = = = ' TEXTAREA ' ) {
data . type = hitTestResultType . EDIT_TEXT_TYPE
}
element = element . parentNode ;
}
return data ;
}
" " "
2020-05-21 01:34:39 +00:00
let getSelectedTextJS = " " "
( function ( ) {
var txt ;
if ( window . getSelection ) {
txt = window . getSelection ( ) . toString ( ) ;
} else if ( window . document . getSelection ) {
txt = window . document . getSelection ( ) . toString ( ) ;
} else if ( window . document . selection ) {
txt = window . document . selection . createRange ( ) . text ;
}
return txt ;
} ) ( ) ;
" " "
2020-06-12 02:04:41 +00:00
let lastTouchedAnchorOrImageJS = " " "
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastAnchorOrImageTouched = null ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastImageTouched = null ;
( function ( ) {
document . addEventListener ( ' touchstart ' , function ( event ) {
var target = event . target ;
while ( target ) {
if ( target . tagName = = = ' IMG ' ) {
var img = target ;
2021-02-04 00:43:55 +00:00
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastImageTouched = {
2020-06-12 02:04:41 +00:00
src : img . src
} ;
var parent = img . parentNode ;
while ( parent ) {
if ( parent . tagName = = = ' A ' ) {
2021-02-04 00:43:55 +00:00
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastAnchorOrImageTouched = {
2020-06-12 02:04:41 +00:00
title : parent . textContent ,
url : parent . href ,
src : img . src
} ;
break ;
}
parent = parent . parentNode ;
}
return ;
} else if ( target . tagName = = = ' A ' ) {
var link = target ;
var images = link . getElementsByTagName ( ' img ' ) ;
var img = ( images . length > 0 ) ? images [ 0 ] : null ;
var imgSrc = ( img != null ) ? img . src : null ;
2021-02-04 00:43:55 +00:00
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastImageTouched = ( img != null ) ? { src : img . src } : window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastImageTouched ;
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _lastAnchorOrImageTouched = {
2020-06-12 02:04:41 +00:00
title : link . textContent ,
url : link . href ,
src : imgSrc
} ;
return ;
}
target = target . parentNode ;
}
} ) ;
} ) ( ) ;
" " "
let originalViewPortMetaTagContentJS = " " "
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _originalViewPortMetaTagContent = " " ;
( function ( ) {
var metaTagNodes = document . head . getElementsByTagName ( ' meta ' ) ;
for ( var i = 0 ; i < metaTagNodes . length ; i ++ ) {
var metaTagNode = metaTagNodes [ i ] ;
if ( metaTagNode . name = = = " viewport " ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . _originalViewPortMetaTagContent = metaTagNode . content ;
}
}
} ) ( ) ;
" " "
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
let onWindowFocusEventJS = " " "
( function ( ) {
window . addEventListener ( ' focus ' , function ( e ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' onWindowFocus ' ) ;
} ) ;
} ) ( ) ;
" " "
let onWindowBlurEventJS = " " "
( function ( ) {
window . addEventListener ( ' blur ' , function ( e ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) . callHandler ( ' onWindowBlur ' ) ;
} ) ;
} ) ( ) ;
" " "
2021-02-07 15:05:39 +00:00
let callAsyncJavaScriptBelowIOS14WrapperJS = " " "
( function ( obj ) {
( async function ( $ FUNCTION_ARGUMENT_NAMES ) {
$ FUNCTION_BODY
} ) ( $ FUNCTION_ARGUMENT_VALUES ) . then ( function ( value ) {
window . webkit . messageHandlers [ ' onCallAsyncJavaScriptResultBelowIOS14Received ' ] . postMessage ( { ' value ' : value , ' error ' : null , ' resultUuid ' : ' $ RESULT_UUID ' } ) ;
} ) . catch ( function ( error ) {
window . webkit . messageHandlers [ ' onCallAsyncJavaScriptResultBelowIOS14Received ' ] . postMessage ( { ' value ' : null , ' error ' : error , ' resultUuid ' : ' $ RESULT_UUID ' } ) ;
} ) ;
return null ;
} ) ( $ FUNCTION_ARGUMENTS_OBJ ) ;
" " "
2020-05-21 01:34:39 +00:00
var SharedLastTouchPointTimestamp : [ InAppWebView : Int64 ] = [ : ]
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public class WebViewTransport : NSObject {
var webView : InAppWebView
var request : URLRequest
init ( webView : InAppWebView , request : URLRequest ) {
self . webView = webView
self . request = request
}
}
2020-05-09 02:36:07 +00:00
public class InAppWebView : WKWebView , UIScrollViewDelegate , WKUIDelegate , WKNavigationDelegate , WKScriptMessageHandler , UIGestureRecognizerDelegate {
2019-11-02 03:16:47 +00:00
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
var windowId : Int64 ?
2019-11-02 03:16:47 +00:00
var IABController : InAppBrowserWebViewController ?
2019-12-16 22:58:10 +00:00
var channel : FlutterMethodChannel ?
2019-11-02 03:16:47 +00:00
var options : InAppWebViewOptions ?
var currentURL : URL ?
2020-06-15 00:08:23 +00:00
var x509CertificateData : Data ?
static var sslCertificateMap : [ String : Data ] = [ : ] // [ U R L h o s t n a m e : x 5 0 9 C e r t i f i c a t e D a t a ]
2019-11-02 03:16:47 +00:00
var startPageTime : Int64 = 0
static var credentialsProposed : [ URLCredential ] = [ ]
2019-11-18 21:21:35 +00:00
var lastScrollX : CGFloat = 0
var lastScrollY : CGFloat = 0
2019-12-02 23:07:29 +00:00
var isPausedTimers = false
var isPausedTimersCompletionHandler : ( ( ) -> Void ) ?
2019-12-09 23:29:09 +00:00
// T h i s f l a g i s u s e d t o b l o c k t h e " s h o u l d O v e r r i d e U r l L o a d i n g " e v e n t w h e n t h e W K W e b V i e w i s l o a d i n g t h e f i r s t t i m e ,
// i n o r d e r t o h a v e t h e s a m e b e h a v i o r a s A n d r o i d
var activateShouldOverrideUrlLoading = false
2020-05-21 01:34:39 +00:00
var contextMenu : [ String : Any ] ?
2021-02-01 14:55:27 +00:00
var userScripts : [ WKUserScript ] = [ ]
2019-11-02 03:16:47 +00:00
2020-05-09 02:36:07 +00:00
// h t t p s : / / g i t h u b . c o m / m o z i l l a - m o b i l e / f i r e f o x - i o s / b l o b / 5 0 5 3 1 a 7 e 9 e 4 d 4 5 9 f b 1 1 d 4 f c b 7 d 4 3 2 2 e 0 8 1 0 3 5 0 1 f / C l i e n t / F r o n t e n d / B r o w s e r / C o n t e x t M e n u H e l p e r . s w i f t
fileprivate var nativeHighlightLongPressRecognizer : UILongPressGestureRecognizer ?
var longPressRecognizer : UILongPressGestureRecognizer ?
2020-05-21 01:34:39 +00:00
var lastLongPressTouchPoint : CGPoint ?
var lastTouchPoint : CGPoint ?
var lastTouchPointTimestamp = Int64 ( Date ( ) . timeIntervalSince1970 * 1000 )
var contextMenuIsShowing = false
2020-05-21 21:02:08 +00:00
// f l a g u s e d f o r t h e w o r k a r o u n d t o t r i g g e r o n C r e a t e C o n t e x t M e n u e v e n t a s t h e s a m e o n A n d r o i d
var onCreateContextMenuEventTriggeredWhenMenuDisabled = false
2020-05-09 02:36:07 +00:00
2020-05-21 01:34:39 +00:00
var customIMPs : [ IMP ] = [ ]
2020-05-09 02:36:07 +00:00
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
static var windowWebViews : [ Int64 : WebViewTransport ] = [ : ]
static var windowAutoincrementId : Int64 = 0 ;
2021-02-06 01:30:15 +00:00
var userScriptsContentWorlds : [ String ] = [ " page " ]
2021-02-04 00:43:55 +00:00
2021-02-07 15:05:39 +00:00
var callAsyncJavaScriptBelowIOS14Results : [ String : FlutterResult ] = [ : ]
2020-05-21 01:34:39 +00:00
init ( frame : CGRect , configuration : WKWebViewConfiguration , IABController : InAppBrowserWebViewController ? , contextMenu : [ String : Any ] ? , channel : FlutterMethodChannel ? ) {
2019-11-02 03:16:47 +00:00
super . init ( frame : frame , configuration : configuration )
2019-12-16 22:58:10 +00:00
self . channel = channel
2020-05-21 01:34:39 +00:00
self . contextMenu = contextMenu
2019-11-02 03:16:47 +00:00
self . IABController = IABController
uiDelegate = self
navigationDelegate = self
scrollView . delegate = self
2020-05-09 02:36:07 +00:00
self . longPressRecognizer = UILongPressGestureRecognizer ( )
self . longPressRecognizer ! . delegate = self
self . longPressRecognizer ! . addTarget ( self , action : #selector ( longPressGestureDetected ) )
2019-11-02 03:16:47 +00:00
}
2020-07-02 15:30:55 +00:00
override public var frame : CGRect {
get {
return super . frame
}
set {
super . frame = newValue
self . scrollView . contentInset = UIEdgeInsets . zero ;
if #available ( iOS 11 , * ) {
// A b o v e i O S 1 1 , a d j u s t c o n t e n t I n s e t t o c o m p e n s a t e t h e a d j u s t e d C o n t e n t I n s e t s o t h e s u m w i l l
// a l w a y s b e 0 .
if ( scrollView . adjustedContentInset != UIEdgeInsets . zero ) {
let insetToAdjust = self . scrollView . adjustedContentInset ;
scrollView . contentInset = UIEdgeInsets ( top : - insetToAdjust . top , left : - insetToAdjust . left ,
bottom : - insetToAdjust . bottom , right : - insetToAdjust . right ) ;
}
}
}
}
2019-11-02 03:16:47 +00:00
required public init ( coder aDecoder : NSCoder ) {
super . init ( coder : aDecoder ) !
}
2020-05-09 02:36:07 +00:00
public func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldRecognizeSimultaneouslyWith otherGestureRecognizer : UIGestureRecognizer ) -> Bool {
return true
}
// B V C K V O e v e n t s f o r a l l c h a n g e s o n t h e w e b v i e w w i l l c a l l t h i s .
// I t i s c a l l e d f r e q u e n t l y d u r i n g a p a g e l o a d ( p a r t i c u l a r l y o n p r o g r e s s c h a n g e s a n d U R L c h a n g e s ) .
// A s o f i O S 1 2 , W K C o n t e n t V i e w g e s t u r e s e t u p i s a s y n c , b u t i t h a s b e e n c a l l e d b y t h e t i m e
// t h e w e b v i e w i s r e a d y t o l o a d a n U R L . A f t e r t h i s h a s h a p p e n e d , w e c a n o v e r r i d e t h e g e s t u r e .
func replaceGestureHandlerIfNeeded ( ) {
DispatchQueue . main . async {
if self . gestureRecognizerWithDescriptionFragment ( " InAppWebView " ) = = nil {
self . replaceWebViewLongPress ( )
}
}
}
private func replaceWebViewLongPress ( ) {
// W e b K i t i n s t a l l s g e s t u r e h a n d l e r s a s y n c . I f ` r e p l a c e W e b V i e w L o n g P r e s s ` i s c a l l e d a f t e r a w k w e b v i e w i n m o s t c a s e s a s m a l l d e l a y i s s u f f i c i e n t
// S e e a l s o h t t p s : / / b u g s . w e b k i t . o r g / s h o w _ b u g . c g i ? i d = 1 9 3 3 6 6
nativeHighlightLongPressRecognizer = gestureRecognizerWithDescriptionFragment ( " action=_highlightLongPressRecognized: " )
if let nativeLongPressRecognizer = gestureRecognizerWithDescriptionFragment ( " action=_longPressRecognized: " ) {
nativeLongPressRecognizer . removeTarget ( nil , action : nil )
nativeLongPressRecognizer . addTarget ( self , action : #selector ( self . longPressGestureDetected ) )
}
}
private func gestureRecognizerWithDescriptionFragment ( _ descriptionFragment : String ) -> UILongPressGestureRecognizer ? {
let result = self . scrollView . subviews . compactMap ( { $0 . gestureRecognizers } ) . joined ( ) . first ( where : {
( ( $0 as ? UILongPressGestureRecognizer ) != nil ) && $0 . description . contains ( descriptionFragment )
} )
return result as ? UILongPressGestureRecognizer
}
@objc func longPressGestureDetected ( _ sender : UIGestureRecognizer ) {
if sender . state = = . cancelled {
return
}
guard sender . state = = . began else {
return
}
// T o p r e v e n t t h e t a p p e d l i n k f r o m p r o c e e d i n g w i t h n a v i g a t i o n , " c a n c e l " t h e n a t i v e W K W e b V i e w
// ` _ h i g h l i g h t L o n g P r e s s R e c o g n i z e r ` . T h i s p r e s e r v e s t h e o r i g i n a l b e h a v i o r a s s e e n h e r e :
// h t t p s : / / g i t h u b . c o m / W e b K i t / w e b k i t / b l o b / d 5 9 1 6 4 7 b a f 5 4 b 4 b 3 0 0 c a 5 5 0 1 c 2 1 a 6 8 4 5 5 4 2 9 e 1 8 2 / S o u r c e / W e b K i t / U I P r o c e s s / i o s / W K C o n t e n t V i e w I n t e r a c t i o n . m m # L 1 6 0 0 - L 1 6 1 4
if let nativeHighlightLongPressRecognizer = self . nativeHighlightLongPressRecognizer ,
nativeHighlightLongPressRecognizer . isEnabled {
nativeHighlightLongPressRecognizer . isEnabled = false
nativeHighlightLongPressRecognizer . isEnabled = true
}
// F i n d i n g a c t u a l t o u c h l o c a t i o n i n w e b V i e w
var touchLocation = sender . location ( in : self )
touchLocation . x -= self . scrollView . contentInset . left
touchLocation . y -= self . scrollView . contentInset . top
touchLocation . x /= self . scrollView . zoomScale
touchLocation . y /= self . scrollView . zoomScale
2020-05-21 01:34:39 +00:00
lastLongPressTouchPoint = touchLocation
2020-05-09 02:36:07 +00:00
self . evaluateJavaScript ( " window. \( JAVASCRIPT_BRIDGE_NAME ) ._findElementsAtPoint( \( touchLocation . x ) , \( touchLocation . y ) ) " , completionHandler : { ( value , error ) in
if error != nil {
2020-05-21 01:34:39 +00:00
print ( " Long press gesture recognizer error: \( error ? . localizedDescription ? ? " " ) " )
2020-05-09 02:36:07 +00:00
} else {
self . onLongPressHitTestResult ( hitTestResult : value as ! [ String : Any ? ] )
}
} )
}
2020-05-21 01:34:39 +00:00
public override func hitTest ( _ point : CGPoint , with event : UIEvent ? ) -> UIView ? {
lastTouchPoint = point
lastTouchPointTimestamp = Int64 ( Date ( ) . timeIntervalSince1970 * 1000 )
SharedLastTouchPointTimestamp [ self ] = lastTouchPointTimestamp
2020-05-22 14:52:04 +00:00
// r e - b u i l d c o n t e x t m e n u i t e m s f o r t h e c u r r e n t w e b v i e w
2020-05-21 01:34:39 +00:00
UIMenuController . shared . menuItems = [ ]
if let menu = self . contextMenu {
if let menuItems = menu [ " menuItems " ] as ? [ [ String : Any ] ] {
for menuItem in menuItems {
let id = menuItem [ " iosId " ] as ! String
let title = menuItem [ " title " ] as ! String
let targetMethodName = " onContextMenuActionItemClicked- " + String ( self . hash ) + " - " + id
if ! self . responds ( to : Selector ( targetMethodName ) ) {
let customAction : ( ) -> Void = {
let arguments : [ String : Any ? ] = [
" iosId " : id ,
" androidId " : nil ,
" title " : title
]
self . channel ? . invokeMethod ( " onContextMenuActionItemClicked " , arguments : arguments )
}
let castedCustomAction : AnyObject = unsafeBitCast ( customAction as @ convention ( block ) ( ) -> Void , to : AnyObject . self )
let swizzledImplementation = imp_implementationWithBlock ( castedCustomAction )
class_addMethod ( InAppWebView . self , Selector ( targetMethodName ) , swizzledImplementation , nil )
self . customIMPs . append ( swizzledImplementation )
}
let item = UIMenuItem ( title : title , action : Selector ( targetMethodName ) )
UIMenuController . shared . menuItems ! . append ( item )
}
}
}
return super . hitTest ( point , with : event )
}
2020-05-21 21:02:08 +00:00
2020-05-21 01:34:39 +00:00
public override func canPerformAction ( _ action : Selector , withSender sender : Any ? ) -> Bool {
if let _ = sender as ? UIMenuController {
if self . options ? . disableContextMenu = = true {
2020-05-21 21:02:08 +00:00
if ! onCreateContextMenuEventTriggeredWhenMenuDisabled {
// w o r k a r o u n d t o t r i g g e r o n C r e a t e C o n t e x t M e n u e v e n t a s t h e s a m e o n A n d r o i d
self . onCreateContextMenu ( )
onCreateContextMenuEventTriggeredWhenMenuDisabled = true
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.5 ) {
self . onCreateContextMenuEventTriggeredWhenMenuDisabled = false
}
}
2020-05-21 01:34:39 +00:00
return false
}
2020-05-22 14:52:04 +00:00
2020-05-30 18:23:33 +00:00
if let menu = contextMenu {
let contextMenuOptions = ContextMenuOptions ( )
if let contextMenuOptionsMap = menu [ " options " ] as ? [ String : Any ? ] {
let _ = contextMenuOptions . parse ( options : contextMenuOptionsMap )
if ! action . description . starts ( with : " onContextMenuActionItemClicked- " ) && contextMenuOptions . hideDefaultSystemContextMenuItems {
return false
}
}
}
2020-05-21 01:34:39 +00:00
if contextMenuIsShowing , ! action . description . starts ( with : " onContextMenuActionItemClicked- " ) {
let id = action . description . compactMap ( { $0 . asciiValue ? . description } ) . joined ( )
let arguments : [ String : Any ? ] = [
" iosId " : id ,
" androidId " : nil ,
" title " : action . description
]
self . channel ? . invokeMethod ( " onContextMenuActionItemClicked " , arguments : arguments )
}
}
return super . canPerformAction ( action , withSender : sender )
}
2020-05-09 02:36:07 +00:00
2019-11-02 03:16:47 +00:00
public func prepare ( ) {
2020-05-09 02:36:07 +00:00
self . scrollView . addGestureRecognizer ( self . longPressRecognizer ! )
2019-11-02 03:16:47 +00:00
addObserver ( self ,
forKeyPath : # keyPath ( WKWebView . estimatedProgress ) ,
options : . new ,
context : nil )
2019-12-16 22:58:10 +00:00
addObserver ( self ,
forKeyPath : # keyPath ( WKWebView . url ) ,
options : [ . new , . old ] ,
context : nil )
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
addObserver ( self ,
forKeyPath : # keyPath ( WKWebView . title ) ,
options : [ . new , . old ] ,
context : nil )
2020-05-21 01:34:39 +00:00
NotificationCenter . default . addObserver (
self ,
selector : #selector ( onCreateContextMenu ) ,
name : UIMenuController . willShowMenuNotification ,
object : nil )
2019-12-16 22:58:10 +00:00
2020-05-21 01:34:39 +00:00
NotificationCenter . default . addObserver (
self ,
selector : #selector ( onHideContextMenu ) ,
name : UIMenuController . didHideMenuNotification ,
object : nil )
2020-05-09 02:36:07 +00:00
2020-05-22 14:52:04 +00:00
// l i s t e n f o r v i d e o s p l a y i n g i n f u l l s c r e e n
NotificationCenter . default . addObserver ( self ,
selector : #selector ( onEnterFullscreen ( _ : ) ) ,
name : UIWindow . didBecomeVisibleNotification ,
object : window )
// l i s t e n f o r v i d e o s s t o p p i n g t o p l a y i n f u l l s c r e e n
NotificationCenter . default . addObserver ( self ,
selector : #selector ( onExitFullscreen ( _ : ) ) ,
name : UIWindow . didBecomeHiddenNotification ,
object : window )
2020-06-19 19:59:43 +00:00
if let options = options {
if options . transparentBackground {
isOpaque = false
backgroundColor = UIColor . clear
scrollView . backgroundColor = UIColor . clear
2019-11-02 03:16:47 +00:00
}
2020-06-19 19:59:43 +00:00
// p r e v e n t w e b V i e w f r o m b o u n c i n g
if options . disallowOverScroll {
if responds ( to : #selector ( getter : scrollView ) ) {
scrollView . bounces = false
}
else {
for subview : UIView in subviews {
if subview is UIScrollView {
( subview as ! UIScrollView ) . bounces = false
}
2019-11-02 03:16:47 +00:00
}
}
}
2020-06-19 19:59:43 +00:00
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
if #available ( iOS 11.0 , * ) {
accessibilityIgnoresInvertColors = options . accessibilityIgnoresInvertColors
scrollView . contentInsetAdjustmentBehavior =
UIScrollView . ContentInsetAdjustmentBehavior . init ( rawValue : options . contentInsetAdjustmentBehavior ) !
}
allowsBackForwardNavigationGestures = options . allowsBackForwardNavigationGestures
if #available ( iOS 9.0 , * ) {
allowsLinkPreview = options . allowsLinkPreview
if ! options . userAgent . isEmpty {
customUserAgent = options . userAgent
}
}
if #available ( iOS 13.0 , * ) {
scrollView . automaticallyAdjustsScrollIndicatorInsets = options . automaticallyAdjustsScrollIndicatorInsets
}
scrollView . showsVerticalScrollIndicator = ! options . disableVerticalScroll
scrollView . showsHorizontalScrollIndicator = ! options . disableHorizontalScroll
scrollView . showsVerticalScrollIndicator = options . verticalScrollBarEnabled
scrollView . showsHorizontalScrollIndicator = options . horizontalScrollBarEnabled
2021-02-03 14:17:15 +00:00
scrollView . isScrollEnabled = ! ( options . disableVerticalScroll && options . disableHorizontalScroll )
scrollView . isDirectionalLockEnabled = options . isDirectionalLockEnabled
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
scrollView . decelerationRate = InAppWebView . getDecelerationRate ( type : options . decelerationRate )
scrollView . alwaysBounceVertical = options . alwaysBounceVertical
scrollView . alwaysBounceHorizontal = options . alwaysBounceHorizontal
scrollView . scrollsToTop = options . scrollsToTop
scrollView . isPagingEnabled = options . isPagingEnabled
scrollView . maximumZoomScale = CGFloat ( options . maximumZoomScale )
scrollView . minimumZoomScale = CGFloat ( options . minimumZoomScale )
2021-02-04 00:43:55 +00:00
if #available ( iOS 14.0 , * ) {
mediaType = options . mediaType
pageZoom = CGFloat ( options . pageZoom )
}
2021-01-28 16:10:15 +00:00
// d e b u g g i n g i s a l w a y s e n a b l e d f o r i O S ,
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
// t h e r e i s n ' t a n y o p t i o n t o s e t a b o u t i t s u c h a s o n A n d r o i d .
if options . clearCache {
clearCache ( )
}
}
2021-02-01 14:55:27 +00:00
prepareAndAddUserScripts ( )
2020-07-03 18:12:52 +00:00
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
if windowId != nil {
// t h e n e w c r e a t e d w i n d o w w e b v i e w h a s t h e s a m e W K W e b V i e w C o n f i g u r a t i o n v a r i a b l e r e f e r e n c e
return
}
configuration . preferences = WKPreferences ( )
2021-02-01 14:55:27 +00:00
if let options = options {
if #available ( iOS 9.0 , * ) {
configuration . allowsAirPlayForMediaPlayback = options . allowsAirPlayForMediaPlayback
configuration . allowsPictureInPictureMediaPlayback = options . allowsPictureInPictureMediaPlayback
if ! options . applicationNameForUserAgent . isEmpty {
configuration . applicationNameForUserAgent = options . applicationNameForUserAgent
}
}
configuration . preferences . javaScriptCanOpenWindowsAutomatically = options . javaScriptCanOpenWindowsAutomatically
configuration . preferences . minimumFontSize = CGFloat ( options . minimumFontSize )
if #available ( iOS 13.0 , * ) {
configuration . preferences . isFraudulentWebsiteWarningEnabled = options . isFraudulentWebsiteWarningEnabled
configuration . defaultWebpagePreferences . preferredContentMode = WKWebpagePreferences . ContentMode ( rawValue : options . preferredContentMode ) !
}
2021-02-04 00:43:55 +00:00
configuration . preferences . javaScriptEnabled = options . javaScriptEnabled
if #available ( iOS 14.0 , * ) {
configuration . defaultWebpagePreferences . allowsContentJavaScript = options . javaScriptEnabled
}
2021-02-01 14:55:27 +00:00
}
}
public func prepareAndAddUserScripts ( ) -> Void {
addWindowIdUserScript ( )
if windowId != nil {
// t h e n e w c r e a t e d w i n d o w w e b v i e w h a s t h e s a m e W K W e b V i e w C o n f i g u r a t i o n v a r i a b l e r e f e r e n c e
return
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
2021-02-01 14:55:27 +00:00
configuration . userContentController = WKUserContentController ( )
addPluginUserScripts ( )
addAllUserScripts ( )
}
func addWindowIdUserScript ( ) -> Void {
let userScriptWindowId = WKUserScript ( source : " window._flutter_inappwebview_windowId = \( windowId = = nil ? " null " : String ( windowId ! ) ) ; " , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( userScriptWindowId )
}
2021-02-06 01:30:15 +00:00
func getAllPluginUserScriptMergedJS ( ) -> String {
var allPluginUserScriptMergedJS = promisePolyfillJS + " \n " +
javaScriptBridgeJS + " \n " +
consoleLogJS + " \n " +
printJS + " \n "
if let options = options {
if options . useShouldInterceptAjaxRequest {
allPluginUserScriptMergedJS += interceptAjaxRequestsJS + " \n "
}
2020-06-19 19:59:43 +00:00
2021-02-06 01:30:15 +00:00
if options . useShouldInterceptFetchRequest {
allPluginUserScriptMergedJS += interceptFetchRequestsJS + " \n "
}
}
return allPluginUserScriptMergedJS
}
@ available ( iOS 14.0 , * )
func addSharedPluginUserScriptsInContentWorld ( contentWorldName : String ) -> Void {
let contentWorld = getContentWorld ( name : contentWorldName )
let promisePolyfillJSScript = WKUserScript ( source : promisePolyfillJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( promisePolyfillJSScript )
let javaScriptBridgeJSScript = WKUserScript ( source : javaScriptBridgeJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( javaScriptBridgeJSScript )
configuration . userContentController . removeScriptMessageHandler ( forName : " callHandler " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " callHandler " )
let consoleLogJSScript = WKUserScript ( source : consoleLogJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( consoleLogJSScript )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleLog " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " consoleLog " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleDebug " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " consoleDebug " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleError " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " consoleError " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleInfo " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " consoleInfo " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleWarn " , contentWorld : contentWorld )
configuration . userContentController . add ( self , contentWorld : contentWorld , name : " consoleWarn " )
if let options = options {
if options . useShouldInterceptAjaxRequest {
let interceptAjaxRequestsJSScript = WKUserScript ( source : interceptAjaxRequestsJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( interceptAjaxRequestsJSScript )
}
2021-02-04 00:43:55 +00:00
2021-02-06 01:30:15 +00:00
if options . useShouldInterceptFetchRequest {
let interceptFetchRequestsJSScript = WKUserScript ( source : interceptFetchRequestsJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( interceptFetchRequestsJSScript )
2021-02-04 00:43:55 +00:00
}
}
2021-02-06 01:30:15 +00:00
let printJSScript = WKUserScript ( source : printJS , injectionTime : . atDocumentStart , forMainFrameOnly : false , in : contentWorld )
configuration . userContentController . addUserScript ( printJSScript )
2021-02-04 00:43:55 +00:00
}
2021-02-06 01:30:15 +00:00
func addSharedPluginUserScriptsInContentWorlds ( ) -> Void {
2021-02-04 00:43:55 +00:00
if #available ( iOS 14.0 , * ) {
2021-02-06 01:30:15 +00:00
for contentWorldName in userScriptsContentWorlds {
addSharedPluginUserScriptsInContentWorld ( contentWorldName : contentWorldName )
2021-02-04 00:43:55 +00:00
}
} else {
2021-02-06 01:30:15 +00:00
let promisePolyfillJSScript = WKUserScript (
source : promisePolyfillJS ,
injectionTime : . atDocumentStart ,
forMainFrameOnly : false )
2020-06-19 19:59:43 +00:00
configuration . userContentController . addUserScript ( promisePolyfillJSScript )
2021-02-06 01:30:15 +00:00
let javaScriptBridgeJSScript = WKUserScript (
source : javaScriptBridgeJS ,
injectionTime : . atDocumentStart ,
forMainFrameOnly : false )
2020-06-19 19:59:43 +00:00
configuration . userContentController . addUserScript ( javaScriptBridgeJSScript )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " callHandler " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " callHandler " )
2021-02-06 01:30:15 +00:00
let consoleLogJSScript = WKUserScript (
source : consoleLogJS ,
injectionTime : . atDocumentStart ,
forMainFrameOnly : false )
2020-06-19 19:59:43 +00:00
configuration . userContentController . addUserScript ( consoleLogJSScript )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleLog " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " consoleLog " )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleDebug " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " consoleDebug " )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleError " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " consoleError " )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleInfo " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " consoleInfo " )
2021-02-01 14:55:27 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleWarn " )
2020-06-19 19:59:43 +00:00
configuration . userContentController . add ( self , name : " consoleWarn " )
2021-02-04 00:43:55 +00:00
if let options = options {
if options . useShouldInterceptAjaxRequest {
2021-02-06 01:30:15 +00:00
let interceptAjaxRequestsJSScript = WKUserScript (
source : interceptAjaxRequestsJS ,
injectionTime : . atDocumentStart ,
forMainFrameOnly : false )
2021-02-04 00:43:55 +00:00
configuration . userContentController . addUserScript ( interceptAjaxRequestsJSScript )
}
if options . useShouldInterceptFetchRequest {
2021-02-06 01:30:15 +00:00
let interceptFetchRequestsJSScript = WKUserScript (
source : interceptFetchRequestsJS ,
injectionTime : . atDocumentStart ,
forMainFrameOnly : false )
2021-02-04 00:43:55 +00:00
configuration . userContentController . addUserScript ( interceptFetchRequestsJSScript )
}
}
2021-02-06 01:30:15 +00:00
let printJSScript = WKUserScript ( source : printJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( printJSScript )
2021-02-07 15:05:39 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " onCallAsyncJavaScriptResultBelowIOS14Received " )
configuration . userContentController . add ( self , name : " onCallAsyncJavaScriptResultBelowIOS14Received " )
2021-02-04 00:43:55 +00:00
}
2021-02-06 01:30:15 +00:00
}
func addPluginUserScripts ( ) -> Void {
addSharedPluginUserScriptsInContentWorlds ( )
2021-02-04 00:43:55 +00:00
let findElementsAtPointJSScript = WKUserScript ( source : findElementsAtPointJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( findElementsAtPointJSScript )
let lastTouchedAnchorOrImageJSScript = WKUserScript ( source : lastTouchedAnchorOrImageJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( lastTouchedAnchorOrImageJSScript )
let findTextHighlightJSScript = WKUserScript ( source : findTextHighlightJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( findTextHighlightJSScript )
configuration . userContentController . removeScriptMessageHandler ( forName : " onFindResultReceived " )
configuration . userContentController . add ( self , name : " onFindResultReceived " )
let onWindowFocusEventJSScript = WKUserScript ( source : onWindowFocusEventJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( onWindowFocusEventJSScript )
let onWindowBlurEventJSScript = WKUserScript ( source : onWindowBlurEventJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( onWindowBlurEventJSScript )
if let options = options {
let originalViewPortMetaTagContentJSScript = WKUserScript ( source : originalViewPortMetaTagContentJS , injectionTime : . atDocumentEnd , forMainFrameOnly : true )
configuration . userContentController . addUserScript ( originalViewPortMetaTagContentJSScript )
2020-06-19 19:59:43 +00:00
2021-02-04 00:43:55 +00:00
if ! options . supportZoom {
let jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta); "
let userScript = WKUserScript ( source : jscript , injectionTime : . atDocumentEnd , forMainFrameOnly : true )
configuration . userContentController . addUserScript ( userScript )
} else if options . enableViewportScale {
let jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta); "
let userScript = WKUserScript ( source : jscript , injectionTime : . atDocumentEnd , forMainFrameOnly : true )
configuration . userContentController . addUserScript ( userScript )
}
2020-06-19 19:59:43 +00:00
if options . useOnLoadResource {
let resourceObserverJSScript = WKUserScript ( source : resourceObserverJS , injectionTime : . atDocumentStart , forMainFrameOnly : false )
configuration . userContentController . addUserScript ( resourceObserverJSScript )
2019-11-02 03:16:47 +00:00
}
}
}
2021-02-01 14:55:27 +00:00
func addAllUserScripts ( ) -> Void {
for userScript in userScripts {
configuration . userContentController . addUserScript ( userScript )
}
}
public func addUserScript ( wkUserScript : WKUserScript ) -> Void {
userScripts . append ( wkUserScript )
configuration . userContentController . addUserScript ( wkUserScript )
}
public func appendUserScript ( userScript : [ String : Any ] ) -> Void {
2021-02-04 00:43:55 +00:00
var wkUserScript : WKUserScript ?
2021-02-06 01:30:15 +00:00
let contentWorldName = userScript [ " contentWorld " ] as ? String
if contentWorldName != nil , ! userScriptsContentWorlds . contains ( contentWorldName ! ) {
userScriptsContentWorlds . append ( contentWorldName ! )
}
if #available ( iOS 14.0 , * ) , let contentWorldName = contentWorldName {
2021-02-04 00:43:55 +00:00
wkUserScript = WKUserScript ( source : userScript [ " source " ] as ! String ,
injectionTime : WKUserScriptInjectionTime . init ( rawValue : userScript [ " injectionTime " ] as ! Int ) ? ? . atDocumentStart ,
forMainFrameOnly : userScript [ " iosForMainFrameOnly " ] as ! Bool ,
in : getContentWorld ( name : contentWorldName ) )
} else {
wkUserScript = WKUserScript ( source : userScript [ " source " ] as ! String ,
2021-02-06 01:30:15 +00:00
injectionTime : WKUserScriptInjectionTime . init ( rawValue : userScript [ " injectionTime " ] as ! Int ) ? ? . atDocumentStart ,
forMainFrameOnly : userScript [ " iosForMainFrameOnly " ] as ! Bool )
2021-02-04 00:43:55 +00:00
}
userScripts . append ( wkUserScript ! )
2021-02-01 14:55:27 +00:00
}
public func appendUserScripts ( wkUserScripts : [ WKUserScript ] ) -> Void {
for wkUserScript in wkUserScripts {
userScripts . append ( wkUserScript )
}
}
public func appendUserScripts ( userScripts : [ [ String : Any ] ] ) -> Void {
for userScript in userScripts {
appendUserScript ( userScript : userScript )
}
}
public func removeUserScript ( at index : Int ) -> Void {
userScripts . remove ( at : index )
// t h e r e i s n ' t a w a y t o r e m o v e a s p e c i f i c u s e r s c r i p t u s i n g W K U s e r C o n t e n t C o n t r o l l e r ,
// s o w e r e m o v e a l l t h e u s e r s c r i p t s a n d , t h e n , w e a d d t h e m a g a i n w i t h o u t t h e o n e t h a t h a s b e e n r e m o v e d
configuration . userContentController . removeAllUserScripts ( )
addWindowIdUserScript ( )
addPluginUserScripts ( )
addAllUserScripts ( )
}
public func removeAllUserScripts ( ) -> Void {
userScripts . removeAll ( )
configuration . userContentController . removeAllUserScripts ( )
// a d d a l l t h e n e c e s s a r y b a s e W K U s e r S c r i p t s o f t h i s p l u g i n a g a i n
addWindowIdUserScript ( )
addPluginUserScripts ( )
}
2021-02-04 00:43:55 +00:00
@ available ( iOS 14.0 , * )
func getContentWorld ( name : String ) -> WKContentWorld {
switch name {
case " defaultClient " :
return WKContentWorld . defaultClient
case " page " :
return WKContentWorld . page
default :
return WKContentWorld . world ( name : name )
}
}
2019-11-02 03:16:47 +00:00
@ available ( iOS 10.0 , * )
2020-05-29 17:56:03 +00:00
static public func getDataDetectorType ( type : String ) -> WKDataDetectorTypes {
2019-11-02 03:16:47 +00:00
switch type {
case " NONE " :
return WKDataDetectorTypes . init ( rawValue : 0 )
case " PHONE_NUMBER " :
return . phoneNumber
case " LINK " :
return . link
case " ADDRESS " :
return . address
case " CALENDAR_EVENT " :
return . calendarEvent
case " TRACKING_NUMBER " :
return . trackingNumber
case " FLIGHT_NUMBER " :
return . flightNumber
case " LOOKUP_SUGGESTION " :
return . lookupSuggestion
case " SPOTLIGHT_SUGGESTION " :
return . spotlightSuggestion
case " ALL " :
return . all
default :
return WKDataDetectorTypes . init ( rawValue : 0 )
}
}
2020-05-29 17:56:03 +00:00
@ available ( iOS 10.0 , * )
static public func getDataDetectorTypeString ( type : WKDataDetectorTypes ) -> [ String ] {
var dataDetectorTypeString : [ String ] = [ ]
if type . contains ( . all ) {
dataDetectorTypeString . append ( " ALL " )
} else {
if type . contains ( . phoneNumber ) {
dataDetectorTypeString . append ( " PHONE_NUMBER " )
}
if type . contains ( . link ) {
dataDetectorTypeString . append ( " LINK " )
}
if type . contains ( . address ) {
dataDetectorTypeString . append ( " ADDRESS " )
}
if type . contains ( . calendarEvent ) {
dataDetectorTypeString . append ( " CALENDAR_EVENT " )
}
if type . contains ( . trackingNumber ) {
dataDetectorTypeString . append ( " TRACKING_NUMBER " )
}
if type . contains ( . flightNumber ) {
dataDetectorTypeString . append ( " FLIGHT_NUMBER " )
}
if type . contains ( . lookupSuggestion ) {
dataDetectorTypeString . append ( " LOOKUP_SUGGESTION " )
}
if type . contains ( . spotlightSuggestion ) {
dataDetectorTypeString . append ( " SPOTLIGHT_SUGGESTION " )
}
}
if dataDetectorTypeString . count = = 0 {
dataDetectorTypeString = [ " NONE " ]
}
return dataDetectorTypeString
}
static public func getDecelerationRate ( type : String ) -> UIScrollView . DecelerationRate {
2020-05-09 02:36:07 +00:00
switch type {
case " NORMAL " :
return . normal
case " FAST " :
return . fast
default :
return . normal
}
}
2020-05-29 17:56:03 +00:00
static public func getDecelerationRateString ( type : UIScrollView . DecelerationRate ) -> String {
switch type {
case . normal :
return " NORMAL "
case . fast :
return " FAST "
default :
return " NORMAL "
}
}
2019-11-02 03:16:47 +00:00
public static func preWKWebViewConfiguration ( options : InAppWebViewOptions ? ) -> WKWebViewConfiguration {
let configuration = WKWebViewConfiguration ( )
2019-11-19 19:44:06 +00:00
configuration . processPool = WKProcessPoolManager . sharedProcessPool
2020-06-19 19:59:43 +00:00
if let options = options {
configuration . allowsInlineMediaPlayback = options . allowsInlineMediaPlayback
configuration . suppressesIncrementalRendering = options . suppressesIncrementalRendering
configuration . selectionGranularity = WKSelectionGranularity . init ( rawValue : options . selectionGranularity ) !
2021-01-28 17:56:04 +00:00
if options . allowUniversalAccessFromFileURLs {
configuration . setValue ( options . allowUniversalAccessFromFileURLs , forKey : " allowUniversalAccessFromFileURLs " )
}
if options . allowFileAccessFromFileURLs {
configuration . preferences . setValue ( options . allowFileAccessFromFileURLs , forKey : " allowFileAccessFromFileURLs " )
}
2020-06-19 19:59:43 +00:00
if #available ( iOS 9.0 , * ) {
if options . incognito {
configuration . websiteDataStore = WKWebsiteDataStore . nonPersistent ( )
} else if options . cacheEnabled {
configuration . websiteDataStore = WKWebsiteDataStore . default ( )
2019-11-02 03:16:47 +00:00
}
}
2020-06-19 19:59:43 +00:00
if #available ( iOS 10.0 , * ) {
configuration . ignoresViewportScaleLimits = options . ignoresViewportScaleLimits
var dataDetectorTypes = WKDataDetectorTypes . init ( rawValue : 0 )
for type in options . dataDetectorTypes {
let dataDetectorType = InAppWebView . getDataDetectorType ( type : type )
dataDetectorTypes = WKDataDetectorTypes ( rawValue : dataDetectorTypes . rawValue | dataDetectorType . rawValue )
}
configuration . dataDetectorTypes = dataDetectorTypes
configuration . mediaTypesRequiringUserActionForPlayback = options . mediaPlaybackRequiresUserGesture ? . all : [ ]
} else {
// F a l l b a c k o n e a r l i e r v e r s i o n s
configuration . mediaPlaybackRequiresUserAction = options . mediaPlaybackRequiresUserGesture
2020-06-02 23:45:58 +00:00
}
2020-06-19 19:59:43 +00:00
if #available ( iOS 11.0 , * ) {
for scheme in options . resourceCustomSchemes {
configuration . setURLSchemeHandler ( CustomeSchemeHandler ( ) , forURLScheme : scheme )
2020-06-02 23:45:58 +00:00
}
2020-06-19 19:59:43 +00:00
if options . sharedCookiesEnabled {
// M o r e i n f o t o s e n d i n g c o o k i e s w i t h W K W e b V i e w
// h t t p s : / / s t a c k o v e r f l o w . c o m / q u e s t i o n s / 2 6 5 7 3 1 3 7 / c a n - i - s e t - t h e - c o o k i e s - t o - b e - u s e d - b y - a - w k w e b v i e w / 2 6 5 7 7 3 0 3 # 2 6 5 7 7 3 0 3
// S e t C o o k i e s i n i O S 1 1 a n d a b o v e , i n i t i a l i z e w e b s i t e D a t a S t o r e b e f o r e s e t t i n g c o o k i e s
// S e e a l s o h t t p s : / / f o r u m s . d e v e l o p e r . a p p l e . c o m / t h r e a d / 9 7 1 9 4
// c h e c k i f w e b s i t e D a t a S t o r e h a s n o t b e e n i n i t i a l i z e d b e f o r e
if ( ! options . incognito && options . cacheEnabled ) {
configuration . websiteDataStore = WKWebsiteDataStore . nonPersistent ( )
}
for cookie in HTTPCookieStorage . shared . cookies ? ? [ ] {
configuration . websiteDataStore . httpCookieStore . setCookie ( cookie , completionHandler : nil )
}
2020-06-02 23:45:58 +00:00
}
}
2021-02-04 00:43:55 +00:00
if #available ( iOS 14.0 , * ) {
configuration . limitsNavigationsToAppBoundDomains = options . limitsNavigationsToAppBoundDomains
}
2020-06-02 23:45:58 +00:00
}
2019-11-02 03:16:47 +00:00
return configuration
}
2020-05-21 01:34:39 +00:00
@objc func onCreateContextMenu ( ) {
let mapSorted = SharedLastTouchPointTimestamp . sorted { $0 . value > $1 . value }
if ( mapSorted . first ? . key != self ) {
return
}
contextMenuIsShowing = true
var arguments : [ String : Any ? ] = [
" hitTestResult " : nil
]
if let lastLongPressTouhLocation = lastLongPressTouchPoint {
if configuration . preferences . javaScriptEnabled {
self . evaluateJavaScript ( " window. \( JAVASCRIPT_BRIDGE_NAME ) ._findElementsAtPoint( \( lastLongPressTouhLocation . x ) , \( lastLongPressTouhLocation . y ) ) " , completionHandler : { ( value , error ) in
if error != nil {
print ( " Long press gesture recognizer error: \( error ? . localizedDescription ? ? " " ) " )
} else {
let hitTestResult = value as ! [ String : Any ? ]
arguments [ " hitTestResult " ] = hitTestResult
self . channel ? . invokeMethod ( " onCreateContextMenu " , arguments : arguments )
}
} )
} else {
channel ? . invokeMethod ( " onCreateContextMenu " , arguments : arguments )
}
} else {
channel ? . invokeMethod ( " onCreateContextMenu " , arguments : arguments )
}
}
@objc func onHideContextMenu ( ) {
if contextMenuIsShowing = = false {
return
}
contextMenuIsShowing = false
let arguments : [ String : Any ] = [ : ]
channel ? . invokeMethod ( " onHideContextMenu " , arguments : arguments )
}
2019-11-02 03:16:47 +00:00
override public func observeValue ( forKeyPath keyPath : String ? , of object : Any ? ,
change : [ NSKeyValueChangeKey : Any ] ? , context : UnsafeMutableRawPointer ? ) {
2019-11-05 23:23:24 +00:00
if keyPath = = # keyPath ( WKWebView . estimatedProgress ) {
2019-11-02 03:16:47 +00:00
let progress = Int ( estimatedProgress * 100 )
onProgressChanged ( progress : progress )
2019-12-16 22:58:10 +00:00
} else if keyPath = = # keyPath ( WKWebView . url ) && change ? [ NSKeyValueChangeKey . newKey ] is URL {
let newUrl = change ? [ NSKeyValueChangeKey . newKey ] as ? URL
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onUpdateVisitedHistory ( url : newUrl ? . absoluteString )
} else if keyPath = = # keyPath ( WKWebView . title ) && change ? [ NSKeyValueChangeKey . newKey ] is String {
let newTitle = change ? [ NSKeyValueChangeKey . newKey ] as ? String
onTitleChanged ( title : newTitle )
}
2020-05-09 02:36:07 +00:00
replaceGestureHandlerIfNeeded ( )
2019-11-02 03:16:47 +00:00
}
public func goBackOrForward ( steps : Int ) {
if canGoBackOrForward ( steps : steps ) {
if ( steps > 0 ) {
let index = steps - 1
go ( to : self . backForwardList . forwardList [ index ] )
}
else if ( steps < 0 ) {
let backListLength = self . backForwardList . backList . count
let index = backListLength + steps
go ( to : self . backForwardList . backList [ index ] )
}
}
}
public func canGoBackOrForward ( steps : Int ) -> Bool {
let currentIndex = self . backForwardList . backList . count
return ( steps >= 0 )
? steps <= self . backForwardList . forwardList . count
: currentIndex + steps >= 0
}
2021-02-09 00:39:35 +00:00
@ available ( iOS 11.0 , * )
2021-02-08 00:17:12 +00:00
public func takeScreenshot ( with : [ String : Any ? ] ? , completionHandler : @ escaping ( _ screenshot : Data ? ) -> Void ) {
2021-02-09 00:39:35 +00:00
var snapshotConfiguration : WKSnapshotConfiguration ? = nil
if let with = with {
snapshotConfiguration = WKSnapshotConfiguration ( )
if let rect = with [ " rect " ] as ? [ String : Double ] {
snapshotConfiguration ! . rect = CGRect ( x : rect [ " x " ] ! , y : rect [ " y " ] ! , width : rect [ " width " ] ! , height : rect [ " height " ] ! )
2021-02-08 00:17:12 +00:00
}
2021-02-09 00:39:35 +00:00
if let snapshotWidth = with [ " snapshotWidth " ] as ? Double {
snapshotConfiguration ! . snapshotWidth = NSNumber ( value : snapshotWidth )
}
if #available ( iOS 13.0 , * ) , let afterScreenUpdates = with [ " iosAfterScreenUpdates " ] as ? Bool {
snapshotConfiguration ! . afterScreenUpdates = afterScreenUpdates
}
}
takeSnapshot ( with : snapshotConfiguration , completionHandler : { ( image , error ) -> Void in
var imageData : Data ? = nil
if let screenshot = image {
if let with = with {
switch with [ " compressFormat " ] as ! String {
case " JPEG " :
let quality = Float ( with [ " quality " ] as ! Int ) / 100
imageData = screenshot . jpegData ( compressionQuality : CGFloat ( quality ) ) !
break
case " PNG " :
imageData = screenshot . pngData ( ) !
break
default :
2021-02-08 00:17:12 +00:00
imageData = screenshot . pngData ( ) !
}
2019-11-02 03:16:47 +00:00
}
2021-02-09 00:39:35 +00:00
else {
imageData = screenshot . pngData ( ) !
}
}
completionHandler ( imageData )
} )
2019-11-02 03:16:47 +00:00
}
2021-02-09 00:39:35 +00:00
@ available ( iOS 14.0 , * )
2021-02-08 17:28:35 +00:00
public func createPdf ( configuration : [ String : Any ? ] ? , completionHandler : @ escaping ( _ pdf : Data ? ) -> Void ) {
2021-02-09 00:39:35 +00:00
let pdfConfiguration : WKPDFConfiguration = . init ( )
if let configuration = configuration {
if let rect = configuration [ " rect " ] as ? [ String : Double ] {
pdfConfiguration . rect = CGRect ( x : rect [ " x " ] ! , y : rect [ " y " ] ! , width : rect [ " width " ] ! , height : rect [ " height " ] ! )
2021-02-08 17:28:35 +00:00
}
2021-02-09 00:39:35 +00:00
}
createPDF ( configuration : pdfConfiguration ) { ( result ) in
switch ( result ) {
case . success ( let data ) :
completionHandler ( data )
return
case . failure ( let error ) :
print ( error . localizedDescription )
completionHandler ( nil )
return
}
}
}
@ available ( iOS 14.0 , * )
public func createWebArchiveData ( dataCompletionHandler : @ escaping ( _ webArchiveData : Data ? ) -> Void ) {
createWebArchiveData ( completionHandler : { ( result ) in
switch ( result ) {
case . success ( let data ) :
dataCompletionHandler ( data )
return
case . failure ( let error ) :
print ( error . localizedDescription )
dataCompletionHandler ( nil )
return
}
} )
}
@ available ( iOS 14.0 , * )
public func saveWebArchive ( filePath : String , autoname : Bool , completionHandler : @ escaping ( _ path : String ? ) -> Void ) {
createWebArchiveData ( dataCompletionHandler : { ( webArchiveData ) in
if let webArchiveData = webArchiveData {
var localUrl = URL ( fileURLWithPath : filePath )
if autoname {
if let url = self . url {
// t r i e s t o m i m i c A n d r o i d s a v e W e b A r c h i v e m e t h o d
let invalidCharacters = CharacterSet ( charactersIn : " \\ /:*? \" <>| " )
. union ( . newlines )
. union ( . illegalCharacters )
. union ( . controlCharacters )
let currentPageUrlFileName = url . path
. components ( separatedBy : invalidCharacters )
. joined ( separator : " " )
let fullPath = filePath + " / " + currentPageUrlFileName + " .webarchive "
localUrl = URL ( fileURLWithPath : fullPath )
} else {
completionHandler ( nil )
return
}
}
do {
try webArchiveData . write ( to : localUrl )
completionHandler ( localUrl . path )
} catch {
// C a t c h a n y e r r o r s
2021-02-08 17:28:35 +00:00
print ( error . localizedDescription )
completionHandler ( nil )
}
2021-02-09 00:39:35 +00:00
} else {
completionHandler ( nil )
2021-02-08 17:28:35 +00:00
}
2021-02-09 00:39:35 +00:00
} )
2021-02-08 17:28:35 +00:00
}
2019-11-02 03:16:47 +00:00
public func loadUrl ( url : URL , headers : [ String : String ] ? ) {
var request = URLRequest ( url : url )
currentURL = url
if headers != nil {
if let mutableRequest = ( request as NSURLRequest ) . mutableCopy ( ) as ? NSMutableURLRequest {
for ( key , value ) in headers ! {
mutableRequest . setValue ( value , forHTTPHeaderField : key )
}
request = mutableRequest as URLRequest
}
}
load ( request )
}
public func postUrl ( url : URL , postData : Data , completionHandler : @ escaping ( ) -> Void ) {
var request = URLRequest ( url : url )
currentURL = url
request . httpMethod = " POST "
request . httpBody = postData
let task = URLSession . shared . dataTask ( with : request ) { ( data : Data ? , response : URLResponse ? , error : Error ? ) in
var returnString = " "
if data != nil {
returnString = String ( data : data ! , encoding : . utf8 ) ? ? " "
}
DispatchQueue . main . async ( execute : { ( ) -> Void in
self . loadHTMLString ( returnString , baseURL : url )
completionHandler ( )
} )
}
task . resume ( )
}
public func loadData ( data : String , mimeType : String , encoding : String , baseUrl : String ) {
let url = URL ( string : baseUrl ) !
currentURL = url
if #available ( iOS 9.0 , * ) {
load ( data . data ( using : . utf8 ) ! , mimeType : mimeType , characterEncodingName : encoding , baseURL : url )
} else {
loadHTMLString ( data , baseURL : url )
}
}
public func loadFile ( url : String , headers : [ String : String ] ? ) throws {
let key = SwiftFlutterPlugin . instance ! . registrar ! . lookupKey ( forAsset : url )
let assetURL = Bundle . main . url ( forResource : key , withExtension : nil )
if assetURL = = nil {
throw NSError ( domain : url + " asset file cannot be found! " , code : 0 )
}
loadUrl ( url : assetURL ! , headers : headers )
}
func setOptions ( newOptions : InAppWebViewOptions , newOptionsMap : [ String : Any ] ) {
if newOptionsMap [ " transparentBackground " ] != nil && options ? . transparentBackground != newOptions . transparentBackground {
if newOptions . transparentBackground {
isOpaque = false
backgroundColor = UIColor . clear
scrollView . backgroundColor = UIColor . clear
} else {
isOpaque = true
backgroundColor = nil
scrollView . backgroundColor = UIColor ( red : 1 , green : 1 , blue : 1 , alpha : 1 )
}
}
if newOptionsMap [ " disallowOverScroll " ] != nil && options ? . disallowOverScroll != newOptions . disallowOverScroll {
if responds ( to : #selector ( getter : scrollView ) ) {
scrollView . bounces = ! newOptions . disallowOverScroll
}
else {
for subview : UIView in subviews {
if subview is UIScrollView {
( subview as ! UIScrollView ) . bounces = ! newOptions . disallowOverScroll
}
}
}
}
2019-11-07 23:32:29 +00:00
if #available ( iOS 9.0 , * ) {
if ( newOptionsMap [ " incognito " ] != nil && options ? . incognito != newOptions . incognito && newOptions . incognito ) {
configuration . websiteDataStore = WKWebsiteDataStore . nonPersistent ( )
} else if ( newOptionsMap [ " cacheEnabled " ] != nil && options ? . cacheEnabled != newOptions . cacheEnabled && newOptions . cacheEnabled ) {
configuration . websiteDataStore = WKWebsiteDataStore . default ( )
}
}
if #available ( iOS 11.0 , * ) {
if ( newOptionsMap [ " sharedCookiesEnabled " ] != nil && options ? . sharedCookiesEnabled != newOptions . sharedCookiesEnabled && newOptions . sharedCookiesEnabled ) {
if ( ! newOptions . incognito && ! newOptions . cacheEnabled ) {
configuration . websiteDataStore = WKWebsiteDataStore . nonPersistent ( )
}
for cookie in HTTPCookieStorage . shared . cookies ? ? [ ] {
configuration . websiteDataStore . httpCookieStore . setCookie ( cookie , completionHandler : nil )
}
}
2020-05-09 02:36:07 +00:00
if newOptionsMap [ " accessibilityIgnoresInvertColors " ] != nil && options ? . accessibilityIgnoresInvertColors != newOptions . accessibilityIgnoresInvertColors {
accessibilityIgnoresInvertColors = newOptions . accessibilityIgnoresInvertColors
}
2020-06-13 01:50:19 +00:00
if newOptionsMap [ " contentInsetAdjustmentBehavior " ] != nil && options ? . contentInsetAdjustmentBehavior != newOptions . contentInsetAdjustmentBehavior {
scrollView . contentInsetAdjustmentBehavior =
UIScrollView . ContentInsetAdjustmentBehavior . init ( rawValue : newOptions . contentInsetAdjustmentBehavior ) !
}
2019-11-07 23:32:29 +00:00
}
2020-06-12 02:04:41 +00:00
if newOptionsMap [ " enableViewportScale " ] != nil && options ? . enableViewportScale != newOptions . enableViewportScale {
var jscript = " "
if ( newOptions . enableViewportScale ) {
jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta); "
} else {
jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', window. \( JAVASCRIPT_BRIDGE_NAME ) ._originalViewPortMetaTagContent); document.getElementsByTagName('head')[0].appendChild(meta); "
}
evaluateJavaScript ( jscript , completionHandler : nil )
}
if newOptionsMap [ " supportZoom " ] != nil && options ? . supportZoom != newOptions . supportZoom {
var jscript = " "
if ( newOptions . supportZoom ) {
jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', window. \( JAVASCRIPT_BRIDGE_NAME ) ._originalViewPortMetaTagContent); document.getElementsByTagName('head')[0].appendChild(meta); "
} else {
jscript = " var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta); "
}
2019-11-02 03:16:47 +00:00
evaluateJavaScript ( jscript , completionHandler : nil )
}
2019-11-05 02:44:22 +00:00
if newOptionsMap [ " useOnLoadResource " ] != nil && options ? . useOnLoadResource != newOptions . useOnLoadResource && newOptions . useOnLoadResource {
2019-11-06 21:55:54 +00:00
let placeholderValue = newOptions . useOnLoadResource ? " true " : " false "
2021-02-06 01:30:15 +00:00
let source = enableVariableForOnLoadResourceJS . replacingOccurrences ( of : " $PLACEHOLDER_VALUE " , with : placeholderValue )
evaluateJavaScript ( source , completionHandler : nil )
2019-11-05 02:44:22 +00:00
}
if newOptionsMap [ " useShouldInterceptAjaxRequest " ] != nil && options ? . useShouldInterceptAjaxRequest != newOptions . useShouldInterceptAjaxRequest && newOptions . useShouldInterceptAjaxRequest {
2019-11-06 21:55:54 +00:00
let placeholderValue = newOptions . useShouldInterceptAjaxRequest ? " true " : " false "
2021-02-06 01:30:15 +00:00
let source = enableVariableForShouldInterceptAjaxRequestJS . replacingOccurrences ( of : " $PLACEHOLDER_VALUE " , with : placeholderValue )
if #available ( iOS 14.0 , * ) {
for contentWorldName in userScriptsContentWorlds {
let contentWorld = getContentWorld ( name : contentWorldName )
evaluateJavaScript ( source , in : nil , in : contentWorld , completionHandler : nil )
}
} else {
evaluateJavaScript ( source , completionHandler : nil )
}
2019-11-05 02:44:22 +00:00
}
if newOptionsMap [ " useShouldInterceptFetchRequest " ] != nil && options ? . useShouldInterceptFetchRequest != newOptions . useShouldInterceptFetchRequest && newOptions . useShouldInterceptFetchRequest {
2019-11-06 21:55:54 +00:00
let placeholderValue = newOptions . useShouldInterceptFetchRequest ? " true " : " false "
2021-02-06 01:30:15 +00:00
let source = enableVariableForShouldInterceptAjaxRequestJS . replacingOccurrences ( of : " $PLACEHOLDER_VALUE " , with : placeholderValue )
if #available ( iOS 14.0 , * ) {
for contentWorldName in userScriptsContentWorlds {
let contentWorld = getContentWorld ( name : contentWorldName )
evaluateJavaScript ( source , in : nil , in : contentWorld , completionHandler : nil )
}
} else {
evaluateJavaScript ( source , completionHandler : nil )
}
2019-11-05 02:44:22 +00:00
}
2019-11-02 03:16:47 +00:00
if newOptionsMap [ " mediaPlaybackRequiresUserGesture " ] != nil && options ? . mediaPlaybackRequiresUserGesture != newOptions . mediaPlaybackRequiresUserGesture {
if #available ( iOS 10.0 , * ) {
configuration . mediaTypesRequiringUserActionForPlayback = ( newOptions . mediaPlaybackRequiresUserGesture ) ? . all : [ ]
} else {
// F a l l b a c k o n e a r l i e r v e r s i o n s
configuration . mediaPlaybackRequiresUserAction = newOptions . mediaPlaybackRequiresUserGesture
}
}
if newOptionsMap [ " allowsInlineMediaPlayback " ] != nil && options ? . allowsInlineMediaPlayback != newOptions . allowsInlineMediaPlayback {
configuration . allowsInlineMediaPlayback = newOptions . allowsInlineMediaPlayback
}
if newOptionsMap [ " suppressesIncrementalRendering " ] != nil && options ? . suppressesIncrementalRendering != newOptions . suppressesIncrementalRendering {
configuration . suppressesIncrementalRendering = newOptions . suppressesIncrementalRendering
}
if newOptionsMap [ " allowsBackForwardNavigationGestures " ] != nil && options ? . allowsBackForwardNavigationGestures != newOptions . allowsBackForwardNavigationGestures {
allowsBackForwardNavigationGestures = newOptions . allowsBackForwardNavigationGestures
}
if newOptionsMap [ " javaScriptCanOpenWindowsAutomatically " ] != nil && options ? . javaScriptCanOpenWindowsAutomatically != newOptions . javaScriptCanOpenWindowsAutomatically {
configuration . preferences . javaScriptCanOpenWindowsAutomatically = newOptions . javaScriptCanOpenWindowsAutomatically
}
if newOptionsMap [ " minimumFontSize " ] != nil && options ? . minimumFontSize != newOptions . minimumFontSize {
configuration . preferences . minimumFontSize = CGFloat ( newOptions . minimumFontSize )
}
if newOptionsMap [ " selectionGranularity " ] != nil && options ? . selectionGranularity != newOptions . selectionGranularity {
configuration . selectionGranularity = WKSelectionGranularity . init ( rawValue : newOptions . selectionGranularity ) !
}
if #available ( iOS 10.0 , * ) {
if newOptionsMap [ " ignoresViewportScaleLimits " ] != nil && options ? . ignoresViewportScaleLimits != newOptions . ignoresViewportScaleLimits {
configuration . ignoresViewportScaleLimits = newOptions . ignoresViewportScaleLimits
}
if newOptionsMap [ " dataDetectorTypes " ] != nil && options ? . dataDetectorTypes != newOptions . dataDetectorTypes {
var dataDetectorTypes = WKDataDetectorTypes . init ( rawValue : 0 )
for type in newOptions . dataDetectorTypes {
2020-05-29 17:56:03 +00:00
let dataDetectorType = InAppWebView . getDataDetectorType ( type : type )
2019-11-02 03:16:47 +00:00
dataDetectorTypes = WKDataDetectorTypes ( rawValue : dataDetectorTypes . rawValue | dataDetectorType . rawValue )
}
configuration . dataDetectorTypes = dataDetectorTypes
}
}
if #available ( iOS 13.0 , * ) {
2019-12-16 22:58:10 +00:00
if newOptionsMap [ " isFraudulentWebsiteWarningEnabled " ] != nil && options ? . isFraudulentWebsiteWarningEnabled != newOptions . isFraudulentWebsiteWarningEnabled {
configuration . preferences . isFraudulentWebsiteWarningEnabled = newOptions . isFraudulentWebsiteWarningEnabled
}
if newOptionsMap [ " preferredContentMode " ] != nil && options ? . preferredContentMode != newOptions . preferredContentMode {
configuration . defaultWebpagePreferences . preferredContentMode = WKWebpagePreferences . ContentMode ( rawValue : newOptions . preferredContentMode ) !
}
if newOptionsMap [ " automaticallyAdjustsScrollIndicatorInsets " ] != nil && options ? . automaticallyAdjustsScrollIndicatorInsets != newOptions . automaticallyAdjustsScrollIndicatorInsets {
scrollView . automaticallyAdjustsScrollIndicatorInsets = newOptions . automaticallyAdjustsScrollIndicatorInsets
}
2019-11-02 03:16:47 +00:00
}
2020-05-09 02:36:07 +00:00
if newOptionsMap [ " disableVerticalScroll " ] != nil && options ? . disableVerticalScroll != newOptions . disableVerticalScroll {
scrollView . showsVerticalScrollIndicator = ! newOptions . disableVerticalScroll
}
if newOptionsMap [ " disableHorizontalScroll " ] != nil && options ? . disableHorizontalScroll != newOptions . disableHorizontalScroll {
scrollView . showsHorizontalScrollIndicator = ! newOptions . disableHorizontalScroll
}
2019-11-02 03:16:47 +00:00
if newOptionsMap [ " verticalScrollBarEnabled " ] != nil && options ? . verticalScrollBarEnabled != newOptions . verticalScrollBarEnabled {
scrollView . showsVerticalScrollIndicator = newOptions . verticalScrollBarEnabled
}
if newOptionsMap [ " horizontalScrollBarEnabled " ] != nil && options ? . horizontalScrollBarEnabled != newOptions . horizontalScrollBarEnabled {
scrollView . showsHorizontalScrollIndicator = newOptions . horizontalScrollBarEnabled
}
2021-02-03 14:17:15 +00:00
if newOptionsMap [ " isDirectionalLockEnabled " ] != nil && options ? . isDirectionalLockEnabled != newOptions . isDirectionalLockEnabled {
scrollView . isDirectionalLockEnabled = newOptions . isDirectionalLockEnabled
}
2020-05-09 02:36:07 +00:00
if newOptionsMap [ " decelerationRate " ] != nil && options ? . decelerationRate != newOptions . decelerationRate {
2020-05-29 17:56:03 +00:00
scrollView . decelerationRate = InAppWebView . getDecelerationRate ( type : newOptions . decelerationRate )
2019-11-18 21:21:35 +00:00
}
2020-05-09 02:36:07 +00:00
if newOptionsMap [ " alwaysBounceVertical " ] != nil && options ? . alwaysBounceVertical != newOptions . alwaysBounceVertical {
scrollView . alwaysBounceVertical = newOptions . alwaysBounceVertical
}
if newOptionsMap [ " alwaysBounceHorizontal " ] != nil && options ? . alwaysBounceHorizontal != newOptions . alwaysBounceHorizontal {
scrollView . alwaysBounceHorizontal = newOptions . alwaysBounceHorizontal
}
if newOptionsMap [ " scrollsToTop " ] != nil && options ? . scrollsToTop != newOptions . scrollsToTop {
scrollView . scrollsToTop = newOptions . scrollsToTop
}
if newOptionsMap [ " isPagingEnabled " ] != nil && options ? . isPagingEnabled != newOptions . isPagingEnabled {
scrollView . scrollsToTop = newOptions . isPagingEnabled
}
if newOptionsMap [ " maximumZoomScale " ] != nil && options ? . maximumZoomScale != newOptions . maximumZoomScale {
scrollView . maximumZoomScale = CGFloat ( newOptions . maximumZoomScale )
}
if newOptionsMap [ " minimumZoomScale " ] != nil && options ? . minimumZoomScale != newOptions . minimumZoomScale {
scrollView . minimumZoomScale = CGFloat ( newOptions . minimumZoomScale )
2019-11-18 21:21:35 +00:00
}
2019-11-02 03:16:47 +00:00
if #available ( iOS 9.0 , * ) {
if newOptionsMap [ " allowsLinkPreview " ] != nil && options ? . allowsLinkPreview != newOptions . allowsLinkPreview {
allowsLinkPreview = newOptions . allowsLinkPreview
}
2020-05-29 17:56:03 +00:00
if newOptionsMap [ " allowsAirPlayForMediaPlayback " ] != nil && options ? . allowsAirPlayForMediaPlayback != newOptions . allowsAirPlayForMediaPlayback {
configuration . allowsAirPlayForMediaPlayback = newOptions . allowsAirPlayForMediaPlayback
}
2019-11-02 03:16:47 +00:00
if newOptionsMap [ " allowsPictureInPictureMediaPlayback " ] != nil && options ? . allowsPictureInPictureMediaPlayback != newOptions . allowsPictureInPictureMediaPlayback {
configuration . allowsPictureInPictureMediaPlayback = newOptions . allowsPictureInPictureMediaPlayback
}
if newOptionsMap [ " applicationNameForUserAgent " ] != nil && options ? . applicationNameForUserAgent != newOptions . applicationNameForUserAgent && newOptions . applicationNameForUserAgent != " " {
configuration . applicationNameForUserAgent = newOptions . applicationNameForUserAgent
}
if newOptionsMap [ " userAgent " ] != nil && options ? . userAgent != newOptions . userAgent && newOptions . userAgent != " " {
customUserAgent = newOptions . userAgent
}
}
2021-01-28 17:56:04 +00:00
if newOptionsMap [ " allowUniversalAccessFromFileURLs " ] != nil && options ? . allowUniversalAccessFromFileURLs != newOptions . allowUniversalAccessFromFileURLs {
configuration . setValue ( newOptions . allowUniversalAccessFromFileURLs , forKey : " allowUniversalAccessFromFileURLs " )
}
if newOptionsMap [ " allowFileAccessFromFileURLs " ] != nil && options ? . allowFileAccessFromFileURLs != newOptions . allowFileAccessFromFileURLs {
configuration . preferences . setValue ( newOptions . allowFileAccessFromFileURLs , forKey : " allowFileAccessFromFileURLs " )
}
2019-11-02 03:16:47 +00:00
if newOptionsMap [ " clearCache " ] != nil && newOptions . clearCache {
clearCache ( )
}
2021-02-04 00:43:55 +00:00
if newOptionsMap [ " javaScriptEnabled " ] != nil && options ? . javaScriptEnabled != newOptions . javaScriptEnabled {
configuration . preferences . javaScriptEnabled = newOptions . javaScriptEnabled
}
if #available ( iOS 14.0 , * ) {
if options ? . mediaType != newOptions . mediaType {
mediaType = newOptions . mediaType
}
if newOptionsMap [ " pageZoom " ] != nil && options ? . pageZoom != newOptions . pageZoom {
pageZoom = CGFloat ( newOptions . pageZoom )
}
if newOptionsMap [ " limitsNavigationsToAppBoundDomains " ] != nil && options ? . limitsNavigationsToAppBoundDomains != newOptions . limitsNavigationsToAppBoundDomains {
configuration . limitsNavigationsToAppBoundDomains = newOptions . limitsNavigationsToAppBoundDomains
}
if newOptionsMap [ " javaScriptEnabled " ] != nil && options ? . javaScriptEnabled != newOptions . javaScriptEnabled {
configuration . defaultWebpagePreferences . allowsContentJavaScript = newOptions . javaScriptEnabled
}
}
2019-11-02 03:16:47 +00:00
if #available ( iOS 11.0 , * ) , newOptionsMap [ " contentBlockers " ] != nil {
configuration . userContentController . removeAllContentRuleLists ( )
let contentBlockers = newOptions . contentBlockers
if contentBlockers . count > 0 {
do {
let jsonData = try JSONSerialization . data ( withJSONObject : contentBlockers , options : [ ] )
let blockRules = String ( data : jsonData , encoding : String . Encoding . utf8 )
WKContentRuleListStore . default ( ) . compileContentRuleList (
forIdentifier : " ContentBlockingRules " ,
encodedContentRuleList : blockRules ) { ( contentRuleList , error ) in
if let error = error {
print ( error . localizedDescription )
return
}
self . configuration . userContentController . add ( contentRuleList ! )
}
} catch {
print ( error . localizedDescription )
}
}
}
2021-02-04 00:43:55 +00:00
scrollView . isScrollEnabled = ! ( newOptions . disableVerticalScroll && newOptions . disableHorizontalScroll )
2021-02-03 14:17:15 +00:00
2021-02-04 00:43:55 +00:00
self . options = newOptions
2019-11-02 03:16:47 +00:00
}
2019-12-18 20:34:40 +00:00
func getOptions ( ) -> [ String : Any ? ] ? {
2019-11-02 03:16:47 +00:00
if ( self . options = = nil ) {
return nil
}
2020-05-29 17:56:03 +00:00
return self . options ! . getRealOptions ( obj : self )
2019-11-02 03:16:47 +00:00
}
public func clearCache ( ) {
if #available ( iOS 9.0 , * ) {
// l e t w e b s i t e D a t a T y p e s = N S S e t ( a r r a y : [ W K W e b s i t e D a t a T y p e D i s k C a c h e , W K W e b s i t e D a t a T y p e M e m o r y C a c h e ] )
let date = NSDate ( timeIntervalSince1970 : 0 )
WKWebsiteDataStore . default ( ) . removeData ( ofTypes : WKWebsiteDataStore . allWebsiteDataTypes ( ) , modifiedSince : date as Date , completionHandler : { } )
} else {
var libraryPath = NSSearchPathForDirectoriesInDomains ( FileManager . SearchPathDirectory . libraryDirectory , FileManager . SearchPathDomainMask . userDomainMask , false ) . first !
libraryPath += " /Cookies "
do {
try FileManager . default . removeItem ( atPath : libraryPath )
} catch {
print ( " can't clear cache " )
}
URLCache . shared . removeAllCachedResponses ( )
}
}
2021-02-06 01:30:15 +00:00
public func injectDeferredObject ( source : String , contentWorldName : String ? , withWrapper jsWrapper : String ? , result : FlutterResult ? ) {
2019-11-17 17:31:31 +00:00
var jsToInject = source
if let wrapper = jsWrapper {
let jsonData : Data ? = try ? JSONSerialization . data ( withJSONObject : [ source ] , options : [ ] )
let sourceArrayString = String ( data : jsonData ! , encoding : String . Encoding . utf8 )
2019-11-02 03:16:47 +00:00
let sourceString : String ? = ( sourceArrayString ! as NSString ) . substring ( with : NSRange ( location : 1 , length : ( sourceArrayString ? . count ? ? 0 ) - 2 ) )
2019-11-17 17:31:31 +00:00
jsToInject = String ( format : wrapper , sourceString ! )
2019-11-02 03:16:47 +00:00
}
2021-02-06 01:30:15 +00:00
if #available ( iOS 14.0 , * ) , let contentWorldName = contentWorldName {
if ! userScriptsContentWorlds . contains ( contentWorldName ) {
userScriptsContentWorlds . append ( contentWorldName )
addSharedPluginUserScriptsInContentWorld ( contentWorldName : contentWorldName )
// A d d o n l y t h e f i r s t t i m e a l l t h e p l u g i n u s e r s c r i p t s n e e d e d .
// I n t h e n e x t p a g e l o a d , i t w i l l u s e t h e W K U s e r S c r i p t s l o a d e d
jsToInject = getAllPluginUserScriptMergedJS ( ) + " \n " + jsToInject
}
let contentWorld = getContentWorld ( name : contentWorldName )
evaluateJavaScript ( jsToInject , in : nil , in : contentWorld ) { ( evalResult ) in
guard let result = result else {
return
}
switch ( evalResult ) {
case . success ( let value ) :
result ( value )
return
case . failure ( let error ) :
let userInfo = ( error as NSError ) . userInfo
self . onConsoleMessage ( message : userInfo [ " WKJavaScriptExceptionMessage " ] as ? String ? ? " " , messageLevel : 3 )
break
}
result ( nil )
2019-11-17 17:31:31 +00:00
}
2021-02-06 01:30:15 +00:00
} else {
evaluateJavaScript ( jsToInject ) { ( value , error ) in
guard let result = result else {
return
}
if error != nil {
let userInfo = ( error ! as NSError ) . userInfo
self . onConsoleMessage ( message : userInfo [ " WKJavaScriptExceptionMessage " ] as ? String ? ? " " , messageLevel : 3 )
}
if value = = nil {
result ( nil )
return
}
result ( value )
2019-11-17 17:31:31 +00:00
}
2021-02-06 01:30:15 +00:00
}
2019-11-02 03:16:47 +00:00
}
2021-02-07 15:05:39 +00:00
public func evaluateJavascript ( source : String , contentWorldName : String ? , result : @ escaping FlutterResult ) {
2021-02-06 01:30:15 +00:00
injectDeferredObject ( source : source , contentWorldName : contentWorldName , withWrapper : nil , result : result )
2019-11-02 03:16:47 +00:00
}
2021-02-07 15:05:39 +00:00
@ available ( iOS 10.3 , * )
public func callAsyncJavaScript ( functionBody : String , arguments : [ String : Any ] , contentWorldName : String ? , result : @ escaping FlutterResult ) {
var jsToInject = functionBody
if #available ( iOS 14.0 , * ) {
var contentWorld = WKContentWorld . page
if let contentWorldName = contentWorldName {
contentWorld = getContentWorld ( name : contentWorldName )
if ! userScriptsContentWorlds . contains ( contentWorldName ) {
userScriptsContentWorlds . append ( contentWorldName )
addSharedPluginUserScriptsInContentWorld ( contentWorldName : contentWorldName )
// A d d o n l y t h e f i r s t t i m e a l l t h e p l u g i n u s e r s c r i p t s n e e d e d .
// I n t h e n e x t p a g e l o a d , i t w i l l u s e t h e W K U s e r S c r i p t s l o a d e d
jsToInject = getAllPluginUserScriptMergedJS ( ) + " \n " + jsToInject
}
}
callAsyncJavaScript ( jsToInject , arguments : arguments , in : nil , in : contentWorld ) { ( evalResult ) in
var body : [ String : Any ? ] = [
" value " : nil ,
" error " : nil
]
switch ( evalResult ) {
case . success ( let value ) :
body [ " value " ] = value
break
case . failure ( let error ) :
body [ " error " ] = error
break
}
result ( body )
}
} else {
let resultUuid = NSUUID ( ) . uuidString
callAsyncJavaScriptBelowIOS14Results [ resultUuid ] = result
var functionArgumentNamesList : [ String ] = [ ]
var functionArgumentValuesList : [ String ] = [ ]
let keys = arguments . keys
keys . forEach { ( key ) in
functionArgumentNamesList . append ( key )
functionArgumentValuesList . append ( " obj. \( key ) " )
}
let functionArgumentNames = functionArgumentNamesList . joined ( separator : " , " )
let functionArgumentValues = functionArgumentValuesList . joined ( separator : " , " )
jsToInject = callAsyncJavaScriptBelowIOS14WrapperJS
. replacingOccurrences ( of : " $FUNCTION_ARGUMENT_NAMES " , with : functionArgumentNames )
. replacingOccurrences ( of : " $FUNCTION_ARGUMENT_VALUES " , with : functionArgumentValues )
. replacingOccurrences ( of : " $FUNCTION_ARGUMENTS_OBJ " , with : JSONStringify ( value : arguments ) )
. replacingOccurrences ( of : " $FUNCTION_BODY " , with : jsToInject )
. replacingOccurrences ( of : " $RESULT_UUID " , with : resultUuid )
evaluateJavaScript ( jsToInject ) { ( value , error ) in
if error != nil {
let userInfo = ( error ! as NSError ) . userInfo
self . onConsoleMessage ( message :
userInfo [ " WKJavaScriptExceptionMessage " ] as ? String ? ?
userInfo [ " NSLocalizedDescription " ] as ? String ? ?
" " ,
messageLevel : 3 )
result ( nil )
self . callAsyncJavaScriptBelowIOS14Results . removeValue ( forKey : resultUuid )
}
}
}
}
2021-02-09 20:11:50 +00:00
public func injectJavascriptFileFromUrl ( urlFile : String , scriptHtmlTagAttributes : [ String : Any ? ] ? ) {
var scriptAttributes = " "
if let scriptHtmlTagAttributes = scriptHtmlTagAttributes {
if let typeAttr = scriptHtmlTagAttributes [ " type " ] as ? String {
scriptAttributes += " script.type = ' \( typeAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let idAttr = scriptHtmlTagAttributes [ " id " ] as ? String {
scriptAttributes += " script.id = ' \( idAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let asyncAttr = scriptHtmlTagAttributes [ " async " ] as ? Bool , asyncAttr {
scriptAttributes += " script.async = true; "
}
if let deferAttr = scriptHtmlTagAttributes [ " defer " ] as ? Bool , deferAttr {
scriptAttributes += " script.defer = true; "
}
if let crossOriginAttr = scriptHtmlTagAttributes [ " crossOrigin " ] as ? String {
scriptAttributes += " script.crossOrigin = ' \( crossOriginAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let integrityAttr = scriptHtmlTagAttributes [ " integrity " ] as ? String {
scriptAttributes += " script.integrity = ' \( integrityAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let noModuleAttr = scriptHtmlTagAttributes [ " noModule " ] as ? Bool , noModuleAttr {
scriptAttributes += " script.noModule = true; "
}
if let nonceAttr = scriptHtmlTagAttributes [ " nonce " ] as ? String {
scriptAttributes += " script.nonce = ' \( nonceAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let referrerPolicyAttr = scriptHtmlTagAttributes [ " referrerPolicy " ] as ? String {
scriptAttributes += " script.referrerPolicy = ' \( referrerPolicyAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
}
let jsWrapper = " (function(d) { var script = d.createElement('script'); \( scriptAttributes ) script.src = %@; d.body.appendChild(script); })(document); "
2021-02-06 01:30:15 +00:00
injectDeferredObject ( source : urlFile , contentWorldName : nil , withWrapper : jsWrapper , result : nil )
2019-11-02 03:16:47 +00:00
}
2019-11-05 02:44:22 +00:00
public func injectCSSCode ( source : String ) {
2021-02-09 20:11:50 +00:00
let jsWrapper = " (function(d) { var style = d.createElement('style'); style.innerHTML = %@; d.body.appendChild(style); })(document); "
2021-02-06 01:30:15 +00:00
injectDeferredObject ( source : source , contentWorldName : nil , withWrapper : jsWrapper , result : nil )
2019-11-02 03:16:47 +00:00
}
2021-02-09 20:11:50 +00:00
public func injectCSSFileFromUrl ( urlFile : String , cssLinkHtmlTagAttributes : [ String : Any ? ] ? ) {
var cssLinkAttributes = " "
var alternateStylesheet = " "
if let cssLinkHtmlTagAttributes = cssLinkHtmlTagAttributes {
if let idAttr = cssLinkHtmlTagAttributes [ " id " ] as ? String {
cssLinkAttributes += " link.id = ' \( idAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let mediaAttr = cssLinkHtmlTagAttributes [ " media " ] as ? String {
cssLinkAttributes += " link.media = ' \( mediaAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let crossOriginAttr = cssLinkHtmlTagAttributes [ " crossOrigin " ] as ? String {
cssLinkAttributes += " link.crossOrigin = ' \( crossOriginAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let integrityAttr = cssLinkHtmlTagAttributes [ " integrity " ] as ? String {
cssLinkAttributes += " link.integrity = ' \( integrityAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let referrerPolicyAttr = cssLinkHtmlTagAttributes [ " referrerPolicy " ] as ? String {
cssLinkAttributes += " link.referrerPolicy = ' \( referrerPolicyAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
if let disabledAttr = cssLinkHtmlTagAttributes [ " disabled " ] as ? Bool , disabledAttr {
cssLinkAttributes += " link.disabled = true; "
}
if let alternateAttr = cssLinkHtmlTagAttributes [ " alternate " ] as ? Bool , alternateAttr {
alternateStylesheet = " alternate "
}
if let titleAttr = cssLinkHtmlTagAttributes [ " title " ] as ? String {
cssLinkAttributes += " link.title = ' \( titleAttr . replacingOccurrences ( of : " \' " , with : " \\ ' " ) ) '; "
}
}
let jsWrapper = " (function(d) { var link = d.createElement('link'); link.rel=' \( alternateStylesheet ) stylesheet', link.type='text/css'; \( cssLinkAttributes ) link.href = %@; d.body.appendChild(link); })(document); "
2021-02-06 01:30:15 +00:00
injectDeferredObject ( source : urlFile , contentWorldName : nil , withWrapper : jsWrapper , result : nil )
2019-11-02 03:16:47 +00:00
}
public func getCopyBackForwardList ( ) -> [ String : Any ] {
let currentList = backForwardList
let currentIndex = currentList . backList . count
var completeList = currentList . backList
if currentList . currentItem != nil {
completeList . append ( currentList . currentItem ! )
}
completeList . append ( contentsOf : currentList . forwardList )
var history : [ [ String : String ] ] = [ ]
for historyItem in completeList {
var historyItemMap : [ String : String ] = [ : ]
historyItemMap [ " originalUrl " ] = historyItem . initialURL . absoluteString
historyItemMap [ " title " ] = historyItem . title
historyItemMap [ " url " ] = historyItem . url . absoluteString
history . append ( historyItemMap )
}
var result : [ String : Any ] = [ : ]
result [ " history " ] = history
result [ " currentIndex " ] = currentIndex
return result ;
}
2021-02-09 23:15:10 +00:00
@ available ( iOS 13.0 , * )
public func webView ( _ webView : WKWebView ,
decidePolicyFor navigationAction : WKNavigationAction ,
preferences : WKWebpagePreferences ,
decisionHandler : @ escaping ( WKNavigationActionPolicy , WKWebpagePreferences ) -> Void ) {
self . webView ( webView , decidePolicyFor : navigationAction , decisionHandler : { ( navigationActionPolicy ) -> Void in
decisionHandler ( navigationActionPolicy , preferences )
} )
}
2019-11-02 03:16:47 +00:00
public func webView ( _ webView : WKWebView ,
decidePolicyFor navigationAction : WKNavigationAction ,
decisionHandler : @ escaping ( WKNavigationActionPolicy ) -> Void ) {
2021-02-09 23:15:10 +00:00
if navigationAction . request . url != nil {
2019-11-02 03:16:47 +00:00
2021-02-09 23:15:10 +00:00
if activateShouldOverrideUrlLoading , let useShouldOverrideUrlLoading = options ? . useShouldOverrideUrlLoading , useShouldOverrideUrlLoading {
shouldOverrideUrlLoading ( navigationAction : navigationAction , result : { ( result ) -> Void in
2019-12-09 23:29:09 +00:00
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2020-05-09 02:36:07 +00:00
decisionHandler ( . allow )
return
2019-11-02 03:16:47 +00:00
}
2019-12-09 23:29:09 +00:00
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
self . updateUrlTextFieldForIABController ( navigationAction : navigationAction )
decisionHandler ( . allow )
2020-05-09 02:36:07 +00:00
return
2019-12-09 23:29:09 +00:00
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 1 :
self . updateUrlTextFieldForIABController ( navigationAction : navigationAction )
decisionHandler ( . allow )
break
default :
decisionHandler ( . cancel )
}
return ;
}
self . updateUrlTextFieldForIABController ( navigationAction : navigationAction )
decisionHandler ( . allow )
}
} )
2019-11-02 03:16:47 +00:00
return
2019-12-09 23:29:09 +00:00
2019-11-02 03:16:47 +00:00
}
2019-12-09 23:29:09 +00:00
updateUrlTextFieldForIABController ( navigationAction : navigationAction )
}
if ! activateShouldOverrideUrlLoading {
activateShouldOverrideUrlLoading = true
2019-11-02 03:16:47 +00:00
}
decisionHandler ( . allow )
}
2019-12-09 23:29:09 +00:00
public func updateUrlTextFieldForIABController ( navigationAction : WKNavigationAction ) {
if navigationAction . navigationType = = . linkActivated || navigationAction . navigationType = = . backForward {
currentURL = url
if IABController != nil {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
IABController ! . updateUrlTextField ( url : currentURL ? . absoluteString ? ? " " )
2019-12-09 23:29:09 +00:00
}
}
}
2019-11-02 03:16:47 +00:00
public func webView ( _ webView : WKWebView ,
decidePolicyFor navigationResponse : WKNavigationResponse ,
decisionHandler : @ escaping ( WKNavigationResponsePolicy ) -> Void ) {
2019-11-21 01:19:43 +00:00
if navigationResponse . isForMainFrame , let response = navigationResponse . response as ? HTTPURLResponse {
if response . statusCode >= 400 {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onLoadHttpError ( url : response . url ? . absoluteString , statusCode : response . statusCode , description : " " )
2019-11-21 01:19:43 +00:00
}
}
2019-11-02 03:16:47 +00:00
2021-02-09 23:15:10 +00:00
let useOnNavigationResponse = options ? . useOnNavigationResponse
if useOnNavigationResponse != nil , useOnNavigationResponse ! {
onNavigationResponse ( navigationResponse : navigationResponse , result : { ( result ) -> Void in
if result is FlutterError {
print ( ( result as ! FlutterError ) . message ? ? " " )
decisionHandler ( . allow )
return
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
decisionHandler ( . allow )
return
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 1 :
decisionHandler ( . allow )
break
default :
decisionHandler ( . cancel )
}
return ;
}
decisionHandler ( . allow )
}
} )
}
if let useOnDownloadStart = options ? . useOnDownloadStart , useOnDownloadStart {
2019-11-02 03:16:47 +00:00
let mimeType = navigationResponse . response . mimeType
2020-06-30 08:58:59 +00:00
if let url = navigationResponse . response . url , navigationResponse . isForMainFrame {
2019-11-02 03:16:47 +00:00
if mimeType != nil && ! mimeType ! . starts ( with : " text/ " ) {
onDownloadStart ( url : url . absoluteString )
2021-02-09 23:15:10 +00:00
if useOnNavigationResponse = = nil || ! useOnNavigationResponse ! {
decisionHandler ( . cancel )
}
2019-11-02 03:16:47 +00:00
return
}
}
}
2021-02-09 23:15:10 +00:00
if useOnNavigationResponse = = nil || ! useOnNavigationResponse ! {
decisionHandler ( . allow )
}
2019-11-02 03:16:47 +00:00
}
public func webView ( _ webView : WKWebView , didStartProvisionalNavigation navigation : WKNavigation ! ) {
2020-06-15 00:08:23 +00:00
self . x509CertificateData = nil
2019-11-02 03:16:47 +00:00
self . startPageTime = currentTimeInMilliSeconds ( )
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onLoadStart ( url : url ? . absoluteString )
2019-11-02 03:16:47 +00:00
if IABController != nil {
// l o a d i n g u r l , s t a r t s p i n n e r , u p d a t e b a c k / f o r w a r d
IABController ! . backButton . isEnabled = canGoBack
IABController ! . forwardButton . isEnabled = canGoForward
if ( IABController ! . browserOptions ? . spinner ) ! {
IABController ! . spinner . startAnimating ( )
}
}
}
public func webView ( _ webView : WKWebView , didFinish navigation : WKNavigation ! ) {
currentURL = url
InAppWebView . credentialsProposed = [ ]
2019-11-16 11:41:30 +00:00
evaluateJavaScript ( platformReadyJS , completionHandler : nil )
2021-02-06 01:30:15 +00:00
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onLoadStop ( url : url ? . absoluteString )
2021-02-01 14:55:27 +00:00
2019-11-02 03:16:47 +00:00
if IABController != nil {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
IABController ! . updateUrlTextField ( url : currentURL ? . absoluteString ? ? " " )
2019-11-02 03:16:47 +00:00
IABController ! . backButton . isEnabled = canGoBack
IABController ! . forwardButton . isEnabled = canGoForward
IABController ! . spinner . stopAnimating ( )
}
}
public func webView ( _ view : WKWebView ,
didFailProvisionalNavigation navigation : WKNavigation ! ,
withError error : Error ) {
webView ( view , didFail : navigation , withError : error )
}
public func webView ( _ webView : WKWebView , didFail navigation : WKNavigation ! , withError error : Error ) {
InAppWebView . credentialsProposed = [ ]
2020-06-30 08:58:59 +00:00
var urlError = url ? . absoluteString
if let info = error . _userInfo as ? [ String : Any ] {
if let failingUrl = info [ NSURLErrorFailingURLErrorKey ] as ? URL {
urlError = failingUrl . absoluteString
}
if let failingUrlString = info [ NSURLErrorFailingURLStringErrorKey ] as ? String {
urlError = failingUrlString
}
}
onLoadError ( url : urlError , error : error )
2019-11-02 03:16:47 +00:00
if IABController != nil {
IABController ! . backButton . isEnabled = canGoBack
IABController ! . forwardButton . isEnabled = canGoForward
IABController ! . spinner . stopAnimating ( )
}
}
public func webView ( _ webView : WKWebView , didReceive challenge : URLAuthenticationChallenge , completionHandler : @ escaping ( URLSession . AuthChallengeDisposition , URLCredential ? ) -> Void ) {
if challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodHTTPBasic ||
challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodDefault ||
2021-01-06 11:48:26 +00:00
challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodHTTPDigest ||
challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodNegotiate ||
challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodNTLM {
2019-11-02 03:16:47 +00:00
let host = challenge . protectionSpace . host
let prot = challenge . protectionSpace . protocol
let realm = challenge . protectionSpace . realm
let port = challenge . protectionSpace . port
onReceivedHttpAuthRequest ( challenge : challenge , result : { ( result ) -> Void in
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
completionHandler ( . performDefaultHandling , nil )
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 0 :
InAppWebView . credentialsProposed = [ ]
2019-11-21 01:19:43 +00:00
// u s e d . p e r f o r m D e f a u l t H a n d l i n g t o m a n t a i n c o n s i s t e n c y w i t h A n d r o i d
// b e c a u s e . c a n c e l A u t h e n t i c a t i o n C h a l l e n g e w i l l c a l l w e b V i e w ( _ : d i d F a i l : w i t h E r r o r : )
completionHandler ( . performDefaultHandling , nil )
// c o m p l e t i o n H a n d l e r ( . c a n c e l A u t h e n t i c a t i o n C h a l l e n g e , n i l )
2019-11-02 03:16:47 +00:00
break
case 1 :
let username = response [ " username " ] as ! String
let password = response [ " password " ] as ! String
let permanentPersistence = response [ " permanentPersistence " ] as ? Bool ? ? false
let persistence = ( permanentPersistence ) ? URLCredential . Persistence . permanent : URLCredential . Persistence . forSession
let credential = URLCredential ( user : username , password : password , persistence : persistence )
completionHandler ( . useCredential , credential )
break
case 2 :
if InAppWebView . credentialsProposed . count = = 0 {
for ( protectionSpace , credentials ) in CredentialDatabase . credentialStore ! . allCredentials {
if protectionSpace . host = = host && protectionSpace . realm = = realm &&
protectionSpace . protocol = = prot && protectionSpace . port = = port {
for credential in credentials {
InAppWebView . credentialsProposed . append ( credential . value )
}
break
}
}
}
if InAppWebView . credentialsProposed . count = = 0 , let credential = challenge . proposedCredential {
InAppWebView . credentialsProposed . append ( credential )
}
if let credential = InAppWebView . credentialsProposed . popLast ( ) {
completionHandler ( . useCredential , credential )
}
else {
completionHandler ( . performDefaultHandling , nil )
}
break
default :
InAppWebView . credentialsProposed = [ ]
completionHandler ( . performDefaultHandling , nil )
}
return ;
}
completionHandler ( . performDefaultHandling , nil )
}
} )
}
else if challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodServerTrust {
guard let serverTrust = challenge . protectionSpace . serverTrust else {
completionHandler ( . performDefaultHandling , nil )
return
}
onReceivedServerTrustAuthRequest ( challenge : challenge , result : { ( result ) -> Void in
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
completionHandler ( . performDefaultHandling , nil )
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 0 :
InAppWebView . credentialsProposed = [ ]
completionHandler ( . cancelAuthenticationChallenge , nil )
break
case 1 :
let exceptions = SecTrustCopyExceptions ( serverTrust )
SecTrustSetExceptions ( serverTrust , exceptions )
let credential = URLCredential ( trust : serverTrust )
completionHandler ( . useCredential , credential )
break
default :
InAppWebView . credentialsProposed = [ ]
completionHandler ( . performDefaultHandling , nil )
}
return ;
}
completionHandler ( . performDefaultHandling , nil )
}
} )
}
else if challenge . protectionSpace . authenticationMethod = = NSURLAuthenticationMethodClientCertificate {
onReceivedClientCertRequest ( challenge : challenge , result : { ( result ) -> Void in
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
completionHandler ( . performDefaultHandling , nil )
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 0 :
completionHandler ( . cancelAuthenticationChallenge , nil )
break
case 1 :
let certificatePath = response [ " certificatePath " ] as ! String ;
let certificatePassword = response [ " certificatePassword " ] as ? String ? ? " " ;
let key = SwiftFlutterPlugin . instance ! . registrar ! . lookupKey ( forAsset : certificatePath )
let path = Bundle . main . path ( forResource : key , ofType : nil ) !
let PKCS12Data = NSData ( contentsOfFile : path ) !
if let identityAndTrust : IdentityAndTrust = self . extractIdentity ( PKCS12Data : PKCS12Data , password : certificatePassword ) {
let urlCredential : URLCredential = URLCredential (
identity : identityAndTrust . identityRef ,
certificates : identityAndTrust . certArray as ? [ AnyObject ] ,
persistence : URLCredential . Persistence . forSession ) ;
completionHandler ( . useCredential , urlCredential )
} else {
completionHandler ( . performDefaultHandling , nil )
}
break
case 2 :
completionHandler ( . cancelAuthenticationChallenge , nil )
break
default :
completionHandler ( . performDefaultHandling , nil )
}
return ;
}
completionHandler ( . performDefaultHandling , nil )
}
} )
}
else {
completionHandler ( . performDefaultHandling , nil )
}
}
struct IdentityAndTrust {
var identityRef : SecIdentity
var trust : SecTrust
var certArray : AnyObject
}
func extractIdentity ( PKCS12Data : NSData , password : String ) -> IdentityAndTrust ? {
var identityAndTrust : IdentityAndTrust ?
var securityError : OSStatus = errSecSuccess
var importResult : CFArray ? = nil
securityError = SecPKCS12Import (
PKCS12Data as NSData ,
[ kSecImportExportPassphrase as String : password ] as NSDictionary ,
& importResult
)
if securityError = = errSecSuccess {
let certItems : CFArray = importResult ! as CFArray ;
let certItemsArray : Array = certItems as Array
let dict : AnyObject ? = certItemsArray . first ;
if let certEntry : Dictionary = dict as ? Dictionary < String , AnyObject > {
// g r a b t h e i d e n t i t y
let identityPointer : AnyObject ? = certEntry [ " identity " ] ;
let secIdentityRef : SecIdentity = ( identityPointer as ! SecIdentity ? ) ! ;
// g r a b t h e t r u s t
let trustPointer : AnyObject ? = certEntry [ " trust " ] ;
let trustRef : SecTrust = trustPointer as ! SecTrust ;
// g r a b t h e c e r t
let chainPointer : AnyObject ? = certEntry [ " chain " ] ;
identityAndTrust = IdentityAndTrust ( identityRef : secIdentityRef , trust : trustRef , certArray : chainPointer ! ) ;
}
} else {
print ( " Security Error: " + securityError . description )
if #available ( iOS 11.3 , * ) {
2019-12-18 20:34:40 +00:00
print ( SecCopyErrorMessageString ( securityError , nil ) ? ? " " )
2019-11-02 03:16:47 +00:00
}
}
return identityAndTrust ;
}
func createAlertDialog ( message : String ? , responseMessage : String ? , confirmButtonTitle : String ? , completionHandler : @ escaping ( ) -> Void ) {
let title = responseMessage != nil && ! responseMessage ! . isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && ! confirmButtonTitle ! . isEmpty ? confirmButtonTitle : NSLocalizedString ( " Ok " , comment : " " )
let alertController = UIAlertController ( title : title , message : nil ,
preferredStyle : UIAlertController . Style . alert ) ;
alertController . addAction ( UIAlertAction ( title : okButton , style : UIAlertAction . Style . default ) {
_ in completionHandler ( ) }
) ;
2021-02-01 14:55:27 +00:00
if let presentingViewController = ( ( self . IABController != nil ) ? self . IABController ! : self . window ? . rootViewController ! ) {
presentingViewController . present ( alertController , animated : true , completion : { } )
} else {
completionHandler ( )
}
2019-11-02 03:16:47 +00:00
}
public func webView ( _ webView : WKWebView , runJavaScriptAlertPanelWithMessage message : String ,
initiatedByFrame frame : WKFrameInfo , completionHandler : @ escaping ( ) -> Void ) {
2019-12-02 23:07:29 +00:00
if ( isPausedTimers ) {
isPausedTimersCompletionHandler = completionHandler
return
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onJsAlert ( frame : frame , message : message , result : { ( result ) -> Void in
2019-11-02 03:16:47 +00:00
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
self . createAlertDialog ( message : message , responseMessage : nil , confirmButtonTitle : nil , completionHandler : completionHandler )
}
else {
let response : [ String : Any ]
var responseMessage : String ? ;
var confirmButtonTitle : String ? ;
if let r = result {
response = r as ! [ String : Any ]
responseMessage = response [ " message " ] as ? String
confirmButtonTitle = response [ " confirmButtonTitle " ] as ? String
let handledByClient = response [ " handledByClient " ] as ? Bool
if handledByClient != nil , handledByClient ! {
var action = response [ " action " ] as ? Int
action = action != nil ? action : 1 ;
switch action {
case 0 :
completionHandler ( )
break
default :
completionHandler ( )
}
return ;
}
}
self . createAlertDialog ( message : message , responseMessage : responseMessage , confirmButtonTitle : confirmButtonTitle , completionHandler : completionHandler )
}
} )
}
func createConfirmDialog ( message : String ? , responseMessage : String ? , confirmButtonTitle : String ? , cancelButtonTitle : String ? , completionHandler : @ escaping ( Bool ) -> Void ) {
let dialogMessage = responseMessage != nil && ! responseMessage ! . isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && ! confirmButtonTitle ! . isEmpty ? confirmButtonTitle : NSLocalizedString ( " Ok " , comment : " " )
let cancelButton = cancelButtonTitle != nil && ! cancelButtonTitle ! . isEmpty ? cancelButtonTitle : NSLocalizedString ( " Cancel " , comment : " " )
let alertController = UIAlertController ( title : nil , message : dialogMessage , preferredStyle : . alert )
alertController . addAction ( UIAlertAction ( title : okButton , style : . default , handler : { ( action ) in
completionHandler ( true )
} ) )
alertController . addAction ( UIAlertAction ( title : cancelButton , style : . cancel , handler : { ( action ) in
completionHandler ( false )
} ) )
2021-02-01 14:55:27 +00:00
if let presentingViewController = ( ( self . IABController != nil ) ? self . IABController ! : self . window ? . rootViewController ! ) {
presentingViewController . present ( alertController , animated : true , completion : nil )
} else {
completionHandler ( false )
}
2019-11-02 03:16:47 +00:00
}
public func webView ( _ webView : WKWebView , runJavaScriptConfirmPanelWithMessage message : String , initiatedByFrame frame : WKFrameInfo ,
completionHandler : @ escaping ( Bool ) -> Void ) {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onJsConfirm ( frame : frame , message : message , result : { ( result ) -> Void in
2019-11-02 03:16:47 +00:00
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
self . createConfirmDialog ( message : message , responseMessage : nil , confirmButtonTitle : nil , cancelButtonTitle : nil , completionHandler : completionHandler )
}
else {
let response : [ String : Any ]
var responseMessage : String ? ;
var confirmButtonTitle : String ? ;
var cancelButtonTitle : String ? ;
if let r = result {
response = r as ! [ String : Any ]
responseMessage = response [ " message " ] as ? String
confirmButtonTitle = response [ " confirmButtonTitle " ] as ? String
cancelButtonTitle = response [ " cancelButtonTitle " ] as ? String
let handledByClient = response [ " handledByClient " ] as ? Bool
if handledByClient != nil , handledByClient ! {
var action = response [ " action " ] as ? Int
action = action != nil ? action : 1 ;
switch action {
case 0 :
completionHandler ( true )
break
case 1 :
completionHandler ( false )
break
default :
completionHandler ( false )
}
return ;
}
}
self . createConfirmDialog ( message : message , responseMessage : responseMessage , confirmButtonTitle : confirmButtonTitle , cancelButtonTitle : cancelButtonTitle , completionHandler : completionHandler )
}
} )
}
func createPromptDialog ( message : String , defaultValue : String ? , responseMessage : String ? , confirmButtonTitle : String ? , cancelButtonTitle : String ? , value : String ? , completionHandler : @ escaping ( String ? ) -> Void ) {
let dialogMessage = responseMessage != nil && ! responseMessage ! . isEmpty ? responseMessage : message
let okButton = confirmButtonTitle != nil && ! confirmButtonTitle ! . isEmpty ? confirmButtonTitle : NSLocalizedString ( " Ok " , comment : " " )
let cancelButton = cancelButtonTitle != nil && ! cancelButtonTitle ! . isEmpty ? cancelButtonTitle : NSLocalizedString ( " Cancel " , comment : " " )
let alertController = UIAlertController ( title : nil , message : dialogMessage , preferredStyle : . alert )
alertController . addTextField { ( textField ) in
textField . text = defaultValue
}
alertController . addAction ( UIAlertAction ( title : okButton , style : . default , handler : { ( action ) in
if let v = value {
completionHandler ( v )
}
else if let text = alertController . textFields ? . first ? . text {
completionHandler ( text )
} else {
completionHandler ( " " )
}
} ) )
alertController . addAction ( UIAlertAction ( title : cancelButton , style : . cancel , handler : { ( action ) in
completionHandler ( nil )
} ) )
2021-02-01 14:55:27 +00:00
if let presentingViewController = ( ( self . IABController != nil ) ? self . IABController ! : self . window ? . rootViewController ! ) {
presentingViewController . present ( alertController , animated : true , completion : nil )
} else {
completionHandler ( nil )
}
2019-11-02 03:16:47 +00:00
}
public func webView ( _ webView : WKWebView , runJavaScriptTextInputPanelWithPrompt message : String , defaultText defaultValue : String ? , initiatedByFrame frame : WKFrameInfo ,
completionHandler : @ escaping ( String ? ) -> Void ) {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
onJsPrompt ( frame : frame , message : message , defaultValue : defaultValue , result : { ( result ) -> Void in
2019-11-02 03:16:47 +00:00
if result is FlutterError {
2019-12-18 20:34:40 +00:00
print ( ( result as ! FlutterError ) . message ? ? " " )
2019-11-02 03:16:47 +00:00
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
self . createPromptDialog ( message : message , defaultValue : defaultValue , responseMessage : nil , confirmButtonTitle : nil , cancelButtonTitle : nil , value : nil , completionHandler : completionHandler )
}
else {
let response : [ String : Any ]
var responseMessage : String ? ;
var confirmButtonTitle : String ? ;
var cancelButtonTitle : String ? ;
var value : String ? ;
if let r = result {
response = r as ! [ String : Any ]
responseMessage = response [ " message " ] as ? String
confirmButtonTitle = response [ " confirmButtonTitle " ] as ? String
cancelButtonTitle = response [ " cancelButtonTitle " ] as ? String
let handledByClient = response [ " handledByClient " ] as ? Bool
value = response [ " value " ] as ? String ;
if handledByClient != nil , handledByClient ! {
var action = response [ " action " ] as ? Int
action = action != nil ? action : 1 ;
switch action {
case 0 :
completionHandler ( value )
break
case 1 :
completionHandler ( nil )
break
default :
completionHandler ( nil )
}
return ;
}
}
self . createPromptDialog ( message : message , defaultValue : defaultValue , responseMessage : responseMessage , confirmButtonTitle : confirmButtonTitle , cancelButtonTitle : cancelButtonTitle , value : value , completionHandler : completionHandler )
}
} )
}
public func scrollViewDidScroll ( _ scrollView : UIScrollView ) {
2019-11-18 21:21:35 +00:00
let disableVerticalScroll = options ? . disableVerticalScroll ? ? false
let disableHorizontalScroll = options ? . disableHorizontalScroll ? ? false
if disableVerticalScroll && disableHorizontalScroll {
scrollView . contentOffset = CGPoint ( x : lastScrollX , y : lastScrollY ) ;
}
else if disableVerticalScroll {
if ( scrollView . contentOffset . y >= 0 || scrollView . contentOffset . y < 0 ) {
scrollView . contentOffset = CGPoint ( x : scrollView . contentOffset . x , y : lastScrollY ) ;
}
}
else if disableHorizontalScroll {
if ( scrollView . contentOffset . x >= 0 || scrollView . contentOffset . x < 0 ) {
scrollView . contentOffset = CGPoint ( x : lastScrollX , y : scrollView . contentOffset . y ) ;
}
}
2019-11-19 19:44:06 +00:00
if navigationDelegate != nil && ! ( disableVerticalScroll && disableHorizontalScroll ) {
2019-11-02 03:16:47 +00:00
let x = Int ( scrollView . contentOffset . x / scrollView . contentScaleFactor )
let y = Int ( scrollView . contentOffset . y / scrollView . contentScaleFactor )
onScrollChanged ( x : x , y : y )
}
setNeedsLayout ( )
2019-11-18 21:21:35 +00:00
lastScrollX = scrollView . contentOffset . x
lastScrollY = scrollView . contentOffset . y
2019-11-02 03:16:47 +00:00
}
2019-12-09 23:29:09 +00:00
public func webView ( _ webView : WKWebView ,
createWebViewWith configuration : WKWebViewConfiguration ,
for navigationAction : WKNavigationAction ,
windowFeatures : WKWindowFeatures ) -> WKWebView ? {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
InAppWebView . windowAutoincrementId += 1
let windowId = InAppWebView . windowAutoincrementId
let windowWebView = InAppWebView ( frame : CGRect . zero , configuration : configuration , IABController : nil , contextMenu : nil , channel : nil )
windowWebView . windowId = windowId
let webViewTransport = WebViewTransport (
webView : windowWebView ,
request : navigationAction . request
)
InAppWebView . windowWebViews [ windowId ] = webViewTransport
windowWebView . stopLoading ( )
2021-02-09 23:15:10 +00:00
var iosAllowsConstrainedNetworkAccess : Bool ? = nil
var iosAllowsExpensiveNetworkAccess : Bool ? = nil
if #available ( iOS 13.0 , * ) {
iosAllowsConstrainedNetworkAccess = navigationAction . request . allowsConstrainedNetworkAccess
iosAllowsExpensiveNetworkAccess = navigationAction . request . allowsExpensiveNetworkAccess
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
let arguments : [ String : Any ? ] = [
" url " : navigationAction . request . url ? . absoluteString ,
" windowId " : windowId ,
" androidIsDialog " : nil ,
" androidIsUserGesture " : nil ,
" iosWKNavigationType " : navigationAction . navigationType . rawValue ,
2021-02-09 23:15:10 +00:00
" iosIsForMainFrame " : navigationAction . targetFrame ? . isMainFrame ? ? false ,
" iosAllowsCellularAccess " : navigationAction . request . allowsCellularAccess ,
" iosAllowsConstrainedNetworkAccess " : iosAllowsConstrainedNetworkAccess ,
" iosAllowsExpensiveNetworkAccess " : iosAllowsExpensiveNetworkAccess ,
" iosCachePolicy " : navigationAction . request . cachePolicy . rawValue ,
" iosHttpShouldHandleCookies " : navigationAction . request . httpShouldHandleCookies ,
" iosHttpShouldUsePipelining " : navigationAction . request . httpShouldUsePipelining ,
" iosNetworkServiceType " : navigationAction . request . networkServiceType . rawValue ,
" iosTimeoutInterval " : navigationAction . request . timeoutInterval ,
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
]
channel ? . invokeMethod ( " onCreateWindow " , arguments : arguments , result : { ( result ) -> Void in
if result is FlutterError {
print ( ( result as ! FlutterError ) . message ? ? " " )
if InAppWebView . windowWebViews [ windowId ] != nil {
InAppWebView . windowWebViews . removeValue ( forKey : windowId )
}
return
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
self . updateUrlTextFieldForIABController ( navigationAction : navigationAction )
if InAppWebView . windowWebViews [ windowId ] != nil {
InAppWebView . windowWebViews . removeValue ( forKey : windowId )
}
return
}
else {
2020-06-30 08:58:59 +00:00
var handledByClient = false
if result != nil , result is Bool {
handledByClient = result as ! Bool
}
if ! handledByClient , InAppWebView . windowWebViews [ windowId ] != nil {
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
InAppWebView . windowWebViews . removeValue ( forKey : windowId )
2021-01-28 16:10:15 +00:00
if let url = navigationAction . request . url {
self . loadUrl ( url : url , headers : nil )
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
}
}
} )
return windowWebView
}
2021-02-10 01:32:05 +00:00
public func webView ( _ webView : WKWebView ,
authenticationChallenge challenge : URLAuthenticationChallenge ,
shouldAllowDeprecatedTLS decisionHandler : @ escaping ( Bool ) -> Void ) {
shouldAllowDeprecatedTLS ( challenge : challenge , result : { ( result ) -> Void in
if result is FlutterError {
print ( ( result as ! FlutterError ) . message ? ? " " )
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented {
decisionHandler ( false )
}
else {
var response : [ String : Any ]
if let r = result {
response = r as ! [ String : Any ]
var action = response [ " action " ] as ? Int
action = action != nil ? action : 0 ;
switch action {
case 0 :
decisionHandler ( false )
break
case 1 :
decisionHandler ( true )
break
default :
decisionHandler ( false )
}
return ;
}
decisionHandler ( false )
}
} )
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func webViewDidClose ( _ webView : WKWebView ) {
let arguments : [ String : Any ? ] = [ : ]
channel ? . invokeMethod ( " onCloseWindow " , arguments : arguments )
2019-12-09 23:29:09 +00:00
}
2020-05-09 02:36:07 +00:00
public func webViewWebContentProcessDidTerminate ( _ webView : WKWebView ) {
onWebContentProcessDidTerminate ( )
}
public func webView ( _ webView : WKWebView ,
didCommit navigation : WKNavigation ! ) {
2020-05-28 23:03:45 +00:00
onPageCommitVisible ( url : url ? . absoluteString )
2020-05-09 02:36:07 +00:00
}
public func webView ( _ webView : WKWebView ,
didReceiveServerRedirectForProvisionalNavigation navigation : WKNavigation ! ) {
onDidReceiveServerRedirectForProvisionalNavigation ( )
}
// @ a v a i l a b l e ( i O S 1 3 . 0 , * )
// p u b l i c f u n c w e b V i e w ( _ w e b V i e w : W K W e b V i e w ,
// c o n t e x t M e n u C o n f i g u r a t i o n F o r E l e m e n t e l e m e n t I n f o : W K C o n t e x t M e n u E l e m e n t I n f o ,
// c o m p l e t i o n H a n d l e r : @ e s c a p i n g ( U I C o n t e x t M e n u C o n f i g u r a t i o n ? ) - > V o i d ) {
2020-05-21 01:34:39 +00:00
// p r i n t ( " c o n t e x t M e n u C o n f i g u r a t i o n F o r E l e m e n t " )
2020-05-09 02:36:07 +00:00
// l e t a c t i o n P r o v i d e r : U I C o n t e x t M e n u A c t i o n P r o v i d e r = { _ i n
// l e t e d i t M e n u = U I M e n u ( t i t l e : " E d i t . . . " , c h i l d r e n : [
// U I A c t i o n ( t i t l e : " C o p y " ) { a c t i o n i n
//
// } ,
// U I A c t i o n ( t i t l e : " D u p l i c a t e " ) { a c t i o n i n
//
// }
// ] )
// r e t u r n U I M e n u ( t i t l e : " T i t l e " , c h i l d r e n : [
// U I A c t i o n ( t i t l e : " S h a r e " ) { a c t i o n i n
//
// } ,
// e d i t M e n u
// ] )
// }
// l e t c o n t e x t M e n u C o n f i g u r a t i o n = U I C o n t e x t M e n u C o n f i g u r a t i o n ( i d e n t i f i e r : n i l , p r e v i e w P r o v i d e r : n i l , a c t i o n P r o v i d e r : a c t i o n P r o v i d e r )
// / / c o m p l e t i o n H a n d l e r ( c o n t e x t M e n u C o n f i g u r a t i o n )
// c o m p l e t i o n H a n d l e r ( n i l )
2020-05-21 01:34:39 +00:00
// / / o n C o n t e x t M e n u C o n f i g u r a t i o n F o r E l e m e n t ( l i n k U R L : e l e m e n t I n f o . l i n k U R L ? . a b s o l u t e S t r i n g , r e s u l t : n i l / * { ( r e s u l t ) - > V o i d i n
// / / i f r e s u l t i s F l u t t e r E r r o r {
// / / p r i n t ( ( r e s u l t a s ! F l u t t e r E r r o r ) . m e s s a g e ? ? " " )
// / / }
// / / e l s e i f ( r e s u l t a s ? N S O b j e c t ) = = F l u t t e r M e t h o d N o t I m p l e m e n t e d {
// / / c o m p l e t i o n H a n d l e r ( n i l )
// / / }
// / / e l s e {
// / / v a r r e s p o n s e : [ S t r i n g : A n y ]
// / / i f l e t r = r e s u l t {
// / / r e s p o n s e = r a s ! [ S t r i n g : A n y ]
// / / v a r a c t i o n = r e s p o n s e [ " a c t i o n " ] a s ? I n t
// / / a c t i o n = a c t i o n ! = n i l ? a c t i o n : 0 ;
// / / s w i t c h a c t i o n {
// / / c a s e 0 :
// / / b r e a k
// / / c a s e 1 :
// / / b r e a k
// / / d e f a u l t :
// / / c o m p l e t i o n H a n d l e r ( n i l )
// / / }
// / / r e t u r n ;
// / / }
// / / c o m p l e t i o n H a n d l e r ( n i l )
// / / }
// / / } * / )
2020-05-09 02:36:07 +00:00
// }
2020-05-21 01:34:39 +00:00
// / /
2020-05-09 02:36:07 +00:00
// @ a v a i l a b l e ( i O S 1 3 . 0 , * )
// p u b l i c f u n c w e b V i e w ( _ w e b V i e w : W K W e b V i e w ,
// c o n t e x t M e n u D i d E n d F o r E l e m e n t e l e m e n t I n f o : W K C o n t e x t M e n u E l e m e n t I n f o ) {
2020-05-21 01:34:39 +00:00
// p r i n t ( " c o n t e x t M e n u D i d E n d F o r E l e m e n t " )
// p r i n t ( e l e m e n t I n f o )
// / / o n C o n t e x t M e n u D i d E n d F o r E l e m e n t ( l i n k U R L : e l e m e n t I n f o . l i n k U R L ? . a b s o l u t e S t r i n g )
2020-05-09 02:36:07 +00:00
// }
//
// @ a v a i l a b l e ( i O S 1 3 . 0 , * )
// p u b l i c f u n c w e b V i e w ( _ w e b V i e w : W K W e b V i e w ,
// c o n t e x t M e n u F o r E l e m e n t e l e m e n t I n f o : W K C o n t e x t M e n u E l e m e n t I n f o ,
// w i l l C o m m i t W i t h A n i m a t o r a n i m a t o r : U I C o n t e x t M e n u I n t e r a c t i o n C o m m i t A n i m a t i n g ) {
2020-05-21 01:34:39 +00:00
// p r i n t ( " w i l l C o m m i t W i t h A n i m a t o r " )
// p r i n t ( e l e m e n t I n f o )
// / / o n W i l l C o m m i t W i t h A n i m a t o r ( l i n k U R L : e l e m e n t I n f o . l i n k U R L ? . a b s o l u t e S t r i n g , r e s u l t : n i l / * { ( r e s u l t ) - > V o i d i n
// / / i f r e s u l t i s F l u t t e r E r r o r {
// / / p r i n t ( ( r e s u l t a s ! F l u t t e r E r r o r ) . m e s s a g e ? ? " " )
// / / }
// / / e l s e i f ( r e s u l t a s ? N S O b j e c t ) = = F l u t t e r M e t h o d N o t I m p l e m e n t e d {
2020-05-09 02:36:07 +00:00
// / /
2020-05-21 01:34:39 +00:00
// / / }
// / / e l s e {
// / / v a r r e s p o n s e : [ S t r i n g : A n y ]
// / / i f l e t r = r e s u l t {
// / / r e s p o n s e = r a s ! [ S t r i n g : A n y ]
// / / v a r a c t i o n = r e s p o n s e [ " a c t i o n " ] a s ? I n t
// / / a c t i o n = a c t i o n ! = n i l ? a c t i o n : 0 ;
// / / / / s w i t c h a c t i o n {
// / / / / c a s e 0 :
// / / / / b r e a k
// / / / / c a s e 1 :
// / / / / b r e a k
// / / / / d e f a u l t :
// / / / /
// / / / / }
// / / r e t u r n ;
// / / }
// / /
// / / }
// / / } * / )
2020-05-09 02:36:07 +00:00
// }
//
// @ a v a i l a b l e ( i O S 1 3 . 0 , * )
// p u b l i c f u n c w e b V i e w ( _ w e b V i e w : W K W e b V i e w ,
// c o n t e x t M e n u W i l l P r e s e n t F o r E l e m e n t e l e m e n t I n f o : W K C o n t e x t M e n u E l e m e n t I n f o ) {
2020-05-21 01:34:39 +00:00
// p r i n t ( " c o n t e x t M e n u W i l l P r e s e n t F o r E l e m e n t " )
// p r i n t ( e l e m e n t I n f o . l i n k U R L )
// / / o n C o n t e x t M e n u W i l l P r e s e n t F o r E l e m e n t ( l i n k U R L : e l e m e n t I n f o . l i n k U R L ? . a b s o l u t e S t r i n g )
2020-05-09 02:36:07 +00:00
// }
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onLoadStart ( url : String ? ) {
let arguments : [ String : Any ? ] = [ " url " : url ]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onLoadStart " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onLoadStop ( url : String ? ) {
let arguments : [ String : Any ? ] = [ " url " : url ]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onLoadStop " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onLoadError ( url : String ? , error : Error ) {
let arguments : [ String : Any ? ] = [ " url " : url , " code " : error . _code , " message " : error . localizedDescription ]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onLoadError " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onLoadHttpError ( url : String ? , statusCode : Int , description : String ) {
let arguments : [ String : Any ? ] = [ " url " : url , " statusCode " : statusCode , " description " : description ]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onLoadHttpError " , arguments : arguments )
2019-11-21 01:19:43 +00:00
}
2019-11-02 03:16:47 +00:00
public func onProgressChanged ( progress : Int ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " progress " : progress ]
channel ? . invokeMethod ( " onProgressChanged " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
public func onFindResultReceived ( activeMatchOrdinal : Int , numberOfMatches : Int , isDoneCounting : Bool ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [
2019-11-02 03:16:47 +00:00
" activeMatchOrdinal " : activeMatchOrdinal ,
" numberOfMatches " : numberOfMatches ,
" isDoneCounting " : isDoneCounting
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onFindResultReceived " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
public func onScrollChanged ( x : Int , y : Int ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " x " : x , " y " : y ]
channel ? . invokeMethod ( " onScrollChanged " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
public func onDownloadStart ( url : String ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " url " : url ]
channel ? . invokeMethod ( " onDownloadStart " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
public func onLoadResourceCustomScheme ( scheme : String , url : String , result : FlutterResult ? ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " scheme " : scheme , " url " : url ]
channel ? . invokeMethod ( " onLoadResourceCustomScheme " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
2021-02-09 23:15:10 +00:00
public func shouldOverrideUrlLoading ( navigationAction : WKNavigationAction , result : FlutterResult ? ) {
var iosAllowsConstrainedNetworkAccess : Bool ? = nil
var iosAllowsExpensiveNetworkAccess : Bool ? = nil
if #available ( iOS 13.0 , * ) {
iosAllowsConstrainedNetworkAccess = navigationAction . request . allowsConstrainedNetworkAccess
iosAllowsExpensiveNetworkAccess = navigationAction . request . allowsExpensiveNetworkAccess
}
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ? ] = [
2021-02-09 23:15:10 +00:00
" url " : navigationAction . request . url ? . absoluteString ,
" method " : navigationAction . request . httpMethod ,
" headers " : navigationAction . request . allHTTPHeaderFields ,
" isForMainFrame " : navigationAction . targetFrame ? . isMainFrame ? ? false ,
2019-12-09 23:29:09 +00:00
" androidHasGesture " : nil ,
" androidIsRedirect " : nil ,
2021-02-09 23:15:10 +00:00
" iosWKNavigationType " : navigationAction . navigationType . rawValue ,
" iosAllowsCellularAccess " : navigationAction . request . allowsCellularAccess ,
" iosAllowsConstrainedNetworkAccess " : iosAllowsConstrainedNetworkAccess ,
" iosAllowsExpensiveNetworkAccess " : iosAllowsExpensiveNetworkAccess ,
" iosCachePolicy " : navigationAction . request . cachePolicy . rawValue ,
" iosHttpShouldHandleCookies " : navigationAction . request . httpShouldHandleCookies ,
" iosHttpShouldUsePipelining " : navigationAction . request . httpShouldUsePipelining ,
" iosNetworkServiceType " : navigationAction . request . networkServiceType . rawValue ,
" iosTimeoutInterval " : navigationAction . request . timeoutInterval ,
2019-12-09 23:29:09 +00:00
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " shouldOverrideUrlLoading " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
2021-02-09 23:15:10 +00:00
public func onNavigationResponse ( navigationResponse : WKNavigationResponse , result : FlutterResult ? ) {
let arguments : [ String : Any ? ] = [
" url " : navigationResponse . response . url ? . absoluteString ,
" isForMainFrame " : navigationResponse . isForMainFrame ,
" canShowMIMEType " : navigationResponse . canShowMIMEType ,
" expectedContentLength " : navigationResponse . response . expectedContentLength ,
" mimeType " : navigationResponse . response . mimeType ,
" suggestedFilename " : navigationResponse . response . suggestedFilename ,
" textEncodingName " : navigationResponse . response . textEncodingName ,
]
channel ? . invokeMethod ( " onNavigationResponse " , arguments : arguments , result : result )
}
2019-11-02 03:16:47 +00:00
public func onReceivedHttpAuthRequest ( challenge : URLAuthenticationChallenge , result : FlutterResult ? ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ? ] = [
2019-11-02 03:16:47 +00:00
" host " : challenge . protectionSpace . host ,
" protocol " : challenge . protectionSpace . protocol ,
" realm " : challenge . protectionSpace . realm ,
" port " : challenge . protectionSpace . port ,
" previousFailureCount " : challenge . previousFailureCount
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onReceivedHttpAuthRequest " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
public func onReceivedServerTrustAuthRequest ( challenge : URLAuthenticationChallenge , result : FlutterResult ? ) {
var serverCertificateData : NSData ?
let serverTrust = challenge . protectionSpace . serverTrust !
2020-06-13 01:50:19 +00:00
var secResult = SecTrustResultType . invalid
SecTrustEvaluate ( serverTrust , & secResult ) ;
2019-11-02 03:16:47 +00:00
if let serverCertificate = SecTrustGetCertificateAtIndex ( serverTrust , 0 ) {
let serverCertificateCFData = SecCertificateCopyData ( serverCertificate )
let data = CFDataGetBytePtr ( serverCertificateCFData )
let size = CFDataGetLength ( serverCertificateCFData )
serverCertificateData = NSData ( bytes : data , length : size )
2020-06-15 00:08:23 +00:00
if ( x509CertificateData = = nil ) {
x509CertificateData = Data ( serverCertificateData ! )
InAppWebView . sslCertificateMap [ challenge . protectionSpace . host ] = x509CertificateData ;
}
2019-11-02 03:16:47 +00:00
}
2020-06-13 01:50:19 +00:00
let error = secResult != SecTrustResultType . proceed ? secResult . rawValue : nil
var message = " "
switch secResult {
case . deny :
message = " Indicates a user-configured deny; do not proceed. "
break
case . fatalTrustFailure :
message = " Indicates a trust failure which cannot be overridden by the user. "
break
case . invalid :
message = " Indicates an invalid setting or result. "
break
case . otherError :
message = " Indicates a failure other than that of trust evaluation. "
break
case . recoverableTrustFailure :
message = " Indicates a trust policy failure which can be overridden by the user. "
break
case . unspecified :
message = " Indicates the evaluation succeeded and the certificate is implicitly trusted, but user intent was not explicitly specified. "
break
default :
message = " "
}
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ? ] = [
2019-11-02 03:16:47 +00:00
" host " : challenge . protectionSpace . host ,
" protocol " : challenge . protectionSpace . protocol ,
" realm " : challenge . protectionSpace . realm ,
" port " : challenge . protectionSpace . port ,
" previousFailureCount " : challenge . previousFailureCount ,
2020-06-15 00:08:23 +00:00
" sslCertificate " : InAppWebView . getCertificateMap ( x509Certificate :
( ( serverCertificateData != nil ) ? Data ( serverCertificateData ! ) : nil ) ) ,
2020-06-13 01:50:19 +00:00
" androidError " : nil ,
" iosError " : error ,
" message " : message ,
2019-11-02 03:16:47 +00:00
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onReceivedServerTrustAuthRequest " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
public func onReceivedClientCertRequest ( challenge : URLAuthenticationChallenge , result : FlutterResult ? ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ? ] = [
2019-11-02 03:16:47 +00:00
" host " : challenge . protectionSpace . host ,
" protocol " : challenge . protectionSpace . protocol ,
" realm " : challenge . protectionSpace . realm ,
" port " : challenge . protectionSpace . port
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onReceivedClientCertRequest " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
2021-02-10 01:32:05 +00:00
public func shouldAllowDeprecatedTLS ( challenge : URLAuthenticationChallenge , result : FlutterResult ? ) {
let arguments : [ String : Any ? ] = [
" host " : challenge . protectionSpace . host ,
" protocol " : challenge . protectionSpace . protocol ,
" realm " : challenge . protectionSpace . realm ,
" port " : challenge . protectionSpace . port ,
" previousFailureCount " : challenge . previousFailureCount
]
channel ? . invokeMethod ( " shouldAllowDeprecatedTLS " , arguments : arguments , result : result )
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onJsAlert ( frame : WKFrameInfo , message : String , result : FlutterResult ? ) {
let arguments : [ String : Any ? ] = [
" url " : frame . request . url ? . absoluteString ,
" message " : message ,
" iosIsMainFrame " : frame . isMainFrame
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onJsAlert " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onJsConfirm ( frame : WKFrameInfo , message : String , result : FlutterResult ? ) {
let arguments : [ String : Any ? ] = [
" url " : frame . request . url ? . absoluteString ,
" message " : message ,
" iosIsMainFrame " : frame . isMainFrame
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onJsConfirm " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onJsPrompt ( frame : WKFrameInfo , message : String , defaultValue : String ? , result : FlutterResult ? ) {
let arguments : [ String : Any ? ] = [
" url " : frame . request . url ? . absoluteString ,
" message " : message ,
" defaultValue " : defaultValue as Any ,
" iosIsMainFrame " : frame . isMainFrame
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onJsPrompt " , arguments : arguments , result : result )
2019-11-02 03:16:47 +00:00
}
2019-11-17 17:31:31 +00:00
public func onConsoleMessage ( message : String , messageLevel : Int ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " message " : message , " messageLevel " : messageLevel ]
channel ? . invokeMethod ( " onConsoleMessage " , arguments : arguments )
2019-11-02 03:16:47 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onUpdateVisitedHistory ( url : String ? ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ? ] = [
2019-12-16 22:58:10 +00:00
" url " : url ,
" androidIsReload " : nil
]
2019-12-18 20:34:40 +00:00
channel ? . invokeMethod ( " onUpdateVisitedHistory " , arguments : arguments )
2019-12-16 22:58:10 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
public func onTitleChanged ( title : String ? ) {
let arguments : [ String : Any ? ] = [
" title " : title
]
channel ? . invokeMethod ( " onTitleChanged " , arguments : arguments )
}
2020-05-09 02:36:07 +00:00
public func onLongPressHitTestResult ( hitTestResult : [ String : Any ? ] ) {
let arguments : [ String : Any ? ] = [
" hitTestResult " : hitTestResult
]
channel ? . invokeMethod ( " onLongPressHitTestResult " , arguments : arguments )
}
2019-11-02 03:16:47 +00:00
public func onCallJsHandler ( handlerName : String , _callHandlerID : Int64 , args : String ) {
2019-12-18 20:34:40 +00:00
let arguments : [ String : Any ] = [ " handlerName " : handlerName , " args " : args ]
channel ? . invokeMethod ( " onCallJsHandler " , arguments : arguments , result : { ( result ) -> Void in
if result is FlutterError {
print ( ( result as ! FlutterError ) . message ? ? " " )
}
else if ( result as ? NSObject ) = = FlutterMethodNotImplemented { }
else {
var json = " null "
if let r = result {
json = r as ! String
2019-11-02 03:16:47 +00:00
}
2020-07-03 18:12:52 +00:00
self . evaluateJavaScript ( " " "
if ( window . \ ( JAVASCRIPT_BRIDGE_NAME ) [ \ ( _callHandlerID ) ] != null ) {
window . \ ( JAVASCRIPT_BRIDGE_NAME ) [ \ ( _callHandlerID ) ] ( \ ( json ) ) ;
delete window . \ ( JAVASCRIPT_BRIDGE_NAME ) [ \ ( _callHandlerID ) ] ;
}
" " " , completionHandler: nil)
2019-12-18 20:34:40 +00:00
}
} )
2019-11-02 03:16:47 +00:00
}
2020-05-09 02:36:07 +00:00
public func onWebContentProcessDidTerminate ( ) {
channel ? . invokeMethod ( " onWebContentProcessDidTerminate " , arguments : [ ] )
}
2020-05-28 23:03:45 +00:00
public func onPageCommitVisible ( url : String ? ) {
let arguments : [ String : Any ? ] = [
" url " : url
]
channel ? . invokeMethod ( " onPageCommitVisible " , arguments : arguments )
2020-05-09 02:36:07 +00:00
}
public func onDidReceiveServerRedirectForProvisionalNavigation ( ) {
channel ? . invokeMethod ( " onDidReceiveServerRedirectForProvisionalNavigation " , arguments : [ ] )
}
2020-05-22 14:52:04 +00:00
// h t t p s : / / s t a c k o v e r f l o w . c o m / a / 4 2 8 4 0 5 4 1 / 4 6 3 7 6 3 8
public func isVideoPlayerWindow ( _ notificationObject : AnyObject ? ) -> Bool {
let nonVideoClasses = [ " _UIAlertControllerShimPresenterWindow " ,
" UITextEffectsWindow " ,
" UIRemoteKeyboardWindow " ]
var isVideo = true
if let obj = notificationObject {
for nonVideoClass in nonVideoClasses {
if let clazz = NSClassFromString ( nonVideoClass ) {
isVideo = isVideo && ! ( obj . isKind ( of : clazz ) )
}
}
}
return isVideo
}
@objc func onEnterFullscreen ( _ notification : Notification ) {
if ( isVideoPlayerWindow ( notification . object as AnyObject ? ) ) {
channel ? . invokeMethod ( " onEnterFullscreen " , arguments : [ ] )
}
}
@objc func onExitFullscreen ( _ notification : Notification ) {
if ( isVideoPlayerWindow ( notification . object as AnyObject ? ) ) {
channel ? . invokeMethod ( " onExitFullscreen " , arguments : [ ] )
}
}
2020-05-09 02:36:07 +00:00
// p u b l i c f u n c o n C o n t e x t M e n u C o n f i g u r a t i o n F o r E l e m e n t ( l i n k U R L : S t r i n g ? , r e s u l t : F l u t t e r R e s u l t ? ) {
// l e t a r g u m e n t s : [ S t r i n g : A n y ? ] = [ " l i n k U R L " : l i n k U R L ]
// c h a n n e l ? . i n v o k e M e t h o d ( " o n C o n t e x t M e n u C o n f i g u r a t i o n F o r E l e m e n t " , a r g u m e n t s : a r g u m e n t s , r e s u l t : r e s u l t )
// }
//
// p u b l i c f u n c o n C o n t e x t M e n u D i d E n d F o r E l e m e n t ( l i n k U R L : S t r i n g ? ) {
// l e t a r g u m e n t s : [ S t r i n g : A n y ? ] = [ " l i n k U R L " : l i n k U R L ]
// c h a n n e l ? . i n v o k e M e t h o d ( " o n C o n t e x t M e n u D i d E n d F o r E l e m e n t " , a r g u m e n t s : a r g u m e n t s )
// }
//
// p u b l i c f u n c o n W i l l C o m m i t W i t h A n i m a t o r ( l i n k U R L : S t r i n g ? , r e s u l t : F l u t t e r R e s u l t ? ) {
// l e t a r g u m e n t s : [ S t r i n g : A n y ? ] = [ " l i n k U R L " : l i n k U R L ]
// c h a n n e l ? . i n v o k e M e t h o d ( " o n W i l l C o m m i t W i t h A n i m a t o r " , a r g u m e n t s : a r g u m e n t s , r e s u l t : r e s u l t )
// }
//
// p u b l i c f u n c o n C o n t e x t M e n u W i l l P r e s e n t F o r E l e m e n t ( l i n k U R L : S t r i n g ? ) {
// l e t a r g u m e n t s : [ S t r i n g : A n y ? ] = [ " l i n k U R L " : l i n k U R L ]
// c h a n n e l ? . i n v o k e M e t h o d ( " o n C o n t e x t M e n u W i l l P r e s e n t F o r E l e m e n t " , a r g u m e n t s : a r g u m e n t s )
// }
2019-11-02 03:16:47 +00:00
public func userContentController ( _ userContentController : WKUserContentController , didReceive message : WKScriptMessage ) {
if message . name . starts ( with : " console " ) {
var messageLevel = 1
switch ( message . name ) {
2019-12-09 23:29:09 +00:00
case " consoleLog " :
messageLevel = 1
break ;
case " consoleDebug " :
// o n A n d r o i d , c o n s o l e . d e b u g i s T I P
messageLevel = 0
break ;
case " consoleError " :
messageLevel = 3
break ;
case " consoleInfo " :
// o n A n d r o i d , c o n s o l e . i n f o i s L O G
messageLevel = 1
break ;
case " consoleWarn " :
messageLevel = 2
break ;
default :
messageLevel = 1
break ;
2019-11-02 03:16:47 +00:00
}
2020-07-03 18:12:52 +00:00
let body = message . body as ! [ String : Any ? ]
let consoleMessage = body [ " message " ] as ! String
let _windowId = body [ " _windowId " ] as ? Int64
var webView = self
if let wId = _windowId , let webViewTransport = InAppWebView . windowWebViews [ wId ] {
webView = webViewTransport . webView
}
webView . onConsoleMessage ( message : consoleMessage , messageLevel : messageLevel )
2019-12-09 23:29:09 +00:00
} else if message . name = = " callHandler " {
2020-07-03 18:12:52 +00:00
let body = message . body as ! [ String : Any ? ]
2019-11-02 03:16:47 +00:00
let handlerName = body [ " handlerName " ] as ! String
2019-12-09 23:29:09 +00:00
if handlerName = = " onPrint " {
printCurrentPage ( printCompletionHandler : nil )
}
2019-11-02 03:16:47 +00:00
let _callHandlerID = body [ " _callHandlerID " ] as ! Int64
let args = body [ " args " ] as ! String
2020-07-03 18:12:52 +00:00
let _windowId = body [ " _windowId " ] as ? Int64
var webView = self
if let wId = _windowId , let webViewTransport = InAppWebView . windowWebViews [ wId ] {
webView = webViewTransport . webView
}
webView . onCallJsHandler ( handlerName : handlerName , _callHandlerID : _callHandlerID , args : args )
2019-11-05 23:23:24 +00:00
} else if message . name = = " onFindResultReceived " {
2020-07-03 18:12:52 +00:00
let body = message . body as ! [ String : Any ? ]
let findResult = body [ " findResult " ] as ! [ String : Any ]
let activeMatchOrdinal = findResult [ " activeMatchOrdinal " ] as ! Int
let numberOfMatches = findResult [ " numberOfMatches " ] as ! Int
let isDoneCounting = findResult [ " isDoneCounting " ] as ! Bool
let _windowId = body [ " _windowId " ] as ? Int64
var webView = self
if let wId = _windowId , let webViewTransport = InAppWebView . windowWebViews [ wId ] {
webView = webViewTransport . webView
2019-11-02 03:16:47 +00:00
}
2020-07-03 18:12:52 +00:00
webView . onFindResultReceived ( activeMatchOrdinal : activeMatchOrdinal , numberOfMatches : numberOfMatches , isDoneCounting : isDoneCounting )
2021-02-07 15:05:39 +00:00
} else if message . name = = " onCallAsyncJavaScriptResultBelowIOS14Received " {
let body = message . body as ! [ String : Any ? ]
let resultUuid = body [ " resultUuid " ] as ! String
if let result = callAsyncJavaScriptBelowIOS14Results [ resultUuid ] {
result ( [
" value " : body [ " value " ] ,
" error " : body [ " error " ]
] )
callAsyncJavaScriptBelowIOS14Results . removeValue ( forKey : resultUuid )
}
2019-11-02 03:16:47 +00:00
}
}
2019-12-02 23:07:29 +00:00
public func findAllAsync ( find : String ? , completionHandler : ( ( Any ? , Error ? ) -> Void ) ? ) {
2019-11-02 03:16:47 +00:00
let startSearch = " wkwebview_FindAllAsync(' \( find ? ? " " ) '); "
evaluateJavaScript ( startSearch , completionHandler : completionHandler )
}
2019-12-02 23:07:29 +00:00
public func findNext ( forward : Bool , completionHandler : ( ( Any ? , Error ? ) -> Void ) ? ) {
2019-11-02 03:16:47 +00:00
evaluateJavaScript ( " wkwebview_FindNext( \( forward ? " true " : " false " ) ); " , completionHandler : completionHandler )
}
2019-12-02 23:07:29 +00:00
public func clearMatches ( completionHandler : ( ( Any ? , Error ? ) -> Void ) ? ) {
2019-11-02 03:16:47 +00:00
evaluateJavaScript ( " wkwebview_ClearMatches(); " , completionHandler : completionHandler )
}
2019-11-07 23:32:29 +00:00
2020-06-13 01:50:19 +00:00
public func scrollTo ( x : Int , y : Int , animated : Bool ) {
scrollView . setContentOffset ( CGPoint ( x : x , y : y ) , animated : animated )
2019-11-19 19:44:06 +00:00
}
2020-06-13 01:50:19 +00:00
public func scrollBy ( x : Int , y : Int , animated : Bool ) {
2019-11-19 19:44:06 +00:00
let newX = CGFloat ( x ) + scrollView . contentOffset . x
let newY = CGFloat ( y ) + scrollView . contentOffset . y
2020-06-13 01:50:19 +00:00
scrollView . setContentOffset ( CGPoint ( x : newX , y : newY ) , animated : animated )
2019-11-19 19:44:06 +00:00
}
2019-12-02 23:07:29 +00:00
public func pauseTimers ( ) {
2020-06-19 19:59:43 +00:00
if ! isPausedTimers {
isPausedTimers = true
let script = " alert(); " ;
self . evaluateJavaScript ( script , completionHandler : nil )
}
2019-12-02 23:07:29 +00:00
}
public func resumeTimers ( ) {
2020-06-19 19:59:43 +00:00
if isPausedTimers {
if let completionHandler = isPausedTimersCompletionHandler {
self . isPausedTimersCompletionHandler = nil
completionHandler ( )
}
isPausedTimers = false
2019-12-02 23:07:29 +00:00
}
}
2019-12-09 23:29:09 +00:00
public func printCurrentPage ( printCompletionHandler : ( ( _ completed : Bool , _ error : Error ? ) -> Void ) ? ) {
let printController = UIPrintInteractionController . shared
let printFormatter = self . viewPrintFormatter ( )
printController . printFormatter = printFormatter
let completionHandler : UIPrintInteractionController . CompletionHandler = { ( printController , completed , error ) in
if ! completed {
if let e = error {
print ( " [PRINT] Failed: \( e . localizedDescription ) " )
} else {
print ( " [PRINT] Canceled " )
}
}
if let callback = printCompletionHandler {
callback ( completed , error )
}
}
printController . present ( animated : true , completionHandler : completionHandler )
}
2019-12-16 22:58:10 +00:00
public func getContentHeight ( ) -> Int64 {
return Int64 ( scrollView . contentSize . height )
}
2021-02-07 15:37:01 +00:00
public func zoomBy ( zoomFactor : Float , animated : Bool ) {
2019-12-16 22:58:10 +00:00
let currentZoomScale = scrollView . zoomScale
2021-02-07 15:37:01 +00:00
scrollView . setZoomScale ( currentZoomScale * CGFloat ( zoomFactor ) , animated : animated )
2019-12-16 22:58:10 +00:00
}
public func getScale ( ) -> Float {
return Float ( scrollView . zoomScale )
}
2020-05-21 01:34:39 +00:00
public func getSelectedText ( completionHandler : @ escaping ( Any ? , Error ? ) -> Void ) {
if configuration . preferences . javaScriptEnabled {
evaluateJavaScript ( getSelectedTextJS , completionHandler : completionHandler )
} else {
completionHandler ( nil , nil )
}
}
public func getHitTestResult ( completionHandler : @ escaping ( Any ? , Error ? ) -> Void ) {
if configuration . preferences . javaScriptEnabled , let lastTouchLocation = lastTouchPoint {
self . evaluateJavaScript ( " window. \( JAVASCRIPT_BRIDGE_NAME ) ._findElementsAtPoint( \( lastTouchLocation . x ) , \( lastTouchLocation . y ) ) " , completionHandler : { ( value , error ) in
completionHandler ( value , error )
} )
} else {
completionHandler ( nil , nil )
}
}
2020-06-12 02:04:41 +00:00
public func requestFocusNodeHref ( completionHandler : @ escaping ( [ String : Any ? ] ? , Error ? ) -> Void ) {
if configuration . preferences . javaScriptEnabled {
// a d d s o m e d e l a y t o m a k e i t s u r e _ l a s t A n c h o r O r I m a g e T o u c h e d i s u p d a t e d
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.15 ) {
self . evaluateJavaScript ( " window. \( JAVASCRIPT_BRIDGE_NAME ) ._lastAnchorOrImageTouched " , completionHandler : { ( value , error ) in
let lastAnchorOrImageTouched = value as ? [ String : Any ? ]
completionHandler ( lastAnchorOrImageTouched , error )
} )
}
} else {
completionHandler ( nil , nil )
}
}
public func requestImageRef ( completionHandler : @ escaping ( [ String : Any ? ] ? , Error ? ) -> Void ) {
if configuration . preferences . javaScriptEnabled {
// a d d s o m e d e l a y t o m a k e i t s u r e _ l a s t I m a g e T o u c h e d i s u p d a t e d
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.15 ) {
self . evaluateJavaScript ( " window. \( JAVASCRIPT_BRIDGE_NAME ) ._lastImageTouched " , completionHandler : { ( value , error ) in
let lastImageTouched = value as ? [ String : Any ? ]
completionHandler ( lastImageTouched , error )
} )
}
} else {
completionHandler ( nil , nil )
}
}
2020-05-30 18:23:33 +00:00
public func clearFocus ( ) {
self . scrollView . subviews . first ? . resignFirstResponder ( )
}
2020-06-15 00:08:23 +00:00
public func getCertificate ( ) -> Data ? {
var x509Certificate = self . x509CertificateData
if x509Certificate = = nil , let scheme = url ? . scheme , scheme = = " https " ,
let host = url ? . host , let cert = InAppWebView . sslCertificateMap [ host ] {
x509Certificate = cert
}
return x509Certificate
}
public func getCertificateMap ( ) -> [ String : Any ? ] ? {
return InAppWebView . getCertificateMap ( x509Certificate : getCertificate ( ) )
}
public static func getCertificateMap ( x509Certificate : Data ? ) -> [ String : Any ? ] ? {
return x509Certificate != nil ? [
" issuedBy " : nil ,
" issuedTo " : nil ,
" validNotAfterDate " : nil ,
" validNotBeforeDate " : nil ,
" x509Certificate " : x509Certificate
] : nil ;
}
2019-12-16 22:58:10 +00:00
public func dispose ( ) {
2020-06-19 19:59:43 +00:00
if isPausedTimers , let completionHandler = isPausedTimersCompletionHandler {
isPausedTimersCompletionHandler = nil
completionHandler ( )
}
2019-12-16 22:58:10 +00:00
stopLoading ( )
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
if windowId = = nil {
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleLog " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleDebug " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleError " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleInfo " )
configuration . userContentController . removeScriptMessageHandler ( forName : " consoleWarn " )
configuration . userContentController . removeScriptMessageHandler ( forName : " callHandler " )
configuration . userContentController . removeScriptMessageHandler ( forName : " onFindResultReceived " )
2021-02-07 15:05:39 +00:00
configuration . userContentController . removeScriptMessageHandler ( forName : " onCallAsyncJavaScriptResultBelowIOS14Received " )
2021-02-01 14:55:27 +00:00
if #available ( iOS 14.0 , * ) {
configuration . userContentController . removeAllScriptMessageHandlers ( )
2021-02-04 00:43:55 +00:00
for contentWorldName in userScriptsContentWorlds {
let contentWorld = getContentWorld ( name : contentWorldName )
configuration . userContentController . removeAllScriptMessageHandlers ( from : contentWorld )
configuration . userContentController . removeAllScriptMessageHandlers ( from : contentWorld )
}
2021-02-01 14:55:27 +00:00
}
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
configuration . userContentController . removeAllUserScripts ( )
if #available ( iOS 11.0 , * ) {
configuration . userContentController . removeAllContentRuleLists ( )
}
}
2019-12-16 22:58:10 +00:00
removeObserver ( self , forKeyPath : # keyPath ( WKWebView . estimatedProgress ) )
removeObserver ( self , forKeyPath : # keyPath ( WKWebView . url ) )
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
removeObserver ( self , forKeyPath : # keyPath ( WKWebView . title ) )
2020-05-21 01:34:39 +00:00
NotificationCenter . default . removeObserver ( self )
for imp in customIMPs {
imp_removeBlock ( imp )
}
longPressRecognizer ? . removeTarget ( self , action : #selector ( longPressGestureDetected ) )
longPressRecognizer ? . delegate = nil
scrollView . removeGestureRecognizer ( longPressRecognizer ! )
2019-11-07 23:32:29 +00:00
uiDelegate = nil
navigationDelegate = nil
scrollView . delegate = nil
IABController ? . webView = nil
2019-12-02 23:07:29 +00:00
isPausedTimersCompletionHandler = nil
2020-05-21 01:34:39 +00:00
channel = nil
SharedLastTouchPointTimestamp . removeValue ( forKey : self )
Updated onCreateWindow, onJsAlert, onJsConfirm, and onJsPrompt webview events, added onCloseWindow, onTitleChanged, onWindowFocus, and onWindowBlur webview events, added androidOnRequestFocus, androidOnReceivedIcon, androidOnReceivedTouchIconUrl, androidOnJsBeforeUnload, and androidOnReceivedLoginRequest Android-specific webview events, fix #403
2020-06-29 14:34:08 +00:00
if let wId = windowId , InAppWebView . windowWebViews [ wId ] != nil {
InAppWebView . windowWebViews . removeValue ( forKey : wId )
}
2021-02-07 15:05:39 +00:00
callAsyncJavaScriptBelowIOS14Results . removeAll ( )
2019-12-16 22:58:10 +00:00
super . removeFromSuperview ( )
}
deinit {
print ( " InAppWebView - dealloc " )
2019-11-07 23:32:29 +00:00
}
2020-06-20 19:58:29 +00:00
// v a r a c c e s s o r y V i e w : U I V i e w ?
//
// / / h t t p s : / / s t a c k o v e r f l o w . c o m / a / 5 8 0 0 1 3 9 5 / 4 6 3 7 6 3 8
// p u b l i c o v e r r i d e v a r i n p u t A c c e s s o r y V i e w : U I V i e w ? {
// / / r e m o v e / r e p l a c e t h e d e f a u l t a c c e s s o r y v i e w
// r e t u r n a c c e s s o r y V i e w
// }
2019-11-02 03:16:47 +00:00
}