diff --git a/CLOBBER b/CLOBBER index 93aece62ddc3..bc5070e99d5f 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 878935 landed without a UUID change (and was since backed out) +Bug 910189 requires a clobber due to Proguard inner class errors from bug 946083. diff --git a/browser/components/customizableui/src/CustomizableUI.jsm b/browser/components/customizableui/src/CustomizableUI.jsm index 6f086d9cd162..6e0cb152e7ec 100644 --- a/browser/components/customizableui/src/CustomizableUI.jsm +++ b/browser/components/customizableui/src/CustomizableUI.jsm @@ -1695,7 +1695,10 @@ let CustomizableUIInternal = { // If the widget doesn't have an existing placement, and it hasn't been // seen before, then add it to its default area so it can be used. - if (autoAdd && !widget.currentArea && !gSeenWidgets.has(widget.id)) { + // If the widget is not removable, we *have* to add it to its default + // area here. + let canBeAutoAdded = autoAdd && !gSeenWidgets.has(widget.id); + if (!widget.currentArea && (!widget.removable || canBeAutoAdded)) { this.beginBatchUpdate(); try { gSeenWidgets.add(widget.id); diff --git a/browser/components/customizableui/test/browser.ini b/browser/components/customizableui/test/browser.ini index 3a7dfad508bf..e80ed076dbc1 100644 --- a/browser/components/customizableui/test/browser.ini +++ b/browser/components/customizableui/test/browser.ini @@ -48,4 +48,5 @@ skip-if = os == "mac" [browser_944887_destroyWidget_should_destroy_in_palette.js] [browser_945739_showInPrivateBrowsing_customize_mode.js] [browser_947987_removable_default.js] +[browser_948985_non_removable_defaultArea.js] [browser_panel_toggle.js] diff --git a/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js b/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js new file mode 100644 index 000000000000..456c9ed02a97 --- /dev/null +++ b/browser/components/customizableui/test/browser_948985_non_removable_defaultArea.js @@ -0,0 +1,32 @@ +/* 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/. */ + +const kWidgetId = "test-destroy-non-removable-defaultArea"; + +add_task(function() { + let spec = {id: kWidgetId, label: "Test non-removable defaultArea re-adding.", + removable: false, defaultArea: CustomizableUI.AREA_NAVBAR}; + CustomizableUI.createWidget(spec); + let placement = CustomizableUI.getPlacementOfWidget(kWidgetId); + ok(placement, "Should have placed the widget."); + is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar"); + CustomizableUI.destroyWidget(kWidgetId); + CustomizableUI.removeWidgetFromArea(kWidgetId); + + CustomizableUI.createWidget(spec); + ok(placement, "Should have placed the widget."); + is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar"); + CustomizableUI.destroyWidget(kWidgetId); + CustomizableUI.removeWidgetFromArea(kWidgetId); + + const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd"; + Services.prefs.setBoolPref(kPrefCustomizationAutoAdd, false); + CustomizableUI.createWidget(spec); + ok(placement, "Should have placed the widget."); + is(placement && placement.area, CustomizableUI.AREA_NAVBAR, "Widget should be in navbar"); + CustomizableUI.destroyWidget(kWidgetId); + CustomizableUI.removeWidgetFromArea(kWidgetId); + Services.prefs.clearUserPref(kPrefCustomizationAutoAdd); +}); + diff --git a/browser/components/nsBrowserContentHandler.js b/browser/components/nsBrowserContentHandler.js index 466950c1d7c9..c6685a14d26d 100644 --- a/browser/components/nsBrowserContentHandler.js +++ b/browser/components/nsBrowserContentHandler.js @@ -583,6 +583,25 @@ nsBrowserContentHandler.prototype = { overridePage = overridePage.replace("%OLD_VERSION%", old_mstone); break; + + // Temporary case for Australis whatsnew + case OVERRIDE_NEW_BUILD_ID: + let locale = "en-US"; + try { + locale = Services.prefs.getCharPref("general.useragent.locale"); + } catch (e) {} + + let showedAustralisWhatsNew = false; + try { + showedAustralisWhatsNew = Services.prefs.getBoolPref("browser.showedAustralisWhatsNew"); + } catch(e) {} + + // Show the Australis whatsnew page for en-US if we haven't yet shown it + if (!showedAustralisWhatsNew && locale == "en-US") { + Services.prefs.setBoolPref("browser.showedAustralisWhatsNew", true); + overridePage = "https://www.mozilla.org/en-US/firefox/29.0a1/whatsnew/"; + } + break; } } } catch (ex) {} diff --git a/browser/components/sessionstore/src/TabState.jsm b/browser/components/sessionstore/src/TabState.jsm index 3cf7b3bcea8c..e35bf4b5f0d1 100644 --- a/browser/components/sessionstore/src/TabState.jsm +++ b/browser/components/sessionstore/src/TabState.jsm @@ -182,9 +182,6 @@ let TabStateInternal = { tabData.index = history.index; } - // Copy data from the persistent cache. - this._copyFromPersistentCache(tab, tabData); - // If we're still the latest async collection for the given tab and // the cache hasn't been filled by collect() in the meantime, let's // fill the cache with the data we received. @@ -193,6 +190,16 @@ let TabStateInternal = { this._pendingCollections.delete(browser); } + // Copy data from the persistent cache. We need to create an explicit + // copy of the |tabData| object so that the properties injected by + // |_copyFromPersistentCache| don't end up in the non-persistent cache. + // The persistent cache does not store "null" values, so any values that + // have been cleared by the frame script would not be overriden by + // |_copyFromPersistentCache|. These two caches are only an interim + // solution and the non-persistent one will go away soon. + tabData = Utils.copy(tabData); + this._copyFromPersistentCache(tab, tabData); + throw new Task.Result(tabData); }.bind(this)); @@ -219,7 +226,16 @@ let TabStateInternal = { throw new TypeError("Expecting a tab"); } if (TabStateCache.has(tab)) { - return TabStateCache.get(tab); + // Copy data from the persistent cache. We need to create an explicit + // copy of the |tabData| object so that the properties injected by + // |_copyFromPersistentCache| don't end up in the non-persistent cache. + // The persistent cache does not store "null" values, so any values that + // have been cleared by the frame script would not be overriden by + // |_copyFromPersistentCache|. These two caches are only an interim + // solution and the non-persistent one will go away soon. + let tabData = Utils.copy(TabStateCache.get(tab)); + this._copyFromPersistentCache(tab, tabData); + return tabData; } let tabData = this._collectSyncUncached(tab); @@ -228,6 +244,16 @@ let TabStateInternal = { TabStateCache.set(tab, tabData); } + // Copy data from the persistent cache. We need to create an explicit + // copy of the |tabData| object so that the properties injected by + // |_copyFromPersistentCache| don't end up in the non-persistent cache. + // The persistent cache does not store "null" values, so any values that + // have been cleared by the frame script would not be overriden by + // |_copyFromPersistentCache|. These two caches are only an interim + // solution and the non-persistent one will go away soon. + tabData = Utils.copy(tabData); + this._copyFromPersistentCache(tab, tabData); + // Prevent all running asynchronous collections from filling the cache. // Every asynchronous data collection started before a collectSync() call // can't expect to retrieve different data than the sync call. That's why @@ -262,7 +288,13 @@ let TabStateInternal = { * up-to-date. */ clone: function (tab) { - return this._collectSyncUncached(tab, {includePrivateData: true}); + let options = {includePrivateData: true}; + let tabData = this._collectSyncUncached(tab, options); + + // Copy data from the persistent cache. + this._copyFromPersistentCache(tab, tabData, options); + + return tabData; }, /** @@ -305,9 +337,6 @@ let TabStateInternal = { tabData.index = history.index; } - // Copy data from the persistent cache. - this._copyFromPersistentCache(tab, tabData, options); - return tabData; }, diff --git a/browser/components/sessionstore/src/Utils.jsm b/browser/components/sessionstore/src/Utils.jsm index e061cd35b86a..63c3c72da7df 100644 --- a/browser/components/sessionstore/src/Utils.jsm +++ b/browser/components/sessionstore/src/Utils.jsm @@ -64,5 +64,16 @@ this.Utils = Object.freeze({ map.set(otherKey, value); map.delete(key); } + }, + + // Copies all properties of a given object to a new one and returns it. + copy: function (from) { + let to = {}; + + for (let key of Object.keys(from)) { + to[key] = from[key]; + } + + return to; } }); diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index f03d25c453a7..74acab2c7604 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -1928,7 +1928,10 @@ Breakpoints.prototype = { let disabledPromise = this._disabled.get(identifier); if (disabledPromise) { disabledPromise.then(({ conditionalExpression: previousValue }) => { - aBreakpointClient.conditionalExpression = previousValue; + // Setting a falsy conditional expression is redundant. + if (previousValue) { + aBreakpointClient.conditionalExpression = previousValue; + } }); this._disabled.delete(identifier); } diff --git a/browser/devtools/debugger/test/browser.ini b/browser/devtools/debugger/test/browser.ini index 373ff46ac700..000fa52e2b8a 100644 --- a/browser/devtools/debugger/test/browser.ini +++ b/browser/devtools/debugger/test/browser.ini @@ -100,6 +100,7 @@ support-files = [browser_dbg_conditional-breakpoints-01.js] [browser_dbg_conditional-breakpoints-02.js] [browser_dbg_conditional-breakpoints-03.js] +[browser_dbg_conditional-breakpoints-04.js] [browser_dbg_controller-evaluate-01.js] [browser_dbg_controller-evaluate-02.js] [browser_dbg_debugger-statement.js] diff --git a/browser/devtools/debugger/test/browser_dbg_closure-inspection.js b/browser/devtools/debugger/test/browser_dbg_closure-inspection.js index 1acc1d1fc765..f04c12837d22 100644 --- a/browser/devtools/debugger/test/browser_dbg_closure-inspection.js +++ b/browser/devtools/debugger/test/browser_dbg_closure-inspection.js @@ -77,13 +77,13 @@ function test() { .getAttribute("value"), "getName", "Should have the right property name for 'getName' in person."); is(personNode.get("getName").target.querySelector(".value") - .getAttribute("value"), "Function", + .getAttribute("value"), "_pfactory/<.getName()", "'getName' in person should have the right value."); is(personNode.get("getFoo").target.querySelector(".name") .getAttribute("value"), "getFoo", "Should have the right property name for 'getFoo' in person."); is(personNode.get("getFoo").target.querySelector(".value") - .getAttribute("value"), "Function", + .getAttribute("value"), "_pfactory/<.getFoo()", "'getFoo' in person should have the right value."); // Expand the function nodes. This causes their properties to be diff --git a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-04.js b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-04.js new file mode 100644 index 000000000000..055a0864b120 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-04.js @@ -0,0 +1,84 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Make sure that conditional breakpoints with undefined expressions + * are stored as plain breakpoints when re-enabling them. + */ + +const TAB_URL = EXAMPLE_URL + "doc_conditional-breakpoints.html"; + +function test() { + let gTab, gDebuggee, gPanel, gDebugger; + let gSources, gBreakpoints, gLocation; + + initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { + gTab = aTab; + gDebuggee = aDebuggee; + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gSources = gDebugger.DebuggerView.Sources; + gBreakpoints = gDebugger.DebuggerController.Breakpoints; + + gLocation = { url: gSources.selectedValue, line: 18 }; + + waitForSourceAndCaretAndScopes(gPanel, ".html", 17) + .then(addBreakpoint) + .then(setDummyConditional) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_REMOVED); + toggleBreakpoint(); + return finished; + }) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.BREAKPOINT_ADDED); + toggleBreakpoint(); + return finished; + }) + .then(testConditionalExpressionOnClient) + .then(() => { + let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING); + openConditionalPopup(); + finished.then(() => ok(false, "The popup shouldn't have opened.")); + return waitForTime(1000); + }) + .then(() => resumeDebuggerThenCloseAndFinish(gPanel)) + .then(null, aError => { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + }); + + gDebuggee.ermahgerd(); + }); + + function addBreakpoint() { + return gPanel.addBreakpoint(gLocation); + } + + function setDummyConditional(aClient) { + // This happens when a conditional expression input popup is shown + // but the user doesn't type anything into it. + aClient.conditionalExpression = ""; + } + + function toggleBreakpoint() { + EventUtils.sendMouseEvent({ type: "click" }, + gDebugger.document.querySelector(".dbg-breakpoint-checkbox"), + gDebugger); + } + + function openConditionalPopup() { + EventUtils.sendMouseEvent({ type: "click" }, + gDebugger.document.querySelector(".dbg-breakpoint"), + gDebugger); + } + + function testConditionalExpressionOnClient() { + return gBreakpoints._getAdded(gLocation).then(aClient => { + if ("conditionalExpression" in aClient) { + ok(false, "A conditional expression shouldn't have been set."); + } else { + ok(true, "The conditional expression wasn't set, as expected."); + } + }); + } +} diff --git a/browser/devtools/debugger/test/browser_dbg_on-pause-highlight.js b/browser/devtools/debugger/test/browser_dbg_on-pause-highlight.js index 0ca6b4a52afc..891df807315b 100644 --- a/browser/devtools/debugger/test/browser_dbg_on-pause-highlight.js +++ b/browser/devtools/debugger/test/browser_dbg_on-pause-highlight.js @@ -30,13 +30,15 @@ function testPause() { gDebugger.gThreadClient.addOneTimeListener("paused", () => { gToolbox.selectTool("webconsole").then(() => { - ok(gToolboxTab.classList.contains("highlighted"), + ok(gToolboxTab.hasAttribute("highlighted") && + gToolboxTab.getAttribute("highlighted") == "true", "The highlighted class is present"); ok(!gToolboxTab.hasAttribute("selected") || gToolboxTab.getAttribute("selected") != "true", "The tab is not selected"); }).then(() => gToolbox.selectTool("jsdebugger")).then(() => { - ok(gToolboxTab.classList.contains("highlighted"), + ok(gToolboxTab.hasAttribute("highlighted") && + gToolboxTab.getAttribute("highlighted") == "true", "The highlighted class is present"); ok(gToolboxTab.hasAttribute("selected") && gToolboxTab.getAttribute("selected") == "true", diff --git a/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js b/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js index 24c354fe1f34..a911956f40cc 100644 --- a/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js +++ b/browser/devtools/debugger/test/browser_dbg_on-pause-raise.js @@ -78,13 +78,15 @@ function testPause() { "Debugger's tab got selected."); } gToolbox.selectTool("webconsole").then(() => { - ok(gToolboxTab.classList.contains("highlighted"), + ok(gToolboxTab.hasAttribute("highlighted") && + gToolboxTab.getAttribute("highlighted") == "true", "The highlighted class is present"); ok(!gToolboxTab.hasAttribute("selected") || gToolboxTab.getAttribute("selected") != "true", "The tab is not selected"); }).then(() => gToolbox.selectTool("jsdebugger")).then(() => { - ok(gToolboxTab.classList.contains("highlighted"), + ok(gToolboxTab.hasAttribute("highlighted") && + gToolboxTab.getAttribute("highlighted") == "true", "The highlighted class is present"); ok(gToolboxTab.hasAttribute("selected") && gToolboxTab.getAttribute("selected") == "true", @@ -100,7 +102,8 @@ function testPause() { function testResume() { gDebugger.gThreadClient.addOneTimeListener("resumed", () => { gToolbox.selectTool("webconsole").then(() => { - ok(!gToolboxTab.classList.contains("highlighted"), + ok(!gToolboxTab.hasAttribute("highlighted") || + gToolboxTab.getAttribute("highlighted") != "true", "The highlighted class is not present now after the resume"); ok(!gToolboxTab.hasAttribute("selected") || gToolboxTab.getAttribute("selected") != "true", diff --git a/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js b/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js index 407e701b64b3..b51c155a64c7 100644 --- a/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js +++ b/browser/devtools/debugger/test/browser_dbg_pause-exceptions-01.js @@ -58,7 +58,7 @@ function testPauseOnExceptionsDisabled() { is(innerNodes[0].querySelector(".name").getAttribute("value"), "this", "Should have the right property name for 'this'."); - is(innerNodes[0].querySelector(".value").getAttribute("value"), "HTMLButtonElement", + is(innerNodes[0].querySelector(".value").getAttribute("value"), "