Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id AF768106FE for ; Tue, 25 Nov 2014 18:43:11 +0000 (UTC) Received: (qmail 71028 invoked by uid 500); 25 Nov 2014 18:43:11 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 71002 invoked by uid 500); 25 Nov 2014 18:43:11 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 70993 invoked by uid 99); 25 Nov 2014 18:43:11 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Nov 2014 18:43:11 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 47478A1A004; Tue, 25 Nov 2014 18:43:11 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: agrieve@apache.org To: commits@cordova.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: ios commit: CB-8002 CB-7735 Update cordova.js to include bridge fix Date: Tue, 25 Nov 2014 18:43:11 +0000 (UTC) Repository: cordova-ios Updated Branches: refs/heads/master dd54d96bd -> 9e53aa997 CB-8002 CB-7735 Update cordova.js to include bridge fix Project: http://git-wip-us.apache.org/repos/asf/cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-ios/commit/9e53aa99 Tree: http://git-wip-us.apache.org/repos/asf/cordova-ios/tree/9e53aa99 Diff: http://git-wip-us.apache.org/repos/asf/cordova-ios/diff/9e53aa99 Branch: refs/heads/master Commit: 9e53aa9976772ea0a6861d6c451fef019d29fc65 Parents: dd54d96 Author: Andrew Grieve Authored: Tue Nov 25 13:42:40 2014 -0500 Committer: Andrew Grieve Committed: Tue Nov 25 13:42:40 2014 -0500 ---------------------------------------------------------------------- CordovaLib/cordova.js | 122 +++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 60 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-ios/blob/9e53aa99/CordovaLib/cordova.js ---------------------------------------------------------------------- diff --git a/CordovaLib/cordova.js b/CordovaLib/cordova.js index 75729c1..9588ca4 100644 --- a/CordovaLib/cordova.js +++ b/CordovaLib/cordova.js @@ -1,5 +1,5 @@ // Platform: ios -// 91157c2e1bf3eb098c7e2ab31404e895ccb0df2a +// 227d83a122733fdf0300ffe75507a221bcef2b06 /* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -464,9 +464,14 @@ function each(objects, func, context) { function clobber(obj, key, value) { exports.replaceHookForTesting(obj, key); - obj[key] = value; + var needsProperty = false; + try { + obj[key] = value; + } catch (e) { + needsProperty = true; + } // Getters can only be overridden by getters. - if (obj[key] !== value) { + if (needsProperty || obj[key] !== value) { utils.defineGetter(obj, key, function() { return value; }); @@ -821,14 +826,13 @@ var cordova = require('cordova'), // IFRAME_NAV is the fastest. // IFRAME_HASH could be made to enable synchronous bridge calls if we wanted this feature. jsToNativeModes = { - IFRAME_NAV: 0, - XHR_NO_PAYLOAD: 1, - XHR_WITH_PAYLOAD: 2, - XHR_OPTIONAL_PAYLOAD: 3, - IFRAME_HASH_NO_PAYLOAD: 4, - // Bundling the payload turns out to be slower. Probably since it has to be URI encoded / decoded. - IFRAME_HASH_WITH_PAYLOAD: 5, - WK_WEBVIEW_BINDING: 6 + IFRAME_NAV: 0, // Default. Uses a new iframe for each poke. + XHR_NO_PAYLOAD: 1, // About the same speed as IFRAME_NAV. Performance not about the same as IFRAME_NAV, but more variable. + XHR_WITH_PAYLOAD: 2, // Flakey, and not as performant + XHR_OPTIONAL_PAYLOAD: 3, // Flakey, and not as performant + IFRAME_HASH_NO_PAYLOAD: 4, // Not fully baked. A bit faster than IFRAME_NAV, but risks jank since poke happens synchronously. + IFRAME_HASH_WITH_PAYLOAD: 5, // Slower than no payload. Maybe since it has to be URI encoded / decoded. + WK_WEBVIEW_BINDING: 6 // Only way that works for WKWebView :) }, bridgeMode, execIframe, @@ -838,26 +842,8 @@ var cordova = require('cordova'), requestCount = 0, vcHeaderValue = null, commandQueue = [], // Contains pending JS->Native messages. - isInContextOfEvalJs = 0; - -function createExecIframe(src, unloadListener) { - var iframe = document.createElement("iframe"); - iframe.style.display = 'none'; - // Both the unload listener and the src must be set before adding the iframe - // to the document in order to avoid race conditions. Callbacks from native - // can happen within the appendChild() call! - iframe.onunload = unloadListener; - iframe.src = src; - document.body.appendChild(iframe); - return iframe; -} - -function createHashIframe() { - var ret = createExecIframe('about:blank'); - // Hash changes don't work on about:blank, so switch it to file:///. - ret.contentWindow.history.replaceState(null, null, 'file:///#'); - return ret; -} + isInContextOfEvalJs = 0, + failSafeTimerId = 0; function shouldBundleCommandJson() { if (bridgeMode === jsToNativeModes.XHR_WITH_PAYLOAD) { @@ -990,19 +976,23 @@ function iOSExec() { // Also, if there is already a command in the queue, then we've already // poked the native side, so there is no reason to do so again. if (!isInContextOfEvalJs && commandQueue.length == 1) { - switch (bridgeMode) { - case jsToNativeModes.XHR_NO_PAYLOAD: - case jsToNativeModes.XHR_WITH_PAYLOAD: - case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: - pokeNativeViaXhr(); - break; - default: // iframe-based. - pokeNativeViaIframe(); - } + pokeNative(); } } } +function pokeNative() { + switch (bridgeMode) { + case jsToNativeModes.XHR_NO_PAYLOAD: + case jsToNativeModes.XHR_WITH_PAYLOAD: + case jsToNativeModes.XHR_OPTIONAL_PAYLOAD: + pokeNativeViaXhr(); + break; + default: // iframe-based. + pokeNativeViaIframe(); + } +} + function pokeNativeViaXhr() { // This prevents sending an XHR when there is already one being sent. // This should happen only in rare circumstances (refer to unit tests). @@ -1026,11 +1016,6 @@ function pokeNativeViaXhr() { execXhr.send(null); } -function onIframeUnload() { - execIframe = null; - setTimeout(pokeNativeViaIframe, 0); -} - function pokeNativeViaIframe() { // CB-5488 - Don't attempt to create iframe before document.body is available. if (!document.body) { @@ -1039,10 +1024,12 @@ function pokeNativeViaIframe() { } if (bridgeMode === jsToNativeModes.IFRAME_HASH_NO_PAYLOAD || bridgeMode === jsToNativeModes.IFRAME_HASH_WITH_PAYLOAD) { // TODO: This bridge mode doesn't properly support being removed from the DOM (CB-7735) - execHashIframe = execHashIframe || createHashIframe(); - // Check if they've removed it from the DOM, and put it back if so. - if (!execHashIframe.contentWindow) { - execHashIframe = createHashIframe(); + if (!execHashIframe) { + execHashIframe = document.createElement('iframe'); + execHashIframe.style.display = 'none'; + document.body.appendChild(execHashIframe); + // Hash changes don't work on about:blank, so switch it to file:///. + execHashIframe.contentWindow.history.replaceState(null, null, 'file:///#'); } // The delegate method is called only when the hash changes, so toggle it back and forth. hashToggle = hashToggle ^ 3; @@ -1054,13 +1041,25 @@ function pokeNativeViaIframe() { } else { // Check if they've removed it from the DOM, and put it back if so. if (execIframe && execIframe.contentWindow) { - // Listen for unload, since it can happen (CB-7735) that the iframe gets - // removed from the DOM before it gets a chance to poke the native side. - execIframe.contentWindow.onunload = onIframeUnload; - execIframe.src = 'gap://ready'; + execIframe.contentWindow.location = 'gap://ready'; } else { - execIframe = createExecIframe('gap://ready', onIframeUnload); + execIframe = document.createElement('iframe'); + execIframe.style.display = 'none'; + execIframe.src = 'gap://ready'; + document.body.appendChild(execIframe); } + // Use a timer to protect against iframe being unloaded during the poke (CB-7735). + // This makes the bridge ~ 7% slower, but works around the poke getting lost + // when the iframe is removed from the DOM. + // An onunload listener could be used in the case where the iframe has just been + // created, but since unload events fire only once, it doesn't work in the normal + // case of iframe reuse (where unload will have already fired due to the attempted + // navigation of the page). + failSafeTimerId = setTimeout(function() { + if (commandQueue.length) { + pokeNative(); + } + }, 50); // Making this > 0 improves performance (marginally) in the normal case (where it doesn't fire). } } @@ -1071,7 +1070,9 @@ iOSExec.setJsToNativeBridgeMode = function(mode) { // can trigger browser bugs. // https://issues.apache.org/jira/browse/CB-593 if (execIframe) { - execIframe.parentNode.removeChild(execIframe); + if (execIframe.parentNode) { + execIframe.parentNode.removeChild(execIframe); + } execIframe = null; } bridgeMode = mode; @@ -1079,8 +1080,9 @@ iOSExec.setJsToNativeBridgeMode = function(mode) { iOSExec.nativeFetchMessages = function() { // Stop listing for window detatch once native side confirms poke. - if (execIframe && execIframe.contentWindow) { - execIframe.contentWindow.onunload = null; + if (failSafeTimerId) { + clearTimeout(failSafeTimerId); + failSafeTimerId = 0; } // Each entry in commandQueue is a JSON string already. if (!commandQueue.length) { @@ -1184,7 +1186,7 @@ function replaceNavigator(origNavigator) { for (var key in origNavigator) { if (typeof origNavigator[key] == 'function') { newNavigator[key] = origNavigator[key].bind(origNavigator); - } + } else { (function(k) { utils.defineGetterSetter(newNavigator,key,function() { @@ -1312,7 +1314,7 @@ function replaceNavigator(origNavigator) { for (var key in origNavigator) { if (typeof origNavigator[key] == 'function') { newNavigator[key] = origNavigator[key].bind(origNavigator); - } + } else { (function(k) { utils.defineGetterSetter(newNavigator,key,function() { @@ -1367,7 +1369,7 @@ platform.bootstrap && platform.bootstrap(); * Create all cordova objects once native side is ready. */ channel.join(function() { - + platform.initialize && platform.initialize(); // Fire event to notify that all objects are created --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org For additional commands, e-mail: commits-help@cordova.apache.org