diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/AccessibleOrProxy.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/AccessibleOrProxy.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/AccessibleOrProxy.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/AccessibleOrProxy.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -6,6 +6,9 @@ #include "AccessibleOrProxy.h" +namespace mozilla { +namespace a11y { + AccessibleOrProxy AccessibleOrProxy::Parent() const { @@ -25,3 +28,6 @@ // Otherwise this should be the proxy for the tab's top level document. return proxy->OuterDocOfRemoteBrowser(); } + +} +} diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/ARIAMap.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/ARIAMap.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/ARIAMap.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/ARIAMap.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -725,7 +725,7 @@ states::LINKED }, { // list - &nsGkAtoms::list, + &nsGkAtoms::list_, roles::LIST, kUseMapRole, eNoValue, @@ -1064,7 +1064,7 @@ kNoReqStates }, { // switch - &nsGkAtoms::_switch, + &nsGkAtoms::svgSwitch, roles::SWITCH, kUseMapRole, eNoValue, diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/ARIAStateMap.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/ARIAStateMap.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/ARIAStateMap.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/ARIAStateMap.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -90,7 +90,7 @@ static const EnumTypeData data = { nsGkAtoms::aria_autocomplete, { &nsGkAtoms::inlinevalue, - &nsGkAtoms::list, + &nsGkAtoms::list_, &nsGkAtoms::both, nullptr }, { states::SUPPORTS_AUTOCOMPLETION, states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION, diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/EventTree.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/EventTree.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/EventTree.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/EventTree.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -6,6 +6,8 @@ #include "EventTree.h" #include "Accessible-inl.h" +#include "EmbeddedObjCollector.h" +#include "NotificationController.h" #include "nsEventShell.h" #include "DocAccessible.h" #ifdef A11Y_LOG diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/EventTree.h firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/EventTree.h --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/EventTree.h 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/EventTree.h 2018-03-14 13:35:14.000000000 +0000 @@ -9,12 +9,15 @@ #include "AccEvent.h" #include "Accessible.h" +#include "mozilla/a11y/DocAccessible.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" namespace mozilla { namespace a11y { +class NotificationController; + /** * This class makes sure required tasks are done before and after tree * mutations. Currently this only includes group info invalidation. You must diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/Filters.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/Filters.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/Filters.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/Filters.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -33,11 +33,11 @@ uint32_t filters::GetRow(Accessible* aAccessible) { - a11y::role role = aAccessible->Role(); - if (role == roles::ROW) + if (aAccessible->IsTableRow()) return eMatch | eSkipSubtree; // Look for rows inside rowgroup. + a11y::role role = aAccessible->Role(); if (role == roles::GROUPING) return eSkip; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/FocusManager.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/FocusManager.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/FocusManager.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/FocusManager.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -13,6 +13,8 @@ #include "Role.h" #include "nsFocusManager.h" +#include "mozilla/a11y/DocAccessibleParent.h" +#include "mozilla/a11y/DocManager.h" #include "mozilla/EventStateManager.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/TabParent.h" diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/FocusManager.h firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/FocusManager.h --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/FocusManager.h 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/FocusManager.h 2018-03-14 13:35:14.000000000 +0000 @@ -5,6 +5,8 @@ #ifndef mozilla_a11y_FocusManager_h_ #define mozilla_a11y_FocusManager_h_ +#include "mozilla/RefPtr.h" + class nsINode; class nsIDocument; class nsISupports; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/Logging.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/Logging.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/Logging.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/Logging.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -23,6 +23,7 @@ #include "nsIDocShellTreeItem.h" #include "nsIURI.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLBodyElement.h" using namespace mozilla; using namespace mozilla::a11y; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/NotificationController.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/NotificationController.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/NotificationController.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/NotificationController.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -7,6 +7,7 @@ #include "DocAccessible-inl.h" #include "DocAccessibleChild.h" +#include "nsEventShell.h" #include "TextLeafAccessible.h" #include "TextUpdater.h" @@ -16,6 +17,7 @@ using namespace mozilla; using namespace mozilla::a11y; +using namespace mozilla::dom; //////////////////////////////////////////////////////////////////////////////// // NotificationCollector diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsAccUtils.h firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsAccUtils.h --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsAccUtils.h 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsAccUtils.h 2018-03-14 13:35:14.000000000 +0000 @@ -7,6 +7,7 @@ #define nsAccUtils_h_ #include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/DocManager.h" #include "nsAccessibilityService.h" #include "nsCoreUtils.h" diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsEventShell.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsEventShell.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsEventShell.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsEventShell.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -6,6 +6,7 @@ #include "nsEventShell.h" #include "nsAccUtils.h" +#include "Logging.h" #include "mozilla/StaticPtr.h" diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsTextEquivUtils.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsTextEquivUtils.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/nsTextEquivUtils.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/nsTextEquivUtils.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -12,6 +12,7 @@ #include "nsCoreUtils.h" #include "nsIDOMXULLabeledControlEl.h" +using namespace mozilla; using namespace mozilla::a11y; /** diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/TextAttrs.h firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/TextAttrs.h --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/base/TextAttrs.h 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/base/TextAttrs.h 2018-03-14 13:35:14.000000000 +0000 @@ -8,6 +8,7 @@ #include "nsCOMPtr.h" #include "nsColor.h" +#include "nsString.h" #include "nsStyleConsts.h" class nsIFrame; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/generic/DocAccessible.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/generic/DocAccessible.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/generic/DocAccessible.cpp 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/generic/DocAccessible.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -448,6 +448,7 @@ parentDocument->RemoveChildDocument(this); mParent->RemoveChild(this); + MOZ_ASSERT(!mParent, "Parent has to be null!"); } // Walk the array backwards because child documents remove themselves from the diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/generic/OuterDocAccessible.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/generic/OuterDocAccessible.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/generic/OuterDocAccessible.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/generic/OuterDocAccessible.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -104,7 +104,10 @@ // to its parent document. Otherwise a document accessible may be lost if // its outerdoc has being recreated (see bug 862863 for details). if (!mDoc->IsDefunct()) { - mDoc->BindChildDocument(child->AsDoc()); + MOZ_ASSERT(!child->IsDefunct(), "Attempt to reattach shutdown document accessible"); + if (!child->IsDefunct()) { + mDoc->BindChildDocument(child->AsDoc()); + } } } @@ -143,8 +146,8 @@ OuterDocAccessible::RemoveChild(Accessible* aAccessible) { Accessible* child = mChildren.SafeElementAt(0, nullptr); + MOZ_ASSERT(child == aAccessible, "Wrong child to remove!"); if (child != aAccessible) { - NS_ERROR("Wrong child to remove!"); return false; } diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/html/HTMLFormControlAccessible.cpp firefox-trunk-61.0~a1~hg20180314r408098/accessible/html/HTMLFormControlAccessible.cpp --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/html/HTMLFormControlAccessible.cpp 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/html/HTMLFormControlAccessible.cpp 2018-03-14 13:35:14.000000000 +0000 @@ -406,7 +406,7 @@ } // Expose autocomplete state if it has associated autocomplete list. - if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::list)) + if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::list_)) return state | states::SUPPORTS_AUTOCOMPLETION | states::HASPOPUP; // Ordinal XUL textboxes don't support autocomplete. diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/content-script.js firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/content-script.js --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/content-script.js 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/content-script.js 2018-03-14 13:35:14.000000000 +0000 @@ -4,7 +4,6 @@ /* eslint-env mozilla/frame-script */ -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.defineModuleGetter(this, "Logger", "resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "Presentation", diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/Gestures.jsm firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/Gestures.jsm --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/Gestures.jsm 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/Gestures.jsm 2018-03-14 13:35:14.000000000 +0000 @@ -38,8 +38,6 @@ var EXPORTED_SYMBOLS = ["GestureSettings", "GestureTracker"]; // jshint ignore:line -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); - ChromeUtils.defineModuleGetter(this, "Utils", // jshint ignore:line "resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "Logger", // jshint ignore:line diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/OutputGenerator.jsm firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/OutputGenerator.jsm --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/OutputGenerator.jsm 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/OutputGenerator.jsm 2018-03-14 13:35:14.000000000 +0000 @@ -15,7 +15,6 @@ const OUTPUT_DESC_FIRST = 0; const OUTPUT_DESC_LAST = 1; -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.defineModuleGetter(this, "Utils", // jshint ignore:line "resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "PrefCache", // jshint ignore:line diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/PointerAdapter.jsm firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/PointerAdapter.jsm --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/PointerAdapter.jsm 2018-03-11 11:24:58.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/PointerAdapter.jsm 2018-03-14 13:35:14.000000000 +0000 @@ -8,8 +8,6 @@ var EXPORTED_SYMBOLS = ["PointerRelay", "PointerAdapter"]; // jshint ignore:line -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); - ChromeUtils.defineModuleGetter(this, "Utils", // jshint ignore:line "resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "Logger", // jshint ignore:line diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/Presentation.jsm firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/Presentation.jsm --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/jsat/Presentation.jsm 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/jsat/Presentation.jsm 2018-03-14 13:35:14.000000000 +0000 @@ -6,7 +6,6 @@ "use strict"; -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/accessibility/Utils.jsm"); ChromeUtils.defineModuleGetter(this, "Logger", // jshint ignore:line "resource://gre/modules/accessibility/Utils.jsm"); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/accessible/tests/mochitest/tree/test_tabbrowser.xul firefox-trunk-61.0~a1~hg20180314r408098/accessible/tests/mochitest/tree/test_tabbrowser.xul --- firefox-trunk-60.0~a1~hg20180311r407525/accessible/tests/mochitest/tree/test_tabbrowser.xul 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/accessible/tests/mochitest/tree/test_tabbrowser.xul 2018-03-14 13:35:14.000000000 +0000 @@ -163,6 +163,9 @@ // children: [ ... ] // Ignore document content. } ] + }, + { + role: ROLE_STATUSBAR } ] }, diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/app/profile/firefox.js firefox-trunk-61.0~a1~hg20180314r408098/browser/app/profile/firefox.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/app/profile/firefox.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/app/profile/firefox.js 2018-03-14 13:35:14.000000000 +0000 @@ -1541,8 +1541,12 @@ // For speculatively warming up tabs to improve perceived // performance while using the async tab switcher. -// Disabled until bug 1397426 is fixed. +#if defined(NIGHTLY_BUILD) +pref("browser.tabs.remote.warmup.enabled", true); +#else pref("browser.tabs.remote.warmup.enabled", false); +#endif + pref("browser.tabs.remote.warmup.maxTabs", 3); pref("browser.tabs.remote.warmup.unloadDelayMs", 2000); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.css firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.css --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.css 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.css 2018-03-14 13:35:14.000000000 +0000 @@ -976,51 +976,50 @@ /* Status panel */ -statuspanel { - -moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel"); +#statuspanel { position: fixed; margin-top: -3em; max-width: calc(100% - 5px); pointer-events: none; } -statuspanel[mirror] { +#statuspanel[mirror] { offset-inline-start: auto; offset-inline-end: 0; } -statuspanel[sizelimit] { +#statuspanel[sizelimit] { max-width: 50%; } -statuspanel[type=status] { +#statuspanel[type=status] { min-width: 23em; } @media all and (max-width: 800px) { - statuspanel[type=status] { + #statuspanel[type=status] { min-width: 33%; } } -statuspanel[type=overLink] { +#statuspanel[type=overLink] { transition: opacity 120ms ease-out; } -statuspanel[type=overLink] > .statuspanel-inner { +#statuspanel[type=overLink] > #statuspanel-inner { direction: ltr; } -statuspanel[inactive] { +#statuspanel[inactive] { transition: none; opacity: 0; } -statuspanel[inactive][previoustype=overLink] { +#statuspanel[inactive][previoustype=overLink] { transition: opacity 200ms ease-out; } -.statuspanel-inner { +#statuspanel-inner { height: 3em; width: 100%; -moz-box-align: end; @@ -1367,6 +1366,10 @@ box-shadow: 0 0 2px 2px rgba(255,0,0,0.4); } +.popup-notification-description[popupid=webauthn-prompt-register-direct] { + white-space: pre-line; +} + .dragfeedback-tab { -moz-appearance: none; opacity: 0.65; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.js 2018-03-14 13:35:14.000000000 +0000 @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* eslint-env mozilla/browser-window */ +/* globals StatusPanel */ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); @@ -140,25 +141,6 @@ "nsICrashReporter"); } -Object.defineProperty(this, "gBrowser", { - configurable: true, - enumerable: true, - get() { - delete window.gBrowser; - - // The tabbed browser only exists in proper browser windows, but on Mac we - // load browser.js in other windows and might try to access gBrowser. - if (!window._gBrowser) { - return window.gBrowser = null; - } - - window.gBrowser = window._gBrowser; - delete window._gBrowser; - gBrowser.init(); - return gBrowser; - }, -}); - XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() { return Services.strings.createBundle("chrome://browser/locale/browser.properties"); }); @@ -240,8 +222,7 @@ return null; }); -const nsIWebNavigation = Ci.nsIWebNavigation; - +var gBrowser; var gLastValidURLStr = ""; var gInPrintPreviewMode = false; var gContextMenu = null; // nsContextMenu instance @@ -1207,8 +1188,12 @@ delayedStartupFinished: false, onDOMContentLoaded() { + gBrowser = window._gBrowser; + delete window._gBrowser; + gBrowser.init(); + window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(nsIWebNavigation) + .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIXULWindow) @@ -1467,6 +1452,7 @@ BrowserOffline.init(); IndexedDBPromptHelper.init(); CanvasPermissionPromptHelper.init(); + WebAuthnPromptHelper.init(); // Initialize the full zoom setting. // We do this before the session restore service gets initialized so we can @@ -1937,6 +1923,7 @@ BrowserOffline.uninit(); IndexedDBPromptHelper.uninit(); CanvasPermissionPromptHelper.uninit(); + WebAuthnPromptHelper.uninit(); PanelUI.uninit(); AutoShowBookmarksToolbar.uninit(); } @@ -2185,9 +2172,8 @@ } function BrowserStop() { - const stopFlags = nsIWebNavigation.STOP_ALL; maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "stop"); - gBrowser.webNavigation.stop(stopFlags); + gBrowser.webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL); } function BrowserReloadOrDuplicate(aEvent) { @@ -2213,13 +2199,14 @@ // Bug 1167797: For view source, we always skip the cache return BrowserReloadSkipCache(); } - const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE; + const reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE; BrowserReloadWithFlags(reloadFlags); } function BrowserReloadSkipCache() { // Bypass proxy and cache. - const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; + const reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | + Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE; BrowserReloadWithFlags(reloadFlags); } @@ -3164,7 +3151,7 @@ // but add a notify bar as a reminder, so that they don't lose // track after, e.g., tab switching. gBrowser.loadURIWithFlags(gBrowser.currentURI.spec, - nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, + Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, null, null, null); Services.perms.add(gBrowser.currentURI, "safe-browsing", @@ -4389,8 +4376,8 @@ defaultStatus: "", overLink: "", startTime: 0, - statusText: "", isBusy: false, + busyUI: false, // Left here for add-on compatibility, see bug 752434 inContentWhitelist: [], @@ -4412,9 +4399,6 @@ delete this.reloadCommand; return this.reloadCommand = document.getElementById("Browser:Reload"); }, - get statusTextField() { - return gBrowser.getStatusPanel(); - }, get isImage() { delete this.isImage; return this.isImage = document.getElementById("isImage"); @@ -4438,7 +4422,7 @@ setDefaultStatus(status) { this.defaultStatus = status; - this.updateStatusField(); + StatusPanel.update(); }, setOverLink(url, anchorElt) { @@ -4482,29 +4466,6 @@ return gBrowser.tabs.length; }, - updateStatusField() { - var text, type, types = ["overLink"]; - if (this._busyUI) - types.push("status"); - types.push("defaultStatus"); - for (type of types) { - text = this[type]; - if (text) - break; - } - - // check the current value so we don't trigger an attribute change - // and cause needless (slow!) UI updates - if (this.statusText != text) { - let field = this.statusTextField; - field.setAttribute("previoustype", field.getAttribute("type")); - field.setAttribute("type", type); - field.label = text; - field.setAttribute("crop", type == "overLink" ? "center" : "end"); - this.statusText = text; - } - }, - // Called before links are navigated to to allow us to retarget them if needed. onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab) { return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab); @@ -4570,7 +4531,7 @@ this.isBusy = true; if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) { - this._busyUI = true; + this.busyUI = true; // XXX: This needs to be based on window activity... this.stopCommand.removeAttribute("disabled"); @@ -4623,8 +4584,8 @@ this.isBusy = false; - if (this._busyUI) { - this._busyUI = false; + if (this.busyUI) { + this.busyUI = false; this.stopCommand.setAttribute("disabled", "true"); CombinedStopReload.switchToReload(aRequest, aWebProgress); @@ -4785,7 +4746,7 @@ onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { this.status = aMessage; - this.updateStatusField(); + StatusPanel.update(); }, // Properties used to cache security state used to update the UI @@ -4873,10 +4834,6 @@ DELAY_HIDE: 250, _timer: 0, - get _isVisible() { - return XULBrowserWindow.statusTextField.label != ""; - }, - update() { clearTimeout(this._timer); window.removeEventListener("mousemove", this, true); @@ -4889,8 +4846,8 @@ return; } - if (this._isVisible) { - XULBrowserWindow.updateStatusField(); + if (StatusPanel.isVisible) { + StatusPanel.update(); } else { // Let the display appear when the mouse doesn't move within the delay this._showDelayed(); @@ -4910,7 +4867,7 @@ _showDelayed() { this._timer = setTimeout(function(self) { - XULBrowserWindow.updateStatusField(); + StatusPanel.update(); window.removeEventListener("mousemove", self, true); }, this.DELAY_SHOW, this); }, @@ -4918,7 +4875,7 @@ _hide() { clearTimeout(this._timer); - XULBrowserWindow.updateStatusField(); + StatusPanel.update(); } }; @@ -5182,14 +5139,14 @@ } }, - onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI, - aFlags) { + onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI, aFlags) { // Filter out location changes caused by anchor navigation // or history.push/pop/replaceState. if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) { - // Reader mode actually cares about these: - let mm = gBrowser.selectedBrowser.messageManager; - mm.sendAsyncMessage("Reader:PushState", {isArticle: gBrowser.selectedBrowser.isArticle}); + // Reader mode cares about history.pushState and friends. + aBrowser.messageManager.sendAsyncMessage("Reader:PushState", { + isArticle: aBrowser.isArticle, + }); return; } @@ -5362,11 +5319,11 @@ Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL : Ci.nsIWebNavigation.LOAD_FLAGS_NONE; gBrowser.loadURIWithFlags(aURI.spec, { - aTriggeringPrincipal, - flags: loadflags, - referrerURI: referrer, - referrerPolicy, - }); + aTriggeringPrincipal, + flags: loadflags, + referrerURI: referrer, + referrerPolicy, + }); } if (!Services.prefs.getBoolPref("browser.tabs.loadDivertedInBackground")) window.focus(); @@ -6246,7 +6203,7 @@ } function BrowserCharsetReload() { - BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); + BrowserReloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); } function UpdateCurrentCharset(target) { @@ -6769,6 +6726,133 @@ } }; +var WebAuthnPromptHelper = { + _icon: "webauthn-notification-icon", + _topic: "webauthn-prompt", + + // The current notification, if any. The U2F manager is a singleton, we will + // never allow more than one active request. And thus we'll never have more + // than one notification either. + _current: null, + + // The current transaction ID. Will be checked when we're notified of the + // cancellation of an ongoing WebAuthhn request. + _tid: 0, + + init() { + Services.obs.addObserver(this, this._topic); + }, + + uninit() { + Services.obs.removeObserver(this, this._topic); + }, + + observe(aSubject, aTopic, aData) { + let mgr = aSubject.QueryInterface(Ci.nsIU2FTokenManager); + let data = JSON.parse(aData); + + if (data.action == "register") { + this.register(mgr, data); + } else if (data.action == "register-direct") { + this.registerDirect(mgr, data); + } else if (data.action == "sign") { + this.sign(mgr, data); + } else if (data.action == "cancel") { + this.cancel(data); + } + }, + + register(mgr, {origin, tid}) { + let mainAction = this.buildCancelAction(mgr, tid); + this.show(tid, "register", "webauthn.registerPrompt", origin, mainAction); + }, + + registerDirect(mgr, {origin, tid}) { + let mainAction = this.buildProceedAction(mgr, tid); + let secondaryActions = [this.buildCancelAction(mgr, tid)]; + + let learnMoreURL = + Services.urlFormatter.formatURLPref("app.support.baseURL") + + "webauthn-direct-attestation"; + + let options = { + learnMoreURL, + checkbox: { + label: gNavigatorBundle.getString("webauthn.anonymize") + } + }; + + this.show(tid, "register-direct", "webauthn.registerDirectPrompt", + origin, mainAction, secondaryActions, options); + }, + + sign(mgr, {origin, tid}) { + let mainAction = this.buildCancelAction(mgr, tid); + this.show(tid, "sign", "webauthn.signPrompt", origin, mainAction); + }, + + show(tid, id, stringId, origin, mainAction, secondaryActions = [], options = {}) { + this.reset(); + + try { + origin = Services.io.newURI(origin).asciiHost; + } catch (e) { + /* Might fail for arbitrary U2F RP IDs. */ + } + + let brandShortName = + document.getElementById("bundle_brand").getString("brandShortName"); + let message = + gNavigatorBundle.getFormattedString(stringId, ["<>", brandShortName], 1); + + options.name = origin; + options.hideClose = true; + options.eventCallback = event => { + if (event == "removed") { + this._current = null; + this._tid = 0; + } + }; + + this._tid = tid; + this._current = PopupNotifications.show( + gBrowser.selectedBrowser, `webauthn-prompt-${id}`, message, + this._icon, mainAction, secondaryActions, options); + }, + + cancel({tid}) { + if (this._tid == tid) { + this.reset(); + } + }, + + reset() { + if (this._current) { + this._current.remove(); + } + }, + + buildProceedAction(mgr, tid) { + return { + label: gNavigatorBundle.getString("webauthn.proceed"), + accessKey: gNavigatorBundle.getString("webauthn.proceed.accesskey"), + callback(state) { + mgr.resumeRegister(tid, !state.checkboxChecked); + } + }; + }, + + buildCancelAction(mgr, tid) { + return { + label: gNavigatorBundle.getString("webauthn.cancel"), + accessKey: gNavigatorBundle.getString("webauthn.cancel.accesskey"), + callback() { + mgr.cancel(tid); + } + }; + }, +}; + function CanCloseWindow() { // Avoid redundant calls to canClose from showing multiple // PermitUnload dialogs. diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.xul firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.xul --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/browser.xul 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/browser.xul 2018-03-14 13:35:14.000000000 +0000 @@ -866,6 +866,8 @@ tooltiptext="&urlbar.persistentStorageNotificationAnchor.tooltip;"/> + @@ -1232,6 +1234,15 @@ selectmenulist="ContentSelectDropdown" datetimepicker="DateTimePickerPanel"/> + + + + diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/docs/tabbrowser/async-tab-switcher.rst firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/docs/tabbrowser/async-tab-switcher.rst --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/docs/tabbrowser/async-tab-switcher.rst 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/docs/tabbrowser/async-tab-switcher.rst 2018-03-14 13:35:14.000000000 +0000 @@ -33,7 +33,7 @@ For non-remote ````'s, ``hasLayers`` returns the value for ``docShellIsActive``. ``docShellIsActive`` - For remote ````'s, setting ``docShellIsActive`` to ``true`` also sets ``renderLayers`` to true, and then sends a message to the content process to set its top-level docShell active state to ``true``. Similarly, setting ``docShellIsActive`` to ``false`` also sets ``renderLayers`` to false, and then sends a message ot the content process to set its top-level docShell active state to ``false``. + For remote ````'s, setting ``docShellIsActive`` to ``true`` also sets ``renderLayers`` to true, and then sends a message to the content process to set its top-level docShell active state to ``true``. Similarly, setting ``docShellIsActive`` to ``false`` also sets ``renderLayers`` to false, and then sends a message to the content process to set its top-level docShell active state to ``false``. For non-remote ````'s, ``docShellIsActive`` forwards to the ``isActive`` property on the ````'s top-level docShell. @@ -230,4 +230,4 @@ The async tab switcher has some logging capabilities that make it easier to debug and reason about its behaviour. Setting the hidden ``browser.tabs.remote.logSwitchTiming`` pref to true will put logging into the Browser Console. -Alternatively, setting the ``useDumpForLogging`` property to true within the source code of the tab switcher will dump those logs to stdout. \ No newline at end of file +Alternatively, setting the ``useDumpForLogging`` property to true within the source code of the tab switcher will dump those logs to stdout. diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/tabbrowser.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/tabbrowser.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/tabbrowser.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/tabbrowser.js 2018-03-14 13:35:14.000000000 +0000 @@ -25,26 +25,7 @@ window.addEventListener("sizemodechange", this); window.addEventListener("occlusionstatechange", this); - this.mCurrentBrowser = this.initialBrowser; - this.mCurrentBrowser.permanentKey = {}; - - this.mCurrentTab = this.tabs[0]; - - var uniqueId = this._generateUniquePanelID(); - this.mPanelContainer.childNodes[0].id = uniqueId; - this.mCurrentTab.linkedPanel = uniqueId; - this.mCurrentTab.permanentKey = this.mCurrentBrowser.permanentKey; - this.mCurrentTab._tPos = 0; - this.mCurrentTab._fullyOpen = true; - this.mCurrentTab.linkedBrowser = this.mCurrentBrowser; - this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab); - - // set up the shared autoscroll popup - this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup(); - this._autoScrollPopup.id = "autoscroller"; - document.getElementById("mainPopupSet").appendChild(this._autoScrollPopup); - this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id); - this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink; + this._setupInitialBrowserAndTab(); // Hook up the event listeners to the first browser var tabListener = new TabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false); @@ -177,7 +158,7 @@ "resumeMedia", "mute", "unmute", "blockedPopups", "lastURI", "purgeSessionHistory", "stopScroll", "startScroll", "userTypedValue", "userTypedClear", "mediaBlocked", - "didStartLoadSinceLastUserTyping" + "didStartLoadSinceLastUserTyping", "audioMuted" ], _removingTabs: [], @@ -225,11 +206,6 @@ _hoverTabTimer: null, - get initialBrowser() { - delete this.initialBrowser; - return this.initialBrowser = document.getElementById("tabbrowser-initialBrowser"); - }, - get tabContainer() { delete this.tabContainer; return this.tabContainer = document.getElementById("tabbrowser-tabs"); @@ -310,6 +286,36 @@ return this.mCurrentBrowser; }, + get initialBrowser() { + delete this.initialBrowser; + return this.initialBrowser = document.getElementById("tabbrowser-initialBrowser"); + }, + + _setupInitialBrowserAndTab() { + let browser = this.initialBrowser; + this.mCurrentBrowser = browser; + + browser.permanentKey = {}; + browser.droppedLinkHandler = handleDroppedLink; + + this._autoScrollPopup = browser._createAutoScrollPopup(); + this._autoScrollPopup.id = "autoscroller"; + document.getElementById("mainPopupSet").appendChild(this._autoScrollPopup); + browser.setAttribute("autoscrollpopup", this._autoScrollPopup.id); + + let tab = this.tabs[0]; + this.mCurrentTab = tab; + + let uniqueId = this._generateUniquePanelID(); + this.mPanelContainer.childNodes[0].id = uniqueId; + tab.linkedPanel = uniqueId; + tab.permanentKey = browser.permanentKey; + tab._tPos = 0; + tab._fullyOpen = true; + tab.linkedBrowser = browser; + this._tabForBrowser.set(browser, tab); + }, + /** * BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT * MAKE SURE TO ADD IT HERE AS WELL. @@ -497,22 +503,10 @@ return findBar; }, - getStatusPanel() { - if (!this._statusPanel) { - this._statusPanel = document.createElementNS(this._XUL_NS, "statuspanel"); - this._statusPanel.setAttribute("inactive", "true"); - this._statusPanel.setAttribute("layer", "true"); - this._appendStatusPanel(); - } - return this._statusPanel; - }, - _appendStatusPanel() { - if (this._statusPanel) { - let browser = this.selectedBrowser; - let browserContainer = this.getBrowserContainer(browser); - browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling); - } + let browser = this.selectedBrowser; + let browserContainer = this.getBrowserContainer(browser); + browserContainer.insertBefore(StatusPanel.panel, browser.parentNode.nextSibling); }, pinTab(aTab) { @@ -679,7 +673,7 @@ getTabFromAudioEvent(aEvent) { if (!Services.prefs.getBoolPref("browser.tabs.showAudioPlayingIcon") || - !aEvent.isTrusted) { + !aEvent.isTrusted) { return null; } @@ -688,7 +682,7 @@ return tab; }, - _callProgressListeners(aBrowser, aMethod, aArguments, aCallGlobalListeners, aCallTabsListeners) { + _callProgressListeners(aBrowser, aMethod, aArguments, aCallGlobalListeners = true, aCallTabsListeners = true) { var rv = true; function callListeners(listeners, args) { @@ -705,17 +699,13 @@ } } - if (!aBrowser) - aBrowser = this.mCurrentBrowser; + aBrowser = aBrowser || this.mCurrentBrowser; - // eslint-disable-next-line mozilla/no-compare-against-boolean-literals - if (aCallGlobalListeners != false && - aBrowser == this.mCurrentBrowser) { + if (aCallGlobalListeners && aBrowser == this.mCurrentBrowser) { callListeners(this.mProgressListeners, aArguments); } - // eslint-disable-next-line mozilla/no-compare-against-boolean-literals - if (aCallTabsListeners != false) { + if (aCallTabsListeners) { aArguments.unshift(aBrowser); callListeners(this.mTabsProgressListeners, aArguments); @@ -864,7 +854,7 @@ docTitle = tab.getAttribute("label").replace(/\0/g, ""); } - if (!docTitle) + if (!docTitle || docTitle == this.tabContainer.emptyTabTitle) docTitle = docElement.getAttribute("titledefault"); var modifier = docElement.getAttribute("titlemodifier"); @@ -1332,7 +1322,7 @@ } catch (ex) { /* Do nothing. */ } } else { // Still no title? Fall back to our untitled string. - title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"); + title = this.tabContainer.emptyTabTitle; } } @@ -1947,7 +1937,7 @@ let setter; switch (name) { case "audioMuted": - getter = () => false; + getter = () => aTab.hasAttribute("muted"); break; case "contentTitle": getter = () => SessionStore.getLazyTabValue(aTab, "title"); @@ -2264,7 +2254,7 @@ if (!aNoInitialLabel) { if (isBlankPageURL(aURI)) { - t.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle")); + t.setAttribute("label", this.tabContainer.emptyTabTitle); } else { // Set URL as label so that the tab isn't empty initially. this.setInitialTabTitle(t, aURI, { beforeTabOpen: true }); @@ -2689,7 +2679,7 @@ setTimeout(function(tab, tabbrowser) { if (tab.parentNode && window.getComputedStyle(tab).maxWidth == "0.1px") { - NS_ASSERT(false, "Giving up waiting for the tab closing animation to finish (bug 608589)"); + console.assert(false, "Giving up waiting for the tab closing animation to finish (bug 608589)"); tabbrowser._endRemoveTab(tab); } }, 3000, aTab, this); @@ -4608,3 +4598,130 @@ } } +let StatusPanel = { + get panel() { + window.addEventListener("resize", this); + + delete this.panel; + return this.panel = document.getElementById("statuspanel"); + }, + + get isVisible() { + return !this.panel.hasAttribute("inactive"); + }, + + update() { + let text; + let type; + let types = ["overLink"]; + if (XULBrowserWindow.busyUI) { + types.push("status"); + } + types.push("defaultStatus"); + for (type of types) { + if ((text = XULBrowserWindow[type])) { + break; + } + } + + if (this._labelElement.value != text) { + this.panel.setAttribute("previoustype", this.panel.getAttribute("type")); + this.panel.setAttribute("type", type); + this._label = text; + this._labelElement.setAttribute("crop", type == "overLink" ? "center" : "end"); + } + }, + + get _labelElement() { + delete this._labelElement; + return this._labelElement = document.getElementById("statuspanel-label"); + }, + + set _label(val) { + if (!this.isVisible) { + this.panel.removeAttribute("mirror"); + this.panel.removeAttribute("sizelimit"); + } + + if (this.panel.getAttribute("type") == "status" && + this.panel.getAttribute("previoustype") == "status") { + // Before updating the label, set the panel's current width as its + // min-width to let the panel grow but not shrink and prevent + // unnecessary flicker while loading pages. We only care about the + // panel's width once it has been painted, so we can do this + // without flushing layout. + this.panel.style.minWidth = + window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .getBoundsWithoutFlushing(this.panel).width + "px"; + } else { + this.panel.style.minWidth = ""; + } + + if (val) { + this._labelElement.value = val; + this.panel.removeAttribute("inactive"); + this._mouseTargetRect = null; + MousePosTracker.addListener(this); + } else { + this.panel.setAttribute("inactive", "true"); + MousePosTracker.removeListener(this); + } + + return val; + }, + + getMouseTargetRect() { + if (!this._mouseTargetRect) { + this._calcMouseTargetRect(); + } + return this._mouseTargetRect; + }, + + onMouseEnter() { + this._mirror(); + }, + + onMouseLeave() { + this._mirror(); + }, + + handleEvent(event) { + if (!this.isVisible) { + return; + } + switch (event.type) { + case "resize": + this._mouseTargetRect = null; + break; + } + }, + + _calcMouseTargetRect() { + let container = this.panel.parentNode; + let alignRight = (getComputedStyle(container).direction == "rtl"); + let panelRect = this.panel.getBoundingClientRect(); + let containerRect = container.getBoundingClientRect(); + + this._mouseTargetRect = { + top: panelRect.top, + bottom: panelRect.bottom, + left: alignRight ? containerRect.right - panelRect.width : containerRect.left, + right: alignRight ? containerRect.right : containerRect.left + panelRect.width + }; + }, + + _mirror() { + if (this.panel.hasAttribute("mirror")) { + this.panel.removeAttribute("mirror"); + } else { + this.panel.setAttribute("mirror", "true"); + } + + if (!this.panel.hasAttribute("sizelimit")) { + this.panel.setAttribute("sizelimit", "true"); + this._mouseTargetRect = null; + } + } +}; + diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/tabbrowser.xml firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/tabbrowser.xml --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/tabbrowser.xml 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/tabbrowser.xml 2018-03-14 13:35:14.000000000 +0000 @@ -46,8 +46,8 @@ tabs._expandSpacerBy(this._scrollButtonDown.clientWidth); } - for (let tab of Array.from(tabs.tabbrowser._removingTabs)) { - tabs.tabbrowser.removeTab(tab); + for (let tab of Array.from(gBrowser._removingTabs)) { + gBrowser.removeTab(tab); } tabs._positionPinnedTabs(); @@ -115,8 +115,12 @@ let { restoreTabsButton } = this; restoreTabsButton.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.restoreLastTabs")); + let strId = PrivateBrowsingUtils.isWindowPrivate(window) ? + "emptyPrivateTabTitle" : "emptyTabTitle"; + this.emptyTabTitle = gTabBrowserBundle.GetStringFromName("tabs." + strId); + var tab = this.firstChild; - tab.label = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"); + tab.label = this.emptyTabTitle; tab.setAttribute("onerror", "this.removeAttribute('image');"); window.addEventListener("resize", this); @@ -152,7 +156,7 @@ - this.tabbrowser.tabbox; + document.getElementById("tabbrowser-tabbox"); @@ -177,12 +181,6 @@ window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - - - return window.gBrowser; - - - this.style.setProperty("--tab-min-width", val + "px"); @@ -304,13 +302,23 @@ ]]> - + + + + @@ -437,7 +446,7 @@ .getInterface(Ci.nsIDOMWindowUtils) .getBoundsWithoutFlushing(ele); }; - let tab = this.tabbrowser.visibleTabs[this.tabbrowser._numPinnedTabs]; + let tab = this._getVisibleTabs()[gBrowser._numPinnedTabs]; if (tab && rect(tab).width <= this._tabClipWidth) { this.setAttribute("closebuttons", "activetab"); } else { @@ -470,45 +479,48 @@ tabs[tabs.length - 1]._tPos); var tabWidth = aTab.getBoundingClientRect().width; - if (!this._tabDefaultMaxWidth) + if (!this._tabDefaultMaxWidth) { this._tabDefaultMaxWidth = parseFloat(window.getComputedStyle(aTab).maxWidth); + } this._lastTabClosedByMouse = true; if (this.getAttribute("overflow") == "true") { // Don't need to do anything if we're in overflow mode and aren't scrolled // all the way to the right, or if we're closing the last tab. - if (isEndTab || !this.arrowScrollbox._scrollButtonDown.disabled) + if (isEndTab || !this.arrowScrollbox._scrollButtonDown.disabled) { return; - + } // If the tab has an owner that will become the active tab, the owner will // be to the left of it, so we actually want the left tab to slide over. // This can't be done as easily in non-overflow mode, so we don't bother. - if (aTab.owner) + if (aTab.owner) { return; - + } this._expandSpacerBy(tabWidth); } else { // non-overflow mode // Locking is neither in effect nor needed, so let tabs expand normally. - if (isEndTab && !this._hasTabTempMaxWidth) + if (isEndTab && !this._hasTabTempMaxWidth) { return; - - let numPinned = this.tabbrowser._numPinnedTabs; + } + let numPinned = gBrowser._numPinnedTabs; // Force tabs to stay the same width, unless we're closing the last tab, // which case we need to let them expand just enough so that the overall // tabbar width is the same. if (isEndTab) { let numNormalTabs = tabs.length - numPinned; tabWidth = tabWidth * (numNormalTabs + 1) / numNormalTabs; - if (tabWidth > this._tabDefaultMaxWidth) + if (tabWidth > this._tabDefaultMaxWidth) { tabWidth = this._tabDefaultMaxWidth; + } } tabWidth += "px"; for (let i = numPinned; i < tabs.length; i++) { @@ -521,7 +533,7 @@ } } this._hasTabTempMaxWidth = true; - this.tabbrowser.addEventListener("mousemove", this); + gBrowser.addEventListener("mousemove", this); window.addEventListener("mouseout", this); } ]]> @@ -533,21 +545,22 @@ let spacer = this._closingTabsSpacer; spacer.style.width = parseFloat(spacer.style.width) + pixels + "px"; this.setAttribute("using-closing-tabs-spacer", "true"); - this.tabbrowser.addEventListener("mousemove", this); + gBrowser.addEventListener("mousemove", this); window.addEventListener("mouseout", this); ]]> null numPinned && + let numPinned = gBrowser._numPinnedTabs; + let doPosition = this.getAttribute("overflow") == "true" && + this._getVisibleTabs().length > numPinned && numPinned > 0; if (doPosition) { @@ -638,12 +651,13 @@ let rtl = (window.getComputedStyle(this).direction == "rtl"); let pinned = draggedTab.pinned; - let numPinned = this.tabbrowser._numPinnedTabs; - let tabs = this.tabbrowser.visibleTabs - .slice(pinned ? 0 : numPinned, - pinned ? numPinned : undefined); - if (rtl) + let numPinned = gBrowser._numPinnedTabs; + let tabs = this._getVisibleTabs() + .slice(pinned ? 0 : numPinned, + pinned ? numPinned : undefined); + if (rtl) { tabs.reverse(); + } let tabWidth = draggedTab.getBoundingClientRect().width; draggedTab._dragData.tabWidth = tabWidth; @@ -653,8 +667,9 @@ let rightTab = tabs[tabs.length - 1]; let tabScreenX = draggedTab.boxObject.screenX; let translateX = screenX - draggedTab._dragData.screenX; - if (!pinned) + if (!pinned) { translateX += this.arrowScrollbox._scrollbox.scrollLeft - draggedTab._dragData.scrollX; + } let leftBound = leftTab.boxObject.screenX - tabScreenX; let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) - (tabScreenX + tabWidth); @@ -720,11 +735,13 @@ @@ -921,12 +939,19 @@ @@ -988,18 +1013,20 @@ @@ -1075,12 +1102,15 @@ ]]> draggedTab._tPos) dropIndex--; - let animate = this.tabbrowser.animationsEnabled; + let animate = gBrowser.animationsEnabled; if (oldTranslateX && oldTranslateX != newTranslateX && animate) { draggedTab.setAttribute("tabdrop-samewindow", "true"); draggedTab.style.transform = "translateX(" + newTranslateX + "px)"; @@ -1393,20 +1424,22 @@ draggedTab.removeAttribute("tabdrop-samewindow"); this._finishAnimateTabMove(); - if (dropIndex !== false) - this.tabbrowser.moveTabTo(draggedTab, dropIndex); + if (dropIndex !== false) { + gBrowser.moveTabTo(draggedTab, dropIndex); + } - this.tabbrowser.syncThrobberAnimations(draggedTab); + gBrowser.syncThrobberAnimations(draggedTab); }; draggedTab.addEventListener("transitionend", onTransitionEnd); } else { this._finishAnimateTabMove(); - if (dropIndex !== false) - this.tabbrowser.moveTabTo(draggedTab, dropIndex); + if (dropIndex !== false) { + gBrowser.moveTabTo(draggedTab, dropIndex); + } } } else if (draggedTab) { let newIndex = this._getDropIndex(event, false); - this.tabbrowser.adoptTab(draggedTab, newIndex, true); + gBrowser.adoptTab(draggedTab, newIndex, true); } else { // Pass true to disallow dropping javascript: or data: urls let links; @@ -1439,7 +1472,7 @@ } } - this.tabbrowser.loadTabs(urls, { + gBrowser.loadTabs(urls, { inBackground, replace, allowThirdPartyFixup: true, @@ -1520,7 +1553,7 @@ delete draggedTab._dragData; - if (this.tabbrowser.tabs.length == 1) { + if (gBrowser.tabs.length == 1) { // resize _before_ move to ensure the window fits the new screen. if // the window is too large for its screen, the window manager may do // automatic repositioning. @@ -1533,7 +1566,7 @@ props.outerWidth = winWidth; props.outerHeight = winHeight; } - this.tabbrowser.replaceTabWithWindow(draggedTab, props); + gBrowser.replaceTabWithWindow(draggedTab, props); } event.stopPropagation(); ]]> @@ -1622,11 +1655,16 @@ @@ -1740,11 +1778,12 @@ @@ -1871,12 +1910,6 @@ @@ -1978,12 +2011,13 @@ } if (event.originalTarget.getAttribute("anonid") == "close-button") { - let tabContainer = this.parentNode; - tabContainer.tabbrowser.removeTab(this, {animate: true, - byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE}); + gBrowser.removeTab(this, { + animate: true, + byMouse: event.mozInputSource == MouseEvent.MOZ_SOURCE_MOUSE, + }); // This enables double-click protection for the tab container // (see tabbrowser-tabs 'click' handler). - tabContainer._blockDblClick = true; + gBrowser.tabContainer._blockDblClick = true; } ]]> @@ -2227,134 +2261,6 @@ - - - - - - - - - - - - - - - - return this.hasAttribute("inactive") ? "" : this.getAttribute("label"); - - - - - - - - - - this._mirror(); - - - - - - this._mirror(); - - - - - - - - - - - - - - - if (this.hasAttribute("mirror")) - this.removeAttribute("mirror"); - else - this.setAttribute("mirror", "true"); - - if (!this.hasAttribute("sizelimit")) { - this.setAttribute("sizelimit", "true"); - this._mouseTargetRect = null; - } - - - - - diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser.ini 2018-03-14 13:35:14.000000000 +0000 @@ -30,6 +30,7 @@ skip-if = (os == 'linux') || (os == 'win' && debug) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. [browser_urlbar_search_reflows.js] skip-if = (debug || ccov) && (os == 'linux' || os == 'win') # Disabled on Linux and Windows debug and ccov due to intermittent timeouts. Bug 1414126, bug 1426611. +[browser_window_resize_reflows.js] [browser_windowclose_reflows.js] [browser_windowopen_flicker.js] skip-if = (debug && os == 'win') # Disabled on windows debug for intermittent leaks diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser_startup_content.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser_startup_content.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser_startup_content.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser_startup_content.js 2018-03-14 13:35:14.000000000 +0000 @@ -28,7 +28,6 @@ "resource://gre/modules/InlineSpellCheckerContent.jsm", "resource://gre/modules/Promise.jsm", "resource://gre/modules/Task.jsm", - "resource://gre/modules/debug.js", "resource://gre/modules/osfile.jsm", ]), services: new Set([ diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser_window_resize_reflows.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser_window_resize_reflows.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/browser_window_resize_reflows.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/browser_window_resize_reflows.js 2018-03-14 13:35:14.000000000 +0000 @@ -0,0 +1,138 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * WHOA THERE: We should never be adding new things to EXPECTED_REFLOWS. This + * is a whitelist that should slowly go away as we improve the performance of + * the front-end. Instead of adding more reflows to the whitelist, you should + * be modifying your code to avoid the reflow. + * + * See https://developer.mozilla.org/en-US/Firefox/Performance_best_practices_for_Firefox_fe_engineers + * for tips on how to do that. + */ +const EXPECTED_REFLOWS = [ + { + stack: [ + "onOverflow@resource:///modules/CustomizableUI.jsm", + ], + maxCount: 48, + }, + + { + stack: [ + "_moveItemsBackToTheirOrigin@resource:///modules/CustomizableUI.jsm", + "_onLazyResize@resource:///modules/CustomizableUI.jsm", + ], + maxCount: 5, + }, + + { + stack: [ + "_onLazyResize@resource:///modules/CustomizableUI.jsm", + ], + maxCount: 4, + }, +]; + +const gToolbar = document.getElementById("PersonalToolbar"); + +/** + * Sets the visibility state on the Bookmarks Toolbar, and + * waits for it to transition to fully visible. + * + * @param visible (bool) + * Whether or not the bookmarks toolbar should be made visible. + * @returns Promise + */ +async function toggleBookmarksToolbar(visible) { + let transitionPromise = + BrowserTestUtils.waitForEvent(gToolbar, "transitionend", + e => e.propertyName == "max-height"); + + setToolbarVisibility(gToolbar, visible); + await transitionPromise; +} + +/** + * Resizes a browser window to a particular width and height, and + * waits for it to reach a "steady state" with respect to its overflowing + * toolbars. + * @param win (browser window) + * The window to resize. + * @param width (int) + * The width to resize the window to. + * @param height (int) + * The height to resize the window to. + * @returns Promise + */ +async function resizeWindow(win, width, height) { + let toolbarEvent = + BrowserTestUtils.waitForEvent(win, "BookmarksToolbarVisibilityUpdated"); + let resizeEvent = + BrowserTestUtils.waitForEvent(win, "resize"); + let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + dwu.ensureDirtyRootFrame(); + win.resizeTo(width, height); + await resizeEvent; + forceImmediateToolbarOverflowHandling(win); + await toolbarEvent; +} + +/* + * This test ensures that there are no unexpected + * uninterruptible reflows when resizing windows. + */ +add_task(async function() { + const BOOKMARKS_COUNT = 150; + const STARTING_WIDTH = 600; + const STARTING_HEIGHT = 400; + const SMALL_WIDTH = 150; + const SMALL_HEIGHT = 150; + + await PlacesUtils.bookmarks.eraseEverything(); + + // Add a bunch of bookmarks to display in the Bookmarks toolbar + await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.toolbarGuid, + children: Array(BOOKMARKS_COUNT).fill("") + .map((_, i) => ({ url: `http://test.places.${i}/`})) + }); + + let wasCollapsed = gToolbar.collapsed; + Assert.ok(wasCollapsed, "The toolbar is collapsed by default"); + if (wasCollapsed) { + let promiseReady = + BrowserTestUtils.waitForEvent(gToolbar, "BookmarksToolbarVisibilityUpdated"); + await toggleBookmarksToolbar(true); + await promiseReady; + } + + registerCleanupFunction(async () => { + if (wasCollapsed) { + await toggleBookmarksToolbar(false); + } + await PlacesUtils.bookmarks.eraseEverything(); + await PlacesUtils.history.clear(); + }); + + let win = await prepareSettledWindow(); + + if (win.screen.availWidth < STARTING_WIDTH || + win.screen.availHeight < STARTING_HEIGHT) { + Assert.ok(false, "This test is running on too small a display - " + + `(${STARTING_WIDTH}x${STARTING_HEIGHT} min)`); + return; + } + + await resizeWindow(win, STARTING_WIDTH, STARTING_HEIGHT); + + await withReflowObserver(async function() { + await resizeWindow(win, SMALL_WIDTH, SMALL_HEIGHT); + await resizeWindow(win, STARTING_WIDTH, STARTING_HEIGHT); + }, EXPECTED_REFLOWS, win); + + await BrowserTestUtils.closeWindow(win); +}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/head.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/head.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/performance/head.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/performance/head.js 2018-03-14 13:35:14.000000000 +0000 @@ -199,18 +199,33 @@ }); } -async function prepareSettledWindow() { - let win = await BrowserTestUtils.openNewBrowserWindow(); - - await ensureNoPreloadedBrowser(win); - +/** + * The navigation toolbar is overflowable, meaning that some items + * will be moved and held within a sub-panel if the window gets too + * small to show their icons. The calculation for hiding those items + * occurs after resize events, and is debounced using a DeferredTask. + * This utility function allows us to fast-forward to just running + * that function for that DeferredTask instead of waiting for the + * debounce timeout to occur. + */ +function forceImmediateToolbarOverflowHandling(win) { let overflowableToolbar = win.document.getElementById("nav-bar").overflowable; if (overflowableToolbar._lazyResizeHandler && overflowableToolbar._lazyResizeHandler.isArmed) { - info("forcing deferred overflow handling of the navigation toolbar to happen immediately"); overflowableToolbar._lazyResizeHandler.disarm(); + // Ensure the root frame is dirty before resize so that, if we're + // in the middle of a reflow test, we record the reflows deterministically. + let dwu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + dwu.ensureDirtyRootFrame(); overflowableToolbar._onLazyResize(); } +} +async function prepareSettledWindow() { + let win = await BrowserTestUtils.openNewBrowserWindow(); + + await ensureNoPreloadedBrowser(win); + forceImmediateToolbarOverflowHandling(win); return win; } diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/popupNotifications/browser_popupNotification.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/popupNotifications/browser_popupNotification.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/popupNotifications/browser_popupNotification.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/popupNotifications/browser_popupNotification.js 2018-03-14 13:35:15.000000000 +0000 @@ -113,7 +113,7 @@ await BrowserTestUtils.browserLoaded(tab.linkedBrowser); isnot(gBrowser.selectedTab, tab, "new tab isn't selected"); wrongBrowserNotificationObject.browser = gBrowser.getBrowserForTab(tab); - let promiseTopic = promiseTopicObserved("PopupNotifications-backgroundShow"); + let promiseTopic = TestUtils.topicObserved("PopupNotifications-backgroundShow"); wrongBrowserNotification = showNotification(wrongBrowserNotificationObject); await promiseTopic; is(PopupNotifications.isPanelOpen, false, "panel isn't open"); @@ -147,7 +147,7 @@ // test that the removed notification isn't shown on browser re-select { id: "Test#6", async run() { - let promiseTopic = promiseTopicObserved("PopupNotifications-updateNotShowing"); + let promiseTopic = TestUtils.topicObserved("PopupNotifications-updateNotShowing"); gBrowser.selectedTab = gBrowser.tabs[gBrowser.tabs.length - 1]; await promiseTopic; is(PopupNotifications.isPanelOpen, false, "panel isn't open"); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/static/browser.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/static/browser.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/static/browser.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/static/browser.ini 2018-03-14 13:35:14.000000000 +0000 @@ -8,6 +8,7 @@ head.js [browser_all_files_referenced.js] +skip-if = (os == 'win' && bits == 32) [browser_misused_characters_in_strings.js] support-files = bug1262648_string_with_newlines.dtd diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/static/browser_parsable_css.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/static/browser_parsable_css.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/static/browser_parsable_css.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/static/browser_parsable_css.js 2018-03-14 13:35:14.000000000 +0000 @@ -96,9 +96,6 @@ // Bug 1441837 {propName: "--in-content-category-text-active", isFromDevTools: false}, - // Bug 1441844 - {propName: "--chrome-nav-bar-separator-color", - isFromDevTools: false}, // Bug 1441855 {propName: "--chrome-nav-buttons-background", isFromDevTools: false}, diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/tabs/browser_bug_1387976_restore_lazy_tab_browser_muted_state.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/tabs/browser_bug_1387976_restore_lazy_tab_browser_muted_state.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/tabs/browser_bug_1387976_restore_lazy_tab_browser_muted_state.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/tabs/browser_bug_1387976_restore_lazy_tab_browser_muted_state.js 2018-03-14 13:35:14.000000000 +0000 @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { TabState } = ChromeUtils.import("resource:///modules/sessionstore/TabState.jsm", {}); + +/** + * Simulate a restart of a tab by removing it, then add a lazy tab + * which is restored with the tabData of the removed tab. + * + * @param tab + * The tab to restart. + * @return {Object} the restored lazy tab + */ +const restartTab = async function(tab) { + let tabData = TabState.clone(tab); + BrowserTestUtils.removeTab(tab); + + let restoredLazyTab = BrowserTestUtils.addTab(gBrowser, "", {createLazyBrowser: true}); + SessionStore.setTabState(restoredLazyTab, JSON.stringify(tabData)); + return restoredLazyTab; +}; + +function get_tab_state(tab) { + const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore); + return JSON.parse(ss.getTabState(tab)); +} + +add_task(async function() { + const tab = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/"); + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Let's make sure the tab is not in a muted state at the beginning + ok(!("muted" in get_tab_state(tab)), "Tab should not be in a muted state"); + + info("toggling Muted audio..."); + tab.toggleMuteAudio(); + + ok("muted" in get_tab_state(tab), "Tab should be in a muted state"); + + info("Restarting tab..."); + let restartedTab = await restartTab(tab); + + ok("muted" in get_tab_state(restartedTab), "Restored tab should still be in a muted state after restart"); + + BrowserTestUtils.removeTab(restartedTab); +}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/tabs/browser.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/tabs/browser.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/tabs/browser.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/tabs/browser.ini 2018-03-14 13:35:15.000000000 +0000 @@ -35,3 +35,4 @@ [browser_visibleTabs_bookmarkAllTabs.js] [browser_visibleTabs_contextMenu.js] [browser_open_newtab_start_observer_notification.js] +[browser_bug_1387976_restore_lazy_tab_browser_muted_state.js] diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js 2018-03-14 13:35:15.000000000 +0000 @@ -89,7 +89,7 @@ await BrowserTestUtils.removeTab(tab); // Check that we still show the sharing indicators for the first tab's stream. - await promiseWaitForCondition(() => !webrtcUI.showCameraIndicator); + await TestUtils.waitForCondition(() => !webrtcUI.showCameraIndicator); ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown"); ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden"); ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown"); @@ -192,7 +192,7 @@ await BrowserTestUtils.removeTab(tab); // Check that we still show the sharing indicators for the first tab's stream. - await promiseWaitForCondition(() => webrtcUI.showCameraIndicator); + await TestUtils.waitForCondition(() => webrtcUI.showCameraIndicator); ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown"); ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown"); ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden"); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/test/webrtc/browser_devices_get_user_media_screen.js 2018-03-14 13:35:15.000000000 +0000 @@ -67,7 +67,7 @@ menulist.getItemAtIndex(2).doCommand(); ok(!document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should now be visible"); - await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100); + await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100, 100); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(!document.getElementById("webRTC-previewWarning").hidden, @@ -169,7 +169,7 @@ menulist.getItemAtIndex(scaryIndex).doCommand(); ok(document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should still be hidden"); - await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden); + await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(!document.getElementById("webRTC-previewWarning").hidden, @@ -186,7 +186,7 @@ menulist.getItemAtIndex(nonScaryIndex).doCommand(); ok(document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should still be hidden"); - await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden); + await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(document.getElementById("webRTC-previewWarning").hidden, @@ -277,7 +277,7 @@ menulist.getItemAtIndex(2).doCommand(); ok(document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should still be hidden"); - await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden); + await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(document.getElementById("webRTC-previewWarning").hidden, @@ -328,7 +328,7 @@ menulist.getItemAtIndex(2).doCommand(); ok(!document.getElementById("webRTC-all-windows-shared").hidden, "the 'all windows will be shared' warning should now be visible"); - await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden); + await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden); ok(!document.getElementById("webRTC-preview").hidden, "the preview area is visible"); ok(!document.getElementById("webRTC-previewWarning").hidden, @@ -498,7 +498,7 @@ Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator"); let elt = win.document.getElementById("screenShareButton"); EventUtils.synthesizeMouseAtCenter(elt, {}, win); - await promiseWaitForCondition(() => !gIdentityHandler._identityPopup.hidden); + await TestUtils.waitForCondition(() => !gIdentityHandler._identityPopup.hidden); } ok(!gIdentityHandler._identityPopup.hidden, "control center should be open"); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/web-panels.js firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/web-panels.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/base/content/web-panels.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/base/content/web-panels.js 2018-03-14 13:35:15.000000000 +0000 @@ -61,7 +61,7 @@ var panelBrowser = getPanelBrowser(); if (gLoadFired) { panelBrowser.webNavigation - .loadURI(aURI, nsIWebNavigation.LOAD_FLAGS_NONE, + .loadURI(aURI, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); } panelBrowser.setAttribute("cachedurl", aURI); @@ -75,7 +75,7 @@ var cachedurl = panelBrowser.getAttribute("cachedurl"); if (cachedurl) { panelBrowser.webNavigation - .loadURI(cachedurl, nsIWebNavigation.LOAD_FLAGS_NONE, null, + .loadURI(cachedurl, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); } @@ -87,12 +87,12 @@ } function PanelBrowserStop() { - getPanelBrowser().webNavigation.stop(nsIWebNavigation.STOP_ALL); + getPanelBrowser().webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL); } function PanelBrowserReload() { getPanelBrowser().webNavigation .sessionHistory - .QueryInterface(nsIWebNavigation) - .reload(nsIWebNavigation.LOAD_FLAGS_NONE); + .QueryInterface(Ci.nsIWebNavigation) + .reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE); } diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/customizableui/content/panelUI.inc.xul firefox-trunk-61.0~a1~hg20180314r408098/browser/components/customizableui/content/panelUI.inc.xul --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/customizableui/content/panelUI.inc.xul 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/customizableui/content/panelUI.inc.xul 2018-03-14 13:35:15.000000000 +0000 @@ -707,18 +707,16 @@ tabspecific="true"> - - &newTabControlled.message; - + diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/customizableui/test/browser_remote_tabs_button.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/customizableui/test/browser_remote_tabs_button.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/customizableui/test/browser_remote_tabs_button.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/customizableui/test/browser_remote_tabs_button.js 2018-03-14 13:35:15.000000000 +0000 @@ -46,6 +46,10 @@ info("The sync now button was clicked"); await waitForCondition(() => syncWasCalled); + + // We need to stop the Syncing animation manually otherwise the button + // will be disabled at the beginning of a next test. + gSync._onActivityStop(); }); add_task(async function asyncCleanup() { @@ -69,10 +73,10 @@ email: "user@mozilla.com" }); - service.sync = mocked_syncAndReportErrors; + service.sync = mocked_sync; } -function mocked_syncAndReportErrors() { +function mocked_sync() { syncWasCalled = true; } diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/downloads/DownloadsTaskbar.jsm firefox-trunk-61.0~a1~hg20180314r408098/browser/components/downloads/DownloadsTaskbar.jsm --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/downloads/DownloadsTaskbar.jsm 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/downloads/DownloadsTaskbar.jsm 2018-03-14 13:35:15.000000000 +0000 @@ -40,6 +40,12 @@ .getService(Ci.nsITaskbarProgress); }); +XPCOMUtils.defineLazyGetter(this, "gGtkTaskbarProgress", function() { + return ("@mozilla.org/widget/taskbarprogress/gtk;1" in Cc) && + Cc["@mozilla.org/widget/taskbarprogress/gtk;1"] + .getService(Ci.nsIGtkTaskbarProgress); +}); + // DownloadsTaskbar /** @@ -89,6 +95,10 @@ // On Windows, the indicator is currently hidden because we have no // previous browser window, thus we should attach the indicator now. this._attachIndicator(aBrowserWindow); + } else if (gGtkTaskbarProgress) { + this._taskbarProgress = gGtkTaskbarProgress; + + this._attachGtkTaskbarProgress(aBrowserWindow); } else { // The taskbar indicator is not available on this platform. return; @@ -137,6 +147,35 @@ } else { // The last browser window has been closed. We remove the reference to // the taskbar progress object so that the indicator will be registered + // again on the next browser window that is opened. + this._taskbarProgress = null; + } + }); + }, + + /** + * In gtk3, the window itself implements the progress interface. + */ + _attachGtkTaskbarProgress(aWindow) { + // Set the current window. + this._taskbarProgress.setPrimaryWindow(aWindow); + + // If the DownloadSummary object has already been created, we should update + // the state of the new indicator, otherwise it will be updated as soon as + // the DownloadSummary view is registered. + if (this._summary) { + this.onSummaryChanged(); + } + + aWindow.addEventListener("unload", () => { + // Locate another browser window, excluding the one being closed. + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); + if (browserWindow) { + // Move the progress indicator to the other browser window. + this._attachGtkTaskbarProgress(browserWindow); + } else { + // The last browser window has been closed. We remove the reference to + // the taskbar progress object so that the indicator will be registered // again on the next browser window that is opened. this._taskbarProgress = null; } diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/enterprisepolicies/schemas/policies-schema.json firefox-trunk-61.0~a1~hg20180314r408098/browser/components/enterprisepolicies/schemas/policies-schema.json --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/enterprisepolicies/schemas/policies-schema.json 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/enterprisepolicies/schemas/policies-schema.json 2018-03-14 13:35:15.000000000 +0000 @@ -54,7 +54,7 @@ }, "Favicon": { - "type": "URL" + "type": "URLorEmpty" }, "Placement": { @@ -66,7 +66,7 @@ "type": "string" } }, - "required": ["title", "URL"] + "required": ["Title", "URL"] } }, diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/ext-tabs.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/ext-tabs.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/ext-tabs.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/ext-tabs.js 2018-03-14 13:35:15.000000000 +0000 @@ -4,6 +4,7 @@ // The ext-* files are imported into the same scopes. /* import-globals-from ext-browser.js */ +/* import-globals-from ../../../toolkit/components/extensions/ext-tabs-base.js */ ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); @@ -97,6 +98,208 @@ }, }; +const allAttrs = new Set(["audible", "favIconUrl", "mutedInfo", "sharingState", "title"]); +const allProperties = new Set([ + "audible", + "discarded", + "favIconUrl", + "hidden", + "isarticle", + "mutedInfo", + "pinned", + "sharingState", + "status", + "title", +]); +const restricted = new Set(["url", "favIconUrl", "title"]); + +class TabsUpdateFilterEventManager extends EventManager { + constructor(context, eventName) { + let {extension} = context; + let {tabManager} = extension; + + let register = (fire, filterProps) => { + let filter = {...filterProps}; + if (filter.urls) { + filter.urls = new MatchPatternSet(filter.urls); + } + let needsModified = true; + if (filter.properties) { + // Default is to listen for all events. + needsModified = filter.properties.some(p => allAttrs.has(p)); + filter.properties = new Set(filter.properties); + } else { + filter.properties = allProperties; + } + + function sanitize(extension, changeInfo) { + let result = {}; + let nonempty = false; + let hasTabs = extension.hasPermission("tabs"); + for (let prop in changeInfo) { + if (hasTabs || !restricted.has(prop)) { + nonempty = true; + result[prop] = changeInfo[prop]; + } + } + return nonempty && result; + } + + function getWindowID(windowId) { + if (windowId === WINDOW_ID_CURRENT) { + return windowTracker.getId(windowTracker.topWindow); + } + return windowId; + } + + function matchFilters(tab, changed) { + if (!filterProps) { + return true; + } + if (filter.tabId != null && tab.id != filter.tabId) { + return false; + } + if (filter.windowId != null && tab.windowId != getWindowID(filter.windowId)) { + return false; + } + if (filter.urls) { + // We check permission first because tab.uri is null if !hasTabPermission. + return tab.hasTabPermission && filter.urls.matches(tab.uri); + } + return true; + } + + let fireForTab = (tab, changed) => { + if (!matchFilters(tab, changed)) { + return; + } + + let changeInfo = sanitize(extension, changed); + if (changeInfo) { + fire.async(tab.id, changeInfo, tab.convert()); + } + }; + + let listener = event => { + let needed = []; + if (event.type == "TabAttrModified") { + let changed = event.detail.changed; + if (changed.includes("image") && filter.properties.has("favIconUrl")) { + needed.push("favIconUrl"); + } + if (changed.includes("muted") && filter.properties.has("mutedInfo")) { + needed.push("mutedInfo"); + } + if (changed.includes("soundplaying") && filter.properties.has("audible")) { + needed.push("audible"); + } + if (changed.includes("label") && filter.properties.has("title")) { + needed.push("title"); + } + if (changed.includes("sharing") && filter.properties.has("sharingState")) { + needed.push("sharingState"); + } + } else if (event.type == "TabPinned") { + needed.push("pinned"); + } else if (event.type == "TabUnpinned") { + needed.push("pinned"); + } else if (event.type == "TabBrowserInserted" && + !event.detail.insertedOnTabCreation) { + needed.push("discarded"); + } else if (event.type == "TabBrowserDiscarded") { + needed.push("discarded"); + } else if (event.type == "TabShow") { + needed.push("hidden"); + } else if (event.type == "TabHide") { + needed.push("hidden"); + } + + let tab = tabManager.getWrapper(event.originalTarget); + + let changeInfo = {}; + for (let prop of needed) { + changeInfo[prop] = tab[prop]; + } + + fireForTab(tab, changeInfo); + }; + + let statusListener = ({browser, status, url}) => { + let {gBrowser} = browser.ownerGlobal; + let tabElem = gBrowser.getTabForBrowser(browser); + if (tabElem) { + let changed = {status}; + if (url) { + changed.url = url; + } + + fireForTab(tabManager.wrapTab(tabElem), changed); + } + }; + + let isArticleChangeListener = (messageName, message) => { + let {gBrowser} = message.target.ownerGlobal; + let nativeTab = gBrowser.getTabForBrowser(message.target); + + if (nativeTab) { + let tab = tabManager.getWrapper(nativeTab); + fireForTab(tab, {isArticle: message.data.isArticle}); + } + }; + + let listeners = new Map(); + if (filter.properties.has("status")) { + listeners.set("status", statusListener); + } + if (needsModified) { + listeners.set("TabAttrModified", listener); + } + if (filter.properties.has("pinned")) { + listeners.set("TabPinned", listener); + listeners.set("TabUnpinned", listener); + } + if (filter.properties.has("discarded")) { + listeners.set("TabBrowserInserted", listener); + listeners.set("TabBrowserDiscarded", listener); + } + if (filter.properties.has("hidden")) { + listeners.set("TabShow", listener); + listeners.set("TabHide", listener); + } + + for (let [name, listener] of listeners) { + windowTracker.addListener(name, listener); + } + + if (filter.properties.has("isarticle")) { + tabTracker.on("tab-isarticle", isArticleChangeListener); + } + + return () => { + for (let [name, listener] of listeners) { + windowTracker.removeListener(name, listener); + } + + if (filter.properties.has("isarticle")) { + tabTracker.off("tab-isarticle", isArticleChangeListener); + } + }; + }; + + super(context, eventName, register); + } + + addListener(callback, filter) { + let {extension} = this.context; + if (filter && filter.urls && + (!extension.hasPermission("tabs") && !extension.hasPermission("activeTab"))) { + Cu.reportError("Url filtering in tabs.onUpdated requires \"tabs\" or \"activeTab\" permission."); + return false; + } + return super.addListener(callback, filter); + } +} + this.tabs = class extends ExtensionAPI { static onUpdate(id, manifest) { if (!manifest.permissions || !manifest.permissions.includes("tabHide")) { @@ -254,117 +457,7 @@ }; }).api(), - onUpdated: new EventManager(context, "tabs.onUpdated", fire => { - const restricted = ["url", "favIconUrl", "title"]; - - function sanitize(extension, changeInfo) { - let result = {}; - let nonempty = false; - for (let prop in changeInfo) { - if (extension.hasPermission("tabs") || !restricted.includes(prop)) { - nonempty = true; - result[prop] = changeInfo[prop]; - } - } - return [nonempty, result]; - } - - let fireForTab = (tab, changed) => { - let [needed, changeInfo] = sanitize(extension, changed); - if (needed) { - fire.async(tab.id, changeInfo, tab.convert()); - } - }; - - let listener = event => { - let needed = []; - if (event.type == "TabAttrModified") { - let changed = event.detail.changed; - if (changed.includes("image")) { - needed.push("favIconUrl"); - } - if (changed.includes("muted")) { - needed.push("mutedInfo"); - } - if (changed.includes("soundplaying")) { - needed.push("audible"); - } - if (changed.includes("label")) { - needed.push("title"); - } - if (changed.includes("sharing")) { - needed.push("sharingState"); - } - } else if (event.type == "TabPinned") { - needed.push("pinned"); - } else if (event.type == "TabUnpinned") { - needed.push("pinned"); - } else if (event.type == "TabBrowserInserted" && - !event.detail.insertedOnTabCreation) { - needed.push("discarded"); - } else if (event.type == "TabBrowserDiscarded") { - needed.push("discarded"); - } else if (event.type == "TabShow") { - needed.push("hidden"); - } else if (event.type == "TabHide") { - needed.push("hidden"); - } - - let tab = tabManager.getWrapper(event.originalTarget); - let changeInfo = {}; - for (let prop of needed) { - changeInfo[prop] = tab[prop]; - } - - fireForTab(tab, changeInfo); - }; - - let statusListener = ({browser, status, url}) => { - let {gBrowser} = browser.ownerGlobal; - let tabElem = gBrowser.getTabForBrowser(browser); - if (tabElem) { - let changed = {status}; - if (url) { - changed.url = url; - } - - fireForTab(tabManager.wrapTab(tabElem), changed); - } - }; - - let isArticleChangeListener = (messageName, message) => { - let {gBrowser} = message.target.ownerGlobal; - let nativeTab = gBrowser.getTabForBrowser(message.target); - - if (nativeTab) { - let tab = tabManager.getWrapper(nativeTab); - fireForTab(tab, {isArticle: message.data.isArticle}); - } - }; - - windowTracker.addListener("status", statusListener); - windowTracker.addListener("TabAttrModified", listener); - windowTracker.addListener("TabPinned", listener); - windowTracker.addListener("TabUnpinned", listener); - windowTracker.addListener("TabBrowserInserted", listener); - windowTracker.addListener("TabBrowserDiscarded", listener); - windowTracker.addListener("TabShow", listener); - windowTracker.addListener("TabHide", listener); - - tabTracker.on("tab-isarticle", isArticleChangeListener); - - return () => { - windowTracker.removeListener("status", statusListener); - windowTracker.removeListener("TabAttrModified", listener); - windowTracker.removeListener("TabPinned", listener); - windowTracker.removeListener("TabUnpinned", listener); - windowTracker.removeListener("TabBrowserInserted", listener); - windowTracker.removeListener("TabBrowserDiscarded", listener); - windowTracker.removeListener("TabShow", listener); - windowTracker.removeListener("TabHide", listener); - tabTracker.off("tab-isarticle", isArticleChangeListener); - }; - }).api(), + onUpdated: new TabsUpdateFilterEventManager(context, "tabs.onUpdated").api(), create(createProperties) { return new Promise((resolve, reject) => { diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/ext-url-overrides.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/ext-url-overrides.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/ext-url-overrides.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/ext-url-overrides.js 2018-03-14 13:35:15.000000000 +0000 @@ -5,12 +5,14 @@ "use strict"; -ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore", - "resource://gre/modules/ExtensionSettingsStore.jsm"); ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); +ChromeUtils.defineModuleGetter(this, "BrowserUtils", + "resource://gre/modules/BrowserUtils.jsm"); ChromeUtils.defineModuleGetter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm"); +ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore", + "resource://gre/modules/ExtensionSettingsStore.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService", "@mozilla.org/browser/aboutnewtab-service;1", @@ -20,11 +22,29 @@ const NEW_TAB_SETTING_NAME = "newTabURL"; const NEW_TAB_CONFIRMED_TYPE = "newTabNotification"; +XPCOMUtils.defineLazyGetter(this, "strBundle", function() { + return Services.strings.createBundle("chrome://global/locale/extensions.properties"); +}); + function userWasNotified(extensionId) { let setting = ExtensionSettingsStore.getSetting(NEW_TAB_CONFIRMED_TYPE, extensionId); return setting && setting.value; } +function getAddonDetails(doc, addon) { + const defaultIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg"; + + let image = doc.createElement("image"); + image.setAttribute("src", addon.iconURL || defaultIcon); + image.classList.add("extension-controlled-icon"); + + let addonDetails = doc.createDocumentFragment(); + addonDetails.appendChild(image); + addonDetails.appendChild(doc.createTextNode(" " + addon.name)); + + return addonDetails; +} + function replaceUrlInTab(gBrowser, tab, url) { let loaded = new Promise(resolve => { windowTracker.addListener("progress", { @@ -58,6 +78,16 @@ let win = windowTracker.getCurrentWindow({}); let doc = win.document; let panel = doc.getElementById("extension-notification-panel"); + let addon = await AddonManager.getAddonByID(item.id); + + let description = doc.getElementById("extension-new-tab-notification-description"); + while (description.firstChild) { + description.firstChild.remove(); + } + let message = strBundle.GetStringFromName("newTabControlled.message2"); + let addonDetails = getAddonDetails(doc, addon); + description.appendChild( + BrowserUtils.getLocalizedFragment(doc, message, addonDetails)); // Setup the command handler. let handleCommand = async (event) => { @@ -72,7 +102,6 @@ // 2. Now that this tab isn't associated with the add-on, disable the add-on // 3. Replace the tab's URL with the new New Tab URL ExtensionSettingsStore.removeSetting(NEW_TAB_CONFIRMED_TYPE, item.id); - let addon = await AddonManager.getAddonByID(item.id); let gBrowser = win.gBrowser; let tab = gBrowser.selectedTab; await replaceUrlInTab(gBrowser, tab, "about:blank"); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/schemas/tabs.json firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/schemas/tabs.json --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/schemas/tabs.json 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/schemas/tabs.json 2018-03-14 13:35:15.000000000 +0000 @@ -288,6 +288,46 @@ "type": "string", "enum": ["normal", "popup", "panel", "app", "devtools"], "description": "The type of window." + }, + { + "id": "UpdatePropertyName", + "type": "string", + "enum": [ + "audible", + "discarded", + "favIconUrl", + "hidden", + "isarticle", + "mutedInfo", + "pinned", + "sharingState", + "status", + "title" + ], + "description": "Event names supported in onUpdated." + }, + { + "id": "UpdateFilter", + "type": "object", + "description": "An object describing filters to apply to tabs.onUpdated events.", + "properties": { + "urls": { + "type": "array", + "description": "A list of URLs or URL patterns. Events that cannot match any of the URLs will be filtered out. Filtering with urls requires the \"tabs\" or \"activeTab\" permission.", + "optional": true, + "items": { "type": "string" }, + "minItems": 1 + }, + "properties": { + "type": "array", + "optional": true, + "description": "A list of property names. Events that do not match any of the names will be filtered out.", + "items": { "$ref": "UpdatePropertyName" }, + "minItems": 1 + }, + "tabId": { "type": "integer", "optional": true }, + "windowId": { "type": "integer", "optional": true } + } } ], "properties": { @@ -1400,6 +1440,14 @@ "name": "tab", "description": "Gives the state of the tab that was updated." } + ], + "extraParameters": [ + { + "$ref": "UpdateFilter", + "name": "filter", + "optional": true, + "description": "A set of filters that restricts the events that will be sent to this listener." + } ] }, { diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/browser/browser-common.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/browser/browser-common.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/browser/browser-common.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/browser/browser-common.ini 2018-03-14 13:35:15.000000000 +0000 @@ -178,6 +178,7 @@ [browser_ext_tabs_move_window_pinned.js] [browser_ext_tabs_onHighlighted.js] [browser_ext_tabs_onUpdated.js] +[browser_ext_tabs_onUpdated_filter.js] [browser_ext_tabs_opener.js] [browser_ext_tabs_printPreview.js] [browser_ext_tabs_query.js] diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js 2018-03-14 13:35:15.000000000 +0000 @@ -0,0 +1,241 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +add_task(async function test_filter_url() { + let ext_fail = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.fail(`received unexpected onUpdated event ${JSON.stringify(changeInfo)}`); + }, {urls: ["*://*.mozilla.org/*"]}); + }, + }); + await ext_fail.startup(); + + let ext_perm = ExtensionTestUtils.loadExtension({ + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.fail(`received unexpected onUpdated event without tabs permission`); + }, {urls: ["*://mochi.test/*"]}); + }, + }); + await ext_perm.startup(); + + let ext_ok = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.log(`got onUpdated ${JSON.stringify(changeInfo)}`); + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {urls: ["*://mochi.test/*"]}); + }, + }); + await ext_ok.startup(); + let ok1 = ext_ok.awaitFinish("onUpdated"); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/"); + await ok1; + + await ext_ok.unload(); + await ext_fail.unload(); + await ext_perm.unload(); + + await BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_filter_url_activeTab() { + let ext = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["activeTab"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.fail("should only have notification for activeTab, selectedTab is not activeTab"); + }, {urls: ["*://mochi.test/*"]}); + }, + }); + await ext.startup(); + + let ext2 = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {urls: ["*://mochi.test/*"]}); + }, + }); + await ext2.startup(); + let ok = ext2.awaitFinish("onUpdated"); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/#foreground"); + await Promise.all([ok]); + + await ext.unload(); + await ext2.unload(); + await BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_filter_tabId() { + let ext_fail = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.fail(`received unexpected onUpdated event ${JSON.stringify(changeInfo)}`); + }, {tabId: 12345}); + }, + }); + await ext_fail.startup(); + + let ext_ok = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }); + }, + }); + await ext_ok.startup(); + let ok = ext_ok.awaitFinish("onUpdated"); + + let ext_ok2 = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onCreated.addListener(tab => { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {tabId: tab.id}); + browser.test.log(`Tab specific tab listener on tab ${tab.id}`); + }); + }, + }); + await ext_ok2.startup(); + let ok2 = ext_ok2.awaitFinish("onUpdated"); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/"); + await Promise.all([ok, ok2]); + + await ext_ok.unload(); + await ext_ok2.unload(); + await ext_fail.unload(); + + await BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_filter_windowId() { + let ext_fail = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.fail(`received unexpected onUpdated event ${JSON.stringify(changeInfo)}`); + }, {windowId: 12345}); + }, + }); + await ext_fail.startup(); + + let ext_ok = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {windowId: browser.windows.WINDOW_ID_CURRENT}); + }, + }); + await ext_ok.startup(); + let ok = ext_ok.awaitFinish("onUpdated"); + + let ext_ok2 = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + async background() { + let window = await browser.windows.getCurrent(); + browser.test.log(`Window specific tab listener on window ${window.id}`); + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {windowId: window.id}); + browser.test.sendMessage("ready"); + }, + }); + await ext_ok2.startup(); + await ext_ok2.awaitMessage("ready"); + let ok2 = ext_ok2.awaitFinish("onUpdated"); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/"); + await Promise.all([ok, ok2]); + + await ext_ok.unload(); + await ext_ok2.unload(); + await ext_fail.unload(); + + await BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_filter_property() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["tabs"], + }, + background() { + // We expect only status updates, anything else is a failure. + let properties = new Set([ + "audible", + "discarded", + "favIconUrl", + "hidden", + "isarticle", + "mutedInfo", + "pinned", + "sharingState", + "title", + ]); + browser.tabs.onUpdated.addListener((tabId, changeInfo) => { + browser.test.log(`got onUpdated ${JSON.stringify(changeInfo)}`); + browser.test.assertTrue(!!changeInfo.status, "changeInfo has status"); + if (Object.keys(changeInfo).some(p => properties.has(p))) { + browser.test.fail(`received unexpected onUpdated event ${JSON.stringify(changeInfo)}`); + } + if (changeInfo.status === "complete") { + browser.test.notifyPass("onUpdated"); + } + }, {properties: ["status"]}); + }, + }); + await extension.startup(); + let ok = extension.awaitFinish("onUpdated"); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/"); + await ok; + + await extension.unload(); + + await BrowserTestUtils.removeTab(tab); +}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/mochitest/test_ext_all_apis.html firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/mochitest/test_ext_all_apis.html --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/extensions/test/mochitest/test_ext_all_apis.html 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/extensions/test/mochitest/test_ext_all_apis.html 2018-03-14 13:35:15.000000000 +0000 @@ -19,6 +19,7 @@ "tabs.MutedInfoReason", "tabs.TAB_ID_NONE", "tabs.TabStatus", + "tabs.UpdatePropertyName", "tabs.WindowType", "tabs.ZoomSettingsMode", "tabs.ZoomSettingsScope", diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/feeds/FeedConverter.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/feeds/FeedConverter.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/feeds/FeedConverter.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/feeds/FeedConverter.js 2018-03-14 13:35:15.000000000 +0000 @@ -4,7 +4,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); -ChromeUtils.import("resource://gre/modules/debug.js"); ChromeUtils.import("resource://gre/modules/Services.jsm"); function LOG(str) { @@ -391,8 +390,10 @@ * See nsIFeedResultService.idl */ addFeedResult(feedResult) { - NS_ASSERT(feedResult.uri != null, "null URI!"); - NS_ASSERT(feedResult.uri != null, "null feedResult!"); + if (feedResult.uri == null) + throw new Error("null URI!"); + if (feedResult.uri == null) + throw new Error("null feedResult!"); let spec = feedResult.uri.spec; if (!this._results[spec]) this._results[spec] = []; @@ -403,7 +404,8 @@ * See nsIFeedResultService.idl */ getFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); + if (uri == null) + throw new Error("null URI!"); let resultList = this._results[uri.spec]; for (let result of resultList) { if (result.uri == uri) @@ -416,7 +418,8 @@ * See nsIFeedResultService.idl */ removeFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); + if (uri == null) + throw new Error("null URI!"); let resultList = this._results[uri.spec]; if (!resultList) return; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/bookmarkProperties.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/bookmarkProperties.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/bookmarkProperties.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/bookmarkProperties.js 2018-03-14 13:35:15.000000000 +0000 @@ -132,7 +132,8 @@ return this._strings.getString("dialogTitleAddLivemark"); // add folder - NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type"); + if (this._itemType != BOOKMARK_FOLDER) + throw new Error("Unknown item type"); if (this._URIs.length) return this._strings.getString("dialogTitleAddMulti"); @@ -152,7 +153,8 @@ this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT; this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : []; if (this._action == ACTION_ADD) { - NS_ASSERT("type" in dialogInfo, "missing type property for add action"); + if (!("type" in dialogInfo)) + throw new Error("missing type property for add action"); if ("title" in dialogInfo) this._title = dialogInfo.title; @@ -171,8 +173,8 @@ case "bookmark": this._itemType = BOOKMARK_ITEM; if ("uri" in dialogInfo) { - NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI, - "uri property should be a uri object"); + if (!(dialogInfo.uri instanceof Ci.nsIURI)) + throw new Error("uri property should be a uri object"); this._uri = dialogInfo.uri; if (typeof(this._title) != "string") { this._title = await PlacesUtils.history.fetch(this._uri) || diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/browserPlacesViews.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/browserPlacesViews.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/browserPlacesViews.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/browserPlacesViews.js 2018-03-14 13:35:15.000000000 +0000 @@ -1000,6 +1000,8 @@ this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false); } + this._updatingNodesVisibility = false; + PlacesViewBase.call(this, aPlace); Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS") @@ -1253,34 +1255,55 @@ this._updateNodesVisibilityTimer = this._setTimer(100); }, - _updateNodesVisibilityTimerCallback: function PT__updateNodesVisibilityTimerCallback() { - let scrollRect = this._rootElt.getBoundingClientRect(); + async _updateNodesVisibilityTimerCallback() { + if (this._updatingNodesVisibility || window.closed) { + return; + } + this._updatingNodesVisibility = true; + + let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + + let scrollRect = + await window.promiseDocumentFlushed(() => dwu.getBoundsWithoutFlushing(this._rootElt)); + let childOverflowed = false; - for (let child of this._rootElt.childNodes) { - // Once a child overflows, all the next ones will. - if (!childOverflowed) { - let childRect = child.getBoundingClientRect(); - childOverflowed = this.isRTL ? (childRect.left < scrollRect.left) - : (childRect.right > scrollRect.right); + + // We're about to potentially update a bunch of nodes, so we do it + // in a requestAnimationFrame so that other JS that's might execute + // in the same tick can avoid flushing styles and layout for these + // changes. + window.requestAnimationFrame(() => { + for (let child of this._rootElt.childNodes) { + // Once a child overflows, all the next ones will. + if (!childOverflowed) { + let childRect = dwu.getBoundsWithoutFlushing(child); + childOverflowed = this.isRTL ? (childRect.left < scrollRect.left) + : (childRect.right > scrollRect.right); + } + + if (childOverflowed) { + child.removeAttribute("image"); + child.style.visibility = "hidden"; + } else { + let icon = child._placesNode.icon; + if (icon) + child.setAttribute("image", icon); + child.style.visibility = "visible"; + } } - if (childOverflowed) { - child.removeAttribute("image"); - child.style.visibility = "hidden"; - } else { - let icon = child._placesNode.icon; - if (icon) - child.setAttribute("image", icon); - child.style.visibility = "visible"; + // We rebuild the chevron on popupShowing, so if it is open + // we must update it. + if (!this._chevron.collapsed && this._chevron.open) { + this._updateChevronPopupNodesVisibility(); } - } - // We rebuild the chevron on popupShowing, so if it is open - // we must update it. - if (!this._chevron.collapsed && this._chevron.open) - this._updateChevronPopupNodesVisibility(); - let event = new CustomEvent("BookmarksToolbarVisibilityUpdated", {bubbles: true}); - this._viewElt.dispatchEvent(event); + let event = new CustomEvent("BookmarksToolbarVisibilityUpdated", {bubbles: true}); + this._viewElt.dispatchEvent(event); + }); + + this._updatingNodesVisibility = false; }, nodeInserted: @@ -1608,9 +1631,7 @@ notify: function PT_notify(aTimer) { if (aTimer == this._updateNodesVisibilityTimer) { this._updateNodesVisibilityTimer = null; - // Bug 1440070: This should use promiseDocumentFlushed, so that - // _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush. - window.requestAnimationFrame(this._updateNodesVisibilityTimerCallback.bind(this)); + this._updateNodesVisibilityTimerCallback(); } else if (aTimer == this._ibTimer) { // * Timer to turn off indicator bar. this._dropIndicator.collapsed = true; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/controller.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/controller.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/controller.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/controller.js 2018-03-14 13:35:15.000000000 +0000 @@ -661,15 +661,6 @@ }, /** - * This method can be run on a URI parameter to ensure that it didn't - * receive a string instead of an nsIURI object. - */ - _assertURINotString: function PC__assertURINotString(value) { - NS_ASSERT((typeof(value) == "object") && !(value instanceof String), - "This method should be passed a URI as a nsIURI object, not as a string."); - }, - - /** * Reloads the selected livemark if any. */ reloadSelectedLivemark: function PC_reloadSelectedLivemark() { @@ -801,7 +792,8 @@ * @return {Integer} The total number of items affected. */ async _removeRange(range, transactions, removedFolders) { - NS_ASSERT(transactions instanceof Array, "Must pass a transactions array"); + if (!(transactions instanceof Array)) + throw new Error("Must pass a transactions array"); if (!removedFolders) removedFolders = []; @@ -869,12 +861,7 @@ return totalItems; }, - /** - * Removes the set of selected ranges from bookmarks. - * @param txnName - * See |remove|. - */ - async _removeRowsFromBookmarks(txnName) { + async _removeRowsFromBookmarks() { let ranges = this._view.removableSelectionRanges; let transactions = []; let removedFolders = []; @@ -929,8 +916,8 @@ let query = aContainerNode.getQueries()[0]; let beginTime = query.beginTime; let endTime = query.endTime; - NS_ASSERT(query && beginTime && endTime, - "A valid date container query should exist!"); + if (!query || !beginTime || !endTime) + throw new Error("A valid date container query should exist!"); // We want to exclude beginTime from the removal because // removePagesByTimeframe includes both extremes, while date containers // exclude the lower extreme. So, if we would not exclude it, we would @@ -941,31 +928,26 @@ /** * Removes the selection - * @param aTxnName - * A name for the transaction if this is being performed - * as part of another operation. */ - async remove(aTxnName) { + async remove() { if (!this._hasRemovableSelection()) return; - NS_ASSERT(aTxnName !== undefined, "Must supply Transaction Name"); - var root = this._view.result.root; if (PlacesUtils.nodeIsFolder(root)) { - await this._removeRowsFromBookmarks(aTxnName); + await this._removeRowsFromBookmarks(); } else if (PlacesUtils.nodeIsQuery(root)) { var queryType = PlacesUtils.asQuery(root).queryOptions.queryType; if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS) { - await this._removeRowsFromBookmarks(aTxnName); + await this._removeRowsFromBookmarks(); } else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) { this._removeRowsFromHistory(); } else { - NS_ASSERT(false, "implement support for QUERY_TYPE_UNIFIED"); + throw new Error("implement support for QUERY_TYPE_UNIFIED"); } } else - NS_ASSERT(false, "unexpected root"); + throw new Error("unexpected root"); }, /** @@ -1261,7 +1243,8 @@ * The container were we are want to drop */ disallowInsertion(container) { - NS_ASSERT(container, "empty container"); + if (!container) + throw new Error("empty container"); // Allow dropping into Tag containers and editable folders. return !PlacesUtils.nodeIsTagQuery(container) && (!PlacesUtils.nodeIsFolder(container) || diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/places.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/places.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/places.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/places.js 2018-03-14 13:35:15.000000000 +0000 @@ -1002,21 +1002,21 @@ * null if the caller should just append to the popup. */ _clean: function VM__clean(popup, startID, endID) { - if (endID) - NS_ASSERT(startID, "meaningless to have valid endID and null startID"); + if (endID && !startID) + throw new Error("meaningless to have valid endID and null startID"); if (startID) { var startElement = document.getElementById(startID); - NS_ASSERT(startElement.parentNode == - popup, "startElement is not in popup"); - NS_ASSERT(startElement, - "startID does not correspond to an existing element"); + if (startElement.parentNode != popup) + throw new Error("startElement is not in popup"); + if (!startElement) + throw new Error("startID does not correspond to an existing element"); var endElement = null; if (endID) { endElement = document.getElementById(endID); - NS_ASSERT(endElement.parentNode == popup, - "endElement is not in popup"); - NS_ASSERT(endElement, - "endID does not correspond to an existing element"); + if (endElement.parentNode != popup) + throw new Error("endElement is not in popup"); + if (!endElement) + throw new Error("endID does not correspond to an existing element"); } while (startElement.nextSibling != endElement) popup.removeChild(startElement.nextSibling); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/sidebarUtils.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/sidebarUtils.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/sidebarUtils.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/sidebarUtils.js 2018-03-14 13:35:15.000000000 +0000 @@ -43,7 +43,7 @@ var openInTabs = isContainer && (aEvent.button == 1 || (aEvent.button == 0 && modifKey)) && - PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row)); + PlacesUtils.hasChildURIs(aTree.view.nodeForTreeIndex(cell.row)); if (aEvent.button == 0 && isContainer && !openInTabs) { tbo.view.toggleOpenState(cell.row); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/treeView.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/treeView.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/treeView.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/treeView.js 2018-03-14 13:35:15.000000000 +0000 @@ -651,7 +651,7 @@ // nsINavHistoryResultObserver nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); + console.assert(this._result, "Got a notification but have no result!"); if (!this._tree || !this._result) return; @@ -725,7 +725,7 @@ * change for visits, and date sorting is the only time things are collapsed. */ nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); + console.assert(this._result, "Got a notification but have no result!"); if (!this._tree || !this._result) return; @@ -780,7 +780,7 @@ nodeMoved: function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) { - NS_ASSERT(this._result, "Got a notification but have no result!"); + console.assert(this._result, "Got a notification but have no result!"); if (!this._tree || !this._result) return; @@ -829,7 +829,7 @@ _invalidateCellValue: function PTV__invalidateCellValue(aNode, aColumnType) { - NS_ASSERT(this._result, "Got a notification but have no result!"); + console.assert(this._result, "Got a notification but have no result!"); if (!this._tree || !this._result) return; @@ -976,7 +976,7 @@ }, invalidateContainer: function PTV_invalidateContainer(aContainer) { - NS_ASSERT(this._result, "Need to have a result to update"); + console.assert(this._result, "Need to have a result to update"); if (!this._tree) return; diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/tree.xml firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/tree.xml --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/content/tree.xml 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/content/tree.xml 2018-03-14 13:35:15.000000000 +0000 @@ -209,7 +209,7 @@ } var container = this.result.root; - NS_ASSERT(container, "No result, cannot select place URI!"); + console.assert(container, "No result, cannot select place URI!"); if (!container) return; @@ -479,7 +479,7 @@ var resultview = this.view; var container = result.root; var dropNearNode = null; - NS_ASSERT(container, "null container"); + console.assert(container, "null container"); // When there's no selection, assume the container is the container // the view is populated from (i.e. the result's itemId). if (index != -1) { diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_0_library_left_pane_migration.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_0_library_left_pane_migration.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_0_library_left_pane_migration.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_0_library_left_pane_migration.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* 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/. */ - -"use strict"; - -/** - * Test we correctly migrate Library left pane to the latest version. - * Note: this test MUST be the first between browser chrome tests, or results - * of next tests could be unexpected due to PlacesUIUtils getters. - */ - -const TEST_URI = "http://www.mozilla.org/"; - -add_task(async function() { - ok(PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION > 0, - "Left pane version in chrome context, current version is: " + PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION ); - - // Check if we have any left pane folder already set, remove it eventually. - let leftPaneItems = PlacesUtils.annotations - .getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - if (leftPaneItems.length > 0) { - // The left pane has already been created, touching it now would cause - // next tests to rely on wrong values (and possibly crash) - is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder"); - // Check version. - let version = PlacesUtils.annotations.getItemAnnotation(leftPaneItems[0], - PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, "Left pane version is actual"); - ok(true, "left pane has already been created, skipping test"); - return; - } - - // Create a fake left pane folder with an old version (current version - 1). - let folder = await PlacesUtils.bookmarks.insert({ - parentGuid: PlacesUtils.bookmarks.rootGuid, - index: PlacesUtils.bookmarks.DEFAULT_INDEX, - type: PlacesUtils.bookmarks.TYPE_FOLDER, - title: "" - }); - - let folderId = await PlacesUtils.promiseItemId(folder.guid); - PlacesUtils.annotations.setItemAnnotation(folderId, - PlacesUIUtils.ORGANIZER_FOLDER_ANNO, - PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1, - 0, - PlacesUtils.annotations.EXPIRE_NEVER); - - // Check fake left pane root has been correctly created. - leftPaneItems = - PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder"); - is(leftPaneItems[0], folderId, "left pane root itemId is correct"); - - // Check version. - let version = PlacesUtils.annotations.getItemAnnotation(folderId, - PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION - 1, "Left pane version correctly set"); - - // Open Library, this will upgrade our left pane version. - let organizer = await promiseLibrary(); - - // Check left pane. - ok(PlacesUIUtils.leftPaneFolderId > 0, "Left pane folder correctly created"); - leftPaneItems = - PlacesUtils.annotations.getItemsWithAnnotation(PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - is(leftPaneItems.length, 1, "We correctly have only 1 left pane folder"); - let leftPaneRoot = leftPaneItems[0]; - is(leftPaneRoot, PlacesUIUtils.leftPaneFolderId, - "leftPaneFolderId getter has correct value"); - - // Check version has been upgraded. - version = PlacesUtils.annotations.getItemAnnotation(leftPaneRoot, - PlacesUIUtils.ORGANIZER_FOLDER_ANNO); - is(version, PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION, - "Left pane version has been correctly upgraded"); - - // Check left pane is populated. - organizer.PlacesOrganizer.selectLeftPaneBuiltIn("History"); - is(organizer.PlacesOrganizer._places.selectedNode.itemId, - PlacesUIUtils.leftPaneQueries.History, - "Library left pane is populated and working"); - - await promiseLibraryClosed(organizer); -}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser.ini 2018-03-14 13:35:15.000000000 +0000 @@ -11,7 +11,6 @@ sidebarpanels_click_test_page.html keyword_form.html -[browser_0_library_left_pane_migration.js] [browser_addBookmarkForFrame.js] [browser_bookmark_add_tags.js] skip-if = (os == 'win' && ccov) # Bug 1423667 @@ -68,6 +67,7 @@ subsuite = clipboard [browser_drag_bookmarks_on_toolbar.js] [browser_enable_toolbar_sidebar.js] +skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_forgetthissite_single.js] [browser_history_sidebar_search.js] [browser_library_batch_delete.js] @@ -89,6 +89,8 @@ [browser_library_open_leak.js] [browser_library_openFlatContainer.js] skip-if = (os == 'win' && ccov) # Bug 1423667 +[browser_library_open_bookmark.js] +skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_panel_leak.js] skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_library_search.js] @@ -104,6 +106,8 @@ [browser_remove_bookmarks.js] skip-if = (os == 'win' && ccov) # Bug 1423667 subsuite = clipboard +[browser_sidebar_open_bookmarks.js] +skip-if = (os == 'win' && ccov) # Bug 1423667 [browser_sidebarpanels_click.js] skip-if = (os == 'win' && ccov) || (os == "mac" && debug) # Bug 1423667 [browser_sort_in_library.js] diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_library_open_bookmark.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_library_open_bookmark.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_library_open_bookmark.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_library_open_bookmark.js 2018-03-14 13:35:15.000000000 +0000 @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +/** + * Test that the a bookmark can be opened from the Library by mouse double click. + */ +"use strict"; + +const TEST_URL = "about:buildconfig"; + +add_task(async function test_open_bookmark_from_library() { + let bm = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: TEST_URL, + title: TEST_URL, + }); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank"); + + let gLibrary = await promiseLibrary("UnfiledBookmarks"); + + registerCleanupFunction(async function() { + await promiseLibraryClosed(gLibrary); + await PlacesUtils.bookmarks.eraseEverything(); + await BrowserTestUtils.removeTab(tab); + }); + + let bmLibrary = gLibrary.ContentTree.view.view.nodeForTreeIndex(0); + Assert.equal(bmLibrary.title, bm.title, "Found bookmark in the right pane"); + + gLibrary.ContentTree.view.selectNode(bmLibrary); + synthesizeClickOnSelectedTreeCell(gLibrary.ContentTree.view, + { clickCount: 2 }); + + await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, TEST_URL); + Assert.ok(true, "Expected tab was loaded"); +}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_sidebar_open_bookmarks.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_sidebar_open_bookmarks.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/browser/browser_sidebar_open_bookmarks.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/browser/browser_sidebar_open_bookmarks.js 2018-03-14 13:35:15.000000000 +0000 @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var gBms; + +add_task(async function setup() { + gBms = await PlacesUtils.bookmarks.insertTree({ + guid: PlacesUtils.bookmarks.unfiledGuid, + children: [{ + title: "bm1", + url: "about:buildconfig" + }, { + title: "bm2", + url: "about:mozilla", + }] + }); + + registerCleanupFunction(async () => { + await PlacesUtils.bookmarks.eraseEverything(); + }); +}); + +add_task(async function test_open_bookmark_from_sidebar() { + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + await withSidebarTree("bookmarks", async (tree) => { + tree.selectItems([gBms[0].guid]); + + let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, + false, gBms[0].url + ); + + tree.controller.doCommand("placesCmd_open"); + + await loadedPromise; + + // An assert to make the test happy. + Assert.ok(true, "The bookmark was loaded successfully."); + }); + + await BrowserTestUtils.removeTab(tab); +}); + +add_task(async function test_open_bookmark_folder_from_sidebar() { + await withSidebarTree("bookmarks", async (tree) => { + tree.selectItems([PlacesUtils.bookmarks.virtualUnfiledGuid]); + + Assert.equal(tree.view.selection.getRangeCount(), 1, + "Should only have one range selected"); + + let loadedPromises = []; + + for (let bm of gBms) { + loadedPromises.push(BrowserTestUtils.waitForNewTab(gBrowser, + bm.url, false, true)); + } + + synthesizeClickOnSelectedTreeCell(tree, {button: 1}); + + let tabs = await Promise.all(loadedPromises); + + for (let tab of tabs) { + await BrowserTestUtils.removeTab(tab); + } + }); +}); diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/chrome/chrome.ini firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/chrome/chrome.ini --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/chrome/chrome.ini 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/chrome/chrome.ini 2018-03-14 13:35:15.000000000 +0000 @@ -2,7 +2,6 @@ support-files = head.js [test_0_bug510634.xul] -[test_0_multiple_left_pane.xul] [test_bug1163447_selectItems_through_shortcut.xul] skip-if = (os == 'win' && ccov) # Bug 1423667 [test_bug427633_no_newfolder_if_noip.xul] diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/chrome/test_0_multiple_left_pane.xul firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/chrome/test_0_multiple_left_pane.xul --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/places/tests/chrome/test_0_multiple_left_pane.xul 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/places/tests/chrome/test_0_multiple_left_pane.xul 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/preferences/in-content/main.js firefox-trunk-61.0~a1~hg20180314r408098/browser/components/preferences/in-content/main.js --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/preferences/in-content/main.js 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/preferences/in-content/main.js 2018-03-14 13:35:15.000000000 +0000 @@ -467,15 +467,12 @@ OS.File.stat(ignoreSeparateProfile).then(() => separateProfileModeCheckbox.checked = false, () => separateProfileModeCheckbox.checked = true); - if (!Services.prefs.getBoolPref("identity.fxaccounts.enabled")) { - document.getElementById("sync-dev-edition-root").hidden = true; - return; + if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) { + document.getElementById("sync-dev-edition-root").hidden = false; + fxAccounts.getSignedInUser().then(data => { + document.getElementById("getStarted").selectedIndex = data ? 1 : 0; + }).catch(Cu.reportError); } - - fxAccounts.getSignedInUser().then(data => { - document.getElementById("getStarted").selectedIndex = data ? 1 : 0; - }) - .catch(Cu.reportError); } // Initialize the Firefox Updates section. diff -Nru firefox-trunk-60.0~a1~hg20180311r407525/browser/components/preferences/in-content/main.xul firefox-trunk-61.0~a1~hg20180314r408098/browser/components/preferences/in-content/main.xul --- firefox-trunk-60.0~a1~hg20180311r407525/browser/components/preferences/in-content/main.xul 2018-03-11 11:24:59.000000000 +0000 +++ firefox-trunk-61.0~a1~hg20180314r408098/browser/components/preferences/in-content/main.xul 2018-03-14 13:35:15.000000000 +0000 @@ -33,7 +33,7 @@ - + -