diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 2fcce652ce21..76b8fe49caa9 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1494,34 +1494,34 @@ HyperTextAccessible::CaretLineNumber() return lineNumber; } -nsIntRect +LayoutDeviceIntRect HyperTextAccessible::GetCaretRect(nsIWidget** aWidget) { *aWidget = nullptr; RefPtr caret = mDoc->PresShell()->GetCaret(); - NS_ENSURE_TRUE(caret, nsIntRect()); + NS_ENSURE_TRUE(caret, LayoutDeviceIntRect()); bool isVisible = caret->IsVisible(); if (!isVisible) - return nsIntRect(); + return LayoutDeviceIntRect(); nsRect rect; nsIFrame* frame = caret->GetGeometry(&rect); if (!frame || rect.IsEmpty()) - return nsIntRect(); + return LayoutDeviceIntRect(); nsPoint offset; // Offset from widget origin to the frame origin, which includes chrome // on the widget. *aWidget = frame->GetNearestWidget(offset); - NS_ENSURE_TRUE(*aWidget, nsIntRect()); + NS_ENSURE_TRUE(*aWidget, LayoutDeviceIntRect()); rect.MoveBy(offset); - nsIntRect caretRect; - caretRect = rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel()); + LayoutDeviceIntRect caretRect = LayoutDeviceIntRect::FromUnknownRect( + rect.ToOutsidePixels(frame->PresContext()->AppUnitsPerDevPixel())); // ((content screen origin) - (content offset in the widget)) = widget origin on the screen - caretRect.MoveBy((*aWidget)->WidgetToScreenOffsetUntyped() - (*aWidget)->GetClientOffset()); + caretRect.MoveBy((*aWidget)->WidgetToScreenOffset() - (*aWidget)->GetClientOffset()); // Correct for character size, so that caret always matches the size of // the character. This is important for font size transitions, and is diff --git a/accessible/generic/HyperTextAccessible.h b/accessible/generic/HyperTextAccessible.h index 524276caf7f3..b6ba2ee5d2d8 100644 --- a/accessible/generic/HyperTextAccessible.h +++ b/accessible/generic/HyperTextAccessible.h @@ -335,7 +335,7 @@ public: * @param [out] the widget containing the caret * @return the caret rect */ - nsIntRect GetCaretRect(nsIWidget** aWidget); + mozilla::LayoutDeviceIntRect GetCaretRect(nsIWidget** aWidget); /** * Return selected regions count within the accessible. diff --git a/accessible/windows/msaa/AccessibleWrap.cpp b/accessible/windows/msaa/AccessibleWrap.cpp index fd3c07161226..6ac115167b0d 100644 --- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1633,7 +1633,7 @@ AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) return; nsIWidget* widget = nullptr; - nsIntRect caretRect = text->GetCaretRect(&widget); + LayoutDeviceIntRect caretRect = text->GetCaretRect(&widget); HWND caretWnd; if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { return; diff --git a/browser/components/newtab/NewTabPrefsProvider.jsm b/browser/components/newtab/NewTabPrefsProvider.jsm new file mode 100644 index 000000000000..86ce80033a6d --- /dev/null +++ b/browser/components/newtab/NewTabPrefsProvider.jsm @@ -0,0 +1,84 @@ +/* global Services, Preferences, EventEmitter, XPCOMUtils */ +/* exported NewTabPrefsProvider */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["NewTabPrefsProvider"]; + +const {interfaces: Ci, utils: Cu} = Components; +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Preferences.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() { + const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {}); + return EventEmitter; +}); + +// Supported prefs and data type +const gPrefsMap = new Map([ + ["browser.newtabpage.enabled", "bool"], + ["browser.newtabpage.enhanced", "bool"], + ["browser.newtabpage.pinned", "str"], + ["intl.locale.matchOS", "bool"], + ["general.useragent.locale", "localized"], +]); + +let PrefsProvider = function PrefsProvider() { + EventEmitter.decorate(this); +}; + +PrefsProvider.prototype = { + + observe(subject, topic, data) { // jshint ignore:line + if (topic === "nsPref:changed") { + if (gPrefsMap.has(data)) { + switch (gPrefsMap.get(data)) { + case "bool": + this.emit(data, Preferences.get(data, false)); + break; + case "str": + this.emit(data, Preferences.get(data, "")); + break; + case "localized": + try { + this.emit(data, Preferences.get(data, "", Ci.nsIPrefLocalizedString)); + } catch (e) { + this.emit(data, Preferences.get(data, "")); + } + break; + default: + this.emit(data); + break; + } + } + } else { + Cu.reportError(new Error("NewTabPrefsProvider observing unknown topic")); + } + }, + + get prefsMap() { + return gPrefsMap; + }, + + init() { + for (let pref of gPrefsMap.keys()) { + Services.prefs.addObserver(pref, this, false); + } + }, + + uninit() { + for (let pref of gPrefsMap.keys()) { + Services.prefs.removeObserver(pref, this, false); + } + } +}; + +/** + * Singleton that serves as the default new tab pref provider for the grid. + */ +const gPrefs = new PrefsProvider(); + +let NewTabPrefsProvider = { + prefs: gPrefs, +}; diff --git a/browser/components/newtab/RemoteAboutNewTab.jsm b/browser/components/newtab/RemoteAboutNewTab.jsm index 722e0bae85d1..1f335511d2ad 100644 --- a/browser/components/newtab/RemoteAboutNewTab.jsm +++ b/browser/components/newtab/RemoteAboutNewTab.jsm @@ -1,9 +1,8 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - /* globals Services, XPCOMUtils, RemotePages, RemoteNewTabLocation, RemoteNewTabUtils, Task */ -/* globals BackgroundPageThumbs, PageThumbs, DirectoryLinksProvider */ +/* globals BackgroundPageThumbs, PageThumbs, DirectoryLinksProvider, PlacesProvider, NewTabPrefsProvider */ /* exported RemoteAboutNewTab */ "use strict"; @@ -31,6 +30,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider", "resource:///modules/DirectoryLinksProvider.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabLocation", "resource:///modules/RemoteNewTabLocation.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesProvider", + "resource:///modules/PlacesProvider.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", + "resource:///modules/NewTabPrefsProvider.jsm"); let RemoteAboutNewTab = { @@ -43,6 +46,7 @@ let RemoteAboutNewTab = { this.pageListener = new RemotePages("about:remote-newtab"); this.pageListener.addMessageListener("NewTab:InitializeGrid", this.initializeGrid.bind(this)); this.pageListener.addMessageListener("NewTab:UpdateGrid", this.updateGrid.bind(this)); + this.pageListener.addMessageListener("NewTab:Customize", this.customize.bind(this)); this.pageListener.addMessageListener("NewTab:CaptureBackgroundPageThumbs", this.captureBackgroundPageThumb.bind(this)); this.pageListener.addMessageListener("NewTab:PageThumbs", this.createPageThumb.bind(this)); @@ -51,6 +55,43 @@ let RemoteAboutNewTab = { this._addObservers(); }, + customize: function(message) { + if (message.data.enabled !== undefined) { + Services.prefs.setBoolPref("browser.newtabpage.enabled", message.data.enabled); + } + if (message.data.enhanced !== undefined) { + Services.prefs.setBoolPref("browser.newtabpage.enhanced", message.data.enhanced); + } + }, + + /** + * Notifies when history is cleared + */ + placesClearHistory: function() { + this.pageListener.sendAsyncMessage("NewTab:PlacesClearHistory"); + }, + + /** + * Notifies when a link has changed + */ + placesLinkChanged: function(name, data) { // jshint ignore:line + this.pageListener.sendAsyncMessage("NewTab:PlacesLinkChanged", data); + }, + + /** + * Notifies when many links have changed + */ + placesManyLinksChanged: function() { + this.pageListener.sendAsyncMessage("NewTab:PlacesManyLinksChanged"); + }, + + /** + * Notifies when one URL has been deleted + */ + placesDeleteURI: function(name, data) { // jshint ignore:line + this.pageListener.sendAsyncMessage("NewTab:PlacesDeleteURI", data.url); + }, + /** * Initializes the grid for the first time when the page loads. * Fetch all the links and send them down to the child to populate @@ -186,7 +227,6 @@ let RemoteAboutNewTab = { */ observe: function(aSubject, aTopic, aData) { // jshint ignore:line let extraData; - let refreshPage = false; if (aTopic === "browser:purge-session-history") { RemoteNewTabUtils.links.resetCache(); RemoteNewTabUtils.links.populateCache(() => { @@ -206,12 +246,31 @@ let RemoteAboutNewTab = { } }, + setEnabled: function(name, data) { // jshint ignore:line + this.pageListener.sendAsyncMessage("NewTab:setEnabled", data); + }, + + setEnhanced: function(name, data) { // jshint ignore:line + this.pageListener.sendAsyncMessage("NewTab:setEnhanced", data); + }, + + setPinned: function(name, data) { // jshint ignore:line + this.pageListener.sendAsyncMessage("NewTab:setPinnedLinks", data); + }, + /** * Add all observers that about:newtab page must listen for. */ _addObservers: function() { Services.obs.addObserver(this, "page-thumbnail:create", true); Services.obs.addObserver(this, "browser:purge-session-history", true); + PlacesProvider.links.on("deleteURI", this.placesDeleteURI.bind(this)); + PlacesProvider.links.on("clearHistory", this.placesClearHistory.bind(this)); + PlacesProvider.links.on("linkChanged", this.placesLinkChanged.bind(this)); + PlacesProvider.links.on("manyLinksChanged", this.placesManyLinksChanged.bind(this)); + NewTabPrefsProvider.prefs.on("browser.newtabpage.enabled", this.setEnabled.bind(this)); + NewTabPrefsProvider.prefs.on("browser.newtabpage.enhanced", this.setEnhanced.bind(this)); + NewTabPrefsProvider.prefs.on("browser.newtabpage.pinned", this.setPinned.bind(this)); }, /** @@ -220,6 +279,13 @@ let RemoteAboutNewTab = { _removeObservers: function() { Services.obs.removeObserver(this, "page-thumbnail:create"); Services.obs.removeObserver(this, "browser:purge-session-history"); + PlacesProvider.links.off("deleteURI", this.placesDeleteURI); + PlacesProvider.links.off("clearHistory", this.placesClearHistory); + PlacesProvider.links.off("linkChanged", this.placesLinkChanged); + PlacesProvider.links.off("manyLinksChanged", this.placesManyLinksChanged); + NewTabPrefsProvider.prefs.off("browser.newtabpage.enabled", this.setEnabled.bind(this)); + NewTabPrefsProvider.prefs.off("browser.newtabpage.enhanced", this.setEnhanced.bind(this)); + NewTabPrefsProvider.prefs.off("browser.newtabpage.pinned", this.setPinned.bind(this)); }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, diff --git a/browser/components/newtab/moz.build b/browser/components/newtab/moz.build index a2660375a21b..16f657e86f33 100644 --- a/browser/components/newtab/moz.build +++ b/browser/components/newtab/moz.build @@ -11,6 +11,7 @@ XPCSHELL_TESTS_MANIFESTS += [ ] EXTRA_JS_MODULES += [ + 'NewTabPrefsProvider.jsm', 'NewTabURL.jsm', 'PlacesProvider.jsm', 'RemoteAboutNewTab.jsm', diff --git a/browser/components/newtab/tests/xpcshell/test_NewTabPrefsProvider.js b/browser/components/newtab/tests/xpcshell/test_NewTabPrefsProvider.js new file mode 100644 index 000000000000..2d50499051d4 --- /dev/null +++ b/browser/components/newtab/tests/xpcshell/test_NewTabPrefsProvider.js @@ -0,0 +1,51 @@ +"use strict"; + +/* global XPCOMUtils, equal, Preferences, NewTabPrefsProvider, run_next_test */ +/* exported run_test */ +/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */ + +const Cu = Components.utils; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Preferences.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", + "resource:///modules/NewTabPrefsProvider.jsm"); + +function run_test() { + run_next_test(); +} + +add_task(function* test_observe() { + let prefsMap = NewTabPrefsProvider.prefs.prefsMap; + for (let prefName of prefsMap.keys()) { + let prefValueType = prefsMap.get(prefName); + + let beforeVal; + let afterVal; + + switch (prefValueType) { + case "bool": + beforeVal = false; + afterVal = true; + Preferences.set(prefName, beforeVal); + break; + case "localized": + case "str": + beforeVal = ""; + afterVal = "someStr"; + Preferences.set(prefName, beforeVal); + break; + } + NewTabPrefsProvider.prefs.init(); + let promise = new Promise(resolve => { + NewTabPrefsProvider.prefs.once(prefName, (name, data) => { // jshint ignore:line + resolve([name, data]); + }); + }); + Preferences.set(prefName, afterVal); + let [actualName, actualData] = yield promise; + equal(prefName, actualName, `emitter sent the correct pref: ${prefName}`); + equal(afterVal, actualData, `emitter collected correct pref data for ${prefName}`); + NewTabPrefsProvider.prefs.uninit(); + } +}); diff --git a/browser/components/newtab/tests/xpcshell/xpcshell.ini b/browser/components/newtab/tests/xpcshell/xpcshell.ini index 6eb525646088..a9903bba5564 100644 --- a/browser/components/newtab/tests/xpcshell/xpcshell.ini +++ b/browser/components/newtab/tests/xpcshell/xpcshell.ini @@ -5,6 +5,7 @@ firefox-appdir = browser skip-if = toolkit == 'android' || toolkit == 'gonk' [test_AboutNewTabService.js] +[test_NewTabPrefsProvider.js] [test_NewTabURL.js] [test_PlacesProvider.js] [test_RemoteNewTabLocation.js] diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index e803c3cc5d86..8d07060244cb 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -32,6 +32,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "RemoteAboutNewTab", XPCOMUtils.defineLazyModuleGetter(this, "RemoteNewTabUtils", "resource:///modules/RemoteNewTabUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", + "resource:///modules/NewTabPrefsProvider.jsm"); + XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm"); @@ -849,6 +852,7 @@ BrowserGlue.prototype = { RemoteNewTabUtils.init(); RemoteNewTabUtils.links.addProvider(DirectoryLinksProvider); RemoteAboutNewTab.init(); + NewTabPrefsProvider.prefs.init(); SessionStore.init(); BrowserUITelemetry.init(); @@ -1170,6 +1174,7 @@ BrowserGlue.prototype = { WebappManager.uninit(); RemoteAboutNewTab.uninit(); + NewTabPrefsProvider.prefs.uninit(); AboutNewTab.uninit(); #ifdef NIGHTLY_BUILD if (Services.prefs.getBoolPref("dom.identity.enabled")) { diff --git a/browser/extensions/pdfjs/test/browser.ini b/browser/extensions/pdfjs/test/browser.ini index 1856a0472a75..fb4aa9afc2a5 100644 --- a/browser/extensions/pdfjs/test/browser.ini +++ b/browser/extensions/pdfjs/test/browser.ini @@ -1,12 +1,10 @@ [DEFAULT] -support-files = file_pdfjs_test.pdf +support-files = + file_pdfjs_test.pdf + head.js [browser_pdfjs_main.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_navigation.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_savedialog.js] [browser_pdfjs_views.js] -skip-if = e10s # Bug 1159385 [browser_pdfjs_zoom.js] -skip-if = e10s # Bug 1159385 diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_main.js b/browser/extensions/pdfjs/test/browser_pdfjs_main.js index 0abfad679c10..56d03667eeb7 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_main.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_main.js @@ -4,9 +4,7 @@ const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; -function test() { - var tab; - +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -17,82 +15,53 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser: gBrowser, url: TESTROOT + "file_pdfjs_test.pdf" }, + function* (newTabBrowser) { + ok(gBrowser.isFindBarInitialized(), "Browser FindBar initialized!"); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + yield waitForPdfJS(newTabBrowser); - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + yield ContentTask.spawn(newTabBrowser, null, function* () { + // + // Overall sanity tests + // + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, tab, function () { - closePDFViewer(window, finish); + // + // Sidebar: open + // + var sidebar = content.document.querySelector('button#sidebarToggle'), + outerContainer = content.document.querySelector('div#outerContainer'); + + sidebar.click(); + ok(outerContainer.classList.contains('sidebarOpen'), "sidebar opens on click"); + + // + // Sidebar: close + // + sidebar.click(); + ok(!outerContainer.classList.contains('sidebarOpen'), "sidebar closes on click"); + + // + // Page change from prev/next buttons + // + var prevPage = content.document.querySelector('button#previous'), + nextPage = content.document.querySelector('button#next'); + + var pgNumber = content.document.querySelector('input#pageNumber').value; + is(parseInt(pgNumber, 10), 1, 'initial page is 1'); + + // + // Bookmark button + // + var viewBookmark = content.document.querySelector('a#viewBookmark'); + viewBookmark.click(); + + ok(viewBookmark.href.length > 0, "viewBookmark button has href"); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - - -function runTests(document, window, tab, callback) { - - // - // Overall sanity tests - // - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - ok('PDFViewerApplication' in window.wrappedJSObject, - "window content has viewer object"); - - // - // Browser Find - // - ok(gBrowser.isFindBarInitialized(tab), "Browser FindBar initialized!"); - - // - // Sidebar: open - // - var sidebar = document.querySelector('button#sidebarToggle'), - outerContainer = document.querySelector('div#outerContainer'); - - sidebar.click(); - ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); - - // - // Sidebar: close - // - sidebar.click(); - ok(!outerContainer.classList.contains('sidebarOpen'), 'sidebar closes on click'); - - // - // Page change from prev/next buttons - // - var prevPage = document.querySelector('button#previous'), - nextPage = document.querySelector('button#next'); - - var pageNumber = document.querySelector('input#pageNumber'); - is(parseInt(pageNumber.value), 1, 'initial page is 1'); - - // - // Bookmark button - // - var viewBookmark = document.querySelector('a#viewBookmark'); - viewBookmark.click(); - ok(viewBookmark.href.length > 0, 'viewBookmark button has href'); - - callback(); -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js b/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js index a1a501a023f2..c5f2b5279e25 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_navigation.js @@ -139,9 +139,7 @@ const TESTS = [ } ]; -function test() { - var tab; - +add_task(function* test() { let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -151,51 +149,73 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: TESTROOT + "file_pdfjs_test.pdf" }, + function* (newTabBrowser) { + yield waitForPdfJS(newTabBrowser); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - gBrowser.selectedTab = tab; - - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); - - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; - - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, function () { - var pageNumber = document.querySelector('input#pageNumber'); - is(pageNumber.value, pageNumber.max, "Document is left on the last page"); - finish(); + yield ContentTask.spawn(newTabBrowser, null, function* () { + // Check if PDF is opened with internal viewer + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); }); - }, false, true); - }, true); -} -function runTests(document, window, finish) { - // Check if PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); + yield ContentTask.spawn(newTabBrowser, null, contentSetUp); - // Wait for outline items, the start the navigation actions - waitForOutlineItems(document).then(function () { - // The key navigation has to happen in page-fit, otherwise it won't scroll - // trough a complete page - setZoomToPageFit(document).then(function () { - runNextTest(document, window, finish); - }, function () { - ok(false, "Current scale has been set to 'page-fit'"); - finish(); + yield Task.spawn(runTests(newTabBrowser)); + + yield ContentTask.spawn(newTabBrowser, null, function*() { + let pageNumber = content.document.querySelector('input#pageNumber'); + is(pageNumber.value, pageNumber.max, "Document is left on the last page"); + }); }); - }, function () { - ok(false, "Outline items have been found"); - finish(); - }); +}); + +function* contentSetUp() { + /** + * Outline Items gets appended to the document later on we have to + * wait for them before we start to navigate though document + * + * @param document + * @returns {deferred.promise|*} + */ + function waitForOutlineItems(document) { + return new Promise((resolve, reject) => { + document.addEventListener("outlineloaded", function outlineLoaded(evt) { + document.removeEventListener("outlineloaded", outlineLoaded); + var outlineCount = evt.detail.outlineCount; + + if (document.querySelectorAll(".outlineItem").length === outlineCount) { + resolve(); + } else { + reject(); + } + }); + }); + } + + /** + * The key navigation has to happen in page-fit, otherwise it won't scroll + * through a complete page + * + * @param document + * @returns {deferred.promise|*} + */ + function setZoomToPageFit(document) { + return new Promise((resolve) => { + document.addEventListener("pagerendered", function onZoom(e) { + document.removeEventListener("pagerendered", onZoom); + document.querySelector("#viewer").click(); + resolve(); + }); + + var select = document.querySelector("select#scaleSelect"); + select.selectedIndex = 2; + select.dispatchEvent(new Event("change")); + }); + } + + yield waitForOutlineItems(content.document); + yield setZoomToPageFit(content.document); } /** @@ -207,99 +227,55 @@ function runTests(document, window, finish) { * @param test * @param callback */ -function runNextTest(document, window, endCallback) { - var test = TESTS.shift(), - deferred = Promise.defer(), - pageNumber = document.querySelector('input#pageNumber'); +function* runTests(browser) { + yield ContentTask.spawn(browser, TESTS, function* (TESTS) { + let window = content; + let document = window.document; - // Add an event-listener to wait for page to change, afterwards resolve the promise - var timeout = window.setTimeout(() => deferred.reject(), 5000); - window.addEventListener('pagechange', function pageChange() { - if (pageNumber.value == test.expectedPage) { - window.removeEventListener('pagechange', pageChange); - window.clearTimeout(timeout); - deferred.resolve(pageNumber.value); + for (let test of TESTS) { + let deferred = {}; + deferred.promise = new Promise((resolve, reject) => { + deferred.resolve = resolve; + deferred.reject = reject; + }); + + let pageNumber = document.querySelector('input#pageNumber'); + + // Add an event-listener to wait for page to change, afterwards resolve the promise + let timeout = window.setTimeout(() => deferred.reject(), 5000); + window.addEventListener('pagechange', function pageChange() { + if (pageNumber.value == test.expectedPage) { + window.removeEventListener('pagechange', pageChange); + window.clearTimeout(timeout); + deferred.resolve(+pageNumber.value); + } + }); + + // Get the element and trigger the action for changing the page + var el = document.querySelector(test.action.selector); + ok(el, "Element '" + test.action.selector + "' has been found"); + + // The value option is for input case + if (test.action.value) + el.value = test.action.value; + + // Dispatch the event for changing the page + if (test.action.event == "keydown") { + var ev = document.createEvent("KeyboardEvent"); + ev.initKeyEvent("keydown", true, true, null, false, false, false, false, + test.action.keyCode, 0); + el.dispatchEvent(ev); + } + else { + var ev = new Event(test.action.event); + } + el.dispatchEvent(ev); + + let pgNumber = yield deferred.promise; + is(pgNumber, test.expectedPage, test.message); } - }); - // Get the element and trigger the action for changing the page - var el = document.querySelector(test.action.selector); - ok(el, "Element '" + test.action.selector + "' has been found"); - - // The value option is for input case - if (test.action.value) - el.value = test.action.value; - - // Dispatch the event for changing the page - if (test.action.event == "keydown") { - var ev = document.createEvent("KeyboardEvent"); - ev.initKeyEvent("keydown", true, true, null, false, false, false, false, - test.action.keyCode, 0); - el.dispatchEvent(ev); - } - else { - var ev = new Event(test.action.event); - } - el.dispatchEvent(ev); - - - // When the promise gets resolved we call the next test if there are any left - // or else we call the final callback which will end the test - deferred.promise.then(function (pgNumber) { - is(pgNumber, test.expectedPage, test.message); - - if (TESTS.length) - runNextTest(document, window, endCallback); - else - endCallback(); - }, function () { - ok(false, "Test '" + test.message + "' failed with timeout."); - endCallback(); + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); } - -/** - * Outline Items gets appended to the document latter on we have to - * wait for them before we start to navigate though document - * - * @param document - * @returns {deferred.promise|*} - */ -function waitForOutlineItems(document) { - var deferred = Promise.defer(); - document.addEventListener("outlineloaded", function outlineLoaded(evt) { - document.removeEventListener("outlineloaded", outlineLoaded); - var outlineCount = evt.detail.outlineCount; - - if (document.querySelectorAll(".outlineItem").length === outlineCount) { - deferred.resolve(); - } else { - deferred.reject(); - } - }); - - return deferred.promise; -} - -/** - * The key navigation has to happen in page-fit, otherwise it won't scroll - * trough a complete page - * - * @param document - * @returns {deferred.promise|*} - */ -function setZoomToPageFit(document) { - var deferred = Promise.defer(); - document.addEventListener("pagerendered", function onZoom(e) { - document.removeEventListener("pagerendered", onZoom); - document.querySelector("#viewer").click(); - deferred.resolve(); - }); - - var select = document.querySelector("select#scaleSelect"); - select.selectedIndex = 2; - select.dispatchEvent(new Event("change")); - - return deferred.promise; -} - diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_views.js b/browser/extensions/pdfjs/test/browser_pdfjs_views.js index 77e1f737f7f1..ed05ab817030 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_views.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_views.js @@ -4,9 +4,7 @@ const RELATIVE_DIR = "browser/extensions/pdfjs/test/"; const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR; -function test() { - var tab; - +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"].getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); let handlerInfo = mimeService.getFromTypeAndExtension('application/pdf', 'pdf'); @@ -17,70 +15,47 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: TESTROOT + "file_pdfjs_test.pdf" }, + function* (browser) { + // check that PDF is opened with internal viewer + yield waitForPdfJS(browser); - tab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + yield ContentTask.spawn(browser, null, function* () { + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + //open sidebar + var sidebar = content.document.querySelector('button#sidebarToggle'); + var outerContainer = content.document.querySelector('div#outerContainer'); - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - runTests(document, window, function () { - closePDFViewer(window, finish); + sidebar.click(); + ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); + + // check that thumbnail view is open + var thumbnailView = content.document.querySelector('div#thumbnailView'); + var outlineView = content.document.querySelector('div#outlineView'); + + is(thumbnailView.getAttribute('class'), null, 'Initial view is thumbnail view'); + is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden initially'); + + //switch to outline view + var viewOutlineButton = content.document.querySelector('button#viewOutline'); + viewOutlineButton.click(); + + is(thumbnailView.getAttribute('class'), 'hidden', 'Thumbnail view is hidden when outline is selected'); + is(outlineView.getAttribute('class'), '', 'Outline view is visible when selected'); + + //switch back to thumbnail view + var viewThumbnailButton = content.document.querySelector('button#viewThumbnail'); + viewThumbnailButton.click(); + + is(thumbnailView.getAttribute('class'), '', 'Thumbnail view is visible when selected'); + is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden when thumbnail is selected'); + + sidebar.click(); + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - -function runTests(document, window, callback) { - // check that PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - - //open sidebar - var sidebar = document.querySelector('button#sidebarToggle'); - var outerContainer = document.querySelector('div#outerContainer'); - - sidebar.click(); - ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click'); - - // check that thumbnail view is open - var thumbnailView = document.querySelector('div#thumbnailView'); - var outlineView = document.querySelector('div#outlineView'); - - is(thumbnailView.getAttribute('class'), null, 'Initial view is thumbnail view'); - is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden initially'); - - //switch to outline view - var viewOutlineButton = document.querySelector('button#viewOutline'); - viewOutlineButton.click(); - - is(outlineView.getAttribute('class'), '', 'Outline view is visible when selected'); - is(thumbnailView.getAttribute('class'), 'hidden', 'Thumbnail view is hidden when outline is selected'); - - //switch back to thumbnail view - var viewThumbnailButton = document.querySelector('button#viewThumbnail'); - viewThumbnailButton.click(); - - is(thumbnailView.getAttribute('class'), '', 'Thumbnail view is visible when selected'); - is(outlineView.getAttribute('class'), 'hidden', 'Outline view is hidden when thumbnail is selected'); - - sidebar.click(); - - callback(); -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js index 05efa9dfe4ce..11d026f3df08 100644 --- a/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_zoom.js @@ -28,6 +28,7 @@ const TESTS = [ { action: { keyboard: true, + keyCode: 61, event: "+" }, expectedZoom: 1, // 1 - zoom in @@ -37,6 +38,7 @@ const TESTS = [ { action: { keyboard: true, + keyCode: 109, event: "-" }, expectedZoom: -1, // -1 - zoom out @@ -54,11 +56,7 @@ const TESTS = [ } ]; -var initialWidth; // the initial width of the PDF document -var previousWidth; // the width of the PDF document at previous step/test - -function test() { - var tab; +add_task(function* test() { let handlerService = Cc["@mozilla.org/uriloader/handler-service;1"] .getService(Ci.nsIHandlerService); let mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); @@ -72,114 +70,83 @@ function test() { info('Pref action: ' + handlerInfo.preferredAction); - waitForExplicitFinish(); - registerCleanupFunction(function() { - gBrowser.removeTab(tab); - }); + yield BrowserTestUtils.withNewTab({ gBrowser, url: TESTROOT + "file_pdfjs_test.pdf" + "#zoom=100" }, + function* (newTabBrowser) { + yield waitForPdfJS(newTabBrowser); - tab = gBrowser.selectedTab = gBrowser.addTab(TESTROOT + "file_pdfjs_test.pdf"); - var newTabBrowser = gBrowser.getBrowserForTab(tab); + yield ContentTask.spawn(newTabBrowser, TESTS, function* (TESTS) { + let document = content.document; - newTabBrowser.addEventListener("load", function eventHandler() { - newTabBrowser.removeEventListener("load", eventHandler, true); + function waitForRender() { + return new Promise((resolve) => { + document.addEventListener("pagerendered", function onPageRendered(e) { + if(e.detail.pageNumber !== 1) { + return; + } - var document = newTabBrowser.contentDocument, - window = newTabBrowser.contentWindow; + document.removeEventListener("pagerendered", onPageRendered, true); + resolve(); + }, true); + }); + } - // Runs tests after all 'load' event handlers have fired off - window.addEventListener("documentload", function() { - initialWidth = parseInt(document.querySelector("div#pageContainer1").style.width); - previousWidth = initialWidth; - runTests(document, window, function () { - closePDFViewer(window, finish); + // check that PDF is opened with internal viewer + ok(content.document.querySelector('div#viewer'), "document content has viewer UI"); + ok('PDFJS' in content.wrappedJSObject, "window content has PDFJS object"); + + let initialWidth, previousWidth; + initialWidth = previousWidth = + parseInt(content.document.querySelector("div#pageContainer1").style.width); + + for (let test of TESTS) { + // We zoom using an UI element + var ev; + if (test.action.selector) { + // Get the element and trigger the action for changing the zoom + var el = document.querySelector(test.action.selector); + ok(el, "Element '" + test.action.selector + "' has been found"); + + if (test.action.index){ + el.selectedIndex = test.action.index; + } + + // Dispatch the event for changing the zoom + ev = new Event(test.action.event); + } + // We zoom using keyboard + else { + // Simulate key press + ev = new content.KeyboardEvent("keydown", + { key: test.action.event, + keyCode: test.action.keyCode, + ctrlKey: true }); + el = content; + } + + el.dispatchEvent(ev); + yield waitForRender(); + + var pageZoomScale = content.document.querySelector('select#scaleSelect'); + + // The zoom value displayed in the zoom select + var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML; + + let pageContainer = content.document.querySelector('div#pageContainer1'); + let actualWidth = parseInt(pageContainer.style.width); + + // the actual zoom of the PDF document + let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%"; + is(computedZoomValue, zoomValue, "Content has correct zoom"); + + // Check that document zooms in the expected way (in/out) + let zoom = (actualWidth - previousWidth) * test.expectedZoom; + ok(zoom > 0, test.message); + + previousWidth = actualWidth; + } + + var viewer = content.wrappedJSObject.PDFViewerApplication; + yield viewer.close(); }); - }, false, true); - }, true); -} - -function runTests(document, window, callback) { - // check that PDF is opened with internal viewer - ok(document.querySelector('div#viewer'), "document content has viewer UI"); - ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); - - // Start the zooming tests after the document is loaded - waitForDocumentLoad(document).then(function () { - zoomPDF(document, window, TESTS.shift(), callback); - }); -} - -function waitForDocumentLoad(document) { - var deferred = Promise.defer(); - var interval = setInterval(function () { - if (document.querySelector("div#pageContainer1") != null){ - clearInterval(interval); - deferred.resolve(); - } - }, 500); - - return deferred.promise; -} - -function zoomPDF(document, window, test, endCallback) { - var renderedPage; - - document.addEventListener("pagerendered", function onPageRendered(e) { - if(e.detail.pageNumber !== 1) { - return; - } - - document.removeEventListener("pagerendered", onPageRendered, true); - - var pageZoomScale = document.querySelector('select#scaleSelect'); - - // The zoom value displayed in the zoom select - var zoomValue = pageZoomScale.options[pageZoomScale.selectedIndex].innerHTML; - - let pageContainer = document.querySelector('div#pageContainer1'); - let actualWidth = parseInt(pageContainer.style.width); - - // the actual zoom of the PDF document - let computedZoomValue = parseInt(((actualWidth/initialWidth).toFixed(2))*100) + "%"; - is(computedZoomValue, zoomValue, "Content has correct zoom"); - - // Check that document zooms in the expected way (in/out) - let zoom = (actualWidth - previousWidth) * test.expectedZoom; - ok(zoom > 0, test.message); - - // Go to next test (if there is any) or finish - var nextTest = TESTS.shift(); - if (nextTest) { - previousWidth = actualWidth; - zoomPDF(document, window, nextTest, endCallback); - } - else - endCallback(); - }, true); - - // We zoom using an UI element - if (test.action.selector) { - // Get the element and trigger the action for changing the zoom - var el = document.querySelector(test.action.selector); - ok(el, "Element '" + test.action.selector + "' has been found"); - - if (test.action.index){ - el.selectedIndex = test.action.index; - } - - // Dispatch the event for changing the zoom - el.dispatchEvent(new Event(test.action.event)); - } - // We zoom using keyboard - else { - // Simulate key press - EventUtils.synthesizeKey(test.action.event, { ctrlKey: true }); - } -} - -/** - * Destroys PDF.js viewer opened document. - */ -function closePDFViewer(window, callback) { - var viewer = window.wrappedJSObject.PDFViewerApplication; - viewer.close().then(callback); -} + }); +}); diff --git a/browser/extensions/pdfjs/test/head.js b/browser/extensions/pdfjs/test/head.js new file mode 100644 index 000000000000..1997b4612b38 --- /dev/null +++ b/browser/extensions/pdfjs/test/head.js @@ -0,0 +1,11 @@ +function waitForPdfJS(browser) { + // Runs tests after all 'load' event handlers have fired off + return ContentTask.spawn(browser, null, function* () { + yield new Promise((resolve) => { + content.addEventListener("documentload", function listener() { + content.removeEventListener("documentload", listener, false); + resolve(); + }, false, true); + }); + }); +} diff --git a/browser/extensions/shumway/chrome/ShumwayCom.jsm b/browser/extensions/shumway/chrome/ShumwayCom.jsm index b5641d2b6b77..c08ef8fb5106 100644 --- a/browser/extensions/shumway/chrome/ShumwayCom.jsm +++ b/browser/extensions/shumway/chrome/ShumwayCom.jsm @@ -349,7 +349,7 @@ var ShumwayCom = { }, getWeakMapKeys: function (weakMap) { - var keys = Components.utils.nondeterministicGetWeakMapKeys(weakMap); + var keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakMap); var result = new content.Array(); keys.forEach(function (key) { result.push(key); diff --git a/config/config.mk b/config/config.mk index 02b75137bb08..df52066e58b2 100644 --- a/config/config.mk +++ b/config/config.mk @@ -101,7 +101,7 @@ FINAL_TARGET ?= $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin)$(DIS FINAL_TARGET_FROZEN := '$(FINAL_TARGET)' ifdef XPI_NAME -DEFINES += -DXPI_NAME=$(XPI_NAME) +ACDEFINES += -DXPI_NAME=$(XPI_NAME) endif # The VERSION_NUMBER is suffixed onto the end of the DLLs we ship. @@ -533,7 +533,7 @@ sysinstall_cmd = install_cmd # overridden by the command line. (Besides, AB_CD is prettier). AB_CD = $(MOZ_UI_LOCALE) # Many locales directories want this definition. -DEFINES += -DAB_CD=$(AB_CD) +ACDEFINES += -DAB_CD=$(AB_CD) ifndef L10NBASEDIR L10NBASEDIR = $(error L10NBASEDIR not defined by configure) @@ -689,5 +689,3 @@ export CL_INCLUDES_PREFIX # in environment variables to prevent it from breking silently on # non-English systems. export NONASCII - -DEFINES += -DNO_NSPR_10_SUPPORT diff --git a/config/external/nss/Makefile.in b/config/external/nss/Makefile.in index d888143d310d..713c66a3384c 100644 --- a/config/external/nss/Makefile.in +++ b/config/external/nss/Makefile.in @@ -213,7 +213,7 @@ DEFAULT_GMAKE_FLAGS += \ OS_PTHREAD= \ $(NULL) -DEFAULT_GMAKE_FLAGS += ARCHFLAG='$(CFLAGS) -DCHECK_FORK_GETPID $(addprefix -DANDROID_VERSION=,$(ANDROID_VERSION)) -include $(topsrcdir)/security/manager/android_stub.h' +DEFAULT_GMAKE_FLAGS += ARCHFLAG='$(filter-out -W%,$(CFLAGS)) -DCHECK_FORK_GETPID $(addprefix -DANDROID_VERSION=,$(ANDROID_VERSION)) -include $(topsrcdir)/security/manager/android_stub.h' endif endif @@ -241,7 +241,7 @@ ifdef MOZ_NO_WLZDEFS DEFAULT_GMAKE_FLAGS += ZDEFS_FLAG= endif ifdef MOZ_CFLAGS_NSS -DEFAULT_GMAKE_FLAGS += XCFLAGS='$(CFLAGS)' +DEFAULT_GMAKE_FLAGS += XCFLAGS='$(filter-out -W%,$(CFLAGS))' DEFAULT_GMAKE_FLAGS += DARWIN_DYLIB_VERSIONS='-compatibility_version 1 -current_version 1 $(LDFLAGS)' endif ifeq (1_1,$(CLANG_CL)_$(MOZ_ASAN)) @@ -267,7 +267,10 @@ ifdef MOZ_FOLD_LIBS_FLAGS DEFAULT_GMAKE_FLAGS += XCFLAGS='$(MOZ_FOLD_LIBS_FLAGS)' endif -ifeq (1,$(ALLOW_COMPILER_WARNINGS)) +ifndef WARNINGS_AS_ERRORS +DEFAULT_GMAKE_FLAGS += NSS_ENABLE_WERROR=0 +endif +ifeq ($(OS_TARGET),Android) DEFAULT_GMAKE_FLAGS += NSS_ENABLE_WERROR=0 endif diff --git a/config/makefiles/xpidl/Makefile.in b/config/makefiles/xpidl/Makefile.in index 1375e0bd5f76..931807e30501 100644 --- a/config/makefiles/xpidl/Makefile.in +++ b/config/makefiles/xpidl/Makefile.in @@ -35,8 +35,8 @@ process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py %.xpt: @echo "$(@F)" $(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \ - $(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \ - $(dist_include_dir) $(@D) $(idl_deps_dir) $(libxul_sdk_includes) \ + $(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) --depsdir $(idl_deps_dir) \ + $(dist_idl_dir) $(dist_include_dir) $(@D) $(libxul_sdk_includes) \ $(basename $(notdir $@)) $($(basename $(notdir $@))_deps) # When some IDL is added or removed, if the actual IDL file was already, or # still is, in the tree, simple dependencies can't detect that the XPT needs diff --git a/config/tests/src-simple/Makefile.in b/config/tests/src-simple/Makefile.in index aa5de0b0e115..96f9a415408e 100644 --- a/config/tests/src-simple/Makefile.in +++ b/config/tests/src-simple/Makefile.in @@ -13,7 +13,7 @@ include $(topsrcdir)/config/config.mk XPI_NAME = test_jar_mn -DEFINES += \ +ACDEFINES += \ -DAB_CD=ab-X-stuff \ $(NULL) diff --git a/configure.in b/configure.in index 512dffd99205..990f8c80a48e 100644 --- a/configure.in +++ b/configure.in @@ -9172,6 +9172,9 @@ if test -z "$JS_SHARED_LIBRARY"; then fi AC_SUBST(JS_SHARED_LIBRARY) +# Avoid using obsolete NSPR features +AC_DEFINE(NO_NSPR_10_SUPPORT) + MOZ_CREATE_CONFIG_STATUS() if test "$COMPILE_ENVIRONMENT"; then diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js index 1ad4ff10f1e1..7f2ff4b85904 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_variables-view-04.js @@ -138,8 +138,8 @@ function test() { is([...variables].length, 0, "VariablesView should have been emptied."); - is(Cu.nondeterministicGetWeakMapKeys(variables._itemsByElement).length, 0, - "VariablesView _itemsByElement map has been emptied."); + is(ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(variables._itemsByElement).length, + 0, "VariablesView _itemsByElement map has been emptied."); is(variables._currHierarchy.size, 0, "VariablesView _currHierarchy map has been emptied."); is(variables._list.children.length, 0, diff --git a/devtools/shared/heapsnapshot/HeapSnapshot.cpp b/devtools/shared/heapsnapshot/HeapSnapshot.cpp index cbc56c25b0be..e7f0ad304b02 100644 --- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp +++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp @@ -91,7 +91,7 @@ HeapSnapshot::Create(JSContext* cx, ErrorResult& rv) { RefPtr snapshot = new HeapSnapshot(cx, global.GetAsSupports()); - if (!snapshot->init(buffer, size)) { + if (!snapshot->init(cx, buffer, size)) { rv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } @@ -203,7 +203,7 @@ HeapSnapshot::getOrInternString(InternedStringSet& internedStrings, : Nothing()) bool -HeapSnapshot::saveNode(const protobuf::Node& node) +HeapSnapshot::saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents) { // NB: de-duplicated string properties must be read back and interned in the // same order here as they are written and serialized in @@ -248,6 +248,9 @@ HeapSnapshot::saveNode(const protobuf::Node& node) return false; NodeId referent = protoEdge.referent(); + if (NS_WARN_IF(!edgeReferents.put(referent))) + return false; + const char16_t* edgeName = nullptr; if (protoEdge.EdgeNameOrRef_case() != protobuf::Edge::EDGENAMEORREF_NOT_SET) { Maybe edgeNameOrRef = GET_STRING_OR_REF(protoEdge, name); @@ -402,7 +405,7 @@ StreamHasData(GzipInputStream& stream) } bool -HeapSnapshot::init(const uint8_t* buffer, uint32_t size) +HeapSnapshot::init(JSContext* cx, const uint8_t* buffer, uint32_t size) { if (!nodes.init() || !frames.init()) return false; @@ -430,7 +433,12 @@ HeapSnapshot::init(const uint8_t* buffer, uint32_t size) return false; rootId = root.id(); - if (NS_WARN_IF(!saveNode(root))) + // The set of all node ids we've found edges pointing to. + NodeIdSet edgeReferents(cx); + if (NS_WARN_IF(!edgeReferents.init())) + return false; + + if (NS_WARN_IF(!saveNode(root, edgeReferents))) return false; // Finally, the rest of the nodes in the core dump. @@ -439,7 +447,15 @@ HeapSnapshot::init(const uint8_t* buffer, uint32_t size) protobuf::Node node; if (!parseMessage(gzipStream, node)) return false; - if (NS_WARN_IF(!saveNode(node))) + if (NS_WARN_IF(!saveNode(node, edgeReferents))) + return false; + } + + // Check the set of node ids referred to by edges we found and ensure that we + // have the node corresponding to each id. If we don't have all of them, it is + // unsafe to perform analyses of this heap snapshot. + for (auto range = edgeReferents.all(); !range.empty(); range.popFront()) { + if (NS_WARN_IF(!nodes.has(range.front()))) return false; } diff --git a/devtools/shared/heapsnapshot/HeapSnapshot.h b/devtools/shared/heapsnapshot/HeapSnapshot.h index 7a18a3244a60..ec5f6299ac2d 100644 --- a/devtools/shared/heapsnapshot/HeapSnapshot.h +++ b/devtools/shared/heapsnapshot/HeapSnapshot.h @@ -59,11 +59,13 @@ class HeapSnapshot final : public nsISupports // Initialize this HeapSnapshot from the given buffer that contains a // serialized core dump. Do NOT take ownership of the buffer, only borrow it // for the duration of the call. Return false on failure. - bool init(const uint8_t* buffer, uint32_t size); + bool init(JSContext* cx, const uint8_t* buffer, uint32_t size); + + using NodeIdSet = js::HashSet; // Save the given `protobuf::Node` message in this `HeapSnapshot` as a // `DeserializedNode`. - bool saveNode(const protobuf::Node& node); + bool saveNode(const protobuf::Node& node, NodeIdSet& edgeReferents); // Save the given `protobuf::StackFrame` message in this `HeapSnapshot` as a // `DeserializedStackFrame`. The saved stack frame's id is returned via the diff --git a/docshell/resources/content/netError.xhtml b/docshell/resources/content/netError.xhtml index 9b9fe7a631eb..64496bd08bff 100644 --- a/docshell/resources/content/netError.xhtml +++ b/docshell/resources/content/netError.xhtml @@ -5,6 +5,9 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"> %htmlDTD; + + %netErrorAppDTD; %netErrorDTD; diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 7333264fafa9..d4342fc8c696 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -11,6 +11,26 @@ namespace mozilla { namespace dom { /* static */ void +ThreadSafeChromeUtils::NondeterministicGetWeakMapKeys(GlobalObject& aGlobal, + JS::Handle aMap, + JS::MutableHandle aRetval, + ErrorResult& aRv) +{ + if (!aMap.isObject()) { + aRetval.setUndefined(); + } else { + JSContext* cx = aGlobal.Context(); + JS::Rooted objRet(cx); + JS::Rooted mapObj(cx, &aMap.toObject()); + if (!JS_NondeterministicGetWeakMapKeys(cx, mapObj, &objRet)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + } else { + aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue()); + } + } +} + + /* static */ void ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs, nsCString& aSuffix) diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index 915ee030fdd4..1102f16ae83a 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -35,6 +35,11 @@ public: JSContext* cx, const nsAString& filePath, ErrorResult& rv); + + static void NondeterministicGetWeakMapKeys(GlobalObject& aGlobal, + JS::Handle aMap, + JS::MutableHandle aRetval, + ErrorResult& aRv); }; class ChromeUtils : public ThreadSafeChromeUtils diff --git a/dom/base/nsContentList.cpp b/dom/base/nsContentList.cpp index 734c5b0a3e82..b328f5f071e2 100644 --- a/dom/base/nsContentList.cpp +++ b/dom/base/nsContentList.cpp @@ -527,8 +527,9 @@ nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) nsIContent *content = mElements[i]; // XXX Should this pass eIgnoreCase? if (content && - (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, - name, eCaseMatters) || + ((content->IsHTMLElement() && + content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, + name, eCaseMatters)) || content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, name, eCaseMatters))) { return content->AsElement(); diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 4414b71cc8a5..0b6e90934e6d 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -3641,7 +3641,7 @@ nsDOMWindowUtils::SetChromeMargin(int32_t aTop, nsCOMPtr widget; baseWindow->GetMainWidget(getter_AddRefs(widget)); if (widget) { - nsIntMargin margins(aTop, aRight, aBottom, aLeft); + LayoutDeviceIntMargin margins(aTop, aRight, aBottom, aLeft); return widget->SetNonClientMargins(margins); } } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 992b9fb62474..15379bb34c1e 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -13325,7 +13325,8 @@ nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton, aError.Throw(NS_ERROR_FAILURE); return; } - nsIntRect buttonRect = frame->GetScreenRect(); + LayoutDeviceIntRect buttonRect = + LayoutDeviceIntRect::FromUnknownRect(frame->GetScreenRect()); // Get the widget rect in screen coordinates. nsIWidget *widget = GetNearestWidget(); @@ -13333,8 +13334,8 @@ nsGlobalWindow::NotifyDefaultButtonLoaded(Element& aDefaultButton, aError.Throw(NS_ERROR_FAILURE); return; } - nsIntRect widgetRect; - aError = widget->GetScreenBoundsUntyped(widgetRect); + LayoutDeviceIntRect widgetRect; + aError = widget->GetScreenBounds(widgetRect); if (aError.Failed()) { return; } diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index a42c75c0fa8b..b8dc9cb08b1f 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -42,7 +42,7 @@ function getIntPref(prefName, def) { function handleWindowEvent(e) { if (this._browserElementParents) { - let beps = Cu.nondeterministicGetWeakMapKeys(this._browserElementParents); + let beps = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(this._browserElementParents); beps.forEach(bep => bep._handleOwnerEvent(e)); } } diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index ec4b84890c5f..5517bec192e6 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -933,34 +933,37 @@ protected: { } - void DoCreateImageBitmapFromBlob(ErrorResult& aRv) + // Returns true on success, false on failure. + bool DoCreateImageBitmapFromBlob() { - RefPtr imageBitmap = CreateImageBitmap(aRv); + RefPtr imageBitmap = CreateImageBitmap(); // handle errors while creating ImageBitmap // (1) error occurs during reading of the object // (2) the image data is not in a supported file format // (3) the image data is corrupted // All these three cases should reject promise with null value - if (aRv.Failed()) { - mPromise->MaybeReject(aRv); - return; + if (!imageBitmap) { + return false; } if (imageBitmap && mCropRect.isSome()) { - imageBitmap->SetPictureRect(mCropRect.ref(), aRv); + ErrorResult rv; + imageBitmap->SetPictureRect(mCropRect.ref(), rv); - if (aRv.Failed()) { - mPromise->MaybeReject(aRv); - return; + if (rv.Failed()) { + mPromise->MaybeReject(rv); + return false; } } mPromise->MaybeResolve(imageBitmap); - return; + return true; } - virtual already_AddRefed CreateImageBitmap(ErrorResult& aRv) = 0; + // Will return null on failure. In that case, mPromise will already + // be rejected with the right thing. + virtual already_AddRefed CreateImageBitmap() = 0; RefPtr mPromise; nsCOMPtr mGlobalObject; @@ -982,17 +985,18 @@ public: NS_IMETHOD Run() override { - ErrorResult error; - DoCreateImageBitmapFromBlob(error); + DoCreateImageBitmapFromBlob(); return NS_OK; } private: - already_AddRefed CreateImageBitmap(ErrorResult& aRv) override + already_AddRefed CreateImageBitmap() override { - RefPtr data = DecodeAndCropBlob(*mBlob, mCropRect, aRv); + ErrorResult rv; + RefPtr data = DecodeAndCropBlob(*mBlob, mCropRect, rv); - if (NS_WARN_IF(aRv.Failed())) { + if (NS_WARN_IF(rv.Failed())) { + mPromise->MaybeReject(rv); return nullptr; } @@ -1054,23 +1058,22 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - ErrorResult error; - DoCreateImageBitmapFromBlob(error); - return !(error.Failed()); + return DoCreateImageBitmapFromBlob(); } private: - already_AddRefed CreateImageBitmap(ErrorResult& aRv) override + already_AddRefed CreateImageBitmap() override { RefPtr data; + ErrorResult rv; RefPtr task = new DecodeBlobInMainThreadSyncTask(mWorkerPrivate, *mBlob, mCropRect, - aRv, getter_AddRefs(data)); + rv, getter_AddRefs(data)); task->Dispatch(mWorkerPrivate->GetJSContext()); // This is a synchronous call. - if (NS_WARN_IF(aRv.Failed())) { - mPromise->MaybeReject(aRv); + if (NS_WARN_IF(rv.Failed())) { + mPromise->MaybeReject(rv); return nullptr; } diff --git a/dom/geolocation/nsGeolocationSettings.cpp b/dom/geolocation/nsGeolocationSettings.cpp index 2270cadb2572..2f1554cc224e 100644 --- a/dom/geolocation/nsGeolocationSettings.cpp +++ b/dom/geolocation/nsGeolocationSettings.cpp @@ -260,6 +260,7 @@ nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& // if it is an app that is always precise, skip it nsAutoJSString origin; if (!origin.init(cx, id)) { + JS_ClearPendingException(cx); // catch and ignore any exceptions continue; } if (mAlwaysPreciseApps.Contains(origin)) { @@ -268,7 +269,8 @@ nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& // get the app setting object JS::RootedValue propertyValue(cx); - if (!JS_GetPropertyById(cx, obj, id, &propertyValue) || !propertyValue.isObject()) { + if (!JS_GetPropertyById(cx, obj, id, &propertyValue)) { + JS_ClearPendingException(cx); // catch and ignore any exceptions continue; } JS::RootedObject settingObj(cx, &propertyValue.toObject()); @@ -280,6 +282,8 @@ nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& JS::RootedValue fm(cx); if (JS_GetProperty(cx, settingObj, "type", &fm)) { settings->HandleTypeChange(fm); + } else { + JS_ClearPendingException(cx); // catch and ignore any exceptions } #ifdef MOZ_APPROX_LOCATION @@ -287,6 +291,8 @@ nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& JS::RootedValue distance(cx); if (JS_GetProperty(cx, settingObj, "distance", &distance)) { settings->HandleApproxDistanceChange(distance); + } else { + JS_ClearPendingException(cx); // catch and ignore any exceptions } #endif @@ -294,6 +300,8 @@ nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& JS::RootedValue coords(cx); if (JS_GetProperty(cx, settingObj, "coords", &coords)) { settings->HandleFixedCoordsChange(coords); + } else { + JS_ClearPendingException(cx); // catch and ignore any exceptions } // add the per-app setting object to the hashtable @@ -337,11 +345,13 @@ nsGeolocationSettings::HandleGeolocationAlwaysPreciseChange(const JS::Value& aVa JS::RootedValue value(cx); if (!JS_GetElement(cx, obj, i, &value) || !value.isString()) { + JS_ClearPendingException(cx); // catch and ignore any exceptions continue; } nsAutoJSString origin; if (!origin.init(cx, value)) { + JS_ClearPendingException(cx); // catch and ignore any exceptions continue; } diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index 6031d0f534c7..efcb3f1c5671 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -1235,7 +1235,7 @@ nsresult HTMLMediaElement::LoadResource() #ifdef MOZ_EME if (mMediaKeys && - !IsMediaStreamURI(mLoadingSrc) && + !IsMediaSourceURI(mLoadingSrc) && Preferences::GetBool("media.eme.mse-only", true)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl index 60af28f094f3..33f5e6637d3c 100644 --- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -28,7 +28,13 @@ interface nsIServiceWorkerInfo : nsISupports readonly attribute DOMString cacheName; }; -[scriptable, builtinclass, uuid(e908bc07-0a4c-443f-a546-2109bf1f4A01)] +[scriptable, uuid(87e63548-d440-4b8a-b158-65ad1de0211E)] +interface nsIServiceWorkerRegistrationInfoListener : nsISupports +{ + void onChange(); +}; + +[scriptable, builtinclass, uuid(72faba24-0a1b-4284-bad3-d44c044d6d95)] interface nsIServiceWorkerRegistrationInfo : nsISupports { readonly attribute nsIPrincipal principal; @@ -39,6 +45,10 @@ interface nsIServiceWorkerRegistrationInfo : nsISupports readonly attribute nsIServiceWorkerInfo installingWorker; readonly attribute nsIServiceWorkerInfo waitingWorker; readonly attribute nsIServiceWorkerInfo activeWorker; + + void addListener(in nsIServiceWorkerRegistrationInfoListener listener); + + void removeListener(in nsIServiceWorkerRegistrationInfoListener listener); }; [scriptable, uuid(9e523e7c-ad6f-4df0-8077-c74aebbc679d)] diff --git a/dom/locales/en-US/chrome/netError.dtd b/dom/locales/en-US/chrome/netError.dtd index b53350579022..8adc21f27028 100644 --- a/dom/locales/en-US/chrome/netError.dtd +++ b/dom/locales/en-US/chrome/netError.dtd @@ -92,6 +92,12 @@

These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.

"> + +You should not add an exception if you are using an internet connection that you do not trust completely or if you are not used to seeing a warning for this server.

+

If you still wish to add an exception for this site, you can do so in your advanced encryption settings.

+"> + The browser prevented this page from loading in this way because the page has a content security policy that disallows it.

"> @@ -100,10 +106,3 @@
  • Please contact the website owners to inform them of this problem.

"> - - - -%netErrorAppDTD; diff --git a/dom/locales/en-US/chrome/netErrorApp.dtd b/dom/locales/en-US/chrome/netErrorApp.dtd index 54e8da32b6d9..d245555380ca 100644 --- a/dom/locales/en-US/chrome/netErrorApp.dtd +++ b/dom/locales/en-US/chrome/netErrorApp.dtd @@ -2,12 +2,22 @@ - 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/. --> - + + + diff --git a/dom/media/test/mochitest.ini b/dom/media/test/mochitest.ini index b72caa903a64..2eda57c85064 100644 --- a/dom/media/test/mochitest.ini +++ b/dom/media/test/mochitest.ini @@ -629,6 +629,8 @@ skip-if = toolkit == 'android' # bug 1149374 skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351 [test_eme_requestKeySystemAccess.html] skip-if = toolkit == 'android' # bug 1149374 +[test_eme_setMediaKeys_before_attach_MediaSource.html] +skip-if = toolkit == 'android' # bug 1149374 [test_eme_stream_capture_blocked_case1.html] tags=msg capturestream skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351 diff --git a/dom/media/test/test_eme_setMediaKeys_before_attach_MediaSource.html b/dom/media/test/test_eme_setMediaKeys_before_attach_MediaSource.html new file mode 100644 index 000000000000..95ebb55d468d --- /dev/null +++ b/dom/media/test/test_eme_setMediaKeys_before_attach_MediaSource.html @@ -0,0 +1,41 @@ + + + + Test Encrypted Media Extensions + + + + + + +
+
+
+ + diff --git a/dom/quota/QuotaManager.cpp b/dom/quota/QuotaManager.cpp index 9bbc94fbcce4..4b7379c6bcd3 100644 --- a/dom/quota/QuotaManager.cpp +++ b/dom/quota/QuotaManager.cpp @@ -2526,35 +2526,30 @@ QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType, } } -// static -PLDHashOperator -QuotaManager::RemoveQuotaCallback(const nsACString& aKey, - nsAutoPtr& aValue, - void* aUserArg) -{ - NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); - NS_ASSERTION(aValue, "Null pointer!"); - - RefPtr groupInfo = - aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); - if (groupInfo) { - groupInfo->LockedRemoveOriginInfos(); - } - - groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); - if (groupInfo) { - groupInfo->LockedRemoveOriginInfos(); - } - - return PL_DHASH_REMOVE; -} - void QuotaManager::RemoveQuota() { MutexAutoLock lock(mQuotaMutex); - mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr); + for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr& pair = iter.Data(); + + MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!"); + MOZ_ASSERT(pair, "Null pointer!"); + + RefPtr groupInfo = + pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + groupInfo->LockedRemoveOriginInfos(); + } + + groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); + if (groupInfo) { + groupInfo->LockedRemoveOriginInfos(); + } + + iter.Remove(); + } NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!"); } diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index c68eb76e2ec4..094315019c80 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -461,11 +461,6 @@ private: const nsACString& aOrigin, nsAutoCString& _retval); - static PLDHashOperator - RemoveQuotaCallback(const nsACString& aKey, - nsAutoPtr& aValue, - void* aUserArg); - mozilla::Mutex mQuotaMutex; nsClassHashtable mGroupInfoPairs; diff --git a/dom/webidl/ThreadSafeChromeUtils.webidl b/dom/webidl/ThreadSafeChromeUtils.webidl index de1ae2661765..7a4a71d8ad56 100644 --- a/dom/webidl/ThreadSafeChromeUtils.webidl +++ b/dom/webidl/ThreadSafeChromeUtils.webidl @@ -31,6 +31,18 @@ interface ThreadSafeChromeUtils { */ [Throws, NewObject] static HeapSnapshot readHeapSnapshot(DOMString filePath); + + /** + * Return the keys in a weak map. This operation is + * non-deterministic because it is affected by the scheduling of the + * garbage collector and the cycle collector. + * + * @param aMap weak map or other JavaScript value + * @returns If aMap is a weak map object, return the keys of the weak + * map as an array. Otherwise, return undefined. + */ + [Throws, NewObject] + static any nondeterministicGetWeakMapKeys(any map); }; /** diff --git a/dom/workers/ServiceWorkerContainer.cpp b/dom/workers/ServiceWorkerContainer.cpp index cb66f24b8dbe..63cdc35edeec 100644 --- a/dom/workers/ServiceWorkerContainer.cpp +++ b/dom/workers/ServiceWorkerContainer.cpp @@ -158,12 +158,6 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL, } } - - if (!baseURI) { - aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); - return nullptr; - } - nsresult rv; nsCOMPtr scriptURI; rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI); diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 799a2a098a29..e9816149a1b3 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -436,6 +436,36 @@ ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult) return NS_OK; } +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::AddListener( + nsIServiceWorkerRegistrationInfoListener *aListener) +{ + AssertIsOnMainThread(); + + if (!aListener || mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.AppendElement(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::RemoveListener( + nsIServiceWorkerRegistrationInfoListener *aListener) +{ + AssertIsOnMainThread(); + + if (!aListener || !mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.RemoveElement(aListener); + + return NS_OK; +} + already_AddRefed ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) { @@ -970,6 +1000,7 @@ public: } mRegistration->mScriptSpec = mScriptSpec; + mRegistration->NotifyListenersOnChange(); swm->StoreRegistration(mPrincipal, mRegistration); } else { MOZ_ASSERT(mJobType == UPDATE_JOB); @@ -1162,6 +1193,7 @@ public: mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget(); mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing); + mRegistration->NotifyListenersOnChange(); Succeed(); // The job should NOT call fail from this point on. @@ -1329,6 +1361,7 @@ private: mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget(); mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed); + mRegistration->NotifyListenersOnChange(); swm->InvalidateServiceWorkerRegistrationWorker(mRegistration, WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER); @@ -1648,6 +1681,7 @@ ServiceWorkerRegistrationInfo::Activate() mActiveWorker = activatingWorker.forget(); mWaitingWorker = nullptr; mActiveWorker->UpdateState(ServiceWorkerState::Activating); + NotifyListenersOnChange(); // FIXME(nsm): Unlink appcache if there is one. @@ -2462,6 +2496,15 @@ ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const return false; } +void +ServiceWorkerRegistrationInfo::NotifyListenersOnChange() +{ + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnChange(); + } +} + void ServiceWorkerManager::LoadRegistration( const ServiceWorkerRegistrationData& aRegistration) @@ -3883,7 +3926,7 @@ ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener) { AssertIsOnMainThread(); - if (mListeners.Contains(aListener)) { + if (!aListener || mListeners.Contains(aListener)) { return NS_ERROR_INVALID_ARG; } @@ -3897,7 +3940,7 @@ ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener) { AssertIsOnMainThread(); - if (!mListeners.Contains(aListener)) { + if (!aListener || !mListeners.Contains(aListener)) { return NS_ERROR_INVALID_ARG; } diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 7c48f1c61687..217aeb0d9809 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -72,6 +72,8 @@ public: RefPtr mWaitingWorker; RefPtr mInstallingWorker; + nsTArray> mListeners; + uint64_t mLastUpdateCheckTime; // When unregister() is called on a registration, it is not immediately @@ -138,6 +140,9 @@ public: bool IsLastUpdateCheckTimeOverOneDay() const; + + void + NotifyListenersOnChange(); }; class ServiceWorkerUpdateFinishCallback diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini index 60a1b6a4eff1..d69307536107 100644 --- a/dom/workers/test/serviceworkers/chrome.ini +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -3,11 +3,15 @@ skip-if = buildapp == 'b2g' || os == 'android' support-files = app/* app2/* + chrome_helpers.js serviceworkermanager_iframe.html + serviceworkerregistrationinfo_iframe.html worker.js + worker2.js [test_aboutserviceworkers.html] skip-if = true #bug 1193319 [test_app_installation.html] [test_privateBrowsing.html] [test_serviceworkermanager.xul] +[test_serviceworkerregistrationinfo.xul] diff --git a/dom/workers/test/serviceworkers/chrome_helpers.js b/dom/workers/test/serviceworkers/chrome_helpers.js new file mode 100644 index 000000000000..315019c3fdf1 --- /dev/null +++ b/dom/workers/test/serviceworkers/chrome_helpers.js @@ -0,0 +1,59 @@ +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Task.jsm"); + +let swm = Cc["@mozilla.org/serviceworkers/manager;1"]. + getService(Ci.nsIServiceWorkerManager); + +let EXAMPLE_URL = "https://example.com/chrome/dom/workers/test/serviceworkers/"; + +function waitForIframeLoad(iframe) { + return new Promise(function (resolve) { + iframe.onload = resolve; + }); +} + +function waitForRegister(scope, callback) { + return new Promise(function (resolve) { + let listener = { + onRegister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(callback ? callback(registration) : registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForUnregister(scope) { + return new Promise(function (resolve) { + let listener = { + onUnregister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForServiceWorkerRegistrationChange(registration, callback) { + return new Promise(function (resolve) { + let listener = { + onChange: function () { + registration.removeListener(listener); + if (callback) { + callback(); + } + resolve(callback ? callback() : undefined); + } + }; + registration.addListener(listener); + }); +} diff --git a/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html index a0a2de76030e..0df93da96d4e 100644 --- a/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html +++ b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html @@ -9,13 +9,21 @@ } var promise = navigator.serviceWorker.register("worker.js"); window.onmessage = function (event) { - if (event.data !== "unregister") { + if (event.data !== "register") { return; } - promise.then(function (registration) { - registration.unregister(); + promise = promise.then(function (registration) { + return navigator.serviceWorker.register("worker2.js"); }); - window.onmessage = null; + window.onmessage = function (event) { + if (event.data !== "unregister") { + return; + } + promise.then(function (registration) { + registration.unregister(); + }); + window.onmessage = null; + }; }; }; diff --git a/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html new file mode 100644 index 000000000000..e18af92d161d --- /dev/null +++ b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html @@ -0,0 +1,25 @@ + + + + + + + + This is a test page. + + diff --git a/dom/workers/test/serviceworkers/test_serviceworkermanager.xul b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul index cbb4c29e711c..ead935a3c4f4 100644 --- a/dom/workers/test/serviceworkers/test_serviceworkermanager.xul +++ b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul @@ -8,53 +8,10 @@ onload="test();"> + + +

+ +

+    
+  
+