diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 1d81509b584e..c5470ca0f5e4 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -347,6 +347,7 @@ pref("browser.urlbar.match.url", "@"); pref("browser.urlbar.suggest.history", true); pref("browser.urlbar.suggest.bookmark", true); pref("browser.urlbar.suggest.openpage", true); +pref("browser.urlbar.suggest.searches", true); // Restrictions to current suggestions can also be applied (intersection). // Typed suggestion works only if history is set to true. @@ -357,8 +358,8 @@ pref("browser.urlbar.trimURLs", true); pref("browser.altClickSave", false); -// Enable logging downloads operations to the Error Console. -pref("browser.download.debug", false); +// Enable logging downloads operations to the Console. +pref("browser.download.loglevel", "Error"); // Number of milliseconds to wait for the http headers (and thus // the Content-Disposition filename) before giving up and falling back to diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index d7c7992debee..15281d1b74bd 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -32,7 +32,7 @@ command="Browser:Stop"/> diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index d219f9a81fc1..5e1824bf5340 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -1344,11 +1344,13 @@ let BookmarkingUI = { if (aState == "invalid") { this.star.setAttribute("disabled", "true"); - this.button.removeAttribute("starred"); - this.button.setAttribute("buttontooltiptext", ""); + this.broadcaster.setAttribute("stardisabled", "true"); + this.broadcaster.removeAttribute("starred"); + this.broadcaster.setAttribute("buttontooltiptext", ""); } else { this.star.removeAttribute("disabled"); + this.broadcaster.removeAttribute("stardisabled"); this._updateStar(); } this._updateToolbarStyle(); @@ -1523,23 +1525,23 @@ let BookmarkingUI = { _updateStar: function BUI__updateStar() { if (!this._shouldUpdateStarState()) { - if (this.button.hasAttribute("starred")) { - this.button.removeAttribute("starred"); - this.button.removeAttribute("buttontooltiptext"); + if (this.broadcaster.hasAttribute("starred")) { + this.broadcaster.removeAttribute("starred"); + this.broadcaster.removeAttribute("buttontooltiptext"); } return; } if (this._itemIds.length > 0) { - this.button.setAttribute("starred", "true"); - this.button.setAttribute("buttontooltiptext", this._starredTooltip); + this.broadcaster.setAttribute("starred", "true"); + this.broadcaster.setAttribute("buttontooltiptext", this._starredTooltip); if (this.button.getAttribute("overflowedItem") == "true") { this.button.setAttribute("label", this._starButtonOverflowedStarredLabel); } } else { - this.button.removeAttribute("starred"); - this.button.setAttribute("buttontooltiptext", this._unstarredTooltip); + this.broadcaster.removeAttribute("starred"); + this.broadcaster.setAttribute("buttontooltiptext", this._unstarredTooltip); if (this.button.getAttribute("overflowedItem") == "true") { this.button.setAttribute("label", this._starButtonOverflowedLabel); } @@ -1553,7 +1555,9 @@ let BookmarkingUI = { _updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) { let isStarred = !forceReset && this._itemIds.length > 0; let label = isStarred ? "editlabel" : "bookmarklabel"; - this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label)); + if (this.broadcaster) { + this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label)); + } }, onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) { @@ -1671,6 +1675,10 @@ let BookmarkingUI = { } }, + onCurrentPageContextPopupShowing() { + this._updateBookmarkPageMenuItem(); + }, + handleEvent: function BUI_handleEvent(aEvent) { switch (aEvent.type) { case "ViewShowing": diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 7539adaa6158..cb4c8833b237 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -1328,3 +1328,28 @@ toolbarpaletteitem[place="palette"][hidden] { #login-fill-doorhanger:not([inDetailView]) > #login-fill-clickcapturer { pointer-events: none; } + +.popup-notification-invalid-input { + box-shadow: 0 0 1.5px 1px red; +} + +.popup-notification-invalid-input[focused] { + box-shadow: 0 0 2px 2px rgba(255,0,0,0.4); +} + +#password-notification-password::after { + color: hsl(0,0%,60%); + content: attr(show-content); + pointer-events: none; + position: absolute; + right: 0; + transition: color 250ms; +} + +#password-notification-password:hover::after { + color: hsl(210,100%,50%); +} + +#password-notification-password[focused]::after { + content: none; +} diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index f062a8218299..0f4b5f7b6c88 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -7189,15 +7189,18 @@ let gPrivateBrowsingUI = { } } - if (gURLBar && - !PrivateBrowsingUtils.permanentPrivateBrowsing) { - // Disable switch to tab autocompletion for private windows. - // We leave it enabled for permanent private browsing mode though. + if (gURLBar) { let value = gURLBar.getAttribute("autocompletesearchparam") || ""; - if (!value.includes("disable-private-actions")) { - gURLBar.setAttribute("autocompletesearchparam", - value + " disable-private-actions"); + if (!PrivateBrowsingUtils.permanentPrivateBrowsing && + !value.includes("disable-private-actions")) { + // Disable switch to tab autocompletion for private windows. + // We leave it enabled for permanent private browsing mode though. + value += " disable-private-actions"; } + if (!value.includes("private-window")) { + value += " private-window"; + } + gURLBar.setAttribute("autocompletesearchparam", value); } } }; diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index e419ecba3a43..485d84c769f1 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -821,6 +821,8 @@ ondrop="PlacesMenuDNDHandler.onDrop(event);" cui-areatype="toolbar" oncommand="BookmarkingUI.onCommand(event);"> + + .selectable:not([selected]):-moz-locale-dir(rtl), +.selectable:not([selected]):hover:-moz-locale-dir(rtl) { + background-position: right 15px center; +} + .newtab-intro-image-customize .newtab-customize-panel-item, .newtab-intro-image-customize .newtab-customize-complex-option { max-width: 100%; @@ -589,12 +599,13 @@ input[type=button] { .newtab-customize-panel-subitem { font-size: 12px; - padding: 0px 15px 15px 40px; + padding: 0px 15px 15px 15px; + -moz-padding-start: 40px; display: block; } .newtab-customize-panel-subitem > label { - padding-left: 10px; + padding: 0px 10px; line-height: 20px; vertical-align: middle; max-width: 225px; @@ -603,7 +614,8 @@ input[type=button] { .newtab-customize-panel-superitem { line-height: 20px; border-bottom: medium none !important; - padding: 15px 15px 10px 40px; + padding: 15px 15px 10px 15px; + -moz-padding-start: 40px; border-top: 1px solid threedshadow; } diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index ca15a131ffa5..f5b752a32415 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -51,6 +51,10 @@ nsContextMenu.prototype = { this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed; this.onPlainTextLink = false; + let bookmarkPage = document.getElementById("context-bookmarkpage"); + if (bookmarkPage) + BookmarkingUI.onCurrentPageContextPopupShowing(); + // Initialize (disable/remove) menu items. this.initItems(); @@ -269,10 +273,14 @@ nsContextMenu.prototype = { initMiscItems: function CM_initMiscItems() { // Use "Bookmark This Link" if on a link. - this.showItem("context-bookmarkpage", + let bookmarkPage = document.getElementById("context-bookmarkpage"); + this.showItem(bookmarkPage, !(this.isContentSelected || this.onTextInput || this.onLink || this.onImage || this.onVideo || this.onAudio || this.onSocial || this.onCanvas)); + bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext")); + bookmarkPage.disabled = bookmarkPage.hasAttribute("stardisabled"); + this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink && !this.onSocial) || this.onPlainTextLink); this.showItem("context-keywordfield", diff --git a/browser/base/content/popup-notifications.inc b/browser/base/content/popup-notifications.inc index d5bea0637447..b50ff5e6fb30 100644 --- a/browser/base/content/popup-notifications.inc +++ b/browser/base/content/popup-notifications.inc @@ -57,8 +57,7 @@ diff --git a/browser/components/downloads/DownloadsCommon.jsm b/browser/components/downloads/DownloadsCommon.jsm index 2fb06126ed29..1df637571909 100644 --- a/browser/components/downloads/DownloadsCommon.jsm +++ b/browser/components/downloads/DownloadsCommon.jsm @@ -33,10 +33,7 @@ this.EXPORTED_SYMBOLS = [ //////////////////////////////////////////////////////////////////////////////// //// Globals -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -65,8 +62,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger", - "resource:///modules/DownloadsLogger.jsm"); + +XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => { + let { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm", {}); + let consoleOptions = { + maxLogLevelPref: "browser.download.loglevel", + prefix: "Downloads" + }; + return new ConsoleAPI(consoleOptions); +}); const nsIDM = Ci.nsIDownloadManager; @@ -124,7 +128,6 @@ let PrefObserver = { PrefObserver.register({ // prefName: defaultValue - debug: false, animateNotifications: true }); @@ -144,20 +147,6 @@ this.DownloadsCommon = { BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted", BLOCK_VERDICT_UNCOMMON: "Uncommon", - log(...aMessageArgs) { - if (!PrefObserver.debug) { - return; - } - DownloadsLogger.log(...aMessageArgs); - }, - - error(...aMessageArgs) { - if (!PrefObserver.debug) { - return; - } - DownloadsLogger.reportError(...aMessageArgs); - }, - /** * Returns an object whose keys are the string names from the downloads string * bundle, and whose values are either the translated strings or functions @@ -601,6 +590,13 @@ this.DownloadsCommon = { }), }; +XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "log", () => { + return DownloadsLogger.log.bind(DownloadsLogger); +}); +XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => { + return DownloadsLogger.error.bind(DownloadsLogger); +}); + /** * Returns true if we are executing on Windows Vista or a later version. */ diff --git a/browser/components/downloads/DownloadsLogger.jsm b/browser/components/downloads/DownloadsLogger.jsm deleted file mode 100644 index a24c9acc8a1c..000000000000 --- a/browser/components/downloads/DownloadsLogger.jsm +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/** - * The contents of this file were copied almost entirely from - * toolkit/identity/LogUtils.jsm. Until we've got a more generalized logging - * mechanism for toolkit, I think this is going to be how we roll. - */ - -"use strict"; - -this.EXPORTED_SYMBOLS = ["DownloadsLogger"]; -const PREF_DEBUG = "browser.download.debug"; - -const Cu = Components.utils; -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cr = Components.results; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -this.DownloadsLogger = { - _generateLogMessage(args) { - // create a string representation of a list of arbitrary things - let strings = []; - - for (let arg of args) { - if (typeof arg === 'string') { - strings.push(arg); - } else if (arg === undefined) { - strings.push('undefined'); - } else if (arg === null) { - strings.push('null'); - } else { - try { - strings.push(JSON.stringify(arg, null, 2)); - } catch(err) { - strings.push("<>"); - } - } - }; - return 'Downloads: ' + strings.join(' '); - }, - - /** - * log() - utility function to print a list of arbitrary things - * - * Enable with about:config pref browser.download.debug - */ - log(...args) { - let output = this._generateLogMessage(args); - dump(output + "\n"); - - // Additionally, make the output visible in the Error Console - Services.console.logStringMessage(output); - }, - - /** - * reportError() - report an error through component utils as well as - * our log function - */ - reportError(...aArgs) { - // Report the error in the browser - let output = this._generateLogMessage(aArgs); - Cu.reportError(output); - dump("ERROR:" + output + "\n"); - for (let frame = Components.stack.caller; frame; frame = frame.caller) { - dump("\t" + frame + "\n"); - } - }, -}; diff --git a/browser/components/downloads/moz.build b/browser/components/downloads/moz.build index 87e7c39e791f..9d205cae6467 100644 --- a/browser/components/downloads/moz.build +++ b/browser/components/downloads/moz.build @@ -14,7 +14,6 @@ JAR_MANIFESTS += ['jar.mn'] EXTRA_JS_MODULES += [ 'DownloadsCommon.jsm', - 'DownloadsLogger.jsm', 'DownloadsTaskbar.jsm', 'DownloadsViewUI.jsm', ] diff --git a/browser/components/migration/360seProfileMigrator.js b/browser/components/migration/360seProfileMigrator.js new file mode 100644 index 000000000000..1be79e11a4a4 --- /dev/null +++ b/browser/components/migration/360seProfileMigrator.js @@ -0,0 +1,260 @@ +/* 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"; + +const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource:///modules/MigrationUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", + "resource://gre/modules/PlacesUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", + "resource://gre/modules/Sqlite.jsm"); + +function parseINIStrings(file) { + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. + getService(Ci.nsIINIParserFactory); + let parser = factory.createINIParser(file); + let obj = {}; + let sections = parser.getSections(); + while (sections.hasMore()) { + let section = sections.getNext(); + obj[section] = {}; + + let keys = parser.getKeys(section); + while (keys.hasMore()) { + let key = keys.getNext(); + obj[section][key] = parser.getString(section, key); + } + } + return obj; +} + +function getHash(aStr) { + // return the two-digit hexadecimal code for a byte + function toHexString(charCode) + ("0" + charCode.toString(16)).slice(-2); + + let hasher = Cc["@mozilla.org/security/hash;1"]. + createInstance(Ci.nsICryptoHash); + hasher.init(Ci.nsICryptoHash.MD5); + let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]. + createInstance(Ci.nsIStringInputStream); + stringStream.data = aStr; + hasher.updateFromStream(stringStream, -1); + + // convert the binary hash data to a hex string. + let binary = hasher.finish(false); + return [toHexString(binary.charCodeAt(i)) for (i in binary)].join("").toLowerCase(); +} + +function Bookmarks(aProfileFolder) { + let file = aProfileFolder.clone(); + file.append("360sefav.db"); + + this._file = file; +} +Bookmarks.prototype = { + type: MigrationUtils.resourceTypes.BOOKMARKS, + + get exists() { + return this._file.exists() && this._file.isReadable(); + }, + + migrate: function (aCallback) { + return Task.spawn(function* () { + let idToGuid = new Map(); + let folderGuid = PlacesUtils.bookmarks.toolbarGuid; + if (!MigrationUtils.isStartupMigration) { + folderGuid = + yield MigrationUtils.createImportedBookmarksFolder("360se", folderGuid); + } + idToGuid.set(0, folderGuid); + + let connection = yield Sqlite.openConnection({ + path: this._file.path + }); + + try { + let rows = yield connection.execute( + `WITH RECURSIVE + bookmark(id, parent_id, is_folder, title, url, pos) AS ( + VALUES(0, -1, 1, '', '', 0) + UNION + SELECT f.id, f.parent_id, f.is_folder, f.title, f.url, f.pos + FROM tb_fav AS f + JOIN bookmark AS b ON f.parent_id = b.id + ORDER BY f.pos ASC + ) + SELECT id, parent_id, is_folder, title, url FROM bookmark WHERE id`); + + for (let row of rows) { + let id = parseInt(row.getResultByName("id"), 10), + parent_id = parseInt(row.getResultByName("parent_id"), 10), + is_folder = parseInt(row.getResultByName("is_folder"), 10), + title = row.getResultByName("title"), + url = row.getResultByName("url"); + + let parentGuid = idToGuid.get(parent_id) || idToGuid.get("fallback"); + if (!parentGuid) { + parentGuid = PlacesUtils.bookmarks.unfiledGuid; + if (!MigrationUtils.isStartupMigration) { + parentGuid = + yield MigrationUtils.createImportedBookmarksFolder("360se", parentGuid); + } + idToGuid.set("fallback", parentGuid); + } + + try { + if (is_folder == 1) { + let newFolderGuid = (yield PlacesUtils.bookmarks.insert({ + parentGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title + })).guid; + + idToGuid.set(id, newFolderGuid); + } else { + yield PlacesUtils.bookmarks.insert({ + parentGuid, + url, + title + }); + } + } catch (ex) { + Cu.reportError(ex); + } + } + } finally { + yield connection.close(); + } + }.bind(this)).then(() => aCallback(true), + e => { Cu.reportError(e); aCallback(false) }); + } +}; + +function Qihoo360seProfileMigrator() { + let paths = [ + // for v6 and above + { + users: ["360se6", "apps", "data", "users"], + defaultUser: "default" + }, + // for earlier versions + { + users: ["360se"], + defaultUser: "data" + } + ]; + this._usersDir = null; + this._defaultUserPath = null; + for (let path of paths) { + let usersDir = FileUtils.getDir("AppData", path.users, false); + if (usersDir.exists()) { + this._usersDir = usersDir; + this._defaultUserPath = path.defaultUser; + break; + } + } +} + +Qihoo360seProfileMigrator.prototype = Object.create(MigratorPrototype); + +Object.defineProperty(Qihoo360seProfileMigrator.prototype, "sourceProfiles", { + get: function() { + if ("__sourceProfiles" in this) + return this.__sourceProfiles; + + if (!this._usersDir) + return this.__sourceProfiles = []; + + let profiles = []; + let noLoggedInUser = true; + try { + let loginIni = this._usersDir.clone(); + loginIni.append("login.ini"); + if (!loginIni.exists()) { + throw new Error("360 Secure Browser's 'login.ini' does not exist."); + } + if (!loginIni.isReadable()) { + throw new Error("360 Secure Browser's 'login.ini' file could not be read."); + } + let loginIniObj = parseINIStrings(loginIni); + let nowLoginEmail = loginIniObj.NowLogin && loginIniObj.NowLogin.email; + + /* + * NowLogin section may: + * 1. be missing or without email, before any user logs in. + * 2. represents the current logged in user + * 3. represents the most recent logged in user + * + * In the second case, user represented by NowLogin should be the first + * profile; otherwise the default user should be selected by default. + */ + if (nowLoginEmail) { + if (loginIniObj.NowLogin.IsLogined === "1") { + noLoggedInUser = false; + } + + profiles.push({ + id: this._getIdFromConfig(loginIniObj.NowLogin), + name: nowLoginEmail, + }); + } + + for (let section in loginIniObj) { + if (!loginIniObj[section].email || + (nowLoginEmail && loginIniObj[section].email == nowLoginEmail)) { + continue; + } + + profiles.push({ + id: this._getIdFromConfig(loginIniObj[section]), + name: loginIniObj[section].email, + }); + } + } catch (e) { + Cu.reportError("Error detecting 360 Secure Browser profiles: " + e); + } finally { + profiles[noLoggedInUser ? "unshift" : "push"]({ + id: this._defaultUserPath, + name: "Default", + }); + } + + return this.__sourceProfiles = profiles.filter(profile => { + let resources = this.getResources(profile); + return resources && resources.length > 0; + }); + } +}); + +Qihoo360seProfileMigrator.prototype._getIdFromConfig = function(aConfig) { + return aConfig.UserMd5 || getHash(aConfig.email); +}; + +Qihoo360seProfileMigrator.prototype.getResources = function(aProfile) { + let profileFolder = this._usersDir.clone(); + profileFolder.append(aProfile.id); + + if (!profileFolder.exists()) { + return []; + } + + let resources = [ + new Bookmarks(profileFolder) + ]; + return [r for each (r in resources) if (r.exists)]; +}; + +Qihoo360seProfileMigrator.prototype.classDescription = "360 Secure Browser Profile Migrator"; +Qihoo360seProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=360se"; +Qihoo360seProfileMigrator.prototype.classID = Components.ID("{d0037b95-296a-4a4e-94b2-c3d075d20ab1}"); + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Qihoo360seProfileMigrator]); diff --git a/browser/components/migration/BrowserProfileMigrators.manifest b/browser/components/migration/BrowserProfileMigrators.manifest index d7fec75e351e..000a3247f990 100644 --- a/browser/components/migration/BrowserProfileMigrators.manifest +++ b/browser/components/migration/BrowserProfileMigrators.manifest @@ -12,3 +12,7 @@ contract @mozilla.org/profile/migrator;1?app=browser&type=ie {3d2532e3-4932-4774 component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1} #endif +#ifdef HAS_360SE_MIGRATOR +component {d0037b95-296a-4a4e-94b2-c3d075d20ab1} 360seProfileMigrator.js +contract @mozilla.org/profile/migrator;1?app=browser&type=360se {d0037b95-296a-4a4e-94b2-c3d075d20ab1} +#endif diff --git a/browser/components/migration/MigrationUtils.jsm b/browser/components/migration/MigrationUtils.jsm index 1353e1bc0b12..507bab313d6f 100644 --- a/browser/components/migration/MigrationUtils.jsm +++ b/browser/components/migration/MigrationUtils.jsm @@ -43,11 +43,12 @@ function getMigrationBundle() { */ function getMigratorKeyForDefaultBrowser() { const APP_DESC_TO_KEY = { - "Internet Explorer": "ie", - "Safari": "safari", - "Firefox": "firefox", - "Google Chrome": "chrome", // Windows, Linux - "Chrome": "chrome", // OS X + "Internet Explorer": "ie", + "Safari": "safari", + "Firefox": "firefox", + "Google Chrome": "chrome", // Windows, Linux + "Chrome": "chrome", // OS X + "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se", }; let browserDesc = ""; @@ -450,6 +451,7 @@ this.MigrationUtils = Object.freeze({ * Supported values: ie (windows), * safari (mac/windows), * chrome (mac/windows/linux), + * 360se (windows), * firefox. * * If null is returned, either no data can be imported @@ -482,7 +484,7 @@ this.MigrationUtils = Object.freeze({ get migrators() { let migratorKeysOrdered = [ #ifdef XP_WIN - "firefox", "ie", "chrome", "safari" + "firefox", "ie", "chrome", "safari", "360se" #elifdef XP_MACOSX "firefox", "safari", "chrome" #elifdef XP_UNIX diff --git a/browser/components/migration/content/migration.js b/browser/components/migration/content/migration.js index 725944ef063d..c7afd433deb0 100644 --- a/browser/components/migration/content/migration.js +++ b/browser/components/migration/content/migration.js @@ -292,6 +292,9 @@ var MigrationWizard = { case "firefox": source = "sourceNameFirefox"; break; + case "360se": + source = "sourceName360se"; + break; } // semi-wallpaper for crash when multiple profiles exist, since we haven't initialized mSourceProfile in places diff --git a/browser/components/migration/content/migration.xul b/browser/components/migration/content/migration.xul index ef80d047cf5c..9c78edd0f172 100644 --- a/browser/components/migration/content/migration.xul +++ b/browser/components/migration/content/migration.xul @@ -37,6 +37,7 @@ + #elifdef XP_MACOSX diff --git a/browser/components/migration/moz.build b/browser/components/migration/moz.build index 2f16278e9b71..55b3738e5388 100644 --- a/browser/components/migration/moz.build +++ b/browser/components/migration/moz.build @@ -26,8 +26,10 @@ EXTRA_COMPONENTS += [ if CONFIG['OS_ARCH'] == 'WINNT': EXTRA_COMPONENTS += [ + '360seProfileMigrator.js', 'IEProfileMigrator.js', ] + DEFINES['HAS_360SE_MIGRATOR'] = True DEFINES['HAS_IE_MIGRATOR'] = True EXTRA_PP_COMPONENTS += [ diff --git a/browser/components/preferences/fonts.xul b/browser/components/preferences/fonts.xul index 8ef7f5b2697f..172ea440a3f6 100644 --- a/browser/components/preferences/fonts.xul +++ b/browser/components/preferences/fonts.xul @@ -67,6 +67,7 @@ + diff --git a/browser/components/preferences/in-content/privacy.js b/browser/components/preferences/in-content/privacy.js index 97fa72316b4f..c498c7ca5ab3 100644 --- a/browser/components/preferences/in-content/privacy.js +++ b/browser/components/preferences/in-content/privacy.js @@ -358,12 +358,12 @@ var gPrivacyPane = { * Update browser.urlbar.autocomplete.enabled when a * browser.urlbar.suggest.* pref is changed from the ui. */ - writeSuggestionPref: function () { + writeSuggestionPref() { let getVal = (aPref) => { return document.getElementById("browser.urlbar.suggest." + aPref).value; } // autocomplete.enabled is true if any of the suggestions is true - let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v); + let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v); Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled); }, diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul index 0f13aaf7ed1f..557c1d8b7365 100644 --- a/browser/components/preferences/in-content/privacy.xul +++ b/browser/components/preferences/in-content/privacy.xul @@ -38,6 +38,9 @@ + + diff --git a/browser/components/preferences/in-content/tests/browser_privacypane_5.js b/browser/components/preferences/in-content/tests/browser_privacypane_5.js index 5225337d5805..5143231156c5 100644 --- a/browser/components/preferences/in-content/tests/browser_privacypane_5.js +++ b/browser/components/preferences/in-content/tests/browser_privacypane_5.js @@ -15,6 +15,7 @@ function test() { run_test_subset([ test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("bookmark", true), + test_locbar_suggestion_retention("searches", true), test_locbar_suggestion_retention("openpage", false), test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", false), @@ -22,4 +23,4 @@ function test() { // reset all preferences to their default values once we're done reset_preferences ]); -} \ No newline at end of file +} diff --git a/browser/components/preferences/privacy.js b/browser/components/preferences/privacy.js index 701a4cfb1c19..d7dadba42498 100644 --- a/browser/components/preferences/privacy.js +++ b/browser/components/preferences/privacy.js @@ -325,12 +325,12 @@ var gPrivacyPane = { * Update browser.urlbar.autocomplete.enabled when a * browser.urlbar.suggest.* pref is changed from the ui. */ - writeSuggestionPref: function PPP_writeSuggestionPref() { + writeSuggestionPref() { let getVal = (aPref) => { return document.getElementById("browser.urlbar.suggest." + aPref).value; } // autocomplete.enabled is true if any of the suggestions is true - let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v); + let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v); Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled); }, diff --git a/browser/components/preferences/privacy.xul b/browser/components/preferences/privacy.xul index d6f9a0c1b737..f91e145ca961 100644 --- a/browser/components/preferences/privacy.xul +++ b/browser/components/preferences/privacy.xul @@ -51,6 +51,9 @@ + + diff --git a/browser/components/preferences/tests/browser_privacypane_5.js b/browser/components/preferences/tests/browser_privacypane_5.js index 5225337d5805..5143231156c5 100644 --- a/browser/components/preferences/tests/browser_privacypane_5.js +++ b/browser/components/preferences/tests/browser_privacypane_5.js @@ -15,6 +15,7 @@ function test() { run_test_subset([ test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("bookmark", true), + test_locbar_suggestion_retention("searches", true), test_locbar_suggestion_retention("openpage", false), test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", false), @@ -22,4 +23,4 @@ function test() { // reset all preferences to their default values once we're done reset_preferences ]); -} \ No newline at end of file +} diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 4d6a4a9a4972..8452de7f5e0f 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -839,17 +839,12 @@ let SessionStoreInternal = { }, /** - * If it's the first window load since app start... - * - determine if we're reloading after a crash or a forced-restart - * - restore window state - * - restart downloads - * Set up event listeners for this window's tabs + * Registers and tracks a given window. + * * @param aWindow * Window reference - * @param aInitialState - * The initial state to be loaded after startup (optional) */ - onLoad: function ssi_onLoad(aWindow, aInitialState = null) { + onLoad(aWindow) { // return if window has already been initialized if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi]) return; @@ -874,14 +869,43 @@ let SessionStoreInternal = { // and create its data object this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false }; - let isPrivateWindow = false; if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) - this._windows[aWindow.__SSi].isPrivate = isPrivateWindow = true; + this._windows[aWindow.__SSi].isPrivate = true; if (!this._isWindowLoaded(aWindow)) this._windows[aWindow.__SSi]._restoring = true; if (!aWindow.toolbar.visible) this._windows[aWindow.__SSi].isPopup = true; + let tabbrowser = aWindow.gBrowser; + + // add tab change listeners to all already existing tabs + for (let i = 0; i < tabbrowser.tabs.length; i++) { + this.onTabAdd(aWindow, tabbrowser.tabs[i], true); + } + // notification of tab add/remove/selection/show/hide + TAB_EVENTS.forEach(function(aEvent) { + tabbrowser.tabContainer.addEventListener(aEvent, this, true); + }, this); + + // Keep track of a browser's latest frameLoader. + aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this); + }, + + /** + * Initializes a given window. + * + * Windows are registered as soon as they are created but we need to wait for + * the session file to load, and the initial window's delayed startup to + * finish before initializing a window, i.e. restoring data into it. + * + * @param aWindow + * Window reference + * @param aInitialState + * The initial state to be loaded after startup (optional) + */ + initializeWindow(aWindow, aInitialState = null) { + let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(aWindow); + // perform additional initialization when the first window is loading if (RunState.isStopped) { RunState.setRunning(); @@ -1007,20 +1031,6 @@ let SessionStoreInternal = { // undoCloseWindow was executed. this._restoreLastWindow = false; } - - var tabbrowser = aWindow.gBrowser; - - // add tab change listeners to all already existing tabs - for (let i = 0; i < tabbrowser.tabs.length; i++) { - this.onTabAdd(aWindow, tabbrowser.tabs[i], true); - } - // notification of tab add/remove/selection/show/hide - TAB_EVENTS.forEach(function(aEvent) { - tabbrowser.tabContainer.addEventListener(aEvent, this, true); - }, this); - - // Keep track of a browser's latest frameLoader. - aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this); }, /** @@ -1029,9 +1039,12 @@ let SessionStoreInternal = { * Window reference */ onBeforeBrowserWindowShown: function (aWindow) { - // Just call onLoad() directly if we're initialized already. + // Register the window. + this.onLoad(aWindow); + + // Just call initializeWindow() directly if we're initialized already. if (this._sessionInitialized) { - this.onLoad(aWindow); + this.initializeWindow(aWindow); return; } @@ -1067,13 +1080,13 @@ let SessionStoreInternal = { } if (this._sessionInitialized) { - this.onLoad(aWindow); + this.initializeWindow(aWindow); } else { let initialState = this.initSession(); this._sessionInitialized = true; TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS"); - this.onLoad(aWindow, initialState); + this.initializeWindow(aWindow, initialState); TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS"); // Let everyone know we're done. @@ -2173,6 +2186,7 @@ let SessionStoreInternal = { */ navigateAndRestore(tab, loadArguments, historyIndex) { let window = tab.ownerDocument.defaultView; + NS_ASSERT(window.__SSi, "tab's window must be tracked"); let browser = tab.linkedBrowser; // Set tab title to "Connecting..." and start the throbber to pretend we're diff --git a/browser/devtools/framework/test/browser_toolbox_minimize.js b/browser/devtools/framework/test/browser_toolbox_minimize.js index e213fcfa2978..ae75861cd5ea 100644 --- a/browser/devtools/framework/test/browser_toolbox_minimize.js +++ b/browser/devtools/framework/test/browser_toolbox_minimize.js @@ -30,6 +30,16 @@ add_task(function*() { ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0, "The toolbox host is shown again"); + info("Try to minimize again using the keyboard shortcut"); + yield minimizeWithShortcut(toolbox); + ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0, + "The toolbox host has been hidden away with a negative-margin"); + + info("Try to maximize again using the keyboard shortcut"); + yield maximizeWithShortcut(toolbox); + ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0, + "The toolbox host is shown again"); + info("Minimize again and switch to another tool"); yield minimize(toolbox); let onMaximized = toolbox._host.once("maximized"); @@ -67,9 +77,27 @@ function* minimize(toolbox) { yield onMinimized; } +function* minimizeWithShortcut(toolbox) { + let key = toolbox.doc.getElementById("toolbox-minimize-key") + .getAttribute("key"); + let onMinimized = toolbox._host.once("minimized"); + EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true}, + toolbox.doc.defaultView); + yield onMinimized; +} + function* maximize(toolbox) { let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize"); let onMaximized = toolbox._host.once("maximized"); EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView); yield onMaximized; } + +function* maximizeWithShortcut(toolbox) { + let key = toolbox.doc.getElementById("toolbox-minimize-key") + .getAttribute("key"); + let onMaximized = toolbox._host.once("maximized"); + EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true}, + toolbox.doc.defaultView); + yield onMaximized; +} diff --git a/browser/devtools/framework/toolbox.js b/browser/devtools/framework/toolbox.js index 2f8403573f83..cd51057c7804 100644 --- a/browser/devtools/framework/toolbox.js +++ b/browser/devtools/framework/toolbox.js @@ -2,8 +2,8 @@ * 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/. */ /* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection, - getPerformanceActorsConnection, CommandUtils, DevToolsUtils, screenManager, - oscpu, Hosts, is64Bit */ + CommandUtils, DevToolsUtils, screenManager, oscpu, Hosts, is64Bit, + osString, showDoorhanger, getHighlighterUtils, getPerformanceFront */ "use strict"; @@ -21,24 +21,20 @@ let {Cc, Ci, Cu} = require("chrome"); let {Promise: promise} = require("resource://gre/modules/Promise.jsm"); let EventEmitter = require("devtools/toolkit/event-emitter"); let Telemetry = require("devtools/shared/telemetry"); -let {getHighlighterUtils} = require("devtools/framework/toolbox-highlighter-utils"); let HUDService = require("devtools/webconsole/hudservice"); -let {showDoorhanger} = require("devtools/shared/doorhanger"); let sourceUtils = require("devtools/shared/source-utils"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource:///modules/devtools/scratchpad-manager.jsm"); Cu.import("resource:///modules/devtools/DOMHelpers.jsm"); Cu.import("resource://gre/modules/Task.jsm"); -loader.lazyGetter(this, "Hosts", () => require("devtools/framework/toolbox-hosts").Hosts); - -loader.lazyImporter(this, "CommandUtils", "resource:///modules/devtools/DeveloperToolbar.jsm"); - +loader.lazyImporter(this, "CommandUtils", + "resource:///modules/devtools/DeveloperToolbar.jsm"); loader.lazyGetter(this, "toolboxStrings", () => { - let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); + const properties = "chrome://browser/locale/devtools/toolbox.properties"; + const bundle = Services.strings.createBundle(properties); return (name, ...args) => { try { if (!args.length) { @@ -51,22 +47,31 @@ loader.lazyGetter(this, "toolboxStrings", () => { } }; }); - -loader.lazyGetter(this, "Selection", () => require("devtools/framework/selection").Selection); -loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront); -loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/toolkit/DevToolsUtils"); -loader.lazyRequireGetter(this, "getPerformanceFront", "devtools/performance/front", true); - -XPCOMUtils.defineLazyGetter(this, "screenManager", () => { +loader.lazyRequireGetter(this, "getHighlighterUtils", + "devtools/framework/toolbox-highlighter-utils", true); +loader.lazyRequireGetter(this, "Hosts", + "devtools/framework/toolbox-hosts", true); +loader.lazyRequireGetter(this, "Selection", + "devtools/framework/selection", true); +loader.lazyRequireGetter(this, "InspectorFront", + "devtools/server/actors/inspector", true); +loader.lazyRequireGetter(this, "DevToolsUtils", + "devtools/toolkit/DevToolsUtils"); +loader.lazyRequireGetter(this, "showDoorhanger", + "devtools/shared/doorhanger", true); +loader.lazyRequireGetter(this, "getPerformanceFront", + "devtools/performance/front", true); +loader.lazyGetter(this, "osString", () => { + return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; +}); +loader.lazyGetter(this, "screenManager", () => { return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager); }); - -XPCOMUtils.defineLazyGetter(this, "oscpu", () => { +loader.lazyGetter(this, "oscpu", () => { return Cc["@mozilla.org/network/protocol;1?name=http"] .getService(Ci.nsIHttpProtocolHandler).oscpu; }); - -XPCOMUtils.defineLazyGetter(this, "is64Bit", () => { +loader.lazyGetter(this, "is64Bit", () => { return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit; }); @@ -79,8 +84,9 @@ const ToolboxButtons = exports.ToolboxButtons = [ target.getTrait("highlightable") }, { id: "command-button-frames", - isTargetSupported: target => - ( target.activeTab && target.activeTab.traits.frames ) + isTargetSupported: target => { + return target.activeTab && target.activeTab.traits.frames; + } }, { id: "command-button-splitconsole", isTargetSupported: target => !target.isAddon }, @@ -135,6 +141,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) { this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this); this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this); this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this); + this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this); this._target.on("close", this.destroy); @@ -481,7 +488,7 @@ Toolbox.prototype = { ["toolbox-force-reload-key", true], ["toolbox-force-reload-key2", true] ].forEach(([id, force]) => { - this.doc.getElementById(id).addEventListener("command", (event) => { + this.doc.getElementById(id).addEventListener("command", () => { this.reloadTarget(force); }, true); }); @@ -493,6 +500,9 @@ Toolbox.prototype = { let prevKey = this.doc.getElementById("toolbox-previous-tool-key"); prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true); + let minimizeKey = this.doc.getElementById("toolbox-minimize-key"); + minimizeKey.addEventListener("command", this._toggleMinimizeMode, true); + // Split console uses keypress instead of command so the event can be // cancelled with stopPropagation on the keypress, and not preventDefault. this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false); @@ -703,18 +713,11 @@ Toolbox.prototype = { if (this.hostType == Toolbox.HostType.BOTTOM) { let minimizeBtn = this.doc.createElement("toolbarbutton"); minimizeBtn.id = "toolbox-dock-bottom-minimize"; - minimizeBtn.className = "maximized"; - minimizeBtn.setAttribute("tooltiptext", - toolboxStrings("toolboxDockButtons.bottom.minimize")); - // Calculate the height to which the host should be minimized so the - // tabbar is still visible. - let toolbarHeight = this.doc.querySelector(".devtools-tabbar") - .getBoxQuads({box: "content"})[0] - .bounds.height; - minimizeBtn.addEventListener("command", () => { - this._host.toggleMinimizeMode(toolbarHeight); - }); + + minimizeBtn.addEventListener("command", this._toggleMinimizeMode); dockBox.appendChild(minimizeBtn); + // Show the button in its maximized state. + this._onBottomHostMaximized(); // Update the label and icon when the state changes. this._host.on("minimized", this._onBottomHostMinimized); @@ -754,18 +757,29 @@ Toolbox.prototype = { } }, + _getMinimizeButtonShortcutTooltip: function() { + let key = this.doc.getElementById("toolbox-minimize-key") + .getAttribute("key"); + return "(" + (osString == "Darwin" ? "Cmd+Shift+" : "Ctrl+Shift+") + + key.toUpperCase() + ")"; + }, + _onBottomHostMinimized: function() { let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize"); btn.className = "minimized"; + btn.setAttribute("tooltiptext", - toolboxStrings("toolboxDockButtons.bottom.maximize")); + toolboxStrings("toolboxDockButtons.bottom.maximize") + " " + + this._getMinimizeButtonShortcutTooltip()); }, _onBottomHostMaximized: function() { let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize"); btn.className = "maximized"; + btn.setAttribute("tooltiptext", - toolboxStrings("toolboxDockButtons.bottom.minimize")); + toolboxStrings("toolboxDockButtons.bottom.minimize") + " " + + this._getMinimizeButtonShortcutTooltip()); }, _onToolSelectWhileMinimized: function() { @@ -780,6 +794,19 @@ Toolbox.prototype = { this.off("before-select", this._onToolSelectWhileMinimized); }, + _toggleMinimizeMode: function() { + if (this.hostType !== Toolbox.HostType.BOTTOM) { + return; + } + + // Calculate the height to which the host should be minimized so the + // tabbar is still visible. + let toolbarHeight = this.doc.querySelector(".devtools-tabbar") + .getBoxQuads({box: "content"})[0] + .bounds.height; + this._host.toggleMinimizeMode(toolbarHeight); + }, + /** * Add tabs to the toolbox UI for registered tools */ diff --git a/browser/devtools/framework/toolbox.xul b/browser/devtools/framework/toolbox.xul index 77e99108f17e..26c85c2e3e8b 100644 --- a/browser/devtools/framework/toolbox.xul +++ b/browser/devtools/framework/toolbox.xul @@ -70,6 +70,10 @@ keycode="VK_F5" oncommand="void(0);" modifiers="accel"/> + diff --git a/browser/devtools/markupview/test/browser_markupview_search_01.js b/browser/devtools/markupview/test/browser_markupview_search_01.js index 3409bdaecc9a..6dd826047557 100644 --- a/browser/devtools/markupview/test/browser_markupview_search_01.js +++ b/browser/devtools/markupview/test/browser_markupview_search_01.js @@ -11,7 +11,7 @@ const TEST_URL = TEST_URL_ROOT + "doc_markup_search.html"; add_task(function*() { - let {inspector, toolbox} = yield addTab(TEST_URL).then(openInspector); + let {inspector} = yield addTab(TEST_URL).then(openInspector); let container = yield getContainerForSelector("em", inspector); ok(!container, "The tag isn't present yet in the markup-view"); @@ -19,11 +19,10 @@ add_task(function*() { // Searching for the innermost element first makes sure that the inspector // back-end is able to attach the resulting node to the tree it knows at the // moment. When the inspector is started, the is the default selected - // node, and only the parents up to the ROOT are known, and its direct children + // node, and only the parents up to the ROOT are known, and its direct + // children. info("searching for the innermost child: "); - let updated = inspector.once("inspector-updated"); - searchUsingSelectorSearch("em", inspector); - yield updated; + yield searchFor("em", inspector); container = yield getContainerForSelector("em", inspector); ok(container, "The tag is now imported in the markup-view"); @@ -34,12 +33,19 @@ add_task(function*() { info("searching for other nodes too"); for (let node of ["span", "li", "ul"]) { - let updated = inspector.once("inspector-updated"); - searchUsingSelectorSearch(node, inspector); - yield updated; + yield searchFor(node, inspector); nodeFront = yield getNodeFront(node, inspector); is(inspector.selection.nodeFront, nodeFront, "The <" + node + "> tag is the currently selected node"); } }); + +function* searchFor(selector, inspector) { + let onNewNodeFront = inspector.selection.once("new-node-front"); + + searchUsingSelectorSearch(selector, inspector); + + yield onNewNodeFront; + yield inspector.once("inspector-updated"); +} diff --git a/browser/devtools/netmonitor/netmonitor-controller.js b/browser/devtools/netmonitor/netmonitor-controller.js index 753caff3921a..cbd9dc19c2b0 100644 --- a/browser/devtools/netmonitor/netmonitor-controller.js +++ b/browser/devtools/netmonitor/netmonitor-controller.js @@ -23,6 +23,9 @@ const EVENTS = { // more information about what each packet is supposed to deliver. NETWORK_EVENT: "NetMonitor:NetworkEvent", + // When a network event is added to the view + REQUEST_ADDED: "NetMonitor:RequestAdded", + // When request headers begin and finish receiving. UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders", RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders", @@ -528,34 +531,33 @@ NetworkEventsHandler.prototype = { */ _onNetworkEventUpdate: function(type, { packet, networkInfo }) { let { actor, request: { url } } = networkInfo; - switch (packet.updateType) { case "requestHeaders": this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders); - window.emit(EVENTS.UPDATING_REQUEST_HEADERS, [actor, url]); + window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor); break; case "requestCookies": this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies); - window.emit(EVENTS.UPDATING_REQUEST_COOKIES, [actor, url]); + window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor); break; case "requestPostData": this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData); - window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, [actor, url]); + window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor); break; case "securityInfo": NetMonitorView.RequestsMenu.updateRequest(actor, { securityState: networkInfo.securityInfo, }); this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo); - window.emit(EVENTS.UPDATING_SECURITY_INFO, [actor, url]); + window.emit(EVENTS.UPDATING_SECURITY_INFO, actor); break; case "responseHeaders": this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders); - window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, [actor, url]); + window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor); break; case "responseCookies": this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies); - window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, [actor, url]); + window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor); break; case "responseStart": NetMonitorView.RequestsMenu.updateRequest(actor, { @@ -566,7 +568,7 @@ NetworkEventsHandler.prototype = { statusText: networkInfo.response.statusText, headersSize: networkInfo.response.headersSize }); - window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, [actor, url]); + window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor); break; case "responseContent": NetMonitorView.RequestsMenu.updateRequest(actor, { @@ -575,14 +577,14 @@ NetworkEventsHandler.prototype = { mimeType: networkInfo.response.content.mimeType }); this.webConsoleClient.getResponseContent(actor, this._onResponseContent); - window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, [actor, url]); + window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor); break; case "eventTimings": NetMonitorView.RequestsMenu.updateRequest(actor, { totalTime: networkInfo.totalTime }); this.webConsoleClient.getEventTimings(actor, this._onEventTimings); - window.emit(EVENTS.UPDATING_EVENT_TIMINGS, [actor, url]); + window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor); break; } }, @@ -596,8 +598,9 @@ NetworkEventsHandler.prototype = { _onRequestHeaders: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { requestHeaders: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from); }); - window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from); }, /** @@ -609,8 +612,9 @@ NetworkEventsHandler.prototype = { _onRequestCookies: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { requestCookies: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from); }); - window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from); }, /** @@ -622,8 +626,9 @@ NetworkEventsHandler.prototype = { _onRequestPostData: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { requestPostData: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from); }); - window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from); }, /** @@ -635,9 +640,9 @@ NetworkEventsHandler.prototype = { _onSecurityInfo: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { securityInfo: aResponse.securityInfo + }, () => { + window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from); }); - - window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from); }, /** @@ -649,8 +654,9 @@ NetworkEventsHandler.prototype = { _onResponseHeaders: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { responseHeaders: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from); }); - window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from); }, /** @@ -662,8 +668,9 @@ NetworkEventsHandler.prototype = { _onResponseCookies: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { responseCookies: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from); }); - window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from); }, /** @@ -675,8 +682,9 @@ NetworkEventsHandler.prototype = { _onResponseContent: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { responseContent: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from); }); - window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from); }, /** @@ -688,8 +696,9 @@ NetworkEventsHandler.prototype = { _onEventTimings: function(aResponse) { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { eventTimings: aResponse + }, () => { + window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from); }); - window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from); }, /** diff --git a/browser/devtools/netmonitor/netmonitor-view.js b/browser/devtools/netmonitor/netmonitor-view.js index 4da2c7a57250..baf4f5c089a9 100644 --- a/browser/devtools/netmonitor/netmonitor-view.js +++ b/browser/devtools/netmonitor/netmonitor-view.js @@ -75,6 +75,8 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = { const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; // px const FREETEXT_FILTER_SEARCH_DELAY = 200; // ms +const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {}); + /** * Object defining the network monitor view components. */ @@ -95,6 +97,7 @@ let NetMonitorView = { * Destroys the network monitor view. */ destroy: function() { + this._isDestroyed = true; this.Toolbar.destroy(); this.RequestsMenu.destroy(); this.NetworkDetails.destroy(); @@ -375,7 +378,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { this.allowFocusOnRightClick = true; this.maintainSelectionVisible = true; - this.widget.autoscrollWithAppendedItems = true; this.widget.addEventListener("select", this._onSelect, false); this.widget.addEventListener("swap", this._onSwap, false); @@ -394,6 +396,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { this._onContextToggleRawHeadersCommand = this.toggleRawHeaders.bind(this); this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode(); this._onReloadCommand = () => NetMonitorView.reloadPage(); + this._flushRequestsTask = new DeferredTask(this._flushRequests, REQUESTS_REFRESH_RATE); this.sendCustomRequestEvent = this.sendCustomRequest.bind(this); this.closeCustomRequestEvent = this.closeCustomRequest.bind(this); @@ -470,7 +473,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { $("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false); this.freetextFilterBox.removeEventListener("input", this.requestsFreetextFilterEvent, false); this.freetextFilterBox.removeEventListener("command", this.requestsFreetextFilterEvent, false); + this.userInputTimer.cancel(); + this._flushRequestsTask.disarm(); + $("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false); $("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false); $("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false); @@ -496,6 +502,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { */ reset: function() { this.empty(); + this._addQueue = []; + this._updateQueue = []; this._firstRequestStartedMillis = -1; this._lastRequestEndedMillis = -1; }, @@ -503,7 +511,18 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { /** * Specifies if this view may be updated lazily. */ - lazyUpdate: true, + _lazyUpdate: true, + + get lazyUpdate() { + return this._lazyUpdate; + }, + + set lazyUpdate(value) { + this._lazyUpdate = value; + if (!value) { + this._flushRequests(); + } + }, /** * Adds a network request to this container. @@ -523,47 +542,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { * Indicates if the result came from the browser cache */ addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache) { - // Convert the received date/time string to a unix timestamp. - let unixTime = Date.parse(aStartedDateTime); + this._addQueue.push([aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache]); - // Create the element node for the network request item. - let menuView = this._createMenuView(aMethod, aUrl); - - // Remember the first and last event boundaries. - this._registerFirstRequestStart(unixTime); - this._registerLastRequestEnd(unixTime); - - // Append a network request item to this container. - let requestItem = this.push([menuView, aId], { - attachment: { - startedDeltaMillis: unixTime - this._firstRequestStartedMillis, - startedMillis: unixTime, - method: aMethod, - url: aUrl, - isXHR: aIsXHR, - fromCache: aFromCache - } - }); - - // Create a tooltip for the newly appended network request item. - let requestTooltip = requestItem.attachment.tooltip = new Tooltip(document, { - closeOnEvents: [{ - emitter: $("#requests-menu-contents"), - event: "scroll", - useCapture: true - }] - }); - - $("#details-pane-toggle").disabled = false; - $("#requests-menu-empty-notice").hidden = true; - - this.refreshSummary(); - this.refreshZebra(); - this.refreshTooltip(requestItem); - - if (aId == this._preferredItemId) { - this.selectedItem = requestItem; + // Lazy updating is disabled in some tests. + if (!this.lazyUpdate) { + return void this._flushRequests(); } + + this._flushRequestsTask.arm(); }, /** @@ -1326,30 +1312,80 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { * @param object aData * An object containing several { key: value } tuples of network info. * Supported keys are "httpVersion", "status", "statusText" etc. + * @param function aCallback + * A function to call once the request has been updated in the view. */ - updateRequest: function(aId, aData) { - // Prevent interference from zombie updates received after target closed. - if (NetMonitorView._isDestroyed) { - return; - } - this._updateQueue.push([aId, aData]); + updateRequest: function(aId, aData, aCallback) { + this._updateQueue.push([aId, aData, aCallback]); // Lazy updating is disabled in some tests. if (!this.lazyUpdate) { return void this._flushRequests(); } - // Allow requests to settle down first. - setNamedTimeout( - "update-requests", REQUESTS_REFRESH_RATE, () => this._flushRequests()); + + this._flushRequestsTask.arm(); }, /** * Starts adding all queued additional information about network requests. */ _flushRequests: function() { + // Prevent displaying any updates received after the target closed. + if (NetMonitorView._isDestroyed) { + return; + } + + let widget = NetMonitorView.RequestsMenu.widget; + let isScrolledToBottom = widget.isScrolledToBottom(); + + for (let [id, startedDateTime, method, url, isXHR, fromCache] of this._addQueue) { + // Convert the received date/time string to a unix timestamp. + let unixTime = Date.parse(startedDateTime); + + // Create the element node for the network request item. + let menuView = this._createMenuView(method, url); + + // Remember the first and last event boundaries. + this._registerFirstRequestStart(unixTime); + this._registerLastRequestEnd(unixTime); + + // Append a network request item to this container. + let requestItem = this.push([menuView, id], { + attachment: { + startedDeltaMillis: unixTime - this._firstRequestStartedMillis, + startedMillis: unixTime, + method: method, + url: url, + isXHR: isXHR, + fromCache: fromCache + } + }); + + // Create a tooltip for the newly appended network request item. + let requestTooltip = requestItem.attachment.tooltip = new Tooltip(document, { + closeOnEvents: [{ + emitter: $("#requests-menu-contents"), + event: "scroll", + useCapture: true + }] + }); + + this.refreshTooltip(requestItem); + + if (id == this._preferredItemId) { + this.selectedItem = requestItem; + } + + window.emit(EVENTS.REQUEST_ADDED, id); + } + + if (isScrolledToBottom && this._addQueue.length) { + widget.scrollToBottom(); + } + // For each queued additional information packet, get the corresponding // request item in the view and update it based on the specified data. - for (let [id, data] of this._updateQueue) { + for (let [id, data, callback] of this._updateQueue) { let requestItem = this.getItemByValue(id); if (!requestItem) { // Packet corresponds to a dead request item, target navigated. @@ -1482,6 +1518,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { } } refreshNetworkDetailsPaneIfNecessary(requestItem); + + if (callback) { + callback(); + } } /** @@ -1502,6 +1542,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { // We're done flushing all the requests, clear the update queue. this._updateQueue = []; + this._addQueue = []; + + $("#details-pane-toggle").disabled = !this.itemCount; + $("#requests-menu-empty-notice").hidden = !!this.itemCount; // Make sure all the requests are sorted and filtered. // Freshly added requests may not yet contain all the information required @@ -1650,9 +1694,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { } case "responseContent": { let { mimeType } = aItem.attachment; - let { text, encoding } = aValue.content; if (mimeType.includes("image/")) { + let { text, encoding } = aValue.content; let responseBody = yield gNetwork.getString(text); let node = $(".requests-menu-icon", aItem.target); node.src = "data:" + mimeType + ";" + encoding + "," + responseBody; @@ -2158,6 +2202,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, { _firstRequestStartedMillis: -1, _lastRequestEndedMillis: -1, _updateQueue: [], + _addQueue: [], _updateTimeout: null, _resizeTimeout: null, _activeFilters: ["all"], diff --git a/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js b/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js index 343f80607566..3abd16d5e1d5 100644 --- a/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js +++ b/browser/devtools/netmonitor/test/browser_net_details-no-duplicated-content.js @@ -95,6 +95,11 @@ let test = Task.async(function* () { info("Waiting for NETWORK_EVENT"); yield onNetworkEvent; + if (!RequestsMenu.getItemAtIndex(0)) { + info("Waiting for the request to be added to the view") + yield monitor.panelWin.once(monitor.panelWin.EVENTS.REQUEST_ADDED); + } + ok(true, "Received NETWORK_EVENT. Selecting the item."); let item = RequestsMenu.getItemAtIndex(0); RequestsMenu.selectedItem = item; diff --git a/browser/devtools/netmonitor/test/head.js b/browser/devtools/netmonitor/test/head.js index d555d113e144..7701abfb0b7d 100644 --- a/browser/devtools/netmonitor/test/head.js +++ b/browser/devtools/netmonitor/test/head.js @@ -240,20 +240,24 @@ function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) { maybeResolve(event, actor); } - function maybeResolve(event, [actor, url]) { + function maybeResolve(event, actor) { info("> Network events progress: " + genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " + postEvents + "/" + (aPostRequests * 2) + ", " + "got " + event + " for " + actor); + let networkInfo = + panel.NetMonitorController.webConsoleClient.getNetworkRequest(actor) + let url = networkInfo.request.url; updateProgressForURL(url, event); + info("> Current state: " + JSON.stringify(progress, null, 2)); // There are 15 updates which need to be fired for a request to be // considered finished. The "requestPostData" packet isn't fired for // non-POST requests. - if (genericEvents == (aGetRequests + aPostRequests) * 13 && - postEvents == aPostRequests * 2) { + if (genericEvents >= (aGetRequests + aPostRequests) * 13 && + postEvents >= aPostRequests * 2) { awaitedEventsToListeners.forEach(([e, l]) => panel.off(events[e], l)); executeSoon(deferred.resolve); diff --git a/browser/devtools/performance/test/browser_perf-columns-js-calltree.js b/browser/devtools/performance/test/browser_perf-columns-js-calltree.js index f898656f988f..f4baed3e0a81 100644 --- a/browser/devtools/performance/test/browser_perf-columns-js-calltree.js +++ b/browser/devtools/performance/test/browser_perf-columns-js-calltree.js @@ -5,6 +5,9 @@ * Tests that the js call tree view renders the correct columns. */ function* spawnTest() { + // This test seems to take a long time to cleanup on Ubuntu VMs. + requestLongerTimeout(2); + let { panel } = yield initPerformance(SIMPLE_URL); let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin; diff --git a/browser/devtools/performance/test/browser_perf-compatibility-05.js b/browser/devtools/performance/test/browser_perf-compatibility-05.js index 38dbef9b7ee7..a9ca41e196da 100644 --- a/browser/devtools/performance/test/browser_perf-compatibility-05.js +++ b/browser/devtools/performance/test/browser_perf-compatibility-05.js @@ -35,6 +35,7 @@ function* spawnTest() { busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity yield front.stopRecording(firstRecording); + info("The first recording is " + firstRecording.getDuration() + "ms long."); is(firstRecordingStartTime, 0, "The profiling start time should be 0 for the first recording."); @@ -50,6 +51,8 @@ function* spawnTest() { busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity yield front.stopRecording(secondRecording); + info("The second recording is " + secondRecording.getDuration() + "ms long."); + let secondRecordingProfile = secondRecording.getProfile(); let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data; diff --git a/browser/devtools/performance/test/browser_perf-console-record-08.js b/browser/devtools/performance/test/browser_perf-console-record-08.js index 438a145f7b43..a328f24883df 100644 --- a/browser/devtools/performance/test/browser_perf-console-record-08.js +++ b/browser/devtools/performance/test/browser_perf-console-record-08.js @@ -24,64 +24,63 @@ function testRecordings (win, expected) { function* spawnTest() { loadFrameScripts(); let { target, toolbox, panel } = yield initPerformance(SIMPLE_URL); - let win = panel.panelWin; - let { $, EVENTS, gFront, PerformanceController, OverviewView, RecordingsView, WaterfallView } = win; + let { EVENTS, PerformanceController, OverviewView, RecordingsView, WaterfallView } = panel.panelWin; info("Starting console.profile()..."); - yield consoleProfile(win); - testRecordings(win, [C+S+R]); + yield consoleProfile(panel.panelWin); + testRecordings(panel.panelWin, [C+S+R]); info("Starting manual recording..."); yield startRecording(panel); - testRecordings(win, [C+R, R+S]); + testRecordings(panel.panelWin, [C+R, R+S]); info("Starting console.profile(\"3\")..."); - yield consoleProfile(win, "3"); - testRecordings(win, [C+R, R+S, C+R]); + yield consoleProfile(panel.panelWin, "3"); + testRecordings(panel.panelWin, [C+R, R+S, C+R]); info("Starting console.profile(\"3\")..."); - yield consoleProfile(win, "4"); - testRecordings(win, [C+R, R+S, C+R, C+R]); + yield consoleProfile(panel.panelWin, "4"); + testRecordings(panel.panelWin, [C+R, R+S, C+R, C+R]); info("Ending console.profileEnd()..."); - yield consoleProfileEnd(win); + yield consoleProfileEnd(panel.panelWin); - testRecordings(win, [C+R, R+S, C+R, C]); + testRecordings(panel.panelWin, [C+R, R+S, C+R, C]); ok(OverviewView.isRendering(), "still rendering overview with manual recorded selected."); let onSelected = once(WaterfallView, EVENTS.WATERFALL_RENDERED); info("Select last recording..."); RecordingsView.selectedIndex = 3; yield onSelected; - testRecordings(win, [C+R, R, C+R, C+S]); + testRecordings(panel.panelWin, [C+R, R, C+R, C+S]); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); info("Manually stop manual recording..."); yield stopRecording(panel); - testRecordings(win, [C+R, S, C+R, C]); + testRecordings(panel.panelWin, [C+R, S, C+R, C]); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); onSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED); info("Select first recording..."); RecordingsView.selectedIndex = 0; yield onSelected; - testRecordings(win, [C+R+S, 0, C+R, C]); + testRecordings(panel.panelWin, [C+R+S, 0, C+R, C]); yield once(OverviewView, EVENTS.OVERVIEW_RENDERED); ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress."); info("Ending console.profileEnd()..."); - yield consoleProfileEnd(win); - testRecordings(win, [C+R+S, 0, C, C]); + yield consoleProfileEnd(panel.panelWin); + testRecordings(panel.panelWin, [C+R+S, 0, C, C]); ok(OverviewView.isRendering(), "should still be rendering overview when selected recording in progress."); info("Start one more manual recording..."); yield startRecording(panel); - testRecordings(win, [C+R, 0, C, C, R+S]); + testRecordings(panel.panelWin, [C+R, 0, C, C, R+S]); ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress."); info("Stop manual recording..."); yield stopRecording(panel); - testRecordings(win, [C+R, 0, C, C, S]); + testRecordings(panel.panelWin, [C+R, 0, C, C, S]); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); info("Ending console.profileEnd()..."); - yield consoleProfileEnd(win); - testRecordings(win, [C, 0, C, C, S]); + yield consoleProfileEnd(panel.panelWin); + testRecordings(panel.panelWin, [C, 0, C, C, S]); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); yield teardown(panel); diff --git a/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js b/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js index 5d1ee3ee53c8..288482a69b98 100644 --- a/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js +++ b/browser/devtools/performance/test/browser_timeline-waterfall-sidebar.js @@ -7,9 +7,8 @@ function* spawnTest() { let { target, panel } = yield initPerformance(SIMPLE_URL); - let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin; + let { $, $$, PerformanceController, WaterfallView } = panel.panelWin; let { L10N } = devtools.require("devtools/performance/global"); - let { TIMELINE_BLUEPRINT } = devtools.require("devtools/performance/markers"); let { getMarkerLabel } = devtools.require("devtools/performance/marker-utils"); // Hijack the markers massaging part of creating the waterfall view, @@ -34,38 +33,35 @@ function* spawnTest() { yield stopRecording(panel); ok(true, "Recording has ended."); - // Select everything - let timeline = OverviewView.graphs.get("timeline"); - let rerendered = WaterfallView.once(EVENTS.WATERFALL_RENDERED); - timeline.setSelection({ start: 0, end: timeline.width }) - yield rerendered; - let bars = $$(".waterfall-marker-bar"); let markers = PerformanceController.getCurrentRecording().getMarkers(); ok(bars.length > 2, "Got at least 3 markers (1)"); ok(markers.length > 2, "Got at least 3 markers (2)"); + let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms); + for (let i = 0; i < bars.length; i++) { let bar = bars[i]; - let m = markers[i]; + let mkr = markers[i]; EventUtils.sendMouseEvent({ type: "mousedown" }, bar); - is($("#waterfall-details .marker-details-type").getAttribute("value"), getMarkerLabel(m), - "Sidebar title matches markers name."); - + let type = $(".marker-details-type").getAttribute("value"); let tooltip = $(".marker-details-duration").getAttribute("tooltiptext"); let duration = $(".marker-details-duration .marker-details-labelvalue").getAttribute("value"); - let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms); + info("Current marker data: " + mkr.toSource()); + info("Current marker output: " + $("#waterfall-details").innerHTML); + + is(type, getMarkerLabel(mkr), "Sidebar title matches markers name."); // Values are rounded. We don't use a strict equality. - is(toMs(m.end - m.start), duration, "Sidebar duration is valid."); + is(toMs(mkr.end - mkr.start), duration, "Sidebar duration is valid."); // For some reason, anything that creates "→" here turns it into a "â" for some reason. // So just check that start and end time are in there somewhere. - ok(tooltip.indexOf(toMs(m.start)) !== -1, "Tooltip has start time."); - ok(tooltip.indexOf(toMs(m.end)) !== -1, "Tooltip has end time."); + ok(tooltip.indexOf(toMs(mkr.start)) !== -1, "Tooltip has start time."); + ok(tooltip.indexOf(toMs(mkr.end)) !== -1, "Tooltip has end time."); } yield teardown(panel); diff --git a/browser/devtools/shared/widgets/SideMenuWidget.jsm b/browser/devtools/shared/widgets/SideMenuWidget.jsm index d8094f220042..49d768676a87 100644 --- a/browser/devtools/shared/widgets/SideMenuWidget.jsm +++ b/browser/devtools/shared/widgets/SideMenuWidget.jsm @@ -10,12 +10,9 @@ const Cu = Components.utils; Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); Cu.import("resource://gre/modules/devtools/event-emitter.js"); -const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {}); this.EXPORTED_SYMBOLS = ["SideMenuWidget"]; -const SCROLL_FREQUENCY = 16; - /** * A simple side menu, with the ability of grouping menu items. * @@ -81,14 +78,6 @@ SideMenuWidget.prototype = { */ groupSortPredicate: function(a, b) a.localeCompare(b), - /** - * Specifies that the container viewport should be "stuck" to the - * bottom. That is, the container is automatically scrolled down to - * keep appended items visible, but only when the scroll position is - * already at the bottom. - */ - autoscrollWithAppendedItems: false, - /** * Inserts an item in this container at the specified index, optionally * grouping by name. @@ -106,29 +95,10 @@ SideMenuWidget.prototype = { * The element associated with the displayed item. */ insertItemAt: function(aIndex, aContents, aAttachment={}) { - // Maintaining scroll position at the bottom when a new item is inserted - // depends on several factors (the order of testing is important to avoid - // needlessly expensive operations that may cause reflows): - let maintainScrollAtBottom = - // 1. The behavior should be enabled, - this.autoscrollWithAppendedItems && - // 2. There shouldn't currently be any selected item in the list. - !this._selectedItem && - // 3. The new item should be appended at the end of the list. - (aIndex < 0 || aIndex >= this._orderedMenuElementsArray.length) && - // 4. We aren't waiting for a scroll to happen. - (!this._scrollToBottomTask || !this._scrollToBottomTask.isArmed) && - // 5. The list should already be scrolled at the bottom. - this.isScrolledToBottom(); - let group = this._getMenuGroupForName(aAttachment.group); let item = this._getMenuItemForGroup(group, aContents, aAttachment); let element = item.insertSelfAt(aIndex); - if (maintainScrollAtBottom) { - this.scrollToBottom(); - } - return element; }, @@ -158,30 +128,8 @@ SideMenuWidget.prototype = { * If the user scrolls in the meantime, cancel this operation. */ scrollToBottom: function() { - // Lazily attach this functionality to the object, so it won't get - // created unless if this scrollToBottom behavior is needed. - if (!this._scrollToBottomTask) { - // The scroll event fires asynchronously, so we need to keep a bit to - // distinguish between user-initiated events and scrollTop assignment. - let ignoreNextScroll = false; - - this._scrollToBottomTask = new DeferredTask(() => { - ignoreNextScroll = true; - this._list.scrollTop = this._list.scrollHeight; - this.emit("scroll-to-bottom"); - }, SCROLL_FREQUENCY); - - // On a user scroll, cancel any pending calls to the scroll function. - this._list.addEventListener("scroll", () => { - if (!ignoreNextScroll && this._scrollToBottomTask.isArmed && - !this.isScrolledToBottom()) { - this._scrollToBottomTask.disarm(); - } - ignoreNextScroll = false; - }, true); - } - - this._scrollToBottomTask.arm(); + this._list.scrollTop = this._list.scrollHeight; + this.emit("scroll-to-bottom"); }, /** diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_fetch-from-cache.js b/browser/devtools/styleeditor/test/browser_styleeditor_fetch-from-cache.js index 04ce952f3058..dfdee5a75b77 100644 --- a/browser/devtools/styleeditor/test/browser_styleeditor_fetch-from-cache.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_fetch-from-cache.js @@ -17,6 +17,7 @@ add_task(function*() { let target = TargetFactory.forTab(tab); let toolbox = yield gDevTools.showToolbox(target, "netmonitor"); let netmonitor = toolbox.getPanel("netmonitor"); + netmonitor._view.RequestsMenu.lazyUpdate = false; info("Navigating to test page"); yield navigateTo(TEST_URL); diff --git a/browser/devtools/styleinspector/cssruleview.xhtml b/browser/devtools/styleinspector/cssruleview.xhtml index 008fa956899b..d48eb9624ee0 100644 --- a/browser/devtools/styleinspector/cssruleview.xhtml +++ b/browser/devtools/styleinspector/cssruleview.xhtml @@ -47,7 +47,7 @@ - +