diff --git a/aclocal.m4 b/aclocal.m4 index f90f089269bf..273f0d09de5b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -32,7 +32,6 @@ builtin(include, build/autoconf/clang-plugin.m4)dnl builtin(include, build/autoconf/alloc.m4)dnl builtin(include, build/autoconf/ios.m4)dnl builtin(include, build/autoconf/jemalloc.m4)dnl -builtin(include, build/autoconf/rust.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/b2g/app/nsBrowserApp.cpp b/b2g/app/nsBrowserApp.cpp index ab3c18b658de..8e6d2651b0db 100644 --- a/b2g/app/nsBrowserApp.cpp +++ b/b2g/app/nsBrowserApp.cpp @@ -215,17 +215,6 @@ int main(int argc, _CONST char* argv[]) (void)setsid(); #endif - int gotCounters; -#if defined(XP_UNIX) - struct rusage initialRUsage; - gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage); -#elif defined(XP_WIN) - IO_COUNTERS ioCounters; - gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); -#else - #error "Unknown platform" // having this here keeps cppcheck happy -#endif - #ifdef HAS_DLL_BLOCKLIST DllBlocklist_Initialize(); #endif @@ -251,32 +240,6 @@ int main(int argc, _CONST char* argv[]) return 255; } - if (gotCounters) { -#if defined(XP_WIN) - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS, - int(ioCounters.ReadOperationCount)); - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER, - int(ioCounters.ReadTransferCount / 1024)); - IO_COUNTERS newIoCounters; - if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) { - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS, - int(newIoCounters.ReadOperationCount - ioCounters.ReadOperationCount)); - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER, - int((newIoCounters.ReadTransferCount - ioCounters.ReadTransferCount) / 1024)); - } -#elif defined(XP_UNIX) - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS, - int(initialRUsage.ru_majflt)); - struct rusage newRUsage; - if (!getrusage(RUSAGE_SELF, &newRUsage)) { - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS, - int(newRUsage.ru_majflt - initialRUsage.ru_majflt)); - } -#else - #error "Unknown platform" // having this here keeps cppcheck happy -#endif - } - int result; { ScopedLogging log; diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index 9b48033e25e3..b88d79249392 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -362,17 +362,6 @@ int main(int argc, char* argv[], char* envp[]) TriggerQuirks(); #endif - int gotCounters; -#if defined(XP_UNIX) - struct rusage initialRUsage; - gotCounters = !getrusage(RUSAGE_SELF, &initialRUsage); -#elif defined(XP_WIN) - IO_COUNTERS ioCounters; - gotCounters = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); -#else - #error "Unknown platform" // having this here keeps cppcheck happy -#endif - nsIFile *xreDirectory; #ifdef HAS_DLL_BLOCKLIST @@ -394,32 +383,6 @@ int main(int argc, char* argv[], char* envp[]) XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start); - if (gotCounters) { -#if defined(XP_WIN) - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_OPS, - int(ioCounters.ReadOperationCount)); - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_READ_TRANSFER, - int(ioCounters.ReadTransferCount / 1024)); - IO_COUNTERS newIoCounters; - if (GetProcessIoCounters(GetCurrentProcess(), &newIoCounters)) { - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_OPS, - int(newIoCounters.ReadOperationCount - ioCounters.ReadOperationCount)); - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_READ_TRANSFER, - int((newIoCounters.ReadTransferCount - ioCounters.ReadTransferCount) / 1024)); - } -#elif defined(XP_UNIX) - XRE_TelemetryAccumulate(mozilla::Telemetry::EARLY_GLUESTARTUP_HARD_FAULTS, - int(initialRUsage.ru_majflt)); - struct rusage newRUsage; - if (!getrusage(RUSAGE_SELF, &newRUsage)) { - XRE_TelemetryAccumulate(mozilla::Telemetry::GLUESTARTUP_HARD_FAULTS, - int(newRUsage.ru_majflt - initialRUsage.ru_majflt)); - } -#else - #error "Unknown platform" // having this here keeps cppcheck happy -#endif - } - #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC XRE_EnableSameExecutableForContentProc(); #endif diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js index 3af34e9239a2..11c2c22e2b2a 100644 --- a/browser/base/content/browser-fxaccounts.js +++ b/browser/base/content/browser-fxaccounts.js @@ -229,7 +229,7 @@ var gFxAccounts = { this.panelUIFooter.removeAttribute("fxaprofileimage"); this.panelUIAvatar.style.removeProperty("list-style-image"); let showErrorBadge = false; - if (!this._inCustomizationMode && userData) { + if (userData) { // At this point we consider the user as logged-in (but still can be in an error state) if (this.loginFailed) { let tooltipDescription = this.strings.formatStringFromName("reconnectDescription", [userData.email], 1); @@ -260,7 +260,7 @@ var gFxAccounts = { } let updateWithProfile = (profile) => { - if (!this._inCustomizationMode && profileInfoEnabled) { + if (profileInfoEnabled) { if (profile.displayName) { this.panelUILabel.setAttribute("label", profile.displayName); } diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js index a29f733eeec1..d356d5aba454 100644 --- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js @@ -987,7 +987,6 @@ var gHistorySwipeAnimation = { let canvas = null; - TelemetryStopwatch.start("FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE"); try { let browser = gBrowser.selectedBrowser; let r = browser.getBoundingClientRect(); @@ -1006,7 +1005,6 @@ var gHistorySwipeAnimation = { ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES | ctx.DRAWWINDOW_USE_WIDGET_LAYERS); } finally { - TelemetryStopwatch.finish("FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE"); } TelemetryStopwatch.start("FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE"); diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 31603fad2af6..6c4707696eb5 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -243,6 +243,10 @@ toolbar[customizing] > .overflow-button { -moz-appearance: -moz-window-button-box; } +#personal-bookmarks { + -moz-window-dragging: inherit; +} + %ifdef XP_MACOSX #titlebar-fullscreen-button { -moz-appearance: -moz-mac-fullscreen-button; @@ -286,8 +290,8 @@ toolbar[customizing] > .overflow-button { } #main-window[tabsintitlebar] #TabsToolbar, -#main-window[tabsintitlebar] #toolbar-menubar:not([autohide=true]), -#main-window[tabsintitlebar] #navigator-toolbox > toolbar:not(#toolbar-menubar):-moz-lwtheme { +#main-window[tabsintitlebar] #toolbar-menubar, +#main-window[tabsintitlebar] #navigator-toolbox > toolbar:-moz-lwtheme { -moz-window-dragging: drag; } %endif @@ -360,16 +364,6 @@ toolbar:not(#TabsToolbar) > #personal-bookmarks { -moz-box-flex: 1; } -/* Ensure that empty parts of the bookmarks container can be dragged on OSX, and on other OSes - * only when a lwtheme is in use. */ -%ifdef XP_MACOSX -#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks { -%else -#main-window[tabsintitlebar]:not([customizing]) #personal-bookmarks:-moz-lwtheme { -%endif - -moz-window-dragging: drag; -} - #zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text { display: -moz-box; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 0b6f29f8f771..6117295bdd1d 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6574,13 +6574,25 @@ var gIdentityHandler = { delete this._identityBox; return this._identityBox = document.getElementById("identity-box"); }, + get _identityPopupMultiView () { + delete _identityPopupMultiView; + return document.getElementById("identity-popup-multiView"); + }, get _identityPopupContentHosts () { delete this._identityPopupContentHosts; - return this._identityPopupContentHosts = [...document.querySelectorAll(".identity-popup-headline.host")]; + let selector = ".identity-popup-headline.host"; + return this._identityPopupContentHosts = [ + ...this._identityPopupMultiView._mainView.querySelectorAll(selector), + ...document.querySelectorAll(selector) + ]; }, get _identityPopupContentHostless () { delete this._identityPopupContentHostless; - return this._identityPopupContentHostless = [...document.querySelectorAll(".identity-popup-headline.hostless")]; + let selector = ".identity-popup-headline.hostless"; + return this._identityPopupContentHostless = [ + ...this._identityPopupMultiView._mainView.querySelectorAll(selector), + ...document.querySelectorAll(selector) + ]; }, get _identityPopupContentOwner () { delete this._identityPopupContentOwner; @@ -6640,7 +6652,7 @@ var gIdentityHandler = { }, toggleSubView(name, anchor) { - let view = document.getElementById("identity-popup-multiView"); + let view = this._identityPopupMultiView; if (view.showingSubView) { view.showMainView(); } else { diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index b938de197ecf..dfc3b15b6049 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -5625,10 +5625,6 @@ return; } - Services.telemetry.getHistogramById(aTab.closing ? - "FX_TAB_ANIM_CLOSE_MS" : - "FX_TAB_ANIM_OPEN_MS") - .add(Date.now() - aTab._animStartTime); aTab._animStartTime = 0; // Handle tab animation smoothness telemetry/logging of frame intervals and paint times diff --git a/browser/base/content/test/general/head.js b/browser/base/content/test/general/head.js index c378b8769e50..056c24895041 100644 --- a/browser/base/content/test/general/head.js +++ b/browser/base/content/test/general/head.js @@ -767,7 +767,6 @@ function assertMixedContentBlockingState(tabbrowser, states = {}) { // Make sure the correct icon is visible in the Control Center. // This logic is controlled with CSS, so this helps prevent regressions there. let securityView = doc.getElementById("identity-popup-securityView"); - let securityContent = doc.getElementById("identity-popup-security-content"); let securityViewBG = tabbrowser.ownerGlobal.getComputedStyle(securityView, ""). getPropertyValue("background-image"); let securityContentBG = tabbrowser.ownerGlobal.getComputedStyle(securityView, ""). diff --git a/browser/components/migration/360seProfileMigrator.js b/browser/components/migration/360seProfileMigrator.js index c4fc8693df97..a65f96243bae 100644 --- a/browser/components/migration/360seProfileMigrator.js +++ b/browser/components/migration/360seProfileMigrator.js @@ -18,6 +18,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", XPCOMUtils.defineLazyModuleGetter(this, "Sqlite", "resource://gre/modules/Sqlite.jsm"); +const kBookmarksFileName = "360sefav.db"; + function copyToTempUTF8File(file, charset) { let inputStream = Cc["@mozilla.org/network/file-input-stream;1"] .createInstance(Ci.nsIFileInputStream); @@ -95,7 +97,7 @@ function getHash(aStr) { function Bookmarks(aProfileFolder) { let file = aProfileFolder.clone(); - file.append("360sefav.db"); + file.append(kBookmarksFileName); this._file = file; } @@ -298,6 +300,23 @@ Qihoo360seProfileMigrator.prototype.getResources = function(aProfile) { return resources.filter(r => r.exists); }; +Qihoo360seProfileMigrator.prototype.getLastUsedDate = function() { + let bookmarksPaths = this.sourceProfiles.map(({id}) => { + return OS.Path.join(this._usersDir.path, id, kBookmarksFileName); + }); + if (!bookmarksPaths.length) { + return Promise.resolve(new Date(0)); + } + let datePromises = bookmarksPaths.map(path => { + return OS.File.stat(path).catch(_ => null).then(info => { + return info ? info.lastModificationDate : 0; + }); + }); + return Promise.all(datePromises).then(dates => { + return new Date(Math.max.apply(Math, dates)); + }); +}; + 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}"); diff --git a/browser/components/migration/AutoMigrate.jsm b/browser/components/migration/AutoMigrate.jsm index 3561699078fe..320d66964764 100644 --- a/browser/components/migration/AutoMigrate.jsm +++ b/browser/components/migration/AutoMigrate.jsm @@ -8,13 +8,19 @@ this.EXPORTED_SYMBOLS = ["AutoMigrate"]; const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; +const kAutoMigrateStartedPref = "browser.migrate.automigrate-started"; +const kAutoMigrateFinishedPref = "browser.migrate.automigrate-finished"; + Cu.import("resource:///modules/MigrationUtils.jsm"); +Cu.import("resource://gre/modules/PlacesUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const AutoMigrate = { get resourceTypesToUse() { - let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator; - return BOOKMARKS | HISTORY | FORMDATA | PASSWORDS; + let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator; + return BOOKMARKS | HISTORY | PASSWORDS; }, /** @@ -25,35 +31,40 @@ const AutoMigrate = { * failed for some reason. */ migrate(profileStartup, migratorKey, profileToMigrate) { - let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_SUCCEEDED"); - histogram.add("initialized"); + let histogram = Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_PROCESS_SUCCESS"); + histogram.add(0); let migrator = this.pickMigrator(migratorKey); - histogram.add("got-browser"); + histogram.add(5); profileToMigrate = this.pickProfile(migrator, profileToMigrate); - histogram.add("got-profile"); + histogram.add(10); let resourceTypes = migrator.getMigrateData(profileToMigrate, profileStartup); if (!(resourceTypes & this.resourceTypesToUse)) { throw new Error("No usable resources were found for the selected browser!"); } - histogram.add("got-data"); + histogram.add(15); let sawErrors = false; let migrationObserver = function(subject, topic, data) { if (topic == "Migration:ItemError") { sawErrors = true; } else if (topic == "Migration:Ended") { - histogram.add(sawErrors ? "finished-with-errors" : "finished"); + histogram.add(25); + if (sawErrors) { + histogram.add(26); + } Services.obs.removeObserver(migrationObserver, "Migration:Ended"); Services.obs.removeObserver(migrationObserver, "Migration:ItemError"); + Services.prefs.setCharPref(kAutoMigrateFinishedPref, Date.now().toString()); } }; Services.obs.addObserver(migrationObserver, "Migration:Ended", false); Services.obs.addObserver(migrationObserver, "Migration:ItemError", false); + Services.prefs.setCharPref(kAutoMigrateStartedPref, Date.now().toString()); migrator.migrate(this.resourceTypesToUse, profileStartup, profileToMigrate); - histogram.add("migrate-called-without-exceptions"); + histogram.add(20); }, /** @@ -109,5 +120,59 @@ const AutoMigrate = { } return profiles ? profiles[0].id : null; }, + + getUndoRange() { + let start, finish; + try { + start = parseInt(Services.prefs.getCharPref(kAutoMigrateStartedPref), 10); + finish = parseInt(Services.prefs.getCharPref(kAutoMigrateFinishedPref), 10); + } catch (ex) { + Cu.reportError(ex); + } + if (!finish || !start) { + return null; + } + return [new Date(start), new Date(finish)]; + }, + + canUndo() { + if (!this.getUndoRange()) { + return Promise.resolve(false); + } + // Return a promise resolving to false if we're signed into sync, resolve + // to true otherwise. + let {fxAccounts} = Cu.import("resource://gre/modules/FxAccounts.jsm", {}); + return fxAccounts.getSignedInUser().then(user => !user, () => Promise.resolve(true)); + }, + + undo: Task.async(function* () { + if (!(yield this.canUndo())) { + throw new Error("Can't undo!"); + } + + yield PlacesUtils.bookmarks.eraseEverything(); + + // NB: we drop the start time of the migration for now. This is because + // imported history will always end up being 'backdated' to the actual + // visit time recorded by the browser from which we imported. As a result, + // a lower bound on this item doesn't really make sense. + // Note that for form data this could be different, but we currently don't + // support form data import from any non-Firefox browser, so it isn't + // imported from other browsers by the automigration code, nor do we + // remove it here. + let range = this.getUndoRange(); + yield PlacesUtils.history.removeVisitsByFilter({ + beginDate: new Date(0), + endDate: range[1] + }); + + try { + Services.logins.removeAllLogins(); + } catch (ex) { + // ignore failure. + } + Services.prefs.clearUserPref("browser.migrate.automigrate-started"); + Services.prefs.clearUserPref("browser.migrate.automigrate-finished"); + }), }; diff --git a/browser/components/migration/ChromeProfileMigrator.js b/browser/components/migration/ChromeProfileMigrator.js index 6ab5ab36f41b..5bdf13e2049b 100644 --- a/browser/components/migration/ChromeProfileMigrator.js +++ b/browser/components/migration/ChromeProfileMigrator.js @@ -24,6 +24,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Console.jsm"); Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource:///modules/MigrationUtils.jsm"); @@ -136,6 +138,27 @@ ChromeProfileMigrator.prototype.getResources = return []; }; +ChromeProfileMigrator.prototype.getLastUsedDate = + function Chrome_getLastUsedDate() { + let datePromises = this.sourceProfiles.map(profile => { + let profileFolder = this._chromeUserDataFolder.clone(); + let basePath = OS.Path.join(this._chromeUserDataFolder.path, profile.id); + let fileDatePromises = ["Bookmarks", "History", "Cookies"].map(leafName => { + let path = OS.Path.join(basePath, leafName); + return OS.File.stat(path).catch(_ => null).then(info => { + return info ? info.lastModificationDate : 0; + }); + }); + return Promise.all(fileDatePromises).then(dates => { + return Math.max.apply(Math, dates); + }); + }); + return Promise.all(datePromises).then(dates => { + dates.push(0); + return new Date(Math.max.apply(Math, dates)); + }); + }; + Object.defineProperty(ChromeProfileMigrator.prototype, "sourceProfiles", { get: function Chrome_sourceProfiles() { if ("__sourceProfiles" in this) diff --git a/browser/components/migration/EdgeProfileMigrator.js b/browser/components/migration/EdgeProfileMigrator.js index b4eb8d5ffac4..cc67582356dc 100644 --- a/browser/components/migration/EdgeProfileMigrator.js +++ b/browser/components/migration/EdgeProfileMigrator.js @@ -4,10 +4,11 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/AppConstants.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/MigrationUtils.jsm"); Cu.import("resource:///modules/MSMigrationUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", @@ -420,6 +421,34 @@ EdgeProfileMigrator.prototype.getResources = function() { return resources.filter(r => r.exists); }; +EdgeProfileMigrator.prototype.getLastUsedDate = function() { + // Don't do this if we don't have a single profile (see the comment for + // sourceProfiles) or if we can't find the database file: + if (this.sourceProfiles !== null || !gEdgeDatabase) { + return Promise.resolve(new Date(0)); + } + let logFilePath = OS.Path.join(gEdgeDatabase.parent.path, "LogFiles", "edb.log"); + let dbPath = gEdgeDatabase.path; + let cookieMigrator = MSMigrationUtils.getCookiesMigrator(MSMigrationUtils.MIGRATION_TYPE_EDGE); + let cookiePaths = cookieMigrator._cookiesFolders.map(f => f.path); + let datePromises = [logFilePath, dbPath, ... cookiePaths].map(path => { + return OS.File.stat(path).catch(_ => null).then(info => { + return info ? info.lastModificationDate : 0; + }); + }); + datePromises.push(new Promise(resolve => { + let typedURLs = new Map(); + try { + typedURLs = MSMigrationUtils.getTypedURLs(kEdgeRegistryRoot); + } catch (ex) {} + let times = [0, ... typedURLs.values()]; + resolve(Math.max.apply(Math, times)); + })); + return Promise.all(datePromises).then(dates => { + return new Date(Math.max.apply(Math, dates)); + }); +}; + /* Somewhat counterintuitively, this returns: * - |null| to indicate "There is only 1 (default) profile" (on win10+) * - |[]| to indicate "There are no profiles" (on <=win8.1) which will avoid using this migrator. diff --git a/browser/components/migration/FirefoxProfileMigrator.js b/browser/components/migration/FirefoxProfileMigrator.js index c42f6d32a311..b07f51ec10f3 100644 --- a/browser/components/migration/FirefoxProfileMigrator.js +++ b/browser/components/migration/FirefoxProfileMigrator.js @@ -97,6 +97,13 @@ FirefoxProfileMigrator.prototype.getResources = function(aProfile) { return this._getResourcesInternal(sourceProfileDir, currentProfileDir, aProfile); }; +FirefoxProfileMigrator.prototype.getLastUsedDate = function() { + // We always pretend we're really old, so that we don't mess + // up the determination of which browser is the most 'recent' + // to import from. + return Promise.resolve(new Date(0)); +}; + FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileDir, currentProfileDir, aProfile) { let getFileResource = function(aMigrationType, aFileNames) { let files = []; diff --git a/browser/components/migration/IEProfileMigrator.js b/browser/components/migration/IEProfileMigrator.js index 8d2e52961e20..e42ba7144906 100644 --- a/browser/components/migration/IEProfileMigrator.js +++ b/browser/components/migration/IEProfileMigrator.js @@ -13,9 +13,10 @@ const kLoginsKey = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storag const kMainKey = "Software\\Microsoft\\Internet Explorer\\Main"; Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/MigrationUtils.jsm"); Cu.import("resource:///modules/MSMigrationUtils.jsm"); Cu.import("resource://gre/modules/LoginHelper.jsm"); @@ -495,6 +496,26 @@ IEProfileMigrator.prototype.getResources = function IE_getResources() { return resources.filter(r => r.exists); }; +IEProfileMigrator.prototype.getLastUsedDate = function IE_getLastUsedDate() { + let datePromises = ["Favs", "CookD"].map(dirId => { + let {path} = Services.dirsvc.get(dirId, Ci.nsIFile); + return OS.File.stat(path).catch(_ => null).then(info => { + return info ? info.lastModificationDate : 0; + }); + }); + datePromises.push(new Promise(resolve => { + let typedURLs = new Map(); + try { + typedURLs = MSMigrationUtils.getTypedURLs("Software\\Microsoft\\Internet Explorer"); + } catch (ex) {} + let dates = [0, ... typedURLs.values()]; + resolve(Math.max.apply(Math, dates)); + })); + return Promise.all(datePromises).then(dates => { + return new Date(Math.max.apply(Math, dates)); + }); +}; + Object.defineProperty(IEProfileMigrator.prototype, "sourceHomePageURL", { get: function IE_get_sourceHomePageURL() { let defaultStartPage = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, diff --git a/browser/components/migration/MigrationUtils.jsm b/browser/components/migration/MigrationUtils.jsm index b868dd72d6f2..f757099b57f2 100644 --- a/browser/components/migration/MigrationUtils.jsm +++ b/browser/components/migration/MigrationUtils.jsm @@ -137,6 +137,20 @@ this.MigratorPrototype = { throw new Error("getResources must be overridden"); }, + /** + * OVERRIDE in order to provide an estimate of when the last time was + * that somebody used the browser. It is OK that this is somewhat fuzzy - + * history may not be available (or be wiped or not present due to e.g. + * incognito mode). + * + * @return a Promise that resolves to the last used date. + * + * @note If not overridden, the promise will resolve to the unix epoch. + */ + getLastUsedDate() { + return Promise.resolve(new Date(0)); + }, + /** * OVERRIDE IF AND ONLY IF the migrator is a startup-only migrator (For now, * that is just the Firefox migrator, see bug 737381). Default: false. diff --git a/browser/components/migration/SafariProfileMigrator.js b/browser/components/migration/SafariProfileMigrator.js index d3f93225dd70..8cc9236fa955 100644 --- a/browser/components/migration/SafariProfileMigrator.js +++ b/browser/components/migration/SafariProfileMigrator.js @@ -9,10 +9,11 @@ var Ci = Components.interfaces; var Cu = Components.utils; Cu.import("resource://gre/modules/AppConstants.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/FileUtils.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/MigrationUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Downloads", @@ -640,6 +641,24 @@ SafariProfileMigrator.prototype.getResources = function SM_getResources() { return resources; }; +SafariProfileMigrator.prototype.getLastUsedDate = function SM_getLastUsedDate() { + let profileDir; + if (AppConstants.platform == "macosx") { + profileDir = FileUtils.getDir("ULibDir", ["Safari"], false); + } else { + profileDir = FileUtils.getDir("AppData", ["Apple Computer", "Safari"], false); + } + let datePromises = ["Bookmarks.plist", "History.plist"].map(file => { + let path = OS.Path.join(profileDir.path, file); + return OS.File.stat(path).catch(_ => null).then(info => { + return info ? info.lastModificationDate : 0; + }); + }); + return Promise.all(datePromises).then(dates => { + return new Date(Math.max.apply(Math, dates)); + }); +}; + Object.defineProperty(SafariProfileMigrator.prototype, "mainPreferencesPropertyList", { get: function get_mainPreferencesPropertyList() { if (this._mainPreferencesPropertyList === undefined) { diff --git a/browser/components/migration/content/migration.js b/browser/components/migration/content/migration.js index f8d423940604..f58d9326aa42 100644 --- a/browser/components/migration/content/migration.js +++ b/browser/components/migration/content/migration.js @@ -83,10 +83,10 @@ var MigrationWizard = { this._wiz.canRewind = false; var selectedMigrator = null; + this._availableMigrators = []; // Figure out what source apps are are available to import from: var group = document.getElementById("importSourceGroup"); - var availableMigratorCount = 0; for (var i = 0; i < group.childNodes.length; ++i) { var migratorKey = group.childNodes[i].id; if (migratorKey != "nothing") { @@ -96,7 +96,7 @@ var MigrationWizard = { // one, or if it is the migrator that was passed to us. if (!selectedMigrator || this._source == migratorKey) selectedMigrator = group.childNodes[i]; - availableMigratorCount++; + this._availableMigrators.push([migratorKey, migrator]); } else { // Hide this option group.childNodes[i].hidden = true; @@ -105,7 +105,7 @@ var MigrationWizard = { } if (this.isInitialMigration) { Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT") - .add(availableMigratorCount); + .add(this._availableMigrators.length); let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser(); // This will record 0 for unknown default browser IDs. defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser); @@ -448,6 +448,9 @@ var MigrationWizard = { label.removeAttribute("style"); break; case "Migration:Ended": + if (this.isInitialMigration) { + this.reportDataRecencyTelemetry(); + } if (this._autoMigrate) { Services.telemetry.getKeyedHistogramById("FX_MIGRATION_HOMEPAGE_IMPORTED") .add(this._source, !!this._newHomePage); @@ -532,5 +535,31 @@ var MigrationWizard = { this._wiz.getButton("cancel").disabled = true; this._wiz.canRewind = false; this._listItems("doneItems"); - } + }, + + reportDataRecencyTelemetry() { + let histogram = Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_DATA_RECENCY"); + let lastUsedPromises = []; + for (let [key, migrator] of this._availableMigrators) { + // No block-scoped let in for...of loop conditions, so get the source: + let localKey = key; + lastUsedPromises.push(migrator.getLastUsedDate().then(date => { + const ONE_YEAR = 24 * 365; + let diffInHours = Math.round((Date.now() - date) / (60 * 60 * 1000)); + if (diffInHours > ONE_YEAR) { + diffInHours = ONE_YEAR; + } + histogram.add(localKey, diffInHours); + return [localKey, diffInHours]; + })); + } + Promise.all(lastUsedPromises).then(migratorUsedTimeDiff => { + // Sort low to high. + migratorUsedTimeDiff.sort(([keyA, diffA], [keyB, diffB]) => diffA - diffB); + let usedMostRecentBrowser = migratorUsedTimeDiff.length && this._source == migratorUsedTimeDiff[0][0]; + let usedRecentBrowser = + Services.telemetry.getKeyedHistogramById("FX_STARTUP_MIGRATION_USED_RECENT_BROWSER"); + usedRecentBrowser.add(this._source, usedMostRecentBrowser); + }); + }, }; diff --git a/browser/components/migration/nsIBrowserProfileMigrator.idl b/browser/components/migration/nsIBrowserProfileMigrator.idl index a79f31711458..a251c3683a08 100644 --- a/browser/components/migration/nsIBrowserProfileMigrator.idl +++ b/browser/components/migration/nsIBrowserProfileMigrator.idl @@ -43,6 +43,12 @@ interface nsIBrowserProfileMigrator : nsISupports */ unsigned short getMigrateData(in jsval aProfile, in boolean aDoingStartup); + /** + * Get the last time data from this browser was modified + * @return a promise that resolves to a JS Date object + */ + jsval getLastUsedDate(); + /** * Whether or not there is any data that can be imported from this * browser (i.e. whether or not it is installed, and there exists diff --git a/browser/components/migration/tests/unit/test_automigration.js b/browser/components/migration/tests/unit/test_automigration.js index 1d47f031272b..ccd212d537d7 100644 --- a/browser/components/migration/tests/unit/test_automigration.js +++ b/browser/components/migration/tests/unit/test_automigration.js @@ -1,9 +1,14 @@ Cu.import("resource:///modules/MigrationUtils.jsm"); +Cu.import("resource://gre/modules/PlacesUtils.jsm"); +Cu.import("resource://testing-common/TestUtils.jsm"); +Cu.import("resource://testing-common/PlacesTestUtils.jsm"); let AutoMigrateBackstage = Cu.import("resource:///modules/AutoMigrate.jsm"); let gShimmedMigratorKeyPicker = null; let gShimmedMigrator = null; +const kUsecPerMin = 60 * 1000000; + // This is really a proxy on MigrationUtils, but if we specify that directly, // we get in trouble because the object itself is frozen, and Proxies can't // return a different value to an object when directly proxying a frozen @@ -112,8 +117,133 @@ add_task(function* checkIntegration() { Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null, "getMigrateData called with 'null' as a profile"); - let {BOOKMARKS, HISTORY, FORMDATA, PASSWORDS} = Ci.nsIBrowserProfileMigrator; - let expectedTypes = BOOKMARKS | HISTORY | FORMDATA | PASSWORDS; + let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator; + let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS; Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null], - "getMigrateData called with 'null' as a profile"); + "migrate called with 'null' as a profile"); +}); + +/** + * Test the undo preconditions and a no-op undo in the automigrator. + */ +add_task(function* checkUndoPreconditions() { + gShimmedMigrator = { + get sourceProfiles() { + do_print("Read sourceProfiles"); + return null; + }, + getMigrateData(profileToMigrate) { + this._getMigrateDataArgs = profileToMigrate; + return Ci.nsIBrowserProfileMigrator.BOOKMARKS; + }, + migrate(types, startup, profileToMigrate) { + this._migrateArgs = [types, startup, profileToMigrate]; + TestUtils.executeSoon(function() { + Services.obs.notifyObservers(null, "Migration:Ended", undefined); + }); + }, + }; + + gShimmedMigratorKeyPicker = function() { + return "gobbledygook"; + }; + AutoMigrate.migrate("startup"); + let migrationFinishedPromise = TestUtils.topicObserved("Migration:Ended"); + Assert.strictEqual(gShimmedMigrator._getMigrateDataArgs, null, + "getMigrateData called with 'null' as a profile"); + + let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator; + let expectedTypes = BOOKMARKS | HISTORY | PASSWORDS; + Assert.deepEqual(gShimmedMigrator._migrateArgs, [expectedTypes, "startup", null], + "migrate called with 'null' as a profile"); + + yield migrationFinishedPromise; + Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-started"), + "Should have set start time pref"); + Assert.ok(Services.prefs.getPrefType("browser.migrate.automigrate-finished"), + "Should have set finish time pref"); + Assert.ok((yield AutoMigrate.canUndo()), "Should be able to undo migration"); + + let [beginRange, endRange] = AutoMigrate.getUndoRange(); + let stringRange = `beginRange: ${beginRange}; endRange: ${endRange}`; + Assert.ok(beginRange <= endRange, + "Migration should have started before or when it ended " + stringRange); + + yield AutoMigrate.undo(); + Assert.ok(true, "Should be able to finish an undo cycle."); +}); + +/** + * Fake a migration and then try to undo it to verify all data gets removed. + */ +add_task(function* checkUndoRemoval() { + let startTime = "" + Date.now(); + Services.prefs.setCharPref("browser.migrate.automigrate-started", startTime); + + // Insert a login and check that that worked. + let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(Ci.nsILoginInfo); + login.init("www.mozilla.org", "http://www.mozilla.org", null, "user", "pass", "userEl", "passEl"); + Services.logins.addLogin(login); + let storedLogins = Services.logins.findLogins({}, "www.mozilla.org", + "http://www.mozilla.org", null); + Assert.equal(storedLogins.length, 1, "Should have 1 login"); + + // Insert a bookmark and check that we have exactly 1 bookmark for that URI. + yield PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://www.example.org/", + title: "Some example bookmark", + }); + + let bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"}); + Assert.ok(bookmark, "Should have a bookmark before undo"); + Assert.equal(bookmark.title, "Some example bookmark", "Should have correct bookmark before undo."); + + // Insert 2 history visits - one in the current migration time, one from before. + let now_uSec = Date.now() * 1000; + let visitedURI = Services.io.newURI("http://www.example.com/", null, null); + yield PlacesTestUtils.addVisits([ + {uri: visitedURI, visitDate: now_uSec}, + {uri: visitedURI, visitDate: now_uSec - 100 * kUsecPerMin}, + ]); + + // Verify that both visits get reported. + let opts = PlacesUtils.history.getNewQueryOptions(); + opts.resultType = opts.RESULTS_AS_VISIT; + let query = PlacesUtils.history.getNewQuery(); + query.uri = visitedURI; + let visits = PlacesUtils.history.executeQuery(query, opts); + visits.root.containerOpen = true; + Assert.equal(visits.root.childCount, 2, "Should have 2 visits"); + // Clean up: + visits.root.containerOpen = false; + + // Now set finished pref: + let endTime = "" + Date.now(); + Services.prefs.setCharPref("browser.migrate.automigrate-finished", endTime); + + // Verify that we can undo, then undo: + Assert.ok(yield AutoMigrate.canUndo(), "Should be possible to undo migration"); + yield AutoMigrate.undo(); + + // Check that the undo removed the history visits: + visits = PlacesUtils.history.executeQuery(query, opts); + visits.root.containerOpen = true; + Assert.equal(visits.root.childCount, 0, "Should have no more visits"); + visits.root.containerOpen = false; + + // Check that the undo removed the bookmarks: + bookmark = yield PlacesUtils.bookmarks.fetch({url: "http://www.example.org/"}); + Assert.ok(!bookmark, "Should have no bookmarks after undo"); + + // Check that the undo removed the passwords: + storedLogins = Services.logins.findLogins({}, "www.mozilla.org", + "http://www.mozilla.org", null); + Assert.equal(storedLogins.length, 0, "Should have no logins"); + + // Finally check prefs got cleared: + Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-started"), + "Should no longer have pref for migration start time."); + Assert.ok(!Services.prefs.getPrefType("browser.migrate.automigrate-finished"), + "Should no longer have pref for migration finish time."); }); diff --git a/browser/components/preferences/fonts.xul b/browser/components/preferences/fonts.xul index 172ea440a3f6..ed1d1ecc21b0 100644 --- a/browser/components/preferences/fonts.xul +++ b/browser/components/preferences/fonts.xul @@ -68,7 +68,7 @@ - + diff --git a/browser/components/preferences/in-content/privacy.xul b/browser/components/preferences/in-content/privacy.xul index f67ba9ba202d..3f9de6839ced 100644 --- a/browser/components/preferences/in-content/privacy.xul +++ b/browser/components/preferences/in-content/privacy.xul @@ -136,9 +136,9 @@ - &doNotTrack.pre.label;&doNotTrack.post.label; + &doNotTrack.pre.label;&doNotTrack.settings.label;&doNotTrack.post.label; @@ -165,11 +165,11 @@ &rememberDescription.label; - &rememberActions.pre.label;&rememberActions.middle.label;&rememberActions.post.label; + &rememberActions.pre.label;&rememberActions.clearHistory.label;&rememberActions.middle.label;&rememberActions.removeCookies.label;&rememberActions.post.label; @@ -178,9 +178,9 @@ &dontrememberDescription.label; - &dontrememberActions.pre.label;&dontrememberActions.post.label; + &dontrememberActions.pre.label;&dontrememberActions.clearHistory.label;&dontrememberActions.post.label; diff --git a/browser/components/sessionstore/SessionWorker.js b/browser/components/sessionstore/SessionWorker.js index 7b53c6d83f0e..bc3071f73302 100644 --- a/browser/components/sessionstore/SessionWorker.js +++ b/browser/components/sessionstore/SessionWorker.js @@ -344,25 +344,29 @@ var Agent = { let exn = null; let iterator = new File.DirectoryIterator(path); - if (!iterator.exists()) { - return; - } - for (let entry in iterator) { - if (entry.isDir) { - continue; + try { + if (!iterator.exists()) { + return; } - if (!prefix || entry.name.startsWith(prefix)) { - try { - File.remove(entry.path); - } catch (ex) { - // Don't stop immediately - exn = exn || ex; + for (let entry in iterator) { + if (entry.isDir) { + continue; + } + if (!prefix || entry.name.startsWith(prefix)) { + try { + File.remove(entry.path); + } catch (ex) { + // Don't stop immediately + exn = exn || ex; + } } } - } - if (exn) { - throw exn; + if (exn) { + throw exn; + } + } finally { + iterator.close(); } }, }; diff --git a/browser/config/mozconfigs/linux64/artifact b/browser/config/mozconfigs/linux64/artifact index fb28e4c5116f..4a1ecafc2ea3 100644 --- a/browser/config/mozconfigs/linux64/artifact +++ b/browser/config/mozconfigs/linux64/artifact @@ -1,9 +1,8 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0 MOZ_AUTOMATION_L10N_CHECK=0 -NO_CACHE=1 - -. "$topsrcdir/browser/config/mozconfigs/linux64/nightly" +. "$topsrcdir/browser/config/mozconfigs/linux64/common-opt" +. "$topsrcdir/build/mozconfig.common.override" ac_add_options --enable-artifact-builds unset CC diff --git a/browser/extensions/loop/bootstrap.js b/browser/extensions/loop/bootstrap.js index 14f532f10c70..1cf8f9146508 100644 --- a/browser/extensions/loop/bootstrap.js +++ b/browser/extensions/loop/bootstrap.js @@ -876,6 +876,25 @@ var WindowListener = { */ _browserSharePaused: false, + /** + * Stores details about the last notification. + * + * @type {Object} + */ + _lastNotification: {}, + + /** + * Used to determine if the browser sharing info bar is currently being + * shown or not. + */ + _showingBrowserSharingInfoBar: function _showingBrowserSharingInfoBar() { + var browser = gBrowser.selectedBrowser; + var box = gBrowser.getNotificationBox(browser); + var notification = box.getNotificationWithValue(kBrowserSharingNotificationId); + + return !!notification;}, + + /** * Shows an infobar notification at the top of the browser window that warns * the user that their browser tabs are being broadcasted through the current @@ -884,10 +903,25 @@ var WindowListener = { * @return {void} */ _maybeShowBrowserSharingInfoBar: function _maybeShowBrowserSharingInfoBar(currentRoomToken) {var _this13 = this; - this._hideBrowserSharingInfoBar(); - var participantsCount = this.LoopRooms.getNumParticipants(currentRoomToken); + if (this._showingBrowserSharingInfoBar()) { + // When we first open the room, there will be one or zero partipicants + // in the room. The notification box changes when there's more than one, + // so work that out here. + var notAlone = participantsCount > 1; + var previousNotAlone = this._lastNotification.participantsCount <= 1; + + // If we're not actually changing the notification bar, then don't + // re-display it. This avoids the bar sliding in twice. + if (notAlone !== previousNotAlone && + this._browserSharePaused === this._lastNotification.paused) { + return;} + + + this._hideBrowserSharingInfoBar();} + + var initStrings = this._setInfoBarStrings(participantsCount > 1, this._browserSharePaused); var box = gBrowser.getNotificationBox(); @@ -935,7 +969,10 @@ var WindowListener = { bar.classList.toggle("paused", !!this._browserSharePaused); // Keep showing the notification bar until the user explicitly closes it. - bar.persistence = -1;}, + bar.persistence = -1; + + this._lastNotification.participantsCount = participantsCount; + this._lastNotification.paused = this._browserSharePaused;}, /** diff --git a/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm b/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm index 440dd92e4e87..89485ecbd366 100644 --- a/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm +++ b/browser/extensions/loop/chrome/content/modules/MozLoopService.jsm @@ -1105,19 +1105,19 @@ var MozLoopServiceInternal = { // It's common for unit tests to overload Chat.open, so check if we actually // got a DOM node back. } else if (chatboxInstance.setAttribute) { - // Set properties that influence visual appearance of the chatbox right - // away to circumvent glitches. - chatboxInstance.setAttribute("customSize", "loopDefault"); - chatboxInstance.parentNode.setAttribute("customSize", "loopDefault"); - var buttons = "minimize,"; - if (MozLoopService.getLoopPref("conversationPopOut.enabled")) { - buttons += "swap,";} + // Set properties that influence visual appearance of the chatbox right + // away to circumvent glitches. + chatboxInstance.setAttribute("customSize", "loopDefault"); + chatboxInstance.parentNode.setAttribute("customSize", "loopDefault"); + var buttons = "minimize,"; + if (MozLoopService.getLoopPref("conversationPopOut.enabled")) { + buttons += "swap,";} - Chat.loadButtonSet(chatboxInstance, buttons + kChatboxHangupButton.id); - // Final fall-through in case a unit test overloaded Chat.open. Here we can - // immediately resolve the promise. - } else { - resolve(windowId);}});}, + Chat.loadButtonSet(chatboxInstance, buttons + kChatboxHangupButton.id); + // Final fall-through in case a unit test overloaded Chat.open. Here we can + // immediately resolve the promise. + } else { + resolve(windowId);}});}, diff --git a/browser/extensions/loop/chrome/content/panels/js/roomViews.js b/browser/extensions/loop/chrome/content/panels/js/roomViews.js index eaa3e179229d..973481001382 100644 --- a/browser/extensions/loop/chrome/content/panels/js/roomViews.js +++ b/browser/extensions/loop/chrome/content/panels/js/roomViews.js @@ -185,20 +185,11 @@ loop.roomViews = function (mozL10n) { nextState.roomState === ROOM_STATES.MEDIA_WAIT) { this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ publisherConfig: this.getDefaultPublisherConfig({ - publishVideo: !this.state.videoMuted }) }));} + publishVideo: !this.state.videoMuted }) }));}}, - // Now that we're ready to share, automatically start sharing a tab only - // if we're not already connected to the room via the sdk, e.g. not in the - // case a remote participant just left. - if (nextState.roomState === ROOM_STATES.SESSION_CONNECTED && - !(this.state.roomState === ROOM_STATES.SESSION_CONNECTED || - this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS)) { - this.props.dispatcher.dispatch(new sharedActions.StartBrowserShare());}}, - - /** * User clicked on the "Leave" button. diff --git a/browser/extensions/loop/chrome/content/panels/test/roomViews_test.js b/browser/extensions/loop/chrome/content/panels/test/roomViews_test.js index 4aa040d7fa90..4e3c42d87aea 100644 --- a/browser/extensions/loop/chrome/content/panels/test/roomViews_test.js +++ b/browser/extensions/loop/chrome/content/panels/test/roomViews_test.js @@ -370,44 +370,11 @@ describe("loop.roomViews", function () { sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.SetupStreamElements({ publisherConfig: { - fake: "config" } }));}); + fake: "config" } }));});}); - it("should dispatch a `StartBrowserShare` action when the SESSION_CONNECTED state is entered", function () { - activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY }); - mountTestComponent(); - - activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED }); - - sinon.assert.calledOnce(dispatcher.dispatch); - sinon.assert.calledWithExactly(dispatcher.dispatch, - new sharedActions.StartBrowserShare());}); - - - it("should not dispatch a `StartBrowserShare` action when the previous state was HAS_PARTICIPANTS", function () { - activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS }); - mountTestComponent(); - - activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED }); - - sinon.assert.notCalled(dispatcher.dispatch);}); - - - it("should not dispatch a `StartBrowserShare` action when the previous state was SESSION_CONNECTED", function () { - activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED }); - mountTestComponent(); - - activeRoomStore.setStoreState({ - roomState: ROOM_STATES.SESSION_CONNECTED, - // Additional change to force an update. - screenSharingState: "fake" }); - - - sinon.assert.notCalled(dispatcher.dispatch);});}); - - describe("#render", function () { it("should set document.title to store.serverData.roomName", function () { diff --git a/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js b/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js index f175baf3360e..98d391d0d981 100644 --- a/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js +++ b/browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js @@ -718,8 +718,16 @@ loop.store.ActiveRoomStore = function (mozL10n) { this._sdkDriver.connectSession(actionData); - loop.request("AddConversationContext", this._storeState.windowId, - actionData.sessionId, "");}, + this._browserSharingListener = this._handleSwitchBrowserShare.bind(this); + + // Set up a listener for watching screen shares. This will get notified + // with the first windowId when it is added, so we may start off the sharing + // from within the listener. + loop.subscribe("BrowserSwitch", this._browserSharingListener); + + loop.requestMulti(["AddConversationContext", this._storeState.windowId, + actionData.sessionId, ""], + ["AddBrowserSharingListener", this.getStoreState().windowId]);}, /** @@ -780,7 +788,14 @@ loop.store.ActiveRoomStore = function (mozL10n) { this.setStoreState({ remoteAudioEnabled: actionData.hasAudio, remoteVideoEnabled: actionData.hasVideo, - remoteSrcMediaElement: actionData.srcMediaElement });}, + remoteSrcMediaElement: actionData.srcMediaElement }); + + + // We start browser sharing here so that it starts *after* the audio/video + // has connected. This is to attempt to help performance when a room is + // initially joined. + if (this._isDesktop) { + this.startBrowserShare();}}, @@ -866,10 +881,9 @@ loop.store.ActiveRoomStore = function (mozL10n) { if (Array.isArray(windowId)) { windowId = windowId[0];} - if (!windowId) { - return;} - if (windowId.isError) { + // There was an error getting the windowId, so lets just reset things. + if (windowId && windowId.isError) { console.error("Error getting the windowId: " + windowId.message); this.dispatchAction(new sharedActions.ScreenSharingState({ state: SCREEN_SHARE_STATES.INACTIVE })); @@ -877,8 +891,27 @@ loop.store.ActiveRoomStore = function (mozL10n) { return;} + // If there's no windowId, see if we've got one saved. + if (!windowId) { + // If we really don't have a window Id, then don't do anything. + if (!this._savedWindowId) { + return;} + + + windowId = this._savedWindowId; + delete this._savedWindowId;} + + var screenSharingState = this.getStoreState().screenSharingState; + // If we're inactive, or screen sharing is paused, just save the windowId + // for when we're ready. + if (screenSharingState === SCREEN_SHARE_STATES.INACTIVE || + this._storeState.sharingPaused) { + this._savedWindowId = windowId; + return;} + + if (screenSharingState === SCREEN_SHARE_STATES.PENDING) { // Screen sharing is still pending, so assume that we need to kick it off. var options = { @@ -947,14 +980,10 @@ loop.store.ActiveRoomStore = function (mozL10n) { state: SCREEN_SHARE_STATES.PENDING })); - this._browserSharingListener = this._handleSwitchBrowserShare.bind(this); - - // Set up a listener for watching screen shares. This will get notified - // with the first windowId when it is added, so we start off the sharing - // from within the listener. - loop.request("AddBrowserSharingListener", this.getStoreState().windowId). - then(this._browserSharingListener); - loop.subscribe("BrowserSwitch", this._browserSharingListener);}, + // This attempts to start the actual browser sharing - we assume we've + // already got a windowId due to having requested it before connecting the + // media. + this._handleSwitchBrowserShare();}, /** @@ -985,6 +1014,16 @@ loop.store.ActiveRoomStore = function (mozL10n) { sharingPaused: !actionData.enabled }); + // If we've un-paused screen sharing, but we haven't started sharing, then + // we need to start that off. + if (actionData.enabled && + this._storeState.screenSharingState === SCREEN_SHARE_STATES.PENDING) { + this._handleSwitchBrowserShare();} else + { + // Otherwise just toggle the stream. + this._sdkDriver.toggleBrowserSharing(actionData.enabled);} + + // If unpausing, check the context as it might have changed. if (actionData.enabled) { this._checkTabContext();}}, diff --git a/browser/extensions/loop/chrome/content/shared/js/otSdkDriver.js b/browser/extensions/loop/chrome/content/shared/js/otSdkDriver.js index 94c75c43c3b6..3b35a131c49a 100644 --- a/browser/extensions/loop/chrome/content/shared/js/otSdkDriver.js +++ b/browser/extensions/loop/chrome/content/shared/js/otSdkDriver.js @@ -41,8 +41,7 @@ loop.OTSdkDriver = function () { this.dispatcher.register(this, [ "setupStreamElements", - "setMute", - "toggleBrowserSharing"]); + "setMute"]); // Set loop.debug.sdk to true in the browser, or in standalone: @@ -237,11 +236,12 @@ loop.OTSdkDriver = function () { /** * Paused or resumes an active screenshare session as appropriate. * - * @param {sharedActions.ToggleBrowserSharing} actionData The data associated with the - * action. See action.js. + * @param {Boolean} enabled True if browser sharing should be enabled. */ - toggleBrowserSharing: function toggleBrowserSharing(actionData) { - this.screenshare.publishVideo(actionData.enabled);}, + toggleBrowserSharing: function toggleBrowserSharing(enabled) { + if (this.screenshare) { + this.screenshare.publishVideo(enabled);}}, + /** @@ -537,10 +537,8 @@ loop.OTSdkDriver = function () { _handleRemoteScreenShareCreated: function _handleRemoteScreenShareCreated(stream) { // Let the stores know first if the screen sharing is paused or not so // they can update the display properly - if (!stream[STREAM_PROPERTIES.HAS_VIDEO]) { - this.dispatcher.dispatch(new sharedActions.VideoScreenStreamChanged({ - hasVideo: false }));} - + this.dispatcher.dispatch(new sharedActions.VideoScreenStreamChanged({ + hasVideo: stream[STREAM_PROPERTIES.HAS_VIDEO] })); // Let the stores know so they can update the display if needed. diff --git a/browser/extensions/loop/chrome/content/shared/js/utils.js b/browser/extensions/loop/chrome/content/shared/js/utils.js index 89206e953d71..009787cb8966 100644 --- a/browser/extensions/loop/chrome/content/shared/js/utils.js +++ b/browser/extensions/loop/chrome/content/shared/js/utils.js @@ -361,17 +361,17 @@ if (inChrome) { // MediaStreamTrack is the older version of the API, implemented originally // by Google Chrome. } else if ("MediaStreamTrack" in rootObject && - "getSources" in rootObject.MediaStreamTrack) { - rootObject.MediaStreamTrack.getSources(function (result) { - function checkForInput(device) { - return device.kind === "audio" || device.kind === "video";} + "getSources" in rootObject.MediaStreamTrack) { + rootObject.MediaStreamTrack.getSources(function (result) { + function checkForInput(device) { + return device.kind === "audio" || device.kind === "video";} - callback(result.some(checkForInput));});} else + callback(result.some(checkForInput));});} else - { - // We don't know, so assume true. - callback(true);}} + { + // We don't know, so assume true. + callback(true);}} diff --git a/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js b/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js index 07d41c1918b5..1ff05d192ee5 100644 --- a/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js +++ b/browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js @@ -51,6 +51,7 @@ describe("loop.store.ActiveRoomStore", function () { retryPublishWithoutVideo: sinon.stub(), startScreenShare: sinon.stub(), switchAcquiredWindow: sinon.stub(), + toggleBrowserSharing: sinon.stub(), endScreenShare: sinon.stub().returns(true) }; @@ -195,7 +196,12 @@ describe("loop.store.ActiveRoomStore", function () { sandbox.stub(loop, "unsubscribe"); // Setup the listener. - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + // Now simulate room failure. store.roomFailure(new sharedActions.RoomFailure({ @@ -1206,7 +1212,12 @@ describe("loop.store.ActiveRoomStore", function () { sandbox.stub(loop, "unsubscribe"); // Setup the listener. - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + // Now simulate connection failure. store.connectionFailure(connectionFailureAction); @@ -1337,7 +1348,25 @@ describe("loop.store.ActiveRoomStore", function () { expect(store.getStoreState().localVideoEnabled).eql(false); expect(store.getStoreState().remoteVideoEnabled).eql(true); - expect(store.getStoreState().remoteAudioEnabled).eql(true);});}); + expect(store.getStoreState().remoteAudioEnabled).eql(true);}); + + + it("should call startBrowserShare when is desktop", function () { + sandbox.stub(store, "startBrowserShare"); + store._isDesktop = true; + store.setStoreState({ + localVideoEnabled: false, + remoteVideoEnabled: false }); + + + store.mediaStreamCreated(new sharedActions.MediaStreamCreated({ + hasAudio: true, + hasVideo: true, + isLocal: false, + srcMediaElement: fakeStreamElement })); + + + sinon.assert.calledOnce(store.startBrowserShare);});}); @@ -1494,18 +1523,7 @@ describe("loop.store.ActiveRoomStore", function () { describe("#startBrowserShare", function () { - var getSelectedTabMetadataStub; - beforeEach(function () { - getSelectedTabMetadataStub = sinon.stub(); - LoopMochaUtils.stubLoopRequest({ - GetSelectedTabMetadata: getSelectedTabMetadataStub.returns({ - title: "fakeTitle", - favicon: "fakeFavicon", - url: "http://www.fakeurl.com" }) }); - - - store.setStoreState({ roomState: ROOM_STATES.JOINED, roomToken: "fakeToken", @@ -1519,7 +1537,8 @@ describe("loop.store.ActiveRoomStore", function () { - sandbox.stub(console, "error");}); + sandbox.stub(console, "error"); + sandbox.stub(store, "_handleSwitchBrowserShare");}); afterEach(function () { @@ -1556,13 +1575,140 @@ describe("loop.store.ActiveRoomStore", function () { - it("should add a browser sharing listener for tab sharing", function () { + it("should call _handleSwitchBrowserShare", function () { store.startBrowserShare(new sharedActions.StartBrowserShare()); - sinon.assert.calledOnce(requestStubs.AddBrowserSharingListener);}); + + sinon.assert.calledOnce(store._handleSwitchBrowserShare);});}); - it("should invoke the SDK driver with the correct options for tab sharing", function () { - store.startBrowserShare(new sharedActions.StartBrowserShare()); + + describe("Screen share Events", function () { + it("should call _handleSwitchBrowserShare", function () { + sandbox.stub(store, "_handleSwitchBrowserShare"); + + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + + + LoopMochaUtils.publish("BrowserSwitch", 72); + + sinon.assert.calledOnce(store._handleSwitchBrowserShare);});}); + + + + describe("#_handleSwitchBrowserShare", function () { + var getSelectedTabMetadataStub; + + beforeEach(function () { + getSelectedTabMetadataStub = sinon.stub(); + LoopMochaUtils.stubLoopRequest({ + GetSelectedTabMetadata: getSelectedTabMetadataStub.returns({ + title: "fakeTitle", + favicon: "fakeFavicon", + url: "http://www.fakeurl.com" }) }); + + + + store.setStoreState({ + roomState: ROOM_STATES.JOINED, + roomToken: "fakeToken", + screenSharingState: SCREEN_SHARE_STATES.ACTIVE, + sessionToken: "1627384950", + participants: [{ + displayName: "Owner", + owner: true }, + { + displayName: "Guest", + owner: false }] }); + + + + // Stub to prevent errors surfacing in the console. + sandbox.stub(console, "error");}); + + + afterEach(function () { + store.endScreenShare();}); + + + it("should log an error in the console", function () { + var err = new Error("foo"); + err.isError = true; + store._handleSwitchBrowserShare(err); + + sinon.assert.calledOnce(console.error);}); + + + it("should end the screen sharing session when the listener receives an error", function () { + var err = new Error("foo"); + err.isError = true; + store._handleSwitchBrowserShare(err); + + // The dispatcher was already called once in beforeEach(). + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWith(dispatcher.dispatch, + new sharedActions.ScreenSharingState({ + state: SCREEN_SHARE_STATES.INACTIVE })); + + sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);}); + + + it("should save the windowId when the state is INACTIVE", function () { + store.setStoreState({ + screenSharingState: SCREEN_SHARE_STATES.INACTIVE }); + + + store._handleSwitchBrowserShare(72); + + expect(store._savedWindowId, 72);}); + + + it("should not do anything else when the state is INACTIVE", function () { + store.setStoreState({ + screenSharingState: SCREEN_SHARE_STATES.INACTIVE }); + + + store._handleSwitchBrowserShare(72); + + sinon.assert.notCalled(dispatcher.dispatch); + sinon.assert.notCalled(fakeSdkDriver.startScreenShare); + sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);}); + + + it("should save the windowId when sharing is paused", function () { + store.setStoreState({ + screenSharingState: SCREEN_SHARE_STATES.ACTIVE, + sharingPaused: true }); + + + store._handleSwitchBrowserShare(72); + + expect(store._savedWindowId, 72);}); + + + it("should not do anything else when the state is paused", function () { + store.setStoreState({ + screenSharingState: SCREEN_SHARE_STATES.ACTIVE, + sharingPaused: true }); + + + store._handleSwitchBrowserShare(72); + + sinon.assert.notCalled(dispatcher.dispatch); + sinon.assert.notCalled(fakeSdkDriver.startScreenShare); + sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);}); + + + it("should invoke the SDK driver with the correct options if the state is pending", function () { + store.setStoreState({ + screenSharingState: SCREEN_SHARE_STATES.PENDING }); + + + store._handleSwitchBrowserShare(42); + sinon.assert.calledOnce(fakeSdkDriver.startScreenShare); sinon.assert.calledWith(fakeSdkDriver.startScreenShare, { videoSource: "browser", @@ -1573,13 +1719,31 @@ describe("loop.store.ActiveRoomStore", function () { + it("should update the SDK driver when the state is active", function () { + store._handleSwitchBrowserShare(72); + + sinon.assert.calledOnce(fakeSdkDriver.switchAcquiredWindow); + sinon.assert.calledWithExactly(fakeSdkDriver.switchAcquiredWindow, 72);}); + + + it("should log an error if the state is unexpected", function () { + store.setStoreState({ + screenSharingState: "invalid" }); + + + store._handleSwitchBrowserShare(72); + + sinon.assert.calledOnce(console.error);}); + + it("should request the new metadata when the browser being shared change", function () { - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store._handleSwitchBrowserShare(42); + clock.tick(500); sinon.assert.calledOnce(getSelectedTabMetadataStub); - sinon.assert.calledTwice(dispatcher.dispatch); - sinon.assert.calledWith(dispatcher.dispatch.getCall(1), + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.UpdateRoomContext({ newRoomDescription: "fakeTitle", newRoomThumbnail: "fakeFavicon", @@ -1591,13 +1755,14 @@ describe("loop.store.ActiveRoomStore", function () { it("should process only one request", function () { store.startBrowserShare(new sharedActions.StartBrowserShare()); // Simulates multiple requests. - LoopMochaUtils.publish("BrowserSwitch", 72); - LoopMochaUtils.publish("BrowserSwitch", 72); + store._handleSwitchBrowserShare(42); + store._handleSwitchBrowserShare(42); clock.tick(500); - sinon.assert.calledThrice(getSelectedTabMetadataStub); - sinon.assert.calledTwice(dispatcher.dispatch); - sinon.assert.calledWith(dispatcher.dispatch.getCall(1), + + sinon.assert.calledTwice(getSelectedTabMetadataStub); + sinon.assert.calledOnce(dispatcher.dispatch); + sinon.assert.calledWithExactly(dispatcher.dispatch, new sharedActions.UpdateRoomContext({ newRoomDescription: "fakeTitle", newRoomThumbnail: "fakeFavicon", @@ -1612,11 +1777,12 @@ describe("loop.store.ActiveRoomStore", function () { favicon: "fakeFavicon" }); - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store._handleSwitchBrowserShare(42); + clock.tick(500); sinon.assert.calledOnce(getSelectedTabMetadataStub); - sinon.assert.calledOnce(dispatcher.dispatch);}); + sinon.assert.notCalled(dispatcher.dispatch);}); it("should not process a request if sharing is paused", function () { @@ -1624,11 +1790,11 @@ describe("loop.store.ActiveRoomStore", function () { sharingPaused: true }); - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store._handleSwitchBrowserShare(42); clock.tick(500); sinon.assert.notCalled(getSelectedTabMetadataStub); - sinon.assert.calledOnce(dispatcher.dispatch);}); + sinon.assert.notCalled(dispatcher.dispatch);}); it("should not process a request if no-one is in the room", function () { @@ -1639,57 +1805,12 @@ describe("loop.store.ActiveRoomStore", function () { - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store._handleSwitchBrowserShare(42); + clock.tick(500); sinon.assert.notCalled(getSelectedTabMetadataStub); - sinon.assert.calledOnce(dispatcher.dispatch);});}); - - - - describe("Screen share Events", function () { - beforeEach(function () { - store.startBrowserShare(new sharedActions.StartBrowserShare()); - - store.setStoreState({ - screenSharingState: SCREEN_SHARE_STATES.ACTIVE }); - - - // Stub to prevent errors surfacing in the console. - sandbox.stub(window.console, "error");}); - - - afterEach(function () { - store.endScreenShare();}); - - - it("should log an error in the console", function () { - var err = new Error("foo"); - err.isError = true; - LoopMochaUtils.publish("BrowserSwitch", err); - - sinon.assert.calledOnce(console.error);}); - - - it("should update the SDK driver when a new window id is received", function () { - LoopMochaUtils.publish("BrowserSwitch", 72); - - sinon.assert.calledOnce(fakeSdkDriver.switchAcquiredWindow); - sinon.assert.calledWithExactly(fakeSdkDriver.switchAcquiredWindow, 72);}); - - - it("should end the screen sharing session when the listener receives an error", function () { - var err = new Error("foo"); - err.isError = true; - LoopMochaUtils.publish("BrowserSwitch", err); - - // The dispatcher was already called once in beforeEach(). - sinon.assert.calledTwice(dispatcher.dispatch); - sinon.assert.calledWith(dispatcher.dispatch, - new sharedActions.ScreenSharingState({ - state: SCREEN_SHARE_STATES.INACTIVE })); - - sinon.assert.notCalled(fakeSdkDriver.switchAcquiredWindow);});}); + sinon.assert.notCalled(dispatcher.dispatch);});}); @@ -1706,7 +1827,12 @@ describe("loop.store.ActiveRoomStore", function () { it("should remove the sharing listener", function () { // Setup the listener. - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + // Now stop the screen share. store.endScreenShare(); @@ -1923,7 +2049,12 @@ describe("loop.store.ActiveRoomStore", function () { sandbox.stub(loop, "unsubscribe"); // Setup the listener. - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + // Now unload the window. store.windowUnload(); @@ -1992,7 +2123,12 @@ describe("loop.store.ActiveRoomStore", function () { sandbox.stub(loop, "unsubscribe"); // Setup the listener. - store.startBrowserShare(new sharedActions.StartBrowserShare()); + store.joinedRoom(new sharedActions.JoinedRoom({ + apiKey: "", + sessionToken: "", + sessionId: "", + expires: 0 })); + // Now leave the room. store.leaveRoom(); diff --git a/browser/extensions/loop/chrome/content/shared/vendor/sdk.js b/browser/extensions/loop/chrome/content/shared/vendor/sdk.js index 64f7df02ff2d..eb8d8a004d86 100755 --- a/browser/extensions/loop/chrome/content/shared/vendor/sdk.js +++ b/browser/extensions/loop/chrome/content/shared/vendor/sdk.js @@ -1,11 +1,11 @@ /** - * @license OpenTok.js v2.7.5 ccd6792 HEAD + * @license OpenTok.js v2.7.6 cff9122 HEAD * * Copyright (c) 2010-2015 TokBox, Inc. * Released under the MIT license * http://opensource.org/licenses/MIT * - * Date: March 18 10:46:23 2016 + * Date: June 24 07:05:09 2016 */ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o loop@mozilla.org true - 1.4.1 + 1.4.2 2 + + + + Test for diabled SRI require-sri-for CSP directive + + + + +Mozilla Bug 1265318 + + + + diff --git a/dom/storage/DOMStorage.cpp b/dom/storage/DOMStorage.cpp index b4f2acb70b37..68116d5d4481 100644 --- a/dom/storage/DOMStorage.cpp +++ b/dom/storage/DOMStorage.cpp @@ -112,13 +112,6 @@ DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData, return; } - Telemetry::Accumulate(GetType() == LocalStorage - ? Telemetry::LOCALDOMSTORAGE_KEY_SIZE_BYTES - : Telemetry::SESSIONDOMSTORAGE_KEY_SIZE_BYTES, aKey.Length()); - Telemetry::Accumulate(GetType() == LocalStorage - ? Telemetry::LOCALDOMSTORAGE_VALUE_SIZE_BYTES - : Telemetry::SESSIONDOMSTORAGE_VALUE_SIZE_BYTES, aData.Length()); - nsString data; bool ok = data.Assign(aData, fallible); if (!ok) { diff --git a/dom/storage/DOMStorageDBThread.cpp b/dom/storage/DOMStorageDBThread.cpp index 95e461c9f6fe..16a4bd0f344b 100644 --- a/dom/storage/DOMStorageDBThread.cpp +++ b/dom/storage/DOMStorageDBThread.cpp @@ -489,8 +489,6 @@ DOMStorageDBThread::OpenAndUpdateDatabase() nsresult DOMStorageDBThread::InitDatabase() { - Telemetry::AutoTimer timer; - nsresult rv; // Here we are on the worker thread. This opens the worker connection. diff --git a/dom/url/URL.cpp b/dom/url/URL.cpp new file mode 100644 index 000000000000..be15b6cab2b8 --- /dev/null +++ b/dom/url/URL.cpp @@ -0,0 +1,1763 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#include "URL.h" + +#include "DOMMediaStream.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/MediaSource.h" +#include "mozilla/dom/URLBinding.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "mozilla/dom/ipc/nsIRemoteBlob.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "nsContentUtils.h" +#include "nsEscape.h" +#include "nsHostObjectProtocolHandler.h" +#include "nsIIOService.h" +#include "nsIURL.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" +#include "WorkerScope.h" + +namespace mozilla { +namespace dom { + +/////////////////////////////////////////////////////////////////////////////// +// URL for main-thread +/////////////////////////////////////////////////////////////////////////////// + +namespace { + +// The URL implementation for the main-thread +class URLMainThread final : public URL +{ +public: + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(nsISupports* aParent, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(nsISupports* aParent, const nsAString& aURL, nsIURI* aBase, + ErrorResult& aRv); + + static void + CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv) + { + MOZ_ASSERT(NS_IsMainThread()); + CreateObjectURLInternal(aGlobal, aBlob.Impl(), + NS_LITERAL_CSTRING(BLOBURI_SCHEME), aOptions, + aResult, aRv); + } + + static void + CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv) + { + MOZ_ASSERT(NS_IsMainThread()); + CreateObjectURLInternal(aGlobal, &aStream, + NS_LITERAL_CSTRING(MEDIASTREAMURI_SCHEME), aOptions, + aResult, aRv); + } + + static void + CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv); + + static void + CreateObjectURLInternal(const GlobalObject& aGlobal, nsISupports* aObject, + const nsACString& aScheme, + const objectURLOptions& aOptions, + nsAString& aResult, ErrorResult& aRv); + + static void + RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL, + ErrorResult& aRv); + + URLMainThread(nsISupports* aParent, already_AddRefed aURI) + : URL(aParent) + , mURI(aURI) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + virtual void + GetHref(nsAString& aHref, ErrorResult& aRv) const override; + + virtual void + SetHref(const nsAString& aHref, ErrorResult& aRv) override; + + virtual void + GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const override; + + virtual void + GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const override; + + virtual void + SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) override; + + virtual void + GetUsername(nsAString& aUsername, ErrorResult& aRv) const override; + + virtual void + SetUsername(const nsAString& aUsername, ErrorResult& aRv) override; + + virtual void + GetPassword(nsAString& aPassword, ErrorResult& aRv) const override; + + virtual void + SetPassword(const nsAString& aPassword, ErrorResult& aRv) override; + + virtual void + GetHost(nsAString& aHost, ErrorResult& aRv) const override; + + virtual void + SetHost(const nsAString& aHost, ErrorResult& aRv) override; + + virtual void + GetHostname(nsAString& aHostname, ErrorResult& aRv) const override; + + virtual void + SetHostname(const nsAString& aHostname, ErrorResult& aRv) override; + + virtual void + GetPort(nsAString& aPort, ErrorResult& aRv) const override; + + virtual void + SetPort(const nsAString& aPort, ErrorResult& aRv) override; + + virtual void + GetPathname(nsAString& aPathname, ErrorResult& aRv) const override; + + virtual void + SetPathname(const nsAString& aPathname, ErrorResult& aRv) override; + + virtual void + GetSearch(nsAString& aSearch, ErrorResult& aRv) const override; + + virtual void + GetHash(nsAString& aHost, ErrorResult& aRv) const override; + + virtual void + SetHash(const nsAString& aHash, ErrorResult& aRv) override; + + virtual void UpdateURLSearchParams() override; + + virtual void + SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) override; + + nsIURI* + GetURI() const + { + MOZ_ASSERT(NS_IsMainThread()); + return mURI; + } + +private: + ~URLMainThread() + { + MOZ_ASSERT(NS_IsMainThread()); + } + + nsCOMPtr mURI; +}; + +/* static */ already_AddRefed +URLMainThread::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + URLMainThread& base = static_cast(aBase); + return Constructor(aGlobal.GetAsSupports(), aURL, base.GetURI(), aRv); +} + +/* static */ already_AddRefed +URLMainThread::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv) +{ + if (aBase.WasPassed()) { + return Constructor(aGlobal.GetAsSupports(), aURL, aBase.Value(), aRv); + } + + return Constructor(aGlobal.GetAsSupports(), aURL, nullptr, aRv); +} + +/* static */ already_AddRefed +URLMainThread::Constructor(nsISupports* aParent, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr baseUri; + nsresult rv = NS_NewURI(getter_AddRefs(baseUri), aBase, nullptr, nullptr, + nsContentUtils::GetIOService()); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.ThrowTypeError(aBase); + return nullptr; + } + + return Constructor(aParent, aURL, baseUri, aRv); +} + +/* static */ already_AddRefed +URLMainThread::Constructor(nsISupports* aParent, const nsAString& aURL, + nsIURI* aBase, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr uri; + nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, aBase, + nsContentUtils::GetIOService()); + if (NS_WARN_IF(NS_FAILED(rv))) { + aRv.ThrowTypeError(aURL); + return nullptr; + } + + RefPtr url = new URLMainThread(aParent, uri.forget()); + return url.forget(); +} + +/* static */ void +URLMainThread::CreateObjectURL(const GlobalObject& aGlobal, + MediaSource& aSource, + const objectURLOptions& aOptions, + nsAString& aResult, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr principal = + nsContentUtils::ObjectPrincipal(aGlobal.Get()); + + nsAutoCString url; + aRv = nsHostObjectProtocolHandler:: + AddDataEntry(NS_LITERAL_CSTRING(MEDIASOURCEURI_SCHEME), + &aSource, principal, url); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsCOMPtr revocation = NS_NewRunnableFunction( + [url] { + nsHostObjectProtocolHandler::RemoveDataEntry(url); + }); + + nsContentUtils::RunInStableState(revocation.forget()); + + CopyASCIItoUTF16(url, aResult); +} + +/* static */ void +URLMainThread::CreateObjectURLInternal(const GlobalObject& aGlobal, + nsISupports* aObject, + const nsACString& aScheme, + const objectURLOptions& aOptions, + nsAString& aResult, ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nsCOMPtr principal = + nsContentUtils::ObjectPrincipal(aGlobal.Get()); + + nsAutoCString url; + nsresult rv = nsHostObjectProtocolHandler::AddDataEntry(aScheme, aObject, + principal, url); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + global->RegisterHostObjectURI(url); + CopyASCIItoUTF16(url, aResult); +} + +/* static */ void +URLMainThread::RevokeObjectURL(const GlobalObject& aGlobal, + const nsAString& aURL, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal.Get()); + + NS_LossyConvertUTF16toASCII asciiurl(aURL); + + nsIPrincipal* urlPrincipal = + nsHostObjectProtocolHandler::GetDataEntryPrincipal(asciiurl); + + if (urlPrincipal && principal->Subsumes(urlPrincipal)) { + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + global->UnregisterHostObjectURI(asciiurl); + nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl); + } +} + +void +URLMainThread::GetHref(nsAString& aHref, ErrorResult& aRv) const +{ + aHref.Truncate(); + + nsAutoCString href; + nsresult rv = mURI->GetSpec(href); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(href, aHref); + } +} + +void +URLMainThread::SetHref(const nsAString& aHref, ErrorResult& aRv) +{ + NS_ConvertUTF16toUTF8 href(aHref); + + nsresult rv; + nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + nsCOMPtr uri; + rv = ioService->NewURI(href, nullptr, nullptr, getter_AddRefs(uri)); + if (NS_FAILED(rv)) { + aRv.ThrowTypeError(aHref); + return; + } + + mURI = uri; + UpdateURLSearchParams(); +} + +void +URLMainThread::GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const +{ + nsContentUtils::GetUTFOrigin(mURI, aOrigin); +} + +void +URLMainThread::GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const +{ + nsAutoCString protocol; + if (NS_SUCCEEDED(mURI->GetScheme(protocol))) { + aProtocol.Truncate(); + } + + CopyASCIItoUTF16(protocol, aProtocol); + aProtocol.Append(char16_t(':')); +} + +void +URLMainThread::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) +{ + nsAString::const_iterator start, end; + aProtocol.BeginReading(start); + aProtocol.EndReading(end); + nsAString::const_iterator iter(start); + + FindCharInReadable(':', iter, end); + + // Changing the protocol of a URL, changes the "nature" of the URI + // implementation. In order to do this properly, we have to serialize the + // existing URL and reparse it in a new object. + nsCOMPtr clone; + nsresult rv = mURI->Clone(getter_AddRefs(clone)); + if (NS_WARN_IF(NS_FAILED(rv)) || !clone) { + return; + } + + rv = clone->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter))); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsAutoCString href; + rv = clone->GetSpec(href); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsCOMPtr uri; + rv = NS_NewURI(getter_AddRefs(uri), href); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + mURI = uri; +} + +#define URL_GETTER( value, func ) \ + value.Truncate(); \ + nsAutoCString tmp; \ + nsresult rv = mURI->func(tmp); \ + if (NS_SUCCEEDED(rv)) { \ + CopyUTF8toUTF16(tmp, value); \ + } + +void +URLMainThread::GetUsername(nsAString& aUsername, ErrorResult& aRv) const +{ + URL_GETTER(aUsername, GetUsername); +} + +void +URLMainThread::SetUsername(const nsAString& aUsername, ErrorResult& aRv) +{ + mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername)); +} + +void +URLMainThread::GetPassword(nsAString& aPassword, ErrorResult& aRv) const +{ + URL_GETTER(aPassword, GetPassword); +} + +void +URLMainThread::SetPassword(const nsAString& aPassword, ErrorResult& aRv) +{ + mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword)); +} + +void +URLMainThread::GetHost(nsAString& aHost, ErrorResult& aRv) const +{ + URL_GETTER(aHost, GetHostPort); +} + +void +URLMainThread::SetHost(const nsAString& aHost, ErrorResult& aRv) +{ + mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost)); +} + +void +URLMainThread::UpdateURLSearchParams() +{ + if (!mSearchParams) { + return; + } + + nsAutoCString search; + nsCOMPtr url(do_QueryInterface(mURI)); + if (url) { + nsresult rv = url->GetQuery(search); + if (NS_WARN_IF(NS_FAILED(rv))) { + search.Truncate(); + } + } + + mSearchParams->ParseInput(search); +} + +void +URLMainThread::GetHostname(nsAString& aHostname, ErrorResult& aRv) const +{ + aHostname.Truncate(); + nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname); +} + +void +URLMainThread::SetHostname(const nsAString& aHostname, ErrorResult& aRv) +{ + // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname + // The return code is silently ignored + mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname)); +} + +void +URLMainThread::GetPort(nsAString& aPort, ErrorResult& aRv) const +{ + aPort.Truncate(); + + int32_t port; + nsresult rv = mURI->GetPort(&port); + if (NS_SUCCEEDED(rv) && port != -1) { + nsAutoString portStr; + portStr.AppendInt(port, 10); + aPort.Assign(portStr); + } +} + +void +URLMainThread::SetPort(const nsAString& aPort, ErrorResult& aRv) +{ + nsresult rv; + nsAutoString portStr(aPort); + int32_t port = -1; + + // nsIURI uses -1 as default value. + if (!portStr.IsEmpty()) { + port = portStr.ToInteger(&rv); + if (NS_FAILED(rv)) { + return; + } + } + + mURI->SetPort(port); +} + +void +URLMainThread::GetPathname(nsAString& aPathname, ErrorResult& aRv) const +{ + aPathname.Truncate(); + + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + nsAutoCString path; + nsresult rv = mURI->GetPath(path); + if (NS_FAILED(rv)){ + // Do not throw! Not having a valid URI or URL should result in an empty + // string. + return; + } + + CopyUTF8toUTF16(path, aPathname); + return; + } + + nsAutoCString file; + nsresult rv = url->GetFilePath(file); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(file, aPathname); + } +} + +void +URLMainThread::SetPathname(const nsAString& aPathname, ErrorResult& aRv) +{ + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Ignore failures to be compatible with NS4. + return; + } + + url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname)); +} + +void +URLMainThread::GetSearch(nsAString& aSearch, ErrorResult& aRv) const +{ + aSearch.Truncate(); + + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Do not throw! Not having a valid URI or URL should result in an empty + // string. + return; + } + + nsAutoCString search; + nsresult rv = url->GetQuery(search); + if (NS_SUCCEEDED(rv) && !search.IsEmpty()) { + CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch); + } +} + +void +URLMainThread::GetHash(nsAString& aHash, ErrorResult& aRv) const +{ + aHash.Truncate(); + + nsAutoCString ref; + nsresult rv = mURI->GetRef(ref); + if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { + aHash.Assign(char16_t('#')); + if (nsContentUtils::GettersDecodeURLHash()) { + NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes! + } + AppendUTF8toUTF16(ref, aHash); + } +} + +void +URLMainThread::SetHash(const nsAString& aHash, ErrorResult& aRv) +{ + mURI->SetRef(NS_ConvertUTF16toUTF8(aHash)); +} + +void +URLMainThread::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) +{ + nsCOMPtr url(do_QueryInterface(mURI)); + if (!url) { + // Ignore failures to be compatible with NS4. + return; + } + + url->SetQuery(NS_ConvertUTF16toUTF8(aSearch)); +} + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// URL for Workers +/////////////////////////////////////////////////////////////////////////////// + +namespace { + +using namespace workers; + +// Proxy class to forward all the requests to a URLMainThread object. +class URLProxy final +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(URLProxy) + + explicit URLProxy(already_AddRefed aURL) + : mURL(aURL) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + URLMainThread* URL() + { + MOZ_ASSERT(NS_IsMainThread()); + return mURL; + } + + nsIURI* URI() + { + MOZ_ASSERT(NS_IsMainThread()); + return mURL->GetURI(); + } + + void ReleaseURI() + { + MOZ_ASSERT(NS_IsMainThread()); + mURL = nullptr; + } + +private: + // Private destructor, to discourage deletion outside of Release(): + ~URLProxy() + { + MOZ_ASSERT(!mURL); + } + + RefPtr mURL; +}; + +// URLWorker implements the URL object in workers. +class URLWorker final : public URL +{ +public: + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv); + + static void + CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, + const mozilla::dom::objectURLOptions& aOptions, + nsAString& aResult, mozilla::ErrorResult& aRv); + + static void + RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl, + ErrorResult& aRv); + + URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy); + + virtual void + GetHref(nsAString& aHref, ErrorResult& aRv) const override; + + virtual void + SetHref(const nsAString& aHref, ErrorResult& aRv) override; + + virtual void + GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const override; + + virtual void + GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const override; + + virtual void + SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) override; + + virtual void + GetUsername(nsAString& aUsername, ErrorResult& aRv) const override; + + virtual void + SetUsername(const nsAString& aUsername, ErrorResult& aRv) override; + + virtual void + GetPassword(nsAString& aPassword, ErrorResult& aRv) const override; + + virtual void + SetPassword(const nsAString& aPassword, ErrorResult& aRv) override; + + virtual void + GetHost(nsAString& aHost, ErrorResult& aRv) const override; + + virtual void + SetHost(const nsAString& aHost, ErrorResult& aRv) override; + + virtual void + GetHostname(nsAString& aHostname, ErrorResult& aRv) const override; + + virtual void + SetHostname(const nsAString& aHostname, ErrorResult& aRv) override; + + virtual void + GetPort(nsAString& aPort, ErrorResult& aRv) const override; + + virtual void + SetPort(const nsAString& aPort, ErrorResult& aRv) override; + + virtual void + GetPathname(nsAString& aPathname, ErrorResult& aRv) const override; + + virtual void + SetPathname(const nsAString& aPathname, ErrorResult& aRv) override; + + virtual void + GetSearch(nsAString& aSearch, ErrorResult& aRv) const override; + + virtual void + GetHash(nsAString& aHost, ErrorResult& aRv) const override; + + virtual void + SetHash(const nsAString& aHash, ErrorResult& aRv) override; + + virtual void UpdateURLSearchParams() override; + + virtual void + SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) override; + + URLProxy* + GetURLProxy() const + { + mWorkerPrivate->AssertIsOnWorkerThread(); + return mURLProxy; + } + +private: + ~URLWorker(); + + workers::WorkerPrivate* mWorkerPrivate; + RefPtr mURLProxy; +}; + +// This class creates an URL from a DOM Blob on the main thread. +class CreateURLRunnable : public WorkerMainThreadRunnable +{ +private: + BlobImpl* mBlobImpl; + nsAString& mURL; + +public: + CreateURLRunnable(WorkerPrivate* aWorkerPrivate, BlobImpl* aBlobImpl, + const objectURLOptions& aOptions, + nsAString& aURL) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("URL :: CreateURL")) + , mBlobImpl(aBlobImpl) + , mURL(aURL) + { + MOZ_ASSERT(aBlobImpl); + + DebugOnly isMutable; + MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + } + + bool + MainThreadRun() + { + using namespace mozilla::ipc; + + AssertIsOnMainThread(); + + RefPtr newBlobImplHolder; + + if (nsCOMPtr remoteBlob = do_QueryInterface(mBlobImpl)) { + if (BlobChild* blobChild = remoteBlob->GetBlobChild()) { + if (PBackgroundChild* blobManager = blobChild->GetBackgroundManager()) { + PBackgroundChild* backgroundManager = + BackgroundChild::GetForCurrentThread(); + MOZ_ASSERT(backgroundManager); + + if (blobManager != backgroundManager) { + // Always make sure we have a blob from an actor we can use on this + // thread. + blobChild = BlobChild::GetOrCreate(backgroundManager, mBlobImpl); + MOZ_ASSERT(blobChild); + + newBlobImplHolder = blobChild->GetBlobImpl(); + MOZ_ASSERT(newBlobImplHolder); + + mBlobImpl = newBlobImplHolder; + } + } + } + } + + DebugOnly isMutable; + MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + nsCOMPtr principal = mWorkerPrivate->GetPrincipal(); + + nsAutoCString url; + nsresult rv = nsHostObjectProtocolHandler::AddDataEntry( + NS_LITERAL_CSTRING(BLOBURI_SCHEME), + mBlobImpl, principal, url); + + if (NS_FAILED(rv)) { + NS_WARNING("Failed to add data entry for the blob!"); + SetDOMStringToNull(mURL); + return false; + } + + if (!mWorkerPrivate->IsSharedWorker() && + !mWorkerPrivate->IsServiceWorker()) { + // Walk up to top worker object. + WorkerPrivate* wp = mWorkerPrivate; + while (WorkerPrivate* parent = wp->GetParent()) { + wp = parent; + } + + nsCOMPtr sc = wp->GetScriptContext(); + // We could not have a ScriptContext in JSM code. In this case, we leak. + if (sc) { + nsCOMPtr global = sc->GetGlobalObject(); + MOZ_ASSERT(global); + + global->RegisterHostObjectURI(url); + } + } + + mURL = NS_ConvertUTF8toUTF16(url); + return true; + } +}; + +// This class revokes an URL on the main thread. +class RevokeURLRunnable : public WorkerMainThreadRunnable +{ +private: + const nsString mURL; + +public: + RevokeURLRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aURL) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("URL :: RevokeURL")) + , mURL(aURL) + {} + + bool + MainThreadRun() + { + AssertIsOnMainThread(); + + NS_ConvertUTF16toUTF8 url(mURL); + + nsIPrincipal* urlPrincipal = + nsHostObjectProtocolHandler::GetDataEntryPrincipal(url); + + nsCOMPtr principal = mWorkerPrivate->GetPrincipal(); + + bool subsumes; + if (urlPrincipal && + NS_SUCCEEDED(principal->Subsumes(urlPrincipal, &subsumes)) && + subsumes) { + nsHostObjectProtocolHandler::RemoveDataEntry(url); + } + + if (!mWorkerPrivate->IsSharedWorker() && + !mWorkerPrivate->IsServiceWorker()) { + // Walk up to top worker object. + WorkerPrivate* wp = mWorkerPrivate; + while (WorkerPrivate* parent = wp->GetParent()) { + wp = parent; + } + + nsCOMPtr sc = wp->GetScriptContext(); + // We could not have a ScriptContext in JSM code. In this case, we leak. + if (sc) { + nsCOMPtr global = sc->GetGlobalObject(); + MOZ_ASSERT(global); + + global->UnregisterHostObjectURI(url); + } + } + + return true; + } +}; + +// This class creates a URL object on the main thread. +class ConstructorRunnable : public WorkerMainThreadRunnable +{ +private: + const nsString mURL; + + nsString mBase; // IsVoid() if we have no base URI string. + RefPtr mBaseProxy; + ErrorResult& mRv; + + RefPtr mRetval; + +public: + ConstructorRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aURL, const Optional& aBase, + ErrorResult& aRv) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("URL :: Constructor")) + , mURL(aURL) + , mRv(aRv) + { + if (aBase.WasPassed()) { + mBase = aBase.Value(); + } else { + mBase.SetIsVoid(true); + } + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + ConstructorRunnable(WorkerPrivate* aWorkerPrivate, + const nsAString& aURL, URLProxy* aBaseProxy, + ErrorResult& aRv) + : WorkerMainThreadRunnable(aWorkerPrivate, + NS_LITERAL_CSTRING("URL :: Constructor with BaseURL")) + , mURL(aURL) + , mBaseProxy(aBaseProxy) + , mRv(aRv) + { + mBase.SetIsVoid(true); + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + MainThreadRun() + { + AssertIsOnMainThread(); + + RefPtr url; + if (mBaseProxy) { + url = URLMainThread::Constructor(nullptr, mURL, mBaseProxy->URI(), mRv); + } else if (!mBase.IsVoid()) { + url = URLMainThread::Constructor(nullptr, mURL, mBase, mRv); + } else { + url = URLMainThread::Constructor(nullptr, mURL, nullptr, mRv); + } + + if (mRv.Failed()) { + return true; + } + + mRetval = new URLProxy(url.forget()); + return true; + } + + URLProxy* + GetURLProxy() + { + return mRetval; + } +}; + +class TeardownURLRunnable : public Runnable +{ +public: + explicit TeardownURLRunnable(URLProxy* aURLProxy) + : mURLProxy(aURLProxy) + { + } + + NS_IMETHOD Run() + { + AssertIsOnMainThread(); + + mURLProxy->ReleaseURI(); + mURLProxy = nullptr; + + return NS_OK; + } + +private: + RefPtr mURLProxy; +}; + +// This class is the generic getter for any URL property. +class GetterRunnable : public WorkerMainThreadRunnable +{ +public: + enum GetterType { + GetterHref, + GetterOrigin, + GetterProtocol, + GetterUsername, + GetterPassword, + GetterHost, + GetterHostname, + GetterPort, + GetterPathname, + GetterSearch, + GetterHash, + }; + + GetterRunnable(WorkerPrivate* aWorkerPrivate, + GetterType aType, nsAString& aValue, + URLProxy* aURLProxy) + : WorkerMainThreadRunnable(aWorkerPrivate, + // We can have telemetry keys for each getter when + // needed. + NS_LITERAL_CSTRING("URL :: getter")) + , mValue(aValue) + , mType(aType) + , mURLProxy(aURLProxy) + { + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + MainThreadRun() + { + AssertIsOnMainThread(); + ErrorResult rv; + + switch (mType) { + case GetterHref: + mURLProxy->URL()->GetHref(mValue, rv); + break; + + case GetterOrigin: + mURLProxy->URL()->GetOrigin(mValue, rv); + break; + + case GetterProtocol: + mURLProxy->URL()->GetProtocol(mValue, rv); + break; + + case GetterUsername: + mURLProxy->URL()->GetUsername(mValue, rv); + break; + + case GetterPassword: + mURLProxy->URL()->GetPassword(mValue, rv); + break; + + case GetterHost: + mURLProxy->URL()->GetHost(mValue, rv); + break; + + case GetterHostname: + mURLProxy->URL()->GetHostname(mValue, rv); + break; + + case GetterPort: + mURLProxy->URL()->GetPort(mValue, rv); + break; + + case GetterPathname: + mURLProxy->URL()->GetPathname(mValue, rv); + break; + + case GetterSearch: + mURLProxy->URL()->GetSearch(mValue, rv); + break; + + case GetterHash: + mURLProxy->URL()->GetHash(mValue, rv); + break; + } + + MOZ_ASSERT(!rv.Failed(), "Main-thread getters do not fail."); + return true; + } + +private: + nsAString& mValue; + GetterType mType; + RefPtr mURLProxy; +}; + +// This class is the generic setter for any URL property. +class SetterRunnable : public WorkerMainThreadRunnable +{ +public: + enum SetterType { + SetterHref, + SetterProtocol, + SetterUsername, + SetterPassword, + SetterHost, + SetterHostname, + SetterPort, + SetterPathname, + SetterSearch, + SetterHash, + }; + + SetterRunnable(WorkerPrivate* aWorkerPrivate, + SetterType aType, const nsAString& aValue, + URLProxy* aURLProxy) + : WorkerMainThreadRunnable(aWorkerPrivate, + // We can have telemetry keys for each setter when + // needed. + NS_LITERAL_CSTRING("URL :: setter")) + , mValue(aValue) + , mType(aType) + , mURLProxy(aURLProxy) + , mFailed(false) + { + mWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool + MainThreadRun() + { + AssertIsOnMainThread(); + ErrorResult rv; + + switch (mType) { + case SetterHref: { + mURLProxy->URL()->SetHref(mValue, rv); + break; + } + + case SetterProtocol: + mURLProxy->URL()->SetProtocol(mValue, rv); + break; + + case SetterUsername: + mURLProxy->URL()->SetUsername(mValue, rv); + break; + + case SetterPassword: + mURLProxy->URL()->SetPassword(mValue, rv); + break; + + case SetterHost: + mURLProxy->URL()->SetHost(mValue, rv); + break; + + case SetterHostname: + mURLProxy->URL()->SetHostname(mValue, rv); + break; + + case SetterPort: + mURLProxy->URL()->SetPort(mValue, rv); + break; + + case SetterPathname: + mURLProxy->URL()->SetPathname(mValue, rv); + break; + + case SetterSearch: + mURLProxy->URL()->SetSearch(mValue, rv); + break; + + case SetterHash: + mURLProxy->URL()->SetHash(mValue, rv); + break; + } + + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + mFailed = true; + } + + return true; + } + + bool Failed() const + { + return mFailed; + } + +private: + const nsString mValue; + SetterType mType; + RefPtr mURLProxy; + bool mFailed; +}; + +already_AddRefed +FinishConstructor(JSContext* aCx, WorkerPrivate* aPrivate, + ConstructorRunnable* aRunnable, ErrorResult& aRv) +{ + aRunnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr proxy = aRunnable->GetURLProxy(); + if (NS_WARN_IF(!proxy)) { + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); + return nullptr; + } + + RefPtr url = new URLWorker(aPrivate, proxy); + return url.forget(); +} + +/* static */ already_AddRefed +URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv) +{ + MOZ_ASSERT(!NS_IsMainThread()); + + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + + URLWorker& base = static_cast(aBase); + RefPtr runnable = + new ConstructorRunnable(workerPrivate, aURL, base.GetURLProxy(), aRv); + + return FinishConstructor(cx, workerPrivate, runnable, aRv); +} + +/* static */ already_AddRefed +URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv) +{ + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + + RefPtr runnable = + new ConstructorRunnable(workerPrivate, aURL, aBase, aRv); + + return FinishConstructor(cx, workerPrivate, runnable, aRv); +} + +/* static */ already_AddRefed +URLWorker::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv) +{ + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + + Optional base; + base = &aBase; + + RefPtr runnable = + new ConstructorRunnable(workerPrivate, aURL, base, aRv); + + return FinishConstructor(cx, workerPrivate, runnable, aRv); +} + +/* static */ void +URLWorker::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, + const mozilla::dom::objectURLOptions& aOptions, + nsAString& aResult, mozilla::ErrorResult& aRv) +{ + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + + RefPtr blobImpl = aBlob.Impl(); + MOZ_ASSERT(blobImpl); + + aRv = blobImpl->SetMutable(false); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + RefPtr runnable = + new CreateURLRunnable(workerPrivate, blobImpl, aOptions, aResult); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) { + WorkerGlobalScope* scope = workerPrivate->GlobalScope(); + MOZ_ASSERT(scope); + + scope->RegisterHostObjectURI(NS_ConvertUTF16toUTF8(aResult)); + } +} + +/* static */ void +URLWorker::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl, + ErrorResult& aRv) +{ + JSContext* cx = aGlobal.Context(); + WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); + + RefPtr runnable = + new RevokeURLRunnable(workerPrivate, aUrl); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (workerPrivate->IsSharedWorker() || workerPrivate->IsServiceWorker()) { + WorkerGlobalScope* scope = workerPrivate->GlobalScope(); + MOZ_ASSERT(scope); + + scope->UnregisterHostObjectURI(NS_ConvertUTF16toUTF8(aUrl)); + } +} + +URLWorker::URLWorker(WorkerPrivate* aWorkerPrivate, URLProxy* aURLProxy) + : URL(nullptr) + , mWorkerPrivate(aWorkerPrivate) + , mURLProxy(aURLProxy) +{} + +URLWorker::~URLWorker() +{ + if (mURLProxy) { + mWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr runnable = + new TeardownURLRunnable(mURLProxy); + mURLProxy = nullptr; + + if (NS_FAILED(NS_DispatchToMainThread(runnable))) { + NS_ERROR("Failed to dispatch teardown runnable!"); + } + } +} + +void +URLWorker::GetHref(nsAString& aHref, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHref, aHref, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetHref(const nsAString& aHref, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHref, aHref, + mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + if (runnable->Failed()) { + aRv.ThrowTypeError(aHref); + return; + } + + UpdateURLSearchParams(); +} + +void +URLWorker::GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterProtocol, aProtocol, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterProtocol, + aProtocol, mURLProxy); + + runnable->Dispatch(aRv); + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetUsername(nsAString& aUsername, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterUsername, aUsername, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetUsername(const nsAString& aUsername, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterUsername, + aUsername, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetPassword(nsAString& aPassword, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPassword, aPassword, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetPassword(const nsAString& aPassword, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPassword, + aPassword, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetHost(nsAString& aHost, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHost, aHost, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetHost(const nsAString& aHost, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHost, + aHost, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetHostname(nsAString& aHostname, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHostname, aHostname, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetHostname(const nsAString& aHostname, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHostname, + aHostname, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetPort(nsAString& aPort, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPort, aPort, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetPort(const nsAString& aPort, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPort, + aPort, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetPathname(nsAString& aPathname, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPathname, + aPathname, mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetPathname(const nsAString& aPathname, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPathname, + aPathname, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::GetSearch(nsAString& aSearch, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterSearch, aSearch, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::GetHash(nsAString& aHash, ErrorResult& aRv) const +{ + RefPtr runnable = + new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash, + mURLProxy); + + runnable->Dispatch(aRv); +} + +void +URLWorker::SetHash(const nsAString& aHash, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHash, + aHash, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) +{ + RefPtr runnable = + new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterSearch, + aSearch, mURLProxy); + + runnable->Dispatch(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + MOZ_ASSERT(!runnable->Failed()); +} + +void +URLWorker::UpdateURLSearchParams() +{ + if (mSearchParams) { + nsAutoString search; + + ErrorResult rv; + GetSearch(search, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + + mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1))); + } +} + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// Base class for URL +/////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URL, mParent, mSearchParams) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(URL) +NS_IMPL_CYCLE_COLLECTING_RELEASE(URL) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URL) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +URL::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return URLBinding::Wrap(aCx, this, aGivenProto); +} + +/* static */ already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + return URLMainThread::Constructor(aGlobal, aURL, aBase, aRv); + } + + return URLWorker::Constructor(aGlobal, aURL, aBase, aRv); +} + +/* static */ already_AddRefed +URL::Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + return URLMainThread::Constructor(aGlobal, aURL, aBase, aRv); + } + + return URLWorker::Constructor(aGlobal, aURL, aBase, aRv); +} + +/* static */ already_AddRefed +URL::WorkerConstructor(const GlobalObject& aGlobal, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv) +{ + return URLWorker::Constructor(aGlobal, aURL, aBase, aRv); +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + URLMainThread::CreateObjectURL(aGlobal, aBlob, aOptions, aResult, aRv); + } else { + URLWorker::CreateObjectURL(aGlobal, aBlob, aOptions, aResult, aRv); + } +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + URLMainThread::CreateObjectURL(aGlobal, aStream, aOptions, aResult, aRv); +} + +void +URL::CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource, + const objectURLOptions& aOptions, + nsAString& aResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + URLMainThread::CreateObjectURL(aGlobal, aSource, aOptions, aResult, aRv); +} + +void +URL::RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL, + ErrorResult& aRv) +{ + if (NS_IsMainThread()) { + URLMainThread::RevokeObjectURL(aGlobal, aURL, aRv); + } else { + URLWorker::RevokeObjectURL(aGlobal, aURL, aRv); + } +} + +URLSearchParams* +URL::SearchParams() +{ + CreateSearchParamsIfNeeded(); + return mSearchParams; +} + +bool IsChromeURI(nsIURI* aURI) +{ + bool isChrome = false; + if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome))) + return isChrome; + return false; +} + +void +URL::CreateSearchParamsIfNeeded() +{ + if (!mSearchParams) { + mSearchParams = new URLSearchParams(mParent, this); + UpdateURLSearchParams(); + } +} + +void +URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv) +{ + SetSearchInternal(aSearch, aRv); + UpdateURLSearchParams(); +} + +void +URL::URLSearchParamsUpdated(URLSearchParams* aSearchParams) +{ + MOZ_ASSERT(mSearchParams); + MOZ_ASSERT(mSearchParams == aSearchParams); + + nsAutoString search; + mSearchParams->Serialize(search); + + ErrorResult rv; + SetSearchInternal(search, rv); + NS_WARN_IF(rv.Failed()); + rv.SuppressException(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/url/URL.h b/dom/url/URL.h new file mode 100644 index 000000000000..3f38e8066dc4 --- /dev/null +++ b/dom/url/URL.h @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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/. */ + +#ifndef mozilla_dom_URL_h +#define mozilla_dom_URL_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/URLSearchParams.h" +#include "nsCycleCollectionParticipant.h" +#include "nsString.h" +#include "nsWrapperCache.h" + +class nsISupports; +class nsIURI; + +namespace mozilla { + +class ErrorResult; +class DOMMediaStream; + +namespace dom { + +class Blob; +class MediaSource; +class GlobalObject; +struct objectURLOptions; + +class URL : public URLSearchParamsObserver + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URL) + + URL(nsISupports* aParent) + : mParent(aParent) + {} + + // WebIDL methods + nsISupports* GetParentObject() const + { + return mParent; + } + + virtual JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + URL& aBase, ErrorResult& aRv); + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, const nsAString& aURL, + const Optional& aBase, ErrorResult& aRv); + + // Helper for Fetch API + static already_AddRefed + WorkerConstructor(const GlobalObject& aGlobal, const nsAString& aURL, + const nsAString& aBase, ErrorResult& aRv); + + + static void + CreateObjectURL(const GlobalObject& aGlobal, Blob& aBlob, + const objectURLOptions& aOptions, + nsAString& aResult, ErrorResult& aRv); + + static void + CreateObjectURL(const GlobalObject& aGlobal, DOMMediaStream& aStream, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv); + + static void + CreateObjectURL(const GlobalObject& aGlobal, MediaSource& aSource, + const objectURLOptions& aOptions, nsAString& aResult, + ErrorResult& aRv); + + static void + RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aURL, + ErrorResult& aRv); + + virtual void + GetHref(nsAString& aHref, ErrorResult& aRv) const = 0; + + virtual void + SetHref(const nsAString& aHref, ErrorResult& aRv) = 0; + + virtual void + GetOrigin(nsAString& aOrigin, ErrorResult& aRv) const = 0; + + virtual void + GetProtocol(nsAString& aProtocol, ErrorResult& aRv) const = 0; + + virtual void + SetProtocol(const nsAString& aProtocol, ErrorResult& aRv) = 0; + + virtual void + GetUsername(nsAString& aUsername, ErrorResult& aRv) const = 0; + + virtual void + SetUsername(const nsAString& aUsername, ErrorResult& aRv) = 0; + + virtual void + GetPassword(nsAString& aPassword, ErrorResult& aRv) const = 0; + + virtual void + SetPassword(const nsAString& aPassword, ErrorResult& aRv) = 0; + + virtual void + GetHost(nsAString& aHost, ErrorResult& aRv) const = 0; + + virtual void + SetHost(const nsAString& aHost, ErrorResult& aRv) = 0; + + virtual void + GetHostname(nsAString& aHostname, ErrorResult& aRv) const = 0; + + virtual void + SetHostname(const nsAString& aHostname, ErrorResult& aRv) = 0; + + virtual void + GetPort(nsAString& aPort, ErrorResult& aRv) const = 0; + + virtual void + SetPort(const nsAString& aPort, ErrorResult& aRv) = 0; + + virtual void + GetPathname(nsAString& aPathname, ErrorResult& aRv) const = 0; + + virtual void + SetPathname(const nsAString& aPathname, ErrorResult& aRv) = 0; + + virtual void + GetSearch(nsAString& aSearch, ErrorResult& aRv) const = 0; + + virtual void + SetSearch(const nsAString& aSearch, ErrorResult& aRv); + + URLSearchParams* SearchParams(); + + virtual void + GetHash(nsAString& aHost, ErrorResult& aRv) const = 0; + + virtual void + SetHash(const nsAString& aHash, ErrorResult& aRv) = 0; + + void Stringify(nsAString& aRetval, ErrorResult& aRv) const + { + GetHref(aRetval, aRv); + } + + // URLSearchParamsObserver + void + URLSearchParamsUpdated(URLSearchParams* aSearchParams) override; + +protected: + virtual ~URL() + {} + + virtual void + UpdateURLSearchParams() = 0; + + virtual void + SetSearchInternal(const nsAString& aSearch, ErrorResult& aRv) = 0; + + void CreateSearchParamsIfNeeded(); + + nsCOMPtr mParent; + RefPtr mSearchParams; +}; + +bool IsChromeURI(nsIURI* aURI); + +} // namespace dom +} // namespace mozilla + +#endif /* mozilla_dom_URL_h */ diff --git a/dom/base/URLSearchParams.cpp b/dom/url/URLSearchParams.cpp similarity index 100% rename from dom/base/URLSearchParams.cpp rename to dom/url/URLSearchParams.cpp diff --git a/dom/base/URLSearchParams.h b/dom/url/URLSearchParams.h similarity index 100% rename from dom/base/URLSearchParams.h rename to dom/url/URLSearchParams.h diff --git a/dom/url/moz.build b/dom/url/moz.build new file mode 100644 index 000000000000..915227079bb5 --- /dev/null +++ b/dom/url/moz.build @@ -0,0 +1,26 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom += [ + 'URL.h', + 'URLSearchParams.h', +] + +UNIFIED_SOURCES += [ + 'URL.cpp', + 'URLSearchParams.cpp', +] + +LOCAL_INCLUDES += [ + '../workers', +] + +MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] +MOCHITEST_CHROME_MANIFESTS += [ 'tests/chrome.ini' ] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul' diff --git a/dom/url/tests/chrome.ini b/dom/url/tests/chrome.ini new file mode 100644 index 000000000000..1e9cfc43a09c --- /dev/null +++ b/dom/url/tests/chrome.ini @@ -0,0 +1,12 @@ +[DEFAULT] +skip-if = buildapp == 'b2g' || os == 'android' +support-files = + file_url.jsm + file_worker_url.jsm + test_bug883784.jsm + jsm_url_worker.js + !/dom/workers/test/dom_worker_helper.js + +[test_url.xul] +[test_worker_url.xul] +[test_bug883784.xul] diff --git a/dom/base/test/file_url.jsm b/dom/url/tests/file_url.jsm similarity index 100% rename from dom/base/test/file_url.jsm rename to dom/url/tests/file_url.jsm diff --git a/dom/workers/test/file_url.jsm b/dom/url/tests/file_worker_url.jsm similarity index 93% rename from dom/workers/test/file_url.jsm rename to dom/url/tests/file_worker_url.jsm index c6e33acdc983..d0bbf62fed24 100644 --- a/dom/workers/test/file_url.jsm +++ b/dom/url/tests/file_worker_url.jsm @@ -15,11 +15,10 @@ this.checkFromJSM = function checkFromJSM(ok, is, finish) { } } - var self = this; worker.onerror = function(event) { is(event.target, worker); ok(false, "Worker had an error: " + event.data); - self.worker.terminate(); + worker.terminate(); finish(); }; diff --git a/dom/workers/test/jsm_url_worker.js b/dom/url/tests/jsm_url_worker.js similarity index 100% rename from dom/workers/test/jsm_url_worker.js rename to dom/url/tests/jsm_url_worker.js diff --git a/dom/url/tests/mochitest.ini b/dom/url/tests/mochitest.ini new file mode 100644 index 000000000000..370ef2c2ba9d --- /dev/null +++ b/dom/url/tests/mochitest.ini @@ -0,0 +1,20 @@ +[DEFAULT] +support-files = + url_worker.js + urlApi_worker.js + urlSearchParams_worker.js + url_exceptions_worker.js + +[test_url.html] +[test_url_data.html] +[test_url_empty_port.html] +[test_url_malformedHost.html] +[test_urlExceptions.html] +[test_urlSearchParams.html] +[test_urlSearchParams_utf8.html] +[test_urlutils_stringify.html] +[test_worker_url.html] +[test_worker_urlApi.html] +[test_worker_url_exceptions.html] +[test_worker_urlSearchParams.html] +[test_unknown_url_origin.html] diff --git a/dom/workers/test/test_bug883784.jsm b/dom/url/tests/test_bug883784.jsm similarity index 100% rename from dom/workers/test/test_bug883784.jsm rename to dom/url/tests/test_bug883784.jsm diff --git a/dom/workers/test/test_bug883784.xul b/dom/url/tests/test_bug883784.xul similarity index 85% rename from dom/workers/test/test_bug883784.xul rename to dom/url/tests/test_bug883784.xul index 89217bca4093..4fdcf5eb10bf 100644 --- a/dom/workers/test/test_bug883784.xul +++ b/dom/url/tests/test_bug883784.xul @@ -11,7 +11,7 @@ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> diff --git a/dom/workers/test/test_url.html b/dom/url/tests/test_worker_url.html similarity index 100% rename from dom/workers/test/test_url.html rename to dom/url/tests/test_worker_url.html diff --git a/dom/workers/test/test_url.xul b/dom/url/tests/test_worker_url.xul similarity index 83% rename from dom/workers/test/test_url.xul rename to dom/url/tests/test_worker_url.xul index fe8d49dc9e66..2f4d79b2c96e 100644 --- a/dom/workers/test/test_url.xul +++ b/dom/url/tests/test_worker_url.xul @@ -11,17 +11,16 @@ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index c44d18375380..da35d4c82242 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -2888,7 +2888,6 @@ SearchService.prototype = { if (this._batchTask) this._batchTask.disarm(); - TelemetryStopwatch.start("SEARCH_SERVICE_BUILD_CACHE_MS"); let cache = {}; let locale = getLocale(); let buildID = Services.appinfo.platformBuildID; @@ -2932,7 +2931,6 @@ SearchService.prototype = { } catch (ex) { LOG("_buildCache: Could not write to cache file: " + ex); } - TelemetryStopwatch.finish("SEARCH_SERVICE_BUILD_CACHE_MS"); }, _syncLoadEngines: function SRCH_SVC__syncLoadEngines(cache) { diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index aa989d6c3925..9a87c77da53b 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -775,60 +775,6 @@ "description": "Number of low-commit-space events fired since last ping", "cpp_guard": "XP_WIN" }, - "EARLY_GLUESTARTUP_READ_OPS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "linear", - "high": 100, - "n_buckets": 12, - "description": "ProcessIoCounters.ReadOperationCount before glue startup *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_WIN" - }, - "EARLY_GLUESTARTUP_READ_TRANSFER": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "exponential", - "high": 51200, - "n_buckets": 12, - "description": "ProcessIoCounters.ReadTransferCount before glue startup (KB) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_WIN" - }, - "GLUESTARTUP_READ_OPS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "linear", - "high": 100, - "n_buckets": 12, - "description": "ProcessIoCounters.ReadOperationCount after glue startup *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_WIN" - }, - "GLUESTARTUP_READ_TRANSFER": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "exponential", - "high": 51200, - "n_buckets": 12, - "description": "ProcessIoCounters.ReadTransferCount after glue startup (KB) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_WIN" - }, - "EARLY_GLUESTARTUP_HARD_FAULTS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "linear", - "high": 100, - "n_buckets": 12, - "description": "Hard faults count before glue startup *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_UNIX" - }, - "GLUESTARTUP_HARD_FAULTS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "exponential", - "high": 500, - "n_buckets": 12, - "description": "Hard faults count after glue startup *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***", - "cpp_guard": "XP_UNIX" - }, "PAGE_FAULTS_HARD": { "expires_in_version": "default", "kind": "exponential", @@ -1726,32 +1672,6 @@ "kind": "boolean", "description": "Fraction of sockets that used a nsConnectionEntry with history - size 300." }, - "DISK_CACHE_CORRUPT_DETAILS": { - "expires_in_version": "40", - "kind": "enumerated", - "n_values": 50, - "description": "Why the HTTP disk cache was corrupted at startup" - }, - "DISK_CACHE_REDUCTION_TRIAL": { - "expires_in_version": "40", - "kind": "boolean", - "description": "Stores 1 if the cache would be clean with the disk cache corruption plan of Bug 105843" - }, - "DISK_CACHE_REVALIDATION_SAFE": { - "expires_in_version": "40", - "kind": "boolean", - "description": "Stores 1 if the cache clean file was revalidated, or 0 if a non empty doom list prevented revalidation" - }, - "DISK_CACHE_INVALIDATION_SUCCESS": { - "expires_in_version": "40", - "kind": "boolean", - "description": "Stores 1 if writing '0' to the cache clean file succeeded, and 0 if it failed." - }, - "DISK_CACHE_REVALIDATION_SUCCESS": { - "expires_in_version": "40", - "kind": "boolean", - "description": "Stores 1 if writing '1' to the cache clean file succeeded, and 0 if it failed." - }, "HTTP_CACHE_DISPOSITION_2": { "expires_in_version": "never", "kind": "enumerated", @@ -1814,6 +1734,54 @@ "kind": "boolean", "description": "Rate of page load from offline cache" }, + "HTTP_CACHE_IO_QUEUE_OPEN_PRIORITY": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_READ_PRIORITY": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_OPEN": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_READ": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_MANAGEMENT": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_WRITE": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_INDEX": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, + "HTTP_CACHE_IO_QUEUE_EVICT": { + "expires_in_version": "54", + "kind": "enumerated", + "n_values": 10, + "description": "HTTP Cache IO queue length" + }, "CACHE_DEVICE_SEARCH_2": { "expires_in_version": "never", "kind": "exponential", @@ -1900,11 +1868,6 @@ "n_buckets": 50, "description": "Time spent waiting on the cache service lock on the main thread (ms)" }, - "DISK_CACHE_SMART_SIZE_USING_OLD_MAX": { - "expires_in_version": "40", - "kind": "boolean", - "description": "Whether we are using the old default cache smart size" - }, "CACHE_SERVICE_LOCK_WAIT_MAINTHREAD_NSSETDISKSMARTSIZECALLBACK_NOTIFY": { "expires_in_version": "never", "kind": "exponential", @@ -3348,6 +3311,8 @@ "description": "Time spent (ms) during showdown deleting disk cache v2 for 'clear private data' option" }, "NETWORK_DISK_CACHE_REVALIDATION": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "exponential", "high": 10000, @@ -3355,6 +3320,8 @@ "description": "Total Time spent (ms) during disk cache revalidation" }, "NETWORK_DISK_CACHE_STREAMIO_CLOSE": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "exponential", "high": 10000, @@ -3362,6 +3329,8 @@ "description": "Time spent in nsDiskCacheStreamIO::Close() on non-main thread (ms)" }, "NETWORK_DISK_CACHE_STREAMIO_CLOSE_MAIN_THREAD": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "exponential", "high": 10000, @@ -3377,6 +3346,8 @@ "description": "Network identification (0=None, 1=New, 2=Same)" }, "IDLE_NOTIFY_BACK_MS": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "exponential", "high": 5000, @@ -3384,6 +3355,8 @@ "description": "Time spent checking for and notifying listeners that the user is back (ms)" }, "IDLE_NOTIFY_BACK_LISTENERS": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "linear", "high": 100, @@ -3391,6 +3364,8 @@ "description": "Number of listeners notified that the user is back" }, "IDLE_NOTIFY_IDLE_MS": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "default", "kind": "exponential", "high": 5000, @@ -3398,6 +3373,8 @@ "description": "Time spent checking for and notifying listeners that the user is idle (ms)" }, "IDLE_NOTIFY_IDLE_LISTENERS": { + "alert_emails": ["froydnj@mozilla.com"], + "bug_numbers": [731004], "expires_in_version": "40", "kind": "linear", "high": 100, @@ -4207,13 +4184,6 @@ "n_buckets": 20, "description": "Gloda: indexing rate (message/s)" }, - "FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE": { - "expires_in_version": "40", - "kind": "exponential", - "high": 1000, - "n_buckets": 30, - "description": "Firefox: Time taken to capture the page to a canvas, for reuse while swiping through history (ms)." - }, "FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE": { "expires_in_version": "50", "kind": "exponential", @@ -4228,22 +4198,6 @@ "n_buckets": 30, "description": "Firefox: Time taken to kick off image compression of the canvas that will be used during swiping through history (ms)." }, - "FX_TAB_ANIM_OPEN_MS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "Firefox: Time taken by the tab opening animation in milliseconds *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "FX_TAB_ANIM_CLOSE_MS": { - "alert_emails": ["perf-telemetry-alerts@mozilla.com"], - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "Firefox: Time taken by the tab closing animation in milliseconds *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "FX_TAB_ANIM_OPEN_PREVIEW_FRAME_INTERVAL_MS": { "expires_in_version": "never", "kind": "exponential", @@ -4268,13 +4222,6 @@ "n_buckets": 50, "description": "Average frame interval during any tab open/close animation (excluding tabstrip scroll)" }, - "FX_TAB_ANIM_ANY_FRAME_PAINT_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 500, - "n_buckets": 30, - "description": "Average paint duration during any tab open/close animation (excluding tabstrip scroll) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "FX_REFRESH_DRIVER_CHROME_FRAME_DELAY_MS": { "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", @@ -4338,13 +4285,6 @@ "n_buckets": 20, "description": "Firefox: Time in ms spent on switching tabs in response to a tab click" }, - "FX_IDENTITY_POPUP_OPEN_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 1000, - "n_buckets": 10, - "description": "Firefox: Time taken by the identity popup to open in milliseconds" - }, "FX_BOOKMARKS_TOOLBAR_INIT_MS": { "expires_in_version": "never", "kind": "exponential", @@ -4451,14 +4391,34 @@ "releaseChannelCollection": "opt-out", "description": "The browser that was the default on the initial profile migration. The values correspond to the internal browser ID (see MigrationUtils.jsm)" }, - "FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_SUCCEEDED": { + "FX_STARTUP_MIGRATION_AUTOMATED_IMPORT_PROCESS_SUCCESS": { "bug_numbers": [1271775], "alert_emails": ["gijs@mozilla.com"], "expires_in_version": "53", - "kind": "count", - "keyed": true, + "kind": "enumerated", + "n_values": 27, "releaseChannelCollection": "opt-out", - "description": "Where automatic migration was attempted, indicates to what degree we succeeded." + "description": "Where automatic migration was attempted, indicates to what degree we succeeded. Values 0-25 indicate progress through the automatic migration sequence, with 25 indicating the migration finished. 26 is only used when the migration produced errors before it finished." + }, + "FX_STARTUP_MIGRATION_DATA_RECENCY": { + "bug_numbers": [1276694], + "alert_emails": ["gijs@mozilla.com"], + "expires_in_version": "53", + "keyed": true, + "kind": "exponential", + "n_buckets": 50, + "high": 8760, + "releaseChannelCollection": "opt-out", + "description": "The 'last modified' time of the data we imported on the initial profile migration (time delta with 'now' at the time of migration, in hours). Collected for all browsers for which migration data is available, and stored keyed by browser identifier (e.g. 'ie', 'edge', 'safari', etc.)." + }, + "FX_STARTUP_MIGRATION_USED_RECENT_BROWSER": { + "bug_numbers": [1276694], + "alert_emails": ["gijs@mozilla.com"], + "expires_in_version": "53", + "keyed": true, + "kind": "boolean", + "releaseChannelCollection": "opt-out", + "description": "Whether the browser we migrated from was the browser with the most recent data. Keyed by that browser's identifier (e.g. 'ie', 'edge', 'safari', etc.)." }, "FX_STARTUP_EXTERNAL_CONTENT_HANDLER": { "bug_numbers": [1276027], @@ -4778,20 +4738,6 @@ "kind": "flag", "description": "Whether the decoder for ISO-8859-5 has been instantiated in this session." }, - "XUL_FOREGROUND_REFLOW_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "XUL reflows in foreground windows (ms) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "XUL_BACKGROUND_REFLOW_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "XUL reflows in background windows (ms) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "HTML_FOREGROUND_REFLOW_MS_2": { "expires_in_version": "never", "kind": "exponential", @@ -4799,61 +4745,16 @@ "n_buckets": 20, "description": "HTML reflows in foreground windows (ms)" }, - "HTML_BACKGROUND_REFLOW_MS_2": { - "expires_in_version": "40", - "kind": "exponential", - "high": 10000, - "n_buckets": 20, - "description": "HTML reflows in background windows (ms)" - }, "LONG_REFLOW_INTERRUPTIBLE": { "expires_in_version": "never", "kind": "boolean", "description": "Long running reflow, interruptible or not" }, - "XUL_INITIAL_FRAME_CONSTRUCTION": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "initial xul frame construction *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "XMLHTTPREQUEST_ASYNC_OR_SYNC": { "expires_in_version": "never", "kind": "boolean", "description": "Type of XMLHttpRequest, async or sync" }, - "DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "DOM: Timer handlers called per native timer expiration *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "DOM_TIMERS_RECENTLY_SET": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "DOM: setTimeout/setInterval calls recently (last 30s or more) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "DOM_RANGE_DETACHED": { - "expires_in_version": "40", - "kind": "boolean", - "description": "DOM: Ranges that are detached on destruction (bug 702948) *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "DOM_WINDOW_SHOWMODALDIALOG_USED": { - "expires_in_version": "41", - "kind": "flag", - "description": "Whether Window.showModalDialog was used in this session" - }, - "LOCALDOMSTORAGE_INIT_DATABASE_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 3000, - "n_buckets": 10, - "description": "Time to open the localStorage database (ms)" - }, "LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS": { "expires_in_version": "default", "kind": "exponential", @@ -4929,38 +4830,6 @@ "n_buckets": 10, "description": "Time to fetch LocalStorage data before we can expose them as session only data (ms)" }, - "LOCALDOMSTORAGE_KEY_SIZE_BYTES": { - "expires_in_version": "40", - "kind": "exponential", - "low": 1024, - "high": 32768, - "n_buckets": 10, - "description": "DOM storage: size of keys stored in localStorage" - }, - "LOCALDOMSTORAGE_VALUE_SIZE_BYTES": { - "expires_in_version": "40", - "kind": "exponential", - "low": 1024, - "high": 32768, - "n_buckets": 10, - "description": "DOM storage: size of values stored in localStorage" - }, - "SESSIONDOMSTORAGE_KEY_SIZE_BYTES": { - "expires_in_version": "40", - "kind": "exponential", - "low": 1024, - "high": 32768, - "n_buckets": 10, - "description": "DOM storage: size of keys stored in sessionStorage" - }, - "SESSIONDOMSTORAGE_VALUE_SIZE_BYTES": { - "expires_in_version": "40", - "kind": "exponential", - "low": 1024, - "high": 32768, - "n_buckets": 10, - "description": "DOM storage: size of values stored in sessionStorage" - }, "RANGE_CHECKSUM_ERRORS": { "alert_emails": ["perf-telemetry-alerts@mozilla.com"], "expires_in_version": "never", @@ -5405,15 +5274,6 @@ "n_values": 10, "description": "Track click count on about:newtab tiles per index (0-8). For non-default row or column configurations all clicks into the '9' bucket. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" }, - "BROWSERPROVIDER_XUL_IMPORT_TIME": { - "expires_in_version": "40", - "kind": "exponential", - "low": 20, - "high": 600000, - "n_buckets": 20, - "description": "Time for the initial conversion of a XUL places database (ms)", - "cpp_guard": "ANDROID" - }, "BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS": { "expires_in_version": "default", "kind": "exponential", @@ -5422,14 +5282,6 @@ "description": "Number of bookmarks in the original XUL places database", "cpp_guard": "ANDROID" }, - "BROWSERPROVIDER_XUL_IMPORT_HISTORY": { - "expires_in_version": "40", - "kind": "exponential", - "high": 1000000, - "n_buckets": 20, - "description": "Number of history entries in the original XUL places database", - "cpp_guard": "ANDROID" - }, "FENNEC_GLOBALHISTORY_ADD_MS": { "expires_in_version": "never", "kind": "exponential", @@ -5623,13 +5475,6 @@ "description": "Recorded once per session near startup: records true/false whether the search service has engines with icon update URLs.", "releaseChannelCollection": "opt-out" }, - "SEARCH_SERVICE_BUILD_CACHE_MS": { - "expires_in_version": "40", - "kind": "exponential", - "high": 1000, - "n_buckets": 15, - "description": "Time (ms) it takes to build the cache of the search service" - }, "SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS": { "alert_emails": ["mhammond@mozilla.com", "gavin@mozilla.com"], "expires_in_version": "never", @@ -7047,38 +6892,6 @@ "n_buckets": 1000, "description": "The time (in milliseconds) that it took a 'stopTrace' request to go round trip." }, - "COOKIES_3RDPARTY_NUM_SITES_ACCEPTED": { - "expires_in_version": "40", - "kind": "linear", - "low": 5, - "high": 145, - "n_buckets": 30, - "description": "The number of distinct pairs (first-party site, third-party site attempting to set cookie) for which the third-party cookie has been accepted. Sites are considered identical if they have the same eTLD + 1. Measures are normalized per 24h. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "COOKIES_3RDPARTY_NUM_SITES_BLOCKED": { - "expires_in_version": "40", - "kind": "linear", - "low": 5, - "high": 145, - "n_buckets": 30, - "description": "The number of distinct pairs (first-party site, third-party site attempting to set cookie) for which the third-party cookie has been rejected. Sites are considered identical if they have the same eTLD + 1. Measures are normalized per 24h. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "COOKIES_3RDPARTY_NUM_ATTEMPTS_ACCEPTED": { - "expires_in_version": "40", - "kind": "linear", - "low": 10, - "high": 500, - "n_buckets": 50, - "description": "The total number of distinct attempts by third-party sites to place cookies which have been accepted. Measures are normalized per 24h. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "COOKIES_3RDPARTY_NUM_ATTEMPTS_BLOCKED": { - "expires_in_version": "40", - "kind": "linear", - "low": 10, - "high": 500, - "n_buckets": 50, - "description": "The total number of distinct attempts by third-party sites to place cookies which have been rejected. Measures are normalized per 24h. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "DEVTOOLS_DEBUGGER_RDP_LOCAL_GET_EXECUTABLE_LINES_MS": { "expires_in_version": "never", "kind": "exponential", @@ -8364,24 +8177,6 @@ "description": "Record on which attempt we successfully unlocked a database. See DATABASE_LOCKED_EXCEPTION.", "n_values": 5 }, - "SQLITEBRIDGE_PROVIDER_PASSWORDS_LOCKED": { - "expires_in_version": "40", - "kind": "enumerated", - "n_values": 10, - "description": "The number of errors using the PasswordsProvider due to a locked DB. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "SQLITEBRIDGE_PROVIDER_FORMS_LOCKED": { - "expires_in_version": "40", - "kind": "enumerated", - "n_values": 10, - "description": "The number of errors using the FormHistoryProvider due to a locked DB. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, - "SQLITEBRIDGE_PROVIDER_HOME_LOCKED": { - "expires_in_version": "40", - "kind": "enumerated", - "n_values": 10, - "description": "The number of errors using the HomeProvider due to a locked DB. *** No longer needed (bug 1156565). Delete histogram and accumulation code! ***" - }, "SSL_TLS13_INTOLERANCE_REASON_PRE": { "alert_emails": ["seceng-telemetry@mozilla.com"], "bug_numbers": [1250568], @@ -10898,5 +10693,21 @@ "bug_numbers": [1280229], "description": "Number of the use of XHR in workers.", "releaseChannelCollection": "opt-out" + }, + "BLINK_FILESYSTEM_USED": { + "alert_emails": ["amarchesini@mozilla.com"], + "expires_in_version": "never", + "kind": "boolean", + "bug_numbers": [1272501], + "releaseChannelCollection": "opt-out", + "description": "Webkit/Blink filesystem used" + }, + "WEBKIT_DIRECTORY_USED": { + "alert_emails": ["amarchesini@mozilla.com"], + "expires_in_version": "never", + "kind": "boolean", + "bug_numbers": [1272501], + "releaseChannelCollection": "opt-out", + "description": "HTMLInputElement.webkitdirectory attribute used" } } diff --git a/toolkit/components/telemetry/TelemetryEnvironment.jsm b/toolkit/components/telemetry/TelemetryEnvironment.jsm index 2b1809d1c664..2540b6f9fafa 100644 --- a/toolkit/components/telemetry/TelemetryEnvironment.jsm +++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm @@ -133,6 +133,8 @@ const DEFAULT_ENVIRONMENT_PREFS = new Map([ ["browser.tabs.animate", {what: RECORD_PREF_VALUE}], ["browser.urlbar.suggest.searches", {what: RECORD_PREF_VALUE}], ["browser.urlbar.userMadeSearchSuggestionsChoice", {what: RECORD_PREF_VALUE}], + // Record "Zoom Text Only" pref in Firefox 50 to 52 (Bug 979323). + ["browser.zoom.full", {what: RECORD_PREF_VALUE}], ["devtools.chrome.enabled", {what: RECORD_PREF_VALUE}], ["devtools.debugger.enabled", {what: RECORD_PREF_VALUE}], ["devtools.debugger.remote-enabled", {what: RECORD_PREF_VALUE}], diff --git a/toolkit/components/telemetry/TelemetryStorage.jsm b/toolkit/components/telemetry/TelemetryStorage.jsm index 58d6fae2c8ca..93a62afcac53 100644 --- a/toolkit/components/telemetry/TelemetryStorage.jsm +++ b/toolkit/components/telemetry/TelemetryStorage.jsm @@ -823,6 +823,7 @@ var TelemetryStorageImpl = { const startTimeStamp = nowDate.getTime(); let dirIterator = new OS.File.DirectoryIterator(gPingsArchivePath); let subdirs = (yield dirIterator.nextBatch()).filter(e => e.isDir); + dirIterator.close(); // Keep track of the newest removed month to update the cache, if needed. let newestRemovedMonthTimestamp = null; @@ -1189,12 +1190,14 @@ var TelemetryStorageImpl = { let dirIterator = new OS.File.DirectoryIterator(gPingsArchivePath); let subdirs = (yield dirIterator.nextBatch()).filter(e => e.isDir).filter(e => isValidArchiveDir(e.name)); + dirIterator.close(); // Walk through the monthly subdirs of the form / for (let dir of subdirs) { this._log.trace("_scanArchive - checking in subdir: " + dir.path); let pingIterator = new OS.File.DirectoryIterator(dir.path); let pings = (yield pingIterator.nextBatch()).filter(e => !e.isDir); + pingIterator.close(); // Now process any ping files of the form "...[json|jsonlz4]". for (let p of pings) { @@ -1479,56 +1482,56 @@ var TelemetryStorageImpl = { let iter = new OS.File.DirectoryIterator(directory); let exists = yield iter.exists(); - if (!exists) { - yield iter.close(); - return []; - } - - let files = (yield iter.nextBatch()).filter(e => !e.isDir); - - for (let file of files) { - if (this._shutdown) { - yield iter.close(); + try { + if (!exists) { return []; } - let info; - try { - info = yield OS.File.stat(file.path); - } catch (ex) { - this._log.error("_scanPendingPings - failed to stat file " + file.path, ex); - continue; - } + let files = (yield iter.nextBatch()).filter(e => !e.isDir); - // Enforce a maximum file size limit on pending pings. - if (info.size > PING_FILE_MAXIMUM_SIZE_BYTES) { - this._log.error("_scanPendingPings - removing file exceeding size limit " + file.path); + for (let file of files) { + if (this._shutdown) { + return []; + } + + let info; try { - yield OS.File.remove(file.path); + info = yield OS.File.stat(file.path); } catch (ex) { - this._log.error("_scanPendingPings - failed to remove file " + file.path, ex); - } finally { - Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB") - .add(Math.floor(info.size / 1024 / 1024)); - Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").add(); + this._log.error("_scanPendingPings - failed to stat file " + file.path, ex); continue; } - } - let id = OS.Path.basename(file.path); - if (!UUID_REGEX.test(id)) { - this._log.trace("_scanPendingPings - filename is not a UUID: " + id); - id = Utils.generateUUID(); - } + // Enforce a maximum file size limit on pending pings. + if (info.size > PING_FILE_MAXIMUM_SIZE_BYTES) { + this._log.error("_scanPendingPings - removing file exceeding size limit " + file.path); + try { + yield OS.File.remove(file.path); + } catch (ex) { + this._log.error("_scanPendingPings - failed to remove file " + file.path, ex); + } finally { + Telemetry.getHistogramById("TELEMETRY_DISCARDED_PENDING_PINGS_SIZE_MB") + .add(Math.floor(info.size / 1024 / 1024)); + Telemetry.getHistogramById("TELEMETRY_PING_SIZE_EXCEEDED_PENDING").add(); + continue; + } + } - this._pendingPings.set(id, { - path: file.path, - lastModificationDate: info.lastModificationDate.getTime(), - }); + let id = OS.Path.basename(file.path); + if (!UUID_REGEX.test(id)) { + this._log.trace("_scanPendingPings - filename is not a UUID: " + id); + id = Utils.generateUUID(); + } + + this._pendingPings.set(id, { + path: file.path, + lastModificationDate: info.lastModificationDate.getTime(), + }); + } + } finally { + yield iter.close(); } - yield iter.close(); - // Explicitly load the deletion ping from its known path, if it's there. if (yield OS.File.exists(gDeletionPingFilePath)) { this._log.trace("_scanPendingPings - Adding pending deletion ping."); diff --git a/toolkit/components/telemetry/ThirdPartyCookieProbe.jsm b/toolkit/components/telemetry/ThirdPartyCookieProbe.jsm index 8c6496b3b1d4..fedac17100b8 100644 --- a/toolkit/components/telemetry/ThirdPartyCookieProbe.jsm +++ b/toolkit/components/telemetry/ThirdPartyCookieProbe.jsm @@ -116,16 +116,6 @@ this.ThirdPartyCookieProbe.prototype = { return; } this._latestFlush = aNow; - let acceptedSites = Services.telemetry.getHistogramById("COOKIES_3RDPARTY_NUM_SITES_ACCEPTED"); - let rejectedSites = Services.telemetry.getHistogramById("COOKIES_3RDPARTY_NUM_SITES_BLOCKED"); - let acceptedRequests = Services.telemetry.getHistogramById("COOKIES_3RDPARTY_NUM_ATTEMPTS_ACCEPTED"); - let rejectedRequests = Services.telemetry.getHistogramById("COOKIES_3RDPARTY_NUM_ATTEMPTS_BLOCKED"); - for (let [k, data] of this._thirdPartyCookies) { - acceptedSites.add(data.countAcceptedSites / updays); - rejectedSites.add(data.countRejectedSites / updays); - acceptedRequests.add(data.countAcceptedRequests / updays); - rejectedRequests.add(data.countRejectedRequests / updays); - } this._thirdPartyCookies.clear(); } }; diff --git a/toolkit/components/telemetry/docs/environment.rst b/toolkit/components/telemetry/docs/environment.rst index d2e3ceac43fb..39bb03b1eb85 100644 --- a/toolkit/components/telemetry/docs/environment.rst +++ b/toolkit/components/telemetry/docs/environment.rst @@ -312,6 +312,8 @@ The following is a partial list of collected preferences. - ``browser.urlbar.userMadeSearchSuggestionsChoice``: True if the user has clicked Yes or No in the urlbar's opt-in notification. Defaults to false. +- ``browser.zoom.full``: True if zoom is enabled for both text and images, that is if "Zoom Text Only" is not enabled. Defaults to true. Collection of this preference has been enabled in Firefox 50 and will be disabled again in Firefox 53 (`Bug 979323 `_). + partner ------- diff --git a/toolkit/components/telemetry/histogram-whitelists.json b/toolkit/components/telemetry/histogram-whitelists.json index be179c2eaa5d..28a74c28576d 100644 --- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -17,8 +17,6 @@ "BACKGROUNDFILESAVER_THREAD_COUNT", "BAD_FALLBACK_FONT", "BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS", - "BROWSERPROVIDER_XUL_IMPORT_HISTORY", - "BROWSERPROVIDER_XUL_IMPORT_TIME", "BROWSER_IS_ASSIST_DEFAULT", "BROWSER_IS_USER_DEFAULT", "BROWSER_IS_USER_DEFAULT_ERROR", @@ -126,10 +124,6 @@ "COMPOSITE_FRAME_ROUNDTRIP_TIME", "COMPOSITE_TIME", "CONTENT_DOCUMENTS_DESTROYED", - "COOKIES_3RDPARTY_NUM_ATTEMPTS_ACCEPTED", - "COOKIES_3RDPARTY_NUM_ATTEMPTS_BLOCKED", - "COOKIES_3RDPARTY_NUM_SITES_ACCEPTED", - "COOKIES_3RDPARTY_NUM_SITES_BLOCKED", "CRASH_STORE_COMPRESSED_BYTES", "DATABASE_LOCKED_EXCEPTION", "DATABASE_SUCCESSFUL_UNLOCK", @@ -382,12 +376,6 @@ "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS", "DEVTOOLS_WEBIDE_USB_CONNECTION_RESULT", "DEVTOOLS_WEBIDE_WIFI_CONNECTION_RESULT", - "DISK_CACHE_CORRUPT_DETAILS", - "DISK_CACHE_INVALIDATION_SUCCESS", - "DISK_CACHE_REDUCTION_TRIAL", - "DISK_CACHE_REVALIDATION_SAFE", - "DISK_CACHE_REVALIDATION_SUCCESS", - "DISK_CACHE_SMART_SIZE_USING_OLD_MAX", "DISPLAY_SCALING_LINUX", "DISPLAY_SCALING_MSWIN", "DISPLAY_SCALING_OSX", @@ -399,10 +387,6 @@ "DNS_RENEWAL_TIME", "DNS_RENEWAL_TIME_FOR_TTL", "DNT_USAGE", - "DOM_RANGE_DETACHED", - "DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT", - "DOM_TIMERS_RECENTLY_SET", - "DOM_WINDOW_SHOWMODALDIALOG_USED", "DWRITEFONT_DELAYEDINITFONTLIST_COLLECT", "DWRITEFONT_DELAYEDINITFONTLIST_COUNT", "DWRITEFONT_DELAYEDINITFONTLIST_TOTAL", @@ -453,8 +437,6 @@ "FX_BROWSER_FULLSCREEN_USED", "FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE", "FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE", - "FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE", - "FX_IDENTITY_POPUP_OPEN_MS", "FX_MIGRATION_ENTRY_POINT", "FX_MIGRATION_ERRORS", "FX_MIGRATION_HOMEPAGE_IMPORTED", @@ -468,7 +450,6 @@ "FX_SESSION_RESTORE_NUMBER_OF_WINDOWS_RESTORED", "FX_TABLETMODE_PAGE_LOAD", "FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS", - "FX_TAB_ANIM_ANY_FRAME_PAINT_MS", "FX_TAB_ANIM_OPEN_FRAME_INTERVAL_MS", "FX_TAB_ANIM_OPEN_PREVIEW_FRAME_INTERVAL_MS", "FX_TAB_CLICK_MS", @@ -498,7 +479,6 @@ "GRADIENT_DURATION", "GRADIENT_RETENTION_TIME", "HISTORY_LASTVISITED_TREE_QUERY_TIME_MS", - "HTML_BACKGROUND_REFLOW_MS_2", "HTML_FOREGROUND_REFLOW_MS_2", "HTTPCONNMGR_TOTAL_SPECULATIVE_CONN", "HTTPCONNMGR_UNUSED_SPECULATIVE_CONN", @@ -518,6 +498,14 @@ "HTTP_MEMORY_CACHE_DISPOSITION_2", "HTTP_OFFLINE_CACHE_DISPOSITION_2", "HTTP_OFFLINE_CACHE_DOCUMENT_LOAD", + "HTTP_CACHE_IO_QUEUE_OPEN_PRIORITY", + "HTTP_CACHE_IO_QUEUE_READ_PRIORITY", + "HTTP_CACHE_IO_QUEUE_OPEN", + "HTTP_CACHE_IO_QUEUE_READ", + "HTTP_CACHE_IO_QUEUE_MANAGEMENT", + "HTTP_CACHE_IO_QUEUE_WRITE", + "HTTP_CACHE_IO_QUEUE_INDEX", + "HTTP_CACHE_IO_QUEUE_EVICT", "HTTP_PAGELOAD_IS_SSL", "HTTP_PAGE_CACHE_READ_TIME", "HTTP_PAGE_CACHE_READ_TIME_V2", @@ -590,15 +578,12 @@ "LOCALDOMSTORAGE_GETKEY_BLOCKING_MS", "LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS", "LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS", - "LOCALDOMSTORAGE_INIT_DATABASE_MS", - "LOCALDOMSTORAGE_KEY_SIZE_BYTES", "LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS", "LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS", "LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS", "LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS", "LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS", "LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS", - "LOCALDOMSTORAGE_VALUE_SIZE_BYTES", "LONG_REFLOW_INTERRUPTIBLE", "LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS", @@ -848,7 +833,6 @@ "REQUESTS_OF_ORIGINAL_CONTENT", "SAFE_MODE_USAGE", "SEARCH_COUNTS", - "SEARCH_SERVICE_BUILD_CACHE_MS", "SEARCH_SERVICE_INIT_MS", "SECURITY_UI", "SERVICE_WORKER_CONTROLLED_DOCUMENTS", @@ -860,8 +844,6 @@ "SERVICE_WORKER_SPAWN_GETS_QUEUED", "SERVICE_WORKER_UPDATED", "SERVICE_WORKER_WAS_SPAWNED", - "SESSIONDOMSTORAGE_KEY_SIZE_BYTES", - "SESSIONDOMSTORAGE_VALUE_SIZE_BYTES", "SHARED_WORKER_SPAWN_GETS_QUEUED", "SHOULD_AUTO_DETECT_LANGUAGE", "SHOULD_TRANSLATION_UI_APPEAR", @@ -898,9 +880,6 @@ "SPDY_SYN_REPLY_SIZE", "SPDY_SYN_SIZE", "SPDY_VERSION2", - "SQLITEBRIDGE_PROVIDER_FORMS_LOCKED", - "SQLITEBRIDGE_PROVIDER_HOME_LOCKED", - "SQLITEBRIDGE_PROVIDER_PASSWORDS_LOCKED", "SSL_AUTH_ALGORITHM_FULL", "SSL_AUTH_ECDSA_CURVE_FULL", "SSL_AUTH_RSA_KEY_SIZE_FULL", @@ -1031,10 +1010,7 @@ "WORD_CACHE_MISSES_CHROME", "WORD_CACHE_MISSES_CONTENT", "XMLHTTPREQUEST_ASYNC_OR_SYNC", - "XUL_BACKGROUND_REFLOW_MS", - "XUL_CACHE_DISABLED", - "XUL_FOREGROUND_REFLOW_MS", - "XUL_INITIAL_FRAME_CONSTRUCTION" + "XUL_CACHE_DISABLED" ], "bug_numbers": [ "A11Y_CONSUMERS", @@ -1063,8 +1039,6 @@ "BLOCKED_ON_PLUGIN_STREAM_INIT_MS", "BLOCKLIST_SYNC_FILE_LOAD", "BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS", - "BROWSERPROVIDER_XUL_IMPORT_HISTORY", - "BROWSERPROVIDER_XUL_IMPORT_TIME", "BROWSER_IS_ASSIST_DEFAULT", "BROWSER_IS_USER_DEFAULT", "BROWSER_IS_USER_DEFAULT_ERROR", @@ -1180,10 +1154,6 @@ "COMPOSITE_FRAME_ROUNDTRIP_TIME", "COMPOSITE_TIME", "CONTENT_DOCUMENTS_DESTROYED", - "COOKIES_3RDPARTY_NUM_ATTEMPTS_ACCEPTED", - "COOKIES_3RDPARTY_NUM_ATTEMPTS_BLOCKED", - "COOKIES_3RDPARTY_NUM_SITES_ACCEPTED", - "COOKIES_3RDPARTY_NUM_SITES_BLOCKED", "COOKIE_SCHEME_SECURITY", "CRASH_STORE_COMPRESSED_BYTES", "CYCLE_COLLECTOR", @@ -1473,12 +1443,6 @@ "DEVTOOLS_WEBIDE_TIME_ACTIVE_SECONDS", "DEVTOOLS_WEBIDE_USB_CONNECTION_RESULT", "DEVTOOLS_WEBIDE_WIFI_CONNECTION_RESULT", - "DISK_CACHE_CORRUPT_DETAILS", - "DISK_CACHE_INVALIDATION_SUCCESS", - "DISK_CACHE_REDUCTION_TRIAL", - "DISK_CACHE_REVALIDATION_SAFE", - "DISK_CACHE_REVALIDATION_SUCCESS", - "DISK_CACHE_SMART_SIZE_USING_OLD_MAX", "DISPLAY_SCALING_LINUX", "DISPLAY_SCALING_MSWIN", "DISPLAY_SCALING_OSX", @@ -1490,10 +1454,6 @@ "DNS_RENEWAL_TIME", "DNS_RENEWAL_TIME_FOR_TTL", "DNT_USAGE", - "DOM_RANGE_DETACHED", - "DOM_TIMERS_FIRED_PER_NATIVE_TIMEOUT", - "DOM_TIMERS_RECENTLY_SET", - "DOM_WINDOW_SHOWMODALDIALOG_USED", "DWRITEFONT_DELAYEDINITFONTLIST_COLLECT", "DWRITEFONT_DELAYEDINITFONTLIST_COUNT", "DWRITEFONT_DELAYEDINITFONTLIST_TOTAL", @@ -1501,9 +1461,6 @@ "E10S_BLOCKED_FROM_RUNNING", "E10S_STILL_ACCEPTED_FROM_PROMPT", "E10S_WINDOW", - "EARLY_GLUESTARTUP_HARD_FAULTS", - "EARLY_GLUESTARTUP_READ_OPS", - "EARLY_GLUESTARTUP_READ_TRANSFER", "ENABLE_PRIVILEGE_EVER_CALLED", "FENNEC_DISTRIBUTION_CODE_CATEGORY", "FENNEC_DISTRIBUTION_DOWNLOAD_TIME_MS", @@ -1549,8 +1506,6 @@ "FX_BROWSER_FULLSCREEN_USED", "FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE", "FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE", - "FX_GESTURE_TAKE_SNAPSHOT_OF_PAGE", - "FX_IDENTITY_POPUP_OPEN_MS", "FX_MIGRATION_ENTRY_POINT", "FX_MIGRATION_ERRORS", "FX_MIGRATION_HOMEPAGE_IMPORTED", @@ -1593,10 +1548,7 @@ "FX_SESSION_RESTORE_WRITE_FILE_MS", "FX_TABLETMODE_PAGE_LOAD", "FX_TAB_ANIM_ANY_FRAME_INTERVAL_MS", - "FX_TAB_ANIM_ANY_FRAME_PAINT_MS", - "FX_TAB_ANIM_CLOSE_MS", "FX_TAB_ANIM_OPEN_FRAME_INTERVAL_MS", - "FX_TAB_ANIM_OPEN_MS", "FX_TAB_ANIM_OPEN_PREVIEW_FRAME_INTERVAL_MS", "FX_TAB_CLICK_MS", "FX_TAB_SWITCH_SPINNER_VISIBLE_MS", @@ -1644,9 +1596,6 @@ "GFX_CONTENT_FAILED_TO_ACQUIRE_DEVICE", "GFX_CRASH", "GHOST_WINDOWS", - "GLUESTARTUP_HARD_FAULTS", - "GLUESTARTUP_READ_OPS", - "GLUESTARTUP_READ_TRANSFER", "GRADIENT_DURATION", "GRADIENT_RETENTION_TIME", "GRAPHICS_DRIVER_STARTUP_TEST", @@ -1654,7 +1603,6 @@ "GRAPHICS_SANITY_TEST_OS_SNAPSHOT", "GRAPHICS_SANITY_TEST_REASON", "HISTORY_LASTVISITED_TREE_QUERY_TIME_MS", - "HTML_BACKGROUND_REFLOW_MS_2", "HTML_FOREGROUND_REFLOW_MS_2", "HTTPCONNMGR_TOTAL_SPECULATIVE_CONN", "HTTPCONNMGR_UNUSED_SPECULATIVE_CONN", @@ -1674,6 +1622,14 @@ "HTTP_MEMORY_CACHE_DISPOSITION_2", "HTTP_OFFLINE_CACHE_DISPOSITION_2", "HTTP_OFFLINE_CACHE_DOCUMENT_LOAD", + "HTTP_CACHE_IO_QUEUE_OPEN_PRIORITY", + "HTTP_CACHE_IO_QUEUE_READ_PRIORITY", + "HTTP_CACHE_IO_QUEUE_OPEN", + "HTTP_CACHE_IO_QUEUE_READ", + "HTTP_CACHE_IO_QUEUE_MANAGEMENT", + "HTTP_CACHE_IO_QUEUE_WRITE", + "HTTP_CACHE_IO_QUEUE_INDEX", + "HTTP_CACHE_IO_QUEUE_EVICT", "HTTP_PAGELOAD_IS_SSL", "HTTP_PAGE_CACHE_READ_TIME", "HTTP_PAGE_CACHE_READ_TIME_V2", @@ -1750,15 +1706,12 @@ "LOCALDOMSTORAGE_GETKEY_BLOCKING_MS", "LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS", "LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS", - "LOCALDOMSTORAGE_INIT_DATABASE_MS", - "LOCALDOMSTORAGE_KEY_SIZE_BYTES", "LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS", "LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS", "LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS", "LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS", "LOCALDOMSTORAGE_SHUTDOWN_DATABASE_MS", "LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS", - "LOCALDOMSTORAGE_VALUE_SIZE_BYTES", "LONG_REFLOW_INTERRUPTIBLE", "LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS", "LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS", @@ -2065,7 +2018,6 @@ "REQUESTS_OF_ORIGINAL_CONTENT", "SAFE_MODE_USAGE", "SEARCH_COUNTS", - "SEARCH_SERVICE_BUILD_CACHE_MS", "SEARCH_SERVICE_COUNTRY_FETCH_CAUSED_SYNC_INIT", "SEARCH_SERVICE_COUNTRY_FETCH_RESULT", "SEARCH_SERVICE_COUNTRY_FETCH_TIME_MS", @@ -2088,8 +2040,6 @@ "SERVICE_WORKER_SPAWN_GETS_QUEUED", "SERVICE_WORKER_UPDATED", "SERVICE_WORKER_WAS_SPAWNED", - "SESSIONDOMSTORAGE_KEY_SIZE_BYTES", - "SESSIONDOMSTORAGE_VALUE_SIZE_BYTES", "SHARED_WORKER_SPAWN_GETS_QUEUED", "SHOULD_AUTO_DETECT_LANGUAGE", "SHOULD_TRANSLATION_UI_APPEAR", @@ -2127,9 +2077,6 @@ "SPDY_SYN_REPLY_SIZE", "SPDY_SYN_SIZE", "SPDY_VERSION2", - "SQLITEBRIDGE_PROVIDER_FORMS_LOCKED", - "SQLITEBRIDGE_PROVIDER_HOME_LOCKED", - "SQLITEBRIDGE_PROVIDER_PASSWORDS_LOCKED", "SSL_AUTH_ALGORITHM_FULL", "SSL_AUTH_ECDSA_CURVE_FULL", "SSL_AUTH_RSA_KEY_SIZE_FULL", @@ -2415,10 +2362,7 @@ "WORD_CACHE_MISSES_CHROME", "WORD_CACHE_MISSES_CONTENT", "XMLHTTPREQUEST_ASYNC_OR_SYNC", - "XUL_BACKGROUND_REFLOW_MS", - "XUL_CACHE_DISABLED", - "XUL_FOREGROUND_REFLOW_MS", - "XUL_INITIAL_FRAME_CONSTRUCTION" + "XUL_CACHE_DISABLED" ], "n_buckets": [ "MEMORY_JS_GC_HEAP", diff --git a/toolkit/components/thumbnails/PageThumbsWorker.js b/toolkit/components/thumbnails/PageThumbsWorker.js index 1149727e7d4a..83171c91f434 100644 --- a/toolkit/components/thumbnails/PageThumbsWorker.js +++ b/toolkit/components/thumbnails/PageThumbsWorker.js @@ -79,19 +79,23 @@ var Agent = { getFileEntriesInDirectory: function Agent_getFileEntriesInDirectory(path, skipFiles) { let iter = new OS.File.DirectoryIterator(path); - if (!iter.exists()) { - return []; - } - - let skip = new Set(skipFiles); - - let entries = []; - for (let entry in iter) { - if (!entry.isDir && !entry.isSymLink && !skip.has(entry.name)) { - entries.push(entry); + try { + if (!iter.exists()) { + return []; } + + let skip = new Set(skipFiles); + + let entries = []; + for (let entry in iter) { + if (!entry.isDir && !entry.isSymLink && !skip.has(entry.name)) { + entries.push(entry); + } + } + return entries; + } finally { + iter.close(); } - return entries; }, moveOrDeleteAllThumbnails: diff --git a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl index 6df3cdabe6f2..379d2c2a261e 100644 --- a/toolkit/components/typeaheadfind/nsITypeAheadFind.idl +++ b/toolkit/components/typeaheadfind/nsITypeAheadFind.idl @@ -18,7 +18,7 @@ interface nsIDocShell; /****************************** nsTypeAheadFind ******************************/ -[scriptable, uuid(c8ca2c38-7030-4453-ae63-a16eeb10e096)] +[scriptable, uuid(ae501e28-c57f-4692-ac74-410e1bed98b7)] interface nsITypeAheadFind : nsISupports { /****************************** Initializer ******************************/ @@ -65,6 +65,7 @@ interface nsITypeAheadFind : nsISupports readonly attribute AString searchString; // Most recent search string attribute boolean caseSensitive; // Searches are case sensitive + attribute boolean entireWord; // Search for whole words only readonly attribute nsIDOMElement foundLink; // Most recent elem found, if a link readonly attribute nsIDOMElement foundEditable; diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp index 7619216d2878..d6e1f84f718d 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.cpp @@ -80,7 +80,8 @@ nsTypeAheadFind::nsTypeAheadFind(): mDidAddObservers(false), mLastFindLength(0), mIsSoundInitialized(false), - mCaseSensitive(false) + mCaseSensitive(false), + mEntireWord(false) { } @@ -167,6 +168,26 @@ nsTypeAheadFind::GetCaseSensitive(bool* isCaseSensitive) return NS_OK; } +NS_IMETHODIMP +nsTypeAheadFind::SetEntireWord(bool isEntireWord) +{ + mEntireWord = isEntireWord; + + if (mFind) { + mFind->SetEntireWord(mEntireWord); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsTypeAheadFind::GetEntireWord(bool* isEntireWord) +{ + *isEntireWord = mEntireWord; + + return NS_OK; +} + NS_IMETHODIMP nsTypeAheadFind::SetDocShell(nsIDocShell* aDocShell) { diff --git a/toolkit/components/typeaheadfind/nsTypeAheadFind.h b/toolkit/components/typeaheadfind/nsTypeAheadFind.h index 4b6346ad078c..8ff5ad1bfe1e 100644 --- a/toolkit/components/typeaheadfind/nsTypeAheadFind.h +++ b/toolkit/components/typeaheadfind/nsTypeAheadFind.h @@ -106,6 +106,7 @@ protected: nsCOMPtr mFind; bool mCaseSensitive; + bool mEntireWord; bool EnsureFind() { if (mFind) { @@ -118,7 +119,7 @@ protected: } mFind->SetCaseSensitive(mCaseSensitive); - mFind->SetWordBreaker(nullptr); + mFind->SetEntireWord(mEntireWord); return true; } diff --git a/toolkit/content/tests/chrome/chrome.ini b/toolkit/content/tests/chrome/chrome.ini index eb309d79c123..7299d41ae0ef 100644 --- a/toolkit/content/tests/chrome/chrome.ini +++ b/toolkit/content/tests/chrome/chrome.ini @@ -15,6 +15,7 @@ support-files = dialog_dialogfocus.xul file_about_networking_wsh.py file_autocomplete_with_composition.js + findbar_entireword_window.xul findbar_events_window.xul findbar_window.xul frame_popup_anchor.xul @@ -23,6 +24,7 @@ support-files = frame_subframe_origin_subframe2.xul popup_childframe_node.xul popup_trigger.js + sample_entireword_latin1.html window_browser_drop.xul window_keys.xul window_largemenu.xul @@ -101,6 +103,8 @@ skip-if = toolkit == "cocoa" [test_findbar.xul] subsuite = clipboard skip-if = buildapp == 'mulet' +[test_findbar_entireword.xul] +skip-if = buildapp == 'mulet' [test_findbar_events.xul] [test_focus_anons.xul] [test_hiddenitems.xul] diff --git a/toolkit/content/tests/chrome/findbar_entireword_window.xul b/toolkit/content/tests/chrome/findbar_entireword_window.xul new file mode 100644 index 000000000000..fef300a1f752 --- /dev/null +++ b/toolkit/content/tests/chrome/findbar_entireword_window.xul @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + diff --git a/toolkit/content/tests/chrome/sample_entireword_latin1.html b/toolkit/content/tests/chrome/sample_entireword_latin1.html new file mode 100644 index 000000000000..b2d66fa3c4d2 --- /dev/null +++ b/toolkit/content/tests/chrome/sample_entireword_latin1.html @@ -0,0 +1,11 @@ + + + Latin entire-word find test page + + +

The twins of Mammon quarrelled. Their warring plunged the world into a new darkness, and the beast abhorred the darkness. So it began to move swiftly, and grew more powerful, and went forth and multiplied. And the beasts brought fire and light to the darkness.

+

from The Book of Mozilla, 15:1

+ + diff --git a/toolkit/content/tests/chrome/test_findbar_entireword.xul b/toolkit/content/tests/chrome/test_findbar_entireword.xul new file mode 100644 index 000000000000..dc39fe09ddc9 --- /dev/null +++ b/toolkit/content/tests/chrome/test_findbar_entireword.xul @@ -0,0 +1,41 @@ + + + + + + + + + + + Mozilla Bug 269442 + + +

+ +
+    
+ + + + +
diff --git a/toolkit/content/widgets/findbar.xml b/toolkit/content/widgets/findbar.xml index 06a520341dd5..f8dfe7a24e1e 100644 --- a/toolkit/content/widgets/findbar.xml +++ b/toolkit/content/widgets/findbar.xml @@ -196,7 +196,16 @@ oncommand="_setCaseSensitivity(this.checked ? 1 : 0);" type="checkbox" xbl:inherits="accesskey=matchcaseaccesskey"/> + +