http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginManager.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginManager.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginManager.java new file mode 100755 index 0000000..c9576a6 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginManager.java @@ -0,0 +1,526 @@ +/* + 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; + +import java.util.Collection; +import java.util.LinkedHashMap; + +import org.json.JSONException; + +import android.content.Intent; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Bundle; +import android.os.Debug; + +/** + * PluginManager is exposed to JavaScript in the Cordova WebView. + * + * Calling native plugin code can be done by calling PluginManager.exec(...) + * from JavaScript. + */ +public class PluginManager { + private static String TAG = "PluginManager"; + private static final int SLOW_EXEC_WARNING_THRESHOLD = Debug.isDebuggerConnected() ? 60 : 16; + + // List of service entries + private final LinkedHashMap<String, CordovaPlugin> pluginMap = new LinkedHashMap<String, CordovaPlugin>(); + private final LinkedHashMap<String, PluginEntry> entryMap = new LinkedHashMap<String, PluginEntry>(); + + private final CordovaInterface ctx; + private final CordovaWebView app; + private boolean isInitialized; + + private CordovaPlugin permissionRequester; + + public PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, Collection<PluginEntry> pluginEntries) { + this.ctx = cordova; + this.app = cordovaWebView; + setPluginEntries(pluginEntries); + } + + public Collection<PluginEntry> getPluginEntries() { + return entryMap.values(); + } + + public void setPluginEntries(Collection<PluginEntry> pluginEntries) { + if (isInitialized) { + this.onPause(false); + this.onDestroy(); + pluginMap.clear(); + entryMap.clear(); + } + for (PluginEntry entry : pluginEntries) { + addService(entry); + } + if (isInitialized) { + startupPlugins(); + } + } + + /** + * Init when loading a new HTML page into webview. + */ + public void init() { + LOG.d(TAG, "init()"); + isInitialized = true; + this.onPause(false); + this.onDestroy(); + pluginMap.clear(); + this.startupPlugins(); + } + + /** + * Create plugins objects that have onload set. + */ + private void startupPlugins() { + for (PluginEntry entry : entryMap.values()) { + // Add a null entry to for each non-startup plugin to avoid ConcurrentModificationException + // When iterating plugins. + if (entry.onload) { + getPlugin(entry.service); + } else { + pluginMap.put(entry.service, null); + } + } + } + + /** + * Receives a request for execution and fulfills it by finding the appropriate + * Java class and calling it's execute method. + * + * PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded + * string is returned that will indicate if any errors have occurred when trying to find + * or execute the class denoted by the clazz argument. + * + * @param service String containing the service to run + * @param action String containing the action that the class is supposed to perform. This is + * passed to the plugin execute method and it is up to the plugin developer + * how to deal with it. + * @param callbackId String containing the id of the callback that is execute in JavaScript if + * this is an async plugin call. + * @param rawArgs An Array literal string containing any arguments needed in the + * plugin execute method. + */ + public void exec(final String service, final String action, final String callbackId, final String rawArgs) { + CordovaPlugin plugin = getPlugin(service); + if (plugin == null) { + LOG.d(TAG, "exec() call to unknown plugin: " + service); + PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION); + app.sendPluginResult(cr, callbackId); + return; + } + CallbackContext callbackContext = new CallbackContext(callbackId, app); + try { + long pluginStartTime = System.currentTimeMillis(); + boolean wasValidAction = plugin.execute(action, rawArgs, callbackContext); + long duration = System.currentTimeMillis() - pluginStartTime; + + if (duration > SLOW_EXEC_WARNING_THRESHOLD) { + LOG.w(TAG, "THREAD WARNING: exec() call to " + service + "." + action + " blocked the main thread for " + duration + "ms. Plugin should use CordovaInterface.getThreadPool()."); + } + if (!wasValidAction) { + PluginResult cr = new PluginResult(PluginResult.Status.INVALID_ACTION); + callbackContext.sendPluginResult(cr); + } + } catch (JSONException e) { + PluginResult cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION); + callbackContext.sendPluginResult(cr); + } catch (Exception e) { + LOG.e(TAG, "Uncaught exception from plugin", e); + callbackContext.error(e.getMessage()); + } + } + + /** + * Get the plugin object that implements the service. + * If the plugin object does not already exist, then create it. + * If the service doesn't exist, then return null. + * + * @param service The name of the service. + * @return CordovaPlugin or null + */ + public CordovaPlugin getPlugin(String service) { + CordovaPlugin ret = pluginMap.get(service); + if (ret == null) { + PluginEntry pe = entryMap.get(service); + if (pe == null) { + return null; + } + if (pe.plugin != null) { + ret = pe.plugin; + } else { + ret = instantiatePlugin(pe.pluginClass); + } + ret.privateInitialize(service, ctx, app, app.getPreferences()); + pluginMap.put(service, ret); + } + return ret; + } + + /** + * Add a plugin class that implements a service to the service entry table. + * This does not create the plugin object instance. + * + * @param service The service name + * @param className The plugin class name + */ + public void addService(String service, String className) { + PluginEntry entry = new PluginEntry(service, className, false); + this.addService(entry); + } + + /** + * Add a plugin class that implements a service to the service entry table. + * This does not create the plugin object instance. + * + * @param entry The plugin entry + */ + public void addService(PluginEntry entry) { + this.entryMap.put(entry.service, entry); + if (entry.plugin != null) { + entry.plugin.privateInitialize(entry.service, ctx, app, app.getPreferences()); + pluginMap.put(entry.service, entry.plugin); + } + } + + /** + * Called when the system is about to start resuming a previous activity. + * + * @param multitasking Flag indicating if multitasking is turned on for app + */ + public void onPause(boolean multitasking) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onPause(multitasking); + } + } + } + + /** + * Called when the system received an HTTP authentication request. Plugins can use + * the supplied HttpAuthHandler to process this auth challenge. + * + * @param view The WebView that is initiating the callback + * @param handler The HttpAuthHandler used to set the WebView's response + * @param host The host requiring authentication + * @param realm The realm for which authentication is required + * + * @return Returns True if there is a plugin which will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedHttpAuthRequest(CordovaWebView view, ICordovaHttpAuthHandler handler, String host, String realm) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedHttpAuthRequest(app, handler, host, realm)) { + return true; + } + } + return false; + } + + /** + * Called when he system received an SSL client certificate request. Plugin can use + * the supplied ClientCertRequest to process this certificate challenge. + * + * @param view The WebView that is initiating the callback + * @param request The client certificate request + * + * @return Returns True if plugin will resolve this auth challenge, otherwise False + * + */ + public boolean onReceivedClientCertRequest(CordovaWebView view, ICordovaClientCertRequest request) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null && plugin.onReceivedClientCertRequest(app, request)) { + return true; + } + } + return false; + } + + /** + * Called when the activity will start interacting with the user. + * + * @param multitasking Flag indicating if multitasking is turned on for app + */ + public void onResume(boolean multitasking) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onResume(multitasking); + } + } + } + + /** + * Called when the activity is becoming visible to the user. + */ + public void onStart() { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onStart(); + } + } + } + + /** + * Called when the activity is no longer visible to the user. + */ + public void onStop() { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onStop(); + } + } + } + + /** + * The final call you receive before your activity is destroyed. + */ + public void onDestroy() { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onDestroy(); + } + } + } + + /** + * Send a message to all plugins. + * + * @param id The message id + * @param data The message data + * @return Object to stop propagation or null + */ + public Object postMessage(String id, Object data) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + Object obj = plugin.onMessage(id, data); + if (obj != null) { + return obj; + } + } + } + return ctx.onMessage(id, data); + } + + /** + * Called when the activity receives a new intent. + */ + public void onNewIntent(Intent intent) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onNewIntent(intent); + } + } + } + + /** + * Called when the webview is going to request an external resource. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the resource to load, + * false to block the resource. + */ + public boolean shouldAllowRequest(String url) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowRequest(url); + if (result != null) { + return result; + } + } + } + + // Default policy: + if (url.startsWith("blob:") || url.startsWith("data:") || url.startsWith("about:blank")) { + return true; + } + // TalkBack requires this, so allow it by default. + if (url.startsWith("https://ssl.gstatic.com/accessibility/javascript/android/")) { + return true; + } + if (url.startsWith("file://")) { + //This directory on WebKit/Blink based webviews contains SQLite databases! + //DON'T CHANGE THIS UNLESS YOU KNOW WHAT YOU'RE DOING! + return !url.contains("/app_webview/"); + } + return false; + } + + /** + * Called when the webview is going to change the URL of the loaded content. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the navigation, + * false to block the navigation. + */ + public boolean shouldAllowNavigation(String url) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowNavigation(url); + if (result != null) { + return result; + } + } + } + + // Default policy: + return url.startsWith("file://") || url.startsWith("about:blank"); + } + + + /** + * Called when the webview is requesting the exec() bridge be enabled. + */ + public boolean shouldAllowBridgeAccess(String url) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldAllowBridgeAccess(url); + if (result != null) { + return result; + } + } + } + + // Default policy: + return url.startsWith("file://"); + } + + /** + * Called when the webview is going not going to navigate, but may launch + * an Intent for an URL. + * + * This delegates to the installed plugins, and returns true/false for the + * first plugin to provide a non-null result. If no plugins respond, then + * the default policy is applied. + * + * @param url The URL that is being requested. + * @return Returns true to allow the URL to launch an intent, + * false to block the intent. + */ + public Boolean shouldOpenExternalUrl(String url) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null) { + Boolean result = plugin.shouldOpenExternalUrl(url); + if (result != null) { + return result; + } + } + } + // Default policy: + // External URLs are not allowed + return false; + } + + /** + * Called when the URL of the webview changes. + * + * @param url The URL that is being changed to. + * @return Return false to allow the URL to load, return true to prevent the URL from loading. + */ + public boolean onOverrideUrlLoading(String url) { + for (PluginEntry entry : this.entryMap.values()) { + CordovaPlugin plugin = pluginMap.get(entry.service); + if (plugin != null && plugin.onOverrideUrlLoading(url)) { + return true; + } + } + return false; + } + + /** + * Called when the app navigates or refreshes. + */ + public void onReset() { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onReset(); + } + } + } + + Uri remapUri(Uri uri) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + Uri ret = plugin.remapUri(uri); + if (ret != null) { + return ret; + } + } + } + return null; + } + + /** + * Create a plugin based on class name. + */ + private CordovaPlugin instantiatePlugin(String className) { + CordovaPlugin ret = null; + try { + Class<?> c = null; + if ((className != null) && !("".equals(className))) { + c = Class.forName(className); + } + if (c != null & CordovaPlugin.class.isAssignableFrom(c)) { + ret = (CordovaPlugin) c.newInstance(); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Error adding plugin " + className + "."); + } + return ret; + } + + /** + * Called by the system when the device configuration changes while your activity is running. + * + * @param newConfig The new device configuration + */ + public void onConfigurationChanged(Configuration newConfig) { + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + plugin.onConfigurationChanged(newConfig); + } + } + } + + public Bundle onSaveInstanceState() { + Bundle state = new Bundle(); + for (CordovaPlugin plugin : this.pluginMap.values()) { + if (plugin != null) { + Bundle pluginState = plugin.onSaveInstanceState(); + if(pluginState != null) { + state.putBundle(plugin.getServiceName(), pluginState); + } + } + } + return state; + } +}
http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginResult.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginResult.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginResult.java new file mode 100644 index 0000000..2b3ac72 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/PluginResult.java @@ -0,0 +1,198 @@ +/* + 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; + +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; + +import android.util.Base64; + +public class PluginResult { + private final int status; + private final int messageType; + private boolean keepCallback = false; + private String strMessage; + private String encodedMessage; + private List<PluginResult> multipartMessages; + + public PluginResult(Status status) { + this(status, PluginResult.StatusMessages[status.ordinal()]); + } + + public PluginResult(Status status, String message) { + this.status = status.ordinal(); + this.messageType = message == null ? MESSAGE_TYPE_NULL : MESSAGE_TYPE_STRING; + this.strMessage = message; + } + + public PluginResult(Status status, JSONArray message) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_JSON; + encodedMessage = message.toString(); + } + + public PluginResult(Status status, JSONObject message) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_JSON; + encodedMessage = message.toString(); + } + + public PluginResult(Status status, int i) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_NUMBER; + this.encodedMessage = ""+i; + } + + public PluginResult(Status status, float f) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_NUMBER; + this.encodedMessage = ""+f; + } + + public PluginResult(Status status, boolean b) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_BOOLEAN; + this.encodedMessage = Boolean.toString(b); + } + + public PluginResult(Status status, byte[] data) { + this(status, data, false); + } + + public PluginResult(Status status, byte[] data, boolean binaryString) { + this.status = status.ordinal(); + this.messageType = binaryString ? MESSAGE_TYPE_BINARYSTRING : MESSAGE_TYPE_ARRAYBUFFER; + this.encodedMessage = Base64.encodeToString(data, Base64.NO_WRAP); + } + + // The keepCallback and status of multipartMessages are ignored. + public PluginResult(Status status, List<PluginResult> multipartMessages) { + this.status = status.ordinal(); + this.messageType = MESSAGE_TYPE_MULTIPART; + this.multipartMessages = multipartMessages; + } + + public void setKeepCallback(boolean b) { + this.keepCallback = b; + } + + public int getStatus() { + return status; + } + + public int getMessageType() { + return messageType; + } + + public String getMessage() { + if (encodedMessage == null) { + encodedMessage = JSONObject.quote(strMessage); + } + return encodedMessage; + } + + public int getMultipartMessagesSize() { + return multipartMessages.size(); + } + + public PluginResult getMultipartMessage(int index) { + return multipartMessages.get(index); + } + + /** + * If messageType == MESSAGE_TYPE_STRING, then returns the message string. + * Otherwise, returns null. + */ + public String getStrMessage() { + return strMessage; + } + + public boolean getKeepCallback() { + return this.keepCallback; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String getJSONString() { + return "{\"status\":" + this.status + ",\"message\":" + this.getMessage() + ",\"keepCallback\":" + this.keepCallback + "}"; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toCallbackString(String callbackId) { + // If no result to be sent and keeping callback, then no need to sent back to JavaScript + if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) { + return null; + } + + // Check the success (OK, NO_RESULT & !KEEP_CALLBACK) + if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) { + return toSuccessCallbackString(callbackId); + } + + return toErrorCallbackString(callbackId); + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toSuccessCallbackString(String callbackId) { + return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; + } + + @Deprecated // Use sendPluginResult instead of sendJavascript. + public String toErrorCallbackString(String callbackId) { + return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");"; + } + + public static final int MESSAGE_TYPE_STRING = 1; + public static final int MESSAGE_TYPE_JSON = 2; + public static final int MESSAGE_TYPE_NUMBER = 3; + public static final int MESSAGE_TYPE_BOOLEAN = 4; + public static final int MESSAGE_TYPE_NULL = 5; + public static final int MESSAGE_TYPE_ARRAYBUFFER = 6; + // Use BINARYSTRING when your string may contain null characters. + // This is required to work around a bug in the platform :(. + public static final int MESSAGE_TYPE_BINARYSTRING = 7; + public static final int MESSAGE_TYPE_MULTIPART = 8; + + public static String[] StatusMessages = new String[] { + "No result", + "OK", + "Class not found", + "Illegal access", + "Instantiation error", + "Malformed url", + "IO error", + "Invalid action", + "JSON error", + "Error" + }; + + public enum Status { + NO_RESULT, + OK, + CLASS_NOT_FOUND_EXCEPTION, + ILLEGAL_ACCESS_EXCEPTION, + INSTANTIATION_EXCEPTION, + MALFORMED_URL_EXCEPTION, + IO_EXCEPTION, + INVALID_ACTION, + JSON_EXCEPTION, + ERROR + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/ResumeCallback.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/ResumeCallback.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/ResumeCallback.java new file mode 100644 index 0000000..49a43b5 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/ResumeCallback.java @@ -0,0 +1,76 @@ +/* + 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; + + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class ResumeCallback extends CallbackContext { + private final String TAG = "CordovaResumeCallback"; + private String serviceName; + private PluginManager pluginManager; + + public ResumeCallback(String serviceName, PluginManager pluginManager) { + super("resumecallback", null); + this.serviceName = serviceName; + this.pluginManager = pluginManager; + } + + @Override + public void sendPluginResult(PluginResult pluginResult) { + synchronized (this) { + if (finished) { + LOG.w(TAG, serviceName + " attempted to send a second callback to ResumeCallback\nResult was: " + pluginResult.getMessage()); + return; + } else { + finished = true; + } + } + + JSONObject event = new JSONObject(); + JSONObject pluginResultObject = new JSONObject(); + + try { + pluginResultObject.put("pluginServiceName", this.serviceName); + pluginResultObject.put("pluginStatus", PluginResult.StatusMessages[pluginResult.getStatus()]); + + event.put("action", "resume"); + event.put("pendingResult", pluginResultObject); + } catch (JSONException e) { + LOG.e(TAG, "Unable to create resume object for Activity Result"); + } + + PluginResult eventResult = new PluginResult(PluginResult.Status.OK, event); + + // We send a list of results to the js so that we don't have to decode + // the PluginResult passed to this CallbackContext into JSON twice. + // The results are combined into an event payload before the event is + // fired on the js side of things (see platform.js) + List<PluginResult> result = new ArrayList<PluginResult>(); + result.add(eventResult); + result.add(pluginResult); + + CoreAndroid appPlugin = (CoreAndroid) pluginManager.getPlugin(CoreAndroid.PLUGIN_NAME); + appPlugin.sendResumeEvent(new PluginResult(PluginResult.Status.OK, result)); + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/Whitelist.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/Whitelist.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/Whitelist.java new file mode 100644 index 0000000..d0f823c --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/Whitelist.java @@ -0,0 +1,170 @@ +/* + 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; + +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.cordova.LOG; + +import android.net.Uri; + +public class Whitelist { + private static class URLPattern { + public Pattern scheme; + public Pattern host; + public Integer port; + public Pattern path; + + private String regexFromPattern(String pattern, boolean allowWildcards) { + final String toReplace = "\\.[]{}()^$?+|"; + StringBuilder regex = new StringBuilder(); + for (int i=0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + if (c == '*' && allowWildcards) { + regex.append("."); + } else if (toReplace.indexOf(c) > -1) { + regex.append('\\'); + } + regex.append(c); + } + return regex.toString(); + } + + public URLPattern(String scheme, String host, String port, String path) throws MalformedURLException { + try { + if (scheme == null || "*".equals(scheme)) { + this.scheme = null; + } else { + this.scheme = Pattern.compile(regexFromPattern(scheme, false), Pattern.CASE_INSENSITIVE); + } + if ("*".equals(host)) { + this.host = null; + } else if (host.startsWith("*.")) { + this.host = Pattern.compile("([a-z0-9.-]*\\.)?" + regexFromPattern(host.substring(2), false), Pattern.CASE_INSENSITIVE); + } else { + this.host = Pattern.compile(regexFromPattern(host, false), Pattern.CASE_INSENSITIVE); + } + if (port == null || "*".equals(port)) { + this.port = null; + } else { + this.port = Integer.parseInt(port,10); + } + if (path == null || "/*".equals(path)) { + this.path = null; + } else { + this.path = Pattern.compile(regexFromPattern(path, true)); + } + } catch (NumberFormatException e) { + throw new MalformedURLException("Port must be a number"); + } + } + + public boolean matches(Uri uri) { + try { + return ((scheme == null || scheme.matcher(uri.getScheme()).matches()) && + (host == null || host.matcher(uri.getHost()).matches()) && + (port == null || port.equals(uri.getPort())) && + (path == null || path.matcher(uri.getPath()).matches())); + } catch (Exception e) { + LOG.d(TAG, e.toString()); + return false; + } + } + } + + private ArrayList<URLPattern> whiteList; + + public static final String TAG = "Whitelist"; + + public Whitelist() { + this.whiteList = new ArrayList<URLPattern>(); + } + + /* Match patterns (from http://developer.chrome.com/extensions/match_patterns.html) + * + * <url-pattern> := <scheme>://<host><path> + * <scheme> := '*' | 'http' | 'https' | 'file' | 'ftp' | 'chrome-extension' + * <host> := '*' | '*.' <any char except '/' and '*'>+ + * <path> := '/' <any chars> + * + * We extend this to explicitly allow a port attached to the host, and we allow + * the scheme to be omitted for backwards compatibility. (Also host is not required + * to begin with a "*" or "*.".) + */ + public void addWhiteListEntry(String origin, boolean subdomains) { + if (whiteList != null) { + try { + // Unlimited access to network resources + if (origin.compareTo("*") == 0) { + LOG.d(TAG, "Unlimited access to network resources"); + whiteList = null; + } + else { // specific access + Pattern parts = Pattern.compile("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?"); + Matcher m = parts.matcher(origin); + if (m.matches()) { + String scheme = m.group(2); + String host = m.group(4); + // Special case for two urls which are allowed to have empty hosts + if (("file".equals(scheme) || "content".equals(scheme)) && host == null) host = "*"; + String port = m.group(8); + String path = m.group(9); + if (scheme == null) { + // XXX making it stupid friendly for people who forget to include protocol/SSL + whiteList.add(new URLPattern("http", host, port, path)); + whiteList.add(new URLPattern("https", host, port, path)); + } else { + whiteList.add(new URLPattern(scheme, host, port, path)); + } + } + } + } catch (Exception e) { + LOG.d(TAG, "Failed to add origin %s", origin); + } + } + } + + + /** + * Determine if URL is in approved list of URLs to load. + * + * @param uri + * @return true if wide open or whitelisted + */ + public boolean isUrlWhiteListed(String uri) { + // If there is no whitelist, then it's wide open + if (whiteList == null) return true; + + Uri parsedUri = Uri.parse(uri); + // Look for match in white list + Iterator<URLPattern> pit = whiteList.iterator(); + while (pit.hasNext()) { + URLPattern p = pit.next(); + if (p.matches(parsedUri)) { + return true; + } + } + return false; + } + +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemCookieManager.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemCookieManager.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemCookieManager.java new file mode 100644 index 0000000..acf795f --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemCookieManager.java @@ -0,0 +1,69 @@ +/* + 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.engine; + +import android.annotation.TargetApi; +import android.os.Build; +import android.webkit.CookieManager; +import android.webkit.WebView; + +import org.apache.cordova.ICordovaCookieManager; + +class SystemCookieManager implements ICordovaCookieManager { + + protected final WebView webView; + private final CookieManager cookieManager; + + //Added because lint can't see the conditional RIGHT ABOVE this + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public SystemCookieManager(WebView webview) { + webView = webview; + cookieManager = CookieManager.getInstance(); + + //REALLY? Nobody has seen this UNTIL NOW? + cookieManager.setAcceptFileSchemeCookies(true); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cookieManager.setAcceptThirdPartyCookies(webView, true); + } + } + + public void setCookiesEnabled(boolean accept) { + cookieManager.setAcceptCookie(accept); + } + + public void setCookie(final String url, final String value) { + cookieManager.setCookie(url, value); + } + + public String getCookie(final String url) { + return cookieManager.getCookie(url); + } + + public void clearCookies() { + cookieManager.removeAllCookie(); + } + + public void flush() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cookieManager.flush(); + } + } +}; http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemExposedJsApi.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemExposedJsApi.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemExposedJsApi.java new file mode 100755 index 0000000..94c3d93 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemExposedJsApi.java @@ -0,0 +1,53 @@ +/* + 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.engine; + +import android.webkit.JavascriptInterface; + +import org.apache.cordova.CordovaBridge; +import org.apache.cordova.ExposedJsApi; +import org.json.JSONException; + +/** + * Contains APIs that the JS can call. All functions in here should also have + * an equivalent entry in CordovaChromeClient.java, and be added to + * cordova-js/lib/android/plugin/android/promptbasednativeapi.js + */ +class SystemExposedJsApi implements ExposedJsApi { + private final CordovaBridge bridge; + + SystemExposedJsApi(CordovaBridge bridge) { + this.bridge = bridge; + } + + @JavascriptInterface + public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { + return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments); + } + + @JavascriptInterface + public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { + bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value); + } + + @JavascriptInterface + public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { + return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent); + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebChromeClient.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebChromeClient.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebChromeClient.java new file mode 100755 index 0000000..3ea5e57 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebChromeClient.java @@ -0,0 +1,292 @@ +/* + 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.engine; + +import java.util.Arrays; +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.webkit.ConsoleMessage; +import android.webkit.GeolocationPermissions.Callback; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebStorage; +import android.webkit.WebView; +import android.webkit.PermissionRequest; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; + +import org.apache.cordova.CordovaDialogsHelper; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.LOG; + +/** + * This class is the WebChromeClient that implements callbacks for our web view. + * The kind of callbacks that happen here are on the chrome outside the document, + * such as onCreateWindow(), onConsoleMessage(), onProgressChanged(), etc. Related + * to but different than CordovaWebViewClient. + */ +public class SystemWebChromeClient extends WebChromeClient { + + private static final int FILECHOOSER_RESULTCODE = 5173; + private static final String LOG_TAG = "SystemWebChromeClient"; + private long MAX_QUOTA = 100 * 1024 * 1024; + protected final SystemWebViewEngine parentEngine; + + // the video progress view + private View mVideoProgressView; + + private CordovaDialogsHelper dialogsHelper; + private Context appContext; + + private WebChromeClient.CustomViewCallback mCustomViewCallback; + private View mCustomView; + + public SystemWebChromeClient(SystemWebViewEngine parentEngine) { + this.parentEngine = parentEngine; + appContext = parentEngine.webView.getContext(); + dialogsHelper = new CordovaDialogsHelper(appContext); + } + + /** + * Tell the client to display a javascript alert dialog. + */ + @Override + public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { + dialogsHelper.showAlert(message, new CordovaDialogsHelper.Result() { + @Override public void gotResult(boolean success, String value) { + if (success) { + result.confirm(); + } else { + result.cancel(); + } + } + }); + return true; + } + + /** + * Tell the client to display a confirm dialog to the user. + */ + @Override + public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { + dialogsHelper.showConfirm(message, new CordovaDialogsHelper.Result() { + @Override + public void gotResult(boolean success, String value) { + if (success) { + result.confirm(); + } else { + result.cancel(); + } + } + }); + return true; + } + + /** + * 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. + * + * Since we are hacking prompts for our own purposes, we should not be using them for + * this purpose, perhaps we should hack console.log to do this instead! + */ + @Override + public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, final JsPromptResult result) { + // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread. + String handledRet = parentEngine.bridge.promptOnJsPrompt(origin, message, defaultValue); + if (handledRet != null) { + result.confirm(handledRet); + } else { + dialogsHelper.showPrompt(message, defaultValue, new CordovaDialogsHelper.Result() { + @Override + public void gotResult(boolean success, String value) { + if (success) { + result.confirm(value); + } else { + result.cancel(); + } + } + }); + } + return true; + } + + /** + * Handle database quota exceeded notification. + */ + @Override + public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, + long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) + { + LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); + quotaUpdater.updateQuota(MAX_QUOTA); + } + + // console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html + // Expect this to not compile in a future Android release! + @SuppressWarnings("deprecation") + @Override + public void onConsoleMessage(String message, int lineNumber, String sourceID) + { + //This is only for Android 2.1 + if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1) + { + LOG.d(LOG_TAG, "%s: Line %d : %s", sourceID, lineNumber, message); + super.onConsoleMessage(message, lineNumber, sourceID); + } + } + + @TargetApi(8) + @Override + public boolean onConsoleMessage(ConsoleMessage consoleMessage) + { + if (consoleMessage.message() != null) + LOG.d(LOG_TAG, "%s: Line %d : %s" , consoleMessage.sourceId() , consoleMessage.lineNumber(), consoleMessage.message()); + return super.onConsoleMessage(consoleMessage); + } + + @Override + /** + * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin. + * + * This also checks for the Geolocation Plugin and requests permission from the application to use Geolocation. + * + * @param origin + * @param callback + */ + public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { + super.onGeolocationPermissionsShowPrompt(origin, callback); + callback.invoke(origin, true, false); + //Get the plugin, it should be loaded + CordovaPlugin geolocation = parentEngine.pluginManager.getPlugin("Geolocation"); + if(geolocation != null && !geolocation.hasPermisssion()) + { + geolocation.requestPermissions(0); + } + + } + + // API level 7 is required for this, see if we could lower this using something else + @Override + public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) { + parentEngine.getCordovaWebView().showCustomView(view, callback); + } + + @Override + public void onHideCustomView() { + parentEngine.getCordovaWebView().hideCustomView(); + } + + @Override + /** + * Ask the host application for a custom progress view to show while + * a <video> is loading. + * @return View The progress view. + */ + public View getVideoLoadingProgressView() { + + if (mVideoProgressView == null) { + // Create a new Loading view programmatically. + + // create the linear layout + LinearLayout layout = new LinearLayout(parentEngine.getView().getContext()); + layout.setOrientation(LinearLayout.VERTICAL); + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); + layout.setLayoutParams(layoutParams); + // the proress bar + ProgressBar bar = new ProgressBar(parentEngine.getView().getContext()); + LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + barLayoutParams.gravity = Gravity.CENTER; + bar.setLayoutParams(barLayoutParams); + layout.addView(bar); + + mVideoProgressView = layout; + } + return mVideoProgressView; + } + + // <input type=file> support: + // openFileChooser() is for pre KitKat and in KitKat mr1 (it's known broken in KitKat). + // For Lollipop, we use onShowFileChooser(). + public void openFileChooser(ValueCallback<Uri> uploadMsg) { + this.openFileChooser(uploadMsg, "*/*"); + } + + public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) { + this.openFileChooser(uploadMsg, acceptType, null); + } + + public void openFileChooser(final ValueCallback<Uri> uploadMsg, String acceptType, String capture) + { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + parentEngine.cordova.startActivityForResult(new CordovaPlugin() { + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); + LOG.d(LOG_TAG, "Receive file chooser URL: " + result); + uploadMsg.onReceiveValue(result); + } + }, intent, FILECHOOSER_RESULTCODE); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean onShowFileChooser(WebView webView, final ValueCallback<Uri[]> filePathsCallback, final WebChromeClient.FileChooserParams fileChooserParams) { + Intent intent = fileChooserParams.createIntent(); + try { + parentEngine.cordova.startActivityForResult(new CordovaPlugin() { + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + Uri[] result = WebChromeClient.FileChooserParams.parseResult(resultCode, intent); + LOG.d(LOG_TAG, "Receive file chooser URL: " + result); + filePathsCallback.onReceiveValue(result); + } + }, intent, FILECHOOSER_RESULTCODE); + } catch (ActivityNotFoundException e) { + LOG.w("No activity found to handle file chooser intent.", e); + filePathsCallback.onReceiveValue(null); + } + return true; + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void onPermissionRequest(final PermissionRequest request) { + LOG.d(LOG_TAG, "onPermissionRequest: " + Arrays.toString(request.getResources())); + request.grant(request.getResources()); + } + + public void destroyLastDialog(){ + dialogsHelper.destroyLastDialog(); + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebView.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebView.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebView.java new file mode 100644 index 0000000..01c2f00 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebView.java @@ -0,0 +1,88 @@ +/* + 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.engine; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CordovaWebViewEngine; + +/** + * Custom WebView subclass that enables us to capture events needed for Cordova. + */ +public class SystemWebView extends WebView implements CordovaWebViewEngine.EngineView { + private SystemWebViewClient viewClient; + SystemWebChromeClient chromeClient; + private SystemWebViewEngine parentEngine; + private CordovaInterface cordova; + + public SystemWebView(Context context) { + this(context, null); + } + + public SystemWebView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + // Package visibility to enforce that only SystemWebViewEngine should call this method. + void init(SystemWebViewEngine parentEngine, CordovaInterface cordova) { + this.cordova = cordova; + this.parentEngine = parentEngine; + if (this.viewClient == null) { + setWebViewClient(new SystemWebViewClient(parentEngine)); + } + + if (this.chromeClient == null) { + setWebChromeClient(new SystemWebChromeClient(parentEngine)); + } + } + + @Override + public CordovaWebView getCordovaWebView() { + return parentEngine != null ? parentEngine.getCordovaWebView() : null; + } + + @Override + public void setWebViewClient(WebViewClient client) { + viewClient = (SystemWebViewClient)client; + super.setWebViewClient(client); + } + + @Override + public void setWebChromeClient(WebChromeClient client) { + chromeClient = (SystemWebChromeClient)client; + super.setWebChromeClient(client); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + Boolean ret = parentEngine.client.onDispatchKeyEvent(event); + if (ret != null) { + return ret.booleanValue(); + } + return super.dispatchKeyEvent(event); + } +} http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/da952e07/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewClient.java ---------------------------------------------------------------------- diff --git a/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewClient.java b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewClient.java new file mode 100755 index 0000000..d176502 --- /dev/null +++ b/cordova-lib/spec-cordova/fixtures/platforms/atari/framework/src/org/apache/cordova/engine/SystemWebViewClient.java @@ -0,0 +1,374 @@ +/* + 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.engine; + +import android.annotation.TargetApi; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.Bitmap; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Build; +import android.webkit.ClientCertRequest; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import org.apache.cordova.AuthenticationToken; +import org.apache.cordova.CordovaClientCertRequest; +import org.apache.cordova.CordovaHttpAuthHandler; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginManager; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Hashtable; + + +/** + * This class is the WebViewClient that implements callbacks for our web view. + * The kind of callbacks that happen here are regarding the rendering of the + * document instead of the chrome surrounding it, such as onPageStarted(), + * shouldOverrideUrlLoading(), etc. Related to but different than + * CordovaChromeClient. + */ +public class SystemWebViewClient extends WebViewClient { + + private static final String TAG = "SystemWebViewClient"; + protected final SystemWebViewEngine parentEngine; + private boolean doClearHistory = false; + boolean isCurrentlyLoading; + + /** The authorization tokens. */ + private Hashtable<String, AuthenticationToken> authenticationTokens = new Hashtable<String, AuthenticationToken>(); + + public SystemWebViewClient(SystemWebViewEngine parentEngine) { + this.parentEngine = parentEngine; + } + + /** + * Give the host application a chance to take over the control when a new url + * is about to be loaded in the current WebView. + * + * @param view The WebView that is initiating the callback. + * @param url The url to be loaded. + * @return true to override, false for default behavior + */ + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return parentEngine.client.onNavigationAttempt(url); + } + + /** + * On received http auth request. + * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination + */ + @Override + public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { + + // Get the authentication token (if specified) + AuthenticationToken token = this.getAuthenticationToken(host, realm); + if (token != null) { + handler.proceed(token.getUserName(), token.getPassword()); + return; + } + + // Check if there is some plugin which can resolve this auth challenge + PluginManager pluginManager = this.parentEngine.pluginManager; + if (pluginManager != null && pluginManager.onReceivedHttpAuthRequest(null, new CordovaHttpAuthHandler(handler), host, realm)) { + parentEngine.client.clearLoadTimeoutTimer(); + return; + } + + // By default handle 401 like we'd normally do! + super.onReceivedHttpAuthRequest(view, handler, host, realm); + } + + /** + * On received client cert request. + * The method forwards the request to any running plugins before using the default implementation. + * + * @param view + * @param request + */ + @Override + @TargetApi(21) + public void onReceivedClientCertRequest (WebView view, ClientCertRequest request) + { + + // Check if there is some plugin which can resolve this certificate request + PluginManager pluginManager = this.parentEngine.pluginManager; + if (pluginManager != null && pluginManager.onReceivedClientCertRequest(null, new CordovaClientCertRequest(request))) { + parentEngine.client.clearLoadTimeoutTimer(); + return; + } + + // By default pass to WebViewClient + super.onReceivedClientCertRequest(view, request); + } + + /** + * Notify the host application that a page has started loading. + * This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted + * one time for the main frame. This also means that onPageStarted will not be called when the contents of an + * embedded frame changes, i.e. clicking a link whose target is an iframe. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + isCurrentlyLoading = true; + // Flush stale messages & reset plugins. + parentEngine.bridge.reset(); + parentEngine.client.onPageStarted(url); + } + + /** + * Notify the host application that a page has finished loading. + * This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet. + * + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + // Ignore excessive calls, if url is not about:blank (CB-8317). + if (!isCurrentlyLoading && !url.startsWith("about:")) { + return; + } + isCurrentlyLoading = false; + + /** + * Because of a timing issue we need to clear this history in onPageFinished as well as + * onPageStarted. However we only want to do this if the doClearHistory boolean is set to + * true. You see when you load a url with a # in it which is common in jQuery applications + * onPageStared is not called. Clearing the history at that point would break jQuery apps. + */ + if (this.doClearHistory) { + view.clearHistory(); + this.doClearHistory = false; + } + parentEngine.client.onPageFinishedLoading(url); + + } + + /** + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * The errorCode parameter corresponds to one of the ERROR_* constants. + * + * @param view The WebView that is initiating the callback. + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + */ + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + // Ignore error due to stopLoading(). + if (!isCurrentlyLoading) { + return; + } + LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); + + // If this is a "Protocol Not Supported" error, then revert to the previous + // page. If there was no previous page, then punt. The application's config + // is likely incorrect (start page set to sms: or something like that) + if (errorCode == WebViewClient.ERROR_UNSUPPORTED_SCHEME) { + parentEngine.client.clearLoadTimeoutTimer(); + + if (view.canGoBack()) { + view.goBack(); + return; + } else { + super.onReceivedError(view, errorCode, description, failingUrl); + } + } + parentEngine.client.onReceivedError(errorCode, description, failingUrl); + } + + /** + * Notify the host application that an SSL error occurred while loading a resource. + * The host application must call either handler.cancel() or handler.proceed(). + * Note that the decision may be retained for use in response to future SSL errors. + * The default behavior is to cancel the load. + * + * @param view The WebView that is initiating the callback. + * @param handler An SslErrorHandler object that will handle the user's response. + * @param error The SSL error object. + */ + @TargetApi(8) + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + + final String packageName = parentEngine.cordova.getActivity().getPackageName(); + final PackageManager pm = parentEngine.cordova.getActivity().getPackageManager(); + + ApplicationInfo appInfo; + try { + appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); + if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { + // debug = true + handler.proceed(); + return; + } else { + // debug = false + super.onReceivedSslError(view, handler, error); + } + } catch (NameNotFoundException e) { + // When it doubt, lock it out! + super.onReceivedSslError(view, handler, error); + } + } + + + /** + * Sets the authentication token. + * + * @param authenticationToken + * @param host + * @param realm + */ + public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) { + if (host == null) { + host = ""; + } + if (realm == null) { + realm = ""; + } + this.authenticationTokens.put(host.concat(realm), authenticationToken); + } + + /** + * Removes the authentication token. + * + * @param host + * @param realm + * + * @return the authentication token or null if did not exist + */ + public AuthenticationToken removeAuthenticationToken(String host, String realm) { + return this.authenticationTokens.remove(host.concat(realm)); + } + + /** + * Gets the authentication token. + * + * In order it tries: + * 1- host + realm + * 2- host + * 3- realm + * 4- no host, no realm + * + * @param host + * @param realm + * + * @return the authentication token + */ + public AuthenticationToken getAuthenticationToken(String host, String realm) { + AuthenticationToken token = null; + token = this.authenticationTokens.get(host.concat(realm)); + + if (token == null) { + // try with just the host + token = this.authenticationTokens.get(host); + + // Try the realm + if (token == null) { + token = this.authenticationTokens.get(realm); + } + + // if no host found, just query for default + if (token == null) { + token = this.authenticationTokens.get(""); + } + } + + return token; + } + + /** + * Clear all authentication tokens. + */ + public void clearAuthenticationTokens() { + this.authenticationTokens.clear(); + } + + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + try { + // Check the against the whitelist and lock out access to the WebView directory + // Changing this will cause problems for your application + if (!parentEngine.pluginManager.shouldAllowRequest(url)) { + LOG.w(TAG, "URL blocked by whitelist: " + url); + // Results in a 404. + return new WebResourceResponse("text/plain", "UTF-8", null); + } + + CordovaResourceApi resourceApi = parentEngine.resourceApi; + Uri origUri = Uri.parse(url); + // Allow plugins to intercept WebView requests. + Uri remappedUri = resourceApi.remapUri(origUri); + + if (!origUri.equals(remappedUri) || needsSpecialsInAssetUrlFix(origUri) || needsKitKatContentUrlFix(origUri)) { + CordovaResourceApi.OpenForReadResult result = resourceApi.openForRead(remappedUri, true); + return new WebResourceResponse(result.mimeType, "UTF-8", result.inputStream); + } + // If we don't need to special-case the request, let the browser load it. + return null; + } catch (IOException e) { + if (!(e instanceof FileNotFoundException)) { + LOG.e(TAG, "Error occurred while loading a file (returning a 404).", e); + } + // Results in a 404. + return new WebResourceResponse("text/plain", "UTF-8", null); + } + } + + private static boolean needsKitKatContentUrlFix(Uri uri) { + return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && "content".equals(uri.getScheme()); + } + + private static boolean needsSpecialsInAssetUrlFix(Uri uri) { + if (CordovaResourceApi.getUriType(uri) != CordovaResourceApi.URI_TYPE_ASSET) { + return false; + } + if (uri.getQuery() != null || uri.getFragment() != null) { + return true; + } + + if (!uri.toString().contains("%")) { + return false; + } + + switch(android.os.Build.VERSION.SDK_INT){ + case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH: + case android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1: + return true; + } + return false; + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org For additional commands, e-mail: commits-h...@cordova.apache.org