From 7e221ab8017b611dcff6a1cf8a3657a3eb6b90b6 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Tue, 25 Mar 2014 14:16:51 +0000 Subject: [PATCH 01/47] Bug 986529 - invert tab close icons on windows classic, r=jaws --HG-- extra : rebase_source : 92e549df10f4df26cfd1096340c6583d38fd691c --- browser/themes/windows/browser.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 06e2efa728c..9533bbe2fdf 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -178,6 +178,10 @@ z-index: 1; } + #main-window[tabsintitlebar]:not([inFullscreen]) .tab-close-button:not(:-moz-any(:hover,:-moz-lwtheme,[selected="true"])) { + -moz-image-region: rect(0, 64px, 16px, 48px); + } + #main-window[tabsintitlebar][sizemode="normal"] #titlebar-content:-moz-lwtheme { /* Render a window top border: */ background-image: linear-gradient(to bottom, From 2bdb00fc33b987ac29bd91e61fb0be4415fcfe5b Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Sat, 22 Mar 2014 12:56:00 +0000 Subject: [PATCH 02/47] Bug 984979 - Fix back button :active state on Windows 8, r=gijs --HG-- extra : rebase_source : 39a2fa92fab331861e9503fb82a485435d56758a --- browser/themes/windows/browser.css | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 9533bbe2fdf..6c830dc28d5 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -892,6 +892,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { box-shadow: none !important; } +#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon, #back-button[open="true"] > .toolbarbutton-icon { background-color: hsla(210,4%,10%,.12) !important; box-shadow: 0 1px 0 0 hsla(210,80%,20%,.1) inset !important; From 552d918b13e060543835e7cee6d674b95498023d Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Mon, 24 Mar 2014 15:38:13 -0400 Subject: [PATCH 03/47] Bug 977708 - Annotate crash reports with the current experiment, r=felipe --- browser/experiments/Experiments.jsm | 18 +++++++++++++----- browser/experiments/moz.build | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/browser/experiments/Experiments.jsm b/browser/experiments/Experiments.jsm index 7a4f5701ef8..d70749d2c29 100644 --- a/browser/experiments/Experiments.jsm +++ b/browser/experiments/Experiments.jsm @@ -39,6 +39,11 @@ XPCOMUtils.defineLazyGetter(this, "CertUtils", return mod; }); +#ifdef MOZ_CRASHREPORTER +XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter", + "@mozilla.org/xre/app-info;1", + "nsICrashReporter"); +#endif const FILE_CACHE = "experiments.json"; const OBSERVER_TOPIC = "experiments-changed"; @@ -673,11 +678,6 @@ Experiments.Experiments.prototype = { return this._pendingTasks.loadFromCache; } - if (this._pendingTasks.updateManifest) { - // We're already updating the manifest, no need to load the cached version. - return this._pendingTasks.updateManifest; - } - let path = this._cacheFilePath; this._pendingTasks.loadFromCache = Task.spawn(function () { try { @@ -888,6 +888,7 @@ Experiments.Experiments.prototype = { yield activeExperiment.stop(); yield activeExperiment.start(); } catch (e) { + gLogger.error(e); // On failure try the next experiment. activeExperiment = null; } @@ -921,6 +922,7 @@ Experiments.Experiments.prototype = { try { yield experiment.start(); activeChanged = true; + activeExperiment = experiment; break; } catch (e) { // On failure try the next experiment. @@ -933,6 +935,12 @@ Experiments.Experiments.prototype = { Services.obs.notifyObservers(null, OBSERVER_TOPIC, null); } +#ifdef MOZ_CRASHREPORTER + if (activeExperiment) { + gCrashReporter.annotateCrashReport("ActiveExperiment", activeExperiment.id); + } +#endif + throw new Task.Result(activeChanged); }.bind(this)); }, diff --git a/browser/experiments/moz.build b/browser/experiments/moz.build index 383e90fc1b0..3598f8766bf 100644 --- a/browser/experiments/moz.build +++ b/browser/experiments/moz.build @@ -9,7 +9,7 @@ EXTRA_COMPONENTS += [ JS_MODULES_PATH = 'modules/experiments' -EXTRA_JS_MODULES += [ +EXTRA_PP_JS_MODULES += [ 'Experiments.jsm', ] From f4f14fd3f4c4bdd806f32bd0d9b14b9dc4203eb4 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Mon, 24 Mar 2014 14:23:22 -0700 Subject: [PATCH 04/47] Bug 946957 - Disable testSessionOOMRestore. r=gbrown --- mobile/android/base/tests/robocop.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index fe43363c415..b9dc3373dd7 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -91,9 +91,9 @@ skip-if = android_version == "10" || processor == "x86" [testSessionOOMSave] # disabled on x86 and 2.3; bug 945395 skip-if = android_version == "10" || processor == "x86" -[testSessionOOMRestore] +#[testSessionOOMRestore] # see bug 946957 # disabled on Android 2.3; bug 979600 -skip-if = android_version == "10" +#skip-if = android_version == "10" [testSettingsMenuItems] # disabled on Android 2.3; bug 979552 skip-if = android_version == "10" From b587b458fc995e228176bca86b2f5da9c6bee21b Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Thu, 13 Mar 2014 01:43:05 +0100 Subject: [PATCH 05/47] [Australis] Bug 477948: Keyhole back/ forward button for Linux. r=jaws --HG-- extra : rebase_source : ec4a59c66b824a5137d6ca10f154d682c1b0a612 --- browser/base/content/browser.xul | 9 +- browser/themes/linux/browser.css | 251 +++++++++++++++++++++++------ browser/themes/windows/browser.css | 6 +- 3 files changed, 213 insertions(+), 53 deletions(-) diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 3d92f1bcdc8..0a8f7720607 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -1179,15 +1179,14 @@ #include tab-shape.inc.svg -#ifndef XP_UNIX - +#ifndef XP_MACOSX + - + -#endif -#ifdef XP_MACOSX +#else diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 9f916ec755c..eed9da208db 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -16,7 +16,7 @@ %define forwardTransitionLength 150ms %define conditionalForwardWithUrlbar window:not([chromehidden~="toolbar"]) #urlbar-container -%define conditionalForwardWithUrlbarWidth 40 +%define conditionalForwardWithUrlbarWidth 27 #menubar-items { -moz-box-orient: vertical; /* for flex hack */ @@ -662,21 +662,98 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { -moz-margin-start: -4px; } -#forward-button[disabled] { - transform: scale(0); - opacity: 0; - pointer-events: none; +#back-button { + padding-top: 3px; + padding-bottom: 3px; + -moz-padding-start: 5px; + -moz-padding-end: 0; + position: relative; + z-index: 1; + border-radius: 0 10000px 10000px 0; } -@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button { - transition: @forwardTransitionLength@ ease-out; +#back-button:-moz-locale-dir(rtl) { + border-radius: 10000px 0 0 10000px; +} + +#back-button > menupopup { + margin-top: -1px; +} + +#back-button > .toolbarbutton-icon { + border-radius: 10000px; + padding: 5px; + margin-top: -5px; + margin-bottom: -5px; + border: none; + box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset, + 0 0 0 1px hsla(0,0%,100%,.3) inset, + 0 0 0 1px hsla(210,54%,20%,.25), + 0 1px 0 hsla(210,54%,20%,.35); + background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1)); + transition-property: background-color, box-shadow; + transition-duration: 250ms; +} + +#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon { + background-color: hsla(210,48%,96%,.75); + box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset, + 0 0 0 1px hsla(0,0%,100%,.3) inset, + 0 0 0 1px hsla(210,54%,20%,.3), + 0 1px 0 hsla(210,54%,20%,.4), + 0 0 4px hsla(210,54%,20%,.2); +} + +#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon, +#back-button[open="true"] > .toolbarbutton-icon { + background-color: hsla(210,54%,20%,.15); + box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset, + 0 0 1px hsla(210,54%,20%,.2) inset, + 0 0 0 1px hsla(210,54%,20%,.4), + 0 1px 0 hsla(210,54%,20%,.2); + transition: none; +} + +#main-window:not([customizing]) #back-button[disabled] > .toolbarbutton-icon { + box-shadow: 0 0 0 1px hsla(210,54%,20%,.55), + 0 1px 0 hsla(210,54%,20%,.65) !important; + transition: none; } #back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon, -#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-icon { +#forward-button:-moz-locale-dir(rtl) { transform: scaleX(-1); } +@conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] { + opacity: 0; +} + +@conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button { + transition: opacity @forwardTransitionLength@ ease-out; +} + +@conditionalForwardWithUrlbar@ > #forward-button[occluded-by-urlbar] { + visibility: hidden; +} + +#forward-button { + padding: 0; +} + +#forward-button > .toolbarbutton-icon { + background-clip: padding-box; + clip-path: url("chrome://browser/content/browser.xul#keyhole-forward-clip-path"); + margin-left: -7px; + border-left-style: none; + border-radius: 0; + padding-left: 7px; + padding-right: 3px; + padding-top: 2px; + padding-bottom: 2px; + border: 1px solid #9a9a9a; +} + /* tabview menu item */ #menu_tabview { @@ -785,9 +862,21 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { } /* Location bar */ +#urlbar, +.searchbar-textbox { + -moz-appearance: none; + padding: 1px; + border: 1px solid ThreeDShadow; + border-radius: 2px; +} + +#urlbar[focused], +.searchbar-textbox[focused] { + border-color: Highlight; +} + #urlbar { - -moz-appearance: textfield; - padding: 0; + background-color: -moz-field; } .urlbar-textbox-container { @@ -809,26 +898,56 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { } @conditionalForwardWithUrlbar@ > #urlbar-wrapper { - -moz-padding-start: @conditionalForwardWithUrlbarWidth@px; + padding-left: @conditionalForwardWithUrlbarWidth@px; -moz-margin-start: -@conditionalForwardWithUrlbarWidth@px; position: relative; pointer-events: none; } @conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar { + -moz-border-start: none; + margin-left: 0; pointer-events: all; } @conditionalForwardWithUrlbar@:not([switchingtabs]) > #urlbar-wrapper > #urlbar { - transition: margin-left @forwardTransitionLength@ ease-out, - margin-right @forwardTransitionLength@ ease-out; + transition: margin-left @forwardTransitionLength@ ease-out; } -@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) { +@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(ltr) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper { + clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path"); +} + +@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar { margin-left: -@conditionalForwardWithUrlbarWidth@px; } -@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) { - margin-right: -@conditionalForwardWithUrlbarWidth@px; + +@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar { + /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */ + transition-delay: 100s; +} + +@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar, +@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar { + /* when switching tabs, or when not hovered anymore, trigger a new transition + * to hide the forward button immediately */ + margin-left: -@conditionalForwardWithUrlbarWidth@.01px; +} + +@conditionalForwardWithUrlbar@ > #urlbar-wrapper:-moz-locale-dir(rtl), +@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar:-moz-locale-dir(rtl) { + /* let windows-urlbar-back-button-mask clip the urlbar's right side for RTL */ + transform: scaleX(-1); } #urlbar-icons { @@ -868,41 +987,66 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { min-width: calc(54px + 11ch); } -%include ../shared/identity-block.inc.css +/* identity box */ -#page-proxy-favicon { - margin-top: 2px; - margin-bottom: 2px; - -moz-margin-start: 4px; - -moz-margin-end: 3px; - -moz-image-region: rect(0, 16px, 16px, 0); -} - -#identity-box:hover > #page-proxy-favicon { - -moz-image-region: rect(0, 32px, 16px, 16px); -} - -#identity-box:hover:active > #page-proxy-favicon, -#identity-box[open=true] > #page-proxy-favicon { - -moz-image-region: rect(0, 48px, 16px, 32px); -} - -/* Identity indicator */ #identity-box { padding: 1px; - margin: -1px; - -moz-margin-end: 0; font-size: .9em; } #identity-box:-moz-locale-dir(ltr) { - border-top-left-radius: 2.5px; - border-bottom-left-radius: 2.5px; + border-top-left-radius: 1.5px; + border-bottom-left-radius: 1.5px; } #identity-box:-moz-locale-dir(rtl) { - border-top-right-radius: 2.5px; - border-bottom-right-radius: 2.5px; + border-top-right-radius: 1.5px; + border-bottom-right-radius: 1.5px; +} + +#notification-popup-box:not([hidden]) + #identity-box { + -moz-padding-start: 10px; + border-radius: 0; +} + +@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box { + border-radius: 0; +} + +@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) { + padding-left: 5px; + transition: padding-left; +} + +@conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) { + padding-right: 5px; + transition: padding-right; +} + +@conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box { + /* forward button hiding is delayed when hovered */ + transition-delay: 100s; +} + +@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr), +@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) { + /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */ + padding-left: 5.01px; +} + +@conditionalForwardWithUrlbar@[forwarddisabled][switchingtabs] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl), +@conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) > #urlbar-wrapper > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) { + /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */ + padding-right: 5.01px; +} + +#urlbar[pageproxystate="valid"] > #identity-box.chromeUI, +#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity { + -moz-margin-end: 4px; +} + +#identity-box.verifiedIdentity:not(:-moz-lwtheme) { + background-color: #fff; } #identity-box:-moz-focusring { @@ -915,10 +1059,27 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { -moz-padding-end: 5px; } -#urlbar[pageproxystate="valid"] > #identity-box.chromeUI, -#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity { - background-color: #fff; - -moz-margin-end: 4px; +%include ../shared/identity-block.inc.css + +#page-proxy-favicon { + margin-top: 1px; + margin-bottom: 1px; + -moz-margin-start: 3px; + -moz-margin-end: 2px; + -moz-image-region: rect(0, 16px, 16px, 0); +} + +@conditionalForwardWithUrlbar@ > #urlbar-wrapper > #urlbar > #identity-box > #page-proxy-favicon { + -moz-margin-end: 1px; +} + +#identity-box:hover > #page-proxy-favicon { + -moz-image-region: rect(0, 32px, 16px, 16px); +} + +#identity-box:hover:active > #page-proxy-favicon, +#identity-box[open=true] > #page-proxy-favicon { + -moz-image-region: rect(0, 48px, 16px, 32px); } /* Identity popup icons */ diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 6c830dc28d5..acd83932a89 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -774,7 +774,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4); box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset, 0 0 1px hsla(210,54%,20%,.2) inset, - /* allows windows-keyhole-forward-clip-path to be used for non-hover as well as hover: */ + /* allows keyhole-forward-clip-path to be used for non-hover as well as hover: */ 0 1px 0 hsla(210,54%,20%,0), 0 0 2px hsla(210,54%,20%,0); text-shadow: none; @@ -828,7 +828,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { #forward-button > .toolbarbutton-icon { background-clip: padding-box !important; /*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */ - clip-path: url(chrome://browser/content/browser.xul#windows-keyhole-forward-clip-path) !important; + clip-path: url(chrome://browser/content/browser.xul#keyhole-forward-clip-path) !important; margin-left: -6px !important; border-left-style: none !important; border-radius: 0 !important; @@ -1131,7 +1131,7 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { } @conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper { - clip-path: url("chrome://browser/content/browser.xul#windows-urlbar-back-button-clip-path"); + clip-path: url("chrome://browser/content/browser.xul#urlbar-back-button-clip-path"); } @conditionalForwardWithUrlbar@[forwarddisabled] > #urlbar-wrapper > #urlbar { From 7354369863be7b02230f7f19ebcfa9204200ab74 Mon Sep 17 00:00:00 2001 From: Chris Kitching Date: Mon, 24 Mar 2014 21:29:09 +0000 Subject: [PATCH 06/47] Bug 987340: Prevent favicon decoder choking on corrupt non-ICO bitmaps with valid magic numbers. r=rnewman --- mobile/android/base/favicons/decoders/FaviconDecoder.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/favicons/decoders/FaviconDecoder.java b/mobile/android/base/favicons/decoders/FaviconDecoder.java index 38b38693457..6842f04d299 100644 --- a/mobile/android/base/favicons/decoders/FaviconDecoder.java +++ b/mobile/android/base/favicons/decoders/FaviconDecoder.java @@ -93,9 +93,15 @@ public class FaviconDecoder { result.length = length; result.isICO = false; + Bitmap decodedImage = BitmapUtils.decodeByteArray(buffer, offset, length); + if (decodedImage == null) { + // What we got wasn't decodable after all. Probably corrupted image, or we got a muffled OOM. + return null; + } + // We assume here that decodeByteArray doesn't hold on to the entire supplied // buffer -- worst case, each of our buffers will be twice the necessary size. - result.bitmapsDecoded = new SingleBitmapIterator(BitmapUtils.decodeByteArray(buffer, offset, length)); + result.bitmapsDecoded = new SingleBitmapIterator(decodedImage); result.faviconBytes = buffer; return result; From ba35c67c92153dbe018918d915d6060748e29b0e Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Mon, 24 Mar 2014 14:30:09 -0700 Subject: [PATCH 07/47] Bug 987368: DebuggerView doesn't destroy its editor properly. r=fitzgen --- browser/devtools/debugger/debugger-view.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/devtools/debugger/debugger-view.js b/browser/devtools/debugger/debugger-view.js index dc0987be27e..14af8f0a0df 100644 --- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -273,6 +273,8 @@ let DebuggerView = { DebuggerController.Breakpoints.destroy().then(() => { window.emit(EVENTS.EDITOR_UNLOADED, this.editor); + this.editor.destroy(); + this.editor = null; aCallback(); }); }, From b6d95b9ffab5f2ab5dcea0876f73d2f640b71a93 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Mon, 24 Mar 2014 14:40:54 -0700 Subject: [PATCH 08/47] Bug 987294 - Unsafe access to mTabsChangedListeners in Tabs. r=bnicholson --- mobile/android/base/BrowserApp.java | 13 ++++------- .../android/base/FilePickerResultHandler.java | 11 +++------ mobile/android/base/Tabs.java | 23 ++++++++----------- mobile/android/base/home/TwoLinePageRow.java | 11 +++------ 4 files changed, 19 insertions(+), 39 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 48a7a88f6a7..b986dc8d36e 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -225,15 +225,10 @@ abstract public class BrowserApp extends GeckoApp final TabsPanel.Panel panel = tab.isPrivate() ? TabsPanel.Panel.PRIVATE_TABS : TabsPanel.Panel.NORMAL_TABS; - // Delay calling showTabs so that it does not modify the mTabsChangedListeners - // array while we are still iterating through the array. - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - if (areTabsShown() && mTabsPanel.getCurrentPanel() != panel) - showTabs(panel); - } - }); + + if (areTabsShown() && mTabsPanel.getCurrentPanel() != panel) { + showTabs(panel); + } } break; case START: diff --git a/mobile/android/base/FilePickerResultHandler.java b/mobile/android/base/FilePickerResultHandler.java index 2059e666c31..90485d44819 100644 --- a/mobile/android/base/FilePickerResultHandler.java +++ b/mobile/android/base/FilePickerResultHandler.java @@ -244,14 +244,9 @@ class FilePickerResultHandler implements ActivityResultHandler { } }); - // We're already on the UIThread, but we have to post this back to the uithread to avoid - // modifying the listener array while its being iterated through. - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Tabs.unregisterOnTabsChangedListener(FileLoaderCallbacks.this); - } - }); + // Tabs' listener array is safe to modify during use: its + // iteration pattern is based on snapshots. + Tabs.unregisterOnTabsChangedListener(this); } } } diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 7f6c3cdb4a0..ae12acab01e 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -5,8 +5,6 @@ package org.mozilla.gecko; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -528,15 +526,14 @@ public class Tabs implements GeckoEventListener { public void onTabChanged(Tab tab, TabEvents msg, Object data); } - private static List mTabsChangedListeners = - Collections.synchronizedList(new ArrayList()); + private static final List TABS_CHANGED_LISTENERS = new CopyOnWriteArrayList(); public static void registerOnTabsChangedListener(OnTabsChangedListener listener) { - mTabsChangedListeners.add(listener); + TABS_CHANGED_LISTENERS.add(listener); } public static void unregisterOnTabsChangedListener(OnTabsChangedListener listener) { - mTabsChangedListeners.remove(listener); + TABS_CHANGED_LISTENERS.remove(listener); } public enum TabEvents { @@ -578,15 +575,13 @@ public class Tabs implements GeckoEventListener { public void run() { onTabChanged(tab, msg, data); - synchronized (mTabsChangedListeners) { - if (mTabsChangedListeners.isEmpty()) { - return; - } + if (TABS_CHANGED_LISTENERS.isEmpty()) { + return; + } - Iterator items = mTabsChangedListeners.iterator(); - while (items.hasNext()) { - items.next().onTabChanged(tab, msg, data); - } + Iterator items = TABS_CHANGED_LISTENERS.iterator(); + while (items.hasNext()) { + items.next().onTabChanged(tab, msg, data); } } }); diff --git a/mobile/android/base/home/TwoLinePageRow.java b/mobile/android/base/home/TwoLinePageRow.java index 73503d22d0a..ea32c093a46 100644 --- a/mobile/android/base/home/TwoLinePageRow.java +++ b/mobile/android/base/home/TwoLinePageRow.java @@ -102,14 +102,9 @@ public class TwoLinePageRow extends LinearLayout @Override protected void onDetachedFromWindow() { - // Delay removing the listener to avoid modifying mTabsChangedListeners - // while notifyListeners is iterating through the array. - ThreadUtils.postToUiThread(new Runnable() { - @Override - public void run() { - Tabs.unregisterOnTabsChangedListener(TwoLinePageRow.this); - } - }); + // Tabs' listener array is safe to modify during use: its + // iteration pattern is based on snapshots. + Tabs.unregisterOnTabsChangedListener(this); } @Override From 100d82349e54490159ee7e648e3fe4572a401edb Mon Sep 17 00:00:00 2001 From: "Francesco Lodolo (:flod)" Date: Mon, 24 Mar 2014 17:46:13 -0400 Subject: [PATCH 09/47] Bug 901488 - Spelling "addon(s)" in syncSetup.properties is inconsistent with existing strings. r=gps --- browser/locales/en-US/chrome/browser/syncSetup.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/locales/en-US/chrome/browser/syncSetup.properties b/browser/locales/en-US/chrome/browser/syncSetup.properties index 17e679f9267..d17eb293e62 100644 --- a/browser/locales/en-US/chrome/browser/syncSetup.properties +++ b/browser/locales/en-US/chrome/browser/syncSetup.properties @@ -35,7 +35,7 @@ passwordsCount.label = #1 password;#1 passwords # LOCALIZATION NOTE (addonsCount.label): Semicolon-separated list of plural forms. # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals # #1 is the number of add-ons, see the link above for forms -addonsCount.label = #1 addon;#1 addons +addonsCount.label = #1 add-on;#1 add-ons save.recoverykey.title = Save Recovery Key save.recoverykey.defaultfilename = Firefox Recovery Key.html From 51e1cbcb4621d5d3b63d72be8911a34dafe76e6a Mon Sep 17 00:00:00 2001 From: Brian Grinstead Date: Mon, 24 Mar 2014 10:25:33 -0500 Subject: [PATCH 10/47] Bug 962931 - Request longer timeout for browser_webconsole_split.js. r=msucan --- browser/devtools/webconsole/test/browser_webconsole_split.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/devtools/webconsole/test/browser_webconsole_split.js b/browser/devtools/webconsole/test/browser_webconsole_split.js index f6659e6bc9a..0b5cf17f63d 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_split.js +++ b/browser/devtools/webconsole/test/browser_webconsole_split.js @@ -5,6 +5,9 @@ function test() { + // Test is slow on Linux EC2 instances - Bug 962931 + requestLongerTimeout(2); + let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); let {Task} = Cu.import("resource://gre/modules/Task.jsm", {}); let Toolbox = devtools.Toolbox; From 7d0aa1599f9f0feb8e640884de741788252281a2 Mon Sep 17 00:00:00 2001 From: Mihaela Velimiroviciu Date: Fri, 14 Mar 2014 09:01:17 +0200 Subject: [PATCH 11/47] Bug 982140 - Add automated test for removing properties from the manifest. r=jryans --- .../test/browser_manifest_editor.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/browser/devtools/app-manager/test/browser_manifest_editor.js b/browser/devtools/app-manager/test/browser_manifest_editor.js index a02fc6d54eb..434b5ab7d35 100644 --- a/browser/devtools/app-manager/test/browser_manifest_editor.js +++ b/browser/devtools/app-manager/test/browser_manifest_editor.js @@ -24,6 +24,7 @@ function test() { yield changeManifestValueBad("name", "the worst app"); yield addNewManifestProperty("developer", "foo", "bar"); yield addNewManifestPropertyBad("developer", "blob", "bob"); + yield removeManifestProperty("developer", "foo"); gManifestWindow = null; gManifestEditor = null; @@ -162,3 +163,26 @@ function addNewManifestPropertyBad(parent, key, value) { "Manifest contains key, but it should not"); }); } + +function removeManifestProperty(parent, key) { + info("*** Remove property test ***"); + + return Task.spawn(function() { + let parentElem = gManifestWindow.document + .querySelector("[id ^= '" + parent + "']"); + ok(parentElem, "Found parent element"); + + let keyExists = key in gManifestEditor.manifest[parent]; + ok(keyExists, + "The manifest contains the key under the expected parent"); + + let newElem = gManifestWindow.document.querySelector("[id ^= '" + key + "']"); + let removePropertyButton = newElem.querySelector(".variables-view-delete"); + ok(removePropertyButton, "The remove property button was found"); + removePropertyButton.click(); + + yield waitForUpdate(); + + ok(!(key in gManifestEditor.manifest[parent]), "Property was successfully removed"); + }); +} From cfb8e0372c23741fa59b0aea39fb6e1cf2a8e3df Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Mon, 24 Mar 2014 15:35:44 -0700 Subject: [PATCH 12/47] Bug 911098 Implement addon debugger UI, r=mossop,fitzgen,harthur --- .../devtools/debugger/debugger-controller.js | 36 +++++- browser/devtools/debugger/debugger-panes.js | 17 ++- browser/devtools/debugger/test/browser.ini | 1 + .../test/browser_dbg_addon-sources.js | 107 +++++++++++++++++ browser/devtools/debugger/test/head.js | 29 +++++ browser/devtools/framework/ToolboxProcess.jsm | 31 ++++- .../framework/toolbox-process-window.js | 18 ++- modules/libpref/src/init/all.js | 3 + .../chrome/mozapps/extensions/extensions.dtd | 1 + .../mozapps/extensions/content/extensions.js | 55 +++++++-- .../mozapps/extensions/content/extensions.xml | 31 ++++- .../mozapps/extensions/content/extensions.xul | 6 + .../extensions/internal/XPIProvider.jsm | 21 ++++ .../extensions/internal/XPIProviderUtils.js | 2 +- .../test/addons/test_jetpack/bootstrap.js | 17 +++ .../addons/test_jetpack/harness-options.json | 1 + .../test/addons/test_jetpack/install.rdf | 28 +++++ .../test/browser/browser-common.ini | 1 + .../test/browser/browser_debug_button.js | 112 ++++++++++++++++++ .../mozapps/extensions/test/browser/head.js | 3 + .../test/xpcshell/test_isDebuggable.js | 36 ++++++ .../test/xpcshell/xpcshell-shared.ini | 1 + 22 files changed, 524 insertions(+), 33 deletions(-) create mode 100644 browser/devtools/debugger/test/browser_dbg_addon-sources.js create mode 100644 toolkit/mozapps/extensions/test/addons/test_jetpack/bootstrap.js create mode 100644 toolkit/mozapps/extensions/test/addons/test_jetpack/harness-options.json create mode 100644 toolkit/mozapps/extensions/test/addons/test_jetpack/install.rdf create mode 100644 toolkit/mozapps/extensions/test/browser/browser_debug_button.js create mode 100644 toolkit/mozapps/extensions/test/xpcshell/test_isDebuggable.js diff --git a/browser/devtools/debugger/debugger-controller.js b/browser/devtools/debugger/debugger-controller.js index 0a111f013bb..758410db198 100644 --- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -191,13 +191,15 @@ let DebuggerController = { this._connection = startedDebugging.promise; let target = this._target; - let { client, form: { chromeDebugger, traceActor } } = target; + let { client, form: { chromeDebugger, traceActor, addonActor } } = target; target.on("close", this._onTabDetached); target.on("navigate", this._onTabNavigated); target.on("will-navigate", this._onTabNavigated); this.client = client; - if (target.chrome) { + if (addonActor) { + this._startAddonDebugging(addonActor, startedDebugging.resolve); + } else if (target.chrome) { this._startChromeDebugging(chromeDebugger, startedDebugging.resolve); } else { this._startDebuggingTab(startedDebugging.resolve); @@ -309,6 +311,20 @@ let DebuggerController = { }); }, + /** + * Sets up an addon debugging session. + * + * @param object aAddonActor + * The actor for the addon that is being debugged. + * @param function aCallback + * A function to invoke once the client attaches to the active thread. + */ + _startAddonDebugging: function(aAddonActor, aCallback) { + this.client.attachAddon(aAddonActor, (aResponse) => { + return this._startChromeDebugging(aResponse.threadActor, aCallback); + }); + }, + /** * Sets up a chrome debugging session. * @@ -2159,6 +2175,22 @@ Object.defineProperties(window, { } }); +/** + * Helper method for parsing a resource URI, like + * `resource://gre/modules/commonjs/sdk/tabs.js`, and pulling out `sdk/tabs.js` + * if it's in the SDK, or `null` otherwise. + * + * @param string url + * @return string|null + */ +function getSDKModuleName(url) { + let match = (url || "").match(/^resource:\/\/gre\/modules\/commonjs\/(.*)/); + if (match) { + return match[1]; + } + return null; +} + /** * Helper method for debugging. * @param string diff --git a/browser/devtools/debugger/debugger-panes.js b/browser/devtools/debugger/debugger-panes.js index 6c5b36d4765..52334fbeccd 100644 --- a/browser/devtools/debugger/debugger-panes.js +++ b/browser/devtools/debugger/debugger-panes.js @@ -126,10 +126,17 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, { * - staged: true to stage the item to be appended later */ addSource: function(aSource, aOptions = {}) { - let url = aSource.url; - let label = SourceUtils.getSourceLabel(url.split(" -> ").pop()); - let group = SourceUtils.getSourceGroup(url.split(" -> ").pop()); - let unicodeUrl = NetworkHelper.convertToUnicode(unescape(url)); + let fullUrl = aSource.url; + let url = fullUrl.split(" -> ").pop(); + let label = SourceUtils.getSourceLabel(url); + let group = SourceUtils.getSourceGroup(url); + let unicodeUrl = NetworkHelper.convertToUnicode(unescape(fullUrl)); + + let sdkModuleName = getSDKModuleName(url); + if (sdkModuleName) { + label = sdkModuleName; + group = "Add-on SDK"; + } let contents = document.createElement("label"); contents.className = "plain dbg-source-item"; @@ -139,7 +146,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, { contents.setAttribute("tooltiptext", unicodeUrl); // Append a source item to this container. - this.push([contents, url], { + this.push([contents, fullUrl], { staged: aOptions.staged, /* stage the item to be appended later? */ attachment: { label: label, diff --git a/browser/devtools/debugger/test/browser.ini b/browser/devtools/debugger/test/browser.ini index 42813a7932a..28094b40cfc 100644 --- a/browser/devtools/debugger/test/browser.ini +++ b/browser/devtools/debugger/test/browser.ini @@ -82,6 +82,7 @@ support-files = [browser_dbg_aaa_run_first_leaktest.js] [browser_dbg_addonactor.js] +[browser_dbg_addon-sources.js] [browser_dbg_auto-pretty-print-01.js] [browser_dbg_auto-pretty-print-02.js] [browser_dbg_bfcache.js] diff --git a/browser/devtools/debugger/test/browser_dbg_addon-sources.js b/browser/devtools/debugger/test/browser_dbg_addon-sources.js new file mode 100644 index 00000000000..8e6e9a4f732 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_addon-sources.js @@ -0,0 +1,107 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure that the sources listed when debugging an addon are either from the +// addon itself, or the SDK, with proper groups and labels. + +const ADDON3_URL = EXAMPLE_URL + "addon3.xpi"; + +let gAddon, gClient, gThreadClient, gDebugger, gSources; + +function test() { + Task.spawn(function () { + if (!DebuggerServer.initialized) { + DebuggerServer.init(() => true); + DebuggerServer.addBrowserActors(); + } + + gBrowser.selectedTab = gBrowser.addTab(); + let iframe = document.createElement("iframe"); + document.documentElement.appendChild(iframe); + + let transport = DebuggerServer.connectPipe(); + gClient = new DebuggerClient(transport); + + let connected = promise.defer(); + gClient.connect(connected.resolve); + yield connected.promise; + + yield installAddon(); + let debuggerPanel = yield initAddonDebugger(gClient, ADDON3_URL, iframe); + gDebugger = debuggerPanel.panelWin; + gThreadClient = gDebugger.gThreadClient; + gSources = gDebugger.DebuggerView.Sources; + + yield testSources(); + yield uninstallAddon(); + yield closeConnection(); + iframe.remove(); + finish(); + }); +} + +function installAddon () { + return addAddon(ADDON3_URL).then(aAddon => { + gAddon = aAddon; + }); +} + +function testSources() { + let deferred = promise.defer(); + let foundAddonModule = false; + let foundSDKModule = 0; + + gThreadClient.getSources(({sources}) => { + ok(sources.length, "retrieved sources"); + + sources.forEach(source => { + let url = source.url.split(" -> ").pop(); + info(source.url + "\n\n\n" + url); + let { label, group } = gSources.getItemByValue(source.url).attachment; + + if (url.indexOf("resource://gre/modules/commonjs/sdk") === 0) { + is(label.indexOf("sdk/"), 0, "correct truncated label"); + is(group, "Add-on SDK", "correct SDK group"); + foundSDKModule++; + } else if (url.indexOf("resource://gre/modules/commonjs/method") === 0) { + is(label.indexOf("method/"), 0, "correct truncated label"); + is(group, "Add-on SDK", "correct SDK group"); + foundSDKModule++; + } else if (url.indexOf("resource://jid1-ami3akps3baaeg-at-jetpack") === 0) { + is(label, "main.js", "correct label for addon code"); + is(group, "resource://jid1-ami3akps3baaeg-at-jetpack", "addon code is in its own group"); + foundAddonModule = true; + } else { + throw new Error("Found source outside of the SDK or addon"); + } + }); + + ok(foundAddonModule, "found code for the addon in the list"); + // Be flexible in this number, as SDK changes could change the exact number of + // built-in browser SDK modules + ok(foundSDKModule > 10, "SDK modules are listed"); + + deferred.resolve(); + }); + + return deferred.promise; +} + +function uninstallAddon() { + return removeAddon(gAddon); +} + +function closeConnection () { + let deferred = promise.defer(); + gClient.close(deferred.resolve); + return deferred.promise; +} + +registerCleanupFunction(function() { + gClient = null; + gAddon = null; + gDebugger = null; + gSources = null; + while (gBrowser.tabs.length > 1) + gBrowser.removeCurrentTab(); +}); diff --git a/browser/devtools/debugger/test/head.js b/browser/devtools/debugger/test/head.js index 2472a024b73..1258ff9dd75 100644 --- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -164,6 +164,7 @@ function getAddonActorForUrl(aClient, aUrl) { aClient.listAddons(aResponse => { let addonActor = aResponse.addons.filter(aGrip => aGrip.url == aUrl).pop(); + info("got addon actor for URL: " + addonActor.actor); deferred.resolve(addonActor); }); @@ -500,6 +501,34 @@ function initDebugger(aTarget, aWindow) { }); } +function initAddonDebugger(aClient, aUrl, aFrame) { + info("Initializing an addon debugger panel."); + + return getAddonActorForUrl(aClient, aUrl).then(({actor}) => { + let targetOptions = { + form: { addonActor: actor }, + client: aClient, + chrome: true + }; + + let toolboxOptions = { + customIframe: aFrame + }; + + let target = devtools.TargetFactory.forTab(targetOptions); + return gDevTools.showToolbox(target, "jsdebugger", devtools.Toolbox.HostType.CUSTOM, toolboxOptions); + }).then(aToolbox => { + info("Addon debugger panel shown successfully."); + + let debuggerPanel = aToolbox.getCurrentPanel(); + + // Wait for the initial resume... + return waitForClientEvents(debuggerPanel, "resumed") + .then(() => prepareDebugger(debuggerPanel)) + .then(() => debuggerPanel); + }); +} + function initChromeDebugger(aOnClose) { info("Initializing a chrome debugger process."); diff --git a/browser/devtools/framework/ToolboxProcess.jsm b/browser/devtools/framework/ToolboxProcess.jsm index 7bebce25e08..5492a45b77a 100644 --- a/browser/devtools/framework/ToolboxProcess.jsm +++ b/browser/devtools/framework/ToolboxProcess.jsm @@ -26,10 +26,22 @@ this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"]; * A function called when the process stops running. * @param function aOnRun [optional] * A function called when the process starts running. + * @param object aOptions [optional] + * An object with properties for configuring BrowserToolboxProcess. */ -this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun) { - this._closeCallback = aOnClose; - this._runCallback = aOnRun; +this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun, aOptions) { + // If first argument is an object, use those properties instead of + // all three arguments + if (typeof aOnClose === "object") { + this._closeCallback = aOnClose.onClose; + this._runCallback = aOnClose.onRun; + this._options = aOnClose; + } else { + this._closeCallback = aOnClose; + this._runCallback = aOnRun; + this._options = aOptions || {}; + } + this._telemetry = new Telemetry(); this.close = this.close.bind(this); @@ -43,8 +55,8 @@ this.BrowserToolboxProcess = function BrowserToolboxProcess(aOnClose, aOnRun) { * Initializes and starts a chrome toolbox process. * @return object */ -BrowserToolboxProcess.init = function(aOnClose, aOnRun) { - return new BrowserToolboxProcess(aOnClose, aOnRun); +BrowserToolboxProcess.init = function(aOnClose, aOnRun, aOptions) { + return new BrowserToolboxProcess(aOnClose, aOnRun, aOptions); }; BrowserToolboxProcess.prototype = { @@ -143,8 +155,15 @@ BrowserToolboxProcess.prototype = { let process = this._dbgProcess = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess); process.init(Services.dirsvc.get("XREExeF", Ci.nsIFile)); + let xulURI = DBG_XUL; + + if (this._options.addonID) { + xulURI += "?addonID=" + this._options.addonID; + } + dumpn("Running chrome debugging process."); - let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", DBG_XUL]; + let args = ["-no-remote", "-foreground", "-P", this._dbgProfile.name, "-chrome", xulURI]; + process.runwAsync(args, args.length, { observe: () => this.close() }); this._telemetry.toolOpened("jsbrowserdebugger"); diff --git a/browser/devtools/framework/toolbox-process-window.js b/browser/devtools/framework/toolbox-process-window.js index f6d710b85ce..dd158846717 100644 --- a/browser/devtools/framework/toolbox-process-window.js +++ b/browser/devtools/framework/toolbox-process-window.js @@ -32,7 +32,16 @@ function connect() { ); gClient = new DebuggerClient(transport); gClient.connect(() => { - gClient.listTabs(openToolbox); + let addonID = getParameterByName("addonID"); + + if (addonID) { + gClient.listAddons(({addons}) => { + let addonActor = addons.filter(addon => addon.id === addonID).pop(); + openToolbox({ addonActor: addonActor.actor }); + }); + } else { + gClient.listTabs(openToolbox); + } }); } @@ -106,3 +115,10 @@ function quitApp() { Services.startup.quit(Ci.nsIAppStartup.eForceQuit); } } + +function getParameterByName (name) { + let name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); + let regex = new RegExp("[\\?&]" + name + "=([^&#]*)"); + let results = regex.exec(window.location.search); + return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); +} diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 2d350cd40bf..256a17f2d39 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -565,6 +565,9 @@ pref("devtools.debugger.prompt-connection", true); // Block tools from seeing / interacting with certified apps pref("devtools.debugger.forbid-certified-apps", true); +// Disable add-on debugging +pref("devtools.debugger.addon-enabled", false); + // DevTools default color unit pref("devtools.defaultColorUnit", "hex"); diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd index f2ae1274e0c..bbbd7bded82 100644 --- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd @@ -101,6 +101,7 @@ + diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index aa2139f3f32..b04767400b5 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -16,7 +16,10 @@ Cu.import("resource://gre/modules/PluralForm.jsm"); Cu.import("resource://gre/modules/DownloadUtils.jsm"); Cu.import("resource://gre/modules/AddonManager.jsm"); Cu.import("resource://gre/modules/addons/AddonRepository.jsm"); - +XPCOMUtils.defineLazyGetter(this, "BrowserToolboxProcess", function () { + return Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {}). + BrowserToolboxProcess; +}); const PREF_DISCOVERURL = "extensions.webservice.discoverURL"; const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane"; @@ -26,6 +29,8 @@ const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled"; const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled"; const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden"; const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory"; +const PREF_ADDON_DEBUGGING_ENABLED = "devtools.debugger.addon-enabled"; +const PREF_REMOTE_DEBUGGING_ENABLED = "devtools.debugger.remote-enabled"; const LOADING_MSG_DELAY = 100; @@ -143,6 +148,9 @@ function initialize(event) { } gViewController.loadInitialView(view); + + Services.prefs.addObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged, false); + Services.prefs.addObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged, false); } function notifyInitialized() { @@ -163,6 +171,8 @@ function shutdown() { gEventManager.shutdown(); gViewController.shutdown(); Services.obs.removeObserver(sendEMPong, "EM-ping"); + Services.prefs.removeObserver(PREF_ADDON_DEBUGGING_ENABLED, debuggingPrefChanged); + Services.prefs.removeObserver(PREF_REMOTE_DEBUGGING_ENABLED, debuggingPrefChanged); } function sendEMPong(aSubject, aTopic, aData) { @@ -372,7 +382,7 @@ var gEventManager = { contextMenu.addEventListener("popupshowing", function contextMenu_onPopupshowing() { var addon = gViewController.currentViewObj.getSelectedAddon(); contextMenu.setAttribute("addontype", addon.type); - + var menuSep = document.getElementById("addonitem-menuseparator"); var countEnabledMenuCmds = 0; for (let child of contextMenu.children) { @@ -381,10 +391,10 @@ var gEventManager = { countEnabledMenuCmds++; } } - + // with only one menu item, we hide the menu separator menuSep.hidden = (countEnabledMenuCmds <= 1); - + }, false); }, @@ -460,14 +470,14 @@ var gEventManager = { } } }, - + refreshGlobalWarning: function gEM_refreshGlobalWarning() { var page = document.getElementById("addons-page"); if (Services.appinfo.inSafeMode) { page.setAttribute("warning", "safemode"); return; - } + } if (AddonManager.checkUpdateSecurityDefault && !AddonManager.checkUpdateSecurity) { @@ -945,6 +955,20 @@ var gViewController = { } }, + cmd_debugItem: { + doCommand: function cmd_debugItem_doCommand(aAddon) { + BrowserToolboxProcess.init({ addonID: aAddon.id }); + }, + + isEnabled: function cmd_debugItem_isEnabled(aAddon) { + let debuggerEnabled = Services.prefs. + getBoolPref(PREF_ADDON_DEBUGGING_ENABLED); + let remoteEnabled = Services.prefs. + getBoolPref(PREF_REMOTE_DEBUGGING_ENABLED); + return aAddon && aAddon.isDebuggable && debuggerEnabled && remoteEnabled; + } + }, + cmd_showItemPreferences: { isEnabled: function cmd_showItemPreferences_isEnabled(aAddon) { if (!aAddon || !aAddon.isActive || !aAddon.optionsURL) @@ -1282,7 +1306,7 @@ function openOptionsInTab(optionsURL) { .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem) .rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); + .getInterface(Ci.nsIDOMWindow); if ("switchToTabHavingURI" in mainWindow) { mainWindow.switchToTabHavingURI(optionsURL, true); return true; @@ -1979,7 +2003,7 @@ var gDiscoverView = { this._loadURL(this.homepageURL.spec, aIsRefresh, gViewController.notifyViewChanged.bind(gViewController)); }, - + canRefresh: function gDiscoverView_canRefresh() { if (this._browser.currentURI && this._browser.currentURI.spec == this._browser.homePage) @@ -2279,7 +2303,7 @@ var gSearchView = { } }); }, - + showLoading: function gSearchView_showLoading(aLoading) { this._loading.hidden = !aLoading; this._listBox.hidden = aLoading; @@ -2620,7 +2644,7 @@ var gDetailView = { self._addon.applyBackgroundUpdates = self._autoUpdate.value; }, true); }, - + shutdown: function gDetailView_shutdown() { AddonManager.removeManagerListener(this); }, @@ -2791,7 +2815,7 @@ var gDetailView = { document.getElementById("detail-prefs-btn").hidden = !aIsRemote && !gViewController.commands.cmd_showItemPreferences.isEnabled(aAddon); - + var gridRows = document.querySelectorAll("#detail-grid rows row"); let first = true; for (let gridRow of gridRows) { @@ -3108,7 +3132,7 @@ var gDetailView = { if (firstRow) { let top = firstRow.boxObject.y; top -= parseInt(window.getComputedStyle(firstRow, null).getPropertyValue("margin-top")); - + let detailViewBoxObject = gDetailView.node.boxObject; top -= detailViewBoxObject.y; @@ -3348,7 +3372,7 @@ var gUpdatesView = { notifyInitialized(); }); }, - + maybeDisableUpdateSelected: function gUpdatesView_maybeDisableUpdateSelected() { for (let item of this._listBox.childNodes) { if (item.includeUpdate) { @@ -3411,6 +3435,11 @@ var gUpdatesView = { } }; +function debuggingPrefChanged() { + gViewController.updateState(); + gViewController.updateCommands(); + gViewController.notifyViewChanged(); +} var gDragDrop = { onDragOver: function gDragDrop_onDragOver(aEvent) { diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index 92871ec5c70..d41bcbae3a3 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -196,7 +196,7 @@ this._status.value = val; ]]> - + + + + @@ -1003,6 +1008,10 @@ document.getAnonymousElementByAttribute(this, "anonid", "enable-btn"); + + document.getAnonymousElementByAttribute(this, "anonid", + "debug-btn"); + document.getAnonymousElementByAttribute(this, "anonid", "disable-btn"); @@ -1332,6 +1341,12 @@ var showProgress = this.mAddon.purchaseURL || (this.mAddon.install && this.mAddon.install.state != AddonManager.STATE_INSTALLED); this._showStatus(showProgress ? "progress" : "none"); + + let debuggable = this.mAddon.isDebuggable && + Services.prefs.getBoolPref('devtools.debugger.addon-enabled') && + Services.prefs.getBoolPref('devtools.debugger.remote-enabled'); + + this._debugBtn.disabled = this._debugBtn.hidden = !debuggable ]]> @@ -1353,11 +1368,11 @@ } var relNotesData = null, transformData = null; - + this._relNotesLoaded = true; this._relNotesLoading.hidden = false; this._relNotesError.hidden = true; - + function sendToggleEvent() { var event = document.createEvent("Events"); event.initEvent("RelNotesToggle", true, true); @@ -1492,6 +1507,12 @@ ]]> + + + + - + + + @@ -597,6 +600,9 @@ #endif command="cmd_showItemPreferences"/> +