diff --git a/.eslintignore b/.eslintignore index a68e7c0db582..d055965d2ec1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -83,12 +83,6 @@ devtools/client/commandline/** devtools/client/debugger/** devtools/client/eyedropper/** devtools/client/framework/** -# devtools/client/inspector/shared/*.js files are eslint-clean, so they aren't -# included in the ignore list. -devtools/client/inspector/fonts/** -devtools/client/inspector/test/** -devtools/client/inspector/*.js -!devtools/client/inspector/breadcrumbs.js devtools/client/jsonview/lib/** devtools/client/memory/** devtools/client/netmonitor/test/** diff --git a/addon-sdk/source/test/preferences/no-connections.json b/addon-sdk/source/test/preferences/no-connections.json index 91329b6132fa..5bc55f3a9508 100644 --- a/addon-sdk/source/test/preferences/no-connections.json +++ b/addon-sdk/source/test/preferences/no-connections.json @@ -28,7 +28,7 @@ "extensions.blocklist.url": "http://localhost/extensions-dummy/blocklistURL", "extensions.webservice.discoverURL": "http://localhost/extensions-dummy/discoveryURL", "extensions.getAddons.maxResults": 0, - "services.kinto.base": "http://localhost/dummy-kinto/v1", + "services.blocklist.base": "http://localhost/dummy-kinto/v1", "geo.wifi.uri": "http://localhost/location-dummy/locationURL", "browser.search.geoip.url": "http://localhost/location-dummy/locationURL", "browser.search.isUS": true, diff --git a/autom4te.cache/output.0t b/autom4te.cache/output.0t new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/autom4te.cache/requests b/autom4te.cache/requests new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/autom4te.cache/traces.0t b/autom4te.cache/traces.0t new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 088c4d88f942..4a39d77ca026 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -1087,11 +1087,6 @@ pref("dom.performance.enable_notify_performance_timing", true); pref("b2g.multiscreen.chrome_remote_url", "chrome://b2g/content/shell_remote.html"); pref("b2g.multiscreen.system_remote_url", "index_remote.html"); -// Blocklist service -pref("extensions.blocklist.enabled", true); -pref("extensions.blocklist.interval", 86400); -pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/"); -pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/"); // Because we can't have nice things. #ifdef MOZ_GRAPHENE diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index f12c3938148a..2ea04cd0561c 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -50,38 +50,6 @@ pref("extensions.webservice.discoverURL", "https://discovery.addons.mozilla.org/ pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/list/recommended/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox"); pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/"); -// Blocklist preferences -pref("extensions.blocklist.enabled", true); -// OneCRL freshness checking depends on this value, so if you change it, -// please also update security.onecrl.maximum_staleness_in_seconds. -pref("extensions.blocklist.interval", 86400); -// Controls what level the blocklist switches from warning about items to forcibly -// blocking them. -pref("extensions.blocklist.level", 2); -pref("extensions.blocklist.url", "https://blocklist.addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/"); -pref("extensions.blocklist.detailsURL", "https://www.mozilla.org/%LOCALE%/blocklist/"); -pref("extensions.blocklist.itemURL", "https://blocklist.addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%"); - -// Kinto blocklist preferences -pref("services.kinto.base", "https://firefox.settings.services.mozilla.com/v1"); -pref("services.kinto.changes.path", "/buckets/monitor/collections/changes/records"); -pref("services.kinto.bucket", "blocklists"); -pref("services.kinto.onecrl.collection", "certificates"); -pref("services.kinto.onecrl.checked", 0); -pref("services.kinto.addons.collection", "addons"); -pref("services.kinto.addons.checked", 0); -pref("services.kinto.plugins.collection", "plugins"); -pref("services.kinto.plugins.checked", 0); -pref("services.kinto.gfx.collection", "gfx"); -pref("services.kinto.gfx.checked", 0); - -// for now, let's keep kinto update out of the release channel -#ifdef RELEASE_BUILD -pref("services.kinto.update_enabled", false); -#else -pref("services.kinto.update_enabled", true); -#endif - pref("extensions.update.autoUpdateDefault", true); pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org"); @@ -1211,9 +1179,6 @@ pref("security.insecure_password.ui.enabled", false); // 1 = allow MITM for certificate pinning checks. pref("security.cert_pinning.enforcement_level", 1); -// Required blocklist freshness for OneCRL OCSP bypass -// (default is 1.25x extensions.blocklist.interval, or 30 hours) -pref("security.onecrl.maximum_staleness_in_seconds", 108000); // Override the Gecko-default value of false for Firefox. pref("plain_text.wrap_long_lines", true); diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index adce2f088056..19d1d019eb34 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -1946,3 +1946,27 @@ var BookmarkingUI = { Ci.nsINavBookmarkObserver ]) }; + +var AutoShowBookmarksToolbar = { + init() { + Services.obs.addObserver(this, "autoshow-bookmarks-toolbar", false); + }, + + uninit() { + Services.obs.removeObserver(this, "autoshow-bookmarks-toolbar"); + }, + + observe(subject, topic, data) { + let toolbar = document.getElementById("PersonalToolbar"); + if (!toolbar.collapsed) + return; + + let placement = CustomizableUI.getPlacementOfWidget("personal-bookmarks"); + let area = placement && placement.area; + if (area != CustomizableUI.AREA_BOOKMARKS) + return; + + setToolbarVisibility(toolbar, true); + } +}; + diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 53b1afcc5dba..cc21f68fb26d 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1206,6 +1206,7 @@ var gBrowserInit = { gBrowser.tabContainer.updateVisibility(); BookmarkingUI.init(); + AutoShowBookmarksToolbar.init(); gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false); @@ -1500,6 +1501,7 @@ var gBrowserInit = { IndexedDBPromptHelper.uninit(); LightweightThemeListener.uninit(); PanelUI.uninit(); + AutoShowBookmarksToolbar.uninit(); } // Final window teardown, do this last. @@ -7859,11 +7861,17 @@ TabModalPromptBox.prototype = { const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; let newPrompt = document.createElementNS(XUL_NS, "tabmodalprompt"); let browser = this.browser; - browser.parentNode.appendChild(newPrompt); + browser.parentNode.insertBefore(newPrompt, browser.nextSibling); browser.setAttribute("tabmodalPromptShowing", true); newPrompt.clientTop; // style flush to assure binding is attached + let prompts = this.listPrompts(); + if (prompts.length > 1) { + // Let's hide ourself behind the current prompt. + newPrompt.hidden = true; + } + let principalToAllowFocusFor = this._allowTabFocusByPromptPrincipal; delete this._allowTabFocusByPromptPrincipal; @@ -7898,6 +7906,7 @@ TabModalPromptBox.prototype = { let prompts = this.listPrompts(); if (prompts.length) { let prompt = prompts[prompts.length - 1]; + prompt.hidden = false; prompt.Dialog.setDefaultFocus(); } else { browser.removeAttribute("tabmodalPromptShowing"); diff --git a/browser/base/content/content.js b/browser/base/content/content.js index 9a6f384154f4..d414d618fbb5 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -231,7 +231,7 @@ const SEC_ERROR_OCSP_FUTURE_RESPONSE = SEC_ERROR_BASE + 131; const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132; const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5; -const PREF_KINTO_CLOCK_SKEW_SECONDS = "services.kinto.clock_skew_seconds"; +const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds"; const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.min", "security.tls.version.max", "security.ssl3."]; @@ -280,16 +280,16 @@ var AboutNetAndCertErrorListener = { break; // in case the certificate expired we make sure the system clock - // matches kinto server time + // matches settings server (kinto) time case SEC_ERROR_EXPIRED_CERTIFICATE: case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: case SEC_ERROR_OCSP_FUTURE_RESPONSE: case SEC_ERROR_OCSP_OLD_RESPONSE: case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: - // use Kinto stats if available - if (Services.prefs.getPrefType(PREF_KINTO_CLOCK_SKEW_SECONDS)) { - let difference = Services.prefs.getIntPref(PREF_KINTO_CLOCK_SKEW_SECONDS); + // use blocklist stats if available + if (Services.prefs.getPrefType(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS)) { + let difference = Services.prefs.getIntPref(PREF_BLOCKLIST_CLOCK_SKEW_SECONDS); // if the difference is more than a day if (Math.abs(difference) > 60 * 60 * 24) { diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index bbbb799f3b7b..7be8d98deab6 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -343,8 +343,6 @@ support-files = feed_discovery.html [browser_gZipOfflineChild.js] skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works? support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^ -[browser_openPromptInBackgroundTab.js] -support-files = openPromptOffTimeout.html [browser_overflowScroll.js] [browser_pageInfo.js] skip-if = buildapp == 'mulet' diff --git a/browser/base/content/test/general/browser_aboutCertError.js b/browser/base/content/test/general/browser_aboutCertError.js index 5fd74ebd1772..727c3bfa6f18 100644 --- a/browser/base/content/test/general/browser_aboutCertError.js +++ b/browser/base/content/test/general/browser_aboutCertError.js @@ -105,7 +105,7 @@ add_task(function* checkBadStsCert() { yield BrowserTestUtils.removeTab(gBrowser.selectedTab); }); -const PREF_KINTO_CLOCK_SKEW_SECONDS = "services.kinto.clock_skew_seconds"; +const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds"; add_task(function* checkWrongSystemTimeWarning() { function* setUpPage() { @@ -144,7 +144,7 @@ add_task(function* checkWrongSystemTimeWarning() { let skew = Math.floor((Date.now() - serverDate.getTime()) / 1000); yield new Promise(r => SpecialPowers.pushPrefEnv({set: - [[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r)); + [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r)); info("Loading a bad cert page with a skewed clock"); let message = yield Task.spawn(setUpPage); @@ -165,7 +165,7 @@ add_task(function* checkWrongSystemTimeWarning() { skew = Math.floor((Date.now() - serverDate.getTime()) / 1000); yield new Promise(r => SpecialPowers.pushPrefEnv({set: - [[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r)); + [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r)); info("Loading a bad cert page with a skewed clock"); message = yield Task.spawn(setUpPage); @@ -182,7 +182,7 @@ add_task(function* checkWrongSystemTimeWarning() { // pretend we only have a slightly skewed system time, four hours skew = 60 * 60 * 4; yield new Promise(r => SpecialPowers.pushPrefEnv({set: - [[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r)); + [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r)); info("Loading a bad cert page with an only slightly skewed clock"); message = yield Task.spawn(setUpPage); @@ -194,7 +194,7 @@ add_task(function* checkWrongSystemTimeWarning() { // now pretend we have no skewed system time skew = 0; yield new Promise(r => SpecialPowers.pushPrefEnv({set: - [[PREF_KINTO_CLOCK_SKEW_SECONDS, skew]]}, r)); + [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]}, r)); info("Loading a bad cert page with no skewed clock"); message = yield Task.spawn(setUpPage); diff --git a/browser/base/content/test/tabPrompts/browser.ini b/browser/base/content/test/tabPrompts/browser.ini new file mode 100644 index 000000000000..187ac7c9b14b --- /dev/null +++ b/browser/base/content/test/tabPrompts/browser.ini @@ -0,0 +1,3 @@ +[browser_multiplePrompts.js] +[browser_openPromptInBackgroundTab.js] +support-files = openPromptOffTimeout.html diff --git a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js new file mode 100644 index 000000000000..9991a22541dd --- /dev/null +++ b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js @@ -0,0 +1,66 @@ +"use strict"; + +/* + * This test triggers multiple alerts on one single tab, because it"s possible + * for web content to do so. The behavior is described in bug 1266353. + * + * We assert the presentation of the multiple alerts, ensuring we show only + * the oldest one. + */ +add_task(function*() { + const PROMPTCOUNT = 5; + + let contentScript = function() { + var i = 5; // contentScript has no access to PROMPTCOUNT. + window.addEventListener("message", function() { + i--; + if (i) { + window.postMessage("ping", "*"); + } + alert("Alert countdown #" + i); + }); + window.postMessage("ping", "*"); + }; + let url = "data:text/html," + + let promptsOpenedPromise = new Promise(function(resolve) { + let unopenedPromptCount = PROMPTCOUNT; + Services.obs.addObserver(function observer() { + unopenedPromptCount--; + if (!unopenedPromptCount) { + Services.obs.removeObserver(observer, "tabmodal-dialog-loaded"); + info("Prompts opened."); + resolve(); + } + }, "tabmodal-dialog-loaded", false); + }); + + let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url, true); + info("Tab loaded"); + + yield promptsOpenedPromise; + + let promptsCount = PROMPTCOUNT; + while (promptsCount--) { + let prompts = tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt"); + is(prompts.length, promptsCount + 1, "There should be " + (promptsCount + 1) + " prompt(s)."); + // The oldest should be the first. + let i = 0; + for (let prompt of prompts) { + is(prompt.Dialog.args.text, "Alert countdown #" + i, "The #" + i + " alert should be labelled as such."); + if (i !== promptsCount) { + is(prompt.hidden, true, "This prompt should be hidden."); + i++; + continue; + } + + is(prompt.hidden, false, "The last prompt should not be hidden."); + prompt.onButtonClick(0); + } + } + + let prompts = tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt"); + is(prompts.length, 0, "Prompts should all be dismissed."); + + yield BrowserTestUtils.removeTab(tab); +}); diff --git a/browser/base/content/test/general/browser_openPromptInBackgroundTab.js b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js similarity index 96% rename from browser/base/content/test/general/browser_openPromptInBackgroundTab.js rename to browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js index ee188e7cf01c..96a70110f4d3 100644 --- a/browser/base/content/test/general/browser_openPromptInBackgroundTab.js +++ b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js @@ -15,7 +15,7 @@ registerCleanupFunction(function() { * checking the checkbox does actually enable that behaviour. */ add_task(function*() { - yield pushPrefs(["browser.tabs.dontfocusfordialogs", true]); + yield SpecialPowers.pushPrefEnv({"set": [["browser.tabs.dontfocusfordialogs", true]]}); let firstTab = gBrowser.selectedTab; // load page that opens prompt when page is hidden let openedTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageWithAlert, true); @@ -60,4 +60,3 @@ add_task(function*() { yield BrowserTestUtils.removeTab(openedTab); }); - diff --git a/browser/base/content/test/general/openPromptOffTimeout.html b/browser/base/content/test/tabPrompts/openPromptOffTimeout.html similarity index 100% rename from browser/base/content/test/general/openPromptOffTimeout.html rename to browser/base/content/test/tabPrompts/openPromptOffTimeout.html diff --git a/browser/base/moz.build b/browser/base/moz.build index 932b40131471..2ce039711756 100644 --- a/browser/base/moz.build +++ b/browser/base/moz.build @@ -23,6 +23,7 @@ BROWSER_CHROME_MANIFESTS += [ 'content/test/popupNotifications/browser.ini', 'content/test/referrer/browser.ini', 'content/test/social/browser.ini', + 'content/test/tabPrompts/browser.ini', 'content/test/urlbar/browser.ini', ] diff --git a/browser/components/customizableui/CustomizableUI.jsm b/browser/components/customizableui/CustomizableUI.jsm index db79207d4fc0..e7f2e19c840e 100644 --- a/browser/components/customizableui/CustomizableUI.jsm +++ b/browser/components/customizableui/CustomizableUI.jsm @@ -27,6 +27,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gELS", "@mozilla.org/eventlistenerservice;1", "nsIEventListenerService"); +XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager", + "resource://gre/modules/LightweightThemeManager.jsm"); const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; @@ -151,6 +153,7 @@ var gListeners = new Set(); var gUIStateBeforeReset = { uiCustomizationState: null, drawInTitlebar: null, + currentTheme: null, }; XPCOMUtils.defineLazyGetter(this, "log", () => { @@ -2527,12 +2530,14 @@ var CustomizableUIInternal = { try { gUIStateBeforeReset.drawInTitlebar = Services.prefs.getBoolPref(kPrefDrawInTitlebar); gUIStateBeforeReset.uiCustomizationState = Services.prefs.getCharPref(kPrefCustomizationState); + gUIStateBeforeReset.currentTheme = LightweightThemeManager.currentTheme; } catch(e) { } this._resetExtraToolbars(); Services.prefs.clearUserPref(kPrefCustomizationState); Services.prefs.clearUserPref(kPrefDrawInTitlebar); + LightweightThemeManager.currentTheme = null; log.debug("State reset"); // Reset placements to make restoring default placements possible. @@ -2601,6 +2606,7 @@ var CustomizableUIInternal = { let uiCustomizationState = gUIStateBeforeReset.uiCustomizationState; let drawInTitlebar = gUIStateBeforeReset.drawInTitlebar; + let currentTheme = gUIStateBeforeReset.currentTheme; // Need to clear the previous state before setting the prefs // because pref observers may check if there is a previous UI state. @@ -2608,6 +2614,7 @@ var CustomizableUIInternal = { Services.prefs.setCharPref(kPrefCustomizationState, uiCustomizationState); Services.prefs.setBoolPref(kPrefDrawInTitlebar, drawInTitlebar); + LightweightThemeManager.currentTheme = currentTheme; this.loadSavedState(); // If the user just customizes toolbar/titlebar visibility, gSavedState will be null // and we don't need to do anything else here: @@ -2786,6 +2793,11 @@ var CustomizableUIInternal = { return false; } + if(LightweightThemeManager.currentTheme) { + log.debug(LightweightThemeManager.currentTheme + " theme is non-default"); + return false; + } + return true; }, @@ -3490,7 +3502,8 @@ this.CustomizableUI = { */ get canUndoReset() { return gUIStateBeforeReset.uiCustomizationState != null || - gUIStateBeforeReset.drawInTitlebar != null; + gUIStateBeforeReset.drawInTitlebar != null || + gUIStateBeforeReset.currentTheme != null; }, /** diff --git a/browser/components/customizableui/CustomizeMode.jsm b/browser/components/customizableui/CustomizeMode.jsm index fa50f26bbafd..35ab84a738f1 100644 --- a/browser/components/customizableui/CustomizeMode.jsm +++ b/browser/components/customizableui/CustomizeMode.jsm @@ -1177,6 +1177,8 @@ CustomizeMode.prototype = { CustomizableUI.reset(); + this.swatchForTheme(this.document); + yield this._wrapToolbarItems(); this.populatePalette(); @@ -1203,6 +1205,8 @@ CustomizeMode.prototype = { CustomizableUI.undoReset(); + this.swatchForTheme(this.document); + yield this._wrapToolbarItems(); this.populatePalette(); @@ -1451,6 +1455,10 @@ CustomizeMode.prototype = { } } target.removeAttribute("height"); + + if (LightweightThemeManager.currentTheme) { + this._onUIChange(); + } }, _onUIChange: function() { diff --git a/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js index 57bef5dbef92..db4f88e6d5c7 100644 --- a/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js +++ b/browser/components/customizableui/test/browser_1007336_lwthemes_in_customize_mode.js @@ -63,6 +63,22 @@ add_task(function* () { defaultTheme.doCommand(); is(Services.prefs.getCharPref("lightweightThemes.selectedThemeID"), "", "No lwtheme should be selected"); + // ensure current theme isn't set to "Default" + popupShownPromise = popupShown(popup); + EventUtils.synthesizeMouseAtCenter(themesButton, {}); + info("Clicked on themes button a second time"); + yield popupShownPromise; + + firstLWTheme = recommendedHeader.nextSibling; + themeChangedPromise = promiseObserverNotified("lightweight-theme-changed"); + firstLWTheme.doCommand(); + info("Clicked on first theme again"); + yield themeChangedPromise; + + // check that "Restore Defaults" button resets theme + yield gCustomizeMode.reset(); + is(LightweightThemeManager.currentTheme, null, "Current theme reset to default"); + yield endCustomizing(); Services.prefs.setCharPref("lightweightThemes.usedThemes", "[]"); Services.prefs.setCharPref("lightweightThemes.recommendedThemes", "[]"); diff --git a/browser/components/customizableui/test/browser_970511_undo_restore_default.js b/browser/components/customizableui/test/browser_970511_undo_restore_default.js index cf32546804e3..e7b3ca674e88 100644 --- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js +++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js @@ -6,7 +6,7 @@ requestLongerTimeout(2); -// Restoring default should show an "undo" option which undoes the restoring operation. +// Restoring default should reset theme and show an "undo" option which undoes the restoring operation. add_task(function*() { let homeButtonId = "home-button"; CustomizableUI.removeWidgetFromArea(homeButtonId); @@ -16,13 +16,32 @@ add_task(function*() { let undoResetButton = document.getElementById("customization-undo-reset-button"); is(undoResetButton.hidden, true, "The undo button is hidden before reset"); + let themesButton = document.getElementById("customization-lwtheme-button"); + let popup = document.getElementById("customization-lwtheme-menu"); + let popupShownPromise = popupShown(popup); + EventUtils.synthesizeMouseAtCenter(themesButton, {}); + info("Clicked on themes button"); + yield popupShownPromise; + + let recommendedHeader = document.getElementById("customization-lwtheme-menu-recommended"); + let firstLWTheme = recommendedHeader.nextSibling; + let firstLWThemeId = firstLWTheme.theme.id; + let themeChangedPromise = promiseObserverNotified("lightweight-theme-changed"); + firstLWTheme.doCommand(); + info("Clicked on first theme"); + yield themeChangedPromise; + + is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme changed to first option"); + yield gCustomizeMode.reset(); ok(CustomizableUI.inDefaultState, "In default state after reset"); is(undoResetButton.hidden, false, "The undo button is visible after reset"); + is(LightweightThemeManager.currentTheme, null, "Theme reset to default"); yield gCustomizeMode.undoReset() + is(LightweightThemeManager.currentTheme.id, firstLWThemeId, "Theme has been reset from default to original choice"); ok(!CustomizableUI.inDefaultState, "Not in default state after undo-reset"); is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button"); is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette"); diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js index 6f3cefd69d19..25f67c92352b 100644 --- a/browser/components/migration/ChromeProfileMigrator.js +++ b/browser/components/migration/ChromeProfileMigrator.js @@ -31,7 +31,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OSCrypto", "resource://gre/modules/OSCrypto.jsm"); - +XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", + "resource://gre/modules/Sqlite.jsm"); /** * Get an nsIFile instance representing the expected location of user data * for this copy of Chrome/Chromium/Canary on different OSes. @@ -290,54 +291,63 @@ function GetHistoryResource(aProfileFolder) { return { type: MigrationUtils.resourceTypes.HISTORY, - migrate: function(aCallback) { - let dbConn = Services.storage.openUnsharedDatabase(historyFile); - let stmt = dbConn.createAsyncStatement( - "SELECT url, title, last_visit_time, typed_count FROM urls WHERE hidden = 0"); + migrate(aCallback) { + Task.spawn(function* () { + let db = yield Sqlite.openConnection({ + path: historyFile.path + }); - stmt.executeAsync({ - handleResult : function(aResults) { - let places = []; - for (let row = aResults.getNextRow(); row; row = aResults.getNextRow()) { - try { - // if having typed_count, we changes transition type to typed. - let transType = PlacesUtils.history.TRANSITION_LINK; - if (row.getResultByName("typed_count") > 0) - transType = PlacesUtils.history.TRANSITION_TYPED; - - places.push({ - uri: NetUtil.newURI(row.getResultByName("url")), - title: row.getResultByName("title"), - visits: [{ - transitionType: transType, - visitDate: chromeTimeToDate( - row.getResultByName( - "last_visit_time")) * 1000, - }], - }); - } catch (e) { - Cu.reportError(e); - } - } + let rows = yield db.execute(`SELECT url, title, last_visit_time, typed_count + FROM urls WHERE hidden = 0`); + yield db.close(); + let places = []; + for (let row of rows) { try { - PlacesUtils.asyncHistory.updatePlaces(places); + // if having typed_count, we changes transition type to typed. + let transType = PlacesUtils.history.TRANSITION_LINK; + if (row.getResultByName("typed_count") > 0) + transType = PlacesUtils.history.TRANSITION_TYPED; + + places.push({ + uri: NetUtil.newURI(row.getResultByName("url")), + title: row.getResultByName("title"), + visits: [{ + transitionType: transType, + visitDate: chromeTimeToDate( + row.getResultByName( + "last_visit_time")) * 1000, + }], + }); } catch (e) { Cu.reportError(e); } - }, - - handleError : function(aError) { - Cu.reportError("Async statement execution returned with '" + - aError.result + "', '" + aError.message + "'"); - }, - - handleCompletion : function(aReason) { - dbConn.asyncClose(); - aCallback(aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED); } - }); - stmt.finalize(); + + if (places.length > 0) { + yield new Promise((resolve, reject) => { + PlacesUtils.asyncHistory.updatePlaces(places, { + _success: false, + handleResult: function() { + // Importing any entry is considered a successful import. + this._success = true; + }, + handleError: function() {}, + handleCompletion: function() { + if (this._success) { + resolve(); + } else { + reject(new Error("Couldn't add visits")); + } + } + }); + }); + } + }).then(() => { aCallback(true); }, + ex => { + Cu.reportError(ex); + aCallback(false); + }); } }; } diff --git a/browser/components/migration/MigrationUtils.jsm b/browser/components/migration/MigrationUtils.jsm index 981875ca73cc..2f03ea074b43 100644 --- a/browser/components/migration/MigrationUtils.jsm +++ b/browser/components/migration/MigrationUtils.jsm @@ -19,6 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils", "resource://gre/modules/BookmarkHTMLUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils", + "resource://gre/modules/PromiseUtils.jsm"); var gMigrators = null; var gProfileStartup = null; @@ -227,16 +229,21 @@ this.MigratorPrototype = { if (aItems != Ci.nsIBrowserProfileMigrator.ALL) resources = resources.filter(r => aItems & r.type); + // Used to periodically give back control to the main-thread loop. + let unblockMainThread = function () { + return new Promise(resolve => { + Services.tm.mainThread.dispatch(resolve, Ci.nsIThread.DISPATCH_NORMAL); + }); + }; + // Called either directly or through the bookmarks import callback. - function doMigrate() { - // TODO: use Map (for the items) and Set (for the resources) - // once they are iterable. + let doMigrate = Task.async(function*() { let resourcesGroupedByItems = new Map(); resources.forEach(function(resource) { - if (resourcesGroupedByItems.has(resource.type)) - resourcesGroupedByItems.get(resource.type).push(resource); - else - resourcesGroupedByItems.set(resource.type, [resource]); + if (!resourcesGroupedByItems.has(resource.type)) { + resourcesGroupedByItems.set(resource.type, new Set()); + } + resourcesGroupedByItems.get(resource.type).add(resource) }); if (resourcesGroupedByItems.size == 0) @@ -248,44 +255,52 @@ this.MigratorPrototype = { notify("Migration:Started"); for (let [key, value] of resourcesGroupedByItems) { - // TODO: (bug 449811). + // Workaround bug 449811. let migrationType = key, itemResources = value; notify("Migration:ItemBeforeMigrate", migrationType); let itemSuccess = false; for (let res of itemResources) { + // Workaround bug 449811. let resource = res; + let completeDeferred = PromiseUtils.defer(); let resourceDone = function(aSuccess) { - let resourceIndex = itemResources.indexOf(resource); - if (resourceIndex != -1) { - itemResources.splice(resourceIndex, 1); - itemSuccess |= aSuccess; - if (itemResources.length == 0) { - resourcesGroupedByItems.delete(migrationType); - notify(itemSuccess ? - "Migration:ItemAfterMigrate" : "Migration:ItemError", - migrationType); - if (resourcesGroupedByItems.size == 0) - notify("Migration:Ended"); + itemResources.delete(resource); + itemSuccess |= aSuccess; + if (itemResources.size == 0) { + notify(itemSuccess ? + "Migration:ItemAfterMigrate" : "Migration:ItemError", + migrationType); + resourcesGroupedByItems.delete(migrationType); + if (resourcesGroupedByItems.size == 0) { + notify("Migration:Ended"); } } + completeDeferred.resolve(); } - Services.tm.mainThread.dispatch(function() { - // If migrate throws, an error occurred, and the callback - // (itemMayBeDone) might haven't been called. - try { - resource.migrate(resourceDone); - } - catch(ex) { - Cu.reportError(ex); - resourceDone(false); - } - }, Ci.nsIThread.DISPATCH_NORMAL); + // If migrate throws, an error occurred, and the callback + // (itemMayBeDone) might haven't been called. + try { + resource.migrate(resourceDone); + } + catch(ex) { + Cu.reportError(ex); + resourceDone(false); + } + + // Certain resources must be ran sequentially or they could fail, + // for example bookmarks and history (See bug 1272652). + if (migrationType == MigrationUtils.resourceTypes.BOOKMARKS || + migrationType == MigrationUtils.resourceTypes.HISTORY) { + yield completeDeferred.promise; + } + + yield unblockMainThread(); } } - } + }); if (MigrationUtils.isStartupMigration && !this.startupOnlyMigrator) { MigrationUtils.profileStartup.doStartup(); diff --git a/browser/components/migration/tests/marionette/test_refresh_firefox.py b/browser/components/migration/tests/marionette/test_refresh_firefox.py index 1b8b806cf9ad..631ddfe9d8c1 100644 --- a/browser/components/migration/tests/marionette/test_refresh_firefox.py +++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py @@ -230,17 +230,24 @@ class TestFirefoxRefresh(MarionetteTestCase): let cookieEnum = Services.cookies.getCookiesFromHost(arguments[0]); let cookie = null; while (cookieEnum.hasMoreElements()) { + let hostCookie = cookieEnum.getNext(); + hostCookie.QueryInterface(Ci.nsICookie2); + // getCookiesFromHost returns any cookie from the BASE host. + if (hostCookie.rawHost != arguments[0]) + continue; if (cookie != null) { return "more than 1 cookie! That shouldn't happen!"; } - cookie = cookieEnum.getNext(); - cookie.QueryInterface(Ci.nsICookie2); + cookie = hostCookie; } return {path: cookie.path, name: cookie.name, value: cookie.value}; } catch (ex) { return "got exception trying to fetch cookie: " + ex; } """, script_args=[self._cookieHost]) + if not isinstance(cookieInfo, dict): + self.fail(cookieInfo) + return self.assertEqual(cookieInfo['path'], self._cookiePath) self.assertEqual(cookieInfo['value'], self._cookieValue) self.assertEqual(cookieInfo['name'], self._cookieName) diff --git a/browser/components/places/content/editBookmarkOverlay.js b/browser/components/places/content/editBookmarkOverlay.js index e2b6b1b61f4a..24ff7eaf215c 100644 --- a/browser/components/places/content/editBookmarkOverlay.js +++ b/browser/components/places/content/editBookmarkOverlay.js @@ -761,6 +761,11 @@ var gEditItemOverlay = { this._markFolderAsRecentlyUsed(containerId) .catch(Components.utils.reportError); } + + // Auto-show the bookmarks toolbar when adding / moving an item there. + if (containerId == PlacesUtils.toolbarFolderId) { + Services.obs.notifyObservers(null, "autoshow-bookmarks-toolbar", null); + } } // Update folder-tree selection diff --git a/browser/components/sessionstore/test/browser_newtab_userTypedValue.js b/browser/components/sessionstore/test/browser_newtab_userTypedValue.js index 63e259d43f16..66dc93380398 100644 --- a/browser/components/sessionstore/test/browser_newtab_userTypedValue.js +++ b/browser/components/sessionstore/test/browser_newtab_userTypedValue.js @@ -1,6 +1,6 @@ "use strict"; -requestLongerTimeout(2); +requestLongerTimeout(4); /** * Test that when restoring an 'initial page' with session restore, it diff --git a/devtools/client/aboutdebugging/aboutdebugging.css b/devtools/client/aboutdebugging/aboutdebugging.css index 22b594694114..172bcae5e9f4 100644 --- a/devtools/client/aboutdebugging/aboutdebugging.css +++ b/devtools/client/aboutdebugging/aboutdebugging.css @@ -136,10 +136,15 @@ button { margin: 0 5px 5px 0; } -.page-not-found { +.error-page { display: flex; justify-content: center; align-items: center; + flex-direction: column; width: 100%; height: 100%; } + +.error-page .error-page-details { + color: gray; +} diff --git a/devtools/client/aboutdebugging/components/aboutdebugging.js b/devtools/client/aboutdebugging/components/aboutdebugging.js index bf5d6c3afe22..9b93b48f6629 100644 --- a/devtools/client/aboutdebugging/components/aboutdebugging.js +++ b/devtools/client/aboutdebugging/components/aboutdebugging.js @@ -83,10 +83,12 @@ module.exports = createClass({ panel = selectedPanel.component({ client, id: selectedPanel.id }); } else { panel = ( - dom.div({ className: "page-not-found" }, + dom.div({ className: "error-page" }, dom.h1({ className: "header-name" }, Strings.GetStringFromName("pageNotFound") - ) + ), + dom.h4({ className: "error-page-details" }, + Strings.formatStringFromName("doesNotExist", [selectedPanelId], 1)) ) ); } diff --git a/devtools/client/aboutdebugging/test/browser_service_workers.js b/devtools/client/aboutdebugging/test/browser_service_workers.js index 6e8e96c58ff6..d46e931a0e83 100644 --- a/devtools/client/aboutdebugging/test/browser_service_workers.js +++ b/devtools/client/aboutdebugging/test/browser_service_workers.js @@ -2,7 +2,6 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /* eslint-disable mozilla/no-cpows-in-tests */ -/* global sendAsyncMessage */ "use strict"; @@ -40,29 +39,12 @@ add_task(function* () { let aboutDebuggingUpdate = waitForMutation(serviceWorkersElement, { childList: true }); - // Use message manager to work with e10s - let frameScript = function () { - // Retrieve the `sw` promise created in the html page - let { sw } = content.wrappedJSObject; - sw.then(function (registration) { - registration.unregister().then(function () { - sendAsyncMessage("sw-unregistered"); - }, - function (e) { - dump("SW not unregistered; " + e + "\n"); - }); - }); - }; - let mm = swTab.linkedBrowser.messageManager; - mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); - - yield new Promise(done => { - mm.addMessageListener("sw-unregistered", function listener() { - mm.removeMessageListener("sw-unregistered", listener); - done(); - }); - }); - ok(true, "Service worker registration unregistered"); + try { + yield unregisterServiceWorker(swTab); + ok(true, "Service worker registration unregistered"); + } catch (e) { + ok(false, "SW not unregistered; " + e); + } yield aboutDebuggingUpdate; diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_push.js b/devtools/client/aboutdebugging/test/browser_service_workers_push.js index 173cb3f58e46..3f13ca134971 100644 --- a/devtools/client/aboutdebugging/test/browser_service_workers_push.js +++ b/devtools/client/aboutdebugging/test/browser_service_workers_push.js @@ -37,16 +37,16 @@ add_task(function* () { let swTab = yield addTab(TAB_URL); info("Make the test page notify us when the service worker sends a message."); - let frameScript = function () { + + yield ContentTask.spawn(swTab.linkedBrowser, {}, function () { let win = content.wrappedJSObject; win.navigator.serviceWorker.addEventListener("message", function (event) { sendAsyncMessage(event.data); }, false); - }; - let mm = swTab.linkedBrowser.messageManager; - mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); + }); // Expect the service worker to claim the test window when activating. + let mm = swTab.linkedBrowser.messageManager; let onClaimed = new Promise(done => { mm.addMessageListener("sw-claimed", function listener() { mm.removeMessageListener("sw-claimed", listener); @@ -90,8 +90,12 @@ add_task(function* () { ok(true, "Service worker received a push notification"); // Finally, unregister the service worker itself. - yield unregisterServiceWorker(swTab); - ok(true, "Service worker registration unregistered"); + try { + yield unregisterServiceWorker(swTab); + ok(true, "Service worker registration unregistered"); + } catch (e) { + ok(false, "SW not unregistered; " + e); + } yield removeTab(swTab); yield closeAboutDebugging(tab); diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_start.js b/devtools/client/aboutdebugging/test/browser_service_workers_start.js index 7521f0bce67a..7dbe571e15bc 100644 --- a/devtools/client/aboutdebugging/test/browser_service_workers_start.js +++ b/devtools/client/aboutdebugging/test/browser_service_workers_start.js @@ -77,8 +77,12 @@ add_task(function* () { ok(!targetElement.querySelector(".start-button"), "No start button"); // Finally, unregister the service worker itself. - yield unregisterServiceWorker(swTab); - ok(true, "Service worker registration unregistered"); + try { + yield unregisterServiceWorker(swTab); + ok(true, "Service worker registration unregistered"); + } catch (e) { + ok(false, "SW not unregistered; " + e); + } yield removeTab(swTab); yield closeAboutDebugging(tab); diff --git a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js index de199a34cf99..e30a9b86f7fe 100644 --- a/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js +++ b/devtools/client/aboutdebugging/test/browser_service_workers_timeout.js @@ -78,8 +78,12 @@ add_task(function* () { "The debug button was removed when the worker was killed"); // Finally, unregister the service worker itself. - yield unregisterServiceWorker(swTab); - ok(true, "Service worker registration unregistered"); + try { + yield unregisterServiceWorker(swTab); + ok(true, "Service worker registration unregistered"); + } catch (e) { + ok(false, "SW not unregistered; " + e); + } // Now ensure that the worker registration is correctly removed. // The list should update once the registration is destroyed. diff --git a/devtools/client/aboutdebugging/test/head.js b/devtools/client/aboutdebugging/test/head.js index 16c3ec685d45..e623eae1c850 100644 --- a/devtools/client/aboutdebugging/test/head.js +++ b/devtools/client/aboutdebugging/test/head.js @@ -7,7 +7,6 @@ installAddon, uninstallAddon, waitForMutation, assertHasTarget, getServiceWorkerList, getTabList, openPanel, waitForInitialAddonList, waitForServiceWorkerRegistered, unregisterServiceWorker */ -/* global sendAsyncMessage */ "use strict"; @@ -273,24 +272,13 @@ function assertHasTarget(expected, document, type, name) { * Returns a promise that will resolve after the service worker in the page * has successfully registered itself. * @param {Tab} tab + * @return {Promise} Resolves when the service worker is registered. */ function waitForServiceWorkerRegistered(tab) { - // Make the test page notify us when the service worker is registered. - let frameScript = function () { + return ContentTask.spawn(tab.linkedBrowser, {}, function* () { // Retrieve the `sw` promise created in the html page. let { sw } = content.wrappedJSObject; - sw.then(function (registration) { - sendAsyncMessage("sw-registered"); - }); - }; - let mm = tab.linkedBrowser.messageManager; - mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); - - return new Promise(done => { - mm.addMessageListener("sw-registered", function listener() { - mm.removeMessageListener("sw-registered", listener); - done(); - }); + yield sw; }); } @@ -298,28 +286,13 @@ function waitForServiceWorkerRegistered(tab) { * Asks the service worker within the test page to unregister, and returns a * promise that will resolve when it has successfully unregistered itself. * @param {Tab} tab + * @return {Promise} Resolves when the service worker is unregistered. */ function unregisterServiceWorker(tab) { - // Use message manager to work with e10s. - let frameScript = function () { - // Retrieve the `sw` promise created in the html page. + return ContentTask.spawn(tab.linkedBrowser, {}, function* () { + // Retrieve the `sw` promise created in the html page let { sw } = content.wrappedJSObject; - sw.then(function (registration) { - registration.unregister().then(function () { - sendAsyncMessage("sw-unregistered"); - }, - function (e) { - dump("SW not unregistered; " + e + "\n"); - }); - }); - }; - let mm = tab.linkedBrowser.messageManager; - mm.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); - - return new Promise(done => { - mm.addMessageListener("sw-unregistered", function listener() { - mm.removeMessageListener("sw-unregistered", listener); - done(); - }); + let registration = yield sw; + yield registration.unregister(); }); } diff --git a/devtools/client/framework/test/shared-head.js b/devtools/client/framework/test/shared-head.js index b6ae4274bd4f..d49739d4562a 100644 --- a/devtools/client/framework/test/shared-head.js +++ b/devtools/client/framework/test/shared-head.js @@ -96,11 +96,8 @@ registerCleanupFunction(() => { }); registerCleanupFunction(function* cleanup() { - let target = TargetFactory.forTab(gBrowser.selectedTab); - yield gDevTools.closeToolbox(target); - while (gBrowser.tabs.length > 1) { - gBrowser.removeCurrentTab(); + yield closeTabAndToolbox(gBrowser.selectedTab); } }); diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector.js b/devtools/client/inspector/fonts/test/browser_fontinspector.js index be24de2c0039..a36c57771bb4 100644 --- a/devtools/client/inspector/fonts/test/browser_fontinspector.js +++ b/devtools/client/inspector/fonts/test/browser_fontinspector.js @@ -6,16 +6,31 @@ requestLongerTimeout(2); const TEST_URI = URL_ROOT + "browser_fontinspector.html"; -const FONTS = [ - {name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf", - format: "truetype", cssName: "bar"}, - {name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf", - format: "", cssName: "bar"}, - {name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf", - format: "", cssName: "bar"}, - {name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf", - format: "", cssName: "barnormal"}, -]; +const FONTS = [{ + name: "Ostrich Sans Medium", + remote: true, + url: URL_ROOT + "ostrich-regular.ttf", + format: "truetype", + cssName: "bar" +}, { + name: "Ostrich Sans Black", + remote: true, + url: URL_ROOT + "ostrich-black.ttf", + format: "", + cssName: "bar" +}, { + name: "Ostrich Sans Black", + remote: true, + url: URL_ROOT + "ostrich-black.ttf", + format: "", + cssName: "bar" +}, { + name: "Ostrich Sans Medium", + remote: true, + url: URL_ROOT + "ostrich-regular.ttf", + format: "", + cssName: "barnormal" +}]; add_task(function* () { let { inspector, view } = yield openFontInspectorForURL(TEST_URI); @@ -74,8 +89,9 @@ function* testDivFonts(inspector, viewDoc) { let sections1 = viewDoc.querySelectorAll("#all-fonts > section"); is(sections1.length, 1, "Found 1 font on DIV"); - is(sections1[0].querySelector(".font-name").textContent, "Ostrich Sans Medium", - "The DIV font has the right name"); + is(sections1[0].querySelector(".font-name").textContent, + "Ostrich Sans Medium", + "The DIV font has the right name"); } function* testShowAllFonts(inspector, viewDoc) { diff --git a/devtools/client/inspector/fonts/test/head.js b/devtools/client/inspector/fonts/test/head.js index 38b87338b5d9..f510ed798d2d 100644 --- a/devtools/client/inspector/fonts/test/head.js +++ b/devtools/client/inspector/fonts/test/head.js @@ -2,6 +2,8 @@ /* 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/. */ +/* eslint no-unused-vars: [2, {"vars": "local"}] */ +/* import-globals-from ../../test/head.js */ "use strict"; // Import the inspector's head.js first (which itself imports shared-head.js). diff --git a/devtools/client/inspector/inspector-panel.js b/devtools/client/inspector/inspector-panel.js index 7e9842de1a2a..34eeada58509 100644 --- a/devtools/client/inspector/inspector-panel.js +++ b/devtools/client/inspector/inspector-panel.js @@ -3,16 +3,17 @@ /* 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/. */ +/* Experimenting with 100 char long lines */ +/* eslint max-len: [2, 100, 2, {ignoreUrls: true, "ignorePattern": "\\s*require\\s*\\(|^\\s*loader\\.lazy|-\\*-"}] */ // eslint-disable-line "use strict"; -const {Cc, Ci, Cu} = require("chrome"); +const {Cc, Ci} = require("chrome"); var Services = require("Services"); var promise = require("promise"); var EventEmitter = require("devtools/shared/event-emitter"); var clipboard = require("sdk/clipboard"); -var {HostType} = require("devtools/client/framework/toolbox").Toolbox; const {executeSoon} = require("devtools/shared/DevToolsUtils"); var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts"); var {Task} = require("devtools/shared/task"); @@ -40,7 +41,6 @@ loader.lazyGetter(this, "clipboardHelper", () => { return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); }); - /** * Represents an open instance of the Inspector for a tab. * The inspector controls the breadcrumbs, the markup view, and the sidebar @@ -191,7 +191,6 @@ InspectorPanel.prototype = { if (notification && !this._toolbox.threadClient.paused) { notificationBox.removeNotification(notification); } - }; this.target.on("thread-paused", this.updateDebuggerPausedWarning); this.target.on("thread-resumed", this.updateDebuggerPausedWarning); @@ -251,15 +250,16 @@ InspectorPanel.prototype = { // If available, set either the previously selected node or the body // as default selected, else set documentElement - return walker.getRootNode().then(aRootNode => { + return walker.getRootNode().then(node => { if (hasNavigated()) { return promise.reject("navigated; resolution of _defaultNode aborted"); } - rootNode = aRootNode; + rootNode = node; if (this.selectionCssSelector) { return walker.querySelector(rootNode, this.selectionCssSelector); } + return null; }).then(front => { if (hasNavigated()) { return promise.reject("navigated; resolution of _defaultNode aborted"); @@ -391,7 +391,7 @@ InspectorPanel.prototype = { if (this.target.form.animationsActor) { this.sidebar.addTab("animationinspector", "chrome://devtools/content/animationinspector/animation-inspector.xhtml", - "animationinspector" == defaultTab); + defaultTab == "animationinspector"); } this.sidebar.show(defaultTab); @@ -467,9 +467,8 @@ InspectorPanel.prototype = { if (this._selectionCssSelector && this._selectionCssSelector.url === this._target.url) { return this._selectionCssSelector.selector; - } else { - return null; } + return null; }, /** @@ -688,13 +687,13 @@ InspectorPanel.prototype = { /** * Show the node menu. */ - showNodeMenu: function (aButton, aPosition, aExtraItems) { - if (aExtraItems) { - for (let item of aExtraItems) { + showNodeMenu: function (button, position, extraItems) { + if (extraItems) { + for (let item of extraItems) { this.nodemenu.appendChild(item); } } - this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false); + this.nodemenu.openPopup(button, position, 0, 0, true, false); }, hideNodeMenu: function () { @@ -796,8 +795,7 @@ InspectorPanel.prototype = { if (isDuplicatableElement) { duplicateNode.removeAttribute("disabled"); - } - else { + } else { duplicateNode.setAttribute("disabled", "true"); } @@ -997,7 +995,8 @@ InspectorPanel.prototype = { this._markupBox.setAttribute("collapsed", true); this._markupBox.appendChild(this._markupFrame); this._markupFrame.setAttribute("src", "chrome://devtools/content/inspector/markup/markup.xhtml"); - this._markupFrame.setAttribute("aria-label", strings.GetStringFromName("inspector.panelLabel.markupView")); + this._markupFrame.setAttribute("aria-label", + strings.GetStringFromName("inspector.panelLabel.markupView")); }, _onMarkupFrameLoad: function () { @@ -1096,16 +1095,17 @@ InspectorPanel.prototype = { /** * Toggle a pseudo class. */ - togglePseudoClass: function (aPseudo) { + togglePseudoClass: function (pseudo) { if (this.selection.isElementNode()) { let node = this.selection.nodeFront; - if (node.hasPseudoClassLock(aPseudo)) { - return this.walker.removePseudoClassLock(node, aPseudo, {parents: true}); + if (node.hasPseudoClassLock(pseudo)) { + return this.walker.removePseudoClassLock(node, pseudo, {parents: true}); } - let hierarchical = aPseudo == ":hover" || aPseudo == ":active"; - return this.walker.addPseudoClassLock(node, aPseudo, {parents: hierarchical}); + let hierarchical = pseudo == ":hover" || pseudo == ":active"; + return this.walker.addPseudoClassLock(node, pseudo, {parents: hierarchical}); } + return promise.resolve(); }, /** @@ -1156,7 +1156,7 @@ InspectorPanel.prototype = { */ clearPseudoClasses: function () { if (!this.walker) { - return; + return promise.resolve(); } return this.walker.clearPseudoClassLocks().then(null, console.error); }, @@ -1178,8 +1178,9 @@ InspectorPanel.prototype = { */ pasteOuterHTML: function () { let content = this._getClipboardContentForPaste(); - if (!content) + if (!content) { return promise.reject("No clipboard content for paste"); + } let node = this.selection.nodeFront; return this.markup.getNodeOuterHTML(node).then(oldContent => { @@ -1192,8 +1193,9 @@ InspectorPanel.prototype = { */ pasteInnerHTML: function () { let content = this._getClipboardContentForPaste(); - if (!content) + if (!content) { return promise.reject("No clipboard content for paste"); + } let node = this.selection.nodeFront; return this.markup.getNodeInnerHTML(node).then(oldContent => { @@ -1208,8 +1210,9 @@ InspectorPanel.prototype = { */ pasteAdjacentHTML: function (position) { let content = this._getClipboardContentForPaste(); - if (!content) + if (!content) { return promise.reject("No clipboard content for paste"); + } let node = this.selection.nodeFront; return this.markup.insertAdjacentHTMLToNode(node, position, content); @@ -1415,16 +1418,18 @@ InspectorPanel.prototype = { // Open link in a new tab. // When the inspector menu was setup on click (see _setupNodeLinkMenu), we // already checked that resolveRelativeURL existed. - this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => { - if (type === "uri") { - let browserWin = this.target.tab.ownerDocument.defaultView; - browserWin.openUILinkIn(url, "tab"); - } else if (type === "cssresource") { - return this.toolbox.viewSourceInStyleEditor(url); - } else if (type === "jsresource") { - return this.toolbox.viewSourceInDebugger(url); - } - }).catch(e => console.error(e)); + this.inspector.resolveRelativeURL( + link, this.selection.nodeFront).then(url => { + if (type === "uri") { + let browserWin = this.target.tab.ownerDocument.defaultView; + browserWin.openUILinkIn(url, "tab"); + } else if (type === "cssresource") { + return this.toolbox.viewSourceInStyleEditor(url); + } else if (type === "jsresource") { + return this.toolbox.viewSourceInDebugger(url); + } + return null; + }).catch(e => console.error(e)); } else if (type == "idref") { // Select the node in the same document. this.walker.document(this.selection.nodeFront).then(doc => { diff --git a/devtools/client/inspector/inspector-search.js b/devtools/client/inspector/inspector-search.js index 5c0740b1443d..1ce8b407fdf9 100644 --- a/devtools/client/inspector/inspector-search.js +++ b/devtools/client/inspector/inspector-search.js @@ -4,8 +4,7 @@ "use strict"; -const {Cu, Ci} = require("chrome"); - +const {Ci} = require("chrome"); const promise = require("promise"); const {Task} = require("devtools/shared/task"); @@ -112,8 +111,8 @@ InspectorSearch.prototype = { this._onSearch(event.shiftKey); } - const modifierKey = system.constants.platform === "macosx" ? event.metaKey : -event.ctrlKey; + const modifierKey = system.constants.platform === "macosx" + ? event.metaKey : event.ctrlKey; if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_G && modifierKey) { this._onSearch(event.shiftKey); event.preventDefault(); @@ -214,7 +213,8 @@ SelectorAutocompleter.prototype = { this._state = null; let subQuery = ""; - // Now we iterate over the query and decide the state character by character. + // Now we iterate over the query and decide the state character by + // character. // The logic here is that while iterating, the state can go from one to // another with some restrictions. Like, if the state is Class, then it can // never go to Tag state without a space or '>' character; Or like, a Class @@ -229,7 +229,8 @@ SelectorAutocompleter.prototype = { case null: // This will happen only in the first iteration of the for loop. lastChar = secondLastChar; - case this.States.TAG: + + case this.States.TAG: // eslint-disable-line if (lastChar == ".") { this._state = this.States.CLASS; } else if (lastChar == "#") { @@ -243,7 +244,8 @@ SelectorAutocompleter.prototype = { case this.States.CLASS: if (subQuery.match(/[\.]+[^\.]*$/)[0].length > 2) { - // Checks whether the subQuery has atleast one [a-zA-Z] after the '.'. + // Checks whether the subQuery has atleast one [a-zA-Z] after the + // '.'. if (lastChar == " " || lastChar == ">") { this._state = this.States.TAG; } else if (lastChar == "#") { @@ -258,7 +260,8 @@ SelectorAutocompleter.prototype = { case this.States.ID: if (subQuery.match(/[#]+[^#]*$/)[0].length > 2) { - // Checks whether the subQuery has atleast one [a-zA-Z] after the '#'. + // Checks whether the subQuery has atleast one [a-zA-Z] after the + // '#'. if (lastChar == " " || lastChar == ">") { this._state = this.States.TAG; } else if (lastChar == ".") { @@ -295,7 +298,8 @@ SelectorAutocompleter.prototype = { */ destroy: function () { this.searchBox.removeEventListener("input", this.showSuggestions, true); - this.searchBox.removeEventListener("keypress", this._onSearchKeypress, true); + this.searchBox.removeEventListener("keypress", + this._onSearchKeypress, true); this.inspector.off("markupmutation", this._onMarkupMutation); this.searchPopup.destroy(); this.searchPopup = null; @@ -308,16 +312,19 @@ SelectorAutocompleter.prototype = { */ _onSearchKeypress: function (event) { let query = this.searchBox.value; + let popup = this.searchPopup; + switch (event.keyCode) { case event.DOM_VK_RETURN: case event.DOM_VK_TAB: - if (this.searchPopup.isOpen && - this.searchPopup.getItemAtIndex(this.searchPopup.itemCount - 1) + if (popup.isOpen && + popup.getItemAtIndex(popup.itemCount - 1) .preLabel == query) { - this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1; - this.searchBox.value = this.searchPopup.selectedItem.label; + popup.selectedIndex = popup.itemCount - 1; + this.searchBox.value = popup.selectedItem.label; this.hidePopup(); - } else if (!this.searchPopup.isOpen && event.keyCode === event.DOM_VK_TAB) { + } else if (!popup.isOpen && + event.keyCode === event.DOM_VK_TAB) { // When tab is pressed with focus on searchbox and closed popup, // do not prevent the default to avoid a keyboard trap and move focus // to next/previous element. @@ -327,24 +334,23 @@ SelectorAutocompleter.prototype = { break; case event.DOM_VK_UP: - if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) { - this.searchPopup.focus(); - if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) { - this.searchPopup.selectedIndex = - Math.max(0, this.searchPopup.itemCount - 2); + if (popup.isOpen && popup.itemCount > 0) { + popup.focus(); + if (popup.selectedIndex == popup.itemCount - 1) { + popup.selectedIndex = + Math.max(0, popup.itemCount - 2); + } else { + popup.selectedIndex = popup.itemCount - 1; } - else { - this.searchPopup.selectedIndex = this.searchPopup.itemCount - 1; - } - this.searchBox.value = this.searchPopup.selectedItem.label; + this.searchBox.value = popup.selectedItem.label; } break; case event.DOM_VK_DOWN: - if (this.searchPopup.isOpen && this.searchPopup.itemCount > 0) { - this.searchPopup.focus(); - this.searchPopup.selectedIndex = 0; - this.searchBox.value = this.searchPopup.selectedItem.label; + if (popup.isOpen && popup.itemCount > 0) { + popup.focus(); + popup.selectedIndex = 0; + this.searchBox.value = popup.selectedItem.label; } break; @@ -361,40 +367,41 @@ SelectorAutocompleter.prototype = { * Handles keypress and mouse click on the suggestions richlistbox. */ _onListBoxKeypress: function (event) { + let popup = this.searchPopup; + switch (event.keyCode || event.button) { case event.DOM_VK_RETURN: case event.DOM_VK_TAB: - case 0: // left mouse button + case 0: + // left mouse button event.stopPropagation(); event.preventDefault(); - this.searchBox.value = this.searchPopup.selectedItem.label; + this.searchBox.value = popup.selectedItem.label; this.searchBox.focus(); this.hidePopup(); break; case event.DOM_VK_UP: - if (this.searchPopup.selectedIndex == 0) { - this.searchPopup.selectedIndex = -1; + if (popup.selectedIndex == 0) { + popup.selectedIndex = -1; event.stopPropagation(); event.preventDefault(); this.searchBox.focus(); - } - else { - let index = this.searchPopup.selectedIndex; - this.searchBox.value = this.searchPopup.getItemAtIndex(index - 1).label; + } else { + let index = popup.selectedIndex; + this.searchBox.value = popup.getItemAtIndex(index - 1).label; } break; case event.DOM_VK_DOWN: - if (this.searchPopup.selectedIndex == this.searchPopup.itemCount - 1) { - this.searchPopup.selectedIndex = -1; + if (popup.selectedIndex == popup.itemCount - 1) { + popup.selectedIndex = -1; event.stopPropagation(); event.preventDefault(); this.searchBox.focus(); - } - else { - let index = this.searchPopup.selectedIndex; - this.searchBox.value = this.searchPopup.getItemAtIndex(index + 1).label; + } else { + let index = popup.selectedIndex; + this.searchBox.value = popup.getItemAtIndex(index + 1).label; } break; @@ -403,8 +410,8 @@ SelectorAutocompleter.prototype = { event.preventDefault(); this.searchBox.focus(); if (this.searchBox.selectionStart > 0) { - this.searchBox.value = - this.searchBox.value.substring(0, this.searchBox.selectionStart - 1); + this.searchBox.value = this.searchBox.value.substring(0, + this.searchBox.selectionStart - 1); } this.hidePopup(); break; @@ -424,12 +431,12 @@ SelectorAutocompleter.prototype = { /** * Populates the suggestions list and show the suggestion popup. */ - _showPopup: function (list, firstPart, aState) { + _showPopup: function (list, firstPart, popupState) { let total = 0; let query = this.searchBox.value; let items = []; - for (let [value, /* count*/, state] of list) { + for (let [value, , state] of list) { if (query.match(/[\s>+]$/)) { // for cases like 'div ' or 'div >' or 'div+' value = query + value; @@ -454,10 +461,10 @@ SelectorAutocompleter.prototype = { // In case the query's state is tag and the item's state is id or class // adjust the preLabel - if (aState === this.States.TAG && state === this.States.CLASS) { + if (popupState === this.States.TAG && state === this.States.CLASS) { item.preLabel = "." + item.preLabel; } - if (aState === this.States.TAG && state === this.States.ID) { + if (popupState === this.States.TAG && state === this.States.ID) { item.preLabel = "#" + item.preLabel; } @@ -469,13 +476,11 @@ SelectorAutocompleter.prototype = { if (total > 0) { this.searchPopup.setItems(items); this.searchPopup.openPopup(this.searchBox); - } - else { + } else { this.hidePopup(); } }, - /** * Hide the suggestion popup if necessary. */ @@ -503,17 +508,15 @@ SelectorAutocompleter.prototype = { } if (state === this.States.TAG) { - // gets the tag that is being completed. For ex. 'div.foo > s' returns 's', - // 'di' returns 'di' and likewise. + // gets the tag that is being completed. For ex. 'div.foo > s' returns + // 's', 'di' returns 'di' and likewise. firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1]; query = query.slice(0, query.length - firstPart.length); - } - else if (state === this.States.CLASS) { + } else if (state === this.States.CLASS) { // gets the class that is being completed. For ex. '.foo.b' returns 'b' firstPart = query.match(/\.([^\.]*)$/)[1]; query = query.slice(0, query.length - firstPart.length - 1); - } - else if (state === this.States.ID) { + } else if (state === this.States.ID) { // gets the id that is being completed. For ex. '.foo#b' returns 'b' firstPart = query.match(/#([^#]*)$/)[1]; query = query.slice(0, query.length - firstPart.length - 1); @@ -524,7 +527,9 @@ SelectorAutocompleter.prototype = { query += "*"; } - this._lastQuery = this.walker.getSuggestionsForQuery(query, firstPart, state).then(result => { + let suggestionsPromise = this.walker.getSuggestionsForQuery( + query, firstPart, state); + this._lastQuery = suggestionsPromise.then(result => { this.emit("processing-done"); if (result.query !== query) { // This means that this response is for a previous request and the user @@ -545,10 +550,9 @@ SelectorAutocompleter.prototype = { result.suggestions = []; } - this._showPopup(result.suggestions, firstPart, state); }); - return this._lastQuery; + return; } }; diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js index b0121ebbbbf4..606c985fa55e 100644 --- a/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js @@ -4,32 +4,37 @@ "use strict"; -// Test that hovering over nodes on the breadcrumb buttons in the inspector shows the highlighter over -// those nodes +// Test that hovering over nodes on the breadcrumb buttons in the inspector +// shows the highlighter over those nodes add_task(function* () { info("Loading the test document and opening the inspector"); - let {toolbox, inspector, testActor} = yield openInspectorForURL("data:text/html;charset=utf-8,