diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2020-09-21 18:43:03 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2020-09-21 18:43:03 +0200 |
| commit | 7f6c5134780ad3cedc718772b40578f0170f200e (patch) | |
| tree | 15a120d91855ae66f42111e05f696e5600a9b832 /StoneIsland/plugins/cordova-plugin-inappbrowser/src | |
| parent | 853bd5fa85812316a72b2f5ce755dc0facb91932 (diff) | |
cordova-plugin-inappbrowser welcome to the family!!
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-inappbrowser/src')
33 files changed, 2041 insertions, 3029 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppBrowser.java b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppBrowser.java deleted file mode 100644 index 0263ea2c..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppBrowser.java +++ /dev/null @@ -1,846 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*/ -package org.apache.cordova.inappbrowser; - -import android.annotation.SuppressLint; -import org.apache.cordova.inappbrowser.InAppBrowserDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.text.InputType; -import android.util.Log; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import com.amazon.android.webkit.AmazonWebChromeClient; -import com.amazon.android.webkit.AmazonGeolocationPermissions.Callback; -import com.amazon.android.webkit.AmazonJsPromptResult; -import com.amazon.android.webkit.AmazonWebSettings; -import com.amazon.android.webkit.AmazonWebStorage; -import com.amazon.android.webkit.AmazonWebView; -import com.amazon.android.webkit.AmazonWebViewClient; -import com.amazon.android.webkit.AmazonCookieManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; - -import org.apache.cordova.CallbackContext; -import org.apache.cordova.Config; -import org.apache.cordova.CordovaArgs; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.LOG; -import org.apache.cordova.PluginResult; -import org.apache.cordova.CordovaActivity; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; -import java.util.StringTokenizer; - -@SuppressLint("SetJavaScriptEnabled") -public class InAppBrowser extends CordovaPlugin { - - private static final String NULL = "null"; - protected static final String LOG_TAG = "InAppBrowser"; - private static final String SELF = "_self"; - private static final String SYSTEM = "_system"; - // private static final String BLANK = "_blank"; - private static final String EXIT_EVENT = "exit"; - private static final String LOCATION = "location"; - private static final String HIDDEN = "hidden"; - private static final String ZOOM = "zoom"; - private static final String LOAD_START_EVENT = "loadstart"; - private static final String LOAD_STOP_EVENT = "loadstop"; - private static final String LOAD_ERROR_EVENT = "loaderror"; - private static final String CLEAR_ALL_CACHE = "clearcache"; - private static final String CLEAR_SESSION_CACHE = "clearsessioncache"; - - private InAppBrowserDialog dialog; - private AmazonWebView inAppWebView; - private EditText edittext; - private CallbackContext callbackContext; - private boolean showLocationBar = true; - private boolean showZoomControls = true; - private boolean openWindowHidden = false; - private boolean clearAllCache= false; - private boolean clearSessionCache=false; - - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException { - if (action.equals("open")) { - this.callbackContext = callbackContext; - final String url = args.getString(0); - String t = args.optString(1); - if (t == null || t.equals("") || t.equals(NULL)) { - t = SELF; - } - final String target = t; - final HashMap<String, Boolean> features = parseFeature(args.optString(2)); - - Log.d(LOG_TAG, "target = " + target); - - this.cordova.getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - String result = ""; - // SELF - if (SELF.equals(target)) { - Log.d(LOG_TAG, "in self"); - // load in webview - if (url.startsWith("file://") || url.startsWith("javascript:") - || Config.isUrlWhiteListed(url)) { - Log.d(LOG_TAG, "loading in webview"); - webView.loadUrl(url); - } - //Load the dialer - else if (url.startsWith(AmazonWebView.SCHEME_TEL)) - { - try { - Log.d(LOG_TAG, "loading in dialer"); - Intent intent = new Intent(Intent.ACTION_DIAL); - intent.setData(Uri.parse(url)); - cordova.getActivity().startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); - } - } - // load in InAppBrowser - else { - Log.d(LOG_TAG, "loading in InAppBrowser"); - result = showWebPage(url, features); - } - } - // SYSTEM - else if (SYSTEM.equals(target)) { - Log.d(LOG_TAG, "in system"); - result = openExternal(url); - } - // BLANK - or anything else - else { - Log.d(LOG_TAG, "in blank"); - result = showWebPage(url, features); - } - - PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); - pluginResult.setKeepCallback(true); - callbackContext.sendPluginResult(pluginResult); - } - }); - } - else if (action.equals("close")) { - closeDialog(); - } - else if (action.equals("injectScriptCode")) { - String jsWrapper = null; - if (args.getBoolean(1)) { - jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId()); - } - injectDeferredObject(args.getString(0), jsWrapper); - } - else if (action.equals("injectScriptFile")) { - String jsWrapper; - if (args.getBoolean(1)) { - jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId()); - } else { - jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)"; - } - injectDeferredObject(args.getString(0), jsWrapper); - } - else if (action.equals("injectStyleCode")) { - String jsWrapper; - if (args.getBoolean(1)) { - jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); - } else { - jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)"; - } - injectDeferredObject(args.getString(0), jsWrapper); - } - else if (action.equals("injectStyleFile")) { - String jsWrapper; - if (args.getBoolean(1)) { - jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); - } else { - jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)"; - } - injectDeferredObject(args.getString(0), jsWrapper); - } - else if (action.equals("show")) { - this.cordova.getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - dialog.show(); - } - }); - PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); - pluginResult.setKeepCallback(true); - this.callbackContext.sendPluginResult(pluginResult); - } - else { - return false; - } - return true; - } - - /** - * Called when the view navigates. - */ - @Override - public void onReset() { - closeDialog(); - } - - /** - * Called by AccelBroker when listener is to be shut down. - * Stop listener. - */ - public void onDestroy() { - closeDialog(); - } - - /** - * Inject an object (script or style) into the InAppBrowser AmazonWebView. - * - * This is a helper method for the inject{Script|Style}{Code|File} API calls, which - * provides a consistent method for injecting JavaScript code into the document. - * - * If a wrapper string is supplied, then the source string will be JSON-encoded (adding - * quotes) and wrapped using string formatting. (The wrapper string should have a single - * '%s' marker) - * - * @param source The source object (filename or script/style text) to inject into - * the document. - * @param jsWrapper A JavaScript string to wrap the source string in, so that the object - * is properly injected, or null if the source string is JavaScript text - * which should be executed directly. - */ - private void injectDeferredObject(String source, String jsWrapper) { - final String scriptToInject; - if (jsWrapper != null) { - org.json.JSONArray jsonEsc = new org.json.JSONArray(); - jsonEsc.put(source); - String jsonRepr = jsonEsc.toString(); - String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1); - scriptToInject = String.format(jsWrapper, jsonSourceString); - } else { - scriptToInject = source; - } - final String finalScriptToInject = scriptToInject; - this.cordova.getActivity().runOnUiThread(new Runnable() { - @SuppressLint("NewApi") - @Override - public void run() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - // This action will have the side-effect of blurring the currently focused element - inAppWebView.loadUrl("javascript:" + finalScriptToInject); - } /*else { - inAppWebView.evaluateJavascript(finalScriptToInject, null); - }*/ - } - }); - } - - /** - * Put the list of features into a hash map - * - * @param optString - * @return - */ - private HashMap<String, Boolean> parseFeature(String optString) { - if (optString.equals(NULL)) { - return null; - } else { - HashMap<String, Boolean> map = new HashMap<String, Boolean>(); - StringTokenizer features = new StringTokenizer(optString, ","); - StringTokenizer option; - while(features.hasMoreElements()) { - option = new StringTokenizer(features.nextToken(), "="); - if (option.hasMoreElements()) { - String key = option.nextToken(); - Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE; - map.put(key, value); - } - } - return map; - } - } - - /** - * Display a new browser with the specified URL. - * - * @param url The url to load. - * @param usePhoneGap Load url in PhoneGap webview - * @return "" if ok, or error message. - */ - public String openExternal(String url) { - try { - Intent intent = null; - intent = new Intent(Intent.ACTION_VIEW); - // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent". - // Adding the MIME type to http: URLs causes them to not be handled by the downloader. - Uri uri = Uri.parse(url); - if ("file".equals(uri.getScheme())) { - intent.setDataAndType(uri, webView.getResourceApi().getMimeType(uri)); - } else { - intent.setData(uri); - } - this.cordova.getActivity().startActivity(intent); - return ""; - } catch (android.content.ActivityNotFoundException e) { - Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString()); - return e.toString(); - } - } - - /** - * Closes the dialog - */ - public void closeDialog() { - final AmazonWebView childView = this.inAppWebView; - // The JS protects against multiple calls, so this should happen only when - // closeDialog() is called by other native code. - if (childView == null) { - return; - } - this.cordova.getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - childView.setWebViewClient(new AmazonWebViewClient() { - // NB: wait for about:blank before dismissing - public void onPageFinished(AmazonWebView view, String url) { - if (dialog != null) { - dialog.dismiss(); - } - } - }); - // NB: From SDK 19: "If you call methods on WebView from any thread - // other than your app's UI thread, it can cause unexpected results." - // http://developer.android.com/guide/webapps/migrating.html#Threads - childView.loadUrl("about:blank"); - } - }); - - try { - JSONObject obj = new JSONObject(); - obj.put("type", EXIT_EVENT); - sendUpdate(obj, false); - } catch (JSONException ex) { - Log.d(LOG_TAG, "Should never happen"); - } - } - /** - * Checks to see if it is possible to go back one page in history, then does so. - */ - private void goBack() { - this.cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - if (InAppBrowser.this.inAppWebView.canGoBack()) { - InAppBrowser.this.inAppWebView.goBack(); - } - } - }); - } - - /** - * Checks to see if it is possible to go forward one page in history, then does so. - */ - private void goForward() { - this.cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - if (InAppBrowser.this.inAppWebView.canGoForward()) { - InAppBrowser.this.inAppWebView.goForward(); - } - } - }); - } - - /** - * Navigate to the new page - * - * @param url to load - */ - private void navigate(final String url) { - InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); - - this.cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - if (!url.startsWith("http") && !url.startsWith("file:")) { - InAppBrowser.this.inAppWebView.loadUrl("http://" + url); - } else { - InAppBrowser.this.inAppWebView.loadUrl(url); - } - InAppBrowser.this.inAppWebView.requestFocus(); - } - }); - } - - - /** - * Should we show the location bar? - * - * @return boolean - */ - private boolean getShowLocationBar() { - return this.showLocationBar; - } - - /** - * Should we show the zoom controls? - * - * @return boolean - */ - private boolean getShowZoomControls() { - return this.showZoomControls; - } - - private InAppBrowser getInAppBrowser(){ - return this; - } - - /** - * Display a new browser with the specified URL. - * - * @param url The url to load. - * @param jsonObject - */ - public String showWebPage(final String url, HashMap<String, Boolean> features) { - // Determine if we should hide the location bar. - showLocationBar = true; - showZoomControls = true; - openWindowHidden = false; - if (features != null) { - Boolean show = features.get(LOCATION); - if (show != null) { - showLocationBar = show.booleanValue(); - } - Boolean zoom = features.get(ZOOM); - if (zoom != null) { - showZoomControls = zoom.booleanValue(); - } - Boolean hidden = features.get(HIDDEN); - if (hidden != null) { - openWindowHidden = hidden.booleanValue(); - } - Boolean cache = features.get(CLEAR_ALL_CACHE); - if (cache != null) { - clearAllCache = cache.booleanValue(); - } else { - cache = features.get(CLEAR_SESSION_CACHE); - if (cache != null) { - clearSessionCache = cache.booleanValue(); - } - } - } - - final CordovaWebView thatWebView = this.webView; - - // Create dialog in new thread - Runnable runnable = new Runnable() { - /** - * Convert our DIP units to Pixels - * - * @return int - */ - private int dpToPixels(int dipValue) { - int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, - (float) dipValue, - cordova.getActivity().getResources().getDisplayMetrics() - ); - - return value; - } - - @SuppressLint("NewApi") - public void run() { - // Let's create the main dialog - dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); - dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; - dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - dialog.setCancelable(true); - dialog.setInAppBroswer(getInAppBrowser()); - - // Main container layout - LinearLayout main = new LinearLayout(cordova.getActivity()); - main.setOrientation(LinearLayout.VERTICAL); - - // Toolbar layout - RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); - //Please, no more black! - toolbar.setBackgroundColor(android.graphics.Color.LTGRAY); - toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44))); - toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); - toolbar.setHorizontalGravity(Gravity.LEFT); - toolbar.setVerticalGravity(Gravity.TOP); - - // Action Button Container layout - RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity()); - actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); - actionButtonContainer.setHorizontalGravity(Gravity.LEFT); - actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); - actionButtonContainer.setId(1); - - // Back button - Button back = new Button(cordova.getActivity()); - RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT); - back.setLayoutParams(backLayoutParams); - back.setContentDescription("Back Button"); - back.setId(2); - Resources activityRes = cordova.getActivity().getResources(); - int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName()); - Drawable backIcon = activityRes.getDrawable(backResId); - if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) - { - back.setBackgroundDrawable(backIcon); - } - else - { - back.setBackground(backIcon); - } - - back.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - goBack(); - } - }); - - // Forward button - Button forward = new Button(cordova.getActivity()); - RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2); - forward.setLayoutParams(forwardLayoutParams); - forward.setContentDescription("Forward Button"); - forward.setId(3); - int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName()); - Drawable fwdIcon = activityRes.getDrawable(fwdResId); - if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) - { - forward.setBackgroundDrawable(fwdIcon); - } - else - { - forward.setBackground(fwdIcon); - } - forward.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - goForward(); - } - }); - - // Edit Text Box - edittext = new EditText(cordova.getActivity()); - RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1); - textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5); - edittext.setLayoutParams(textLayoutParams); - edittext.setId(4); - edittext.setSingleLine(true); - edittext.setText(url); - edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI); - edittext.setImeOptions(EditorInfo.IME_ACTION_GO); - edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE - edittext.setOnKeyListener(new View.OnKeyListener() { - public boolean onKey(View v, int keyCode, KeyEvent event) { - // If the event is a key-down event on the "enter" button - if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { - navigate(edittext.getText().toString()); - return true; - } - return false; - } - }); - - // Close/Done button - Button close = new Button(cordova.getActivity()); - RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - close.setLayoutParams(closeLayoutParams); - forward.setContentDescription("Close Button"); - close.setId(5); - int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName()); - Drawable closeIcon = activityRes.getDrawable(closeResId); - if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) - { - close.setBackgroundDrawable(closeIcon); - } - else - { - close.setBackground(closeIcon); - } - close.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - closeDialog(); - } - }); - - // WebView - inAppWebView = new AmazonWebView(cordova.getActivity()); - - CordovaActivity app = (CordovaActivity) cordova.getActivity(); - cordova.getFactory().initializeWebView(inAppWebView, 0x00FF00, false, null); - - inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView)); - AmazonWebViewClient client = new InAppBrowserClient(thatWebView, edittext); - inAppWebView.setWebViewClient(client); - AmazonWebSettings settings = inAppWebView.getSettings(); - settings.setJavaScriptEnabled(true); - settings.setJavaScriptCanOpenWindowsAutomatically(true); - settings.setBuiltInZoomControls(getShowZoomControls()); - settings.setPluginState(com.amazon.android.webkit.AmazonWebSettings.PluginState.ON); - - //Toggle whether this is enabled or not! - Bundle appSettings = cordova.getActivity().getIntent().getExtras(); - boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true); - if (enableDatabase) { - String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath(); - settings.setDatabasePath(databasePath); - settings.setDatabaseEnabled(true); - } - settings.setDomStorageEnabled(true); - - if (clearAllCache) { - AmazonCookieManager.getInstance().removeAllCookie(); - } else if (clearSessionCache) { - AmazonCookieManager.getInstance().removeSessionCookie(); - } - - inAppWebView.loadUrl(url); - inAppWebView.setId(6); - inAppWebView.getSettings().setLoadWithOverviewMode(true); - inAppWebView.getSettings().setUseWideViewPort(true); - inAppWebView.requestFocus(); - inAppWebView.requestFocusFromTouch(); - - // Add the back and forward buttons to our action button container layout - actionButtonContainer.addView(back); - actionButtonContainer.addView(forward); - - // Add the views to our toolbar - toolbar.addView(actionButtonContainer); - toolbar.addView(edittext); - toolbar.addView(close); - - // Don't add the toolbar if its been disabled - if (getShowLocationBar()) { - // Add our toolbar to our main view/layout - main.addView(toolbar); - } - - // Add our webview to our main view/layout - main.addView(inAppWebView); - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); - lp.copyFrom(dialog.getWindow().getAttributes()); - lp.width = WindowManager.LayoutParams.MATCH_PARENT; - lp.height = WindowManager.LayoutParams.MATCH_PARENT; - - dialog.setContentView(main); - dialog.show(); - dialog.getWindow().setAttributes(lp); - // the goal of openhidden is to load the url and not display it - // Show() needs to be called to cause the URL to be loaded - if(openWindowHidden) { - dialog.hide(); - } - } - }; - this.cordova.getActivity().runOnUiThread(runnable); - return ""; - } - - /** - * Create a new plugin success result and send it back to JavaScript - * - * @param obj a JSONObject contain event payload information - */ - private void sendUpdate(JSONObject obj, boolean keepCallback) { - sendUpdate(obj, keepCallback, PluginResult.Status.OK); - } - - /** - * Create a new plugin result and send it back to JavaScript - * - * @param obj a JSONObject contain event payload information - * @param status the status code to return to the JavaScript environment - */ - private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) { - if (callbackContext != null) { - PluginResult result = new PluginResult(status, obj); - result.setKeepCallback(keepCallback); - callbackContext.sendPluginResult(result); - if (!keepCallback) { - callbackContext = null; - } - } - } - - /** - * The webview client receives notifications about appView - */ - public class InAppBrowserClient extends AmazonWebViewClient { - EditText edittext; - CordovaWebView webView; - - /** - * Constructor. - * - * @param mContext - * @param edittext - */ - public InAppBrowserClient(CordovaWebView webView, EditText mEditText) { - this.webView = webView; - this.edittext = mEditText; - } - - /** - * Notify the host application that a page has started loading. - * - * @param view The webview initiating the callback. - * @param url The url of the page. - */ - @Override - public void onPageStarted(AmazonWebView view, String url, Bitmap favicon) { - super.onPageStarted(view, url, favicon); - String newloc = ""; - if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) { - newloc = url; - } - // If dialing phone (tel:5551212) - else if (url.startsWith(AmazonWebView.SCHEME_TEL)) { - try { - Intent intent = new Intent(Intent.ACTION_DIAL); - intent.setData(Uri.parse(url)); - cordova.getActivity().startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); - } - } - - else if (url.startsWith("geo:") || url.startsWith(AmazonWebView.SCHEME_MAILTO) || url.startsWith("market:")) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - cordova.getActivity().startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString()); - } - } - // If sms:5551212?body=This is the message - else if (url.startsWith("sms:")) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - - // Get address - String address = null; - int parmIndex = url.indexOf('?'); - if (parmIndex == -1) { - address = url.substring(4); - } - else { - address = url.substring(4, parmIndex); - - // If body, then set sms body - Uri uri = Uri.parse(url); - String query = uri.getQuery(); - if (query != null) { - if (query.startsWith("body=")) { - intent.putExtra("sms_body", query.substring(5)); - } - } - } - intent.setData(Uri.parse("sms:" + address)); - intent.putExtra("address", address); - intent.setType("vnd.android-dir/mms-sms"); - cordova.getActivity().startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); - } - } - else { - newloc = "http://" + url; - } - - if (!newloc.equals(edittext.getText().toString())) { - edittext.setText(newloc); - } - - try { - JSONObject obj = new JSONObject(); - obj.put("type", LOAD_START_EVENT); - obj.put("url", newloc); - - sendUpdate(obj, true); - } catch (JSONException ex) { - Log.d(LOG_TAG, "Should never happen"); - } - } - - public void onPageFinished(AmazonWebView view, String url) { - super.onPageFinished(view, url); - - try { - JSONObject obj = new JSONObject(); - obj.put("type", LOAD_STOP_EVENT); - obj.put("url", url); - - sendUpdate(obj, true); - } catch (JSONException ex) { - Log.d(LOG_TAG, "Should never happen"); - } - } - - public void onReceivedError(AmazonWebView view, int errorCode, String description, String failingUrl) { - super.onReceivedError(view, errorCode, description, failingUrl); - - try { - JSONObject obj = new JSONObject(); - obj.put("type", LOAD_ERROR_EVENT); - obj.put("url", failingUrl); - obj.put("code", errorCode); - obj.put("message", description); - - sendUpdate(obj, true, PluginResult.Status.ERROR); - } catch (JSONException ex) { - Log.d(LOG_TAG, "Should never happen"); - } - } - } -} diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppChromeClient.java b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppChromeClient.java deleted file mode 100644 index 37cf101f..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/amazon/InAppChromeClient.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. -*/ -package org.apache.cordova.inappbrowser; - -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.LOG; -import org.apache.cordova.PluginResult; -import org.json.JSONArray; -import org.json.JSONException; - -import com.amazon.android.webkit.AmazonWebChromeClient; -import com.amazon.android.webkit.AmazonGeolocationPermissions.Callback; -import com.amazon.android.webkit.AmazonJsPromptResult; -import com.amazon.android.webkit.AmazonWebStorage; -import com.amazon.android.webkit.AmazonWebView; -import com.amazon.android.webkit.AmazonWebViewClient; - -public class InAppChromeClient extends AmazonWebChromeClient { - - private CordovaWebView webView; - private String LOG_TAG = "InAppChromeClient"; - private long MAX_QUOTA = 100 * 1024 * 1024; - - public InAppChromeClient(CordovaWebView webView) { - super(); - this.webView = webView; - } - /** - * Handle database quota exceeded notification. - * - * @param url - * @param databaseIdentifier - * @param currentQuota - * @param estimatedSize - * @param totalUsedQuota - * @param quotaUpdater - */ - @Override - public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, - long totalUsedQuota, AmazonWebStorage.QuotaUpdater quotaUpdater) - { - LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); - - if (estimatedSize < MAX_QUOTA) - { - //increase for 1Mb - long newQuota = estimatedSize; - LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota); - quotaUpdater.updateQuota(newQuota); - } - else - { - // Set the quota to whatever it is and force an error - // TODO: get docs on how to handle this properly - quotaUpdater.updateQuota(currentQuota); - } - } - - /** - * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin. - * - * @param origin - * @param callback - */ - @Override - public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { - super.onGeolocationPermissionsShowPrompt(origin, callback); - callback.invoke(origin, true, false); - } - - /** - * Tell the client to display a prompt dialog to the user. - * If the client returns true, WebView will assume that the client will - * handle the prompt dialog and call the appropriate JsPromptResult method. - * - * The prompt bridge provided for the InAppBrowser is capable of executing any - * oustanding callback belonging to the InAppBrowser plugin. Care has been - * taken that other callbacks cannot be triggered, and that no other code - * execution is possible. - * - * To trigger the bridge, the prompt default value should be of the form: - * - * gap-iab://<callbackId> - * - * where <callbackId> is the string id of the callback to trigger (something - * like "InAppBrowser0123456789") - * - * If present, the prompt message is expected to be a JSON-encoded value to - * pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid. - * - * @param view - * @param url - * @param message - * @param defaultValue - * @param result - */ - @Override - public boolean onJsPrompt(AmazonWebView view, String url, String message, String defaultValue, AmazonJsPromptResult result) { - // See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute. - if (defaultValue != null && defaultValue.startsWith("gap")) { - if(defaultValue.startsWith("gap-iab://")) { - PluginResult scriptResult; - String scriptCallbackId = defaultValue.substring(10); - if (scriptCallbackId.startsWith("InAppBrowser")) { - if(message == null || message.length() == 0) { - scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray()); - } else { - try { - scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message)); - } catch(JSONException e) { - scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()); - } - } - this.webView.sendPluginResult(scriptResult, scriptCallbackId); - result.confirm(""); - return true; - } - } - else - { - // Anything else with a gap: prefix should get this message - LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue); - result.cancel(); - return true; - } - } - return false; - } - -} diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java index 8f4f3d97..b3e0e612 100644 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java @@ -19,12 +19,21 @@ package org.apache.cordova.inappbrowser; import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Parcelable; import android.provider.Browser; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Color; +import android.net.http.SslError; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -41,6 +50,12 @@ import android.view.inputmethod.InputMethodManager; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.HttpAuthHandler; +import android.webkit.JavascriptInterface; +import android.webkit.SslErrorHandler; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -49,6 +64,7 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; +import android.widget.TextView; import org.apache.cordova.CallbackContext; import org.apache.cordova.Config; @@ -65,6 +81,9 @@ import org.json.JSONObject; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.HashMap; import java.util.StringTokenizer; @@ -82,11 +101,27 @@ public class InAppBrowser extends CordovaPlugin { private static final String LOAD_START_EVENT = "loadstart"; private static final String LOAD_STOP_EVENT = "loadstop"; private static final String LOAD_ERROR_EVENT = "loaderror"; + private static final String MESSAGE_EVENT = "message"; private static final String CLEAR_ALL_CACHE = "clearcache"; private static final String CLEAR_SESSION_CACHE = "clearsessioncache"; private static final String HARDWARE_BACK_BUTTON = "hardwareback"; private static final String MEDIA_PLAYBACK_REQUIRES_USER_ACTION = "mediaPlaybackRequiresUserAction"; private static final String SHOULD_PAUSE = "shouldPauseOnSuspend"; + private static final Boolean DEFAULT_HARDWARE_BACK = true; + private static final String USER_WIDE_VIEW_PORT = "useWideViewPort"; + private static final String TOOLBAR_COLOR = "toolbarcolor"; + private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption"; + private static final String CLOSE_BUTTON_COLOR = "closebuttoncolor"; + private static final String LEFT_TO_RIGHT = "lefttoright"; + private static final String HIDE_NAVIGATION = "hidenavigationbuttons"; + private static final String NAVIGATION_COLOR = "navigationbuttoncolor"; + private static final String HIDE_URL = "hideurlbar"; + private static final String FOOTER = "footer"; + private static final String FOOTER_COLOR = "footercolor"; + private static final String BEFORELOAD = "beforeload"; + private static final String FULLSCREEN = "fullscreen"; + + private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR); private InAppBrowserDialog dialog; private WebView inAppWebView; @@ -100,6 +135,24 @@ public class InAppBrowser extends CordovaPlugin { private boolean hadwareBackButton = true; private boolean mediaPlaybackRequiresUserGesture = false; private boolean shouldPauseInAppBrowser = false; + private boolean useWideViewPort = true; + private ValueCallback<Uri> mUploadCallback; + private ValueCallback<Uri[]> mUploadCallbackLollipop; + private final static int FILECHOOSER_REQUESTCODE = 1; + private final static int FILECHOOSER_REQUESTCODE_LOLLIPOP = 2; + private String closeButtonCaption = ""; + private String closeButtonColor = ""; + private boolean leftToRight = false; + private int toolbarColor = android.graphics.Color.LTGRAY; + private boolean hideNavigationButtons = false; + private String navigationButtonColor = ""; + private boolean hideUrlBar = false; + private boolean showFooter = false; + private String footerColor = ""; + private String beforeload = ""; + private boolean fullscreen = true; + private String[] allowedSchemes; + private InAppBrowserClient currentClient; /** * Executes the request and returns PluginResult. @@ -118,7 +171,7 @@ public class InAppBrowser extends CordovaPlugin { t = SELF; } final String target = t; - final HashMap<String, Boolean> features = parseFeature(args.optString(2)); + final HashMap<String, String> features = parseFeature(args.optString(2)); LOG.d(LOG_TAG, "target = " + target); @@ -207,6 +260,25 @@ public class InAppBrowser extends CordovaPlugin { else if (action.equals("close")) { closeDialog(); } + else if (action.equals("loadAfterBeforeload")) { + if (beforeload == null) { + LOG.e(LOG_TAG, "unexpected loadAfterBeforeload called without feature beforeload=yes"); + } + final String url = args.getString(0); + this.cordova.getActivity().runOnUiThread(new Runnable() { + @SuppressLint("NewApi") + @Override + public void run() { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { + currentClient.waitForBeforeload = false; + inAppWebView.setWebViewClient(currentClient); + } else { + ((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false; + } + inAppWebView.loadUrl(url); + } + }); + } else if (action.equals("injectScriptCode")) { String jsWrapper = null; if (args.getBoolean(1)) { @@ -245,7 +317,22 @@ public class InAppBrowser extends CordovaPlugin { this.cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { - dialog.show(); + if (dialog != null && !cordova.getActivity().isFinishing()) { + dialog.show(); + } + } + }); + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); + pluginResult.setKeepCallback(true); + this.callbackContext.sendPluginResult(pluginResult); + } + else if (action.equals("hide")) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + if (dialog != null && !cordova.getActivity().isFinishing()) { + dialog.hide(); + } } }); PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); @@ -311,29 +398,33 @@ public class InAppBrowser extends CordovaPlugin { * which should be executed directly. */ private void injectDeferredObject(String source, String jsWrapper) { - String scriptToInject; - if (jsWrapper != null) { - org.json.JSONArray jsonEsc = new org.json.JSONArray(); - jsonEsc.put(source); - String jsonRepr = jsonEsc.toString(); - String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1); - scriptToInject = String.format(jsWrapper, jsonSourceString); + if (inAppWebView!=null) { + String scriptToInject; + if (jsWrapper != null) { + org.json.JSONArray jsonEsc = new org.json.JSONArray(); + jsonEsc.put(source); + String jsonRepr = jsonEsc.toString(); + String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1); + scriptToInject = String.format(jsWrapper, jsonSourceString); + } else { + scriptToInject = source; + } + final String finalScriptToInject = scriptToInject; + this.cordova.getActivity().runOnUiThread(new Runnable() { + @SuppressLint("NewApi") + @Override + public void run() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + // This action will have the side-effect of blurring the currently focused element + inAppWebView.loadUrl("javascript:" + finalScriptToInject); + } else { + inAppWebView.evaluateJavascript(finalScriptToInject, null); + } + } + }); } else { - scriptToInject = source; + LOG.d(LOG_TAG, "Can't inject code into the system browser"); } - final String finalScriptToInject = scriptToInject; - this.cordova.getActivity().runOnUiThread(new Runnable() { - @SuppressLint("NewApi") - @Override - public void run() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - // This action will have the side-effect of blurring the currently focused element - inAppWebView.loadUrl("javascript:" + finalScriptToInject); - } else { - inAppWebView.evaluateJavascript(finalScriptToInject, null); - } - } - }); } /** @@ -342,18 +433,21 @@ public class InAppBrowser extends CordovaPlugin { * @param optString * @return */ - private HashMap<String, Boolean> parseFeature(String optString) { + private HashMap<String, String> parseFeature(String optString) { if (optString.equals(NULL)) { return null; } else { - HashMap<String, Boolean> map = new HashMap<String, Boolean>(); + HashMap<String, String> map = new HashMap<String, String>(); StringTokenizer features = new StringTokenizer(optString, ","); StringTokenizer option; while(features.hasMoreElements()) { option = new StringTokenizer(features.nextToken(), "="); if (option.hasMoreElements()) { String key = option.nextToken(); - Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE; + String value = option.nextToken(); + if (!customizableOptions.contains(key)) { + value = value.equals("yes") || value.equals("no") ? value : "yes"; + } map.put(key, value); } } @@ -380,15 +474,57 @@ public class InAppBrowser extends CordovaPlugin { intent.setData(uri); } intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName()); - this.cordova.getActivity().startActivity(intent); + // CB-10795: Avoid circular loops by preventing it from opening in the current app + this.openExternalExcludeCurrentApp(intent); return ""; - } catch (android.content.ActivityNotFoundException e) { + // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it + } catch (java.lang.RuntimeException e) { LOG.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString()); return e.toString(); } } /** + * Opens the intent, providing a chooser that excludes the current app to avoid + * circular loops. + */ + private void openExternalExcludeCurrentApp(Intent intent) { + String currentPackage = cordova.getActivity().getPackageName(); + boolean hasCurrentPackage = false; + + PackageManager pm = cordova.getActivity().getPackageManager(); + List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0); + ArrayList<Intent> targetIntents = new ArrayList<Intent>(); + + for (ResolveInfo ri : activities) { + if (!currentPackage.equals(ri.activityInfo.packageName)) { + Intent targetIntent = (Intent)intent.clone(); + targetIntent.setPackage(ri.activityInfo.packageName); + targetIntents.add(targetIntent); + } + else { + hasCurrentPackage = true; + } + } + + // If the current app package isn't a target for this URL, then use + // the normal launch behavior + if (hasCurrentPackage == false || targetIntents.size() == 0) { + this.cordova.getActivity().startActivity(intent); + } + // If there's only one possible intent, launch it directly + else if (targetIntents.size() == 1) { + this.cordova.getActivity().startActivity(targetIntents.get(0)); + } + // Otherwise, show a custom chooser without the current app listed + else if (targetIntents.size() > 0) { + Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null); + chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {})); + this.cordova.getActivity().startActivity(chooser); + } + } + + /** * Closes the dialog */ public void closeDialog() { @@ -405,7 +541,7 @@ public class InAppBrowser extends CordovaPlugin { childView.setWebViewClient(new WebViewClient() { // NB: wait for about:blank before dismissing public void onPageFinished(WebView view, String url) { - if (dialog != null) { + if (dialog != null && !cordova.getActivity().isFinishing()) { dialog.dismiss(); dialog = null; } @@ -488,7 +624,7 @@ public class InAppBrowser extends CordovaPlugin { return this.showLocationBar; } - private InAppBrowser getInAppBrowser(){ + private InAppBrowser getInAppBrowser() { return this; } @@ -498,7 +634,7 @@ public class InAppBrowser extends CordovaPlugin { * @param url the url to load. * @param features jsonObject */ - public String showWebPage(final String url, HashMap<String, Boolean> features) { + public String showWebPage(final String url, HashMap<String, String> features) { // Determine if we should hide the location bar. showLocationBar = true; showZoomControls = true; @@ -506,38 +642,84 @@ public class InAppBrowser extends CordovaPlugin { mediaPlaybackRequiresUserGesture = false; if (features != null) { - Boolean show = features.get(LOCATION); + String show = features.get(LOCATION); if (show != null) { - showLocationBar = show.booleanValue(); + showLocationBar = show.equals("yes") ? true : false; + } + if(showLocationBar) { + String hideNavigation = features.get(HIDE_NAVIGATION); + String hideUrl = features.get(HIDE_URL); + if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes") ? true : false; + if(hideUrl != null) hideUrlBar = hideUrl.equals("yes") ? true : false; } - Boolean zoom = features.get(ZOOM); + String zoom = features.get(ZOOM); if (zoom != null) { - showZoomControls = zoom.booleanValue(); + showZoomControls = zoom.equals("yes") ? true : false; } - Boolean hidden = features.get(HIDDEN); + String hidden = features.get(HIDDEN); if (hidden != null) { - openWindowHidden = hidden.booleanValue(); + openWindowHidden = hidden.equals("yes") ? true : false; } - Boolean hardwareBack = features.get(HARDWARE_BACK_BUTTON); + String hardwareBack = features.get(HARDWARE_BACK_BUTTON); if (hardwareBack != null) { - hadwareBackButton = hardwareBack.booleanValue(); + hadwareBackButton = hardwareBack.equals("yes") ? true : false; + } else { + hadwareBackButton = DEFAULT_HARDWARE_BACK; } - Boolean mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION); + String mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION); if (mediaPlayback != null) { - mediaPlaybackRequiresUserGesture = mediaPlayback.booleanValue(); + mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false; } - Boolean cache = features.get(CLEAR_ALL_CACHE); + String cache = features.get(CLEAR_ALL_CACHE); if (cache != null) { - clearAllCache = cache.booleanValue(); + clearAllCache = cache.equals("yes") ? true : false; } else { cache = features.get(CLEAR_SESSION_CACHE); if (cache != null) { - clearSessionCache = cache.booleanValue(); + clearSessionCache = cache.equals("yes") ? true : false; } } - Boolean shouldPause = features.get(SHOULD_PAUSE); + String shouldPause = features.get(SHOULD_PAUSE); if (shouldPause != null) { - shouldPauseInAppBrowser = shouldPause.booleanValue(); + shouldPauseInAppBrowser = shouldPause.equals("yes") ? true : false; + } + String wideViewPort = features.get(USER_WIDE_VIEW_PORT); + if (wideViewPort != null ) { + useWideViewPort = wideViewPort.equals("yes") ? true : false; + } + String closeButtonCaptionSet = features.get(CLOSE_BUTTON_CAPTION); + if (closeButtonCaptionSet != null) { + closeButtonCaption = closeButtonCaptionSet; + } + String closeButtonColorSet = features.get(CLOSE_BUTTON_COLOR); + if (closeButtonColorSet != null) { + closeButtonColor = closeButtonColorSet; + } + String leftToRightSet = features.get(LEFT_TO_RIGHT); + leftToRight = leftToRightSet != null && leftToRightSet.equals("yes"); + + String toolbarColorSet = features.get(TOOLBAR_COLOR); + if (toolbarColorSet != null) { + toolbarColor = android.graphics.Color.parseColor(toolbarColorSet); + } + String navigationButtonColorSet = features.get(NAVIGATION_COLOR); + if (navigationButtonColorSet != null) { + navigationButtonColor = navigationButtonColorSet; + } + String showFooterSet = features.get(FOOTER); + if (showFooterSet != null) { + showFooter = showFooterSet.equals("yes") ? true : false; + } + String footerColorSet = features.get(FOOTER_COLOR); + if (footerColorSet != null) { + footerColor = footerColorSet; + } + if (features.get(BEFORELOAD) != null) { + beforeload = features.get(BEFORELOAD); + } + String fullscreenSet = features.get(FULLSCREEN); + if (fullscreenSet != null) { + fullscreen = fullscreenSet.equals("yes") ? true : false; } } @@ -552,13 +734,61 @@ public class InAppBrowser extends CordovaPlugin { */ private int dpToPixels(int dipValue) { int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, - (float) dipValue, - cordova.getActivity().getResources().getDisplayMetrics() + (float) dipValue, + cordova.getActivity().getResources().getDisplayMetrics() ); return value; } + private View createCloseButton(int id) { + View _close; + Resources activityRes = cordova.getActivity().getResources(); + + if (closeButtonCaption != "") { + // Use TextView for text + TextView close = new TextView(cordova.getActivity()); + close.setText(closeButtonCaption); + close.setTextSize(20); + if (closeButtonColor != "") close.setTextColor(android.graphics.Color.parseColor(closeButtonColor)); + close.setGravity(android.view.Gravity.CENTER_VERTICAL); + close.setPadding(this.dpToPixels(10), 0, this.dpToPixels(10), 0); + _close = close; + } + else { + ImageButton close = new ImageButton(cordova.getActivity()); + int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName()); + Drawable closeIcon = activityRes.getDrawable(closeResId); + if (closeButtonColor != "") close.setColorFilter(android.graphics.Color.parseColor(closeButtonColor)); + close.setImageDrawable(closeIcon); + close.setScaleType(ImageView.ScaleType.FIT_CENTER); + if (Build.VERSION.SDK_INT >= 16) + close.getAdjustViewBounds(); + + _close = close; + } + + RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + if (leftToRight) closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + else closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + _close.setLayoutParams(closeLayoutParams); + + if (Build.VERSION.SDK_INT >= 16) + _close.setBackground(null); + else + _close.setBackgroundDrawable(null); + + _close.setContentDescription("Close Button"); + _close.setId(Integer.valueOf(id)); + _close.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + closeDialog(); + } + }); + + return _close; + } + @SuppressLint("NewApi") public void run() { @@ -571,6 +801,9 @@ public class InAppBrowser extends CordovaPlugin { dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar); dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog; dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + if (fullscreen) { + dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + } dialog.setCancelable(true); dialog.setInAppBroswer(getInAppBrowser()); @@ -581,18 +814,25 @@ public class InAppBrowser extends CordovaPlugin { // Toolbar layout RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); //Please, no more black! - toolbar.setBackgroundColor(android.graphics.Color.LTGRAY); + toolbar.setBackgroundColor(toolbarColor); toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44))); toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); - toolbar.setHorizontalGravity(Gravity.LEFT); + if (leftToRight) { + toolbar.setHorizontalGravity(Gravity.LEFT); + } else { + toolbar.setHorizontalGravity(Gravity.RIGHT); + } toolbar.setVerticalGravity(Gravity.TOP); // Action Button Container layout RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity()); - actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); + RelativeLayout.LayoutParams actionButtonLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + if (leftToRight) actionButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + else actionButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + actionButtonContainer.setLayoutParams(actionButtonLayoutParams); actionButtonContainer.setHorizontalGravity(Gravity.LEFT); actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); - actionButtonContainer.setId(Integer.valueOf(1)); + actionButtonContainer.setId(leftToRight ? Integer.valueOf(5) : Integer.valueOf(1)); // Back button ImageButton back = new ImageButton(cordova.getActivity()); @@ -604,6 +844,7 @@ public class InAppBrowser extends CordovaPlugin { Resources activityRes = cordova.getActivity().getResources(); int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName()); Drawable backIcon = activityRes.getDrawable(backResId); + if (navigationButtonColor != "") back.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor)); if (Build.VERSION.SDK_INT >= 16) back.setBackground(null); else @@ -629,6 +870,7 @@ public class InAppBrowser extends CordovaPlugin { forward.setId(Integer.valueOf(3)); int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName()); Drawable fwdIcon = activityRes.getDrawable(fwdResId); + if (navigationButtonColor != "") forward.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor)); if (Build.VERSION.SDK_INT >= 16) forward.setBackground(null); else @@ -661,53 +903,112 @@ public class InAppBrowser extends CordovaPlugin { public boolean onKey(View v, int keyCode, KeyEvent event) { // If the event is a key-down event on the "enter" button if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { - navigate(edittext.getText().toString()); - return true; + navigate(edittext.getText().toString()); + return true; } return false; } }); - // Close/Done button - ImageButton close = new ImageButton(cordova.getActivity()); - RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); - closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); - close.setLayoutParams(closeLayoutParams); - forward.setContentDescription("Close Button"); - close.setId(Integer.valueOf(5)); - int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName()); - Drawable closeIcon = activityRes.getDrawable(closeResId); - if (Build.VERSION.SDK_INT >= 16) - close.setBackground(null); - else - close.setBackgroundDrawable(null); - close.setImageDrawable(closeIcon); - close.setScaleType(ImageView.ScaleType.FIT_CENTER); - back.setPadding(0, this.dpToPixels(10), 0, this.dpToPixels(10)); - if (Build.VERSION.SDK_INT >= 16) - close.getAdjustViewBounds(); - close.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - closeDialog(); - } - }); + // Header Close/Done button + int closeButtonId = leftToRight ? 1 : 5; + View close = createCloseButton(closeButtonId); + toolbar.addView(close); + + // Footer + RelativeLayout footer = new RelativeLayout(cordova.getActivity()); + int _footerColor; + if(footerColor != "") { + _footerColor = Color.parseColor(footerColor); + } else { + _footerColor = android.graphics.Color.LTGRAY; + } + footer.setBackgroundColor(_footerColor); + RelativeLayout.LayoutParams footerLayout = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)); + footerLayout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE); + footer.setLayoutParams(footerLayout); + if (closeButtonCaption != "") footer.setPadding(this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8)); + footer.setHorizontalGravity(Gravity.LEFT); + footer.setVerticalGravity(Gravity.BOTTOM); + + View footerClose = createCloseButton(7); + footer.addView(footerClose); + // WebView inAppWebView = new WebView(cordova.getActivity()); inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); inAppWebView.setId(Integer.valueOf(6)); - inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView)); - WebViewClient client = new InAppBrowserClient(thatWebView, edittext); - inAppWebView.setWebViewClient(client); + // File Chooser Implemented ChromeClient + inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView) { + // For Android 5.0+ + public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) + { + LOG.d(LOG_TAG, "File Chooser 5.0+"); + // If callback exists, finish it. + if(mUploadCallbackLollipop != null) { + mUploadCallbackLollipop.onReceiveValue(null); + } + mUploadCallbackLollipop = filePathCallback; + + // Create File Chooser Intent + Intent content = new Intent(Intent.ACTION_GET_CONTENT); + content.addCategory(Intent.CATEGORY_OPENABLE); + content.setType("*/*"); + + // Run cordova startActivityForResult + cordova.startActivityForResult(InAppBrowser.this, Intent.createChooser(content, "Select File"), FILECHOOSER_REQUESTCODE_LOLLIPOP); + return true; + } + + // For Android 4.1+ + public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) + { + LOG.d(LOG_TAG, "File Chooser 4.1+"); + // Call file chooser for Android 3.0+ + openFileChooser(uploadMsg, acceptType); + } + + // For Android 3.0+ + public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) + { + LOG.d(LOG_TAG, "File Chooser 3.0+"); + mUploadCallback = uploadMsg; + Intent content = new Intent(Intent.ACTION_GET_CONTENT); + content.addCategory(Intent.CATEGORY_OPENABLE); + + // run startActivityForResult + cordova.startActivityForResult(InAppBrowser.this, Intent.createChooser(content, "Select File"), FILECHOOSER_REQUESTCODE); + } + + }); + currentClient = new InAppBrowserClient(thatWebView, edittext, beforeload); + inAppWebView.setWebViewClient(currentClient); WebSettings settings = inAppWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setBuiltInZoomControls(showZoomControls); settings.setPluginState(android.webkit.WebSettings.PluginState.ON); + // Add postMessage interface + class JsObject { + @JavascriptInterface + public void postMessage(String data) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", MESSAGE_EVENT); + obj.put("data", new JSONObject(data)); + sendUpdate(obj, true); + } catch (JSONException ex) { + LOG.e(LOG_TAG, "data object passed to postMessage has caused a JSON error."); + } + } + } + if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { settings.setMediaPlaybackRequiresUserGesture(mediaPlaybackRequiresUserGesture); + inAppWebView.addJavascriptInterface(new JsObject(), "cordova_iab"); } String overrideUserAgent = preferences.getString("OverrideUserAgent", null); @@ -736,10 +1037,15 @@ public class InAppBrowser extends CordovaPlugin { CookieManager.getInstance().removeSessionCookie(); } + // Enable Thirdparty Cookies on >=Android 5.0 device + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().setAcceptThirdPartyCookies(inAppWebView,true); + } + inAppWebView.loadUrl(url); inAppWebView.setId(Integer.valueOf(6)); inAppWebView.getSettings().setLoadWithOverviewMode(true); - inAppWebView.getSettings().setUseWideViewPort(true); + inAppWebView.getSettings().setUseWideViewPort(useWideViewPort); inAppWebView.requestFocus(); inAppWebView.requestFocusFromTouch(); @@ -747,10 +1053,9 @@ public class InAppBrowser extends CordovaPlugin { actionButtonContainer.addView(back); actionButtonContainer.addView(forward); - // Add the views to our toolbar - toolbar.addView(actionButtonContainer); - toolbar.addView(edittext); - toolbar.addView(close); + // Add the views to our toolbar if they haven't been disabled + if (!hideNavigationButtons) toolbar.addView(actionButtonContainer); + if (!hideUrlBar) toolbar.addView(edittext); // Don't add the toolbar if its been disabled if (getShowLocationBar()) { @@ -759,19 +1064,28 @@ public class InAppBrowser extends CordovaPlugin { } // Add our webview to our main view/layout - main.addView(inAppWebView); + RelativeLayout webViewLayout = new RelativeLayout(cordova.getActivity()); + webViewLayout.addView(inAppWebView); + main.addView(webViewLayout); + + // Don't add the footer unless it's been enabled + if (showFooter) { + webViewLayout.addView(footer); + } WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); lp.copyFrom(dialog.getWindow().getAttributes()); lp.width = WindowManager.LayoutParams.MATCH_PARENT; lp.height = WindowManager.LayoutParams.MATCH_PARENT; - dialog.setContentView(main); - dialog.show(); - dialog.getWindow().setAttributes(lp); + if (dialog != null) { + dialog.setContentView(main); + dialog.show(); + dialog.getWindow().setAttributes(lp); + } // the goal of openhidden is to load the url and not display it // Show() needs to be called to cause the URL to be loaded - if(openWindowHidden) { + if (openWindowHidden && dialog != null) { dialog.hide(); } } @@ -807,11 +1121,49 @@ public class InAppBrowser extends CordovaPlugin { } /** + * Receive File Data from File Chooser + * + * @param requestCode the requested code from chromeclient + * @param resultCode the result code returned from android system + * @param intent the data from android file chooser + */ + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + // For Android >= 5.0 + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + LOG.d(LOG_TAG, "onActivityResult (For Android >= 5.0)"); + // If RequestCode or Callback is Invalid + if(requestCode != FILECHOOSER_REQUESTCODE_LOLLIPOP || mUploadCallbackLollipop == null) { + super.onActivityResult(requestCode, resultCode, intent); + return; + } + mUploadCallbackLollipop.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent)); + mUploadCallbackLollipop = null; + } + // For Android < 5.0 + else { + LOG.d(LOG_TAG, "onActivityResult (For Android < 5.0)"); + // If RequestCode or Callback is Invalid + if(requestCode != FILECHOOSER_REQUESTCODE || mUploadCallback == null) { + super.onActivityResult(requestCode, resultCode, intent); + return; + } + + if (null == mUploadCallback) return; + Uri result = intent == null || resultCode != cordova.getActivity().RESULT_OK ? null : intent.getData(); + + mUploadCallback.onReceiveValue(result); + mUploadCallback = null; + } + } + + /** * The webview client receives notifications about appView */ public class InAppBrowserClient extends WebViewClient { EditText edittext; CordovaWebView webView; + String beforeload; + boolean waitForBeforeload; /** * Constructor. @@ -819,27 +1171,97 @@ public class InAppBrowser extends CordovaPlugin { * @param webView * @param mEditText */ - public InAppBrowserClient(CordovaWebView webView, EditText mEditText) { + public InAppBrowserClient(CordovaWebView webView, EditText mEditText, String beforeload) { this.webView = webView; this.edittext = mEditText; + this.beforeload = beforeload; + this.waitForBeforeload = beforeload != null; } /** * Override the URL that should be loaded * - * This handles a small subset of all the URIs that would be encountered. + * Legacy (deprecated in API 24) + * For Android 6 and below. * * @param webView * @param url */ + @SuppressWarnings("deprecation") @Override public boolean shouldOverrideUrlLoading(WebView webView, String url) { + return shouldOverrideUrlLoading(url, null); + } + + /** + * Override the URL that should be loaded + * + * New (added in API 24) + * For Android 7 and above. + * + * @param webView + * @param request + */ + @TargetApi(Build.VERSION_CODES.N) + @Override + public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request) { + return shouldOverrideUrlLoading(request.getUrl().toString(), request.getMethod()); + } + + /** + * Override the URL that should be loaded + * + * This handles a small subset of all the URIs that would be encountered. + * + * @param url + * @param method + */ + public boolean shouldOverrideUrlLoading(String url, String method) { + boolean override = false; + boolean useBeforeload = false; + String errorMessage = null; + + if (beforeload.equals("yes") && method == null) { + useBeforeload = true; + } else if(beforeload.equals("yes") + //TODO handle POST requests then this condition can be removed: + && !method.equals("POST")) + { + useBeforeload = true; + } else if(beforeload.equals("get") && (method == null || method.equals("GET"))) { + useBeforeload = true; + } else if(beforeload.equals("post") && (method == null || method.equals("POST"))) { + //TODO handle POST requests + errorMessage = "beforeload doesn't yet support POST requests"; + } + + // On first URL change, initiate JS callback. Only after the beforeload event, continue. + if (useBeforeload && this.waitForBeforeload) { + if(sendBeforeLoad(url, method)) { + return true; + } + } + + if(errorMessage != null) { + try { + LOG.e(LOG_TAG, errorMessage); + JSONObject obj = new JSONObject(); + obj.put("type", LOAD_ERROR_EVENT); + obj.put("url", url); + obj.put("code", -1); + obj.put("message", errorMessage); + sendUpdate(obj, true, PluginResult.Status.ERROR); + } catch(Exception e) { + LOG.e(LOG_TAG, "Error sending loaderror for " + url + ": " + e.toString()); + } + } + if (url.startsWith(WebView.SCHEME_TEL)) { try { Intent intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(url)); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); } @@ -848,7 +1270,7 @@ public class InAppBrowser extends CordovaPlugin { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString()); } @@ -879,15 +1301,89 @@ public class InAppBrowser extends CordovaPlugin { intent.putExtra("address", address); intent.setType("vnd.android-dir/mms-sms"); cordova.getActivity().startActivity(intent); - return true; + override = true; } catch (android.content.ActivityNotFoundException e) { LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); } } + // Test for whitelisted custom scheme names like mycoolapp:// or twitteroauthresponse:// (Twitter Oauth Response) + else if (!url.startsWith("http:") && !url.startsWith("https:") && url.matches("^[A-Za-z0-9+.-]*://.*?$")) { + if (allowedSchemes == null) { + String allowed = preferences.getString("AllowedSchemes", null); + if(allowed != null) { + allowedSchemes = allowed.split(","); + } + } + if (allowedSchemes != null) { + for (String scheme : allowedSchemes) { + if (url.startsWith(scheme)) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", "customscheme"); + obj.put("url", url); + sendUpdate(obj, true); + override = true; + } catch (JSONException ex) { + LOG.e(LOG_TAG, "Custom Scheme URI passed in has caused a JSON error."); + } + } + } + } + } + + if (useBeforeload) { + this.waitForBeforeload = true; + } + return override; + } + + private boolean sendBeforeLoad(String url, String method) { + try { + JSONObject obj = new JSONObject(); + obj.put("type", BEFORELOAD); + obj.put("url", url); + if(method != null) { + obj.put("method", method); + } + sendUpdate(obj, true); + return true; + } catch (JSONException ex) { + LOG.e(LOG_TAG, "URI passed in has caused a JSON error."); + } return false; } + /** + * Legacy (deprecated in API 21) + * For Android 4.4 and below. + * @param view + * @param url + * @return + */ + @SuppressWarnings("deprecation") + @Override + public WebResourceResponse shouldInterceptRequest (final WebView view, String url) { + return shouldInterceptRequest(url, super.shouldInterceptRequest(view, url), null); + } + + /** + * New (added in API 21) + * For Android 5.0 and above. + * + * @param webView + * @param request + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + return shouldInterceptRequest(request.getUrl().toString(), super.shouldInterceptRequest(view, request), request.getMethod()); + } + + public WebResourceResponse shouldInterceptRequest(String url, WebResourceResponse response, String method) { + return response; + } + /* * onPageStarted fires the LOAD_START_EVENT * @@ -913,7 +1409,7 @@ public class InAppBrowser extends CordovaPlugin { // Update the UI if we haven't already if (!newloc.equals(edittext.getText().toString())) { edittext.setText(newloc); - } + } try { JSONObject obj = new JSONObject(); @@ -925,11 +1421,14 @@ public class InAppBrowser extends CordovaPlugin { } } - - public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); + // Set the namespace for postMessage() + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + injectDeferredObject("window.webkit={messageHandlers:{cordova_iab:cordova_iab}}", null); + } + // CB-10395 InAppBrowser's WebView not storing cookies reliable to local device storage if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { CookieManager.getInstance().flush(); @@ -937,6 +1436,10 @@ public class InAppBrowser extends CordovaPlugin { CookieSyncManager.getInstance().sync(); } + // https://issues.apache.org/jira/browse/CB-11248 + view.clearFocus(); + view.requestFocus(); + try { JSONObject obj = new JSONObject(); obj.put("type", LOAD_STOP_EVENT); @@ -964,6 +1467,46 @@ public class InAppBrowser extends CordovaPlugin { } } + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + super.onReceivedSslError(view, handler, error); + try { + JSONObject obj = new JSONObject(); + obj.put("type", LOAD_ERROR_EVENT); + obj.put("url", error.getUrl()); + obj.put("code", 0); + obj.put("sslerror", error.getPrimaryError()); + String message; + switch (error.getPrimaryError()) { + case SslError.SSL_DATE_INVALID: + message = "The date of the certificate is invalid"; + break; + case SslError.SSL_EXPIRED: + message = "The certificate has expired"; + break; + case SslError.SSL_IDMISMATCH: + message = "Hostname mismatch"; + break; + default: + case SslError.SSL_INVALID: + message = "A generic error occurred"; + break; + case SslError.SSL_NOTYETVALID: + message = "The certificate is not yet valid"; + break; + case SslError.SSL_UNTRUSTED: + message = "The certificate authority is not trusted"; + break; + } + obj.put("message", message); + + sendUpdate(obj, true, PluginResult.Status.ERROR); + } catch (JSONException ex) { + LOG.d(LOG_TAG, "Should never happen"); + } + handler.cancel(); + } + /** * On received http auth request. */ diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppChromeClient.java b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppChromeClient.java index a2145e6a..fe5dd349 100644 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppChromeClient.java +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppChromeClient.java @@ -104,7 +104,7 @@ public class InAppChromeClient extends WebChromeClient { if(defaultValue.startsWith("gap-iab://")) { PluginResult scriptResult; String scriptCallbackId = defaultValue.substring(10); - if (scriptCallbackId.startsWith("InAppBrowser")) { + if (scriptCallbackId.matches("^InAppBrowser[0-9]{1,10}$")) { if(message == null || message.length() == 0) { scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray()); } else { @@ -118,9 +118,14 @@ public class InAppChromeClient extends WebChromeClient { result.confirm(""); return true; } + else { + // Anything else that doesn't look like InAppBrowser0123456789 should end up here + LOG.w(LOG_TAG, "InAppBrowser callback called with invalid callbackId : "+ scriptCallbackId); + result.cancel(); + return true; + } } - else - { + else { // Anything else with a gap: prefix should get this message LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue); result.cancel(); diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/README.md deleted file mode 100644 index f0fa8607..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/README.md +++ /dev/null @@ -1,43 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> -# BlackBerry 10 In-App-Browser Plugin - -The in app browser functionality is entirely contained within common js. There is no native implementation required. -To install this plugin, follow the [Command-line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -If you are not using the Cordova Command-line Interface, follow [Using Plugman to Manage Plugins](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). -./cordova-plugin-battery-status/README.md -./cordova-plugin-camera/README.md -./cordova-plugin-console/README.md -./cordova-plugin-contacts/README.md -./cordova-plugin-device/README.md -./cordova-plugin-device-motion/README.md -./cordova-plugin-device-orientation/README.md -./cordova-plugin-device-orientation/src/blackberry10/README.md -./cordova-plugin-file/README.md -./cordova-plugin-file-transfer/README.md -./cordova-plugin-geolocation/README.md -./cordova-plugin-globalization/README.md -./cordova-plugin-inappbrowser/README.md -./cordova-plugin-inappbrowser/src/blackberry10/README.md -./cordova-plugin-media/README.md -./cordova-plugin-media-capture/README.md -./cordova-plugin-network-information/README.md -./cordova-plugin-splashscreen/README.md -./cordova-plugin-vibration/README.md diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/de/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/de/README.md deleted file mode 100644 index e3944876..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/de/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10-In-App-Browser-Plugin - -Die Funktionalität ist im app-Browser vollständig in gemeinsamen Js enthalten. Es gibt keine native Implementierung benötigt. Um dieses Plugin zu installieren, folgen Sie dem [Command-Line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -Wenn Sie nicht die Cordova-Befehlszeilenschnittstelle verwenden, folgen Sie [Verwenden Plugman zu Plugins verwalten](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/es/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/es/README.md deleted file mode 100644 index 75303369..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/es/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10 In-App-Browser Plugin - -El en el navegador de aplicación funcionalidad está enteramente dentro de js común. No hay ninguna aplicación nativa necesaria. Para instalar este plugin, siga la [Guía de la interfaz de línea de comandos](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -Si no utiliza la interfaz de línea de comandos de Cordova, siga [Usando Plugman para gestionar Plugins](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/fr/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/fr/README.md deleted file mode 100644 index 179bd483..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/fr/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10 In-App-Browser Plugin - -Le dans le navigateur de l'application, la fonctionnalité est entièrement contenue dans js commun. Il n'y a aucune implémentation native requise. Pour installer ce plugin, suivez le [Guide de l'Interface de ligne de commande](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -Si vous n'utilisez pas l'Interface de ligne de commande de Cordova, suivez [Les Plugman à l'aide à gérer les Plugins](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/it/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/it/README.md deleted file mode 100644 index 8f0623df..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/it/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10 In-App-Browser Plugin - -Il browser app funzionalità è interamente contenuta nel comune js. Non esiste alcuna implementazione nativa richiesto. Per installare questo plugin, seguire la [Guida per l'interfaccia della riga di comando](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -Se non si utilizza l'interfaccia della riga di comando di Cordova, seguire [Utilizzando Plugman per gestire i plugin](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ja/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ja/README.md deleted file mode 100644 index b9e4b7b7..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ja/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10 In-App-Browser Plugin - -アプリケーション ブラウザーの機能は全く一般的な js に含まれています。 ネイティブ実装する必要はありません。 このプラグインをインストールするには[コマンド ライン インターフェイス ガイド](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -コルドバのコマンド ライン インターフェイスを使用していない場合は場合、[管理のプラグインを使用して Plugman](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html)に従ってください。 ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ko/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ko/README.md deleted file mode 100644 index 67fb8de3..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/ko/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# 블랙베리 10 애플 리 케이 션-브라우저 플러그인 - -응용 프로그램 브라우저에서 기능은 완전히 포함 된 일반적인 js. 필요 없는 기본 구현이입니다. 이 플러그인을 설치 하려면 [명령줄 인터페이스 가이드](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface) 를 따라합니다. - -코르도바 명령줄 인터페이스를 사용 하지 않는 경우 [관리 플러그인을 사용 하 여 Plugman](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html)를 따르십시오. ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/pl/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/pl/README.md deleted file mode 100644 index ef199ee9..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/pl/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# BlackBerry 10 In-App-Browser Plugin - -W aplikacji Przeglądarka funkcjonalność jest całkowicie zawarty w wspólnej js. Tam jest nie native wdrażania wymagane. Aby zainstalować ten plugin, następować po ten [Przewodnik interfejsu wiersza polecenia](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -Jeśli nie używasz interfejsu wiersza polecenia Cordova, następować po [Przy użyciu Plugman do zarządzania wtyczki](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html). ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/zh/README.md b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/zh/README.md deleted file mode 100644 index 241fb550..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/blackberry10/doc/zh/README.md +++ /dev/null @@ -1,24 +0,0 @@ -<!--- - license: Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. ---> - -# 黑莓 10 的應用程式瀏覽器外掛程式 - -在應用程式瀏覽器功能完全包含在常見的 js。 還有沒有本機的實施所需。 若要安裝此外掛程式,請按照[命令列介面指南](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface). - -如果你不使用的科爾多瓦命令列介面,請按照[使用 Plugman 管理外掛程式](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html)。 ./cordova-plugin-battery-status/README.md ./cordova-plugin-camera/README.md ./cordova-plugin-console/README.md ./cordova-plugin-contacts/README.md ./cordova-plugin-device/README.md ./cordova-plugin-device-motion/README.md ./cordova-plugin-device-orientation/README.md ./cordova-plugin-device-orientation/src/blackberry10/README.md ./cordova-plugin-file/README.md ./cordova-plugin-file-transfer/README.md ./cordova-plugin-geolocation/README.md ./cordova-plugin-globalization/README.md ./cordova-plugin-inappbrowser/README.md ./cordova-plugin-inappbrowser/src/blackberry10/README.md ./cordova-plugin-media/README.md ./cordova-plugin-media-capture/README.md ./cordova-plugin-network-information/README.md ./cordova-plugin-splashscreen/README.md ./cordova-plugin-vibration/README.md
\ No newline at end of file diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js index da7dacd3..1c62574b 100644 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/browser/InAppBrowserProxy.js @@ -29,27 +29,43 @@ var browserWrap, forwardButton, closeButton; -function attachNavigationEvents(element, callback) { +function attachNavigationEvents (element, callback) { var onError = function () { - callback({ type: "loaderror", url: this.contentWindow.location}, {keepCallback: true}); + try { + callback({ type: 'loaderror', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } catch (err) { + // blocked by CORS :\ + callback({ type: 'loaderror', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } }; - element.addEventListener("pageshow", function () { - callback({ type: "loadstart", url: this.contentWindow.location}, {keepCallback: true}); + element.addEventListener('pageshow', function () { + try { + callback({ type: 'loadstart', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } catch (err) { + // blocked by CORS :\ + callback({ type: 'loadstart', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } }); - element.addEventListener("load", function () { - callback({ type: "loadstop", url: this.contentWindow.location}, {keepCallback: true}); + element.addEventListener('load', function () { + try { + callback({ type: 'loadstop', url: this.contentWindow.location.href }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } catch (err) { + // blocked by CORS :\ + callback({ type: 'loadstop', url: null }, {keepCallback: true}); // eslint-disable-line standard/no-callback-literal + } }); - element.addEventListener("error", onError); - element.addEventListener("abort", onError); + element.addEventListener('error', onError); + element.addEventListener('abort', onError); } var IAB = { close: function (win, lose) { if (browserWrap) { - if (win) win({ type: "exit" }); + // use the "open" function callback so that the exit event is fired properly + if (IAB._win) IAB._win({ type: 'exit' }); browserWrap.parentNode.removeChild(browserWrap); browserWrap = null; @@ -59,109 +75,108 @@ var IAB = { show: function (win, lose) { if (browserWrap) { - browserWrap.style.display = "block"; + browserWrap.style.display = 'block'; } }, open: function (win, lose, args) { - var strUrl = args[0], - target = args[1], - features = args[2]; + var strUrl = args[0]; + var target = args[1]; + var features = args[2]; - if (target === "_self" || !target) { + IAB._win = win; + + if (target === '_self' || !target) { window.location = strUrl; - } else if (target === "_system") { - modulemapper.getOriginalSymbol(window, 'window.open').call(window, strUrl, "_blank"); + } else if (target === '_system') { + modulemapper.getOriginalSymbol(window, 'window.open').call(window, strUrl, '_blank'); } else { // "_blank" or anything else if (!browserWrap) { - browserWrap = document.createElement("div"); - browserWrap.style.position = "absolute"; - browserWrap.style.top = "0"; - browserWrap.style.left = "0"; - browserWrap.style.boxSizing = "border-box"; - browserWrap.style.borderWidth = "40px"; - browserWrap.style.width = "100vw"; - browserWrap.style.height = "100vh"; - browserWrap.style.borderStyle = "solid"; - browserWrap.style.borderColor = "rgba(0,0,0,0.25)"; + browserWrap = document.createElement('div'); + browserWrap.style.position = 'absolute'; + browserWrap.style.top = '0'; + browserWrap.style.left = '0'; + browserWrap.style.boxSizing = 'border-box'; + browserWrap.style.borderWidth = '40px'; + browserWrap.style.width = '100vw'; + browserWrap.style.height = '100vh'; + browserWrap.style.borderStyle = 'solid'; + browserWrap.style.borderColor = 'rgba(0,0,0,0.25)'; browserWrap.onclick = function () { setTimeout(function () { - IAB.close(win); + IAB.close(); }, 0); }; document.body.appendChild(browserWrap); } - if (features.indexOf("hidden=yes") !== -1) { - browserWrap.style.display = "none"; + if (features.indexOf('hidden=yes') !== -1) { + browserWrap.style.display = 'none'; } - popup = document.createElement("iframe"); - popup.style.borderWidth = "0px"; - popup.style.width = "100%"; + popup = document.createElement('iframe'); + popup.style.borderWidth = '0px'; + popup.style.width = '100%'; browserWrap.appendChild(popup); - if (features.indexOf("location=yes") !== -1 || features.indexOf("location") === -1) { - popup.style.height = "calc(100% - 60px)"; - popup.style.marginBottom = "-4px"; + if (features.indexOf('location=yes') !== -1 || features.indexOf('location') === -1) { + popup.style.height = 'calc(100% - 60px)'; + popup.style.marginBottom = '-4px'; - navigationButtonsDiv = document.createElement("div"); - navigationButtonsDiv.style.height = "60px"; - navigationButtonsDiv.style.backgroundColor = "#404040"; - navigationButtonsDiv.style.zIndex = "999"; + navigationButtonsDiv = document.createElement('div'); + navigationButtonsDiv.style.height = '60px'; + navigationButtonsDiv.style.backgroundColor = '#404040'; + navigationButtonsDiv.style.zIndex = '999'; navigationButtonsDiv.onclick = function (e) { e.cancelBubble = true; }; - navigationButtonsDivInner = document.createElement("div"); - navigationButtonsDivInner.style.paddingTop = "10px"; - navigationButtonsDivInner.style.height = "50px"; - navigationButtonsDivInner.style.width = "160px"; - navigationButtonsDivInner.style.margin = "0 auto"; - navigationButtonsDivInner.style.backgroundColor = "#404040"; - navigationButtonsDivInner.style.zIndex = "999"; + navigationButtonsDivInner = document.createElement('div'); + navigationButtonsDivInner.style.paddingTop = '10px'; + navigationButtonsDivInner.style.height = '50px'; + navigationButtonsDivInner.style.width = '160px'; + navigationButtonsDivInner.style.margin = '0 auto'; + navigationButtonsDivInner.style.backgroundColor = '#404040'; + navigationButtonsDivInner.style.zIndex = '999'; navigationButtonsDivInner.onclick = function (e) { e.cancelBubble = true; }; + backButton = document.createElement('button'); + backButton.style.width = '40px'; + backButton.style.height = '40px'; + backButton.style.borderRadius = '40px'; - backButton = document.createElement("button"); - backButton.style.width = "40px"; - backButton.style.height = "40px"; - backButton.style.borderRadius = "40px"; - - backButton.innerHTML = "←"; - backButton.addEventListener("click", function (e) { - if (popup.canGoBack) - popup.goBack(); + backButton.innerHTML = '←'; + backButton.addEventListener('click', function (e) { + if (popup.canGoBack) { popup.goBack(); } }); - forwardButton = document.createElement("button"); - forwardButton.style.marginLeft = "20px"; - forwardButton.style.width = "40px"; - forwardButton.style.height = "40px"; - forwardButton.style.borderRadius = "40px"; + forwardButton = document.createElement('button'); + forwardButton.style.marginLeft = '20px'; + forwardButton.style.width = '40px'; + forwardButton.style.height = '40px'; + forwardButton.style.borderRadius = '40px'; - forwardButton.innerHTML = "→"; - forwardButton.addEventListener("click", function (e) { - if (popup.canGoForward) - popup.goForward(); + forwardButton.innerHTML = '→'; + forwardButton.addEventListener('click', function (e) { + if (popup.canGoForward) { popup.goForward(); } }); - closeButton = document.createElement("button"); - closeButton.style.marginLeft = "20px"; - closeButton.style.width = "40px"; - closeButton.style.height = "40px"; - closeButton.style.borderRadius = "40px"; + closeButton = document.createElement('button'); + closeButton.style.marginLeft = '20px'; + closeButton.style.width = '40px'; + closeButton.style.height = '40px'; + closeButton.style.borderRadius = '40px'; - closeButton.innerHTML = "✖"; - closeButton.addEventListener("click", function (e) { + closeButton.innerHTML = '✖'; + closeButton.addEventListener('click', function (e) { setTimeout(function () { - IAB.close(win); + IAB.close(); }, 0); }); @@ -176,7 +191,7 @@ var IAB = { browserWrap.appendChild(navigationButtonsDiv); } else { - popup.style.height = "100%"; + popup.style.height = '100%'; } // start listening for navigation events @@ -187,8 +202,8 @@ var IAB = { }, injectScriptCode: function (win, fail, args) { - var code = args[0], - hasCallback = args[1]; + var code = args[0]; + var hasCallback = args[1]; if (browserWrap && popup) { try { @@ -196,7 +211,7 @@ var IAB = { if (hasCallback) { win([]); } - } catch(e) { + } catch (e) { console.error('Error occured while trying to injectScriptCode: ' + JSON.stringify(e)); } } @@ -208,7 +223,7 @@ var IAB = { if (fail) { fail(msg); } - }, + }, injectStyleCode: function (win, fail, args) { var msg = 'Browser cordova-plugin-inappbrowser injectStyleCode is not yet implemented'; @@ -229,4 +244,4 @@ var IAB = { module.exports = IAB; -require("cordova/exec/proxy").add("InAppBrowser", module.exports); +require('cordova/exec/proxy').add('InAppBrowser', module.exports); diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/firefoxos/InAppBrowserProxy.js b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/firefoxos/InAppBrowserProxy.js deleted file mode 100644 index c09e3583..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/firefoxos/InAppBrowserProxy.js +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - -// https://developer.mozilla.org/en-US/docs/WebAPI/Browser - -var modulemapper = require('cordova/modulemapper'); - -var origOpenFunc = modulemapper.getOriginalSymbol(window, 'window.open'); -var browserWrap; - -var IABExecs = { - - close: function (win, lose) { - if (browserWrap) { - browserWrap.parentNode.removeChild(browserWrap); - browserWrap = null; - if (typeof(win) == "function") win({type:'exit'}); - } - }, - - /* - * Reveal browser if opened hidden - */ - show: function (win, lose) { - console.error('[FirefoxOS] show not implemented'); - }, - - open: function (win, lose, args) { - var strUrl = args[0], - target = args[1], - features_string = args[2] || "location=yes", //location=yes is default - features = {}; - - var features_list = features_string.split(','); - features_list.forEach(function(feature) { - var tup = feature.split('='); - if (tup[1] == 'yes') { - tup[1] = true; - } else if (tup[1] == 'no') { - tup[1] = false; - } else { - var number = parseInt(tup[1]); - if (!isNaN(number)) { - tup[1] = number; - } - } - features[tup[0]] = tup[1]; - }); - - function updateIframeSizeNoLocation() { - browserWrap.style.width = window.innerWidth + 'px'; - browserWrap.style.height = window.innerHeight + 'px'; - browserWrap.style.zIndex = '999999999'; - browserWrap.browser.style.height = (window.innerHeight - 60) + 'px'; - browserWrap.browser.style.width = browserWrap.style.width; - } - - if (target === '_system') { - origOpenFunc.apply(window, [strUrl, '_blank']); - } else if (target === '_blank') { - var browserElem = document.createElement('iframe'); - browserElem.setAttribute('mozbrowser', true); - // make this loaded in its own child process - browserElem.setAttribute('remote', true); - browserElem.setAttribute('src', strUrl); - if (browserWrap) { - document.body.removeChild(browserWrap); - } - browserWrap = document.createElement('div'); - // assign browser element to browserWrap for future reference - browserWrap.browser = browserElem; - - browserWrap.classList.add('inAppBrowserWrap'); - // position fixed so that it works even when page is scrolled - browserWrap.style.position = 'fixed'; - browserElem.style.position = 'absolute'; - browserElem.style.border = 0; - browserElem.style.top = '60px'; - browserElem.style.left = '0px'; - updateIframeSizeNoLocation(); - - var menu = document.createElement('menu'); - menu.setAttribute('type', 'toolbar'); - var close = document.createElement('li'); - var back = document.createElement('li'); - var forward = document.createElement('li'); - - close.appendChild(document.createTextNode('×')); - back.appendChild(document.createTextNode('<')); - forward.appendChild(document.createTextNode('>')); - - close.classList.add('inAppBrowserClose'); - back.classList.add('inAppBrowserBack'); - forward.classList.add('inAppBrowserForward'); - - var checkForwardBackward = function () { - var backReq = browserElem.getCanGoBack(); - backReq.onsuccess = function() { - if (this.result) { - back.classList.remove('disabled'); - } else { - back.classList.add('disabled'); - } - }; - var forwardReq = browserElem.getCanGoForward(); - forwardReq.onsuccess = function() { - if (this.result) { - forward.classList.remove('disabled'); - } else { - forward.classList.add('disabled'); - } - }; - }; - - browserElem.addEventListener('mozbrowserloadend', checkForwardBackward); - - close.addEventListener('click', function () { - setTimeout(function () { - IABExecs.close(win, lose); - }, 0); - }, false); - - back.addEventListener('click', function () { - browserElem.goBack(); - }, false); - - forward.addEventListener('click', function () { - browserElem.goForward(); - }, false); - - menu.appendChild(back); - menu.appendChild(forward); - menu.appendChild(close); - - browserWrap.appendChild(menu); - browserWrap.appendChild(browserElem); - document.body.appendChild(browserWrap); - - //we use mozbrowserlocationchange instead of mozbrowserloadstart to get the url - browserElem.addEventListener('mozbrowserlocationchange', function(e){ - win({ - type:'loadstart', - url : e.detail - }); - }, false); - browserElem.addEventListener('mozbrowserloadend', function(e){ - win({type:'loadstop'}); - }, false); - browserElem.addEventListener('mozbrowsererror', function(e){ - win({type:'loaderror'}); - }, false); - browserElem.addEventListener('mozbrowserclose', function(e){ - win({type:'exit'}); - }, false); - } else { - window.location = strUrl; - } - }, - injectScriptCode: function (code, bCB) { - console.error('[FirefoxOS] injectScriptCode not implemented'); - }, - injectScriptFile: function (file, bCB) { - console.error('[FirefoxOS] injectScriptFile not implemented'); - } -}; - -module.exports = IABExecs; - -require('cordova/exec/proxy').add('InAppBrowser', module.exports); diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.h deleted file mode 100644 index 6bb0ec16..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, - software distributed under the License is distributed on an - "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - KIND, either express or implied. See the License for the - specific language governing permissions and limitations - under the License. - */ - -#import <Cordova/CDVPlugin.h> -#import <Cordova/CDVInvokedUrlCommand.h> -#import <Cordova/CDVScreenOrientationDelegate.h> - -#ifdef __CORDOVA_4_0_0 - #import <Cordova/CDVUIWebViewDelegate.h> -#else - #import <Cordova/CDVWebViewDelegate.h> -#endif - -@class CDVInAppBrowserViewController; - -@interface CDVInAppBrowser : CDVPlugin { -} - -@property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController; -@property (nonatomic, copy) NSString* callbackId; -@property (nonatomic, copy) NSRegularExpression *callbackIdPattern; - -- (void)open:(CDVInvokedUrlCommand*)command; -- (void)close:(CDVInvokedUrlCommand*)command; -- (void)injectScriptCode:(CDVInvokedUrlCommand*)command; -- (void)show:(CDVInvokedUrlCommand*)command; - -@end - -@interface CDVInAppBrowserOptions : NSObject {} - -@property (nonatomic, assign) BOOL location; -@property (nonatomic, assign) BOOL toolbar; -@property (nonatomic, copy) NSString* closebuttoncaption; -@property (nonatomic, copy) NSString* toolbarposition; -@property (nonatomic, assign) BOOL clearcache; -@property (nonatomic, assign) BOOL clearsessioncache; - -@property (nonatomic, copy) NSString* presentationstyle; -@property (nonatomic, copy) NSString* transitionstyle; - -@property (nonatomic, assign) BOOL enableviewportscale; -@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction; -@property (nonatomic, assign) BOOL allowinlinemediaplayback; -@property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction; -@property (nonatomic, assign) BOOL suppressesincrementalrendering; -@property (nonatomic, assign) BOOL hidden; -@property (nonatomic, assign) BOOL disallowoverscroll; - -+ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options; - -@end - -@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{ - @private - NSString* _userAgent; - NSString* _prevUserAgent; - NSInteger _userAgentLockToken; - CDVInAppBrowserOptions *_browserOptions; - -#ifdef __CORDOVA_4_0_0 - CDVUIWebViewDelegate* _webViewDelegate; -#else - CDVWebViewDelegate* _webViewDelegate; -#endif - -} - -@property (nonatomic, strong) IBOutlet UIWebView* webView; -@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton; -@property (nonatomic, strong) IBOutlet UILabel* addressLabel; -@property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton; -@property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton; -@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner; -@property (nonatomic, strong) IBOutlet UIToolbar* toolbar; - -@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; -@property (nonatomic, weak) CDVInAppBrowser* navigationDelegate; -@property (nonatomic) NSURL* currentURL; - -- (void)close; -- (void)navigateTo:(NSURL*)url; -- (void)showLocationBar:(BOOL)show; -- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition; -- (void)setCloseButtonTitle:(NSString*)title; - -- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions; - -@end - -@interface CDVInAppBrowserNavigationController : UINavigationController - -@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; - -@end - diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.h new file mode 100644 index 00000000..bd186a2f --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.h @@ -0,0 +1,27 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVScreenOrientationDelegate.h> + + +@interface CDVInAppBrowserNavigationController : UINavigationController + +@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.m b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.m new file mode 100644 index 00000000..3cc9043f --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserNavigationController.m @@ -0,0 +1,63 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInAppBrowserNavigationController.h" + +@implementation CDVInAppBrowserNavigationController : UINavigationController + +- (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { + if ( self.presentedViewController) { + [super dismissViewControllerAnimated:flag completion:completion]; + } +} + +- (void) viewDidLoad { + [super viewDidLoad]; +} + +- (CGRect) invertFrameIfNeeded:(CGRect)rect { + if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { + CGFloat temp = rect.size.width; + rect.size.width = rect.size.height; + rect.size.height = temp; + } + rect.origin = CGPointZero; + return rect; +} + +#pragma mark CDVScreenOrientationDelegate + +- (BOOL)shouldAutorotate +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { + return [self.orientationDelegate shouldAutorotate]; + } + return YES; +} + +- (UIInterfaceOrientationMask)supportedInterfaceOrientations +{ + if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { + return [self.orientationDelegate supportedInterfaceOrientations]; + } + + return 1 << UIInterfaceOrientationPortrait; +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.h new file mode 100644 index 00000000..c1c9fa53 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.h @@ -0,0 +1,50 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + + +@interface CDVInAppBrowserOptions : NSObject {} + +@property (nonatomic, assign) BOOL location; +@property (nonatomic, assign) BOOL toolbar; +@property (nonatomic, copy) NSString* closebuttoncaption; +@property (nonatomic, copy) NSString* closebuttoncolor; +@property (nonatomic, assign) BOOL lefttoright; +@property (nonatomic, copy) NSString* toolbarposition; +@property (nonatomic, copy) NSString* toolbarcolor; +@property (nonatomic, assign) BOOL toolbartranslucent; +@property (nonatomic, assign) BOOL hidenavigationbuttons; +@property (nonatomic, copy) NSString* navigationbuttoncolor; +@property (nonatomic, assign) BOOL cleardata; +@property (nonatomic, assign) BOOL clearcache; +@property (nonatomic, assign) BOOL clearsessioncache; +@property (nonatomic, assign) BOOL hidespinner; + +@property (nonatomic, copy) NSString* presentationstyle; +@property (nonatomic, copy) NSString* transitionstyle; + +@property (nonatomic, assign) BOOL enableviewportscale; +@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction; +@property (nonatomic, assign) BOOL allowinlinemediaplayback; +@property (nonatomic, assign) BOOL hidden; +@property (nonatomic, assign) BOOL disallowoverscroll; +@property (nonatomic, copy) NSString* beforeload; + ++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.m b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.m new file mode 100644 index 00000000..e20d1a8c --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowserOptions.m @@ -0,0 +1,90 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInAppBrowserOptions.h" + +@implementation CDVInAppBrowserOptions + +- (id)init +{ + if (self = [super init]) { + // default values + self.location = YES; + self.toolbar = YES; + self.closebuttoncaption = nil; + self.toolbarposition = @"bottom"; + self.cleardata = NO; + self.clearcache = NO; + self.clearsessioncache = NO; + self.hidespinner = NO; + + self.enableviewportscale = NO; + self.mediaplaybackrequiresuseraction = NO; + self.allowinlinemediaplayback = NO; + self.hidden = NO; + self.disallowoverscroll = NO; + self.hidenavigationbuttons = NO; + self.closebuttoncolor = nil; + self.lefttoright = false; + self.toolbarcolor = nil; + self.toolbartranslucent = YES; + self.beforeload = @""; + } + + return self; +} + ++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options +{ + CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init]; + + // NOTE: this parsing does not handle quotes within values + NSArray* pairs = [options componentsSeparatedByString:@","]; + + // parse keys and values, set the properties + for (NSString* pair in pairs) { + NSArray* keyvalue = [pair componentsSeparatedByString:@"="]; + + if ([keyvalue count] == 2) { + NSString* key = [[keyvalue objectAtIndex:0] lowercaseString]; + NSString* value = [keyvalue objectAtIndex:1]; + NSString* value_lc = [value lowercaseString]; + + BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"]; + NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setAllowsFloats:YES]; + BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil; + + // set the property according to the key name + if ([obj respondsToSelector:NSSelectorFromString(key)]) { + if (isNumber) { + [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key]; + } else if (isBoolean) { + [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key]; + } else { + [obj setValue:value forKey:key]; + } + } + } + } + + return obj; +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowser.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowser.h new file mode 100644 index 00000000..e339be15 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowser.h @@ -0,0 +1,80 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVPlugin.h> +#import <Cordova/CDVInvokedUrlCommand.h> +#import <Cordova/CDVScreenOrientationDelegate.h> +#import "CDVWKInAppBrowserUIDelegate.h" +#import "CDVInAppBrowserOptions.h" +#import "CDVInAppBrowserNavigationController.h" + +@class CDVWKInAppBrowserViewController; + +@interface CDVWKInAppBrowser : CDVPlugin { + UIWindow * tmpWindow; + + @private + NSString* _beforeload; + BOOL _waitForBeforeload; +} + +@property (nonatomic, retain) CDVWKInAppBrowser* instance; +@property (nonatomic, retain) CDVWKInAppBrowserViewController* inAppBrowserViewController; +@property (nonatomic, copy) NSString* callbackId; +@property (nonatomic, copy) NSRegularExpression *callbackIdPattern; + ++ (id) getInstance; +- (void)open:(CDVInvokedUrlCommand*)command; +- (void)close:(CDVInvokedUrlCommand*)command; +- (void)injectScriptCode:(CDVInvokedUrlCommand*)command; +- (void)show:(CDVInvokedUrlCommand*)command; +- (void)hide:(CDVInvokedUrlCommand*)command; +- (void)loadAfterBeforeload:(CDVInvokedUrlCommand*)command; + +@end + +@interface CDVWKInAppBrowserViewController : UIViewController <CDVScreenOrientationDelegate,WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler>{ + @private + CDVInAppBrowserOptions *_browserOptions; + NSDictionary *_settings; +} + +@property (nonatomic, strong) IBOutlet WKWebView* webView; +@property (nonatomic, strong) IBOutlet WKWebViewConfiguration* configuration; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton; +@property (nonatomic, strong) IBOutlet UILabel* addressLabel; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton; +@property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton; +@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner; +@property (nonatomic, strong) IBOutlet UIToolbar* toolbar; +@property (nonatomic, strong) IBOutlet CDVWKInAppBrowserUIDelegate* webViewUIDelegate; + +@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate; +@property (nonatomic, weak) CDVWKInAppBrowser* navigationDelegate; +@property (nonatomic) NSURL* currentURL; + +- (void)close; +- (void)navigateTo:(NSURL*)url; +- (void)showLocationBar:(BOOL)show; +- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition; +- (void)setCloseButtonTitle:(NSString*)title : (NSString*) colorString : (int) buttonIndex; + +- (id)initWithBrowserOptions: (CDVInAppBrowserOptions*) browserOptions andSettings:(NSDictionary*) settings; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.m b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowser.m index b342ca73..1ec20653 100644 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVInAppBrowser.m +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowser.m @@ -6,9 +6,9 @@ to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,9 +17,13 @@ under the License. */ -#import "CDVInAppBrowser.h" +#import "CDVWKInAppBrowser.h" + +#if __has_include("CDVWKProcessPoolFactory.h") +#import "CDVWKProcessPoolFactory.h" +#endif + #import <Cordova/CDVPluginResult.h> -#import <Cordova/CDVUserAgentUtil.h> #define kInAppBrowserTargetSelf @"_self" #define kInAppBrowserTargetSystem @"_system" @@ -28,28 +32,34 @@ #define kInAppBrowserToolbarBarPositionBottom @"bottom" #define kInAppBrowserToolbarBarPositionTop @"top" +#define IAB_BRIDGE_NAME @"cordova_iab" + #define TOOLBAR_HEIGHT 44.0 #define LOCATIONBAR_HEIGHT 21.0 #define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT)) -#pragma mark CDVInAppBrowser +#pragma mark CDVWKInAppBrowser -@interface CDVInAppBrowser () { +@interface CDVWKInAppBrowser () { NSInteger _previousStatusBarStyle; } @end -@implementation CDVInAppBrowser +@implementation CDVWKInAppBrowser + +static CDVWKInAppBrowser* instance = nil; + ++ (id) getInstance{ + return instance; +} - (void)pluginInitialize { + instance = self; _previousStatusBarStyle = -1; _callbackIdPattern = nil; -} - -- (id)settingForKey:(NSString*)key -{ - return [self.commandDelegate.settings objectForKey:[key lowercaseString]]; + _beforeload = @""; + _waitForBeforeload = NO; } - (void)onReset @@ -63,41 +73,38 @@ NSLog(@"IAB.close() called but it was already closed."); return; } + // Things are cleaned up in browserExit. [self.inAppBrowserViewController close]; } - (BOOL) isSystemUrl:(NSURL*)url { - if ([[url host] isEqualToString:@"itunes.apple.com"]) { - return YES; - } - - return NO; + if ([[url host] isEqualToString:@"itunes.apple.com"]) { + return YES; + } + + return NO; } - (void)open:(CDVInvokedUrlCommand*)command { CDVPluginResult* pluginResult; - + NSString* url = [command argumentAtIndex:0]; NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf]; NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]]; - + self.callbackId = command.callbackId; - + if (url != nil) { -#ifdef __CORDOVA_4_0_0 NSURL* baseUrl = [self.webViewEngine URL]; -#else - NSURL* baseUrl = [self.webView.request URL]; -#endif NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL]; - + if ([self isSystemUrl:absoluteUrl]) { target = kInAppBrowserTargetSystem; } - + if ([target isEqualToString:kInAppBrowserTargetSelf]) { [self openInCordovaWebView:absoluteUrl withOptions:options]; } else if ([target isEqualToString:kInAppBrowserTargetSystem]) { @@ -105,12 +112,12 @@ } else { // _blank or anything else [self openInInAppBrowser:absoluteUrl withOptions:options]; } - + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; } else { pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"]; } - + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; } @@ -118,51 +125,92 @@ - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options { CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options]; - + + WKWebsiteDataStore* dataStore = [WKWebsiteDataStore defaultDataStore]; + if (browserOptions.cleardata) { + + NSDate* dateFrom = [NSDate dateWithTimeIntervalSince1970:0]; + [dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:dateFrom completionHandler:^{ + NSLog(@"Removed all WKWebView data"); + self.inAppBrowserViewController.webView.configuration.processPool = [[WKProcessPool alloc] init]; // create new process pool to flush all data + }]; + } + if (browserOptions.clearcache) { - NSHTTPCookie *cookie; - NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - for (cookie in [storage cookies]) - { - if (![cookie.domain isEqual: @".^filecookies^"]) { - [storage deleteCookie:cookie]; - } + bool isAtLeastiOS11 = false; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if (@available(iOS 11.0, *)) { + isAtLeastiOS11 = true; + } +#endif + + if(isAtLeastiOS11){ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + // Deletes all cookies + WKHTTPCookieStore* cookieStore = dataStore.httpCookieStore; + [cookieStore getAllCookies:^(NSArray* cookies) { + NSHTTPCookie* cookie; + for(cookie in cookies){ + [cookieStore deleteCookie:cookie completionHandler:nil]; + } + }]; +#endif + }else{ + // https://stackoverflow.com/a/31803708/777265 + // Only deletes domain cookies (not session cookies) + [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] + completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records) { + for (WKWebsiteDataRecord *record in records){ + NSSet<NSString*>* dataTypes = record.dataTypes; + if([dataTypes containsObject:WKWebsiteDataTypeCookies]){ + [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes + forDataRecords:@[record] + completionHandler:^{}]; + } + } + }]; } } - + if (browserOptions.clearsessioncache) { - NSHTTPCookie *cookie; - NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - for (cookie in [storage cookies]) - { - if (![cookie.domain isEqual: @".^filecookies^"] && cookie.isSessionOnly) { - [storage deleteCookie:cookie]; - } + bool isAtLeastiOS11 = false; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if (@available(iOS 11.0, *)) { + isAtLeastiOS11 = true; + } +#endif + if (isAtLeastiOS11) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + // Deletes session cookies + WKHTTPCookieStore* cookieStore = dataStore.httpCookieStore; + [cookieStore getAllCookies:^(NSArray* cookies) { + NSHTTPCookie* cookie; + for(cookie in cookies){ + if(cookie.sessionOnly){ + [cookieStore deleteCookie:cookie completionHandler:nil]; + } + } + }]; +#endif + }else{ + NSLog(@"clearsessioncache not available below iOS 11.0"); } } if (self.inAppBrowserViewController == nil) { - NSString* userAgent = [CDVUserAgentUtil originalUserAgent]; - NSString* overrideUserAgent = [self settingForKey:@"OverrideUserAgent"]; - NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"]; - if(overrideUserAgent){ - userAgent = overrideUserAgent; - } - if(appendUserAgent){ - userAgent = [userAgent stringByAppendingString: appendUserAgent]; - } - self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:userAgent prevUserAgent:[self.commandDelegate userAgent] browserOptions: browserOptions]; + self.inAppBrowserViewController = [[CDVWKInAppBrowserViewController alloc] initWithBrowserOptions: browserOptions andSettings:self.commandDelegate.settings]; self.inAppBrowserViewController.navigationDelegate = self; - + if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) { self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController; } } - + [self.inAppBrowserViewController showLocationBar:browserOptions.location]; [self.inAppBrowserViewController showToolBar:browserOptions.toolbar :browserOptions.toolbarposition]; - if (browserOptions.closebuttoncaption != nil) { - [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption]; + if (browserOptions.closebuttoncaption != nil || browserOptions.closebuttoncolor != nil) { + int closeButtonIndex = browserOptions.lefttoright ? (browserOptions.hidenavigationbuttons ? 1 : 4) : 0; + [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption :browserOptions.closebuttoncolor :closeButtonIndex]; } // Set Presentation Style UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default @@ -174,7 +222,7 @@ } } self.inAppBrowserViewController.modalPresentationStyle = presentationStyle; - + // Set Transition Style UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default if (browserOptions.transitionstyle != nil) { @@ -185,8 +233,8 @@ } } self.inAppBrowserViewController.modalTransitionStyle = transitionStyle; - - // prevent webView from bouncing + + //prevent webView from bouncing if (browserOptions.disallowoverscroll) { if ([self.inAppBrowserViewController.webView respondsToSelector:@selector(scrollView)]) { ((UIScrollView*)[self.inAppBrowserViewController.webView scrollView]).bounces = NO; @@ -198,24 +246,32 @@ } } } - - // UIWebView options - self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale; - self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction; - self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback; - if (IsAtLeastiOSVersion(@"6.0")) { - self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction; - self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering; + + // use of beforeload event + if([browserOptions.beforeload isKindOfClass:[NSString class]]){ + _beforeload = browserOptions.beforeload; + }else{ + _beforeload = @"yes"; } - + _waitForBeforeload = ![_beforeload isEqualToString:@""]; + [self.inAppBrowserViewController navigateTo:url]; if (!browserOptions.hidden) { - [self show:nil]; + [self show:nil withNoAnimate:browserOptions.hidden]; } } -- (void)show:(CDVInvokedUrlCommand*)command +- (void)show:(CDVInvokedUrlCommand*)command{ + [self show:command withNoAnimate:NO]; +} + +- (void)show:(CDVInvokedUrlCommand*)command withNoAnimate:(BOOL)noAnimate { + BOOL initHidden = NO; + if(command == nil && noAnimate == YES){ + initHidden = YES; + } + if (self.inAppBrowserViewController == nil) { NSLog(@"Tried to show IAB after it was closed."); return; @@ -224,21 +280,69 @@ NSLog(@"Tried to show IAB while already shown"); return; } - - _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; - + + if(!initHidden){ + _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; + } + __block CDVInAppBrowserNavigationController* nav = [[CDVInAppBrowserNavigationController alloc] - initWithRootViewController:self.inAppBrowserViewController]; + initWithRootViewController:self.inAppBrowserViewController]; nav.orientationDelegate = self.inAppBrowserViewController; nav.navigationBarHidden = YES; nav.modalPresentationStyle = self.inAppBrowserViewController.modalPresentationStyle; + + __weak CDVWKInAppBrowser* weakSelf = self; + + // Run later to avoid the "took a long time" log message. + dispatch_async(dispatch_get_main_queue(), ^{ + if (weakSelf.inAppBrowserViewController != nil) { + float osVersion = [[[UIDevice currentDevice] systemVersion] floatValue]; + __strong __typeof(weakSelf) strongSelf = weakSelf; + if (!strongSelf->tmpWindow) { + CGRect frame = [[UIScreen mainScreen] bounds]; + if(initHidden && osVersion < 11){ + frame.origin.x = -10000; + } + strongSelf->tmpWindow = [[UIWindow alloc] initWithFrame:frame]; + } + UIViewController *tmpController = [[UIViewController alloc] init]; + + [strongSelf->tmpWindow setRootViewController:tmpController]; + [strongSelf->tmpWindow setWindowLevel:UIWindowLevelNormal]; - __weak CDVInAppBrowser* weakSelf = self; + if(!initHidden || osVersion < 11){ + [self->tmpWindow makeKeyAndVisible]; + } + [tmpController presentViewController:nav animated:!noAnimate completion:nil]; + } + }); +} +- (void)hide:(CDVInvokedUrlCommand*)command +{ + // Set tmpWindow to hidden to make main webview responsive to touch again + // https://stackoverflow.com/questions/4544489/how-to-remove-a-uiwindow + self->tmpWindow.hidden = YES; + self->tmpWindow = nil; + + if (self.inAppBrowserViewController == nil) { + NSLog(@"Tried to hide IAB after it was closed."); + return; + + + } + if (_previousStatusBarStyle == -1) { + NSLog(@"Tried to hide IAB while already hidden"); + return; + } + + _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; + // Run later to avoid the "took a long time" log message. dispatch_async(dispatch_get_main_queue(), ^{ - if (weakSelf.inAppBrowserViewController != nil) { - [weakSelf.viewController presentViewController:nav animated:YES completion:nil]; + if (self.inAppBrowserViewController != nil) { + _previousStatusBarStyle = -1; + [self.inAppBrowserViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } }); } @@ -246,24 +350,39 @@ - (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options { NSURLRequest* request = [NSURLRequest requestWithURL:url]; - -#ifdef __CORDOVA_4_0_0 // the webview engine itself will filter for this according to <allow-navigation> policy // in config.xml for cordova-ios-4.0 [self.webViewEngine loadRequest:request]; -#else - if ([self.commandDelegate URLIsWhitelisted:url]) { - [self.webView loadRequest:request]; - } else { // this assumes the InAppBrowser can be excepted from the white-list - [self openInInAppBrowser:url withOptions:options]; - } -#endif } - (void)openInSystem:(NSURL*)url { - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; - [[UIApplication sharedApplication] openURL:url]; + if ([[UIApplication sharedApplication] openURL:url] == NO) { + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]]; + [[UIApplication sharedApplication] openURL:url]; + } +} + +- (void)loadAfterBeforeload:(CDVInvokedUrlCommand*)command +{ + NSString* urlStr = [command argumentAtIndex:0]; + + if ([_beforeload isEqualToString:@""]) { + NSLog(@"unexpected loadAfterBeforeload called without feature beforeload=get|post"); + } + if (self.inAppBrowserViewController == nil) { + NSLog(@"Tried to invoke loadAfterBeforeload on IAB after it was closed."); + return; + } + if (urlStr == nil) { + NSLog(@"loadAfterBeforeload called with nil argument, ignoring."); + return; + } + + NSURL* url = [NSURL URLWithString:urlStr]; + //_beforeload = @""; + _waitForBeforeload = NO; + [self.inAppBrowserViewController navigateTo:url]; } // This is a helper method for the inject{Script|Style}{Code|File} API calls, which @@ -277,28 +396,43 @@ - (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper { - // Ensure an iframe bridge is created to communicate with the CDVInAppBrowserViewController - [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){_cdvIframeBridge=d.getElementById('_cdvIframeBridge');if(!_cdvIframeBridge) {var e = _cdvIframeBridge = d.createElement('iframe');e.id='_cdvIframeBridge'; e.style.display='none';d.body.appendChild(e);}})(document)"]; - + // Ensure a message handler bridge is created to communicate with the CDVWKInAppBrowserViewController + [self evaluateJavaScript: [NSString stringWithFormat:@"(function(w){if(!w._cdvMessageHandler) {w._cdvMessageHandler = function(id,d){w.webkit.messageHandlers.%@.postMessage({d:d, id:id});}}})(window)", IAB_BRIDGE_NAME]]; + if (jsWrapper != nil) { NSData* jsonData = [NSJSONSerialization dataWithJSONObject:@[source] options:0 error:nil]; NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; if (sourceArrayString) { NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)]; NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString]; - [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject]; + [self evaluateJavaScript:jsToInject]; } } else { - [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source]; + [self evaluateJavaScript:source]; } } + +//Synchronus helper for javascript evaluation +- (void)evaluateJavaScript:(NSString *)script { + __block NSString* _script = script; + [self.inAppBrowserViewController.webView evaluateJavaScript:script completionHandler:^(id result, NSError *error) { + if (error == nil) { + if (result != nil) { + NSLog(@"%@", result); + } + } else { + NSLog(@"evaluateJavaScript error : %@ : %@", error.localizedDescription, _script); + } + }]; +} + - (void)injectScriptCode:(CDVInvokedUrlCommand*)command { NSString* jsWrapper = nil; - + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { - jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+encodeURIComponent(JSON.stringify([eval(%%@)]));", command.callbackId]; + jsWrapper = [NSString stringWithFormat:@"_cdvMessageHandler('%@',JSON.stringify([eval(%%@)]));", command.callbackId]; } [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper]; } @@ -306,9 +440,9 @@ - (void)injectScriptFile:(CDVInvokedUrlCommand*)command { NSString* jsWrapper; - + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { - jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvMessageHandler('%@'); }; d.body.appendChild(c); })(document)", command.callbackId]; } else { jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)"; } @@ -318,9 +452,9 @@ - (void)injectStyleCode:(CDVInvokedUrlCommand*)command { NSString* jsWrapper; - + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { - jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvMessageHandler('%@'); }; d.body.appendChild(c); })(document)", command.callbackId]; } else { jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)"; } @@ -330,9 +464,9 @@ - (void)injectStyleFile:(CDVInvokedUrlCommand*)command { NSString* jsWrapper; - + if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) { - jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId]; + jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvMessageHandler('%@'); }; d.body.appendChild(c); })(document)", command.callbackId]; } else { jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)"; } @@ -357,94 +491,163 @@ } /** - * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging + * The message handler bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no * other code execution is possible. - * - * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form: - * - * gap-iab://<callbackId>/<arguments> - * - * where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789") - * - * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded - * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION - * is returned if the JSON is invalid. */ -- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType -{ - NSURL* url = request.URL; - BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; - - // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute, - // and the path, if present, should be a JSON-encoded value to pass to the callback. - if ([[url scheme] isEqualToString:@"gap-iab"]) { - NSString* scriptCallbackId = [url host]; - CDVPluginResult* pluginResult = nil; - - if ([self isValidCallbackId:scriptCallbackId]) { - NSString* scriptResult = [url path]; - NSError* __autoreleasing error = nil; +- (void)webView:(WKWebView *)theWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + + NSURL* url = navigationAction.request.URL; + NSURL* mainDocumentURL = navigationAction.request.mainDocumentURL; + BOOL isTopLevelNavigation = [url isEqual:mainDocumentURL]; + BOOL shouldStart = YES; + BOOL useBeforeLoad = NO; + NSString* httpMethod = navigationAction.request.HTTPMethod; + NSString* errorMessage = nil; + + if([_beforeload isEqualToString:@"post"]){ + //TODO handle POST requests by preserving POST data then remove this condition + errorMessage = @"beforeload doesn't yet support POST requests"; + } + else if(isTopLevelNavigation && ( + [_beforeload isEqualToString:@"yes"] + || ([_beforeload isEqualToString:@"get"] && [httpMethod isEqualToString:@"GET"]) + // TODO comment in when POST requests are handled + // || ([_beforeload isEqualToString:@"post"] && [httpMethod isEqualToString:@"POST"]) + )){ + useBeforeLoad = YES; + } - // The message should be a JSON-encoded array of the result of the script which executed. - if ((scriptResult != nil) && ([scriptResult length] > 1)) { - scriptResult = [scriptResult substringFromIndex:1]; - NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; - if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) { - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult]; - } else { - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION]; - } - } else { - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]]; - } - [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId]; - return NO; - } - } + // When beforeload, on first URL change, initiate JS callback. Only after the beforeload event, continue. + if (_waitForBeforeload && useBeforeLoad) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK + messageAsDictionary:@{@"type":@"beforeload", @"url":[url absoluteString]}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + decisionHandler(WKNavigationActionPolicyCancel); + return; + } + + if(errorMessage != nil){ + NSLog(errorMessage); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR + messageAsDictionary:@{@"type":@"loaderror", @"url":[url absoluteString], @"code": @"-1", @"message": errorMessage}]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + } + //if is an app store link, let the system handle it, otherwise it fails to load it - else if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) { + if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) { [theWebView stopLoading]; [self openInSystem:url]; - return NO; + shouldStart = NO; } else if ((self.callbackId != nil) && isTopLevelNavigation) { // Send a loadstart event for each top-level navigation (includes redirects). CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}]; [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; - + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; } - return YES; + if (useBeforeLoad) { + _waitForBeforeload = YES; + } + + if(shouldStart){ + // Fix GH-417 & GH-424: Handle non-default target attribute + // Based on https://stackoverflow.com/a/25713070/777265 + if (!navigationAction.targetFrame){ + [theWebView loadRequest:navigationAction.request]; + decisionHandler(WKNavigationActionPolicyCancel); + }else{ + decisionHandler(WKNavigationActionPolicyAllow); + } + }else{ + decisionHandler(WKNavigationActionPolicyCancel); + } } -- (void)webViewDidStartLoad:(UIWebView*)theWebView +#pragma mark WKScriptMessageHandler delegate +- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { + + CDVPluginResult* pluginResult = nil; + + if([message.body isKindOfClass:[NSDictionary class]]){ + NSDictionary* messageContent = (NSDictionary*) message.body; + NSString* scriptCallbackId = messageContent[@"id"]; + + if([messageContent objectForKey:@"d"]){ + NSString* scriptResult = messageContent[@"d"]; + NSError* __autoreleasing error = nil; + NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; + if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult]; + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION]; + } + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]]; + } + [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId]; + }else if(self.callbackId != nil){ + // Send a message event + NSString* messageContent = (NSString*) message.body; + NSError* __autoreleasing error = nil; + NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[messageContent dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; + if (error == nil) { + NSMutableDictionary* dResult = [NSMutableDictionary new]; + [dResult setValue:@"message" forKey:@"type"]; + [dResult setObject:decodedResult forKey:@"data"]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dResult]; + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + } + } +} + +- (void)didStartProvisionalNavigation:(WKWebView*)theWebView { + NSLog(@"didStartProvisionalNavigation"); +// self.inAppBrowserViewController.currentURL = theWebView.URL; } -- (void)webViewDidFinishLoad:(UIWebView*)theWebView +- (void)didFinishNavigation:(WKWebView*)theWebView { if (self.callbackId != nil) { - // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected). - NSString* url = [self.inAppBrowserViewController.currentURL absoluteString]; + NSString* url = [theWebView.URL absoluteString]; + if(url == nil){ + if(self.inAppBrowserViewController.currentURL != nil){ + url = [self.inAppBrowserViewController.currentURL absoluteString]; + }else{ + url = @""; + } + } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{@"type":@"loadstop", @"url":url}]; [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; - + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; } } -- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +- (void)webView:(WKWebView*)theWebView didFailNavigation:(NSError*)error { if (self.callbackId != nil) { - NSString* url = [self.inAppBrowserViewController.currentURL absoluteString]; + NSString* url = [theWebView.URL absoluteString]; + if(url == nil){ + if(self.inAppBrowserViewController.currentURL != nil){ + url = [self.inAppBrowserViewController.currentURL absoluteString]; + }else{ + url = @""; + } + } CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInteger:error.code], @"message": error.localizedDescription}]; [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; - + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; } } @@ -457,78 +660,135 @@ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; self.callbackId = nil; } + + [self.inAppBrowserViewController.configuration.userContentController removeScriptMessageHandlerForName:IAB_BRIDGE_NAME]; + self.inAppBrowserViewController.configuration = nil; + + [self.inAppBrowserViewController.webView stopLoading]; + [self.inAppBrowserViewController.webView removeFromSuperview]; + [self.inAppBrowserViewController.webView setUIDelegate:nil]; + [self.inAppBrowserViewController.webView setNavigationDelegate:nil]; + self.inAppBrowserViewController.webView = nil; + // Set navigationDelegate to nil to ensure no callbacks are received from it. self.inAppBrowserViewController.navigationDelegate = nil; - // Don't recycle the ViewController since it may be consuming a lot of memory. - // Also - this is required for the PDF/User-Agent bug work-around. self.inAppBrowserViewController = nil; + // Set tmpWindow to hidden to make main webview responsive to touch again + // Based on https://stackoverflow.com/questions/4544489/how-to-remove-a-uiwindow + self->tmpWindow.hidden = YES; + self->tmpWindow = nil; + if (IsAtLeastiOSVersion(@"7.0")) { if (_previousStatusBarStyle != -1) { [[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle]; + } } - + _previousStatusBarStyle = -1; // this value was reset before reapplying it. caused statusbar to stay black on ios7 } -@end +@end //CDVWKInAppBrowser -#pragma mark CDVInAppBrowserViewController +#pragma mark CDVWKInAppBrowserViewController -@implementation CDVInAppBrowserViewController +@implementation CDVWKInAppBrowserViewController @synthesize currentURL; -- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions +CGFloat lastReducedStatusBarHeight = 0.0; +BOOL isExiting = FALSE; + +- (id)initWithBrowserOptions: (CDVInAppBrowserOptions*) browserOptions andSettings:(NSDictionary *)settings { self = [super init]; if (self != nil) { - _userAgent = userAgent; - _prevUserAgent = prevUserAgent; _browserOptions = browserOptions; -#ifdef __CORDOVA_4_0_0 - _webViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:self]; -#else - _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self]; -#endif + _settings = settings; + self.webViewUIDelegate = [[CDVWKInAppBrowserUIDelegate alloc] initWithTitle:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]]; + [self.webViewUIDelegate setViewController:self]; [self createViews]; } - + return self; } -// Prevent crashes on closing windows -(void)dealloc { - self.webView.delegate = nil; + //NSLog(@"dealloc"); } - (void)createViews { // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included - + CGRect webViewBounds = self.view.bounds; BOOL toolbarIsAtBottom = ![_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]; webViewBounds.size.height -= _browserOptions.location ? FOOTER_HEIGHT : TOOLBAR_HEIGHT; - self.webView = [[UIWebView alloc] initWithFrame:webViewBounds]; - - self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + WKUserContentController* userContentController = [[WKUserContentController alloc] init]; + + WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init]; + + NSString *userAgent = configuration.applicationNameForUserAgent; + if ( + [self settingForKey:@"OverrideUserAgent"] == nil && + [self settingForKey:@"AppendUserAgent"] != nil + ) { + userAgent = [NSString stringWithFormat:@"%@ %@", userAgent, [self settingForKey:@"AppendUserAgent"]]; + } + configuration.applicationNameForUserAgent = userAgent; + configuration.userContentController = userContentController; +#if __has_include("CDVWKProcessPoolFactory.h") + configuration.processPool = [[CDVWKProcessPoolFactory sharedFactory] sharedProcessPool]; +#endif + [configuration.userContentController addScriptMessageHandler:self name:IAB_BRIDGE_NAME]; + + //WKWebView options + configuration.allowsInlineMediaPlayback = _browserOptions.allowinlinemediaplayback; + if (IsAtLeastiOSVersion(@"10.0")) { + configuration.ignoresViewportScaleLimits = _browserOptions.enableviewportscale; + if(_browserOptions.mediaplaybackrequiresuseraction == YES){ + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeAll; + }else{ + configuration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; + } + }else{ // iOS 9 + configuration.mediaPlaybackRequiresUserAction = _browserOptions.mediaplaybackrequiresuseraction; + } + + + self.webView = [[WKWebView alloc] initWithFrame:webViewBounds configuration:configuration]; + [self.view addSubview:self.webView]; [self.view sendSubviewToBack:self.webView]; - - self.webView.delegate = _webViewDelegate; + + + self.webView.navigationDelegate = self; + self.webView.UIDelegate = self.webViewUIDelegate; self.webView.backgroundColor = [UIColor whiteColor]; - + if ([self settingForKey:@"OverrideUserAgent"] != nil) { + self.webView.customUserAgent = [self settingForKey:@"OverrideUserAgent"]; + } + self.webView.clearsContextBeforeDrawing = YES; self.webView.clipsToBounds = YES; self.webView.contentMode = UIViewContentModeScaleToFill; self.webView.multipleTouchEnabled = YES; self.webView.opaque = YES; - self.webView.scalesPageToFit = NO; self.webView.userInteractionEnabled = YES; - + self.automaticallyAdjustsScrollViewInsets = YES ; + [self.webView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth]; + self.webView.allowsLinkPreview = NO; + self.webView.allowsBackForwardNavigationGestures = NO; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 + if (@available(iOS 11.0, *)) { + [self.webView.scrollView setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever]; + } +#endif + self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; self.spinner.alpha = 1.000; self.spinner.autoresizesSubviews = YES; @@ -543,18 +803,18 @@ self.spinner.opaque = NO; self.spinner.userInteractionEnabled = NO; [self.spinner stopAnimating]; - + self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)]; self.closeButton.enabled = YES; - + UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - + UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil]; fixedSpaceButton.width = 20; - + float toolbarY = toolbarIsAtBottom ? self.view.bounds.size.height - TOOLBAR_HEIGHT : 0.0; CGRect toolbarFrame = CGRectMake(0.0, toolbarY, self.view.bounds.size.width, TOOLBAR_HEIGHT); - + self.toolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame]; self.toolbar.alpha = 1.000; self.toolbar.autoresizesSubviews = YES; @@ -567,10 +827,16 @@ self.toolbar.multipleTouchEnabled = NO; self.toolbar.opaque = NO; self.toolbar.userInteractionEnabled = YES; - + if (_browserOptions.toolbarcolor != nil) { // Set toolbar color if user sets it in options + self.toolbar.barTintColor = [self colorFromHexString:_browserOptions.toolbarcolor]; + } + if (!_browserOptions.toolbartranslucent) { // Set toolbar translucent to no if user sets it in options + self.toolbar.translucent = NO; + } + CGFloat labelInset = 5.0; float locationBarY = toolbarIsAtBottom ? self.view.bounds.size.height - FOOTER_HEIGHT : self.view.bounds.size.height - LOCATIONBAR_HEIGHT; - + self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, locationBarY, self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)]; self.addressLabel.adjustsFontSizeToFitWidth = NO; self.addressLabel.alpha = 1.000; @@ -584,13 +850,13 @@ self.addressLabel.enabled = YES; self.addressLabel.hidden = NO; self.addressLabel.lineBreakMode = NSLineBreakByTruncatingTail; - + if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumScaleFactor:")]) { [self.addressLabel setValue:@(10.0/[UIFont labelFontSize]) forKey:@"minimumScaleFactor"]; } else if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumFontSize:")]) { [self.addressLabel setValue:@(10.0) forKey:@"minimumFontSize"]; } - + self.addressLabel.multipleTouchEnabled = NO; self.addressLabel.numberOfLines = 1; self.addressLabel.opaque = NO; @@ -599,84 +865,108 @@ self.addressLabel.textAlignment = NSTextAlignmentLeft; self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000]; self.addressLabel.userInteractionEnabled = NO; - + NSString* frontArrowString = NSLocalizedString(@"►", nil); // create arrow from Unicode char self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)]; self.forwardButton.enabled = YES; self.forwardButton.imageInsets = UIEdgeInsetsZero; + if (_browserOptions.navigationbuttoncolor != nil) { // Set button color if user sets it in options + self.forwardButton.tintColor = [self colorFromHexString:_browserOptions.navigationbuttoncolor]; + } NSString* backArrowString = NSLocalizedString(@"◄", nil); // create arrow from Unicode char self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)]; self.backButton.enabled = YES; self.backButton.imageInsets = UIEdgeInsetsZero; + if (_browserOptions.navigationbuttoncolor != nil) { // Set button color if user sets it in options + self.backButton.tintColor = [self colorFromHexString:_browserOptions.navigationbuttoncolor]; + } - [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]]; - - self.view.backgroundColor = [UIColor grayColor]; + // Filter out Navigation Buttons if user requests so + if (_browserOptions.hidenavigationbuttons) { + if (_browserOptions.lefttoright) { + [self.toolbar setItems:@[flexibleSpaceButton, self.closeButton]]; + } else { + [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton]]; + } + } else if (_browserOptions.lefttoright) { + [self.toolbar setItems:@[self.backButton, fixedSpaceButton, self.forwardButton, flexibleSpaceButton, self.closeButton]]; + } else { + [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]]; + } + + self.view.backgroundColor = [UIColor clearColor]; [self.view addSubview:self.toolbar]; [self.view addSubview:self.addressLabel]; [self.view addSubview:self.spinner]; } +- (id)settingForKey:(NSString*)key +{ + return [_settings objectForKey:[key lowercaseString]]; +} + - (void) setWebViewFrame : (CGRect) frame { NSLog(@"Setting the WebView's frame to %@", NSStringFromCGRect(frame)); [self.webView setFrame:frame]; } -- (void)setCloseButtonTitle:(NSString*)title +- (void)setCloseButtonTitle:(NSString*)title : (NSString*) colorString : (int) buttonIndex { // the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically // but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one) self.closeButton = nil; - self.closeButton = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)]; + // Initialize with title if title is set, otherwise the title will be 'Done' localized + self.closeButton = title != nil ? [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)] : [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)]; self.closeButton.enabled = YES; - self.closeButton.tintColor = [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1]; - + // If color on closebutton is requested then initialize with that that color, otherwise use initialize with default + self.closeButton.tintColor = colorString != nil ? [self colorFromHexString:colorString] : [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1]; + NSMutableArray* items = [self.toolbar.items mutableCopy]; - [items replaceObjectAtIndex:0 withObject:self.closeButton]; + [items replaceObjectAtIndex:buttonIndex withObject:self.closeButton]; [self.toolbar setItems:items]; } - (void)showLocationBar:(BOOL)show { CGRect locationbarFrame = self.addressLabel.frame; - + BOOL toolbarVisible = !self.toolbar.hidden; - + // prevent double show/hide if (show == !(self.addressLabel.hidden)) { return; } - + if (show) { self.addressLabel.hidden = NO; - + if (toolbarVisible) { // toolBar at the bottom, leave as is // put locationBar on top of the toolBar - + CGRect webViewBounds = self.view.bounds; webViewBounds.size.height -= FOOTER_HEIGHT; [self setWebViewFrame:webViewBounds]; - + locationbarFrame.origin.y = webViewBounds.size.height; self.addressLabel.frame = locationbarFrame; } else { // no toolBar, so put locationBar at the bottom - + CGRect webViewBounds = self.view.bounds; webViewBounds.size.height -= LOCATIONBAR_HEIGHT; [self setWebViewFrame:webViewBounds]; - + locationbarFrame.origin.y = webViewBounds.size.height; self.addressLabel.frame = locationbarFrame; } } else { self.addressLabel.hidden = YES; - + if (toolbarVisible) { // locationBar is on top of toolBar, hide locationBar - + // webView take up whole height less toolBar height CGRect webViewBounds = self.view.bounds; webViewBounds.size.height -= TOOLBAR_HEIGHT; @@ -692,18 +982,18 @@ { CGRect toolbarFrame = self.toolbar.frame; CGRect locationbarFrame = self.addressLabel.frame; - + BOOL locationbarVisible = !self.addressLabel.hidden; - + // prevent double show/hide if (show == !(self.toolbar.hidden)) { return; } - + if (show) { self.toolbar.hidden = NO; CGRect webViewBounds = self.view.bounds; - + if (locationbarVisible) { // locationBar at the bottom, move locationBar up // put toolBar at the bottom @@ -717,7 +1007,7 @@ webViewBounds.size.height -= TOOLBAR_HEIGHT; self.toolbar.frame = toolbarFrame; } - + if ([toolbarPosition isEqualToString:kInAppBrowserToolbarBarPositionTop]) { toolbarFrame.origin.y = 0; webViewBounds.origin.y += toolbarFrame.size.height; @@ -726,19 +1016,19 @@ toolbarFrame.origin.y = (webViewBounds.size.height + LOCATIONBAR_HEIGHT); } [self setWebViewFrame:webViewBounds]; - + } else { self.toolbar.hidden = YES; - + if (locationbarVisible) { // locationBar is on top of toolBar, hide toolBar // put locationBar at the bottom - + // webView take up whole height less locationBar height CGRect webViewBounds = self.view.bounds; webViewBounds.size.height -= LOCATIONBAR_HEIGHT; [self setWebViewFrame:webViewBounds]; - + // move locationBar down locationbarFrame.origin.y = webViewBounds.size.height; self.addressLabel.frame = locationbarFrame; @@ -754,11 +1044,13 @@ [super viewDidLoad]; } -- (void)viewDidUnload +- (void)viewDidDisappear:(BOOL)animated { - [self.webView loadHTMLString:nil baseURL:nil]; - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; - [super viewDidUnload]; + [super viewDidDisappear:animated]; + if (isExiting && (self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) { + [self.navigationDelegate browserExit]; + isExiting = FALSE; + } } - (UIStatusBarStyle)preferredStatusBarStyle @@ -772,17 +1064,13 @@ - (void)close { - [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; self.currentURL = nil; - - if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) { - [self.navigationDelegate browserExit]; - } - + __weak UIViewController* weakSelf = self; - + // Run later to avoid the "took a long time" log message. dispatch_async(dispatch_get_main_queue(), ^{ + isExiting = TRUE; if ([weakSelf respondsToSelector:@selector(presentingViewController)]) { [[weakSelf presentingViewController] dismissViewControllerAnimated:YES completion:nil]; } else { @@ -793,17 +1081,11 @@ - (void)navigateTo:(NSURL*)url { - NSURLRequest* request = [NSURLRequest requestWithURL:url]; - - if (_userAgentLockToken != 0) { - [self.webView loadRequest:request]; + if ([url.scheme isEqualToString:@"file"]) { + [self.webView loadFileURL:url allowingReadAccessToURL:url]; } else { - __weak CDVInAppBrowserViewController* weakSelf = self; - [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) { - _userAgentLockToken = lockToken; - [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken]; - [weakSelf.webView loadRequest:request]; - }]; + NSURLRequest* request = [NSURLRequest requestWithURL:url]; + [self.webView loadRequest:request]; } } @@ -819,11 +1101,8 @@ - (void)viewWillAppear:(BOOL)animated { - if (IsAtLeastiOSVersion(@"7.0")) { - [[UIApplication sharedApplication] setStatusBarStyle:[self preferredStatusBarStyle]]; - } [self rePositionViews]; - + [super viewWillAppear:animated]; } @@ -833,213 +1112,117 @@ // change that value. // - (float) getStatusBarOffset { - CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame]; - float statusBarOffset = IsAtLeastiOSVersion(@"7.0") ? MIN(statusBarFrame.size.width, statusBarFrame.size.height) : 0.0; - return statusBarOffset; + return (float) IsAtLeastiOSVersion(@"7.0") ? [[UIApplication sharedApplication] statusBarFrame].size.height : 0.0; } - (void) rePositionViews { - if ([_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]) { - [self.webView setFrame:CGRectMake(self.webView.frame.origin.x, TOOLBAR_HEIGHT, self.webView.frame.size.width, self.webView.frame.size.height)]; - [self.toolbar setFrame:CGRectMake(self.toolbar.frame.origin.x, [self getStatusBarOffset], self.toolbar.frame.size.width, self.toolbar.frame.size.height)]; + CGRect viewBounds = [self.webView bounds]; + CGFloat statusBarHeight = [self getStatusBarOffset]; + + // orientation portrait or portraitUpsideDown: status bar is on the top and web view is to be aligned to the bottom of the status bar + // orientation landscapeLeft or landscapeRight: status bar height is 0 in but lets account for it in case things ever change in the future + viewBounds.origin.y = statusBarHeight; + + // account for web view height portion that may have been reduced by a previous call to this method + viewBounds.size.height = viewBounds.size.height - statusBarHeight + lastReducedStatusBarHeight; + lastReducedStatusBarHeight = statusBarHeight; + + if ((_browserOptions.toolbar) && ([_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop])) { + // if we have to display the toolbar on top of the web view, we need to account for its height + viewBounds.origin.y += TOOLBAR_HEIGHT; + self.toolbar.frame = CGRectMake(self.toolbar.frame.origin.x, statusBarHeight, self.toolbar.frame.size.width, self.toolbar.frame.size.height); } + + self.webView.frame = viewBounds; } -#pragma mark UIWebViewDelegate +// Helper function to convert hex color string to UIColor +// Assumes input like "#00FF00" (#RRGGBB). +// Taken from https://stackoverflow.com/questions/1560081/how-can-i-create-a-uicolor-from-a-hex-string +- (UIColor *)colorFromHexString:(NSString *)hexString { + unsigned rgbValue = 0; + NSScanner *scanner = [NSScanner scannerWithString:hexString]; + [scanner setScanLocation:1]; // bypass '#' character + [scanner scanHexInt:&rgbValue]; + return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0]; +} -- (void)webViewDidStartLoad:(UIWebView*)theWebView -{ - // loading url, start spinner, update back/forward +#pragma mark WKNavigationDelegate +- (void)webView:(WKWebView *)theWebView didStartProvisionalNavigation:(WKNavigation *)navigation{ + + // loading url, start spinner, update back/forward + self.addressLabel.text = NSLocalizedString(@"Loading...", nil); self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; - - [self.spinner startAnimating]; - - return [self.navigationDelegate webViewDidStartLoad:theWebView]; + + NSLog(_browserOptions.hidespinner ? @"Yes" : @"No"); + if(!_browserOptions.hidespinner) { + [self.spinner startAnimating]; + } + + return [self.navigationDelegate didStartProvisionalNavigation:theWebView]; } -- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +- (void)webView:(WKWebView *)theWebView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { - BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; - + NSURL *url = navigationAction.request.URL; + NSURL *mainDocumentURL = navigationAction.request.mainDocumentURL; + + BOOL isTopLevelNavigation = [url isEqual:mainDocumentURL]; + if (isTopLevelNavigation) { - self.currentURL = request.URL; + self.currentURL = url; } - return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; + + [self.navigationDelegate webView:theWebView decidePolicyForNavigationAction:navigationAction decisionHandler:decisionHandler]; } -- (void)webViewDidFinishLoad:(UIWebView*)theWebView +- (void)webView:(WKWebView *)theWebView didFinishNavigation:(WKNavigation *)navigation { // update url, stop spinner, update back/forward - + self.addressLabel.text = [self.currentURL absoluteString]; self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; - + theWebView.scrollView.contentInset = UIEdgeInsetsZero; + [self.spinner stopAnimating]; - - // Work around a bug where the first time a PDF is opened, all UIWebViews - // reload their User-Agent from NSUserDefaults. - // This work-around makes the following assumptions: - // 1. The app has only a single Cordova Webview. If not, then the app should - // take it upon themselves to load a PDF in the background as a part of - // their start-up flow. - // 2. That the PDF does not require any additional network requests. We change - // the user-agent here back to that of the CDVViewController, so requests - // from it must pass through its white-list. This *does* break PDFs that - // contain links to other remote PDF/websites. - // More info at https://issues.apache.org/jira/browse/CB-2225 - BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]]; - if (isPDF) { - [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken]; - } - - [self.navigationDelegate webViewDidFinishLoad:theWebView]; + + [self.navigationDelegate didFinishNavigation:theWebView]; } - -- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error -{ + +- (void)webView:(WKWebView*)theWebView failedNavigation:(NSString*) delegateName withError:(nonnull NSError *)error{ // log fail message, stop spinner, update back/forward - NSLog(@"webView:didFailLoadWithError - %ld: %@", (long)error.code, [error localizedDescription]); - + NSLog(@"webView:%@ - %ld: %@", delegateName, (long)error.code, [error localizedDescription]); + self.backButton.enabled = theWebView.canGoBack; self.forwardButton.enabled = theWebView.canGoForward; [self.spinner stopAnimating]; - + self.addressLabel.text = NSLocalizedString(@"Load Error", nil); - - [self.navigationDelegate webView:theWebView didFailLoadWithError:error]; -} - -#pragma mark CDVScreenOrientationDelegate - -- (BOOL)shouldAutorotate -{ - if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) { - return [self.orientationDelegate shouldAutorotate]; - } - return YES; -} - -- (NSUInteger)supportedInterfaceOrientations -{ - if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { - return [self.orientationDelegate supportedInterfaceOrientations]; - } - - return 1 << UIInterfaceOrientationPortrait; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { - return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; - } - - return YES; + + [self.navigationDelegate webView:theWebView didFailNavigation:error]; } -@end - -@implementation CDVInAppBrowserOptions - -- (id)init +- (void)webView:(WKWebView*)theWebView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error { - if (self = [super init]) { - // default values - self.location = YES; - self.toolbar = YES; - self.closebuttoncaption = nil; - self.toolbarposition = kInAppBrowserToolbarBarPositionBottom; - self.clearcache = NO; - self.clearsessioncache = NO; - - self.enableviewportscale = NO; - self.mediaplaybackrequiresuseraction = NO; - self.allowinlinemediaplayback = NO; - self.keyboarddisplayrequiresuseraction = YES; - self.suppressesincrementalrendering = NO; - self.hidden = NO; - self.disallowoverscroll = NO; - } - - return self; + [self webView:theWebView failedNavigation:@"didFailNavigation" withError:error]; } - -+ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options + +- (void)webView:(WKWebView*)theWebView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error { - CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init]; - - // NOTE: this parsing does not handle quotes within values - NSArray* pairs = [options componentsSeparatedByString:@","]; - - // parse keys and values, set the properties - for (NSString* pair in pairs) { - NSArray* keyvalue = [pair componentsSeparatedByString:@"="]; - - if ([keyvalue count] == 2) { - NSString* key = [[keyvalue objectAtIndex:0] lowercaseString]; - NSString* value = [keyvalue objectAtIndex:1]; - NSString* value_lc = [value lowercaseString]; - - BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"]; - NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init]; - [numberFormatter setAllowsFloats:YES]; - BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil; - - // set the property according to the key name - if ([obj respondsToSelector:NSSelectorFromString(key)]) { - if (isNumber) { - [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key]; - } else if (isBoolean) { - [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key]; - } else { - [obj setValue:value forKey:key]; - } - } - } - } - - return obj; + [self webView:theWebView failedNavigation:@"didFailProvisionalNavigation" withError:error]; } -@end - -@implementation CDVInAppBrowserNavigationController : UINavigationController - -- (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { - if ( self.presentedViewController) { - [super dismissViewControllerAnimated:flag completion:completion]; - } -} - -- (void) viewDidLoad { - - CGRect frame = [UIApplication sharedApplication].statusBarFrame; - - // simplified from: http://stackoverflow.com/a/25669695/219684 - - UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:[self invertFrameIfNeeded:frame]]; - bgToolbar.barStyle = UIBarStyleDefault; - [bgToolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; - [self.view addSubview:bgToolbar]; - - [super viewDidLoad]; -} - -- (CGRect) invertFrameIfNeeded:(CGRect)rect { - // We need to invert since on iOS 7 frames are always in Portrait context - if (!IsAtLeastiOSVersion(@"8.0")) { - if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) { - CGFloat temp = rect.size.width; - rect.size.width = rect.size.height; - rect.size.height = temp; - } - rect.origin = CGPointZero; +#pragma mark WKScriptMessageHandler delegate +- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { + if (![message.name isEqualToString:IAB_BRIDGE_NAME]) { + return; } - return rect; + //NSLog(@"Received script message %@", message.body); + [self.navigationDelegate userContentController:userContentController didReceiveScriptMessage:message]; } #pragma mark CDVScreenOrientationDelegate @@ -1052,24 +1235,27 @@ return YES; } -- (NSUInteger)supportedInterfaceOrientations +- (UIInterfaceOrientationMask)supportedInterfaceOrientations { if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) { return [self.orientationDelegate supportedInterfaceOrientations]; } - + return 1 << UIInterfaceOrientationPortrait; } -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { - if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) { - return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation]; - } + [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) + { + [self rePositionViews]; + } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) + { - return YES; -} + }]; + [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; +} -@end +@end //CDVWKInAppBrowserViewController diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.h new file mode 100644 index 00000000..1a6ea220 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.h @@ -0,0 +1,32 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <WebKit/WebKit.h> + +@interface CDVWKInAppBrowserUIDelegate : NSObject <WKUIDelegate>{ + @private + UIViewController* _viewController; +} + +@property (nonatomic, copy) NSString* title; + +- (instancetype)initWithTitle:(NSString*)title; +-(void) setViewController:(UIViewController*) viewController; + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.m b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.m new file mode 100644 index 00000000..4bc7a76b --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ios/CDVWKInAppBrowserUIDelegate.m @@ -0,0 +1,127 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVWKInAppBrowserUIDelegate.h" + +@implementation CDVWKInAppBrowserUIDelegate + +- (instancetype)initWithTitle:(NSString*)title +{ + self = [super init]; + if (self) { + self.title = title; + } + + return self; +} + +- (void) webView:(WKWebView*)webView runJavaScriptAlertPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + [[self getViewController] presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:message + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(YES); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(NO); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + [[self getViewController] presentViewController:alert animated:YES completion:nil]; +} + +- (void) webView:(WKWebView*)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt + defaultText:(NSString*)defaultText initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(NSString* result))completionHandler +{ + UIAlertController* alert = [UIAlertController alertControllerWithTitle:self.title + message:prompt + preferredStyle:UIAlertControllerStyleAlert]; + + UIAlertAction* ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(((UITextField*)alert.textFields[0]).text); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + + [alert addAction:ok]; + + UIAlertAction* cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel") + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) + { + completionHandler(nil); + [alert dismissViewControllerAnimated:YES completion:nil]; + }]; + [alert addAction:cancel]; + + [alert addTextFieldWithConfigurationHandler:^(UITextField* textField) { + textField.text = defaultText; + }]; + + [[self getViewController] presentViewController:alert animated:YES completion:nil]; +} + +-(UIViewController*) getViewController +{ + return _viewController; +} + +-(void) setViewController:(UIViewController*) viewController +{ + _viewController = viewController; +} + +@end diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.h new file mode 100644 index 00000000..742af70a --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.h @@ -0,0 +1,30 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import <Cordova/CDVPlugin.h> + +@interface CDVInAppBrowser : CDVPlugin { +} + +@property (nonatomic, copy) NSString* callbackId; + +- (void)open:(CDVInvokedUrlCommand*)command; + +@end + diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.m b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.m new file mode 100644 index 00000000..9d579e15 --- /dev/null +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/osx/CDVInAppBrowser.m @@ -0,0 +1,89 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVInAppBrowser.h" +#import <Cordova/CDVPluginResult.h> + +#define kInAppBrowserTargetSelf @"_self" +#define kInAppBrowserTargetSystem @"_system" +#define kInAppBrowserTargetBlank @"_blank" + +@interface CDVInAppBrowser () { +} +@end + +@implementation CDVInAppBrowser + +- (void)pluginInitialize +{ +} + +- (BOOL) isSystemUrl:(NSURL*)url +{ + if ([[url host] isEqualToString:@"itunes.apple.com"]) { + return YES; + } + + return NO; +} + +- (void)open:(CDVInvokedUrlCommand*)command +{ + CDVPluginResult* pluginResult; + + NSString* url = [command argumentAtIndex:0]; + NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf]; + + self.callbackId = command.callbackId; + + if (url != nil) { + + NSURL* baseUrl = [NSURL URLWithString:url]; + + NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL]; + + if ([self isSystemUrl:absoluteUrl]) { + target = kInAppBrowserTargetSystem; + } + + if ([target isEqualToString:kInAppBrowserTargetSelf]) { + //[self openInCordovaWebView:absoluteUrl withOptions:options]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Not Yet Implemented for OSX: [self openInCordovaWebView:absoluteUrl withOptions:options]"]; + } else if ([target isEqualToString:kInAppBrowserTargetSystem]) { + [self openInSystem:absoluteUrl]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { // _blank or anything else + //[self openInInAppBrowser:absoluteUrl withOptions:options]; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Not Yet Implemented for OSX: [self openInInAppBrowser:absoluteUrl withOptions:options]"]; + } + + } else { + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"]; + } + [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +} + +- (void)openInSystem:(NSURL*)url +{ + [[NSWorkspace sharedWorkspace] openURL:url]; +} + +@end + diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser.qml b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser.qml deleted file mode 100644 index 781e8a6e..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser.qml +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright 2013 Canonical Ltd. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ -import QtQuick 2.0 -import Ubuntu.Components.Popups 0.1 -import Ubuntu.Components 0.1 -import com.canonical.Oxide 1.0 - -Rectangle { - anchors.fill: parent - id: inappbrowser - property string url1 - Rectangle { - border.color: "black" - width: parent.width - height: urlEntry.height - color: "gray" - TextInput { - id: urlEntry - width: parent.width - closeButton.width - text: url1 - activeFocusOnPress: false - } - Image { - id: closeButton - width: height - x: parent.width - width - height: parent.height - source: "close.png" - MouseArea { - anchors.fill: parent - onClicked: { - root.exec("InAppBrowser", "close", [0, 0]) - } - } - } - } - - property string usContext: "oxide://main-world/2" - - function executeJS(scId, code) { - var req = _view.rootFrame.sendMessage(usContext, "EXECUTE", {code: code}); - - req.onreply = function(response) { - var code = 'cordova.callback(' + scId + ', JSON.parse(\'' + JSON.stringify(response.result) + '\'))'; - console.warn(code); - cordova.javaScriptExecNeeded(code); - console.warn("RESP:" + JSON.stringify(response)); - }; - } - - WebView { - width: parent.width - y: urlEntry.height - height: parent.height - y - url: url1 - id: _view - onLoadingStateChanged: { - root.exec("InAppBrowser", "loadFinished", [_view.loading]) - } - context: WebContext { - id: webcontext - - userScripts: [ - UserScript { - context: usContext - emulateGreasemonkey: true - url: "InAppBrowser_escapeScript.js" - } - ] - } - } -} diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser_escapeScript.js b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser_escapeScript.js deleted file mode 100644 index b01daeb5..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/InAppBrowser_escapeScript.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - -/* jshint -W061 */ -/* global oxide */ - -oxide.addMessageHandler("EXECUTE", function(msg) { - var code = msg.args.code; - try { - msg.reply({result: eval(code)}); - } catch(e) { - msg.error("Code threw exception: \"" + e + "\""); - } -}); diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/close.png b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/close.png Binary files differdeleted file mode 100644 index 56373d1f..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/close.png +++ /dev/null diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.cpp b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.cpp deleted file mode 100644 index c5a9e64a..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * Copyright 2013 Canonical Ltd. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - -#include <QQuickView> -#include <QQuickItem> - -#include "inappbrowser.h" -#include <cordova.h> - -Inappbrowser::Inappbrowser(Cordova *cordova): CPlugin(cordova), _eventCb(0) { -} - -const char code[] = "\ -var component; \ -function createObject() { \ - component = Qt.createComponent(%1); \ - if (component.status == Component.Ready) \ - finishCreation(); \ - else \ - component.statusChanged.connect(finishCreation); \ -} \ -function finishCreation() { \ - CordovaWrapper.global.inappbrowser = component.createObject(root, \ - {root: root, cordova: cordova, url1: %2}); \ -} \ -createObject()"; - -const char EXIT_EVENT[] = "{type: 'exit'}"; -const char LOADSTART_EVENT[] = "{type: 'loadstart'}"; -const char LOADSTOP_EVENT[] = "{type: 'loadstop'}"; -const char LOADERROR_EVENT[] = "{type: 'loaderror'}"; - -void Inappbrowser::open(int cb, int, const QString &url, const QString &, const QString &) { - assert(_eventCb == 0); - - _eventCb = cb; - - QString path = m_cordova->get_app_dir() + "/../qml/InAppBrowser.qml"; - QString qml = QString(code) - .arg(CordovaInternal::format(path)).arg(CordovaInternal::format(url)); - m_cordova->execQML(qml); -} - -void Inappbrowser::show(int, int) { - m_cordova->execQML("CordovaWrapper.global.inappbrowser.visible = true"); -} - -void Inappbrowser::close(int, int) { - m_cordova->execQML("CordovaWrapper.global.inappbrowser.destroy()"); - this->callbackWithoutRemove(_eventCb, EXIT_EVENT); - _eventCb = 0; -} - -void Inappbrowser::injectStyleFile(int scId, int ecId, const QString& src, bool b) { - QString code("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %1; d.head.appendChild(c);})(document)"); - code = code.arg(CordovaInternal::format(src)); - - injectScriptCode(scId, ecId, code, b); -} - -void Inappbrowser::injectStyleCode(int scId, int ecId, const QString& src, bool b) { - QString code("(function(d) { var c = d.createElement('style'); c.innerHTML = %1; d.body.appendChild(c); })(document)"); - code = code.arg(CordovaInternal::format(src)); - - injectScriptCode(scId, ecId, code, b); -} - -void Inappbrowser::injectScriptFile(int scId, int ecId, const QString& src, bool b) { - QString code("(function(d) { var c = d.createElement('script'); c.src = %1; d.body.appendChild(c);})(document)"); - code = code.arg(CordovaInternal::format(src)); - - injectScriptCode(scId, ecId, code, b); -} - -void Inappbrowser::injectScriptCode(int scId, int, const QString& code, bool) { - m_cordova->execQML(QString("CordovaWrapper.global.inappbrowser.executeJS(%2, %1)").arg(CordovaInternal::format(code)).arg(scId)); -} - -void Inappbrowser::loadFinished(bool status) { - if (!status) { - this->callbackWithoutRemove(_eventCb, LOADSTOP_EVENT); - } else { - this->callbackWithoutRemove(_eventCb, LOADSTART_EVENT); - } -} diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.h b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.h deleted file mode 100644 index 1da4e033..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/ubuntu/inappbrowser.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2013 Canonical Ltd. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ -#ifndef INAPPBROWSER_H -#define INAPPBROWSER_H - -#include <QtCore> -#include <cplugin.h> - -class Inappbrowser: public CPlugin { - Q_OBJECT -public: - Inappbrowser(Cordova *cordova); - - virtual const QString fullName() override { - return Inappbrowser::fullID(); - } - - virtual const QString shortName() override { - return "InAppBrowser"; - } - - static const QString fullID() { - return "InAppBrowser"; - } - -public slots: - void open(int cb, int, const QString &url, const QString &windowName, const QString &windowFeatures); - void show(int, int); - void close(int, int); - void injectStyleFile(int cb, int, const QString&, bool); - void injectStyleCode(int cb, int, const QString&, bool); - void injectScriptFile(int cb, int, const QString&, bool); - void injectScriptCode(int cb, int, const QString&, bool); - - void loadFinished(bool status); - -private: - int _eventCb; -}; - -#endif diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/windows/InAppBrowserProxy.js b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/windows/InAppBrowserProxy.js index 23f6e547..9e544c5d 100644 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/windows/InAppBrowserProxy.js +++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/windows/InAppBrowserProxy.js @@ -1,4 +1,4 @@ -/* +/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -21,9 +21,10 @@ /* jslint sloppy:true */ /* global Windows:true, setImmediate */ +/* eslint standard/no-callback-literal : 0 */ -var cordova = require('cordova'), - urlutil = require('cordova/urlutil'); +var cordova = require('cordova'); +var urlutil = require('cordova/urlutil'); var browserWrap, popup, @@ -40,56 +41,56 @@ var browserWrap, // http://msdn.microsoft.com/en-us/library/windows/apps/dn301831.aspx var isWebViewAvailable = cordova.platformId === 'windows'; -function attachNavigationEvents(element, callback) { +function attachNavigationEvents (element, callback) { if (isWebViewAvailable) { - element.addEventListener("MSWebViewNavigationStarting", function (e) { - callback({ type: "loadstart", url: e.uri}, {keepCallback: true} ); + element.addEventListener('MSWebViewNavigationStarting', function (e) { + callback({ type: 'loadstart', url: e.uri }, {keepCallback: true}); }); - element.addEventListener("MSWebViewNavigationCompleted", function (e) { + element.addEventListener('MSWebViewNavigationCompleted', function (e) { if (e.isSuccess) { - callback({ type: "loadstop", url: e.uri }, { keepCallback: true }); + callback({ type: 'loadstop', url: e.uri }, { keepCallback: true }); } else { - callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true }); + callback({ type: 'loaderror', url: e.uri, code: e.webErrorStatus, message: 'Navigation failed with error code ' + e.webErrorStatus }, { keepCallback: true }); } }); - element.addEventListener("MSWebViewUnviewableContentIdentified", function (e) { + element.addEventListener('MSWebViewUnviewableContentIdentified', function (e) { // WebView found the content to be not HTML. // http://msdn.microsoft.com/en-us/library/windows/apps/dn609716.aspx - callback({ type: "loaderror", url: e.uri, code: e.webErrorStatus, message: "Navigation failed with error code " + e.webErrorStatus}, { keepCallback: true }); + callback({ type: 'loaderror', url: e.uri, code: e.webErrorStatus, message: 'Navigation failed with error code ' + e.webErrorStatus }, { keepCallback: true }); }); - element.addEventListener("MSWebViewContentLoading", function (e) { + element.addEventListener('MSWebViewContentLoading', function (e) { if (navigationButtonsDiv && popup) { if (popup.canGoBack) { - backButton.removeAttribute("disabled"); + backButton.removeAttribute('disabled'); } else { - backButton.setAttribute("disabled", "true"); + backButton.setAttribute('disabled', 'true'); } if (popup.canGoForward) { - forwardButton.removeAttribute("disabled"); + forwardButton.removeAttribute('disabled'); } else { - forwardButton.setAttribute("disabled", "true"); + forwardButton.setAttribute('disabled', 'true'); } } }); } else { var onError = function () { - callback({ type: "loaderror", url: this.contentWindow.location}, {keepCallback: true}); + callback({ type: 'loaderror', url: this.contentWindow.location }, {keepCallback: true}); }; - element.addEventListener("unload", function () { - callback({ type: "loadstart", url: this.contentWindow.location}, {keepCallback: true}); + element.addEventListener('unload', function () { + callback({ type: 'loadstart', url: this.contentWindow.location }, {keepCallback: true}); }); - element.addEventListener("load", function () { - callback({ type: "loadstop", url: this.contentWindow.location}, {keepCallback: true}); + element.addEventListener('load', function () { + callback({ type: 'loadstop', url: this.contentWindow.location }, {keepCallback: true}); }); - element.addEventListener("error", onError); - element.addEventListener("abort", onError); + element.addEventListener('error', onError); + element.addEventListener('abort', onError); } } @@ -98,7 +99,7 @@ var IAB = { setImmediate(function () { if (browserWrap) { if (navigationEventsCallback) { - navigationEventsCallback({ type: "exit" }); + navigationEventsCallback({ type: 'exit' }); } browserWrap.parentNode.removeChild(browserWrap); @@ -107,47 +108,52 @@ var IAB = { browserWrap = null; popup = null; - document.removeEventListener("backbutton", hardwareBackCallback, false); + document.removeEventListener('backbutton', hardwareBackCallback, false); } }); }, show: function (win, lose) { setImmediate(function () { if (browserWrap) { - browserWrap.style.display = "block"; + browserWrap.style.display = 'block'; } }); }, + hide: function (win, lose) { + if (browserWrap) { + browserWrap.style.display = 'none'; + } + }, open: function (win, lose, args) { // make function async so that we can add navigation events handlers before view is loaded and navigation occured setImmediate(function () { - var strUrl = args[0], - target = args[1], - features = args[2], - url; + var strUrl = args[0]; + var target = args[1]; + var features = args[2]; + var url; navigationEventsCallback = win; - if (target === "_system") { + if (target === '_system') { url = new Windows.Foundation.Uri(strUrl); Windows.System.Launcher.launchUriAsync(url); - } else if (target === "_self" || !target) { + } else if (target === '_self' || !target) { window.location = strUrl; } else { // "_blank" or anything else if (!browserWrap) { var browserWrapStyle = document.createElement('link'); - browserWrapStyle.rel = "stylesheet"; - browserWrapStyle.type = "text/css"; - browserWrapStyle.href = urlutil.makeAbsolute("/www/css/inappbrowser.css"); + browserWrapStyle.rel = 'stylesheet'; + browserWrapStyle.type = 'text/css'; + browserWrapStyle.href = urlutil.makeAbsolute('/www/css/inappbrowser.css'); document.head.appendChild(browserWrapStyle); - browserWrap = document.createElement("div"); - browserWrap.className = "inAppBrowserWrap"; + browserWrap = document.createElement('div'); + browserWrap.className = 'inAppBrowserWrap'; - if (features.indexOf("fullscreen=yes") > -1) { - browserWrap.classList.add("inAppBrowserWrapFullscreen"); + if (features.indexOf('fullscreen=yes') > -1) { + browserWrap.classList.add('inAppBrowserWrapFullscreen'); } // Save body overflow style to be able to reset it back later @@ -161,22 +167,22 @@ var IAB = { document.body.appendChild(browserWrap); // Hide scrollbars for the whole body while inappbrowser's window is open - document.body.style.msOverflowStyle = "none"; + document.body.style.msOverflowStyle = 'none'; } - if (features.indexOf("hidden=yes") !== -1) { - browserWrap.style.display = "none"; + if (features.indexOf('hidden=yes') !== -1) { + browserWrap.style.display = 'none'; } - popup = document.createElement(isWebViewAvailable ? "x-ms-webview" : "iframe"); - if (popup instanceof HTMLIFrameElement) { + popup = document.createElement(isWebViewAvailable ? 'x-ms-webview' : 'iframe'); + if (popup instanceof HTMLIFrameElement) { // eslint-disable-line no-undef // For iframe we need to override bacground color of parent element here // otherwise pages without background color set will have transparent background - popup.style.backgroundColor = "white"; + popup.style.backgroundColor = 'white'; } - popup.style.borderWidth = "0px"; - popup.style.width = "100%"; - popup.style.marginBottom = "-3px"; + popup.style.borderWidth = '0px'; + popup.style.width = '100%'; + popup.style.marginBottom = '-5px'; browserWrap.appendChild(popup); @@ -186,14 +192,14 @@ var IAB = { }, 0); }; - if (features.indexOf("hardwareback=yes") > -1 || features.indexOf("hardwareback") === -1) { + if (features.indexOf('hardwareback=yes') > -1 || features.indexOf('hardwareback') === -1) { hardwareBackCallback = function () { if (browserWrap.style.display === 'none') { // NOTE: backbutton handlers have to throw an exception in order to prevent // returning 'true' inside cordova-js, which would mean that the event is handled by user. // Throwing an exception means that the default/system navigation behavior will take place, // which is to exit the app if the navigation stack is empty. - throw 'Exit the app'; + throw 'Exit the app'; // eslint-disable-line no-throw-literal } if (popup.canGoBack) { @@ -202,59 +208,57 @@ var IAB = { closeHandler(); } }; - } else if (features.indexOf("hardwareback=no") > -1) { + } else if (features.indexOf('hardwareback=no') > -1) { hardwareBackCallback = function () { if (browserWrap.style.display === 'none') { // See comment above - throw 'Exit the app'; + throw 'Exit the app'; // eslint-disable-line no-throw-literal } closeHandler(); }; } - document.addEventListener("backbutton", hardwareBackCallback, false); + document.addEventListener('backbutton', hardwareBackCallback, false); - if (features.indexOf("location=yes") !== -1 || features.indexOf("location") === -1) { - popup.style.height = "calc(100% - 70px)"; + if (features.indexOf('location=yes') !== -1 || features.indexOf('location') === -1) { + popup.style.height = 'calc(100% - 70px)'; - navigationButtonsDiv = document.createElement("div"); - navigationButtonsDiv.className = "inappbrowser-app-bar"; + navigationButtonsDiv = document.createElement('div'); + navigationButtonsDiv.className = 'inappbrowser-app-bar'; navigationButtonsDiv.onclick = function (e) { e.cancelBubble = true; }; - navigationButtonsDivInner = document.createElement("div"); - navigationButtonsDivInner.className = "inappbrowser-app-bar-inner"; + navigationButtonsDivInner = document.createElement('div'); + navigationButtonsDivInner.className = 'inappbrowser-app-bar-inner'; navigationButtonsDivInner.onclick = function (e) { e.cancelBubble = true; }; - backButton = document.createElement("div"); - backButton.innerText = "back"; - backButton.className = "app-bar-action action-back"; - backButton.addEventListener("click", function (e) { - if (popup.canGoBack) - popup.goBack(); + backButton = document.createElement('div'); + backButton.innerText = 'back'; + backButton.className = 'app-bar-action action-back'; + backButton.addEventListener('click', function (e) { + if (popup.canGoBack) { popup.goBack(); } }); - forwardButton = document.createElement("div"); - forwardButton.innerText = "forward"; - forwardButton.className = "app-bar-action action-forward"; - forwardButton.addEventListener("click", function (e) { - if (popup.canGoForward) - popup.goForward(); + forwardButton = document.createElement('div'); + forwardButton.innerText = 'forward'; + forwardButton.className = 'app-bar-action action-forward'; + forwardButton.addEventListener('click', function (e) { + if (popup.canGoForward) { popup.goForward(); } }); - closeButton = document.createElement("div"); - closeButton.innerText = "close"; - closeButton.className = "app-bar-action action-close"; - closeButton.addEventListener("click", closeHandler); + closeButton = document.createElement('div'); + closeButton.innerText = 'close'; + closeButton.className = 'app-bar-action action-close'; + closeButton.addEventListener('click', closeHandler); if (!isWebViewAvailable) { // iframe navigation is not yet supported - backButton.setAttribute("disabled", "true"); - forwardButton.setAttribute("disabled", "true"); + backButton.setAttribute('disabled', 'true'); + forwardButton.setAttribute('disabled', 'true'); } navigationButtonsDivInner.appendChild(backButton); @@ -264,14 +268,14 @@ var IAB = { browserWrap.appendChild(navigationButtonsDiv); } else { - popup.style.height = "100%"; + popup.style.height = '100%'; } // start listening for navigation events attachNavigationEvents(popup, navigationEventsCallback); if (isWebViewAvailable) { - strUrl = strUrl.replace("ms-appx://", "ms-appx-web://"); + strUrl = strUrl.replace('ms-appx://', 'ms-appx-web://'); } popup.src = strUrl; } @@ -280,11 +284,11 @@ var IAB = { injectScriptCode: function (win, fail, args) { setImmediate(function () { - var code = args[0], - hasCallback = args[1]; + var code = args[0]; + var hasCallback = args[1]; if (isWebViewAvailable && browserWrap && popup) { - var op = popup.invokeScriptAsync("eval", code); + var op = popup.invokeScriptAsync('eval', code); op.oncomplete = function (e) { if (hasCallback) { // return null if event target is unavailable by some reason @@ -300,19 +304,20 @@ var IAB = { injectScriptFile: function (win, fail, args) { setImmediate(function () { - var filePath = args[0], - hasCallback = args[1]; + var filePath = args[0]; + var hasCallback = args[1]; - if (!!filePath) { + if (filePath) { filePath = urlutil.makeAbsolute(filePath); } if (isWebViewAvailable && browserWrap && popup) { - var uri = new Windows.Foundation.Uri(filePath); + // CB-12364 getFileFromApplicationUriAsync does not support ms-appx-web + var uri = new Windows.Foundation.Uri(filePath.replace('ms-appx-web:', 'ms-appx:')); Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).done(function (file) { Windows.Storage.FileIO.readTextAsync(file).done(function (code) { - var op = popup.invokeScriptAsync("eval", code); - op.oncomplete = function(e) { + var op = popup.invokeScriptAsync('eval', code); + op.oncomplete = function (e) { if (hasCallback) { var result = [e.target.result]; win(result); @@ -328,8 +333,8 @@ var IAB = { injectStyleCode: function (win, fail, args) { setImmediate(function () { - var code = args[0], - hasCallback = args[1]; + var code = args[0]; + var hasCallback = args[1]; if (isWebViewAvailable && browserWrap && popup) { injectCSS(popup, code, hasCallback && win); @@ -339,13 +344,14 @@ var IAB = { injectStyleFile: function (win, fail, args) { setImmediate(function () { - var filePath = args[0], - hasCallback = args[1]; + var filePath = args[0]; + var hasCallback = args[1]; filePath = filePath && urlutil.makeAbsolute(filePath); if (isWebViewAvailable && browserWrap && popup) { - var uri = new Windows.Foundation.Uri(filePath); + // CB-12364 getFileFromApplicationUriAsync does not support ms-appx-web + var uri = new Windows.Foundation.Uri(filePath.replace('ms-appx-web:', 'ms-appx:')); Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) { return Windows.Storage.FileIO.readTextAsync(file); }).done(function (code) { @@ -361,11 +367,11 @@ var IAB = { function injectCSS (webView, cssCode, callback) { // This will automatically escape all thing that we need (quotes, slashes, etc.) var escapedCode = JSON.stringify(cssCode); - var evalWrapper = "(function(d){var c=d.createElement('style');c.innerHTML=%s;d.head.appendChild(c);})(document)" + var evalWrapper = '(function(d){var c=d.createElement(\'style\');c.innerHTML=%s;d.head.appendChild(c);})(document)' .replace('%s', escapedCode); - var op = webView.invokeScriptAsync("eval", evalWrapper); - op.oncomplete = function() { + var op = webView.invokeScriptAsync('eval', evalWrapper); + op.oncomplete = function () { if (callback) { callback([]); } @@ -376,4 +382,4 @@ function injectCSS (webView, cssCode, callback) { module.exports = IAB; -require("cordova/exec/proxy").add("InAppBrowser", module.exports); +require('cordova/exec/proxy').add('InAppBrowser', module.exports); diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/wp/InAppBrowser.cs b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/wp/InAppBrowser.cs deleted file mode 100644 index ddb51227..00000000 --- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/wp/InAppBrowser.cs +++ /dev/null @@ -1,515 +0,0 @@ -/* - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.Serialization; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using Microsoft.Phone.Controls; -using Microsoft.Phone.Shell; - -#if WP8 -using System.Threading.Tasks; -using Windows.ApplicationModel; -using Windows.Storage; -using Windows.System; - -//Use alias in case Cordova File Plugin is enabled. Then the File class will be declared in both and error will occur. -using IOFile = System.IO.File; -#else -using Microsoft.Phone.Tasks; -#endif - -namespace WPCordovaClassLib.Cordova.Commands -{ - [DataContract] - public class BrowserOptions - { - [DataMember] - public string url; - - [DataMember] - public bool isGeolocationEnabled; - } - - public class InAppBrowser : BaseCommand - { - - private static WebBrowser browser; - private static ApplicationBarIconButton backButton; - private static ApplicationBarIconButton fwdButton; - - protected ApplicationBar AppBar; - - protected bool ShowLocation {get;set;} - protected bool StartHidden {get;set;} - - protected string NavigationCallbackId { get; set; } - - public void open(string options) - { - // reset defaults on ShowLocation + StartHidden features - ShowLocation = true; - StartHidden = false; - - string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - //BrowserOptions opts = JSON.JsonHelper.Deserialize<BrowserOptions>(options); - string urlLoc = args[0]; - string target = args[1]; - string featString = args[2]; - this.NavigationCallbackId = args[3]; - - if (!string.IsNullOrEmpty(featString)) - { - string[] features = featString.Split(','); - foreach (string str in features) - { - try - { - string[] split = str.Split('='); - switch (split[0]) - { - case "location": - ShowLocation = split[1].StartsWith("yes", StringComparison.OrdinalIgnoreCase); - break; - case "hidden": - StartHidden = split[1].StartsWith("yes", StringComparison.OrdinalIgnoreCase); - break; - } - } - catch (Exception) - { - // some sort of invalid param was passed, moving on ... - } - } - } - /* - _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser - _blank - always open in the InAppBrowser - _system - always open in the system web browser - */ - switch (target) - { - case "_blank": - ShowInAppBrowser(urlLoc); - break; - case "_self": - ShowCordovaBrowser(urlLoc); - break; - case "_system": - ShowSystemBrowser(urlLoc); - break; - } - } - - public void show(string options) - { - string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - - - if (browser != null) - { - Deployment.Current.Dispatcher.BeginInvoke(() => - { - browser.Visibility = Visibility.Visible; - AppBar.IsVisible = true; - }); - } - } - - public void injectScriptCode(string options) - { - string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - - bool bCallback = false; - if (bool.TryParse(args[1], out bCallback)) { }; - - string callbackId = args[2]; - - if (browser != null) - { - Deployment.Current.Dispatcher.BeginInvoke(() => - { - var res = browser.InvokeScript("eval", new string[] { args[0] }); - - if (bCallback) - { - PluginResult result = new PluginResult(PluginResult.Status.OK, res.ToString()); - result.KeepCallback = false; - this.DispatchCommandResult(result); - } - - }); - } - } - - public void injectScriptFile(string options) - { - Debug.WriteLine("Error : Windows Phone cordova-plugin-inappbrowser does not currently support executeScript"); - string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - // throw new NotImplementedException("Windows Phone does not currently support 'executeScript'"); - } - - public void injectStyleCode(string options) - { - Debug.WriteLine("Error : Windows Phone cordova-plugin-inappbrowser does not currently support insertCSS"); - return; - - //string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - //bool bCallback = false; - //if (bool.TryParse(args[1], out bCallback)) { }; - - //string callbackId = args[2]; - - //if (browser != null) - //{ - //Deployment.Current.Dispatcher.BeginInvoke(() => - //{ - // if (bCallback) - // { - // string cssInsertString = "try{(function(doc){var c = '<style>body{background-color:#ffff00;}</style>'; doc.head.innerHTML += c;})(document);}catch(ex){alert('oops : ' + ex.message);}"; - // //cssInsertString = cssInsertString.Replace("_VALUE_", args[0]); - // Debug.WriteLine("cssInsertString = " + cssInsertString); - // var res = browser.InvokeScript("eval", new string[] { cssInsertString }); - // if (bCallback) - // { - // PluginResult result = new PluginResult(PluginResult.Status.OK, res.ToString()); - // result.KeepCallback = false; - // this.DispatchCommandResult(result); - // } - // } - - //}); - //} - } - - public void injectStyleFile(string options) - { - Debug.WriteLine("Error : Windows Phone cordova-plugin-inappbrowser does not currently support insertCSS"); - return; - - //string[] args = JSON.JsonHelper.Deserialize<string[]>(options); - //throw new NotImplementedException("Windows Phone does not currently support 'insertCSS'"); - } - - private void ShowCordovaBrowser(string url) - { - Uri loc = new Uri(url, UriKind.RelativeOrAbsolute); - Deployment.Current.Dispatcher.BeginInvoke(() => - { - PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; - if (frame != null) - { - PhoneApplicationPage page = frame.Content as PhoneApplicationPage; - if (page != null) - { - CordovaView cView = page.FindName("CordovaView") as CordovaView; - if (cView != null) - { - WebBrowser br = cView.Browser; - br.Navigate2(loc); - } - } - - } - }); - } - -#if WP8 - private async void ShowSystemBrowser(string url) - { - var pathUri = new Uri(url, UriKind.Absolute); - if (pathUri.Scheme == Uri.UriSchemeHttp || pathUri.Scheme == Uri.UriSchemeHttps) - { - await Launcher.LaunchUriAsync(pathUri); - return; - } - - var file = await GetFile(pathUri.AbsolutePath.Replace('/', Path.DirectorySeparatorChar)); - if (file != null) - { - await Launcher.LaunchFileAsync(file); - } - else - { - Debug.WriteLine("File not found."); - } - } - - private async Task<StorageFile> GetFile(string fileName) - { - //first try to get the file from the isolated storage - var localFolder = ApplicationData.Current.LocalFolder; - if (IOFile.Exists(Path.Combine(localFolder.Path, fileName))) - { - return await localFolder.GetFileAsync(fileName); - } - - //if file is not found try to get it from the xap - var filePath = Path.Combine(Package.Current.InstalledLocation.Path, fileName); - if (IOFile.Exists(filePath)) - { - return await StorageFile.GetFileFromPathAsync(filePath); - } - - return null; - } -#else - private void ShowSystemBrowser(string url) - { - WebBrowserTask webBrowserTask = new WebBrowserTask(); - webBrowserTask.Uri = new Uri(url, UriKind.Absolute); - webBrowserTask.Show(); - } -#endif - - private void ShowInAppBrowser(string url) - { - Uri loc = new Uri(url, UriKind.RelativeOrAbsolute); - - Deployment.Current.Dispatcher.BeginInvoke(() => - { - if (browser != null) - { - //browser.IsGeolocationEnabled = opts.isGeolocationEnabled; - browser.Navigate2(loc); - } - else - { - PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; - if (frame != null) - { - PhoneApplicationPage page = frame.Content as PhoneApplicationPage; - - string baseImageUrl = "Images/"; - - if (page != null) - { - Grid grid = page.FindName("LayoutRoot") as Grid; - if (grid != null) - { - browser = new WebBrowser(); - browser.IsScriptEnabled = true; - browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted); - - browser.Navigating += new EventHandler<NavigatingEventArgs>(browser_Navigating); - browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed); - browser.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(browser_Navigated); - browser.Navigate2(loc); - - if (StartHidden) - { - browser.Visibility = Visibility.Collapsed; - } - - //browser.IsGeolocationEnabled = opts.isGeolocationEnabled; - grid.Children.Add(browser); - } - - ApplicationBar bar = new ApplicationBar(); - bar.BackgroundColor = Colors.Gray; - bar.IsMenuEnabled = false; - - backButton = new ApplicationBarIconButton(); - backButton.Text = "Back"; - - backButton.IconUri = new Uri(baseImageUrl + "appbar.back.rest.png", UriKind.Relative); - backButton.Click += new EventHandler(backButton_Click); - bar.Buttons.Add(backButton); - - - fwdButton = new ApplicationBarIconButton(); - fwdButton.Text = "Forward"; - fwdButton.IconUri = new Uri(baseImageUrl + "appbar.next.rest.png", UriKind.Relative); - fwdButton.Click += new EventHandler(fwdButton_Click); - bar.Buttons.Add(fwdButton); - - ApplicationBarIconButton closeBtn = new ApplicationBarIconButton(); - closeBtn.Text = "Close"; - closeBtn.IconUri = new Uri(baseImageUrl + "appbar.close.rest.png", UriKind.Relative); - closeBtn.Click += new EventHandler(closeBtn_Click); - bar.Buttons.Add(closeBtn); - - page.ApplicationBar = bar; - bar.IsVisible = !StartHidden; - AppBar = bar; - - page.BackKeyPress += page_BackKeyPress; - - } - - } - } - }); - } - - void page_BackKeyPress(object sender, System.ComponentModel.CancelEventArgs e) - { -#if WP8 - if (browser.CanGoBack) - { - browser.GoBack(); - } - else - { - close(); - } - e.Cancel = true; -#else - browser.InvokeScript("execScript", "history.back();"); -#endif - } - - void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e) - { - - } - - void fwdButton_Click(object sender, EventArgs e) - { - if (browser != null) - { - try - { -#if WP8 - browser.GoForward(); -#else - browser.InvokeScript("execScript", "history.forward();"); -#endif - } - catch (Exception) - { - - } - } - } - - void backButton_Click(object sender, EventArgs e) - { - if (browser != null) - { - try - { -#if WP8 - browser.GoBack(); -#else - browser.InvokeScript("execScript", "history.back();"); -#endif - } - catch (Exception) - { - - } - } - } - - void closeBtn_Click(object sender, EventArgs e) - { - this.close(); - } - - - public void close(string options = "") - { - if (browser != null) - { - Deployment.Current.Dispatcher.BeginInvoke(() => - { - PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame; - if (frame != null) - { - PhoneApplicationPage page = frame.Content as PhoneApplicationPage; - if (page != null) - { - Grid grid = page.FindName("LayoutRoot") as Grid; - if (grid != null) - { - grid.Children.Remove(browser); - } - page.ApplicationBar = null; - page.BackKeyPress -= page_BackKeyPress; - } - } - - browser = null; - string message = "{\"type\":\"exit\"}"; - PluginResult result = new PluginResult(PluginResult.Status.OK, message); - result.KeepCallback = false; - this.DispatchCommandResult(result, NavigationCallbackId); - }); - } - } - - void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e) - { -#if WP8 - if (browser != null) - { - backButton.IsEnabled = browser.CanGoBack; - fwdButton.IsEnabled = browser.CanGoForward; - - } -#endif - string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.OriginalString + "\"}"; - PluginResult result = new PluginResult(PluginResult.Status.OK, message); - result.KeepCallback = true; - this.DispatchCommandResult(result, NavigationCallbackId); - } - - void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e) - { - string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.OriginalString + "\"}"; - PluginResult result = new PluginResult(PluginResult.Status.ERROR, message); - result.KeepCallback = true; - this.DispatchCommandResult(result, NavigationCallbackId); - } - - void browser_Navigating(object sender, NavigatingEventArgs e) - { - string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.OriginalString + "\"}"; - PluginResult result = new PluginResult(PluginResult.Status.OK, message); - result.KeepCallback = true; - this.DispatchCommandResult(result, NavigationCallbackId); - } - - } - - internal static class WebBrowserExtensions - { - /// <summary> - /// Improved method to initiate request to the provided URI. Supports 'data:text/html' urls. - /// </summary> - /// <param name="browser">The browser instance</param> - /// <param name="uri">The requested uri</param> - internal static void Navigate2(this WebBrowser browser, Uri uri) - { - // IE10 does not support data uri so we use NavigateToString method instead - if (uri.Scheme == "data") - { - // we should remove the scheme identifier and unescape the uri - string uriString = Uri.UnescapeDataString(uri.AbsoluteUri); - // format is 'data:text/html, ...' - string html = new System.Text.RegularExpressions.Regex("^data:text/html,").Replace(uriString, ""); - browser.NavigateToString(html); - } - else - { - browser.Navigate(uri); - } - } - } -} |
