From 12ebb7c874fec150a2d22710743081341be709dc Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Wed, 8 Apr 2015 08:26:15 -0700 Subject: [PATCH 01/17] Bug 1151482 - Override lightweight theme text color for #urlbar in Windows Dev Edition theme;r=Gijs --- browser/themes/shared/devedition.inc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/themes/shared/devedition.inc.css b/browser/themes/shared/devedition.inc.css index f3c4dd82f1d..151eaf94db1 100644 --- a/browser/themes/shared/devedition.inc.css +++ b/browser/themes/shared/devedition.inc.css @@ -218,7 +218,7 @@ toolbar[brighttext] #downloads-indicator-counter { :root[devtoolstheme="dark"] .browserContainer > findbar .findbar-textbox { background-color: var(--url-and-searchbar-background-color) !important; background-image: none !important; - color: var(--url-and-searchbar-color); + color: var(--url-and-searchbar-color) !important; border: none !important; box-shadow: none !important; } From 21045bc265b9f7194b84cbbadbc662fd00eb95e8 Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Wed, 8 Apr 2015 17:12:01 +0100 Subject: [PATCH 02/17] Bug 1151832 - Remove some old workarounds for loop-server - optional parameters are now respected for PATCH /rooms/{token} and the 'roomConnectionId' check no longer needs to fallback to 'id'. r=mikedeboer --- browser/components/loop/LoopRooms.jsm | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/browser/components/loop/LoopRooms.jsm b/browser/components/loop/LoopRooms.jsm index 0035756bee2..7925e3ae57f 100644 --- a/browser/components/loop/LoopRooms.jsm +++ b/browser/components/loop/LoopRooms.jsm @@ -62,11 +62,7 @@ const extend = function(target, source) { */ const containsParticipant = function(room, participant) { for (let user of room.participants) { - // XXX until a bug 1100318 is implemented and deployed, - // we need to check the "id" field here as well - roomConnectionId is the - // official value for the interface. - if (user.roomConnectionId == participant.roomConnectionId && - user.id == participant.id) { + if (user.roomConnectionId == participant.roomConnectionId) { return true; } } @@ -451,10 +447,7 @@ let LoopRoomsInternal = { let origRoom = this.rooms.get(roomToken); let patchData = { - roomName: newRoomName, - // XXX We have to supply the max size and room owner due to bug 1099063. - maxSize: origRoom.maxSize, - roomOwner: origRoom.roomOwner + roomName: newRoomName }; MozLoopService.hawkRequest(this.sessionType, url, "PATCH", patchData) .then(response => { From b54330d8266872d35d5fe3c4eec5e79deaf2b8b8 Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Wed, 8 Apr 2015 17:12:01 +0100 Subject: [PATCH 03/17] Bug 1152245 - Receiving a call whilst in private browsing or not browser windows open can stop any calls to contacts being made or received. r=mikedeboer --- browser/components/loop/LoopCalls.jsm | 5 ++- browser/components/loop/MozLoopService.jsm | 7 ++-- .../test/xpcshell/test_loopservice_busy.js | 1 + .../xpcshell/test_loopservice_directcall.js | 32 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/browser/components/loop/LoopCalls.jsm b/browser/components/loop/LoopCalls.jsm index 9574bff83f8..5b34cd44d59 100644 --- a/browser/components/loop/LoopCalls.jsm +++ b/browser/components/loop/LoopCalls.jsm @@ -282,7 +282,10 @@ let LoopCallsInternal = { */ _startCall: function(callData) { const openChat = () => { - this.conversationInProgress.id = MozLoopService.openChatWindow(callData); + let windowId = MozLoopService.openChatWindow(callData); + if (windowId) { + this.conversationInProgress.id = windowId; + } }; if (callData.type == "incoming" && ("callerId" in callData) && diff --git a/browser/components/loop/MozLoopService.jsm b/browser/components/loop/MozLoopService.jsm index 75fb315885a..40071cee4f6 100644 --- a/browser/components/loop/MozLoopService.jsm +++ b/browser/components/loop/MozLoopService.jsm @@ -827,7 +827,8 @@ let MozLoopServiceInternal = { * * @param {Object} conversationWindowData The data to be obtained by the * window when it opens. - * @returns {Number} The id of the window. + * @returns {Number} The id of the window, null if a window could not + * be opened. */ openChatWindow: function(conversationWindowData) { // So I guess the origin is the loop server!? @@ -913,7 +914,9 @@ let MozLoopServiceInternal = { }.bind(this), true); }; - Chat.open(null, origin, "", url, undefined, undefined, callback); + if (!Chat.open(null, origin, "", url, undefined, undefined, callback)) { + return null; + } return windowId; }, diff --git a/browser/components/loop/test/xpcshell/test_loopservice_busy.js b/browser/components/loop/test/xpcshell/test_loopservice_busy.js index c3d302f06f2..7766081eacf 100644 --- a/browser/components/loop/test/xpcshell/test_loopservice_busy.js +++ b/browser/components/loop/test/xpcshell/test_loopservice_busy.js @@ -33,6 +33,7 @@ add_task(function* test_busy_2fxa_calls() { Chat.open = function(contentWindow, origin, title, url) { opened++; windowId = url.match(/about:loopconversation\#(\d+)$/)[1]; + return windowId; }; mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA); diff --git a/browser/components/loop/test/xpcshell/test_loopservice_directcall.js b/browser/components/loop/test/xpcshell/test_loopservice_directcall.js index 958327211ff..e94a2ace591 100644 --- a/browser/components/loop/test/xpcshell/test_loopservice_directcall.js +++ b/browser/components/loop/test/xpcshell/test_loopservice_directcall.js @@ -19,6 +19,7 @@ add_task(function test_startDirectCall_opens_window() { let openedUrl; Chat.open = function(contentWindow, origin, title, url) { openedUrl = url; + return 1; }; LoopCalls.startDirectCall(contact, "audio-video"); @@ -34,6 +35,7 @@ add_task(function test_startDirectCall_getConversationWindowData() { let openedUrl; Chat.open = function(contentWindow, origin, title, url) { openedUrl = url; + return 2; }; LoopCalls.startDirectCall(contact, "audio-video"); @@ -49,6 +51,36 @@ add_task(function test_startDirectCall_getConversationWindowData() { LoopCalls.clearCallInProgress(windowId); }); +add_task(function test_startDirectCall_not_busy_if_window_fails_to_open() { + let openedUrl; + + // Simulate no window available to open. + Chat.open = function(contentWindow, origin, title, url) { + openedUrl = url; + return null; + }; + + LoopCalls.startDirectCall(contact, "audio-video"); + + do_check_true(!!openedUrl, "should have attempted to open chat window"); + + openedUrl = null; + + // Window opens successfully this time. + Chat.open = function(contentWindow, origin, title, url) { + openedUrl = url; + return 3; + }; + + LoopCalls.startDirectCall(contact, "audio-video"); + + do_check_true(!!openedUrl, "should open a chat window"); + + // Stop the busy kicking in for following tests. + let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1]; + LoopCalls.clearCallInProgress(windowId); +}); + function run_test() { do_register_cleanup(function() { // Revert original Chat.open implementation From d609e8024ca87f46611d55446ebf904053faea3d Mon Sep 17 00:00:00 2001 From: Mark Banner Date: Wed, 8 Apr 2015 17:13:03 +0100 Subject: [PATCH 04/17] Bug 1152296 - Move roomStore.js from shared code to desktop specific code for Loop. r=mikedeboer --- browser/components/loop/content/conversation.html | 2 +- browser/components/loop/content/{shared => }/js/roomStore.js | 0 browser/components/loop/content/panel.html | 2 +- browser/components/loop/jar.mn | 2 +- browser/components/loop/test/desktop-local/index.html | 3 ++- .../loop/test/{shared => desktop-local}/roomStore_test.js | 0 browser/components/loop/test/shared/index.html | 2 -- browser/components/loop/ui/index.html | 2 +- 8 files changed, 6 insertions(+), 7 deletions(-) rename browser/components/loop/content/{shared => }/js/roomStore.js (100%) rename browser/components/loop/test/{shared => desktop-local}/roomStore_test.js (100%) diff --git a/browser/components/loop/content/conversation.html b/browser/components/loop/content/conversation.html index 1af5d33365b..9a7f9d15169 100644 --- a/browser/components/loop/content/conversation.html +++ b/browser/components/loop/content/conversation.html @@ -33,7 +33,6 @@ - @@ -46,6 +45,7 @@ + diff --git a/browser/components/loop/content/shared/js/roomStore.js b/browser/components/loop/content/js/roomStore.js similarity index 100% rename from browser/components/loop/content/shared/js/roomStore.js rename to browser/components/loop/content/js/roomStore.js diff --git a/browser/components/loop/content/panel.html b/browser/components/loop/content/panel.html index 80f2fdd8975..8ecc4d90ce3 100644 --- a/browser/components/loop/content/panel.html +++ b/browser/components/loop/content/panel.html @@ -28,11 +28,11 @@ - + diff --git a/browser/components/loop/jar.mn b/browser/components/loop/jar.mn index 7d032cd5578..dd4c26765f4 100644 --- a/browser/components/loop/jar.mn +++ b/browser/components/loop/jar.mn @@ -18,6 +18,7 @@ browser.jar: content/browser/loop/js/panel.js (content/js/panel.js) content/browser/loop/js/contacts.js (content/js/contacts.js) content/browser/loop/js/conversationViews.js (content/js/conversationViews.js) + content/browser/loop/js/roomStore.js (content/js/roomStore.js) content/browser/loop/js/roomViews.js (content/js/roomViews.js) # Desktop styles @@ -71,7 +72,6 @@ browser.jar: content/browser/loop/shared/js/actions.js (content/shared/js/actions.js) content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js) content/browser/loop/shared/js/store.js (content/shared/js/store.js) - content/browser/loop/shared/js/roomStore.js (content/shared/js/roomStore.js) content/browser/loop/shared/js/roomStates.js (content/shared/js/roomStates.js) content/browser/loop/shared/js/fxOSActiveRoomStore.js (content/shared/js/fxOSActiveRoomStore.js) content/browser/loop/shared/js/activeRoomStore.js (content/shared/js/activeRoomStore.js) diff --git a/browser/components/loop/test/desktop-local/index.html b/browser/components/loop/test/desktop-local/index.html index 3a6cf1cb91a..05548b15129 100644 --- a/browser/components/loop/test/desktop-local/index.html +++ b/browser/components/loop/test/desktop-local/index.html @@ -50,7 +50,6 @@ - @@ -59,6 +58,7 @@ + @@ -74,6 +74,7 @@ + - @@ -77,7 +76,6 @@ - - @@ -57,6 +56,7 @@ + From 91af1260141807dad650cea0282f62dacf9d1e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 8 Apr 2015 18:20:18 +0200 Subject: [PATCH 05/17] Bug 1148016 - make browser_bug553455.js handle xpinstall.customConfirmationUI=false. r=dtownsend --- .../content/test/general/browser_bug553455.js | 83 +++++++++++++++---- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js index c468d45f910..2a27ba3db8c 100644 --- a/browser/base/content/test/general/browser_bug553455.js +++ b/browser/base/content/test/general/browser_bug553455.js @@ -5,6 +5,7 @@ const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/"; const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; +const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; const PROGRESS_NOTIFICATION = "addon-progress"; @@ -86,6 +87,52 @@ function wait_for_notification_close(aCallback) { }, false); } +function wait_for_install_dialog(aCallback) { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + wait_for_notification("addon-install-confirmation", function(aPanel) { + aCallback(); + return; + }); + } + + info("Waiting for install dialog"); + + Services.wm.addListener({ + onOpenWindow: function(aXULWindow) { + info("Install dialog opened, waiting for focus"); + Services.wm.removeListener(this); + + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + info("Saw install dialog"); + is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open"); + + // Override the countdown timer on the accept button + var button = domwindow.document.documentElement.getButton("accept"); + button.disabled = false; + + aCallback(); + }, domwindow); + }, + + onCloseWindow: function(aXULWindow) { + }, + + onWindowTitleChange: function(aXULWindow, aNewTitle) { + } + }); +} + +function accept_install_dialog() { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + document.getElementById("addon-install-confirmation-accept").click(); + } else { + let win = Services.wm.getMostRecentWindow("Addons:Install"); + win.document.documentElement.acceptDialog(); + } +} + function wait_for_single_notification(aCallback) { function inner_waiter() { info("Waiting for single notification"); @@ -165,7 +212,7 @@ function test_blocked_install() { "Should have seen the right message"); // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -183,7 +230,7 @@ function test_blocked_install() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); // Click on Allow @@ -208,7 +255,7 @@ function test_whitelisted_install() { gBrowser.selectedTab = originalTab; // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { is(gBrowser.selectedTab, tab, "tab selected in response to the addon-install-confirmation notification"); @@ -230,7 +277,7 @@ function test_whitelisted_install() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -331,7 +378,7 @@ function test_restartless() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -352,7 +399,7 @@ function test_restartless() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -370,7 +417,7 @@ function test_multiple() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -393,7 +440,7 @@ function test_multiple() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -412,7 +459,7 @@ function test_url() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -430,7 +477,7 @@ function test_url() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -499,7 +546,7 @@ function test_reload() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -534,7 +581,7 @@ function test_reload() { gBrowser.loadURI(TESTROOT2 + "enabled.html"); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -552,7 +599,7 @@ function test_theme() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -577,7 +624,7 @@ function test_theme() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -630,7 +677,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { // Dismiss the notification @@ -640,7 +687,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { info("Timeouts after this probably mean bug 589954 regressed"); // Wait for the complete notification @@ -655,7 +702,7 @@ function test_renotify_installed() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -667,7 +714,7 @@ function test_renotify_installed() { aPanel.hidePopup(); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); From b6188758a5372dfbd39c457ba7636841148e7df7 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 8 Apr 2015 12:25:20 -0400 Subject: [PATCH 06/17] Bug 1148590 - Ignore IME notifications outside of the focused editor; r=esawin --- widget/android/nsWindow.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 5dadd77f7cd..10bbc0e6e26 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -32,6 +32,7 @@ using mozilla::unused; #include "nsFocusManager.h" #include "nsIWidgetListener.h" #include "nsViewManager.h" +#include "nsISelection.h" #include "nsIDOMSimpleGestureEvent.h" @@ -2197,7 +2198,19 @@ nsWindow::PostFlushIMEChanges() void nsWindow::FlushIMEChanges() { + // Only send change notifications if we are *not* masking events, + // i.e. if we have a focused editor, + NS_ENSURE_TRUE_VOID(!mIMEMaskEventsCount); + + nsCOMPtr imeSelection; + nsCOMPtr imeRoot; + + // If we are receiving notifications, we must have selection/root content. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(IMEStateManager::GetFocusSelectionAndRoot( + getter_AddRefs(imeSelection), getter_AddRefs(imeRoot)))); + nsRefPtr kungFuDeathGrip(this); + for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) { IMEChange &change = mIMETextChanges[i]; @@ -2213,8 +2226,8 @@ nsWindow::FlushIMEChanges() event.InitForQueryTextContent(change.mStart, change.mNewEnd - change.mStart); DispatchEvent(&event); - if (!event.mSucceeded) - return; + NS_ENSURE_TRUE_VOID(event.mSucceeded); + NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get()); } GeckoAppShell::NotifyIMEChange(event.mReply.mString, change.mStart, @@ -2227,8 +2240,8 @@ nsWindow::FlushIMEChanges() InitEvent(event, nullptr); DispatchEvent(&event); - if (!event.mSucceeded) - return; + NS_ENSURE_TRUE_VOID(event.mSucceeded); + NS_ENSURE_TRUE_VOID(event.mReply.mContentsRoot == imeRoot.get()); GeckoAppShell::NotifyIMEChange(EmptyString(), int32_t(event.GetSelectionStart()), From 7b9c815fed091c7835e118e2197f912178317786 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 8 Apr 2015 12:25:20 -0400 Subject: [PATCH 07/17] Bug 1148590 - Clear existing focus when closing selection; r=capella --- mobile/android/chrome/content/SelectionHandler.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mobile/android/chrome/content/SelectionHandler.js b/mobile/android/chrome/content/SelectionHandler.js index 9df76a501b7..794736325bc 100644 --- a/mobile/android/chrome/content/SelectionHandler.js +++ b/mobile/android/chrome/content/SelectionHandler.js @@ -385,6 +385,12 @@ var SelectionHandler = { return this.START_ERROR_NONTEXT_INPUT; } + const focus = Services.focus.focusedWindow; + if (focus) { + // Make sure any previous focus is cleared. + Services.focus.clearFocus(focus); + } + this._initTargetInfo(aElement, this.TYPE_SELECTION); // Perform the appropriate selection method, if we can't determine method, or it fails, return From d59ed759b9a534893cb9a6ffd78a0ba314bbcbea Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 8 Apr 2015 12:25:20 -0400 Subject: [PATCH 08/17] Bug 1151875 - Refactor GeckoEditable.ActionQueue methods; r=esawin --- mobile/android/base/GeckoEditable.java | 87 ++++++++++++++------------ 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index a0bde368fa3..3cf5e561938 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -297,14 +297,16 @@ final class GeckoEditable } } + /** + * Remove the head of the queue. Throw if queue is empty. + */ void poll() { if (DEBUG) { ThreadUtils.assertOnGeckoThread(); } - if (mActions.isEmpty()) { + if (mActions.poll() == null) { throw new IllegalStateException("empty actions queue"); } - mActions.poll(); synchronized(this) { if (mActions.isEmpty()) { @@ -313,13 +315,15 @@ final class GeckoEditable } } + /** + * Return, but don't remove, the head of the queue, or null if queue is empty. + * + * @return head of the queue or null if empty. + */ Action peek() { if (DEBUG) { ThreadUtils.assertOnGeckoThread(); } - if (mActions.isEmpty()) { - throw new IllegalStateException("empty actions queue"); - } return mActions.peek(); } @@ -669,7 +673,11 @@ final class GeckoEditable // GeckoEditableListener methods should all be called from the Gecko thread ThreadUtils.assertOnGeckoThread(); } + final Action action = mActionQueue.peek(); + if (action == null) { + throw new IllegalStateException("empty actions queue"); + } if (DEBUG) { Log.d(LOGTAG, "reply: Action(" + @@ -834,8 +842,8 @@ final class GeckoEditable /* An event (keypress, etc.) has potentially changed the selection, synchronize the selection here. There is not a race with the IC thread because the IC thread should be blocked on the event action */ - if (!mActionQueue.isEmpty() && - mActionQueue.peek().mType == Action.TYPE_EVENT) { + final Action action = mActionQueue.peek(); + if (action != null && action.mType == Action.TYPE_EVENT) { Selection.setSelection(mText, start, end); return; } @@ -909,46 +917,45 @@ final class GeckoEditable TextUtils.copySpansFrom(mText, start, Math.min(oldEnd, newEnd), Object.class, mChangedText, 0); - if (!mActionQueue.isEmpty()) { - final Action action = mActionQueue.peek(); - if ((action.mType == Action.TYPE_REPLACE_TEXT || - action.mType == Action.TYPE_COMPOSE_TEXT) && - start <= action.mStart && - action.mStart + action.mSequence.length() <= newEnd) { + final Action action = mActionQueue.peek(); + if (action != null && + (action.mType == Action.TYPE_REPLACE_TEXT || + action.mType == Action.TYPE_COMPOSE_TEXT) && + start <= action.mStart && + action.mStart + action.mSequence.length() <= newEnd) { - // actionNewEnd is the new end of the original replacement action - final int actionNewEnd = action.mStart + action.mSequence.length(); - int selStart = Selection.getSelectionStart(mText); - int selEnd = Selection.getSelectionEnd(mText); + // actionNewEnd is the new end of the original replacement action + final int actionNewEnd = action.mStart + action.mSequence.length(); + int selStart = Selection.getSelectionStart(mText); + int selEnd = Selection.getSelectionEnd(mText); - // Replace old spans with new spans - mChangedText.replace(action.mStart - start, actionNewEnd - start, - action.mSequence); - geckoReplaceText(start, oldEnd, mChangedText); + // Replace old spans with new spans + mChangedText.replace(action.mStart - start, actionNewEnd - start, + action.mSequence); + geckoReplaceText(start, oldEnd, mChangedText); - // delete/insert above might have moved our selection to somewhere else - // this happens when the Gecko text change covers a larger range than - // the original replacement action. Fix selection here - if (selStart >= start && selStart <= oldEnd) { - selStart = selStart < action.mStart ? selStart : - selStart < action.mEnd ? actionNewEnd : - selStart + actionNewEnd - action.mEnd; - mText.setSpan(Selection.SELECTION_START, selStart, selStart, - Spanned.SPAN_POINT_POINT); - } - if (selEnd >= start && selEnd <= oldEnd) { - selEnd = selEnd < action.mStart ? selEnd : - selEnd < action.mEnd ? actionNewEnd : - selEnd + actionNewEnd - action.mEnd; - mText.setSpan(Selection.SELECTION_END, selEnd, selEnd, - Spanned.SPAN_POINT_POINT); - } - } else { - geckoReplaceText(start, oldEnd, mChangedText); + // delete/insert above might have moved our selection to somewhere else + // this happens when the Gecko text change covers a larger range than + // the original replacement action. Fix selection here + if (selStart >= start && selStart <= oldEnd) { + selStart = selStart < action.mStart ? selStart : + selStart < action.mEnd ? actionNewEnd : + selStart + actionNewEnd - action.mEnd; + mText.setSpan(Selection.SELECTION_START, selStart, selStart, + Spanned.SPAN_POINT_POINT); } + if (selEnd >= start && selEnd <= oldEnd) { + selEnd = selEnd < action.mStart ? selEnd : + selEnd < action.mEnd ? actionNewEnd : + selEnd + actionNewEnd - action.mEnd; + mText.setSpan(Selection.SELECTION_END, selEnd, selEnd, + Spanned.SPAN_POINT_POINT); + } + } else { geckoReplaceText(start, oldEnd, mChangedText); } + geckoPostToIc(new Runnable() { @Override public void run() { From d29daa51fdf90a9eecda66338de1d3d46900eb9f Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 8 Apr 2015 12:25:20 -0400 Subject: [PATCH 09/17] Bug 1151875 - Never mask text updates in nsWindow; r=esawin --- mobile/android/base/GeckoEditable.java | 11 +++++++++++ widget/android/nsWindow.cpp | 15 ++------------- widget/android/nsWindow.h | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mobile/android/base/GeckoEditable.java b/mobile/android/base/GeckoEditable.java index 3cf5e561938..51c6dbf2bd0 100644 --- a/mobile/android/base/GeckoEditable.java +++ b/mobile/android/base/GeckoEditable.java @@ -876,6 +876,11 @@ final class GeckoEditable mText.insert(start, newText); } + private boolean isSameText(int start, int oldEnd, CharSequence newText) { + return oldEnd - start == newText.length() && + TextUtils.regionMatches(mText, start, newText, 0, oldEnd - start); + } + @Override public void onTextChange(final CharSequence text, final int start, final int unboundedOldEnd, final int unboundedNewEnd) { @@ -953,6 +958,12 @@ final class GeckoEditable } } else { + // Gecko side initiated the text change. + if (isSameText(start, oldEnd, mChangedText)) { + // Nothing to do because the text is the same. + // This could happen when the composition is updated for example. + return; + } geckoReplaceText(start, oldEnd, mChangedText); } diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 10bbc0e6e26..c95262a8376 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -179,7 +179,6 @@ nsWindow::nsWindow() : mParent(nullptr), mFocus(nullptr), mIMEMaskSelectionUpdate(false), - mIMEMaskTextUpdate(false), mIMEMaskEventsCount(1), // Mask IME events since there's no focus yet mIMERanges(new TextRangeArray()), mIMEUpdatingContext(false), @@ -1693,7 +1692,6 @@ nsWindow::RemoveIMEComposition() nsRefPtr kungFuDeathGrip(this); AutoIMEMask selMask(mIMEMaskSelectionUpdate); - AutoIMEMask textMask(mIMEMaskTextUpdate); WidgetCompositionEvent compositionCommitEvent(true, NS_COMPOSITION_COMMIT_AS_IS, @@ -1705,7 +1703,6 @@ nsWindow::RemoveIMEComposition() void nsWindow::OnIMEEvent(AndroidGeckoEvent *ae) { - MOZ_ASSERT(!mIMEMaskTextUpdate); MOZ_ASSERT(!mIMEMaskSelectionUpdate); /* Rules for managing IME between Gecko and Java: @@ -1782,9 +1779,6 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae) Selection updates are masked so the result of our temporary selection event is not passed on to Java - - Text updates are passed on, so the Java text can shadow the - Gecko text */ AutoIMEMask selMask(mIMEMaskSelectionUpdate); const auto composition(GetIMEComposition()); @@ -1933,11 +1927,10 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae) to eliminate the possibility of this event altering the text content unintentionally. - Selection and text updates are masked so the result of + Selection updates are masked so the result of temporary events are not passed on to Java */ AutoIMEMask selMask(mIMEMaskSelectionUpdate); - AutoIMEMask textMask(mIMEMaskTextUpdate); const auto composition(GetIMEComposition()); MOZ_ASSERT(!composition || !composition->IsEditorHandlingEvent()); @@ -2007,11 +2000,10 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae) * Remove any previous composition. This is only used for * visual indication and does not affect the text content. * - * Selection and text updates are masked so the result of + * Selection updates are masked so the result of * temporary events are not passed on to Java */ AutoIMEMask selMask(mIMEMaskSelectionUpdate); - AutoIMEMask textMask(mIMEMaskTextUpdate); RemoveIMEComposition(); mIMERanges->Clear(); } @@ -2256,9 +2248,6 @@ nsWindow::NotifyIMEOfTextChange(const IMENotification& aIMENotification) MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE, "NotifyIMEOfTextChange() is called with invaild notification"); - if (mIMEMaskTextUpdate) - return NS_OK; - ALOGIME("IME: NotifyIMEOfTextChange: s=%d, oe=%d, ne=%d", aIMENotification.mTextChangeData.mStartOffset, aIMENotification.mTextChangeData.mOldEndOffset, diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 407b36dcb73..2875ad8591c 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -217,7 +217,7 @@ protected: nsCOMPtr mIdleService; - bool mIMEMaskSelectionUpdate, mIMEMaskTextUpdate; + bool mIMEMaskSelectionUpdate; int32_t mIMEMaskEventsCount; // Mask events when > 0 nsRefPtr mIMERanges; bool mIMEUpdatingContext; From aaf88a221753264a675633e6777e46dcf0cb628f Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Wed, 8 Apr 2015 09:57:47 -0700 Subject: [PATCH 10/17] Bug 1152358 - Remove :not(:-moz-lw-theme) selector from devedition.css;r=Gijs --- browser/themes/linux/devedition.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/themes/linux/devedition.css b/browser/themes/linux/devedition.css index 37a53f6c8a6..b45b1357698 100644 --- a/browser/themes/linux/devedition.css +++ b/browser/themes/linux/devedition.css @@ -27,8 +27,8 @@ } /* Square back and forward buttons */ -#back-button:not(:-moz-lwtheme) > .toolbarbutton-icon, -#forward-button:not(:-moz-lwtheme) > .toolbarbutton-icon { +#back-button > .toolbarbutton-icon, +#forward-button > .toolbarbutton-icon { margin: 0; border: none; padding: 2px 6px; From c968722433eef1bf1fdb0faeab6f6e8416b79fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 8 Apr 2015 19:25:11 +0200 Subject: [PATCH 11/17] Backed out changeset 5a5243c9e021 --- .../content/test/general/browser_bug553455.js | 83 ++++--------------- 1 file changed, 18 insertions(+), 65 deletions(-) diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js index 2a27ba3db8c..c468d45f910 100644 --- a/browser/base/content/test/general/browser_bug553455.js +++ b/browser/base/content/test/general/browser_bug553455.js @@ -5,7 +5,6 @@ const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/"; const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; -const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; const PROGRESS_NOTIFICATION = "addon-progress"; @@ -87,52 +86,6 @@ function wait_for_notification_close(aCallback) { }, false); } -function wait_for_install_dialog(aCallback) { - if (Preferences.get("xpinstall.customConfirmationUI", false)) { - wait_for_notification("addon-install-confirmation", function(aPanel) { - aCallback(); - return; - }); - } - - info("Waiting for install dialog"); - - Services.wm.addListener({ - onOpenWindow: function(aXULWindow) { - info("Install dialog opened, waiting for focus"); - Services.wm.removeListener(this); - - var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - waitForFocus(function() { - info("Saw install dialog"); - is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open"); - - // Override the countdown timer on the accept button - var button = domwindow.document.documentElement.getButton("accept"); - button.disabled = false; - - aCallback(); - }, domwindow); - }, - - onCloseWindow: function(aXULWindow) { - }, - - onWindowTitleChange: function(aXULWindow, aNewTitle) { - } - }); -} - -function accept_install_dialog() { - if (Preferences.get("xpinstall.customConfirmationUI", false)) { - document.getElementById("addon-install-confirmation-accept").click(); - } else { - let win = Services.wm.getMostRecentWindow("Addons:Install"); - win.document.documentElement.acceptDialog(); - } -} - function wait_for_single_notification(aCallback) { function inner_waiter() { info("Waiting for single notification"); @@ -212,7 +165,7 @@ function test_blocked_install() { "Should have seen the right message"); // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -230,7 +183,7 @@ function test_blocked_install() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); // Click on Allow @@ -255,7 +208,7 @@ function test_whitelisted_install() { gBrowser.selectedTab = originalTab; // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { is(gBrowser.selectedTab, tab, "tab selected in response to the addon-install-confirmation notification"); @@ -277,7 +230,7 @@ function test_whitelisted_install() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -378,7 +331,7 @@ function test_restartless() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -399,7 +352,7 @@ function test_restartless() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -417,7 +370,7 @@ function test_multiple() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -440,7 +393,7 @@ function test_multiple() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -459,7 +412,7 @@ function test_url() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -477,7 +430,7 @@ function test_url() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -546,7 +499,7 @@ function test_reload() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -581,7 +534,7 @@ function test_reload() { gBrowser.loadURI(TESTROOT2 + "enabled.html"); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -599,7 +552,7 @@ function test_theme() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -624,7 +577,7 @@ function test_theme() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -677,7 +630,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { // Dismiss the notification @@ -687,7 +640,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_install_dialog(function() { + wait_for_notification("addon-install-confirmation", function(aPanel) { info("Timeouts after this probably mean bug 589954 regressed"); // Wait for the complete notification @@ -702,7 +655,7 @@ function test_renotify_installed() { }); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); @@ -714,7 +667,7 @@ function test_renotify_installed() { aPanel.hidePopup(); }); - accept_install_dialog(); + document.getElementById("addon-install-confirmation-accept").click(); }); }); From 9163a048c3364e0d22a61597e3afeafd11e13785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 8 Apr 2015 19:26:16 +0200 Subject: [PATCH 12/17] Bug 1148016 - make browser_bug553455.js handle xpinstall.customConfirmationUI=false. r=dtownsend --- .../content/test/general/browser_bug553455.js | 83 +++++++++++++++---- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js index c468d45f910..269137b9aba 100644 --- a/browser/base/content/test/general/browser_bug553455.js +++ b/browser/base/content/test/general/browser_bug553455.js @@ -5,6 +5,7 @@ const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/"; const SECUREROOT = "https://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/"; +const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"; const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts"; const PROGRESS_NOTIFICATION = "addon-progress"; @@ -86,6 +87,52 @@ function wait_for_notification_close(aCallback) { }, false); } +function wait_for_install_dialog(aCallback) { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + wait_for_notification("addon-install-confirmation", function(aPanel) { + aCallback(); + }); + return; + } + + info("Waiting for install dialog"); + + Services.wm.addListener({ + onOpenWindow: function(aXULWindow) { + info("Install dialog opened, waiting for focus"); + Services.wm.removeListener(this); + + var domwindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + waitForFocus(function() { + info("Saw install dialog"); + is(domwindow.document.location.href, XPINSTALL_URL, "Should have seen the right window open"); + + // Override the countdown timer on the accept button + var button = domwindow.document.documentElement.getButton("accept"); + button.disabled = false; + + aCallback(); + }, domwindow); + }, + + onCloseWindow: function(aXULWindow) { + }, + + onWindowTitleChange: function(aXULWindow, aNewTitle) { + } + }); +} + +function accept_install_dialog() { + if (Preferences.get("xpinstall.customConfirmationUI", false)) { + document.getElementById("addon-install-confirmation-accept").click(); + } else { + let win = Services.wm.getMostRecentWindow("Addons:Install"); + win.document.documentElement.acceptDialog(); + } +} + function wait_for_single_notification(aCallback) { function inner_waiter() { info("Waiting for single notification"); @@ -165,7 +212,7 @@ function test_blocked_install() { "Should have seen the right message"); // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -183,7 +230,7 @@ function test_blocked_install() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); // Click on Allow @@ -208,7 +255,7 @@ function test_whitelisted_install() { gBrowser.selectedTab = originalTab; // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { is(gBrowser.selectedTab, tab, "tab selected in response to the addon-install-confirmation notification"); @@ -230,7 +277,7 @@ function test_whitelisted_install() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -331,7 +378,7 @@ function test_restartless() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -352,7 +399,7 @@ function test_restartless() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -370,7 +417,7 @@ function test_multiple() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -393,7 +440,7 @@ function test_multiple() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -412,7 +459,7 @@ function test_url() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -430,7 +477,7 @@ function test_url() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -499,7 +546,7 @@ function test_reload() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -534,7 +581,7 @@ function test_reload() { gBrowser.loadURI(TESTROOT2 + "enabled.html"); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -552,7 +599,7 @@ function test_theme() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { let notification = aPanel.childNodes[0]; @@ -577,7 +624,7 @@ function test_theme() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -630,7 +677,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { // Wait for the complete notification wait_for_notification("addon-install-complete", function(aPanel) { // Dismiss the notification @@ -640,7 +687,7 @@ function test_renotify_installed() { // Wait for the progress notification wait_for_progress_notification(function(aPanel) { // Wait for the install confirmation dialog - wait_for_notification("addon-install-confirmation", function(aPanel) { + wait_for_install_dialog(function() { info("Timeouts after this probably mean bug 589954 regressed"); // Wait for the complete notification @@ -655,7 +702,7 @@ function test_renotify_installed() { }); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); @@ -667,7 +714,7 @@ function test_renotify_installed() { aPanel.hidePopup(); }); - document.getElementById("addon-install-confirmation-accept").click(); + accept_install_dialog(); }); }); From ace3fe6792a3a82c9d0d61cae10296b1d93a515c Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Wed, 1 Apr 2015 13:38:24 -0400 Subject: [PATCH 13/17] Bug 1150120 - Pass outerWindowID up when sending contextmenu message up from content process. r=gabor. This will hopefully allow us to avoid using CPOWs for contentDocument and contentWindow when doing things like printing or viewing the source of subframes. --- browser/base/content/content.js | 6 +++++- browser/base/content/nsContextMenu.js | 31 ++++++++++++++++----------- browser/base/content/tabbrowser.xml | 1 + 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/browser/base/content/content.js b/browser/base/content/content.js index e46053c75c7..15be08c8dc3 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -164,6 +164,9 @@ let handleContentContextMenu = function (event) { let baseURI = doc.baseURI; let referrer = doc.referrer; let referrerPolicy = doc.referrerPolicy; + let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .outerWindowID; // Media related cache info parent needs for saving let contentType = null; @@ -199,7 +202,8 @@ let handleContentContextMenu = function (event) { sendSyncMessage("contextmenu", { editFlags, spellInfo, customMenuItems, addonInfo, principal, docLocation, charSet, baseURI, referrer, - referrerPolicy, contentType, contentDisposition }, + referrerPolicy, contentType, contentDisposition, + frameOuterWindowID }, { event, popupNode: event.target }); } else { diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index c937cd5fd8f..c418626af9e 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -599,24 +599,31 @@ nsContextMenu.prototype = { this.focusedWindow = win; this.focusedElement = elt; + let ownerDoc = this.target.ownerDocument; + // If this is a remote context menu event, use the information from // gContextMenuContentData instead. if (this.isRemote) { this.browser = gContextMenuContentData.browser; this.principal = gContextMenuContentData.principal; + this.frameOuterWindowID = gContextMenuContentData.frameOuterWindowID; } else { editFlags = SpellCheckHelper.isEditable(this.target, window); - this.browser = this.target.ownerDocument.defaultView - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - this.principal = this.target.ownerDocument.nodePrincipal; + this.browser = ownerDoc.defaultView + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler; + this.principal = ownerDoc.nodePrincipal; + this.frameOuterWindowID = ownerDoc.defaultView + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .outerWindowID; } this.onSocial = !!this.browser.getAttribute("origin"); // Check if we are in a synthetic document (stand alone image, video, etc.). - this.inSyntheticDoc = this.target.ownerDocument.mozSyntheticDocument; + this.inSyntheticDoc = ownerDoc.mozSyntheticDocument; // First, do checks for nodes that never have children. if (this.target.nodeType == Node.ELEMENT_NODE) { // See if the user clicked on an image. @@ -635,7 +642,7 @@ nsContextMenu.prototype = { var descURL = this.target.getAttribute("longdesc"); if (descURL) { - this.imageDescURL = makeURLAbsolute(this.target.ownerDocument.body.baseURI, descURL); + this.imageDescURL = makeURLAbsolute(ownerDoc.body.baseURI, descURL); } } else if (this.target instanceof HTMLCanvasElement) { @@ -685,7 +692,7 @@ nsContextMenu.prototype = { this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD); } else if (this.target instanceof HTMLHtmlElement) { - var bodyElt = this.target.ownerDocument.body; + var bodyElt = ownerDoc.body; if (bodyElt) { let computedURL; try { @@ -776,11 +783,11 @@ nsContextMenu.prototype = { this.onMathML = true; // See if the user clicked in a frame. - var docDefaultView = this.target.ownerDocument.defaultView; + var docDefaultView = ownerDoc.defaultView; if (docDefaultView != docDefaultView.top) { this.inFrame = true; - if (this.target.ownerDocument.isSrcdocDocument) { + if (ownerDoc.isSrcdocDocument) { this.inSrcdocFrame = true; } } @@ -805,7 +812,7 @@ nsContextMenu.prototype = { InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo); } else { - var targetWin = this.target.ownerDocument.defaultView; + var targetWin = ownerDoc.defaultView; var editingSession = targetWin.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIInterfaceRequestor) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index fb8563658f3..f9cc65462e7 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3776,6 +3776,7 @@ referrerPolicy: aMessage.data.referrerPolicy, contentType: aMessage.data.contentType, contentDisposition: aMessage.data.contentDisposition, + frameOuterWindowID: aMessage.data.frameOuterWindowID, }; let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); let event = gContextMenuContentData.event; From 67f20bddfb963c15a5a2528c76058982fe9e4426 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Apr 2015 11:25:56 -0700 Subject: [PATCH 14/17] Bug 1145247 - Add AutoTimelineMarker RAII class; r=smaug --- docshell/base/AutoTimelineMarker.h | 73 ++++++++++++++++++++++++++++++ docshell/base/moz.build | 1 + 2 files changed, 74 insertions(+) create mode 100644 docshell/base/AutoTimelineMarker.h diff --git a/docshell/base/AutoTimelineMarker.h b/docshell/base/AutoTimelineMarker.h new file mode 100644 index 00000000000..6a8096dd8fe --- /dev/null +++ b/docshell/base/AutoTimelineMarker.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef AutoTimelineMarker_h__ +#define AutoTimelineMarker_h__ + +#include "mozilla/GuardObjects.h" +#include "mozilla/Move.h" +#include "nsDocShell.h" +#include "nsRefPtr.h" + +namespace mozilla { + +// # AutoTimelineMarker +// +// An RAII class to trace some task in the platform by adding a start and end +// timeline marker pair. These markers are then rendered in the devtools' +// performance tool's waterfall graph. +// +// Example usage: +// +// { +// AutoTimelineMarker marker(mDocShell, "Parse CSS"); +// nsresult rv = ParseTheCSSFile(mFile); +// ... +// } + +class MOZ_STACK_CLASS AutoTimelineMarker +{ + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER; + + nsRefPtr mDocShell; + const char* mName; + + bool + DocShellIsRecording(nsDocShell& aDocShell) + { + bool isRecording = false; + if (nsDocShell::gProfileTimelineRecordingsCount > 0) { + aDocShell.GetRecordProfileTimelineMarkers(&isRecording); + } + return isRecording; + } + +public: + explicit AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mDocShell(nullptr) + , mName(aName) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + nsDocShell* docShell = static_cast(aDocShell); + if (docShell && DocShellIsRecording(*docShell)) { + mDocShell = docShell; + mDocShell->AddProfileTimelineMarker(mName, TRACING_INTERVAL_START); + } + } + + ~AutoTimelineMarker() + { + if (mDocShell) { + mDocShell->AddProfileTimelineMarker(mName, TRACING_INTERVAL_END); + } + } +}; + +} // namespace mozilla + +#endif /* AutoTimelineMarker_h__ */ diff --git a/docshell/base/moz.build b/docshell/base/moz.build index 4da08ab4d1d..92e25846b5b 100644 --- a/docshell/base/moz.build +++ b/docshell/base/moz.build @@ -42,6 +42,7 @@ EXPORTS += [ ] EXPORTS.mozilla += [ + 'AutoTimelineMarker.h', 'IHistory.h', 'LoadContext.h', ] From 41b3a2a72d3ac470b198fc42d75fe14f35dd51cc Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Apr 2015 11:25:56 -0700 Subject: [PATCH 15/17] Bug 1151703 - Part 1: Add timeline tracing markers for HTML and XML parsing; r=smaug --- dom/base/nsContentUtils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4d5c4ddf66d..665e5184fa6 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -30,6 +30,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" +#include "mozilla/AutoTimelineMarker.h" #include "mozilla/Base64.h" #include "mozilla/CheckedInt.h" #include "mozilla/DebugOnly.h" @@ -4246,6 +4247,8 @@ nsContentUtils::ParseFragmentHTML(const nsAString& aSourceBuffer, bool aQuirks, bool aPreventScriptExecution) { + AutoTimelineMarker m(aTargetNode->OwnerDoc()->GetDocShell(), "Parse HTML"); + if (nsContentUtils::sFragmentParsingActive) { NS_NOTREACHED("Re-entrant fragment parsing attempted."); return NS_ERROR_DOM_INVALID_STATE_ERR; @@ -4272,6 +4275,8 @@ nsContentUtils::ParseDocumentHTML(const nsAString& aSourceBuffer, nsIDocument* aTargetDocument, bool aScriptingEnabledForNoscriptParsing) { + AutoTimelineMarker m(aTargetDocument->GetDocShell(), "Parse HTML"); + if (nsContentUtils::sFragmentParsingActive) { NS_NOTREACHED("Re-entrant fragment parsing attempted."); return NS_ERROR_DOM_INVALID_STATE_ERR; @@ -4297,6 +4302,8 @@ nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer, bool aPreventScriptExecution, nsIDOMDocumentFragment** aReturn) { + AutoTimelineMarker m(aDocument->GetDocShell(), "Parse XML"); + if (nsContentUtils::sFragmentParsingActive) { NS_NOTREACHED("Re-entrant fragment parsing attempted."); return NS_ERROR_DOM_INVALID_STATE_ERR; From fdb112a3a6225352d8e3a7e1e1dfbdda3aa73642 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Apr 2015 11:25:56 -0700 Subject: [PATCH 16/17] Bug 1151703 - Part 2: Show XML and HTML parsing markers in the performance tool; r=jsantell --- browser/devtools/shared/timeline/global.js | 10 ++++++++++ .../en-US/chrome/browser/devtools/timeline.properties | 2 ++ toolkit/devtools/server/actors/timeline.js | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/browser/devtools/shared/timeline/global.js b/browser/devtools/shared/timeline/global.js index 24f94a302e1..c1192ace0fe 100644 --- a/browser/devtools/shared/timeline/global.js +++ b/browser/devtools/shared/timeline/global.js @@ -53,6 +53,16 @@ const TIMELINE_BLUEPRINT = { colorName: "highlight-lightorange", label: L10N.getStr("timeline.label.javascript2") }, + "Parse HTML": { + group: 1, + colorName: "highlight-purple", + label: L10N.getStr("timeline.label.parseHTML") + }, + "Parse XML": { + group: 1, + colorName: "highlight-purple", + label: L10N.getStr("timeline.label.parseXML") + }, "ConsoleTime": { group: 2, colorName: "highlight-bluegrey", diff --git a/browser/locales/en-US/chrome/browser/devtools/timeline.properties b/browser/locales/en-US/chrome/browser/devtools/timeline.properties index ec3d0b61f45..bc6336a8bcc 100644 --- a/browser/locales/en-US/chrome/browser/devtools/timeline.properties +++ b/browser/locales/en-US/chrome/browser/devtools/timeline.properties @@ -40,6 +40,8 @@ timeline.label.styles2=Recalculate Style timeline.label.reflow2=Layout timeline.label.paint=Paint timeline.label.javascript2=Function Call +timeline.label.parseHTML=Parse HTML +timeline.label.parseXML=Parse XML timeline.label.domevent=DOM Event timeline.label.consoleTime=Console diff --git a/toolkit/devtools/server/actors/timeline.js b/toolkit/devtools/server/actors/timeline.js index ad901509524..3d2cef62a2b 100644 --- a/toolkit/devtools/server/actors/timeline.js +++ b/toolkit/devtools/server/actors/timeline.js @@ -184,7 +184,7 @@ let TimelineActor = exports.TimelineActor = protocol.ActorClass({ let markers = []; for (let docShell of this.docShells) { - markers = [...markers, ...docShell.popProfileTimelineMarkers()]; + markers.push(...docShell.popProfileTimelineMarkers()); } // The docshell may return markers with stack traces attached. From b13239da087a0df6fb3a84f2b66dd82ddbe6c2a6 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 8 Apr 2015 11:25:56 -0700 Subject: [PATCH 17/17] Bug 1151703 - Part 3: Add a test for HTML parsing markers; r=jsantell --- browser/devtools/performance/test/browser.ini | 4 +++ .../test/browser_markers-parse-html.js | 36 +++++++++++++++++++ .../performance/test/doc_innerHTML.html | 20 +++++++++++ browser/devtools/shared/timeline/global.js | 4 +-- 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 browser/devtools/performance/test/browser_markers-parse-html.js create mode 100644 browser/devtools/performance/test/doc_innerHTML.html diff --git a/browser/devtools/performance/test/browser.ini b/browser/devtools/performance/test/browser.ini index 25b7c6f60e9..b01869a7ede 100644 --- a/browser/devtools/performance/test/browser.ini +++ b/browser/devtools/performance/test/browser.ini @@ -3,6 +3,7 @@ tags = devtools skip-if = e10s # Handle in Bug 1077464 for profiler subsuite = devtools support-files = + doc_innerHTML.html doc_simple-test.html head.js @@ -10,6 +11,9 @@ support-files = # that need to be moved over to performance tool [browser_perf-aaa-run-first-leaktest.js] + +[browser_markers-parse-html.js] + [browser_perf-allocations-to-samples.js] [browser_perf-compatibility-01.js] [browser_perf-compatibility-02.js] diff --git a/browser/devtools/performance/test/browser_markers-parse-html.js b/browser/devtools/performance/test/browser_markers-parse-html.js new file mode 100644 index 00000000000..442ff90c3be --- /dev/null +++ b/browser/devtools/performance/test/browser_markers-parse-html.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that we get a "Parse HTML" marker when a page sets innerHTML. + */ + +const TEST_URL = EXAMPLE_URL + "doc_innerHTML.html" + +function* getMarkers(front) { + const { promise, resolve } = Promise.defer(); + const handler = (_, markers) => { + resolve(markers); + }; + front.on("markers", handler); + + yield front.startRecording({ withTicks: true }); + + const markers = yield promise; + front.off("markers", handler); + yield front.stopRecording(); + + return markers; +} + +function* spawnTest () { + let { target, front } = yield initBackend(TEST_URL); + + const markers = yield getMarkers(front); + info("markers = " + JSON.stringify(markers, null, 2)); + ok(markers.some(m => m.name === "Parse HTML" && m.stack != undefined), + "Should get some Parse HTML markers"); + + yield removeTab(target.tab); + finish(); +} diff --git a/browser/devtools/performance/test/doc_innerHTML.html b/browser/devtools/performance/test/doc_innerHTML.html new file mode 100644 index 00000000000..84439b13d71 --- /dev/null +++ b/browser/devtools/performance/test/doc_innerHTML.html @@ -0,0 +1,20 @@ + + + + + + + Performance tool + innerHTML test page + + + + + + + diff --git a/browser/devtools/shared/timeline/global.js b/browser/devtools/shared/timeline/global.js index c1192ace0fe..6266274d852 100644 --- a/browser/devtools/shared/timeline/global.js +++ b/browser/devtools/shared/timeline/global.js @@ -55,12 +55,12 @@ const TIMELINE_BLUEPRINT = { }, "Parse HTML": { group: 1, - colorName: "highlight-purple", + colorName: "highlight-lightorange", label: L10N.getStr("timeline.label.parseHTML") }, "Parse XML": { group: 1, - colorName: "highlight-purple", + colorName: "highlight-lightorange", label: L10N.getStr("timeline.label.parseXML") }, "ConsoleTime": {