diff --git a/browser/base/content/test/performance/browser.ini b/browser/base/content/test/performance/browser.ini index 5909b57e369e..da50d74a19f3 100644 --- a/browser/base/content/test/performance/browser.ini +++ b/browser/base/content/test/performance/browser.ini @@ -1,5 +1,8 @@ [DEFAULT] prefs = + # Skip migration work in BG__migrateUI for browser_startup.js since it isn't + # representative of common startup. + browser.migration.version=9999999 browser.startup.record=true support-files = head.js diff --git a/browser/components/migration/tests/marionette/test_refresh_firefox.py b/browser/components/migration/tests/marionette/test_refresh_firefox.py index fca0e2c4ce07..c5f8a4648c1f 100644 --- a/browser/components/migration/tests/marionette/test_refresh_firefox.py +++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py @@ -44,7 +44,7 @@ class TestFirefoxRefresh(MarionetteTestCase): Services.logins.addLogin(myLogin) """, script_args=(self._username, self._password)) - def createBookmark(self): + def createBookmarkInMenu(self): self.marionette.execute_script(""" let url = arguments[0]; let title = arguments[1]; @@ -52,6 +52,14 @@ class TestFirefoxRefresh(MarionetteTestCase): makeURI(url), 0, title); """, script_args=(self._bookmarkURL, self._bookmarkText)) + def createBookmarksOnToolbar(self): + self.marionette.execute_script(""" + for (let i = 1; i <= 5; i++) { + PlacesUtils.bookmarks.insertBookmark(PlacesUtils.toolbarFolderId, + makeURI(`about:rights?p=${i}`), 0, `Bookmark ${i}`); + } + """) + def createHistory(self): error = self.runAsyncCode(""" // Copied from PlacesTestUtils, which isn't available in Marionette tests. @@ -200,7 +208,7 @@ class TestFirefoxRefresh(MarionetteTestCase): # Note that we expect 2 logins - one from us, one from sync. self.assertEqual(loginCount, 2, "No other logins are present") - def checkBookmark(self): + def checkBookmarkInMenu(self): titleInBookmarks = self.marionette.execute_script(""" let url = arguments[0]; let bookmarkIds = PlacesUtils.bookmarks.getBookmarkIdsForURI(makeURI(url), {}, {}); @@ -208,6 +216,14 @@ class TestFirefoxRefresh(MarionetteTestCase): """, script_args=(self._bookmarkURL,)) self.assertEqual(titleInBookmarks, self._bookmarkText) + def checkBookmarkToolbarVisibility(self): + toolbarVisible = self.marionette.execute_script(""" + const BROWSER_DOCURL = "chrome://browser/content/browser.xul"; + let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore); + return xulStore.getValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed") + """) + self.assertEqual(toolbarVisible, "false") + def checkHistory(self): historyResult = self.runAsyncCode(""" PlacesUtils.history.fetch(arguments[0]).then(pageInfo => { @@ -378,18 +394,20 @@ class TestFirefoxRefresh(MarionetteTestCase): def checkProfile(self, hasMigrated=False): self.checkPassword() - self.checkBookmark() + self.checkBookmarkInMenu() self.checkHistory() self.checkFormHistory() self.checkFormAutofill() self.checkCookie() self.checkSync(hasMigrated); if hasMigrated: + self.checkBookmarkToolbarVisibility() self.checkSession() def createProfileData(self): self.savePassword() - self.createBookmark() + self.createBookmarkInMenu() + self.createBookmarksOnToolbar() self.createHistory() self.createFormHistory() self.createFormAutofill() diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index ce6237b06d94..225057d1c0cd 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1743,6 +1743,35 @@ BrowserGlue.prototype = { this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback); }, + /** + * Uncollapses PersonalToolbar if its collapsed status is not + * persisted, and user customized it or changed default bookmarks. + * + * If the user does not have a persisted value for the toolbar's + * "collapsed" attribute, try to determine whether it's customized. + */ + _maybeToggleBookmarkToolbarVisibility() { + const BROWSER_DOCURL = "chrome://browser/content/browser.xul"; + const NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE = 3; + let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore); + + if (!xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed")) { + // We consider the toolbar customized if it has more than NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE + // children, or if it has a persisted currentset value. + let toolbarIsCustomized = xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "currentset"); + let getToolbarFolderCount = () => { + let toolbarFolder = PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root; + let toolbarChildCount = toolbarFolder.childCount; + toolbarFolder.containerOpen = false; + return toolbarChildCount; + }; + + if (toolbarIsCustomized || getToolbarFolderCount() > NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE) { + xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false"); + } + } + }, + // eslint-disable-next-line complexity _migrateUI: function BG__migrateUI() { const UI_VERSION = 58; @@ -1754,6 +1783,15 @@ BrowserGlue.prototype = { } else { // This is a new profile, nothing to migrate. Services.prefs.setIntPref("browser.migration.version", UI_VERSION); + + try { + // New profiles may have existing bookmarks (imported from another browser or + // copied into the profile) and we want to show the bookmark toolbar for them + // in some cases. + this._maybeToggleBookmarkToolbarVisibility(); + } catch (ex) { + Cu.reportError(ex); + } return; } diff --git a/browser/components/tests/browser/browser.ini b/browser/components/tests/browser/browser.ini index d5b9a651a7e4..e99fef91dd8b 100644 --- a/browser/components/tests/browser/browser.ini +++ b/browser/components/tests/browser/browser.ini @@ -4,3 +4,4 @@ skip-if = !updater reason = test depends on update channel [browser_contentpermissionprompt.js] +[browser_default_bookmark_toolbar_visibility.js] diff --git a/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js b/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js new file mode 100644 index 000000000000..0e79967171a9 --- /dev/null +++ b/browser/components/tests/browser/browser_default_bookmark_toolbar_visibility.js @@ -0,0 +1,18 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test _maybeToggleBookmarkToolbarVisibility() code running for new profiles. + * Ensure that the bookmarks toolbar is hidden in a default configuration. + * If new default bookmarks are added to the toolbar then the threshold of > 3 + * in NUM_TOOLBAR_BOOKMARKS_TO_UNHIDE may need to be adjusted there. + */ +add_task(async function test_default_bookmark_toolbar_visibility() { + const BROWSER_DOCURL = "chrome://browser/content/browser.xul"; + let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore); + + is(xulStore.getValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed"), "", + "Check that @collapsed isn't persisted"); + ok(document.getElementById("PersonalToolbar").collapsed, + "The bookmarks toolbar should be collapsed by default"); +}); diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 43f0bf9e5553..a895fe06e88a 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -355,7 +355,15 @@ flashActivate.noAllow.accesskey=D flashActivate.allow.accesskey=A # in-page UI -PluginClickToActivate=Activate %S. +# LOCALIZATION NOTE (PluginClickToActivate2): Two changes were done to the +# previous version of the string. The first is that we changed the wording from +# "Activate" to "Run", because it's shorter and feels less technical in English. +# Feel free to keep using the previous wording in your language if it's already +# the best one. +# The second change is that we removed the period at the end of the phrase, because +# it's not natural in our UI, and the underline was removed from this, so it doesn't +# look like a link anymore. We suggest that everyone removes that period too. +PluginClickToActivate2=Run %S PluginVulnerableUpdatable=This plugin is vulnerable and should be updated. PluginVulnerableNoUpdate=This plugin has security vulnerabilities. diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm index abd2aca1771b..539bdcdac4cc 100644 --- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -575,7 +575,7 @@ PluginContent.prototype = { case "PluginClickToPlay": this._handleClickToPlayEvent(plugin); let pluginName = this._getPluginInfo(plugin).pluginName; - let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate", [pluginName], 1); + let messageString = gNavigatorBundle.formatStringFromName("PluginClickToActivate2", [pluginName], 1); let overlayText = this.getPluginUI(plugin, "clickToPlay"); overlayText.textContent = messageString; if (eventType == "PluginVulnerableUpdatable" || diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4 index 20af6ad7d372..9023b8684a9b 100644 --- a/build/autoconf/arch.m4 +++ b/build/autoconf/arch.m4 @@ -31,7 +31,7 @@ if test -z "$MOZ_ARCH"; then arm-Android) MOZ_THUMB=yes MOZ_ARCH=armv7-a - MOZ_FPU=vfp + MOZ_FPU=vfpv3-d16 MOZ_FLOAT_ABI=softfp MOZ_ALIGN=no ;; diff --git a/devtools/server/actors/highlighters.css b/devtools/server/actors/highlighters.css index 82fbbfa023da..f0374e03a2b3 100644 --- a/devtools/server/actors/highlighters.css +++ b/devtools/server/actors/highlighters.css @@ -598,6 +598,10 @@ /* Shapes highlighter */ +:-moz-native-anonymous .shapes-root { + pointer-events: auto; +} + :-moz-native-anonymous .shapes-shape-container { position: absolute; overflow: visible; diff --git a/devtools/server/actors/highlighters/shapes.js b/devtools/server/actors/highlighters/shapes.js index 80d652dea351..59c7b8300261 100644 --- a/devtools/server/actors/highlighters/shapes.js +++ b/devtools/server/actors/highlighters/shapes.js @@ -214,6 +214,35 @@ class ShapesHighlighter extends AutoRefreshHighlighter { }; } + /** + * Changes the appearance of the mouse cursor on the highlighter. + * + * Because we can't attach event handlers to individual elements in the + * highlighter, we determine if the mouse is hovering over a point by seeing if + * it's within 5 pixels of it. This creates a square hitbox that doesn't match + * perfectly with the circular markers. So if we were to use the :hover + * pseudo-class to apply changes to the mouse cursor, the cursor change would not + * always accurately reflect whether you can interact with the point. This is + * also the reason we have the hidden marker-hover element instead of using CSS + * to fill in the marker. + * + * In addition, the cursor CSS property is applied to .shapes-root because if + * it were attached to .shapes-marker, the cursor change no longer applies if + * you are for example resizing the shape and your mouse goes off the point. + * Also, if you are dragging a polygon point, the marker plays catch up to your + * mouse position, resulting in an undesirable visual effect where the cursor + * rapidly flickers between "grab" and "auto". + * + * @param {String} cursorType the name of the cursor to display + */ + setCursor(cursorType) { + let container = this.getElement("root"); + let style = container.getAttribute("style"); + // remove existing cursor definitions in the style + style = style.replace(/cursor:.*?;/g, ""); + container.setAttribute("style", `${style}cursor:${cursorType};`); + } + handleEvent(event, id) { // No event handling if the highlighter is hidden if (this.areShapesHidden()) { @@ -265,6 +294,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { case "mouseup": if (this[_dragging]) { this[_dragging] = null; + this._handleMarkerHover(this.hoveredPoint); } break; case "mousemove": @@ -674,6 +704,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { let ratioX = (valueX / xComputed) || 1; let ratioY = (valueY / yComputed) || 1; + this.setCursor("grabbing"); this[_dragging] = { point, unitX, unitY, valueX, valueY, ratioX, ratioY, x: pageX, y: pageY }; } @@ -752,6 +783,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { return; } + this.setCursor("grabbing"); if (point === "center") { let { cx, cy } = this.coordUnits; let cxComputed = this.coordinates.cx / 100 * width; @@ -833,6 +865,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { return; } + this.setCursor("grabbing"); if (point === "center") { let { cx, cy } = this.coordUnits; let cxComputed = this.coordinates.cx / 100 * width; @@ -932,6 +965,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { return; } + this.setCursor("grabbing"); let value = this.coordUnits[point]; let size = (point === "left" || point === "right") ? width : height; let computedValue = this.coordinates[point] / 100 * size; @@ -1021,40 +1055,51 @@ class ShapesHighlighter extends AutoRefreshHighlighter { } } + /** + * Change the appearance of the given marker when the mouse hovers over it. + * @param {String|Number} point if the shape is a polygon, the integer index of the + * point being hovered. Otherwise, a string identifying the point being hovered. + * Integers < 0 and falsey values excluding 0 indicate no point is being hovered. + */ _handleMarkerHover(point) { // Hide hover marker for now, will be shown if point is a valid hover target this.getElement("marker-hover").setAttribute("hidden", true); - if (point === null || point === undefined) { + // Catch all falsey values except when point === 0, as that's a valid point + if (!point && point !== 0) { + this.setCursor("auto"); return; } + let hoverCursor = (this[_dragging]) ? "grabbing" : "grab"; if (this.transformMode) { - if (!point) { - return; - } let { minX, minY, maxX, maxY } = this.boundingBox; let centerX = (minX + maxX) / 2; let centerY = (minY + maxY) / 2; const points = [ - { pointName: "translate", x: centerX, y: centerY }, - { pointName: "scale-se", x: maxX, y: maxY }, - { pointName: "scale-ne", x: maxX, y: minY }, - { pointName: "scale-sw", x: minX, y: maxY }, - { pointName: "scale-nw", x: minX, y: minY }, + { pointName: "translate", x: centerX, y: centerY, cursor: "move" }, + { pointName: "scale-se", x: maxX, y: maxY, cursor: "nwse-resize" }, + { pointName: "scale-ne", x: maxX, y: minY, cursor: "nesw-resize" }, + { pointName: "scale-sw", x: minX, y: maxY, cursor: "nesw-resize" }, + { pointName: "scale-nw", x: minX, y: minY, cursor: "nwse-resize" }, ]; - for (let { pointName, x, y } of points) { + for (let { pointName, x, y, cursor } of points) { if (point === pointName) { this._drawHoverMarker([[x, y]]); + this.setCursor(cursor); } } } else if (this.shapeType === "polygon") { if (point === -1) { + this.setCursor("auto"); return; } + this.setCursor(hoverCursor); this._drawHoverMarker([this.coordinates[point]]); } else if (this.shapeType === "circle") { + this.setCursor(hoverCursor); + let { cx, cy, rx } = this.coordinates; if (point === "radius") { this._drawHoverMarker([[cx + rx, cy]]); @@ -1062,6 +1107,8 @@ class ShapesHighlighter extends AutoRefreshHighlighter { this._drawHoverMarker([[cx, cy]]); } } else if (this.shapeType === "ellipse") { + this.setCursor(hoverCursor); + if (point === "center") { let { cx, cy } = this.coordinates; this._drawHoverMarker([[cx, cy]]); @@ -1073,9 +1120,7 @@ class ShapesHighlighter extends AutoRefreshHighlighter { this._drawHoverMarker([[cx, cy + ry]]); } } else if (this.shapeType === "inset") { - if (!point) { - return; - } + this.setCursor(hoverCursor); let { top, right, bottom, left } = this.coordinates; let centerX = (left + (100 - right)) / 2; @@ -1803,12 +1848,12 @@ class ShapesHighlighter extends AutoRefreshHighlighter { this._updateInsetShape(width, height, zoom); } - this._handleMarkerHover(this.hoveredPoint); - let { width: winWidth, height: winHeight } = this._winDimensions; root.removeAttribute("hidden"); root.setAttribute("style", - `position:absolute; width:${winWidth}px;height:${winHeight}px; overflow:hidden`); + `position:absolute; width:${winWidth}px;height:${winHeight}px; overflow:hidden;`); + + this._handleMarkerHover(this.hoveredPoint); setIgnoreLayoutChanges(false, this.highlighterEnv.window.document.documentElement); diff --git a/dom/animation/test/style/file_composite.html b/dom/animation/test/style/file_composite.html index ac8717733204..7489c17350b9 100644 --- a/dom/animation/test/style/file_composite.html +++ b/dom/animation/test/style/file_composite.html @@ -33,14 +33,15 @@ promise_test(t => { }, 'Ensure document has been loaded'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate({ transform: ['translateX(0px)', 'translateX(200px)'], + composite: 'accumulate' }, + 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate({ transform: ['translateX(0px)', 'translateX(200px)'], - composite: 'accumulate' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); @@ -50,17 +51,18 @@ promise_test(t => { }, 'Accumulate onto the base value'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + div.animate({ transform: ['translateX(100px)', 'translateX(200px)'], + composite: 'replace' }, + 100 * MS_PER_SEC); + div.animate({ transform: ['translateX(0px)', 'translateX(100px)'], + composite: 'accumulate' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - div.animate({ transform: ['translateX(100px)', 'translateX(200px)'], - composite: 'replace' }, - 100 * MS_PER_SEC); - div.animate({ transform: ['translateX(0px)', 'translateX(100px)'], - composite: 'accumulate' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); @@ -70,14 +72,15 @@ promise_test(t => { }, 'Accumulate onto an underlying animation value'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate([{ transform: 'translateX(100px)', composite: 'accumulate' }, + { transform: 'translateX(300px)', composite: 'replace' }], + 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate([{ transform: 'translateX(100px)', composite: 'accumulate' }, - { transform: 'translateX(300px)', composite: 'replace' }], - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); @@ -87,14 +90,14 @@ promise_test(t => { }, 'Composite when mixing accumulate and replace'); promise_test(t => { - useTestRefreshMode(t); - - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate([{ transform: 'translateX(100px)', composite: 'replace' }, - { transform: 'translateX(300px)' }], - { duration: 100 * MS_PER_SEC, composite: 'accumulate' }); - - return waitForPaintsFlushed().then(() => { + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate([{ transform: 'translateX(100px)', composite: 'replace' }, + { transform: 'translateX(300px)' }], + { duration: 100 * MS_PER_SEC, composite: 'accumulate' }); + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); @@ -105,14 +108,16 @@ promise_test(t => { 'effect'); promise_test(t => { - useTestRefreshMode(t); + var div; + var anim; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + div.animate({ transform: [ 'scale(2)', 'scale(2)' ] }, 100 * MS_PER_SEC); + anim = div.animate({ transform: [ 'scale(4)', 'scale(4)' ] }, + { duration: 100 * MS_PER_SEC, composite: 'add' }); - var div = addDiv(t); - div.animate({ transform: [ 'scale(2)', 'scale(2)' ] }, 100 * MS_PER_SEC); - var anim = div.animate({ transform: [ 'scale(4)', 'scale(4)' ] }, - { duration: 100 * MS_PER_SEC, composite: 'add' }); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); assert_matrix_equals(transform, 'matrix(8, 0, 0, 8, 0, 0)', diff --git a/dom/animation/test/style/file_missing-keyframe-on-compositor.html b/dom/animation/test/style/file_missing-keyframe-on-compositor.html index d0b7dc93326b..6661aca30f3a 100644 --- a/dom/animation/test/style/file_missing-keyframe-on-compositor.html +++ b/dom/animation/test/style/file_missing-keyframe-on-compositor.html @@ -35,12 +35,12 @@ promise_test(t => { }, 'Ensure document has been loaded'); promise_test(t => { - useTestRefreshMode(t); - - var div = addDiv(t, { style: 'opacity: 0.1' }); - div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0.1' }); + div.animate({ opacity: 1 }, 100 * MS_PER_SEC); + return waitForPaintsFlushed(); + }).then(() => { var opacity = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'opacity'); assert_equals(opacity, '0.1', @@ -49,13 +49,14 @@ promise_test(t => { }, 'Initial opacity value for animation with no no keyframe at offset 0'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0.1' }); + div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); + div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'opacity: 0.1' }); - div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); - div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var opacity = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'opacity'); assert_equals(opacity, '0.5', @@ -66,16 +67,16 @@ promise_test(t => { 'there is a lower-priority animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0.1; transition: opacity 100s linear' }); + getComputedStyle(div).opacity; - var div = - addDiv(t, { style: 'opacity: 0.1; transition: opacity 100s linear' }); - getComputedStyle(div).opacity; + div.style.opacity = '0.5'; + div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - div.style.opacity = '0.5'; - div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var opacity = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'opacity'); assert_equals(opacity, '0.1', @@ -86,12 +87,13 @@ promise_test(t => { 'there is a transition on the same property'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0' }); + div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'opacity: 0' }); - div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var opacity = @@ -103,13 +105,14 @@ promise_test(t => { }, 'Opacity value for animation with no keyframe at offset 1 at 50% '); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0' }); + div.animate({ opacity: [ 0.5, 0.5 ] }, 100 * MS_PER_SEC); + div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'opacity: 0' }); - div.animate({ opacity: [ 0.5, 0.5 ] }, 100 * MS_PER_SEC); - div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var opacity = @@ -122,16 +125,16 @@ promise_test(t => { 'there is a lower-priority animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'opacity: 0; transition: opacity 100s linear' }); + getComputedStyle(div).opacity; - var div = - addDiv(t, { style: 'opacity: 0; transition: opacity 100s linear' }); - getComputedStyle(div).opacity; + div.style.opacity = '0.5'; + div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - div.style.opacity = '0.5'; - div.animate([{ offset: 0, opacity: 1 }], 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var opacity = @@ -144,13 +147,15 @@ promise_test(t => { 'there is a transition on the same property'); promise_test(t => { - useTestRefreshMode(t); + var div; + var lowerAnimation; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + lowerAnimation = div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); + var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); - var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { lowerAnimation.pause(); return waitForPaintsFlushed(); }).then(() => { @@ -169,13 +174,15 @@ promise_test(t => { 'composed onto a paused underlying animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + var lowerAnimation; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + lowerAnimation = div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); + var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = div.animate({ opacity: [ 0.5, 1 ] }, 100 * MS_PER_SEC); - var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { lowerAnimation.playbackRate = 0; return waitForPaintsFlushed(); }).then(() => { @@ -194,13 +201,15 @@ promise_test(t => { 'composed onto a zero playback rate underlying animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + var lowerAnimation; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + lowerAnimation = div.animate({ opacity: [ 1, 0.5 ] }, 100 * MS_PER_SEC); + var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = div.animate({ opacity: [ 1, 0.5 ] }, 100 * MS_PER_SEC); - var higherAnimation = div.animate({ opacity: 1 }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { lowerAnimation.effect.timing.duration = 0; lowerAnimation.effect.timing.fill = 'forwards'; return waitForPaintsFlushed(); @@ -220,12 +229,13 @@ promise_test(t => { 'composed onto a zero active duration underlying animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate({ transform: 'translateX(200px)' }, 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate({ transform: 'translateX(200px)' }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 100, 0)', @@ -234,14 +244,15 @@ promise_test(t => { }, 'Initial transform value for animation with no keyframe at offset 0'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate({ transform: [ 'translateX(200px)', 'translateX(300px)' ] }, + 100 * MS_PER_SEC); + div.animate({ transform: 'translateX(400px)' }, 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate({ transform: [ 'translateX(200px)', 'translateX(300px)' ] }, - 100 * MS_PER_SEC); - div.animate({ transform: 'translateX(400px)' }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 200, 0)', @@ -251,17 +262,17 @@ promise_test(t => { 'there is a lower-priority animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px);' + + 'transition: transform 100s linear' }); + getComputedStyle(div).transform; - var div = - addDiv(t, { style: 'transform: translateX(100px);' + - 'transition: transform 100s linear' }); - getComputedStyle(div).transform; + div.style.transform = 'translateX(200px)'; + div.animate({ transform: 'translateX(400px)' }, 100 * MS_PER_SEC); - div.style.transform = 'translateX(200px)'; - div.animate({ transform: 'translateX(400px)' }, 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 100, 0)', @@ -272,13 +283,14 @@ promise_test(t => { 'there is a transition'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate([{ offset: 0, transform: 'translateX(200pX)' }], + 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate([{ offset: 0, transform: 'translateX(200pX)' }], - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = @@ -289,15 +301,16 @@ promise_test(t => { }, 'Transform value for animation with no keyframe at offset 1 at 50%'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate({ transform: [ 'translateX(200px)', 'translateX(200px)' ] }, + 100 * MS_PER_SEC); + div.animate([{ offset: 0, transform: 'translateX(300px)' }], + 100 * MS_PER_SEC); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate({ transform: [ 'translateX(200px)', 'translateX(200px)' ] }, - 100 * MS_PER_SEC); - div.animate([{ offset: 0, transform: 'translateX(300px)' }], - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = @@ -309,18 +322,18 @@ promise_test(t => { 'there is a lower-priority animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px);' + + 'transition: transform 100s linear' }); + getComputedStyle(div).transform; - var div = - addDiv(t, { style: 'transform: translateX(100px);' + - 'transition: transform 100s linear' }); - getComputedStyle(div).transform; + div.style.transform = 'translateX(200px)'; + div.animate([{ offset: 0, transform: 'translateX(300px)' }], + 100 * MS_PER_SEC); - div.style.transform = 'translateX(200px)'; - div.animate([{ offset: 0, transform: 'translateX(300px)' }], - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = @@ -333,16 +346,18 @@ promise_test(t => { 'there is a transition'); promise_test(t => { - useTestRefreshMode(t); + var div; + var lowerAnimation; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + 100 * MS_PER_SEC); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - 100 * MS_PER_SEC); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { lowerAnimation.pause(); return waitForPaintsFlushed(); }).then(() => { @@ -361,16 +376,18 @@ promise_test(t => { 'composed onto a paused underlying animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + var lowerAnimation; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + 100 * MS_PER_SEC); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - 100 * MS_PER_SEC); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { lowerAnimation.playbackRate = 0; return waitForPaintsFlushed(); }).then(() => { @@ -389,17 +406,18 @@ promise_test(t => { 'composed onto a zero playback rate underlying animation'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + var lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + { duration: 10 * MS_PER_SEC, + fill: 'forwards' }); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - { duration: 10 * MS_PER_SEC, - fill: 'forwards' }); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); // We need to wait for a paint so that we can send the state of the lower @@ -417,18 +435,19 @@ promise_test(t => { 'composed onto a underlying animation with fill:forwards'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + var lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + { duration: 10 * MS_PER_SEC, + endDelay: -5 * MS_PER_SEC, + fill: 'forwards' }); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - { duration: 10 * MS_PER_SEC, - endDelay: -5 * MS_PER_SEC, - fill: 'forwards' }); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); // We need to wait for a paint just like the above test. @@ -446,18 +465,19 @@ promise_test(t => { 'endDelay'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + var lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + { duration: 10 * MS_PER_SEC, + endDelay: 100 * MS_PER_SEC, + fill: 'forwards' }); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - { duration: 10 * MS_PER_SEC, - endDelay: 100 * MS_PER_SEC, - fill: 'forwards' }); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = @@ -472,13 +492,14 @@ promise_test(t => { 'endDelay'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate({ transform: 'translateX(200px)' }, + { duration: 100 * MS_PER_SEC, delay: 50 * MS_PER_SEC }); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); - div.animate({ transform: 'translateX(200px)' }, - { duration: 100 * MS_PER_SEC, delay: 50 * MS_PER_SEC }); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(100 * MS_PER_SEC); var transform = @@ -491,16 +512,17 @@ promise_test(t => { 'positive delay'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t, { style: 'transform: translateX(100px)' }); - var div = addDiv(t, { style: 'transform: translateX(100px)' }); + div.animate([{ offset: 0, transform: 'translateX(200px)'}], + { duration: 100 * MS_PER_SEC, + iterationStart: 1, + iterationComposite: 'accumulate' }); - div.animate([{ offset: 0, transform: 'translateX(200px)'}], - { duration: 100 * MS_PER_SEC, - iterationStart: 1, - iterationComposite: 'accumulate' }); - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { var transform = SpecialPowers.DOMWindowUtils.getOMTAStyle(div, 'transform'); assert_matrix_equals(transform, 'matrix(1, 0, 0, 1, 300, 0)', @@ -511,20 +533,21 @@ promise_test(t => { 'iterationComposite is accumulate'); promise_test(t => { - useTestRefreshMode(t); + var div; + return useTestRefreshMode(t).then(() => { + div = addDiv(t); + var lowerAnimation = + div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, + 100 * MS_PER_SEC); + var higherAnimation = div.animate({ transform: 'translateX(300px)' }, + 100 * MS_PER_SEC); - var div = addDiv(t); - var lowerAnimation = - div.animate({ transform: [ 'translateX(100px)', 'translateX(200px)' ] }, - 100 * MS_PER_SEC); - var higherAnimation = div.animate({ transform: 'translateX(300px)' }, - 100 * MS_PER_SEC); + lowerAnimation.timeline = null; + // Set current time at 50% duration. + lowerAnimation.currentTime = 50 * MS_PER_SEC; - lowerAnimation.timeline = null; - // Set current time at 50% duration. - lowerAnimation.currentTime = 50 * MS_PER_SEC; - - return waitForPaintsFlushed().then(() => { + return waitForPaintsFlushed(); + }).then(() => { SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(50 * MS_PER_SEC); var transform = diff --git a/dom/animation/test/testcommon.js b/dom/animation/test/testcommon.js index b4a0449bf20c..ca81cdbb4a48 100644 --- a/dom/animation/test/testcommon.js +++ b/dom/animation/test/testcommon.js @@ -306,9 +306,24 @@ function waitForDocumentLoad() { * Enters test refresh mode, and restores the mode when |t| finishes. */ function useTestRefreshMode(t) { - SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); - t.add_cleanup(() => { - SpecialPowers.DOMWindowUtils.restoreNormalRefresh(); + function ensureNoSuppressedPaints() { + return new Promise(resolve => { + function checkSuppressedPaints() { + if (!SpecialPowers.DOMWindowUtils.paintingSuppressed) { + resolve(); + } else { + window.requestAnimationFrame(checkSuppressedPaints); + } + } + checkSuppressedPaints(); + }); + } + + return ensureNoSuppressedPaints().then(() => { + SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(0); + t.add_cleanup(() => { + SpecialPowers.DOMWindowUtils.restoreNormalRefresh(); + }); }); } diff --git a/dom/base/DOMParser.cpp b/dom/base/DOMParser.cpp index 666a0bcb7173..2027ca034125 100644 --- a/dom/base/DOMParser.cpp +++ b/dom/base/DOMParser.cpp @@ -357,8 +357,8 @@ DOMParser::Init(nsIPrincipal* principal, nsIURI* documentURI, mBaseURI = baseURI; - NS_POSTCONDITION(mPrincipal, "Must have principal"); - NS_POSTCONDITION(mDocumentURI, "Must have document URI"); + MOZ_ASSERT(mPrincipal, "Must have principal"); + MOZ_ASSERT(mDocumentURI, "Must have document URI"); return NS_OK; } diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index a53223415e25..7602cb1c9335 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1888,10 +1888,10 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent, // XXXbz script execution during binding can trigger some of these // postcondition asserts.... But we do want that, since things will // generally be quite broken when that happens. - NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document"); - NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); - NS_POSTCONDITION(aBindingParent == GetBindingParent(), - "Bound to wrong binding parent"); + MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document"); + MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent"); + MOZ_ASSERT(aBindingParent == GetBindingParent(), + "Bound to wrong binding parent"); return NS_OK; } @@ -2223,7 +2223,7 @@ Element::SetInlineStyleDeclaration(DeclarationBlock* aDeclaration, const nsAString* aSerialized, bool aNotify) { - NS_NOTYETIMPLEMENTED("Element::SetInlineStyleDeclaration"); + MOZ_ASSERT_UNREACHABLE("Element::SetInlineStyleDeclaration"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/base/nsGenericDOMDataNode.cpp b/dom/base/nsGenericDOMDataNode.cpp index 0491e880f29d..a0fb9155ab04 100644 --- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -570,10 +570,10 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, UpdateEditableState(false); - NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document"); - NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); - NS_POSTCONDITION(aBindingParent == GetBindingParent(), - "Bound to wrong binding parent"); + MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document"); + MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent"); + MOZ_ASSERT(aBindingParent == GetBindingParent(), + "Bound to wrong binding parent"); return NS_OK; } diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 001ce57a90c8..d8dfe093040e 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1284,8 +1284,8 @@ NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsINode) nsresult nsINode::GetEventTargetParent(EventChainPreVisitor& aVisitor) { - // This is only here so that we can use the NS_DECL_NSIDOMTARGET macro - NS_ABORT(); + MOZ_ASSERT_UNREACHABLE("GetEventTargetParent is only here so that we can " + "use the NS_DECL_NSIDOMTARGET macro"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/base/nsNameSpaceManager.cpp b/dom/base/nsNameSpaceManager.cpp index 0e4202793239..3b9a13224560 100644 --- a/dom/base/nsNameSpaceManager.cpp +++ b/dom/base/nsNameSpaceManager.cpp @@ -110,7 +110,7 @@ nsNameSpaceManager::RegisterNameSpace(const nsAString& aURI, } } - NS_POSTCONDITION(aNameSpaceID >= -1, "Bogus namespace ID"); + MOZ_ASSERT(aNameSpaceID >= -1, "Bogus namespace ID"); return rv; } @@ -159,11 +159,11 @@ nsNameSpaceManager::GetNameSpaceID(nsAtom* aURI, && mDisabledURIToIDTable.Get(aURI, &nameSpaceID) && ((mMathMLDisabled && kNameSpaceID_disabled_MathML == nameSpaceID) || (mSVGDisabled && kNameSpaceID_disabled_SVG == nameSpaceID))) { - NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID"); + MOZ_ASSERT(nameSpaceID >= 0, "Bogus namespace ID"); return nameSpaceID; } if (mURIToIDTable.Get(aURI, &nameSpaceID)) { - NS_POSTCONDITION(nameSpaceID >= 0, "Bogus namespace ID"); + MOZ_ASSERT(nameSpaceID >= 0, "Bogus namespace ID"); return nameSpaceID; } diff --git a/dom/base/nsNodeInfoManager.cpp b/dom/base/nsNodeInfoManager.cpp index 57433147f794..9724277bfa56 100644 --- a/dom/base/nsNodeInfoManager.cpp +++ b/dom/base/nsNodeInfoManager.cpp @@ -459,7 +459,7 @@ nsNodeInfoManager::RemoveNodeInfo(NodeInfo *aNodeInfo) #endif PL_HashTableRemove(mNodeInfoHash, &aNodeInfo->mInner); - NS_POSTCONDITION(ret, "Can't find mozilla::dom::NodeInfo to remove!!!"); + MOZ_ASSERT(ret, "Can't find mozilla::dom::NodeInfo to remove!!!"); } bool diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 0ae1a0731571..271a30e93fbf 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5370,9 +5370,7 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( // Get any existing transforms on the context, including transformations used // for context shadow. Matrix matrix = tempTarget->GetTransform(); - gfxMatrix contextMatrix; - contextMatrix = gfxMatrix(matrix._11, matrix._12, matrix._21, - matrix._22, matrix._31, matrix._32); + gfxMatrix contextMatrix = ThebesMatrix(matrix); gfxSize contextScale(contextMatrix.ScaleFactors(true)); // Scale the dest rect to include the context scale. @@ -5393,10 +5391,10 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( gfxDevCrash(LogReason::InvalidContext) << "Canvas context problem"; return; } - context->SetMatrix(contextMatrix. - PreScale(1.0 / contextScale.width, - 1.0 / contextScale.height). - PreTranslate(aDest.x - aSrc.x, aDest.y - aSrc.y)); + context->SetMatrixDouble(contextMatrix. + PreScale(1.0 / contextScale.width, + 1.0 / contextScale.height). + PreTranslate(aDest.x - aSrc.x, aDest.y - aSrc.y)); // FLAG_CLAMP is added for increased performance, since we never tile here. uint32_t modifiedFlags = aImage.mDrawingFlags | imgIContainer::FLAG_CLAMP; @@ -5603,8 +5601,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow, double aX, thebes = gfxContext::CreateOrNull(mTarget); MOZ_ASSERT(thebes); // already checked the draw target above // (in SupportsAzureContentForDrawTarget) - thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, - matrix._22, matrix._31, matrix._32)); + thebes->SetMatrix(matrix); } else { IntSize dtSize = IntSize::Ceil(sw, sh); if (!Factory::AllowedSurfaceSize(dtSize)) { @@ -5621,7 +5618,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindowInner& aWindow, double aX, thebes = gfxContext::CreateOrNull(drawDT); MOZ_ASSERT(thebes); // alrady checked the draw target above - thebes->SetMatrix(gfxMatrix::Scaling(matrix._11, matrix._22)); + thebes->SetMatrix(Matrix::Scaling(matrix._11, matrix._22)); } nsCOMPtr shell = presContext->PresShell(); diff --git a/dom/canvas/DocumentRendererChild.cpp b/dom/canvas/DocumentRendererChild.cpp index 2668a5071d98..4936d2d31bb3 100644 --- a/dom/canvas/DocumentRendererChild.cpp +++ b/dom/canvas/DocumentRendererChild.cpp @@ -95,7 +95,7 @@ DocumentRendererChild::RenderDocument(nsPIDOMWindowOuter* window, } RefPtr ctx = gfxContext::CreateOrNull(dt); MOZ_ASSERT(ctx); // already checked the draw target above - ctx->SetMatrix(mozilla::gfx::ThebesMatrix(transform)); + ctx->SetMatrix(transform); nsCOMPtr shell = presContext->PresShell(); shell->RenderDocument(documentRect, renderFlags, bgColor, ctx); diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 22e20f6448b1..a7198b073b19 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -1294,10 +1294,11 @@ HTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild, } *firstSubmitSlot = aChild; } - NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements || - mDefaultSubmitElement == mFirstSubmitNotInElements || - !mDefaultSubmitElement, - "What happened here?"); + + MOZ_ASSERT(mDefaultSubmitElement == mFirstSubmitInElements || + mDefaultSubmitElement == mFirstSubmitNotInElements || + !mDefaultSubmitElement, + "What happened here?"); // Notify that the state of the previous default submit element has changed // if the element which is the default submit element has changed. The new @@ -1430,9 +1431,9 @@ HTMLFormElement::HandleDefaultSubmitRemoval() mFirstSubmitInElements : mFirstSubmitNotInElements; } - NS_POSTCONDITION(mDefaultSubmitElement == mFirstSubmitInElements || - mDefaultSubmitElement == mFirstSubmitNotInElements, - "What happened here?"); + MOZ_ASSERT(mDefaultSubmitElement == mFirstSubmitInElements || + mDefaultSubmitElement == mFirstSubmitNotInElements, + "What happened here?"); // Notify about change if needed. if (mDefaultSubmitElement) { diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index ce833a5b4f69..8179d1538cc8 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -6937,7 +6937,7 @@ HTMLInputElement::GetValueMode() const case NS_FORM_INPUT_DATETIME_LOCAL: return VALUE_MODE_VALUE; default: - NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()"); + MOZ_ASSERT_UNREACHABLE("Unexpected input type in GetValueMode()"); return VALUE_MODE_VALUE; #else // DEBUG default: @@ -6985,7 +6985,7 @@ HTMLInputElement::DoesReadOnlyApply() const case NS_FORM_INPUT_DATETIME_LOCAL: return true; default: - NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()"); + MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadOnlyApply()"); return true; #else // DEBUG default: @@ -7025,7 +7025,7 @@ HTMLInputElement::DoesRequiredApply() const case NS_FORM_INPUT_DATETIME_LOCAL: return true; default: - NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()"); + MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesRequiredApply()"); return true; #else // DEBUG default: @@ -7075,7 +7075,7 @@ HTMLInputElement::DoesMinMaxApply() const case NS_FORM_INPUT_COLOR: return false; default: - NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()"); + MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesRequiredApply()"); return false; #else // DEBUG default: @@ -7115,7 +7115,7 @@ HTMLInputElement::DoesAutocompleteApply() const case NS_FORM_INPUT_FILE: return false; default: - NS_NOTYETIMPLEMENTED("Unexpected input type in DoesAutocompleteApply()"); + MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesAutocompleteApply()"); return false; #else // DEBUG default: diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 33c0481895a5..8cf52dda6b03 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -2384,8 +2384,7 @@ ContentParent::InitInternal(ProcessPriority aInitialPriority, // purpose. If the decision is made to permanently rely on the pref, this // should be changed so that it is required to restart firefox for the change // of value to take effect. - shouldSandbox = (GetEffectiveContentSandboxLevel() > 0) && - !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX"); + shouldSandbox = IsContentSandboxEnabled(); #ifdef XP_LINUX if (shouldSandbox) { diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp index 181284ac14a2..88a32f847303 100644 --- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -28,14 +28,6 @@ namespace mozilla { namespace dom { #if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX) -static bool -IsSandboxTempDirRequired() -{ - // On Windows, a sandbox-writable temp directory is only used - // when sandbox pref level >= 1. - return GetEffectiveContentSandboxLevel() >= 1; -} - static void SetTmpEnvironmentVariable(nsIFile* aValue) { @@ -55,13 +47,6 @@ SetTmpEnvironmentVariable(nsIFile* aValue) #endif #if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX) -static bool -IsSandboxTempDirRequired() -{ - // On OSX, use the sandbox-writable temp when the pref level >= 1. - return (GetEffectiveContentSandboxLevel() >= 1); -} - static void SetTmpEnvironmentVariable(nsIFile* aValue) { @@ -81,7 +66,9 @@ SetUpSandboxEnvironment() MOZ_ASSERT(nsDirectoryService::gService, "SetUpSandboxEnvironment relies on nsDirectoryService being initialized"); - if (!IsSandboxTempDirRequired()) { + // On macOS and Windows, a sandbox-writable temp directory is used whenever + // the sandbox is enabled. + if (!IsContentSandboxEnabled()) { return; } diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 3cf4c4831b6d..d5148f1f62fd 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -3482,7 +3482,7 @@ TabChild::AllocPPluginWidgetChild() #ifdef XP_WIN return new mozilla::plugins::PluginWidgetChild(); #else - MOZ_ASSERT_UNREACHABLE(); + MOZ_ASSERT_UNREACHABLE("AllocPPluginWidgetChild only supports Windows"); return nullptr; #endif } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index cd5fdd9b9bb5..d33898972d65 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -3139,7 +3139,7 @@ TabParent::AllocPPluginWidgetParent() #ifdef XP_WIN return new mozilla::plugins::PluginWidgetParent(); #else - MOZ_ASSERT_UNREACHABLE(); + MOZ_ASSERT_UNREACHABLE("AllocPPluginWidgetParent only supports Windows"); return nullptr; #endif } diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index 9885c4daeab8..828e7c68fceb 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -64,32 +64,38 @@ static nsTHashtable> gSessions; class MediaRecorderReporter final : public nsIMemoryReporter { public: - NS_DECL_THREADSAFE_ISUPPORTS - MediaRecorderReporter() {}; - static MediaRecorderReporter* UniqueInstance(); - void InitMemoryReporter(); - static void AddMediaRecorder(MediaRecorder *aRecorder) { - GetRecorders().AppendElement(aRecorder); + if (!sUniqueInstance) { + sUniqueInstance = MakeAndAddRef(); + RegisterWeakAsyncMemoryReporter(sUniqueInstance); + } + sUniqueInstance->mRecorders.AppendElement(aRecorder); } static void RemoveMediaRecorder(MediaRecorder *aRecorder) { - RecordersArray& recorders = GetRecorders(); - recorders.RemoveElement(aRecorder); - if (recorders.IsEmpty()) { + if (!sUniqueInstance) { + return; + } + + sUniqueInstance->mRecorders.RemoveElement(aRecorder); + if (sUniqueInstance->mRecorders.IsEmpty()) { + UnregisterWeakMemoryReporter(sUniqueInstance); sUniqueInstance = nullptr; } } + NS_DECL_THREADSAFE_ISUPPORTS + + MediaRecorderReporter() = default; + NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize) override { - RecordersArray& recorders = GetRecorders(); nsTArray> promises; - for (const RefPtr& recorder: recorders) { + for (const RefPtr& recorder: mRecorders) { promises.AppendElement(recorder->SizeOfExcludingThis(MallocSizeOf)); } @@ -98,6 +104,12 @@ public: MediaRecorder::SizeOfPromise::All(GetCurrentThreadSerialEventTarget(), promises) ->Then(GetCurrentThreadSerialEventTarget(), __func__, [handleReport, data](const nsTArray& sizes) { + nsCOMPtr manager = + do_GetService("@mozilla.org/memory-reporter-manager;1"); + if (!manager) { + return; + } + size_t sum = 0; for (const size_t& size : sizes) { sum += size; @@ -108,6 +120,8 @@ public: KIND_HEAP, UNITS_BYTES, sum, NS_LITERAL_CSTRING("Memory used by media recorder."), data); + + manager->EndReport(); }, [](size_t) { MOZ_CRASH("Unexpected reject"); }); @@ -116,14 +130,15 @@ public: private: MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) - virtual ~MediaRecorderReporter(); - static StaticRefPtr sUniqueInstance; - typedef nsTArray RecordersArray; - static RecordersArray& GetRecorders() + + virtual ~MediaRecorderReporter() { - return UniqueInstance()->mRecorders; + MOZ_ASSERT(mRecorders.IsEmpty(), "All recorders must have been removed"); } - RecordersArray mRecorders; + + static StaticRefPtr sUniqueInstance; + + nsTArray> mRecorders; }; NS_IMPL_ISUPPORTS(MediaRecorderReporter, nsIMemoryReporter); @@ -1750,24 +1765,5 @@ MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) StaticRefPtr MediaRecorderReporter::sUniqueInstance; -MediaRecorderReporter* MediaRecorderReporter::UniqueInstance() -{ - if (!sUniqueInstance) { - sUniqueInstance = new MediaRecorderReporter(); - sUniqueInstance->InitMemoryReporter(); - } - return sUniqueInstance; - } - -void MediaRecorderReporter::InitMemoryReporter() -{ - RegisterWeakAsyncMemoryReporter(this); -} - -MediaRecorderReporter::~MediaRecorderReporter() -{ - UnregisterWeakMemoryReporter(this); -} - } // namespace dom } // namespace mozilla diff --git a/dom/media/test/crashtests/1411322.html b/dom/media/test/crashtests/1411322.html new file mode 100644 index 000000000000..772b68f0cc9b --- /dev/null +++ b/dom/media/test/crashtests/1411322.html @@ -0,0 +1,18 @@ + + + + Bug 1411322: Shutdown after getting memory reports from MediaRecorder + + + + + +
+
+
+ + diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index dbc1b5d40bd2..99a585457d8c 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -96,6 +96,7 @@ load 1378826.html load 1384248.html load 1389304.html load 1393272.webm +load 1411322.html load disconnect-wrong-destination.html load analyser-channels-1.html load audiocontext-double-suspend.html diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index f04ce5deef3e..b34c466472d3 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2644,8 +2644,8 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, // Renderer::Draw() draws a rectangle with top-left at the aContext origin. gfxContextAutoSaveRestore autoSR(aContext); - aContext->SetMatrix( - aContext->CurrentMatrix().PreTranslate(pluginRect.TopLeft())); + aContext->SetMatrixDouble( + aContext->CurrentMatrixDouble().PreTranslate(pluginRect.TopLeft())); Renderer renderer(window, this, pluginSize, pluginDirtyRect); diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp index d7883f0fb332..e3b5c247409e 100644 --- a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp +++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp @@ -793,7 +793,7 @@ nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource, if (mUpdateBatchNest) return NS_OK; - NS_NOTYETIMPLEMENTED("write me"); + MOZ_ASSERT_UNREACHABLE("write me"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/gfx/gl/GLContextProviderGLX.cpp b/gfx/gl/GLContextProviderGLX.cpp index 3ca9a6b4f5a8..c7acae0225bb 100644 --- a/gfx/gl/GLContextProviderGLX.cpp +++ b/gfx/gl/GLContextProviderGLX.cpp @@ -474,7 +474,7 @@ GLXLibrary::AfterGLXCall() const sErrorEvent.mError.request_code, sErrorEvent.mError.minor_code, sErrorEvent.mError.serial); - NS_ABORT(); + MOZ_ASSERT_UNREACHABLE("AfterGLXCall sErrorEvent"); } XSetErrorHandler(sOldErrorHandler); } @@ -1087,4 +1087,3 @@ GLContextProviderGLX::Shutdown() } /* namespace gl */ } /* namespace mozilla */ - diff --git a/gfx/layers/SyncObject.cpp b/gfx/layers/SyncObject.cpp index c6ea3c34f66b..1c4c122e0293 100644 --- a/gfx/layers/SyncObject.cpp +++ b/gfx/layers/SyncObject.cpp @@ -41,7 +41,7 @@ SyncObjectClient::CreateSyncObjectClient(SyncHandle aHandle #ifdef XP_WIN return MakeAndAddRef(aHandle, aDevice); #else - MOZ_ASSERT_UNREACHABLE(); + MOZ_ASSERT_UNREACHABLE("CreateSyncObjectClient only supports Windows"); return nullptr; #endif } diff --git a/gfx/layers/basic/BasicDisplayItemLayer.cpp b/gfx/layers/basic/BasicDisplayItemLayer.cpp index 7664c1affd00..512463777f4e 100644 --- a/gfx/layers/basic/BasicDisplayItemLayer.cpp +++ b/gfx/layers/basic/BasicDisplayItemLayer.cpp @@ -60,7 +60,7 @@ public: AutoRestoreTransform autoRestoreTransform(aDT); Matrix transform = aDT->GetTransform(); RefPtr context = gfxContext::CreateOrNull(aDT, aDeviceOffset); - context->SetMatrix(ThebesMatrix(transform)); + context->SetMatrix(transform); mItem->Paint(mBuilder, context); } diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index a9218cfb04bd..ed654ade73f5 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -67,8 +67,8 @@ ClipToContain(gfxContext* aContext, const IntRect& aRect) gfxRect deviceRect = aContext->UserToDevice(userRect); deviceRect.RoundOut(); - gfxMatrix currentMatrix = aContext->CurrentMatrix(); - aContext->SetMatrix(gfxMatrix()); + Matrix currentMatrix = aContext->CurrentMatrix(); + aContext->SetMatrix(Matrix()); aContext->NewPath(); aContext->Rectangle(deviceRect); aContext->Clip(); @@ -130,7 +130,7 @@ BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const // destination. Since the User->Device space transform will be applied // to the mask by PopGroupAndBlend we need to adjust the transform to // transform the mask to user space. - Matrix currentTransform = ToMatrix(aGroupResult.mFinalTarget->CurrentMatrix()); + Matrix currentTransform = aGroupResult.mFinalTarget->CurrentMatrix(); currentTransform.Invert(); maskTransform = maskTransform * currentTransform; } @@ -249,7 +249,7 @@ public: // it applies an identity. void Apply2DTransform() { - mTarget->SetMatrix(ThebesMatrix(mTransform)); + mTarget->SetMatrix(mTransform); } // Set the opaque rect to match the bounds of the visible region. @@ -591,7 +591,7 @@ BasicLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback, } else { mSnapEffectiveTransforms = true; } - mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(ToMatrix(mTarget->CurrentMatrix())) : Matrix4x4()); + mRoot->ComputeEffectiveTransforms(mTarget ? Matrix4x4::From2D(mTarget->CurrentMatrix()) : Matrix4x4()); ToData(mRoot)->Validate(aCallback, aCallbackData, nullptr); if (mRoot->GetMaskLayer()) { @@ -788,9 +788,9 @@ InstallLayerClipPreserves3D(gfxContext* aTarget, Layer* aLayer) if (!transform3d.CanDraw2D(&transform)) { gfxDevCrash(LogReason::CannotDraw3D) << "GFX: We should not have a 3D transform that CanDraw2D() is false!"; } - gfxMatrix oldTransform = aTarget->CurrentMatrix(); - transform *= ToMatrix(oldTransform); - aTarget->SetMatrix(ThebesMatrix(transform)); + Matrix oldTransform = aTarget->CurrentMatrix(); + transform *= oldTransform; + aTarget->SetMatrix(transform); aTarget->NewPath(); aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y, diff --git a/gfx/layers/basic/BasicLayersImpl.cpp b/gfx/layers/basic/BasicLayersImpl.cpp index f4a2fc9d1666..1cb2c03ecbaa 100644 --- a/gfx/layers/basic/BasicLayersImpl.cpp +++ b/gfx/layers/basic/BasicLayersImpl.cpp @@ -64,7 +64,7 @@ PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer) { AutoMoz2DMaskData mask; if (GetMaskData(aMaskLayer, Point(), &mask)) { - aContext->SetMatrix(ThebesMatrix(mask.GetTransform())); + aContext->SetMatrix(mask.GetTransform()); aContext->Mask(mask.GetSurface(), aOpacity); return; } diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp index c8156efca525..6fb064dbbccc 100644 --- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -991,7 +991,7 @@ void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion, RefPtr ctx = gfxContext::CreateOrNull(drawTarget); MOZ_ASSERT(ctx); // already checked the draw target above ctx->SetMatrix( - ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(ThebesPoint(-mTilingOrigin))); + ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin)); mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion, DrawRegionClip::DRAW, nsIntRegion(), mCallbackData); diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index b0ad1b97a564..1c9b8741cde5 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -426,7 +426,7 @@ nsDeviceContext::CreateRenderingContextCommon(bool aWantReferenceContext) } transform.PreScale(mPrintingScale, mPrintingScale); - pContext->SetMatrix(transform); + pContext->SetMatrixDouble(transform); return pContext.forget(); } diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index 1af8f8747971..8828536aa69b 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -52,7 +52,7 @@ gfxAlphaBoxBlur::Init(gfxContext* aDestinationCtx, RefPtr context = gfxContext::CreateOrNull(dt); MOZ_ASSERT(context); // already checked for target above - context->SetMatrix(gfxMatrix::Translation(-mBlur.GetRect().TopLeft())); + context->SetMatrix(Matrix::Translation(-mBlur.GetRect().TopLeft())); return context.forget(); } @@ -588,7 +588,7 @@ GetBlur(gfxContext* aDestinationCtx, // since our source image is only 1px for some parts, we make thousands of calls. // Instead just render the blur ourself here as one image and send it over for printing. // TODO: May need to change this with the blob renderer in WR since it also records. - Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix()); + Matrix destMatrix = aDestinationCtx->CurrentMatrix(); bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation() || aDestinationCtx->GetDrawTarget()->IsRecording(); if (useDestRect) { diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 96461358169f..b7e1670ba57f 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -112,7 +112,7 @@ gfxContext::CreatePreservingTransformOrNull(DrawTarget* aTarget) Matrix transform = aTarget->GetTransform(); RefPtr result = new gfxContext(aTarget); - result->SetMatrix(ThebesMatrix(transform)); + result->SetMatrix(transform); return result.forget(); } @@ -318,16 +318,28 @@ gfxContext::Multiply(const gfxMatrix& matrix) } void -gfxContext::SetMatrix(const gfxMatrix& matrix) +gfxContext::SetMatrix(const gfx::Matrix& matrix) { CURRENTSTATE_CHANGED() - ChangeTransform(ToMatrix(matrix)); + ChangeTransform(matrix); +} + +void +gfxContext::SetMatrixDouble(const gfxMatrix& matrix) +{ + SetMatrix(ToMatrix(matrix)); +} + +gfx::Matrix +gfxContext::CurrentMatrix() const +{ + return mTransform; } gfxMatrix -gfxContext::CurrentMatrix() const +gfxContext::CurrentMatrixDouble() const { - return ThebesMatrix(mTransform); + return ThebesMatrix(CurrentMatrix()); } gfxPoint diff --git a/gfx/thebes/gfxContext.h b/gfx/thebes/gfxContext.h index f554a4e8ff83..83964d3c4951 100644 --- a/gfx/thebes/gfxContext.h +++ b/gfx/thebes/gfxContext.h @@ -172,12 +172,14 @@ public: /** * Replaces the current transformation matrix with matrix. */ - void SetMatrix(const gfxMatrix& matrix); + void SetMatrix(const mozilla::gfx::Matrix& matrix); + void SetMatrixDouble(const gfxMatrix& matrix); /** * Returns the current transformation matrix. */ - gfxMatrix CurrentMatrix() const; + mozilla::gfx::Matrix CurrentMatrix() const; + gfxMatrix CurrentMatrixDouble() const; /** * Converts a point from device to user coordinates using the inverse @@ -628,7 +630,7 @@ public: } } - const gfxMatrix& Matrix() + const mozilla::gfx::Matrix& Matrix() { MOZ_ASSERT(mContext, "mMatrix doesn't contain a useful matrix"); return mMatrix; @@ -638,7 +640,7 @@ public: private: gfxContext *mContext; - gfxMatrix mMatrix; + mozilla::gfx::Matrix mMatrix; }; diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 6c1b79fc5212..9e0114d5b1f2 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1679,7 +1679,7 @@ private: fillPattern = mFontParams.contextPaint->GetFillPattern( mRunParams.context->GetDrawTarget(), - mRunParams.context->CurrentMatrix(), + mRunParams.context->CurrentMatrixDouble(), imgParams); } if (!fillPattern) { @@ -2163,7 +2163,7 @@ gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT) ? -M_PI / 2.0 : M_PI / 2.0; gfxMatrix mat = - aRunParams.context->CurrentMatrix(). + aRunParams.context->CurrentMatrixDouble(). PreTranslate(p). // translate origin for rotation PreRotate(rotation). // turn 90deg CCW (sideways-left) or CW (*-right) PreTranslate(-p); // undo the translation @@ -2182,7 +2182,7 @@ gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, mat.PreTranslate(baseAdj); } - aRunParams.context->SetMatrix(mat); + aRunParams.context->SetMatrixDouble(mat); } RefPtr contextPaint; @@ -2193,7 +2193,7 @@ gfxFont::Draw(const gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, RefPtr fillPattern = aRunParams.context->GetPattern(); contextPaint = new SimpleTextContextPaint(fillPattern, nullptr, - aRunParams.context->CurrentMatrix()); + aRunParams.context->CurrentMatrixDouble()); fontParams.contextPaint = contextPaint.get(); } diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index bcbc5fef1d71..04849e18fee8 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -438,8 +438,8 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, gfxFloat aOpacity, ExtendMode aExtendMode) { - gfxSize scaleFactor = aContext->CurrentMatrix().ScaleFactors(true); - gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleFactor.width, scaleFactor.height); + Size scaleFactor = aContext->CurrentMatrix().ScaleFactors(true); + Matrix scaleMatrix = Matrix::Scaling(scaleFactor.width, scaleFactor.height); const float fuzzFactor = 0.01; // If we aren't scaling or translating, don't go down this path @@ -456,13 +456,13 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, clipExtents.Inflate(1.0); gfxRect needed = aRegion.IntersectAndRestrict(clipExtents); - Rect scaledNeededRect = ToMatrix(scaleMatrix).TransformBounds(ToRect(needed)); + Rect scaledNeededRect = scaleMatrix.TransformBounds(ToRect(needed)); scaledNeededRect.RoundOut(); if (scaledNeededRect.IsEmpty()) { return false; } - Rect scaledImageRect = ToMatrix(scaleMatrix).TransformBounds(aImageRect); + Rect scaledImageRect = scaleMatrix.TransformBounds(aImageRect); if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) { return false; } @@ -485,7 +485,7 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, RefPtr tmpCtx = gfxContext::CreateOrNull(scaledDT); MOZ_ASSERT(tmpCtx); // already checked the target above - scaledDT->SetTransform(ToMatrix(scaleMatrix)); + scaledDT->SetTransform(scaleMatrix); gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height); // Since this is just the scaled image, we don't want to repeat anything yet. @@ -495,12 +495,12 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, { gfxContextMatrixAutoSaveRestore autoSR(aContext); - Matrix withoutScale = ToMatrix(aContext->CurrentMatrix()); + Matrix withoutScale = aContext->CurrentMatrix(); DrawTarget* destDrawTarget = aContext->GetDrawTarget(); // The translation still is in scaled units withoutScale.PreScale(1.0 / scaleFactor.width, 1.0 / scaleFactor.height); - aContext->SetMatrix(ThebesMatrix(withoutScale)); + aContext->SetMatrix(withoutScale); DrawOptions drawOptions(aOpacity, aContext->CurrentOp(), aContext->CurrentAntialiasMode()); diff --git a/gfx/thebes/gfxWindowsNativeDrawing.cpp b/gfx/thebes/gfxWindowsNativeDrawing.cpp index bd2f78d21059..1504d1e1a7bf 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.cpp +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp @@ -68,7 +68,7 @@ gfxWindowsNativeDrawing::BeginNativeDrawing() if (surf && surf->CairoStatus() != 0) return nullptr; - gfxMatrix m = mContext->CurrentMatrix(); + gfxMatrix m = mContext->CurrentMatrixDouble(); if (!m.HasNonTranslation()) mTransformType = TRANSLATION_ONLY; else if (m.HasNonAxisAlignedTransform()) diff --git a/gfx/thebes/gfxXlibNativeRenderer.cpp b/gfx/thebes/gfxXlibNativeRenderer.cpp index 19213b4e7702..582ecc45742a 100644 --- a/gfx/thebes/gfxXlibNativeRenderer.cpp +++ b/gfx/thebes/gfxXlibNativeRenderer.cpp @@ -467,7 +467,7 @@ void gfxXlibNativeRenderer::Draw(gfxContext* ctx, IntSize size, uint32_t flags, Screen *screen, Visual *visual) { - gfxMatrix matrix = ctx->CurrentMatrix(); + Matrix matrix = ctx->CurrentMatrix(); // We can only draw direct or onto a copied background if pixels align and // native drawing is compatible with the current operator. (The matrix is diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index d4d903c549dd..80f199646988 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -302,10 +302,10 @@ SVGDrawingCallback::operator()(gfxContext* aContext, if (!matrix.Invert()) { return false; } - aContext->SetMatrix( - aContext->CurrentMatrix().PreMultiply(matrix). - PreScale(double(mSize.width) / mViewportSize.width, - double(mSize.height) / mViewportSize.height)); + aContext->SetMatrixDouble( + aContext->CurrentMatrixDouble().PreMultiply(matrix). + PreScale(double(mSize.width) / mViewportSize.width, + double(mSize.height) / mViewportSize.height)); nsPresContext* presContext = presShell->GetPresContext(); MOZ_ASSERT(presContext, "pres shell w/out pres context"); diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 326b53427f9e..2f02bf13efed 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -950,8 +950,7 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector& aExt switch (mProcessType) { case GeckoProcessType_Content: # if defined(MOZ_CONTENT_SANDBOX) - if (mSandboxLevel > 0 && - !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) { + if (mSandboxLevel > 0) { // For now we treat every failure as fatal in SetSecurityLevelForContentProcess // and just crash there right away. Should this change in the future then we // should also handle the error here. diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index f7209f2a6e30..2183cec17f68 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -4685,10 +4685,10 @@ PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, // slight rounding errors here. We use NudgeToIntegers() here to adjust // matrix components that are integers up to the accuracy of floats to be // those integers. - gfxMatrix newTM = aThebesContext->CurrentMatrix().PreTranslate(offset). - PreScale(scale, scale). - NudgeToIntegers(); - aThebesContext->SetMatrix(newTM); + gfxMatrix newTM = aThebesContext->CurrentMatrixDouble().PreTranslate(offset). + PreScale(scale, scale). + NudgeToIntegers(); + aThebesContext->SetMatrixDouble(newTM); AutoSaveRestoreRenderingState _(this); @@ -5088,7 +5088,7 @@ PresShell::PaintRangePaintInfo(const nsTArray>& aItems ctx->Clip(path); } - gfxMatrix initialTM = ctx->CurrentMatrix(); + gfxMatrix initialTM = ctx->CurrentMatrixDouble(); if (resize) initialTM.PreScale(scale, scale); @@ -5118,7 +5118,7 @@ PresShell::PaintRangePaintInfo(const nsTArray>& aItems gfxPoint rootOffset = nsLayoutUtils::PointToGfxPoint(rangeInfo->mRootOffset, pc->AppUnitsPerDevPixel()); - ctx->SetMatrix(gfxMatrix(initialTM).PreTranslate(rootOffset)); + ctx->SetMatrixDouble(initialTM.PreTranslate(rootOffset)); aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y); nsRegion visible(aArea); RefPtr layerManager = @@ -10144,8 +10144,8 @@ void ReflowCountMgr::PaintCount(const char* aName, aRenderingContext->Save(); gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel); - aRenderingContext->SetMatrix( - aRenderingContext->CurrentMatrix().PreTranslate(devPixelOffset)); + aRenderingContext->SetMatrixDouble( + aRenderingContext->CurrentMatrixDouble().PreTranslate(devPixelOffset)); // We don't care about the document language or user fonts here; // just get a default Latin font. diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 837be0931457..f9994b31e649 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1518,7 +1518,7 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems, } } - NS_POSTCONDITION(aFrameItems.IsEmpty(), "How did that happen?"); + MOZ_ASSERT(aFrameItems.IsEmpty(), "How did that happen?"); } @@ -9374,7 +9374,7 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext, newFrame->SetNextContinuation(nextContinuation); } - NS_POSTCONDITION(!newFrame->GetNextSibling(), "unexpected sibling"); + MOZ_ASSERT(!newFrame->GetNextSibling(), "unexpected sibling"); return newFrame; } @@ -13207,7 +13207,7 @@ Iterator::AppendItemsToList(nsCSSFrameConstructor* aFCtor, const Iterator& aEnd, // Point ourselves to aEnd, as advertised SetToEnd(); - NS_POSTCONDITION(*this == aEnd, "How did that happen?"); + MOZ_ASSERT(*this == aEnd, "How did that happen?"); } void @@ -13222,7 +13222,7 @@ Iterator::InsertItem(FrameConstructionItem* aItem) } mList.AdjustCountsForItem(aItem, 1); - NS_POSTCONDITION(aItem->getNext() == mCurrent, "How did that happen?"); + MOZ_ASSERT(aItem->getNext() == mCurrent, "How did that happen?"); } void diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 70c02ffdab15..161d37b3d6ba 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1627,7 +1627,7 @@ nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) if (!found) { found = parent->GetChildList(nsIFrame::kOverflowList) .ContainsFrame(aChildFrame); - NS_POSTCONDITION(found, "not in child list"); + MOZ_ASSERT(found, "not in child list"); } #endif @@ -3728,8 +3728,8 @@ nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame, gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(pos, presContext->AppUnitsPerDevPixel()); - aRenderingContext->SetMatrix( - aRenderingContext->CurrentMatrix().PreTranslate(devPixelOffset)); + aRenderingContext->SetMatrixDouble( + aRenderingContext->CurrentMatrixDouble().PreTranslate(devPixelOffset)); } } builder.SetIgnoreScrollFrame(rootScrollFrame); @@ -6674,7 +6674,7 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx, gfxRect devPixelDirty = nsLayoutUtils::RectToGfxRect(aDirty, aAppUnitsPerDevPixel); - gfxMatrix currentMatrix = aCtx->CurrentMatrix(); + gfxMatrix currentMatrix = aCtx->CurrentMatrixDouble(); gfxRect fill = devPixelFill; gfxRect dest = devPixelDest; bool didSnap; @@ -6901,7 +6901,7 @@ DrawImageInternal(gfxContext& aContext, { gfxContextMatrixAutoSaveRestore contextMatrixRestorer(&aContext); - aContext.SetMatrix(params.imageSpaceToDeviceSpace); + aContext.SetMatrixDouble(params.imageSpaceToDeviceSpace); Maybe fallbackContext; if (!aSVGContext) { diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 0582f1d7f4c6..b96fd39022db 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -2564,6 +2564,6 @@ nsListEventListener::HandleEvent(nsIDOMEvent* aEvent) return mFrame->nsListControlFrame::MouseMove(aEvent); } - NS_ABORT(); + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); return NS_OK; } diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 980edc02cfe2..34d311374c86 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -1315,7 +1315,7 @@ nsContainerFrame::StealFrame(nsIFrame* aChild) } } - NS_POSTCONDITION(removed, "StealFrame: can't find aChild"); + MOZ_ASSERT(removed, "StealFrame: can't find aChild"); return removed ? NS_OK : NS_ERROR_UNEXPECTED; } @@ -1430,7 +1430,7 @@ nsContainerFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, // remove it from its next-in-flow/prev-in-flow chain. aNextInFlow->Destroy(); - NS_POSTCONDITION(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); + MOZ_ASSERT(!prevInFlow->GetNextInFlow(), "non null next-in-flow"); } /** diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index bdc9650bccc7..b10c168ca442 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -6526,7 +6526,8 @@ nsIFrame* nsIFrame::GetTailContinuation() next = frame->GetNextContinuation()) { frame = next; } - NS_POSTCONDITION(frame, "illegal state in continuation chain."); + + MOZ_ASSERT(frame, "illegal state in continuation chain."); return frame; } diff --git a/layout/generic/nsFrameList.cpp b/layout/generic/nsFrameList.cpp index 3326b6fcaf3b..e4d816ceda09 100644 --- a/layout/generic/nsFrameList.cpp +++ b/layout/generic/nsFrameList.cpp @@ -257,7 +257,7 @@ nsFrameList::ExtractTail(FrameLinkEnumerator& aLink) // Now make sure aLink doesn't point to a frame we no longer have. aLink.mFrame = nullptr; - NS_POSTCONDITION(aLink.AtEnd(), "What's going on here?"); + MOZ_ASSERT(aLink.AtEnd(), "What's going on here?"); return nsFrameList(newFirstFrame, newLastFrame); } diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 53807e9fb123..9fee3cc2e502 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -39,6 +39,7 @@ #include "mozilla/Preferences.h" #include "mozilla/LookAndFeel.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" #include "mozilla/dom/HTMLTextAreaElement.h" #include #include "mozilla/MathAlgorithms.h" @@ -2185,6 +2186,7 @@ ScrollFrameHelper::CompleteAsyncScroll(const nsRect &aRange, nsAtom* aOrigin) // We are done scrolling, set our destination to wherever we actually ended // up scrolling to. mDestination = GetScrollPosition(); + FireScrollEndEvent(); } bool @@ -4408,6 +4410,17 @@ ScrollFrameHelper::FireScrollPortEvent() mOuter->PresContext(), &event); } +void +ScrollFrameHelper::FireScrollEndEvent() +{ + MOZ_ASSERT(mOuter->GetContent()); + nsContentUtils::DispatchEventOnlyToChrome(mOuter->GetContent()->OwnerDoc(), + mOuter->GetContent(), + NS_LITERAL_STRING("scrollend"), + true /* aCanBubble */, + false /* aCancelable */); +} + void ScrollFrameHelper::ReloadChildFrames() { diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 548b29a2c3b6..3af6e77a8727 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -69,6 +69,7 @@ public: nsTArray& aElements); void AppendAnonymousContentTo(nsTArray& aElements, uint32_t aFilter); nsresult FireScrollPortEvent(); + void FireScrollEndEvent(); void PostOverflowEvent(); using PostDestroyData = nsIFrame::PostDestroyData; void Destroy(PostDestroyData& aPostDestroyData); @@ -400,6 +401,9 @@ public: nsExpirationState* GetExpirationState() { return &mActivityExpirationState; } void SetTransformingByAPZ(bool aTransforming) { + if (mTransformingByAPZ && !aTransforming) { + FireScrollEndEvent(); + } mTransformingByAPZ = aTransforming; if (!mozilla::css::TextOverflow::HasClippedOverflow(mOuter)) { // If the block has some text-overflow stuff we should kick off a paint diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 6def7f6d53e3..80bc4ad3fad3 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -871,7 +871,7 @@ nsPluginFrame::PaintPrintPlugin(nsIFrame* aFrame, gfxContext* aCtx, nsLayoutUtils::PointToGfxPoint(pt, aFrame->PresContext()->AppUnitsPerDevPixel()); gfxContextMatrixAutoSaveRestore autoSR(aCtx); - aCtx->SetMatrix(aCtx->CurrentMatrix().PreTranslate(devPixelPt)); + aCtx->SetMatrixDouble(aCtx->CurrentMatrixDouble().PreTranslate(devPixelPt)); // FIXME - Bug 385435: Doesn't aDirtyRect need translating too? diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 31d4571d18f6..e59692d07e98 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -606,7 +606,8 @@ ClearAllTextRunReferences(nsTextFrame* aFrame, gfxTextRun* aTextRun, } aFrame = aFrame->GetNextContinuation(); } - NS_POSTCONDITION(!found || aStartContinuation, "how did we find null?"); + + MOZ_ASSERT(!found || aStartContinuation, "how did we find null?"); return found; } @@ -5190,9 +5191,9 @@ nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, // necessary. This is done here because we want selection be // compressed at the same time as text. gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D); - gfxMatrix mat = aCtx->CurrentMatrix() + gfxMatrix mat = aCtx->CurrentMatrixDouble() .PreTranslate(pt).PreScale(scaleFactor, 1.0).PreTranslate(-pt); - aCtx->SetMatrix (mat); + aCtx->SetMatrixDouble(mat); } } nsTextFrame::PaintTextParams params(aCtx); @@ -7240,10 +7241,10 @@ nsTextFrame::DrawTextRunAndDecorations(Range aRange, float scaleFactor = GetTextCombineScaleFactor(this); if (scaleFactor != 1.0f) { scaledRestorer.SetContext(aParams.context); - gfxMatrix unscaled = aParams.context->CurrentMatrix(); + gfxMatrix unscaled = aParams.context->CurrentMatrixDouble(); gfxPoint pt(x / app, y / app); unscaled.PreTranslate(pt).PreScale(1.0f / scaleFactor, 1.0f).PreTranslate(-pt); - aParams.context->SetMatrix(unscaled); + aParams.context->SetMatrixDouble(unscaled); } } @@ -9098,9 +9099,11 @@ nsTextFrame::SetLength(int32_t aLength, nsLineLayout* aLineLayout, } f = next; } - NS_POSTCONDITION(!framesToRemove || (f && f->mContentOffset == end), - "How did we exit the loop if we null out framesToRemove if " - "!next || next->mContentOffset > end ?"); + + MOZ_ASSERT(!framesToRemove || (f && f->mContentOffset == end), + "How did we exit the loop if we null out framesToRemove if " + "!next || next->mContentOffset > end ?"); + if (framesToRemove) { // We are guaranteed that we exited the loop with f not null, per the // postcondition above diff --git a/layout/mathml/nsMathMLChar.cpp b/layout/mathml/nsMathMLChar.cpp index ae7fb2cdb61f..f6483f1d5c96 100644 --- a/layout/mathml/nsMathMLChar.cpp +++ b/layout/mathml/nsMathMLChar.cpp @@ -2025,16 +2025,16 @@ nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext, nsPoint pt = r.TopRight(); gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit), NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)); - aThebesContext->SetMatrix( - aThebesContext->CurrentMatrix().PreTranslate(devPixelOffset). - PreScale(-mScaleX, mScaleY)); + aThebesContext->SetMatrixDouble( + aThebesContext->CurrentMatrixDouble().PreTranslate(devPixelOffset). + PreScale(-mScaleX, mScaleY)); } else { nsPoint pt = r.TopLeft(); gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit), NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit)); - aThebesContext->SetMatrix( - aThebesContext->CurrentMatrix().PreTranslate(devPixelOffset). - PreScale(mScaleX, mScaleY)); + aThebesContext->SetMatrixDouble( + aThebesContext->CurrentMatrixDouble().PreTranslate(devPixelOffset). + PreScale(mScaleX, mScaleY)); } // update the bounding rectangle. diff --git a/layout/mathml/nsMathMLmactionFrame.cpp b/layout/mathml/nsMathMLmactionFrame.cpp index af51234e7537..82f577e3a59f 100644 --- a/layout/mathml/nsMathMLmactionFrame.cpp +++ b/layout/mathml/nsMathMLmactionFrame.cpp @@ -274,7 +274,7 @@ nsMathMLmactionFrame::MouseListener::HandleEvent(nsIDOMEvent* aEvent) mOwner->MouseOut(); } else { - NS_ABORT(); + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); } return NS_OK; diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp index 7a00f899ad27..e588c44d6e91 100644 --- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -3684,8 +3684,8 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder, gfxDevCrash(LogReason::InvalidContext) << "PaintInactive context problem " << gfx::hexa(tempDT); return; } - context->SetMatrix(gfxMatrix::Translation(-itemVisibleRect.x, - -itemVisibleRect.y)); + context->SetMatrix(Matrix::Translation(-itemVisibleRect.x, + -itemVisibleRect.y)); } } #endif @@ -3914,7 +3914,7 @@ ContainerState::SetupMaskLayerForCSSMask(Layer* aLayer, } RefPtr maskCtx = gfxContext::CreateOrNull(dt); - maskCtx->SetMatrix(gfxMatrix::Translation(-itemRect.TopLeft())); + maskCtx->SetMatrix(Matrix::Translation(-itemRect.TopLeft())); maskCtx->Multiply(gfxMatrix::Scaling(mParameters.mXScale, mParameters.mYScale)); bool isPaintFinished = aMaskItem->PaintMask(mBuilder, maskCtx); @@ -5889,7 +5889,7 @@ static void DebugPaintItem(DrawTarget& aDrawTarget, gfxDevCrash(LogReason::InvalidContext) << "DebugPaintItem context problem " << gfx::hexa(tempDT); return; } - context->SetMatrix(gfxMatrix::Translation(-bounds.x, -bounds.y)); + context->SetMatrix(Matrix::Translation(-bounds.x, -bounds.y)); aItem->Paint(aBuilder, context); RefPtr surface = tempDT->Snapshot(); @@ -6177,9 +6177,9 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer, // Apply the residual transform if it has been enabled, to ensure that // snapping when we draw into aContext exactly matches the ideal transform. // See above for why this is OK. - aContext->SetMatrix( - aContext->CurrentMatrix().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)). - PreScale(userData->mXScale, userData->mYScale)); + aContext->SetMatrixDouble( + aContext->CurrentMatrixDouble().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)). + PreScale(userData->mXScale, userData->mYScale)); layerBuilder->PaintItems(entry->mItems, iterRect, aContext, builder, presContext, @@ -6193,9 +6193,9 @@ FrameLayerBuilder::DrawPaintedLayer(PaintedLayer* aLayer, // Apply the residual transform if it has been enabled, to ensure that // snapping when we draw into aContext exactly matches the ideal transform. // See above for why this is OK. - aContext->SetMatrix( - aContext->CurrentMatrix().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)). - PreScale(userData->mXScale,userData->mYScale)); + aContext->SetMatrixDouble( + aContext->CurrentMatrixDouble().PreTranslate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)). + PreScale(userData->mXScale,userData->mYScale)); layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext, builder, presContext, diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp index a1bd785271e5..5d1039ece674 100644 --- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -1685,8 +1685,8 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, nsLayoutUtils::PointToGfxPoint(nsPoint(shadowItem->mXOffset, shadowItem->mYOffset), aPresContext->AppUnitsPerDevPixel()); - shadowContext->SetMatrix( - shadowContext->CurrentMatrix().PreTranslate(devPixelOffset)); + shadowContext->SetMatrixDouble( + shadowContext->CurrentMatrixDouble().PreTranslate(devPixelOffset)); nsRect nativeRect = aDirtyRect; nativeRect.MoveBy(-nsPoint(shadowItem->mXOffset, shadowItem->mYOffset)); @@ -4479,7 +4479,7 @@ nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius, nsLayoutUtils::RectToGfxRect(aDirtyRect, aAppUnitsPerDevPixel); dirtyRect.RoundOut(); - gfxMatrix transform = aDestinationCtx->CurrentMatrix(); + gfxMatrix transform = aDestinationCtx->CurrentMatrixDouble(); rect = transform.TransformBounds(rect); mPreTransformed = !transform.IsIdentity(); @@ -4516,7 +4516,7 @@ nsContextBoxBlur::DoPaint() gfxContextMatrixAutoSaveRestore saveMatrix(mDestinationCtx); if (mPreTransformed) { - mDestinationCtx->SetMatrix(gfxMatrix()); + mDestinationCtx->SetMatrix(Matrix()); } mAlphaBoxBlur.Paint(mDestinationCtx); @@ -4577,12 +4577,12 @@ nsContextBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, // Do blurs in device space when possible. // Chrome/Skia always does the blurs in device space // and will sometimes get incorrect results (e.g. rotated blurs) - gfxMatrix transform = aDestinationCtx->CurrentMatrix(); + gfxMatrix transform = aDestinationCtx->CurrentMatrixDouble(); // XXX: we could probably handle negative scales but for now it's easier just to fallback if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 && transform._22 > 0.0) { scaleX = transform._11; scaleY = transform._22; - aDestinationCtx->SetMatrix(gfxMatrix()); + aDestinationCtx->SetMatrix(Matrix()); } else { transform = gfxMatrix(); } @@ -4677,13 +4677,13 @@ nsContextBoxBlur::InsetBoxBlur(gfxContext* aDestinationCtx, // input data to the blur. This way, we don't have to scale the min // inset blur to the invert of the dest context, then rescale it back // when we draw to the destination surface. - gfxSize scale = aDestinationCtx->CurrentMatrix().ScaleFactors(true); - Matrix transform = ToMatrix(aDestinationCtx->CurrentMatrix()); + gfx::Size scale = aDestinationCtx->CurrentMatrix().ScaleFactors(true); + Matrix transform = aDestinationCtx->CurrentMatrix(); // XXX: we could probably handle negative scales but for now it's easier just to fallback if (!transform.HasNonAxisAlignedTransform() && transform._11 > 0.0 && transform._22 > 0.0) { // If we don't have a rotation, we're pre-transforming all the rects. - aDestinationCtx->SetMatrix(gfxMatrix()); + aDestinationCtx->SetMatrix(Matrix()); } else { // Don't touch anything, we have a rotation. transform = Matrix(); diff --git a/layout/painting/nsCSSRenderingGradients.cpp b/layout/painting/nsCSSRenderingGradients.cpp index b72fe31d02ca..7eb346794bdc 100644 --- a/layout/painting/nsCSSRenderingGradients.cpp +++ b/layout/painting/nsCSSRenderingGradients.cpp @@ -932,7 +932,7 @@ nsCSSGradientRenderer::Paint(gfxContext& aContext, gfxRect dirtyAreaToFill = nsLayoutUtils::RectToGfxRect(dirty, appUnitsPerDevPixel); dirtyAreaToFill.RoundOut(); - gfxMatrix ctm = aContext.CurrentMatrix(); + Matrix ctm = aContext.CurrentMatrix(); bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles(); // xStart/yStart are the top-left corner of the top-left tile. @@ -975,7 +975,7 @@ nsCSSGradientRenderer::Paint(gfxContext& aContext, gfxMatrix transform = gfxUtils::TransformRectToRect(fillRect, snappedFillRectTopLeft, snappedFillRectTopRight, snappedFillRectBottomRight); - aContext.SetMatrix(transform); + aContext.SetMatrixDouble(transform); } aContext.NewPath(); aContext.Rectangle(fillRect); @@ -989,8 +989,8 @@ nsCSSGradientRenderer::Paint(gfxContext& aContext, edgeColor.a *= aOpacity; aContext.SetColor(edgeColor); } else { - aContext.SetMatrix( - aContext.CurrentMatrix().Copy().PreTranslate(tileRect.TopLeft())); + aContext.SetMatrixDouble( + aContext.CurrentMatrixDouble().Copy().PreTranslate(tileRect.TopLeft())); aContext.SetPattern(gradientPattern); } aContext.Fill(); diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 0b9faedde892..14d566ae35f8 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -771,7 +771,7 @@ GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext, { // Paint text selection background into sourceCtx. gfxContextMatrixAutoSaveRestore save(sourceCtx); - sourceCtx->SetMatrix(sourceCtx->CurrentMatrix().PreTranslate(bounds.TopLeft())); + sourceCtx->SetMatrixDouble(sourceCtx->CurrentMatrixDouble().PreTranslate(bounds.TopLeft())); nsLayoutUtils::PaintFrame(aContext, aFrame, nsRect(nsPoint(0, 0), aFrame->GetSize()), @@ -793,10 +793,10 @@ GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext, } RefPtr maskCtx = gfxContext::CreatePreservingTransformOrNull(maskDT); MOZ_ASSERT(maskCtx); - gfxMatrix currentMatrix = sourceCtx->CurrentMatrix(); - maskCtx->SetMatrix(gfxMatrix::Translation(bounds.TopLeft()) * - currentMatrix * - gfxMatrix::Translation(-drawRect.TopLeft())); + gfxMatrix currentMatrix = sourceCtx->CurrentMatrixDouble(); + maskCtx->SetMatrixDouble(gfxMatrix::Translation(bounds.TopLeft()) * + currentMatrix * + gfxMatrix::Translation(-drawRect.TopLeft())); // Shade text shape into mask A8 surface. nsLayoutUtils::PaintFrame(maskCtx, aFrame, @@ -9171,7 +9171,7 @@ ComputeMaskGeometry(PaintFramesParams& aParams) frame->PresContext()->AppUnitsPerDevPixel()); gfxContextMatrixAutoSaveRestore matSR(&ctx); - ctx.SetMatrix(ctx.CurrentMatrix().PreTranslate(devPixelOffsetToUserSpace)); + ctx.SetMatrixDouble(ctx.CurrentMatrixDouble().PreTranslate(devPixelOffsetToUserSpace)); // Convert boaderArea and dirtyRect to user space. int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel(); diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp index 3b0594491de4..4f8d6b31c605 100644 --- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -698,7 +698,7 @@ nsImageRenderer::DrawableForElement(const nsRect& aImageRect, nsSVGIntegrationUtils::DrawableFromPaintServer( mPaintServerFrame, mForFrame, mSize, imageSize, aContext.GetDrawTarget(), - aContext.CurrentMatrix(), + aContext.CurrentMatrixDouble(), nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES); return drawable.forget(); diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 0adf0858e560..750d9369bd4c 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -213,8 +213,8 @@ SheetLoadData::SheetLoadData(Loader* aLoader, ++(mParentData->mPendingChildren); } - NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad, - "Shouldn't use system principal for async loads"); + MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad, + "Shouldn't use system principal for async loads"); } SheetLoadData::SheetLoadData(Loader* aLoader, @@ -248,8 +248,8 @@ SheetLoadData::SheetLoadData(Loader* aLoader, , mPreloadEncoding(aPreloadEncoding) { NS_PRECONDITION(mLoader, "Must have a loader!"); - NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad, - "Shouldn't use system principal for async loads"); + MOZ_ASSERT(!mUseSystemPrincipal || mSyncLoad, + "Shouldn't use system principal for async loads"); } SheetLoadData::~SheetLoadData() diff --git a/layout/style/nsDOMCSSValueList.cpp b/layout/style/nsDOMCSSValueList.cpp index 87fe975d152c..0fb451970d19 100644 --- a/layout/style/nsDOMCSSValueList.cpp +++ b/layout/style/nsDOMCSSValueList.cpp @@ -106,7 +106,7 @@ nsDOMCSSValueList::SetCssText(const nsAString& aCssText) return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; } - NS_NOTYETIMPLEMENTED("Can't SetCssText yet: please write me!"); + MOZ_ASSERT_UNREACHABLE("Can't SetCssText yet: please write me!"); return NS_OK; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index bd869826844d..3df52944c262 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -3110,12 +3110,12 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, GeckoStyleContext* aCon * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_INHERITED(type_, data_) \ - NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ - aRuleDetail == eRuleFullReset || \ - (aStartStruct && aRuleDetail == eRulePartialReset), \ - "conditions.CacheableWithoutDependencies() must be false " \ - "for inherited structs unless all properties have been " \ - "specified with values other than inherit"); \ + MOZ_ASSERT(!conditions.CacheableWithoutDependencies() || \ + aRuleDetail == eRuleFullReset || \ + (aStartStruct && aRuleDetail == eRulePartialReset), \ + "conditions.CacheableWithoutDependencies() must be false " \ + "for inherited structs unless all properties have been " \ + "specified with values other than inherit"); \ if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ @@ -3145,13 +3145,13 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, GeckoStyleContext* aCon * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_RESET(type_, data_) \ - NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ - aRuleDetail == eRuleNone || \ - aRuleDetail == eRulePartialReset || \ - aRuleDetail == eRuleFullReset, \ - "conditions.CacheableWithoutDependencies() must be false " \ - "for reset structs if any properties were specified as " \ - "inherit"); \ + MOZ_ASSERT(!conditions.CacheableWithoutDependencies() || \ + aRuleDetail == eRuleNone || \ + aRuleDetail == eRulePartialReset || \ + aRuleDetail == eRuleFullReset, \ + "conditions.CacheableWithoutDependencies() must be false " \ + "for reset structs if any properties were specified as " \ + "inherit"); \ if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ @@ -4845,13 +4845,12 @@ nsRuleNode::ComputeTextData(void* aStartStruct, } } - // text-align: enum, string, pair(enum|string), inherit, initial // NOTE: string is not implemented yet. const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); text->mTextAlignTrue = false; if (eCSSUnit_String == textAlignValue->GetUnit()) { - NS_NOTYETIMPLEMENTED("align string"); + MOZ_ASSERT_UNREACHABLE("align string"); } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() && NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT == textAlignValue->GetIntValue()) { @@ -4893,7 +4892,7 @@ nsRuleNode::ComputeTextData(void* aStartStruct, textAlignValue = &textAlignValuePair.mYValue; } } else if (eCSSUnit_String == textAlignValue->GetUnit()) { - NS_NOTYETIMPLEMENTED("align string"); + MOZ_ASSERT_UNREACHABLE("align string"); } } else if (eCSSUnit_Inherit == textAlignValue->GetUnit() || eCSSUnit_Unset == textAlignValue->GetUnit()) { diff --git a/layout/style/nsRuleWalker.h b/layout/style/nsRuleWalker.h index 1442969d9fca..6978e5203de5 100644 --- a/layout/style/nsRuleWalker.h +++ b/layout/style/nsRuleWalker.h @@ -30,7 +30,7 @@ public: protected: void DoForward(nsIStyleRule* aRule) { mCurrent = mCurrent->Transition(aRule, mLevel, mImportance); - NS_POSTCONDITION(mCurrent, "Transition messed up"); + MOZ_ASSERT(mCurrent, "Transition messed up"); } public: diff --git a/layout/svg/SVGGeometryFrame.cpp b/layout/svg/SVGGeometryFrame.cpp index fd455618f47d..8d374bf879dd 100644 --- a/layout/svg/SVGGeometryFrame.cpp +++ b/layout/svg/SVGGeometryFrame.cpp @@ -277,7 +277,7 @@ SVGGeometryFrame::PaintSVG(gfxContext& aContext, // Matrix to the geometry's user space: gfxMatrix newMatrix = - aContext.CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers(); + aContext.CurrentMatrixDouble().PreMultiply(aTransform).NudgeToIntegers(); if (newMatrix.IsSingular()) { return; } @@ -767,7 +767,7 @@ SVGGeometryFrame::Render(gfxContext* aContext, // set it unnecessarily if we return early (it's an expensive operation for // some backends). gfxContextMatrixAutoSaveRestore autoRestoreTransform(aContext); - aContext->SetMatrix(aNewTransform); + aContext->SetMatrixDouble(aNewTransform); if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) { // We don't complicate this code with GetAsSimplePath since the cost of diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index d91ac8aad809..4dd448935e79 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -2943,7 +2943,7 @@ SVGTextDrawPathCallbacks::HandleTextGeometry() } else { // Normal painting. gfxContextMatrixAutoSaveRestore saveMatrix(&mContext); - mContext.SetMatrix(mCanvasTM); + mContext.SetMatrixDouble(mCanvasTM); FillAndStrokeGeometry(); } @@ -3589,7 +3589,7 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, nsPresContext* presContext = PresContext(); - gfxMatrix initialMatrix = aContext.CurrentMatrix(); + gfxMatrix initialMatrix = aContext.CurrentMatrixDouble(); if (mState & NS_FRAME_IS_NONDISPLAY) { // If we are in a canvas DrawWindow call that used the @@ -3650,7 +3650,7 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, gfxContextMatrixAutoSaveRestore matSR(&aContext); aContext.NewPath(); aContext.Multiply(canvasTMForChildren); - gfxMatrix currentMatrix = aContext.CurrentMatrix(); + gfxMatrix currentMatrix = aContext.CurrentMatrixDouble(); RefPtr caret = presContext->PresShell()->GetCaret(); nsRect caretRect; @@ -3687,7 +3687,7 @@ SVGTextFrame::PaintSVG(gfxContext& aContext, gfxMatrix runTransform = run.GetTransformFromUserSpaceForPainting(presContext, item) * currentMatrix; - aContext.SetMatrix(runTransform); + aContext.SetMatrixDouble(runTransform); if (drawMode != DrawMode(0)) { bool paintSVGGlyphs; diff --git a/layout/svg/nsFilterInstance.cpp b/layout/svg/nsFilterInstance.cpp index 86fba10a7ec0..72240d9ad898 100644 --- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -71,7 +71,7 @@ nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame, UniquePtr metrics = UserSpaceMetricsForFrame(aFilteredFrame); gfxContextMatrixAutoSaveRestore autoSR(aCtx); - gfxSize scaleFactors = aCtx->CurrentMatrix().ScaleFactors(true); + gfxSize scaleFactors = aCtx->CurrentMatrixDouble().ScaleFactors(true); if (scaleFactors.IsEmpty()) { return; } @@ -85,7 +85,7 @@ nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame, MOZ_ASSERT(invertible); // Pull scale vector out of aCtx's transform, put all scale factors, which // includes css and css-to-dev-px scale, into scaleMatrixInDevUnits. - aCtx->SetMatrix(reverseScaleMatrix * aCtx->CurrentMatrix()); + aCtx->SetMatrixDouble(reverseScaleMatrix * aCtx->CurrentMatrixDouble()); gfxMatrix scaleMatrixInDevUnits = scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(aFilteredFrame); @@ -414,8 +414,8 @@ nsFilterInstance::BuildSourcePaint(SourceInfo *aSource, MOZ_ASSERT(ctx); // already checked the draw target above gfxContextAutoSaveRestore saver(ctx); - ctx->SetMatrix(mPaintTransform * - gfxMatrix::Translation(-neededRect.TopLeft())); + ctx->SetMatrixDouble(mPaintTransform * + gfxMatrix::Translation(-neededRect.TopLeft())); GeneralPattern pattern; if (aSource == &mFillPaint) { nsSVGUtils::MakeFillPatternFor(mTargetFrame, ctx, &pattern, aImgParams); @@ -483,8 +483,8 @@ nsFilterInstance::BuildSourceImage(DrawTarget *aDest, imgDrawingParams& aImgPara gfxMatrix devPxToCssPxTM = nsSVGUtils::GetCSSPxToDevPxMatrix(mTargetFrame); DebugOnly invertible = devPxToCssPxTM.Invert(); MOZ_ASSERT(invertible); - ctx->SetMatrix(devPxToCssPxTM * mPaintTransform * - gfxMatrix::Translation(-neededRect.TopLeft())); + ctx->SetMatrixDouble(devPxToCssPxTM * mPaintTransform * + gfxMatrix::Translation(-neededRect.TopLeft())); mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty, aImgParams); diff --git a/layout/svg/nsSVGClipPathFrame.cpp b/layout/svg/nsSVGClipPathFrame.cpp index dcb2b2e010fb..1e73c39636b5 100644 --- a/layout/svg/nsSVGClipPathFrame.cpp +++ b/layout/svg/nsSVGClipPathFrame.cpp @@ -63,9 +63,9 @@ nsSVGClipPathFrame::ApplyClipPath(gfxContext& aContext, PrependLocalTransformsTo(GetClipPathTransform(aClippedFrame) * aMatrix, eUserSpaceToParent); gfxMatrix newMatrix = - aContext.CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers(); + aContext.CurrentMatrixDouble().PreMultiply(toChildsUserSpace).NudgeToIntegers(); if (!newMatrix.IsSingular()) { - aContext.SetMatrix(newMatrix); + aContext.SetMatrixDouble(newMatrix); FillRule clipRule = nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule); clipPath = pathElement->GetOrBuildPath(aDrawTarget, clipRule); @@ -103,7 +103,7 @@ nsSVGClipPathFrame::CreateClipMask(gfxContext& aReferenceContext, } static void -ComposeExtraMask(DrawTarget* aTarget, const gfxMatrix& aMaskTransfrom, +ComposeExtraMask(DrawTarget* aTarget, SourceSurface* aExtraMask, const Matrix& aExtraMasksTransform) { MOZ_ASSERT(aExtraMask); @@ -178,14 +178,14 @@ nsSVGClipPathFrame::PaintClipMask(gfxContext& aMaskContext, } // Moz2D transforms in the opposite direction to Thebes - gfxMatrix maskTransfrom = aMaskContext.CurrentMatrix(); + Matrix maskTransfrom = aMaskContext.CurrentMatrix(); maskTransfrom.Invert(); if (aExtraMask) { - ComposeExtraMask(maskDT, maskTransfrom, aExtraMask, aExtraMasksTransform); + ComposeExtraMask(maskDT, aExtraMask, aExtraMasksTransform); } - *aMaskTransform = ToMatrix(maskTransfrom); + *aMaskTransform = maskTransfrom; } void @@ -271,7 +271,7 @@ nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, return nullptr; } maskContext->SetMatrix(aReferenceContext.CurrentMatrix() * - gfxMatrix::Translation(-offset)); + Matrix::Translation(-offset)); PaintClipMask(*maskContext, aClippedFrame, aMatrix, aMaskTransform, aExtraMask, aExtraMasksTransform); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index b3f7bc8c6134..40257a774ca3 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -419,7 +419,7 @@ public: basic->SetTarget(&aContext); gfxContextMatrixAutoSaveRestore autoSR(&aContext); - aContext.SetMatrix(aContext.CurrentMatrix().PreTranslate(-mUserSpaceToFrameSpaceOffset)); + aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(-mUserSpaceToFrameSpaceOffset)); mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder); basic->SetTarget(oldCtx); @@ -440,7 +440,7 @@ static void PaintMaskSurface(const PaintFramesParams& aParams, DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC, const nsTArray& aMaskFrames, - const gfxMatrix& aMaskSurfaceMatrix, + const Matrix& aMaskSurfaceMatrix, const nsPoint& aOffsetToUserSpace) { MOZ_ASSERT(aMaskFrames.Length() > 0); @@ -573,8 +573,8 @@ CreateAndPaintMaskSurface(const PaintFramesParams& aParams, // Set context's matrix on maskContext, offset by the maskSurfaceRect's // position. This makes sure that we combine the masks in device space. - gfxMatrix maskSurfaceMatrix = - ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft()); + Matrix maskSurfaceMatrix = + ctx.CurrentMatrix() * Matrix::Translation(-aParams.maskRect.TopLeft()); PaintMaskSurface(aParams, maskDT, paintResult.opacityApplied ? aOpacity : 1.0, @@ -604,7 +604,7 @@ CreateAndPaintMaskSurface(const PaintFramesParams& aParams, return paintResult; } - paintResult.maskTransform = ToMatrix(maskSurfaceMatrix); + paintResult.maskTransform = maskSurfaceMatrix; if (!paintResult.maskTransform.Invert()) { return paintResult; } @@ -711,8 +711,8 @@ MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams) { EffectOffsets offset = ComputeEffectOffset(aFrame, aParams); - aParams.ctx.SetMatrix( - aParams.ctx.CurrentMatrix().PreTranslate(offset.offsetToUserSpaceInDevPx)); + aParams.ctx.SetMatrixDouble( + aParams.ctx.CurrentMatrixDouble().PreTranslate(offset.offsetToUserSpaceInDevPx)); return offset; } @@ -852,7 +852,7 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams) maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr; clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix, &clipMaskTransform, maskSurface, - ToMatrix(ctx.CurrentMatrix())); + ctx.CurrentMatrix()); } } diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index 2cebedf9424c..02f6eee6b751 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -99,8 +99,8 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams) return nullptr; } - gfxMatrix maskSurfaceMatrix = - context->CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft()); + Matrix maskSurfaceMatrix = + context->CurrentMatrix() * ToMatrix(gfxMatrix::Translation(-maskSurfaceRect.TopLeft())); RefPtr tmpCtx = gfxContext::CreateOrNull(maskDT); MOZ_ASSERT(tmpCtx); // already checked the draw target above @@ -153,7 +153,7 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(MaskParams& aParams) return nullptr; } - *aParams.maskTransform = ToMatrix(maskSurfaceMatrix); + *aParams.maskTransform = maskSurfaceMatrix; return surface.forget(); } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index ffac9adc022b..f0ebd9bc446e 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -579,7 +579,7 @@ public: mTargetCtx = gfxContext::CreateOrNull(targetDT); MOZ_ASSERT(mTargetCtx); // already checked the draw target above mTargetCtx->SetMatrix(mSourceCtx->CurrentMatrix() * - gfxMatrix::Translation(-drawRect.TopLeft())); + Matrix::Translation(-drawRect.TopLeft())); mTargetOffset = drawRect.TopLeft(); @@ -595,7 +595,7 @@ public: RefPtr targetSurf = mTargetCtx->GetDrawTarget()->Snapshot(); gfxContextAutoSaveRestore save(mSourceCtx); - mSourceCtx->SetMatrix(gfxMatrix()); // This will be restored right after. + mSourceCtx->SetMatrix(Matrix()); // This will be restored right after. RefPtr pattern = new gfxPattern(targetSurf, Matrix::Translation(mTargetOffset.x, mTargetOffset.y)); @@ -854,8 +854,8 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame, // have to adjust the scale. gfxMatrix reverseScaleMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aFrame); DebugOnly invertible = reverseScaleMatrix.Invert(); - target->SetMatrix(reverseScaleMatrix * aTransform * - target->CurrentMatrix()); + target->SetMatrixDouble(reverseScaleMatrix * aTransform * + target->CurrentMatrixDouble()); SVGPaintCallback paintCallback; nsFilterInstance::PaintFilteredFrame(aFrame, target, &paintCallback, @@ -1509,7 +1509,7 @@ nsSVGUtils::MakeFillPatternFor(nsIFrame* aFrame, if (ps) { RefPtr pattern = - ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(), + ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrixDouble(), &nsStyleSVG::mFill, fillOpacity, aImgParams); if (pattern) { pattern->CacheColorStops(dt); @@ -1524,12 +1524,12 @@ nsSVGUtils::MakeFillPatternFor(nsIFrame* aFrame, case eStyleSVGPaintType_ContextFill: pattern = aContextPaint->GetFillPattern(dt, fillOpacity, - aContext->CurrentMatrix(), aImgParams); + aContext->CurrentMatrixDouble(), aImgParams); break; case eStyleSVGPaintType_ContextStroke: pattern = aContextPaint->GetStrokePattern(dt, fillOpacity, - aContext->CurrentMatrix(), aImgParams); + aContext->CurrentMatrixDouble(), aImgParams); break; default: ; @@ -1585,7 +1585,7 @@ nsSVGUtils::MakeStrokePatternFor(nsIFrame* aFrame, if (ps) { RefPtr pattern = - ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(), + ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrixDouble(), &nsStyleSVG::mStroke, strokeOpacity, aImgParams); if (pattern) { pattern->CacheColorStops(dt); @@ -1600,12 +1600,12 @@ nsSVGUtils::MakeStrokePatternFor(nsIFrame* aFrame, case eStyleSVGPaintType_ContextFill: pattern = aContextPaint->GetFillPattern(dt, strokeOpacity, - aContext->CurrentMatrix(), aImgParams); + aContext->CurrentMatrixDouble(), aImgParams); break; case eStyleSVGPaintType_ContextStroke: pattern = aContextPaint->GetStrokePattern(dt, strokeOpacity, - aContext->CurrentMatrix(), aImgParams); + aContext->CurrentMatrixDouble(), aImgParams); break; default: ; diff --git a/layout/xul/nsButtonBoxFrame.cpp b/layout/xul/nsButtonBoxFrame.cpp index 56759fa25948..c4e6427c0428 100644 --- a/layout/xul/nsButtonBoxFrame.cpp +++ b/layout/xul/nsButtonBoxFrame.cpp @@ -42,8 +42,7 @@ nsButtonBoxFrame::nsButtonBoxListener::HandleEvent(nsIDOMEvent* aEvent) return NS_OK; } - NS_ABORT(); - + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); return NS_OK; } diff --git a/layout/xul/nsMenuBarListener.cpp b/layout/xul/nsMenuBarListener.cpp index 9c6056166443..a4ee19638035 100644 --- a/layout/xul/nsMenuBarListener.cpp +++ b/layout/xul/nsMenuBarListener.cpp @@ -602,7 +602,6 @@ nsMenuBarListener::HandleEvent(nsIDOMEvent* aEvent) return Fullscreen(aEvent); } - NS_ABORT(); - + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); return NS_OK; } diff --git a/layout/xul/nsSplitterFrame.cpp b/layout/xul/nsSplitterFrame.cpp index d775f2e8ebf8..4c3a4b4e8046 100644 --- a/layout/xul/nsSplitterFrame.cpp +++ b/layout/xul/nsSplitterFrame.cpp @@ -589,7 +589,7 @@ nsSplitterFrameInner::HandleEvent(nsIDOMEvent* aEvent) eventType.EqualsLiteral("mouseout")) return MouseMove(aEvent); - NS_ABORT(); + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); return NS_OK; } diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp index 0c5a5306f1c6..db1378827c2d 100644 --- a/layout/xul/nsXULPopupManager.cpp +++ b/layout/xul/nsXULPopupManager.cpp @@ -2612,8 +2612,7 @@ nsXULPopupManager::HandleEvent(nsIDOMEvent* aEvent) return KeyPress(keyEvent); } - NS_ABORT(); - + MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); return NS_OK; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java index 2b7dd781a5b0..77eacdf434aa 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java @@ -8,9 +8,6 @@ package org.mozilla.gecko.mozglue; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.text.NumberFormat; import java.util.Locale; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -473,15 +470,6 @@ public final class GeckoLoader { private static void setupLocaleEnvironment() { putenv("LANG=" + Locale.getDefault().toString()); - NumberFormat nf = NumberFormat.getInstance(); - if (nf instanceof DecimalFormat) { - DecimalFormat df = (DecimalFormat)nf; - DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); - - putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator()); - putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator()); - putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize()); - } } @SuppressWarnings("serial") diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp index 82ba55d5a70c..e55bbfd030dc 100644 --- a/netwerk/base/nsStandardURL.cpp +++ b/netwerk/base/nsStandardURL.cpp @@ -3054,7 +3054,7 @@ nsStandardURL::SetRef(const nsACString &input) NS_IMETHODIMP nsStandardURL::SetDirectory(const nsACString &input) { - NS_NOTYETIMPLEMENTED(""); + MOZ_ASSERT_UNREACHABLE("SetDirectory"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 07cdf6bb3db6..e31d92c211bf 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -772,7 +772,7 @@ HttpBaseChannel::GetContentLength(int64_t *aContentLength) NS_IMETHODIMP HttpBaseChannel::SetContentLength(int64_t value) { - NS_NOTYETIMPLEMENTED("HttpBaseChannel::SetContentLength"); + MOZ_ASSERT_UNREACHABLE("HttpBaseChannel::SetContentLength"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/old-configure.in b/old-configure.in index 2f8161ad035e..c9215442e759 100644 --- a/old-configure.in +++ b/old-configure.in @@ -5101,6 +5101,7 @@ export HOST_CFLAGS export HOST_CPPFLAGS export HOST_CXXFLAGS export HOST_LDFLAGS +export MOZ_PGO if ! test -e js; then mkdir js diff --git a/rdf/base/nsCompositeDataSource.cpp b/rdf/base/nsCompositeDataSource.cpp index 584ffb06f276..1d837264f4d2 100644 --- a/rdf/base/nsCompositeDataSource.cpp +++ b/rdf/base/nsCompositeDataSource.cpp @@ -1068,7 +1068,7 @@ CompositeDataSourceImpl::ArcLabelsOut(nsIRDFResource* aSource, NS_IMETHODIMP CompositeDataSourceImpl::GetAllResources(nsISimpleEnumerator** aResult) { - NS_NOTYETIMPLEMENTED("CompositeDataSourceImpl::GetAllResources"); + MOZ_ASSERT_UNREACHABLE("CompositeDataSourceImpl::GetAllResources"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/rdf/datasource/nsFileSystemDataSource.cpp b/rdf/datasource/nsFileSystemDataSource.cpp index 722714bc8479..262df6647023 100644 --- a/rdf/datasource/nsFileSystemDataSource.cpp +++ b/rdf/datasource/nsFileSystemDataSource.cpp @@ -250,7 +250,7 @@ FileSystemDataSource::GetSources(nsIRDFResource *property, bool tv, nsISimpleEnumerator **sources /* out */) { -// NS_NOTYETIMPLEMENTED("write me"); +// MOZ_ASSERT_UNREACHABLE("write me"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -705,7 +705,7 @@ NS_IMETHODIMP FileSystemDataSource::ArcLabelsIn(nsIRDFNode *node, nsISimpleEnumerator ** labels /* out */) { -// NS_NOTYETIMPLEMENTED("write me"); +// MOZ_ASSERT_UNREACHABLE("write me"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -762,7 +762,7 @@ FileSystemDataSource::ArcLabelsOut(nsIRDFResource *source, NS_IMETHODIMP FileSystemDataSource::GetAllResources(nsISimpleEnumerator** aCursor) { - NS_NOTYETIMPLEMENTED("sorry!"); + MOZ_ASSERT_UNREACHABLE("sorry!"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/security/sandbox/common/SandboxSettings.cpp b/security/sandbox/common/SandboxSettings.cpp index 1e07edceda41..3150422242ea 100644 --- a/security/sandbox/common/SandboxSettings.cpp +++ b/security/sandbox/common/SandboxSettings.cpp @@ -9,9 +9,14 @@ #include "mozilla/ModuleUtils.h" #include "mozilla/Preferences.h" +#include "prenv.h" + namespace mozilla { int GetEffectiveContentSandboxLevel() { + if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) { + return 0; + } int level = Preferences::GetInt("security.sandbox.content.level"); // On Windows and macOS, enforce a minimum content sandbox level of 1 (except on // Nightly, where it can be set to 0). @@ -23,6 +28,10 @@ int GetEffectiveContentSandboxLevel() { return level; } +bool IsContentSandboxEnabled() { + return GetEffectiveContentSandboxLevel() > 0; +} + class SandboxSettings final : public mozISandboxSettings { public: diff --git a/security/sandbox/common/SandboxSettings.h b/security/sandbox/common/SandboxSettings.h index 03d21e3ffacd..3c1489008ed7 100644 --- a/security/sandbox/common/SandboxSettings.h +++ b/security/sandbox/common/SandboxSettings.h @@ -10,8 +10,12 @@ namespace mozilla { // Return the current sandbox level. This is the // "security.sandbox.content.level" preference, but rounded up to the current -// minimum allowed level. +// minimum allowed level. Returns 0 (disabled) if the env var +// MOZ_DISABLE_CONTENT_SANDBOX is set. int GetEffectiveContentSandboxLevel(); +// Checks whether the effective content sandbox level is > 0. +bool IsContentSandboxEnabled(); + } #endif // mozilla_SandboxPolicies_h diff --git a/security/sandbox/linux/Sandbox.h b/security/sandbox/linux/Sandbox.h index 8e2763bf50d7..79f2a04196f1 100644 --- a/security/sandbox/linux/Sandbox.h +++ b/security/sandbox/linux/Sandbox.h @@ -22,7 +22,7 @@ MOZ_EXPORT void SandboxEarlyInit(GeckoProcessType aType); #ifdef MOZ_CONTENT_SANDBOX // Call only if SandboxInfo::CanSandboxContent() returns true. -// (No-op if MOZ_DISABLE_CONTENT_SANDBOX is set.) +// (No-op if the sandbox is disabled.) // aBrokerFd is the filesystem broker client file descriptor, // or -1 to allow direct filesystem access. // isFileProcess determines whether we allow system wide file reads. diff --git a/security/sandbox/linux/SandboxInfo.cpp b/security/sandbox/linux/SandboxInfo.cpp index f7e66441dbbc..46130860d783 100644 --- a/security/sandbox/linux/SandboxInfo.cpp +++ b/security/sandbox/linux/SandboxInfo.cpp @@ -19,6 +19,7 @@ #include "base/posix/eintr_wrapper.h" #include "mozilla/Assertions.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/SandboxSettings.h" #include "sandbox/linux/system_headers/linux_seccomp.h" #include "sandbox/linux/system_headers/linux_syscalls.h" @@ -226,6 +227,9 @@ SandboxInfo::SandboxInfo() { } #ifdef MOZ_CONTENT_SANDBOX + // We can't use mozilla::IsContentSandboxEnabled() here because a) + // libmozsandbox can't depend on libxul, and b) this is called in a static + // initializer before the prefences service is ready. if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) { flags |= kEnabledForContent; } diff --git a/security/sandbox/linux/SandboxInfo.h b/security/sandbox/linux/SandboxInfo.h index 9efba83c2f19..521f5fadb668 100644 --- a/security/sandbox/linux/SandboxInfo.h +++ b/security/sandbox/linux/SandboxInfo.h @@ -25,7 +25,8 @@ public: enum Flags { // System call filtering; kernel config option CONFIG_SECCOMP_FILTER. kHasSeccompBPF = 1 << 0, - // Config flag MOZ_CONTENT_SANDBOX; env var MOZ_DISABLE_CONTENT_SANDBOX. + // Config flag MOZ_CONTENT_SANDBOX; runtime + // mozilla::IsContentSandboxEnabled(). kEnabledForContent = 1 << 1, // Config flag MOZ_GMP_SANDBOX; env var MOZ_DISABLE_GMP_SANDBOX. kEnabledForMedia = 1 << 2, diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp index 95e2049ef9f2..311d98a02681 100644 --- a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp +++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp @@ -403,7 +403,7 @@ SandboxBrokerPolicyFactory::GetContentPolicy(int aPid, bool aFileProcess) MOZ_ASSERT(NS_IsMainThread()); // File broker usage is controlled through a pref. - if (GetEffectiveContentSandboxLevel() <= 1) { + if (!IsContentSandboxEnabled()) { return nullptr; } diff --git a/services/sync/tests/unit/head_helpers.js b/services/sync/tests/unit/head_helpers.js index 8fa9f71745fe..d744a21e63a9 100644 --- a/services/sync/tests/unit/head_helpers.js +++ b/services/sync/tests/unit/head_helpers.js @@ -16,6 +16,7 @@ Cu.import("resource://testing-common/PlacesTestUtils.jsm"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/PlacesUtils.jsm"); +Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://gre/modules/ObjectUtils.jsm"); add_task(async function head_setup() { diff --git a/services/sync/tests/unit/test_bookmark_decline_undecline.js b/services/sync/tests/unit/test_bookmark_decline_undecline.js index 256b2aad29c3..b737c12cfd60 100644 --- a/services/sync/tests/unit/test_bookmark_decline_undecline.js +++ b/services/sync/tests/unit/test_bookmark_decline_undecline.js @@ -1,7 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-sync/constants.js"); diff --git a/services/sync/tests/unit/test_bookmark_duping.js b/services/sync/tests/unit/test_bookmark_duping.js index 16dce8ce5870..9ab38640559d 100644 --- a/services/sync/tests/unit/test_bookmark_duping.js +++ b/services/sync/tests/unit/test_bookmark_duping.js @@ -1,7 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://services-common/async.js"); Cu.import("resource://gre/modules/Log.jsm"); Cu.import("resource://services-sync/engines.js"); @@ -551,8 +550,8 @@ add_task(async function test_dupe_reparented_to_future_arriving_parent_bookmark( // As the incoming parent is missing the item should have been annotated // with that missing parent. - equal(PlacesUtils.annotations.getItemAnnotation((await store.idForGUID(newGUID)), "sync/parent"), - newParentGUID); + equal(PlacesUtils.annotations.getItemAnnotation((await store.idForGUID(newGUID)), + PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO), newParentGUID); // Check the validator. Sadly, this is known to cause a mismatch between // the server and client views of the tree. diff --git a/services/sync/tests/unit/test_bookmark_engine.js b/services/sync/tests/unit/test_bookmark_engine.js index 9fe3f5100f4a..4f25dd94ca10 100644 --- a/services/sync/tests/unit/test_bookmark_engine.js +++ b/services/sync/tests/unit/test_bookmark_engine.js @@ -1,7 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm"); Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm"); Cu.import("resource://gre/modules/Log.jsm"); @@ -113,15 +112,12 @@ add_task(async function bad_record_allIDs() { await SyncTestingInfrastructure(server); _("Ensure that bad Places queries don't cause an error in getAllIDs."); - let badRecordID = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, - CommonUtils.makeURI("place:folder=1138"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - null); + let badRecord = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "place:folder=1138", + }); - do_check_true(badRecordID > 0); - _("Record is " + badRecordID); - _("Type: " + PlacesUtils.bookmarks.getItemType(badRecordID)); + _("Type: " + badRecord.type); _("Fetching all IDs."); let all = await fetchAllRecordIds(); @@ -131,7 +127,7 @@ add_task(async function bad_record_allIDs() { do_check_true(all.has("toolbar")); _("Clean up."); - PlacesUtils.bookmarks.removeItem(badRecordID); + await PlacesUtils.bookmarks.eraseEverything(); await PlacesSyncUtils.bookmarks.reset(); await promiseStopServer(server); }); @@ -149,24 +145,29 @@ add_task(async function test_processIncoming_error_orderChildren() { try { - let folder1_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0); - let folder1_guid = await store.GUIDForId(folder1_id); + let folder1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder 1", + }); - let fxuri = CommonUtils.makeURI("http://getfirefox.com/"); - let tburi = CommonUtils.makeURI("http://getthunderbird.com/"); - - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk2_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, tburi, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!"); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let bmk2 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getthunderbird.com/", + title: "Get Thunderbird!", + }); // Create a server record for folder1 where we flip the order of // the children. - let folder1_record = await store.createRecord(folder1_guid); + let folder1_record = await store.createRecord(folder1.guid); let folder1_payload = folder1_record.cleartext; folder1_payload.children.reverse(); - collection.insert(folder1_guid, encryptPayload(folder1_payload)); + collection.insert(folder1.guid, encryptPayload(folder1_payload)); // Create a bogus record that when synced down will provoke a // network error which in turn provokes an exception in _processIncoming. @@ -190,14 +191,14 @@ add_task(async function test_processIncoming_error_orderChildren() { ok(!!error); // Verify that the bookmark order has been applied. - folder1_record = await store.createRecord(folder1_guid); + folder1_record = await store.createRecord(folder1.guid); let new_children = folder1_record.children; - do_check_eq(new_children.length, 2); - do_check_eq(new_children[0], folder1_payload.children[0]); - do_check_eq(new_children[1], folder1_payload.children[1]); + do_check_matches(new_children, + [folder1_payload.children[0], folder1_payload.children[1]]); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk1_id), 1); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk2_id), 0); + let localChildIds = await PlacesSyncUtils.bookmarks.fetchChildRecordIds( + folder1.guid); + do_check_matches(localChildIds, [bmk2.guid, bmk1.guid]); } finally { await store.wipe(); @@ -238,19 +239,19 @@ async function test_restoreOrImport(aReplace) { try { - let folder1_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0); - let folder1_guid = await store.GUIDForId(folder1_id); - _("Folder 1: " + folder1_id + ", " + folder1_guid); - - let fxuri = CommonUtils.makeURI("http://getfirefox.com/"); - let tburi = CommonUtils.makeURI("http://getthunderbird.com/"); + let folder1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder 1", + }); _("Create a single record."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk1_guid = await store.GUIDForId(bmk1_id); - _(`Get Firefox!: ${bmk1_id}, ${bmk1_guid}`); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + _(`Get Firefox!: ${bmk1.guid}`); let dirSvc = Cc["@mozilla.org/file/directory_service;1"] .getService(Ci.nsIProperties); @@ -264,12 +265,14 @@ async function test_restoreOrImport(aReplace) { await bookmarkUtils.exportToFile(backupFile.path); _("Create a different record and sync."); - let bmk2_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, tburi, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!"); - let bmk2_guid = await store.GUIDForId(bmk2_id); - _(`Get Thunderbird!: ${bmk2_id}, ${bmk2_guid}`); + let bmk2 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getthunderbird.com/", + title: "Get Thunderbird!", + }); + _(`Get Thunderbird!: ${bmk2.guid}`); - PlacesUtils.bookmarks.removeItem(bmk1_id); + await PlacesUtils.bookmarks.remove(bmk1.guid); let error; try { @@ -283,10 +286,10 @@ async function test_restoreOrImport(aReplace) { _("Verify that there's only one bookmark on the server, and it's Thunderbird."); // Of course, there's also the Bookmarks Toolbar and Bookmarks Menu... let wbos = collection.keys(function(id) { - return ["menu", "toolbar", "mobile", "unfiled", folder1_guid].indexOf(id) == -1; + return ["menu", "toolbar", "mobile", "unfiled", folder1.guid].indexOf(id) == -1; }); do_check_eq(wbos.length, 1); - do_check_eq(wbos[0], bmk2_guid); + do_check_eq(wbos[0], bmk2.guid); _(`Now ${verb} from a backup.`); await bookmarkUtils.importFromFile(backupFile, aReplace); @@ -307,24 +310,24 @@ async function test_restoreOrImport(aReplace) { let count = 0; for (let guid of guids) { count++; - let id = await store.idForGUID(guid, true); + let info = await PlacesUtils.bookmarks.fetch( + PlacesSyncUtils.bookmarks.recordIdToGuid(guid)); // Only one bookmark, so _all_ should be Firefox! - if (PlacesUtils.bookmarks.getItemType(id) == PlacesUtils.bookmarks.TYPE_BOOKMARK) { - let uri = PlacesUtils.bookmarks.getBookmarkURI(id); - _(`Found URI ${uri.spec} for GUID ${guid}`); - bookmarkGuids.set(uri.spec, guid); + if (info.type == PlacesUtils.bookmarks.TYPE_BOOKMARK) { + _(`Found URI ${info.url.href} for GUID ${guid}`); + bookmarkGuids.set(info.url.href, guid); } } - do_check_true(bookmarkGuids.has(fxuri.spec)); + do_check_true(bookmarkGuids.has("http://getfirefox.com/")); if (!aReplace) { - do_check_true(bookmarkGuids.has(tburi.spec)); + do_check_true(bookmarkGuids.has("http://getthunderbird.com/")); } _("Have the correct number of IDs locally, too."); - let expectedResults = ["menu", "toolbar", "mobile", "unfiled", folder1_id, - bmk1_id]; + let expectedResults = ["menu", "toolbar", "mobile", "unfiled", folder1.guid, + bmk1.guid]; if (!aReplace) { - expectedResults.push("toolbar", folder1_id, bmk2_id); + expectedResults.push("toolbar", folder1.guid, bmk2.guid); } do_check_eq(count, expectedResults.length); @@ -354,13 +357,13 @@ async function test_restoreOrImport(aReplace) { }); let expectedFX = { - id: bookmarkGuids.get(fxuri.spec), - bmkUri: fxuri.spec, + id: bookmarkGuids.get("http://getfirefox.com/"), + bmkUri: "http://getfirefox.com/", title: "Get Firefox!" }; let expectedTB = { - id: bookmarkGuids.get(tburi.spec), - bmkUri: tburi.spec, + id: bookmarkGuids.get("http://getthunderbird.com/"), + bmkUri: "http://getthunderbird.com/", title: "Get Thunderbird!" }; @@ -458,10 +461,7 @@ add_task(async function test_mismatched_types() { let server = await serverForFoo(engine); await SyncTestingInfrastructure(server); - _("GUID: " + (await store.GUIDForId(6, true))); - try { - let bms = PlacesUtils.bookmarks; let oldR = new FakeRecord(BookmarkFolder, oldRecord); let newR = new FakeRecord(Livemark, newRecord); oldR.parentid = PlacesUtils.bookmarks.toolbarGuid; @@ -469,18 +469,20 @@ add_task(async function test_mismatched_types() { await store.applyIncoming(oldR); _("Applied old. It's a folder."); - let oldID = await store.idForGUID(oldR.id); + let oldID = await PlacesUtils.promiseItemId(oldR.id); _("Old ID: " + oldID); - do_check_eq(bms.getItemType(oldID), bms.TYPE_FOLDER); + let oldInfo = await PlacesUtils.bookmarks.fetch(oldR.id); + do_check_eq(oldInfo.type, PlacesUtils.bookmarks.TYPE_FOLDER); do_check_false(PlacesUtils.annotations .itemHasAnnotation(oldID, PlacesUtils.LMANNO_FEEDURI)); await store.applyIncoming(newR); - let newID = await store.idForGUID(newR.id); + let newID = await PlacesUtils.promiseItemId(newR.id); _("New ID: " + newID); _("Applied new. It's a livemark."); - do_check_eq(bms.getItemType(newID), bms.TYPE_FOLDER); + let newInfo = await PlacesUtils.bookmarks.fetch(newR.id); + do_check_eq(newInfo.type, PlacesUtils.bookmarks.TYPE_FOLDER); do_check_true(PlacesUtils.annotations .itemHasAnnotation(newID, PlacesUtils.LMANNO_FEEDURI)); @@ -506,12 +508,14 @@ add_task(async function test_bookmark_guidMap_fail() { await SyncTestingInfrastructure(server); // Add one item to the server. - let itemID = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder 1", 0); - let itemGUID = await store.GUIDForId(itemID); - let itemRecord = await store.createRecord(itemGUID); + let item = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder 1", + }); + let itemRecord = await store.createRecord(item.guid); let itemPayload = itemRecord.cleartext; - coll.insert(itemGUID, encryptPayload(itemPayload)); + coll.insert(item.guid, encryptPayload(itemPayload)); engine.lastSync = 1; // So we don't back up. @@ -611,11 +615,13 @@ add_task(async function test_misreconciled_root() { // Let's find out where the toolbar is right now. let toolbarBefore = await store.createRecord("toolbar", "bookmarks"); - let toolbarIDBefore = await store.idForGUID("toolbar"); + let toolbarIDBefore = await PlacesUtils.promiseItemId( + PlacesUtils.bookmarks.toolbarGuid); do_check_neq(-1, toolbarIDBefore); let parentGUIDBefore = toolbarBefore.parentid; - let parentIDBefore = await store.idForGUID(parentGUIDBefore); + let parentIDBefore = await PlacesUtils.promiseItemId( + PlacesSyncUtils.bookmarks.recordIdToGuid(parentGUIDBefore)); do_check_neq(-1, parentIDBefore); do_check_eq("string", typeof(parentGUIDBefore)); @@ -641,8 +647,10 @@ add_task(async function test_misreconciled_root() { // the real GUID, instead using a generated one. Sync does the translation. let toolbarAfter = await store.createRecord("toolbar", "bookmarks"); let parentGUIDAfter = toolbarAfter.parentid; - let parentIDAfter = await store.idForGUID(parentGUIDAfter); - do_check_eq((await store.GUIDForId(toolbarIDBefore)), "toolbar"); + let parentIDAfter = await PlacesUtils.promiseItemId( + PlacesSyncUtils.bookmarks.recordIdToGuid(parentGUIDAfter)); + do_check_eq((await PlacesUtils.promiseItemGuid(toolbarIDBefore)), + PlacesUtils.bookmarks.toolbarGuid); do_check_eq(parentGUIDBefore, parentGUIDAfter); do_check_eq(parentIDBefore, parentIDAfter); @@ -760,12 +768,12 @@ add_task(async function test_sync_dateAdded() { collection.insert(item2GUID, encryptPayload(item2.cleartext), now / 1000 - 50); - // Also, add a local bookmark and make sure it's date added makes it up to the server - let bzid = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarksMenuFolderId, CommonUtils.makeURI("https://bugzilla.mozilla.org/"), - PlacesUtils.bookmarks.DEFAULT_INDEX, "Bugzilla"); - - let bzguid = await PlacesUtils.promiseItemGuid(bzid); + // Also, add a local bookmark and make sure its date added makes it up to the server + let bz = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.menuGuid, + url: "https://bugzilla.mozilla.org/", + title: "Bugzilla", + }); // last sync did a POST, which doesn't advance its lastModified value. // Next sync of the engine doesn't hit info/collections, so lastModified @@ -776,10 +784,10 @@ add_task(async function test_sync_dateAdded() { let newRecord2 = await store.createRecord(item2GUID); equal(newRecord2.dateAdded, item2.dateAdded, "dateAdded update should work for earlier date"); - let bzWBO = JSON.parse(JSON.parse(collection._wbos[bzguid].payload).ciphertext); + let bzWBO = JSON.parse(JSON.parse(collection._wbos[bz.guid].payload).ciphertext); ok(bzWBO.dateAdded, "Locally added dateAdded lost"); - let localRecord = await store.createRecord(bzguid); + let localRecord = await store.createRecord(bz.guid); equal(bzWBO.dateAdded, localRecord.dateAdded, "dateAdded should not change during upload"); item2.dateAdded += 10000; diff --git a/services/sync/tests/unit/test_bookmark_invalid.js b/services/sync/tests/unit/test_bookmark_invalid.js index 3b211eb7251d..b9db537d4438 100644 --- a/services/sync/tests/unit/test_bookmark_invalid.js +++ b/services/sync/tests/unit/test_bookmark_invalid.js @@ -19,18 +19,19 @@ add_task(async function test_ignore_invalid_uri() { _("Ensure that we don't die with invalid bookmarks."); // First create a valid bookmark. - let bmid = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, - Services.io.newURI("http://example.com/"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - "the title"); + let bmInfo = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/", + title: "the title", + }); // Now update moz_places with an invalid url. await PlacesUtils.withConnectionWrapper("test_ignore_invalid_uri", async function(db) { await db.execute( `UPDATE moz_places SET url = :url, url_hash = hash(:url) WHERE id = (SELECT b.fk FROM moz_bookmarks b - WHERE b.id = :id LIMIT 1)`, - { id: bmid, url: "" }); + WHERE b.guid = :guid)`, + { guid: bmInfo.guid, url: "" }); }); // Ensure that this doesn't throw even though the DB is now in a bad state (a @@ -42,17 +43,18 @@ add_task(async function test_ignore_missing_uri() { _("Ensure that we don't die with a bookmark referencing an invalid bookmark id."); // First create a valid bookmark. - let bmid = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId, - Services.io.newURI("http://example.com/"), - PlacesUtils.bookmarks.DEFAULT_INDEX, - "the title"); + let bmInfo = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.unfiledGuid, + url: "http://example.com/", + title: "the title", + }); // Now update moz_bookmarks to reference a non-existing places ID await PlacesUtils.withConnectionWrapper("test_ignore_missing_uri", async function(db) { await db.execute( `UPDATE moz_bookmarks SET fk = 999999 - WHERE id = :id` - , { id: bmid }); + WHERE guid = :guid` + , { guid: bmInfo.guid }); }); // Ensure that this doesn't throw even though the DB is now in a bad state (a diff --git a/services/sync/tests/unit/test_bookmark_livemarks.js b/services/sync/tests/unit/test_bookmark_livemarks.js index 297e376be050..7dcbda772257 100644 --- a/services/sync/tests/unit/test_bookmark_livemarks.js +++ b/services/sync/tests/unit/test_bookmark_livemarks.js @@ -86,7 +86,7 @@ add_task(async function test_livemark_descriptions() { await doRecord(makeLivemark(record)); // Attempt to provoke an error by adding a bad description anno. - let id = await store.idForGUID(record.id); + let id = await PlacesUtils.promiseItemId(record.id); PlacesUtils.annotations.setItemAnnotation(id, DESCRIPTION_ANNO, "", 0, PlacesUtils.annotations.EXPIRE_NEVER); @@ -102,29 +102,27 @@ add_task(async function test_livemark_invalid() { _("Parent is unknown. Will be set to unfiled."); let lateParentRec = makeLivemark(record631361.payload, true); - let parentGUID = Utils.makeGUID(); - lateParentRec.parentid = parentGUID; - do_check_eq(-1, (await store.idForGUID(parentGUID))); + lateParentRec.parentid = Utils.makeGUID(); await store.create(lateParentRec); - let recID = await store.idForGUID(lateParentRec.id, true); - do_check_true(recID > 0); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(recID), - PlacesUtils.bookmarks.unfiledBookmarksFolder); + let recInfo = await PlacesUtils.bookmarks.fetch(lateParentRec.id); + do_check_eq(recInfo.parentGuid, PlacesUtils.bookmarks.unfiledGuid); _("No feed URI, which is invalid. Will be skipped."); let noFeedURIRec = makeLivemark(record631361.payload, true); delete noFeedURIRec.cleartext.feedUri; await store.create(noFeedURIRec); // No exception, but no creation occurs. - do_check_eq(-1, (await store.idForGUID(noFeedURIRec.id, true))); + let noFeedURIItem = await PlacesUtils.bookmarks.fetch(noFeedURIRec.id); + do_check_null(noFeedURIItem); _("Parent is a Livemark. Will be skipped."); let lmParentRec = makeLivemark(record631361.payload, true); - lmParentRec.parentid = await store.GUIDForId(recID); + lmParentRec.parentid = recInfo.guid; await store.create(lmParentRec); // No exception, but no creation occurs. - do_check_eq(-1, (await store.idForGUID(lmParentRec.id, true))); + let lmParentItem = await PlacesUtils.bookmarks.fetch(lmParentRec.id); + do_check_null(lmParentItem); await engine.finalize(); }); diff --git a/services/sync/tests/unit/test_bookmark_places_query_rewriting.js b/services/sync/tests/unit/test_bookmark_places_query_rewriting.js index 0e75c51246df..66d92fa68282 100644 --- a/services/sync/tests/unit/test_bookmark_places_query_rewriting.js +++ b/services/sync/tests/unit/test_bookmark_places_query_rewriting.js @@ -32,18 +32,16 @@ add_task(async function run_test() { _("Folder name: " + tagRecord.folderName); await store.applyIncoming(tagRecord); - let tags = PlacesUtils.getFolderContents(PlacesUtils.tagsFolderId).root; - let tagID; - try { - for (let i = 0; i < tags.childCount; ++i) { - let child = tags.getChild(i); - if (child.title == "bar") { - tagID = child.itemId; - } - } - } finally { - tags.containerOpen = false; - } + let tagID = -1; + let db = await PlacesUtils.promiseDBConnection(); + let rows = await db.execute(` + SELECT id FROM moz_bookmarks + WHERE parent = :tagsFolderId AND + title = :title`, + { tagsFolderId: PlacesUtils.tagsFolderId, + title: "bar" }); + equal(rows.length, 1); + tagID = rows[0].getResultByName("id"); _("Tag ID: " + tagID); let insertedRecord = await store.createRecord("abcdefabcdef", "bookmarks"); diff --git a/services/sync/tests/unit/test_bookmark_repair_responder.js b/services/sync/tests/unit/test_bookmark_repair_responder.js index 9655c02ab76e..e2e4620a32c3 100644 --- a/services/sync/tests/unit/test_bookmark_repair_responder.js +++ b/services/sync/tests/unit/test_bookmark_repair_responder.js @@ -1,8 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); -Cu.import("resource://testing-common/PlacesTestUtils.jsm"); Cu.import("resource:///modules/PlacesUIUtils.jsm"); Cu.import("resource://gre/modules/Log.jsm"); diff --git a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js index a6d07d6d244d..647856eca764 100644 --- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js +++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js @@ -9,10 +9,6 @@ Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://testing-common/services/sync/utils.js"); -const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark"; -const IOService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); - async function newSmartBookmark(parentGuid, url, position, title, queryID) { let info = await PlacesUtils.bookmarks.insert({ parentGuid, @@ -21,9 +17,9 @@ async function newSmartBookmark(parentGuid, url, position, title, queryID) { title, }); let id = await PlacesUtils.promiseItemId(info.guid); - PlacesUtils.annotations.setItemAnnotation(id, SMART_BOOKMARKS_ANNO, - queryID, 0, - PlacesUtils.annotations.EXPIRE_NEVER); + PlacesUtils.annotations.setItemAnnotation(id, + PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO, queryID, 0, + PlacesUtils.annotations.EXPIRE_NEVER); return info; } @@ -31,18 +27,11 @@ function smartBookmarkCount() { // We do it this way because PlacesUtils.annotations.getItemsWithAnnotation // doesn't work the same (or at all?) between 3.6 and 4.0. let out = {}; - PlacesUtils.annotations.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, out); + PlacesUtils.annotations.getItemsWithAnnotation( + PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO, out); return out.value; } -function clearBookmarks() { - _("Cleaning up existing items."); - PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.bookmarksMenuFolder); - PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.tagsFolder); - PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.toolbarFolder); - PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.bookmarks.unfiledBookmarksFolder); -} - let engine; let store; @@ -65,16 +54,13 @@ add_task(async function test_annotation_uploaded() { let server = await serverForFoo(engine); await SyncTestingInfrastructure(server); + _("Cleaning up existing items."); + await PlacesUtils.bookmarks.eraseEverything(); + let startCount = smartBookmarkCount(); _("Start count is " + startCount); - if (startCount > 0) { - // This can happen in XULRunner. - clearBookmarks(); - _("Start count is now " + startCount); - } - _("Create a smart bookmark in the toolbar."); let url = "place:sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING + @@ -89,11 +75,11 @@ add_task(async function test_annotation_uploaded() { do_check_true(!!mostVisitedID); let annoValue = PlacesUtils.annotations.getItemAnnotation(mostVisitedID, - SMART_BOOKMARKS_ANNO); + PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO); _("Anno: " + annoValue); do_check_eq("MostVisited", annoValue); - let guid = await store.GUIDForId(mostVisitedID); + let guid = await PlacesUtils.promiseItemGuid(mostVisitedID); _("GUID: " + guid); do_check_true(!!guid); @@ -135,11 +121,13 @@ add_task(async function test_annotation_uploaded() { // "Clear" by changing attributes: if we delete it, apparently it sticks // around as a deleted record... - PlacesUtils.bookmarks.setItemTitle(mostVisitedID, "Not Most Visited"); - PlacesUtils.bookmarks.changeBookmarkURI( - mostVisitedID, CommonUtils.makeURI("http://something/else")); + await PlacesUtils.bookmarks.update({ + guid: mostVisitedInfo.guid, + title: "Not Most Visited", + url: "http://something/else", + }); PlacesUtils.annotations.removeItemAnnotation(mostVisitedID, - SMART_BOOKMARKS_ANNO); + PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO); await store.wipe(); await engine.resetClient(); do_check_eq(smartBookmarkCount(), startCount); @@ -152,11 +140,12 @@ add_task(async function test_annotation_uploaded() { do_check_eq(smartBookmarkCount(), startCount + 1); _("Find by GUID and verify that it's annotated."); - let newID = await store.idForGUID(serverGUID); + let newID = await PlacesUtils.promiseItemId(serverGUID); let newAnnoValue = PlacesUtils.annotations.getItemAnnotation( - newID, SMART_BOOKMARKS_ANNO); + newID, PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO); do_check_eq(newAnnoValue, "MostVisited"); - do_check_eq(PlacesUtils.bookmarks.getBookmarkURI(newID).spec, url); + let newInfo = await PlacesUtils.bookmarks.fetch(serverGUID); + do_check_eq(newInfo.url.href, url); _("Test updating."); let newRecord = await store.createRecord(serverGUID); @@ -166,7 +155,7 @@ add_task(async function test_annotation_uploaded() { engine.lastModified = collection.timestamp + 1; await sync_engine_and_validate_telem(engine, false); do_check_eq("LeastVisited", PlacesUtils.annotations.getItemAnnotation( - newID, SMART_BOOKMARKS_ANNO)); + newID, PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO)); } finally { // Clean up. diff --git a/services/sync/tests/unit/test_bookmark_store.js b/services/sync/tests/unit/test_bookmark_store.js index dee98819f07c..39a7f7eb088d 100644 --- a/services/sync/tests/unit/test_bookmark_store.js +++ b/services/sync/tests/unit/test_bookmark_store.js @@ -1,18 +1,12 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://services-common/utils.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/util.js"); -const PARENT_ANNO = "sync/parent"; - -const fxuri = CommonUtils.makeURI("http://getfirefox.com/"); -const tburi = CommonUtils.makeURI("http://getthunderbird.com/"); - add_task(async function test_ignore_specials() { _("Ensure that we can't delete bookmark roots."); @@ -23,23 +17,23 @@ add_task(async function test_ignore_specials() { let record = new BookmarkFolder("bookmarks", "toolbar", "folder"); record.deleted = true; do_check_neq(null, (await PlacesUtils.promiseItemId( - PlacesSyncUtils.bookmarks.recordIdToGuid("toolbar")))); + PlacesUtils.bookmarks.toolbarGuid))); await store.applyIncoming(record); await store.deletePending(); // Ensure that the toolbar exists. do_check_neq(null, (await PlacesUtils.promiseItemId( - PlacesSyncUtils.bookmarks.recordIdToGuid("toolbar")))); + PlacesUtils.bookmarks.toolbarGuid))); - // This will fail painfully in getItemType if the deletion worked. + // This will fail to build the local tree if the deletion worked. await engine._buildGUIDMap(); // Braces... await store.remove(record); await store.deletePending(); do_check_neq(null, (await PlacesUtils.promiseItemId( - PlacesSyncUtils.bookmarks.recordIdToGuid("toolbar")))); + PlacesUtils.bookmarks.toolbarGuid))); await engine._buildGUIDMap(); await store.wipe(); @@ -53,12 +47,14 @@ add_task(async function test_bookmark_create() { try { _("Ensure the record isn't present yet."); - let ids = PlacesUtils.bookmarks.getBookmarkIdsForURI(fxuri, {}); - do_check_eq(ids.length, 0); + let item = await PlacesUtils.bookmarks.fetch({ + url: "http://getfirefox.com/", + }); + do_check_null(item); _("Let's create a new record."); let fxrecord = new Bookmark("bookmarks", "get-firefox1"); - fxrecord.bmkUri = fxuri.spec; + fxrecord.bmkUri = "http://getfirefox.com/"; fxrecord.description = "Firefox is awesome."; fxrecord.title = "Get Firefox!"; fxrecord.tags = ["firefox", "awesome", "browser"]; @@ -69,17 +65,17 @@ add_task(async function test_bookmark_create() { await store.applyIncoming(fxrecord); _("Verify it has been created correctly."); - let id = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(fxrecord.id)); - do_check_eq((await PlacesUtils.promiseItemGuid(id)), fxrecord.id); - do_check_eq(PlacesUtils.bookmarks.getItemType(id), - PlacesUtils.bookmarks.TYPE_BOOKMARK); - do_check_true(PlacesUtils.bookmarks.getBookmarkURI(id).equals(fxuri)); - do_check_eq(PlacesUtils.bookmarks.getItemTitle(id), fxrecord.title); - do_check_eq(PlacesUtils.annotations.getItemAnnotation(id, "bookmarkProperties/description"), - fxrecord.description); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(id), - PlacesUtils.bookmarks.toolbarFolder); - do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(id), fxrecord.keyword); + item = await PlacesUtils.bookmarks.fetch(fxrecord.id); + do_check_eq(item.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); + do_check_eq(item.url.href, "http://getfirefox.com/"); + do_check_eq(item.title, fxrecord.title); + let id = await PlacesUtils.promiseItemId(item.guid); + let description = PlacesUtils.annotations.getItemAnnotation(id, + PlacesSyncUtils.bookmarks.DESCRIPTION_ANNO); + do_check_eq(description, fxrecord.description); + do_check_eq(item.parentGuid, PlacesUtils.bookmarks.toolbarGuid); + let keyword = await PlacesUtils.keywords.fetch(fxrecord.keyword); + do_check_eq(keyword.url.href, "http://getfirefox.com/"); _("Have the store create a new record object. Verify that it has the same data."); let newrecord = await store.createRecord(fxrecord.id); @@ -96,28 +92,26 @@ add_task(async function test_bookmark_create() { _("Create a record with some values missing."); let tbrecord = new Bookmark("bookmarks", "thunderbird1"); - tbrecord.bmkUri = tburi.spec; + tbrecord.bmkUri = "http://getthunderbird.com/"; tbrecord.parentName = "Bookmarks Toolbar"; tbrecord.parentid = "toolbar"; await store.applyIncoming(tbrecord); _("Verify it has been created correctly."); - id = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(tbrecord.id)); - do_check_eq((await PlacesUtils.promiseItemGuid(id)), tbrecord.id); - do_check_eq(PlacesUtils.bookmarks.getItemType(id), - PlacesUtils.bookmarks.TYPE_BOOKMARK); - do_check_true(PlacesUtils.bookmarks.getBookmarkURI(id).equals(tburi)); - do_check_eq(PlacesUtils.bookmarks.getItemTitle(id), ""); - let error; - try { - PlacesUtils.annotations.getItemAnnotation(id, "bookmarkProperties/description"); - } catch (ex) { - error = ex; - } - do_check_eq(error.result, Cr.NS_ERROR_NOT_AVAILABLE); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(id), - PlacesUtils.bookmarks.toolbarFolder); - do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(id), null); + item = await PlacesUtils.bookmarks.fetch(tbrecord.id); + id = await PlacesUtils.promiseItemId(item.guid); + do_check_eq(item.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); + do_check_eq(item.url.href, "http://getthunderbird.com/"); + do_check_eq(item.title, ""); + do_check_throws(function() { + PlacesUtils.annotations.getItemAnnotation(id, + PlacesSyncUtils.bookmarks.DESCRIPTION_ANNO); + }, Cr.NS_ERROR_NOT_AVAILABLE); + do_check_eq(item.parentGuid, PlacesUtils.bookmarks.toolbarGuid); + keyword = await PlacesUtils.keywords.fetch({ + url: "http://getthunderbird.com/", + }); + do_check_null(keyword); } finally { _("Clean up."); await store.wipe(); @@ -131,18 +125,22 @@ add_task(async function test_bookmark_update() { try { _("Create a bookmark whose values we'll change."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Get Firefox!"); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let bmk1_id = await PlacesUtils.promiseItemId(bmk1.guid); PlacesUtils.annotations.setItemAnnotation( - bmk1_id, "bookmarkProperties/description", "Firefox is awesome.", 0, - PlacesUtils.annotations.EXPIRE_NEVER); - PlacesUtils.bookmarks.setKeywordForBookmark(bmk1_id, "firefox"); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); + bmk1_id, PlacesSyncUtils.bookmarks.DESCRIPTION_ANNO, + "Firefox is awesome.", 0, PlacesUtils.annotations.EXPIRE_NEVER); + await PlacesUtils.keywords.insert({ + url: "http://getfirefox.com/", + keyword: "firefox", + }); _("Update the record with some null values."); - let record = await store.createRecord(bmk1_guid); + let record = await store.createRecord(bmk1.guid); record.title = null; record.description = null; record.keyword = null; @@ -152,10 +150,14 @@ add_task(async function test_bookmark_update() { _("Verify that the values have been cleared."); do_check_throws(function() { PlacesUtils.annotations.getItemAnnotation( - bmk1_id, "bookmarkProperties/description"); + bmk1_id, PlacesSyncUtils.bookmarks.DESCRIPTION_ANNO); }, Cr.NS_ERROR_NOT_AVAILABLE); - do_check_eq(PlacesUtils.bookmarks.getItemTitle(bmk1_id), ""); - do_check_eq(PlacesUtils.bookmarks.getKeywordForBookmark(bmk1_id), null); + let item = await PlacesUtils.bookmarks.fetch(bmk1.guid); + do_check_eq(item.title, ""); + let keyword = await PlacesUtils.keywords.fetch({ + url: "http://getfirefox.com/", + }); + do_check_null(keyword); } finally { _("Clean up."); await store.wipe(); @@ -169,13 +171,13 @@ add_task(async function test_bookmark_createRecord() { try { _("Create a bookmark without a description or title."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, null); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + }); _("Verify that the record is created accordingly."); - let record = await store.createRecord(bmk1_guid); + let record = await store.createRecord(bmk1.guid); do_check_eq(record.title, ""); do_check_eq(record.description, null); do_check_eq(record.keyword, null); @@ -199,18 +201,17 @@ add_task(async function test_folder_create() { await store.applyIncoming(folder); _("Verify it has been created correctly."); - let id = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(folder.id)); - do_check_eq(PlacesUtils.bookmarks.getItemType(id), - PlacesUtils.bookmarks.TYPE_FOLDER); - do_check_eq(PlacesUtils.bookmarks.getItemTitle(id), folder.title); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(id), - PlacesUtils.bookmarks.toolbarFolder); + let item = await PlacesUtils.bookmarks.fetch(folder.id); + do_check_eq(item.type, PlacesUtils.bookmarks.TYPE_FOLDER); + do_check_eq(item.title, folder.title); + do_check_eq(item.parentGuid, PlacesUtils.bookmarks.toolbarGuid); _("Have the store create a new record object. Verify that it has the same data."); let newrecord = await store.createRecord(folder.id); do_check_true(newrecord instanceof BookmarkFolder); - for (let property of ["title", "parentName", "parentid"]) + for (let property of ["title", "parentName", "parentid"]) { do_check_eq(newrecord[property], folder[property]); + } _("Folders have high sort index to ensure they're synced first."); do_check_eq(newrecord.sortindex, 1000000); @@ -227,29 +228,33 @@ add_task(async function test_folder_createRecord() { try { _("Create a folder."); - let folder1_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder1", 0); - let folder1_guid = await PlacesUtils.promiseItemGuid(folder1_id); + let folder1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder1", + }); _("Create two bookmarks in that folder without assigning them GUIDs."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk2_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, tburi, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!"); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let bmk2 = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getthunderbird.com/", + title: "Get Thunderbird!", + }); _("Create a record for the folder and verify basic properties."); - let record = await store.createRecord(folder1_guid); + let record = await store.createRecord(folder1.guid); do_check_true(record instanceof BookmarkFolder); do_check_eq(record.title, "Folder1"); do_check_eq(record.parentid, "toolbar"); do_check_eq(record.parentName, "Bookmarks Toolbar"); _("Verify the folder's children. Ensures that the bookmarks were given GUIDs."); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); - let bmk2_guid = await PlacesUtils.promiseItemGuid(bmk2_id); - do_check_eq(record.children.length, 2); - do_check_eq(record.children[0], bmk1_guid); - do_check_eq(record.children[1], bmk2_guid); + do_check_matches(record.children, [bmk1.guid, bmk2.guid]); } finally { _("Clean up."); @@ -263,26 +268,22 @@ add_task(async function test_deleted() { try { _("Create a bookmark that will be deleted."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); _("Delete the bookmark through the store."); - let record = new PlacesItem("bookmarks", bmk1_guid); + let record = new PlacesItem("bookmarks", bmk1.guid); record.deleted = true; await store.applyIncoming(record); await store.deletePending(); _("Ensure it has been deleted."); - let error; - try { - PlacesUtils.bookmarks.getBookmarkURI(bmk1_id); - } catch (ex) { - error = ex; - } - do_check_eq(error.result, Cr.NS_ERROR_ILLEGAL_VALUE); + let item = await PlacesUtils.bookmarks.fetch(bmk1.guid); + do_check_null(item); - let newrec = await store.createRecord(bmk1_guid); + let newrec = await store.createRecord(bmk1.guid); do_check_eq(newrec.deleted, true); } finally { @@ -298,25 +299,31 @@ add_task(async function test_move_folder() { try { _("Create two folders and a bookmark in one of them."); - let folder1_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder1", 0); - let folder1_guid = await PlacesUtils.promiseItemGuid(folder1_id); - let folder2_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder2", 0); - let folder2_guid = await PlacesUtils.promiseItemGuid(folder2_id); - let bmk_id = PlacesUtils.bookmarks.insertBookmark( - folder1_id, fxuri, PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk_guid = await PlacesUtils.promiseItemGuid(bmk_id); + let folder1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder1", + }); + let folder2 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder2", + }); + let bmk = await PlacesUtils.bookmarks.insert({ + parentGuid: folder1.guid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); _("Get a record, reparent it and apply it to the store."); - let record = await store.createRecord(bmk_guid); - do_check_eq(record.parentid, folder1_guid); - record.parentid = folder2_guid; + let record = await store.createRecord(bmk.guid); + do_check_eq(record.parentid, folder1.guid); + record.parentid = folder2.guid; await store.applyIncoming(record); _("Verify the new parent."); - let new_folder_id = PlacesUtils.bookmarks.getFolderIdForItem(bmk_id); - do_check_eq((await PlacesUtils.promiseItemGuid(new_folder_id)), folder2_guid); + let movedBmk = await PlacesUtils.bookmarks.fetch(bmk.guid); + do_check_eq(movedBmk.parentGuid, folder2.guid); } finally { _("Clean up."); await store.wipe(); @@ -333,26 +340,27 @@ add_task(async function test_move_order() { Svc.Obs.notify("weave:engine:start-tracking"); try { _("Create two bookmarks"); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); - let bmk2_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, tburi, - PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Thunderbird!"); - let bmk2_guid = await PlacesUtils.promiseItemGuid(bmk2_id); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let bmk2 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getthunderbird.com/", + title: "Get Thunderbird!", + }); _("Verify order."); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk1_id), 0); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk2_id), 1); + let childIds = await PlacesSyncUtils.bookmarks.fetchChildRecordIds( + "toolbar"); + do_check_matches(childIds, [bmk1.guid, bmk2.guid]); let toolbar = await store.createRecord("toolbar"); - do_check_eq(toolbar.children.length, 2); - do_check_eq(toolbar.children[0], bmk1_guid); - do_check_eq(toolbar.children[1], bmk2_guid); + do_check_matches(toolbar.children, [bmk1.guid, bmk2.guid]); _("Move bookmarks around."); store._childrenToOrder = {}; - toolbar.children = [bmk2_guid, bmk1_guid]; + toolbar.children = [bmk2.guid, bmk1.guid]; await store.applyIncoming(toolbar); // Bookmarks engine does this at the end of _processIncoming tracker.ignoreAll = true; @@ -361,8 +369,9 @@ add_task(async function test_move_order() { delete store._childrenToOrder; _("Verify new order."); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk2_id), 0); - do_check_eq(PlacesUtils.bookmarks.getItemIndex(bmk1_id), 1); + let newChildIds = await PlacesSyncUtils.bookmarks.fetchChildRecordIds( + "toolbar"); + do_check_matches(newChildIds, [bmk2.guid, bmk1.guid]); } finally { Svc.Obs.notify("weave:engine:stop-tracking"); @@ -379,30 +388,28 @@ add_task(async function test_orphan() { try { _("Add a new bookmark locally."); - let bmk1_id = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); - let bmk1_guid = await PlacesUtils.promiseItemGuid(bmk1_id); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(bmk1_id), - PlacesUtils.bookmarks.toolbarFolder); - let error; - try { - PlacesUtils.annotations.getItemAnnotation(bmk1_id, PARENT_ANNO); - } catch (ex) { - error = ex; - } - do_check_eq(error.result, Cr.NS_ERROR_NOT_AVAILABLE); + let bmk1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let bmk1_id = await PlacesUtils.promiseItemId(bmk1.guid); + do_check_throws(function() { + PlacesUtils.annotations.getItemAnnotation(bmk1_id, + PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO); + }, Cr.NS_ERROR_NOT_AVAILABLE); _("Apply a server record that is the same but refers to non-existent folder."); - let record = await store.createRecord(bmk1_guid); + let record = await store.createRecord(bmk1.guid); record.parentid = "non-existent"; await store.applyIncoming(record); _("Verify that bookmark has been flagged as orphan, has not moved."); - do_check_eq(PlacesUtils.bookmarks.getFolderIdForItem(bmk1_id), - PlacesUtils.bookmarks.toolbarFolder); - do_check_eq(PlacesUtils.annotations.getItemAnnotation(bmk1_id, PARENT_ANNO), - "non-existent"); + let item = await PlacesUtils.bookmarks.fetch(bmk1.guid); + do_check_eq(item.parentGuid, PlacesUtils.bookmarks.toolbarGuid); + let orphanAnno = PlacesUtils.annotations.getItemAnnotation(bmk1_id, + PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO); + do_check_eq(orphanAnno, "non-existent"); } finally { _("Clean up."); @@ -416,22 +423,26 @@ add_task(async function test_reparentOrphans() { let store = engine._store; try { - let folder1_id = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarks.toolbarFolder, "Folder1", 0); - let folder1_guid = await PlacesUtils.promiseItemGuid(folder1_id); + let folder1 = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + type: PlacesUtils.bookmarks.TYPE_FOLDER, + title: "Folder1", + }); + let folder1_id = await PlacesUtils.promiseItemId(folder1.guid); _("Create a bogus orphan record and write the record back to the store to trigger _reparentOrphans."); PlacesUtils.annotations.setItemAnnotation( - folder1_id, PARENT_ANNO, folder1_guid, 0, + folder1_id, PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO, folder1.guid, 0, PlacesUtils.annotations.EXPIRE_NEVER); - let record = await store.createRecord(folder1_guid); + let record = await store.createRecord(folder1.guid); record.title = "New title for Folder 1"; store._childrenToOrder = {}; await store.applyIncoming(record); _("Verify that is has been marked as an orphan even though it couldn't be moved into itself."); - do_check_eq(PlacesUtils.annotations.getItemAnnotation(folder1_id, PARENT_ANNO), - folder1_guid); + let orphanAnno = PlacesUtils.annotations.getItemAnnotation(folder1_id, + PlacesSyncUtils.bookmarks.SYNC_PARENT_ANNO); + do_check_eq(orphanAnno, folder1.guid); } finally { _("Clean up."); @@ -461,14 +472,9 @@ add_task(async function test_empty_query_doesnt_die() { await engine.finalize(); }); -function assertDeleted(id) { - let error; - try { - PlacesUtils.bookmarks.getItemType(id); - } catch (e) { - error = e; - } - equal(error.result, Cr.NS_ERROR_ILLEGAL_VALUE); +async function assertDeleted(guid) { + let item = await PlacesUtils.bookmarks.fetch(guid); + ok(!item); } add_task(async function test_delete_buffering() { @@ -488,13 +494,13 @@ add_task(async function test_delete_buffering() { let fxRecord = new Bookmark("bookmarks", "get-firefox1"); - fxRecord.bmkUri = fxuri.spec; + fxRecord.bmkUri = "http://getfirefox.com/"; fxRecord.title = "Get Firefox!"; fxRecord.parentName = "Test Folder"; fxRecord.parentid = "testfolder-1"; let tbRecord = new Bookmark("bookmarks", "get-tndrbrd1"); - tbRecord.bmkUri = tburi.spec; + tbRecord.bmkUri = "http://getthunderbird.com"; tbRecord.title = "Get Thunderbird!"; tbRecord.parentName = "Test Folder"; tbRecord.parentid = "testfolder-1"; @@ -502,23 +508,19 @@ add_task(async function test_delete_buffering() { await store.applyIncoming(fxRecord); await store.applyIncoming(tbRecord); - let folderId = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(folder.id)); - let fxRecordId = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(fxRecord.id)); - let tbRecordId = await PlacesUtils.promiseItemId(PlacesSyncUtils.bookmarks.recordIdToGuid(tbRecord.id)); - _("Check everything was created correctly."); - equal(PlacesUtils.bookmarks.getItemType(fxRecordId), - PlacesUtils.bookmarks.TYPE_BOOKMARK); - equal(PlacesUtils.bookmarks.getItemType(tbRecordId), - PlacesUtils.bookmarks.TYPE_BOOKMARK); - equal(PlacesUtils.bookmarks.getItemType(folderId), - PlacesUtils.bookmarks.TYPE_FOLDER); + let folderItem = await PlacesUtils.bookmarks.fetch(folder.id); + let fxItem = await PlacesUtils.bookmarks.fetch(fxRecord.id); + let tbItem = await PlacesUtils.bookmarks.fetch(tbRecord.id); - equal(PlacesUtils.bookmarks.getFolderIdForItem(fxRecordId), folderId); - equal(PlacesUtils.bookmarks.getFolderIdForItem(tbRecordId), folderId); - equal(PlacesUtils.bookmarks.getFolderIdForItem(folderId), - PlacesUtils.bookmarks.toolbarFolder); + equal(fxItem.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); + equal(tbItem.type, PlacesUtils.bookmarks.TYPE_BOOKMARK); + equal(folderItem.type, PlacesUtils.bookmarks.TYPE_FOLDER); + + equal(fxItem.parentGuid, folderItem.guid); + equal(tbItem.parentGuid, folderItem.guid); + equal(folderItem.parentGuid, PlacesUtils.bookmarks.toolbarGuid); _("Delete the folder and one bookmark."); @@ -532,14 +534,14 @@ add_task(async function test_delete_buffering() { await store.applyIncoming(deleteFxRecord); _("Check that we haven't deleted them yet, but that the deletions are queued"); - // these will throw if we've deleted them - equal(PlacesUtils.bookmarks.getItemType(fxRecordId), - PlacesUtils.bookmarks.TYPE_BOOKMARK); + // these will return `null` if we've deleted them + fxItem = await PlacesUtils.bookmarks.fetch(fxRecord.id); + ok(fxItem); - equal(PlacesUtils.bookmarks.getItemType(folderId), - PlacesUtils.bookmarks.TYPE_FOLDER); + folderItem = await PlacesUtils.bookmarks.fetch(folder.id); + ok(folderItem); - equal(PlacesUtils.bookmarks.getFolderIdForItem(fxRecordId), folderId); + equal(fxItem.parentGuid, folderItem.guid); ok(store._itemsToDelete.has(folder.id)); ok(store._itemsToDelete.has(fxRecord.id)); @@ -550,14 +552,14 @@ add_task(async function test_delete_buffering() { deepEqual(Object.keys(newChangeRecords).sort(), ["get-tndrbrd1", "toolbar"]); - assertDeleted(fxRecordId); - assertDeleted(folderId); + await assertDeleted(fxItem.guid); + await assertDeleted(folderItem.guid); ok(!store._itemsToDelete.has(folder.id)); ok(!store._itemsToDelete.has(fxRecord.id)); - equal(PlacesUtils.bookmarks.getFolderIdForItem(tbRecordId), - PlacesUtils.bookmarks.toolbarFolder); + tbItem = await PlacesUtils.bookmarks.fetch(tbRecord.id); + equal(tbItem.parentGuid, PlacesUtils.bookmarks.toolbarGuid); } finally { _("Clean up."); diff --git a/services/sync/tests/unit/test_bookmark_tracker.js b/services/sync/tests/unit/test_bookmark_tracker.js index 50141ade1075..3f53d1ca515d 100644 --- a/services/sync/tests/unit/test_bookmark_tracker.js +++ b/services/sync/tests/unit/test_bookmark_tracker.js @@ -2,14 +2,12 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ Cu.import("resource://services-common/utils.js"); -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/util.js"); Cu.import("resource://gre/modules/osfile.jsm"); -Cu.import("resource://testing-common/PlacesTestUtils.jsm"); Cu.import("resource:///modules/PlacesUIUtils.jsm"); let engine; diff --git a/services/sync/tests/unit/test_engine_changes_during_sync.js b/services/sync/tests/unit/test_engine_changes_during_sync.js index f597876bd0e3..c00de651b0da 100644 --- a/services/sync/tests/unit/test_engine_changes_during_sync.js +++ b/services/sync/tests/unit/test_engine_changes_during_sync.js @@ -1,6 +1,5 @@ Cu.import("resource://gre/modules/FormHistory.jsm"); Cu.import("resource://gre/modules/Log.jsm"); -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/engines/history.js"); diff --git a/services/sync/tests/unit/test_history_store.js b/services/sync/tests/unit/test_history_store.js index 1ecf557eb24f..a80659548fe7 100644 --- a/services/sync/tests/unit/test_history_store.js +++ b/services/sync/tests/unit/test_history_store.js @@ -1,8 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://testing-common/PlacesTestUtils.jsm"); -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-common/async.js"); Cu.import("resource://services-common/utils.js"); @@ -14,27 +12,6 @@ const TIMESTAMP1 = (Date.now() - 103406528) * 1000; const TIMESTAMP2 = (Date.now() - 6592903) * 1000; const TIMESTAMP3 = (Date.now() - 123894) * 1000; -function queryPlaces(uri, options) { - let query = PlacesUtils.history.getNewQuery(); - query.uri = uri; - let res = PlacesUtils.history.executeQuery(query, options); - res.root.containerOpen = true; - - let results = []; - for (let i = 0; i < res.root.childCount; i++) - results.push(res.root.getChild(i)); - res.root.containerOpen = false; - return results; -} - -function queryHistoryVisits(uri) { - let options = PlacesUtils.history.getNewQueryOptions(); - options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; - options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT; - options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING; - return queryPlaces(uri, options); -} - function promiseOnVisitObserved() { return new Promise(res => { PlacesUtils.history.addObserver({ @@ -118,17 +95,18 @@ add_task(async function test_store() { visits: [record.visits[0], secondvisit]} ]); await onVisitObserved; - try { - let queryres = queryHistoryVisits(fxuri); - do_check_eq(queryres.length, 2); - do_check_eq(queryres[0].time, TIMESTAMP1); - do_check_eq(queryres[0].title, "Hol Dir Firefox!"); - do_check_eq(queryres[1].time, TIMESTAMP2); - do_check_eq(queryres[1].title, "Hol Dir Firefox!"); - } catch (ex) { - PlacesTestUtils.clearHistory(); - do_throw(ex); - } + let queryres = await PlacesUtils.history.fetch(fxuri.spec, { + includeVisits: true, + }); + do_check_eq(queryres.title, "Hol Dir Firefox!"); + do_check_matches(queryres.visits, [{ + date: new Date(TIMESTAMP2 / 1000), + transition: Ci.nsINavHistoryService.TRANSITION_TYPED, + }, { + date: new Date(TIMESTAMP1 / 1000), + transition: Ci.nsINavHistoryService.TRANSITION_LINK, + }]); + await PlacesTestUtils.clearHistory(); }); add_task(async function test_store_create() { @@ -144,17 +122,17 @@ add_task(async function test_store_create() { type: Ci.nsINavHistoryService.TRANSITION_TYPED}]} ]); await onVisitObserved; - try { - do_check_true((await store.itemExists(tbguid))); - do_check_attribute_count(await store.getAllIDs(), 2); - let queryres = queryHistoryVisits(tburi); - do_check_eq(queryres.length, 1); - do_check_eq(queryres[0].time, TIMESTAMP3); - do_check_eq(queryres[0].title, "The bird is the word!"); - } catch (ex) { - PlacesTestUtils.clearHistory(); - do_throw(ex); - } + do_check_true((await store.itemExists(tbguid))); + do_check_attribute_count(await store.getAllIDs(), 1); + let queryres = await PlacesUtils.history.fetch(tburi.spec, { + includeVisits: true, + }); + do_check_eq(queryres.title, "The bird is the word!"); + do_check_matches(queryres.visits, [{ + date: new Date(TIMESTAMP3 / 1000), + transition: Ci.nsINavHistoryService.TRANSITION_TYPED, + }]); + await PlacesTestUtils.clearHistory(); }); add_task(async function test_null_title() { @@ -168,10 +146,17 @@ add_task(async function test_null_title() { visits: [{date: TIMESTAMP3, type: Ci.nsINavHistoryService.TRANSITION_TYPED}]} ]); - do_check_attribute_count((await store.getAllIDs()), 3); - let queryres = queryHistoryVisits(resuri); - do_check_eq(queryres.length, 1); - do_check_eq(queryres[0].time, TIMESTAMP3); + do_check_attribute_count((await store.getAllIDs()), 1); + + let queryres = await PlacesUtils.history.fetch(resuri.spec, { + includeVisits: true, + }); + do_check_eq(queryres.title, ""); + do_check_matches(queryres.visits, [{ + date: new Date(TIMESTAMP3 / 1000), + transition: Ci.nsINavHistoryService.TRANSITION_TYPED, + }]); + await PlacesTestUtils.clearHistory(); }); add_task(async function test_invalid_records() { @@ -190,7 +175,7 @@ add_task(async function test_invalid_records() { + TIMESTAMP3 + ", " + Ci.nsINavHistoryService.TRANSITION_TYPED + ", 1)" ); }); - do_check_attribute_count((await store.getAllIDs()), 4); + do_check_attribute_count((await store.getAllIDs()), 1); _("Make sure we report records with invalid URIs."); let invalid_uri_guid = Utils.makeGUID(); @@ -337,19 +322,25 @@ add_task(async function test_remove() { await applyEnsureNoFailures([{id: fxguid, deleted: true}, {id: Utils.makeGUID(), deleted: true}]); do_check_false((await store.itemExists(fxguid))); - let queryres = queryHistoryVisits(fxuri); - do_check_eq(queryres.length, 0); + let queryres = await PlacesUtils.history.fetch(fxuri.spec, { + includeVisits: true, + }); + do_check_null(queryres); _("Make sure wipe works."); await store.wipe(); do_check_empty((await store.getAllIDs())); - queryres = queryHistoryVisits(fxuri); - do_check_eq(queryres.length, 0); - queryres = queryHistoryVisits(tburi); - do_check_eq(queryres.length, 0); + queryres = await PlacesUtils.history.fetch(fxuri.spec, { + includeVisits: true, + }); + do_check_null(queryres); + queryres = await PlacesUtils.history.fetch(tburi.spec, { + includeVisits: true, + }); + do_check_null(queryres); }); -add_test(function cleanup() { +add_task(async function cleanup() { _("Clean up."); - PlacesTestUtils.clearHistory().then(run_next_test); + await PlacesTestUtils.clearHistory(); }); diff --git a/services/sync/tests/unit/test_places_guid_downgrade.js b/services/sync/tests/unit/test_places_guid_downgrade.js index 14f2db6bce55..2b24a0e739fe 100644 --- a/services/sync/tests/unit/test_places_guid_downgrade.js +++ b/services/sync/tests/unit/test_places_guid_downgrade.js @@ -12,9 +12,6 @@ const kDBName = "places.sqlite"; const storageSvc = Cc["@mozilla.org/storage/service;1"] .getService(Ci.mozIStorageService); -const fxuri = CommonUtils.makeURI("http://getfirefox.com/"); -const tburi = CommonUtils.makeURI("http://getthunderbird.com/"); - function setPlacesDatabase(aFileName) { removePlacesDatabase(); _("Copying over places.sqlite."); @@ -93,26 +90,26 @@ add_task(async function test_history_guids() { let places = [ { - uri: fxuri, + url: "http://getfirefox.com/", title: "Get Firefox!", visits: [{ - visitDate: Date.now() * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK + date: new Date(), + transition: Ci.nsINavHistoryService.TRANSITION_LINK }] }, { - uri: tburi, + url: "http://getthunderbird.com/", title: "Get Thunderbird!", visits: [{ - visitDate: Date.now() * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK + date: new Date(), + transition: Ci.nsINavHistoryService.TRANSITION_LINK }] } ]; async function onVisitAdded() { - let fxguid = await store.GUIDForUri(fxuri, true); - let tbguid = await store.GUIDForUri(tburi, true); + let fxguid = await store.GUIDForUri("http://getfirefox.com/", true); + let tbguid = await store.GUIDForUri("http://getthunderbird.com/", true); dump("fxguid: " + fxguid + "\n"); dump("tbguid: " + tbguid + "\n"); @@ -144,47 +141,36 @@ add_task(async function test_history_guids() { do_check_eq(result.length, 0); } - await new Promise((resolve, reject) => { - PlacesUtils.asyncHistory.updatePlaces(places, { - handleError: function handleError() { - do_throw("Unexpected error in adding visit."); - }, - handleResult: function handleResult() {}, - handleCompletion: () => { onVisitAdded().then(resolve, reject); }, - }); - }); + await PlacesUtils.history.insertMany(places); + await onVisitAdded(); }); add_task(async function test_bookmark_guids() { - let engine = new BookmarksEngine(Service); - let store = engine._store; - - let fxid = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, - fxuri, - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Get Firefox!"); - let tbid = PlacesUtils.bookmarks.insertBookmark( - PlacesUtils.bookmarks.toolbarFolder, - tburi, - PlacesUtils.bookmarks.DEFAULT_INDEX, - "Get Thunderbird!"); - - let fxguid = await store.GUIDForId(fxid); - let tbguid = await store.GUIDForId(tbid); + let fx = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); + let fxid = await PlacesUtils.promiseItemId(fx.guid); + let tb = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getthunderbird.com/", + title: "Get Thunderbird!", + }); + let tbid = await PlacesUtils.promiseItemId(tb.guid); _("Bookmarks: Verify GUIDs are added to the guid column."); let db = await PlacesUtils.promiseDBConnection(); let result = await db.execute( "SELECT id FROM moz_bookmarks WHERE guid = :guid", - {guid: fxguid} + {guid: fx.guid} ); do_check_eq(result.length, 1); do_check_eq(result[0].getResultByName("id"), fxid); result = await db.execute( "SELECT id FROM moz_bookmarks WHERE guid = :guid", - {guid: tbguid} + {guid: tb.guid} ); do_check_eq(result.length, 1); do_check_eq(result[0].getResultByName("id"), tbid); @@ -192,13 +178,13 @@ add_task(async function test_bookmark_guids() { _("Bookmarks: Verify GUIDs weren't added to annotations."); result = await db.execute( "SELECT a.content AS guid FROM moz_items_annos a WHERE guid = :guid", - {guid: fxguid} + {guid: fx.guid} ); do_check_eq(result.length, 0); result = await db.execute( "SELECT a.content AS guid FROM moz_items_annos a WHERE guid = :guid", - {guid: tbguid} + {guid: tb.guid} ); do_check_eq(result.length, 0); }); diff --git a/services/sync/tests/unit/test_syncengine_sync.js b/services/sync/tests/unit/test_syncengine_sync.js index e70384fa560c..ae88630e3478 100644 --- a/services/sync/tests/unit/test_syncengine_sync.js +++ b/services/sync/tests/unit/test_syncengine_sync.js @@ -1,7 +1,6 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -Cu.import("resource://gre/modules/PlacesSyncUtils.jsm"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); Cu.import("resource://services-sync/main.js"); diff --git a/services/sync/tests/unit/test_telemetry.js b/services/sync/tests/unit/test_telemetry.js index f62ccdaed6aa..a7399e96af25 100644 --- a/services/sync/tests/unit/test_telemetry.js +++ b/services/sync/tests/unit/test_telemetry.js @@ -164,11 +164,11 @@ add_task(async function test_uploading() { let server = await serverForFoo(engine); await SyncTestingInfrastructure(server); - let parent = PlacesUtils.toolbarFolderId; - let uri = CommonUtils.makeURI("http://getfirefox.com/"); - - let bmk_id = PlacesUtils.bookmarks.insertBookmark(parent, uri, - PlacesUtils.bookmarks.DEFAULT_INDEX, "Get Firefox!"); + let bmk = await PlacesUtils.bookmarks.insert({ + parentGuid: PlacesUtils.bookmarks.toolbarGuid, + url: "http://getfirefox.com/", + title: "Get Firefox!", + }); try { let ping = await sync_engine_and_validate_telem(engine, false); @@ -179,7 +179,10 @@ add_task(async function test_uploading() { greater(ping.engines[0].outgoing[0].sent, 0); ok(!ping.engines[0].incoming); - PlacesUtils.bookmarks.setItemTitle(bmk_id, "New Title"); + await PlacesUtils.bookmarks.update({ + guid: bmk.guid, + title: "New Title", + }); await engine.resetClient(); diff --git a/servo/components/config/opts.rs b/servo/components/config/opts.rs index 0cd11825bdfe..76edf2cbefea 100644 --- a/servo/components/config/opts.rs +++ b/servo/components/config/opts.rs @@ -569,7 +569,6 @@ pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult { opts.optopt("o", "output", "Output file", "output.png"); opts.optopt("s", "size", "Size of tiles", "512"); opts.optopt("", "device-pixel-ratio", "Device pixels per px", ""); - opts.optopt("t", "threads", "Number of paint threads", "1"); opts.optflagopt("p", "profile", "Time profiler flag and either a TSV output filename \ OR an interval for output to Stdout (blank for Stdout with interval of 5s)", "10 \ OR time.tsv"); diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index 03d7cc469156..022507fdce0d 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -2402,7 +2402,7 @@ fn static_assert() { <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %> pub fn set_font_variant_alternates(&mut self, - v: longhands::font_variant_alternates::computed_value::T, + v: values::computed::font::FontVariantAlternates, device: &Device) { use gecko_bindings::bindings::{Gecko_ClearAlternateValues, Gecko_AppendAlternateValues}; use gecko_bindings::bindings::Gecko_nsFont_ResetFontFeatureValuesLookup; @@ -2410,7 +2410,7 @@ fn static_assert() { % for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split(): use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()}; % endfor - use self::longhands::font_variant_alternates::VariantAlternates; + use values::specified::font::VariantAlternates; unsafe { Gecko_ClearAlternateValues(&mut self.gecko.mFont, v.len()); @@ -2471,13 +2471,13 @@ fn static_assert() { self.copy_font_variant_alternates_from(other) } - pub fn clone_font_variant_alternates(&self) -> longhands::font_variant_alternates::computed_value::T { + pub fn clone_font_variant_alternates(&self) -> values::computed::font::FontVariantAlternates { use Atom; % for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split(): use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()}; % endfor - use properties::longhands::font_variant_alternates::VariantAlternates; - use properties::longhands::font_variant_alternates::VariantAlternatesList; + use values::specified::font::VariantAlternates; + use values::specified::font::VariantAlternatesList; use values::CustomIdent; if self.gecko.mFont.variantAlternates == NS_FONT_VARIANT_ALTERNATES_NORMAL as u16 { diff --git a/servo/components/style/properties/longhand/font.mako.rs b/servo/components/style/properties/longhand/font.mako.rs index acf9cf334605..08310021cdc8 100644 --- a/servo/components/style/properties/longhand/font.mako.rs +++ b/servo/components/style/properties/longhand/font.mako.rs @@ -670,204 +670,14 @@ ${helpers.single_keyword_system("font-kerning", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", animation_value_type="discrete")} -<%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="discrete" - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER" - spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates"> - use properties::longhands::system_font::SystemFont; - use std::fmt; - use style_traits::ToCss; - use values::CustomIdent; - - - #[derive(Clone, Debug, MallocSizeOf, PartialEq)] - pub enum VariantAlternates { - Stylistic(CustomIdent), - Styleset(Box<[CustomIdent]>), - CharacterVariant(Box<[CustomIdent]>), - Swash(CustomIdent), - Ornaments(CustomIdent), - Annotation(CustomIdent), - HistoricalForms, - } - - #[derive(Clone, Debug, MallocSizeOf, PartialEq)] - pub struct VariantAlternatesList(pub Box<[VariantAlternates]>); - - #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] - pub enum SpecifiedValue { - Value(VariantAlternatesList), - System(SystemFont) - } - - <%self:simple_system_boilerplate name="font_variant_alternates"> - - impl ToCss for VariantAlternates { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - match *self { - % for value in "swash stylistic ornaments annotation".split(): - VariantAlternates::${to_camel_case(value)}(ref atom) => { - dest.write_str("${value}")?; - dest.write_str("(")?; - atom.to_css(dest)?; - dest.write_str(")") - }, - % endfor - % for value in "styleset character-variant".split(): - VariantAlternates::${to_camel_case(value)}(ref vec) => { - dest.write_str("${value}")?; - dest.write_str("(")?; - let mut iter = vec.iter(); - iter.next().unwrap().to_css(dest)?; - for c in iter { - dest.write_str(", ")?; - c.to_css(dest)?; - } - dest.write_str(")") - }, - % endfor - VariantAlternates::HistoricalForms => { - dest.write_str("historical-forms") - }, - } - } - } - - impl ToCss for VariantAlternatesList { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - if self.0.is_empty() { - return dest.write_str("normal"); - } - - let mut iter = self.0.iter(); - iter.next().unwrap().to_css(dest)?; - for alternate in iter { - dest.write_str(" ")?; - alternate.to_css(dest)?; - } - Ok(()) - } - } - - impl VariantAlternatesList { - /// Returns the length of all variant alternates. - pub fn len(&self) -> usize { - self.0.iter().fold(0, |acc, alternate| { - match *alternate { - % for value in "Swash Stylistic Ornaments Annotation".split(): - VariantAlternates::${value}(_) => { - acc + 1 - }, - % endfor - % for value in "Styleset CharacterVariant".split(): - VariantAlternates::${value}(ref slice) => { - acc + slice.len() - } - % endfor - _ => acc, - } - }) - } - } - - pub mod computed_value { - pub type T = super::VariantAlternatesList; - } - #[inline] - pub fn get_initial_value() -> computed_value::T { - VariantAlternatesList(vec![].into_boxed_slice()) - } - #[inline] - pub fn get_initial_specified_value() -> SpecifiedValue { - SpecifiedValue::Value(VariantAlternatesList(vec![].into_boxed_slice())) - } - - bitflags! { - #[cfg_attr(feature = "servo", derive(MallocSizeOf))] - pub struct ParsingFlags: u8 { - const NORMAL = 0; - const HISTORICAL_FORMS = 0x01; - const STYLISTIC = 0x02; - const STYLESET = 0x04; - const CHARACTER_VARIANT = 0x08; - const SWASH = 0x10; - const ORNAMENTS = 0x20; - const ANNOTATION = 0x40; - } - } - - /// normal | - /// [ stylistic() || - /// historical-forms || - /// styleset( #) || - /// character-variant( #) || - /// swash() || - /// ornaments() || - /// annotation() ] - pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) - -> Result> { - let mut alternates = Vec::new(); - if input.try(|input| input.expect_ident_matching("normal")).is_ok() { - return Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))); - } - - let mut parsed_alternates = ParsingFlags::empty(); - macro_rules! check_if_parsed( - ($input:expr, $flag:path) => ( - if parsed_alternates.contains($flag) { - return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - parsed_alternates |= $flag; - ) - ); - while let Ok(_) = input.try(|input| { - // FIXME: remove clone() when lifetimes are non-lexical - match input.next()?.clone() { - Token::Ident(ref ident) => { - if *ident == "historical-forms" { - check_if_parsed!(input, ParsingFlags::HISTORICAL_FORMS); - alternates.push(VariantAlternates::HistoricalForms); - Ok(()) - } else { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - Token::Function(ref name) => { - input.parse_nested_block(|i| { - match_ignore_ascii_case! { &name, - % for value in "swash stylistic ornaments annotation".split(): - "${value}" => { - check_if_parsed!(i, ParsingFlags::${value.upper()}); - let location = i.current_source_location(); - let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; - alternates.push(VariantAlternates::${to_camel_case(value)}(ident)); - Ok(()) - }, - % endfor - % for value in "styleset character-variant".split(): - "${value}" => { - check_if_parsed!(i, ParsingFlags:: ${to_rust_ident(value).upper()}); - let idents = i.parse_comma_separated(|i| { - let location = i.current_source_location(); - CustomIdent::from_ident(location, i.expect_ident()?, &[]) - })?; - alternates.push(VariantAlternates::${to_camel_case(value)}(idents.into_boxed_slice())); - Ok(()) - }, - % endfor - _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - }) - }, - _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), - } - }) { } - - if parsed_alternates.is_empty() { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - Ok(SpecifiedValue::Value(VariantAlternatesList(alternates.into_boxed_slice()))) - } - +${helpers.predefined_type("font-variant-alternates", + "FontVariantAlternates", + products="gecko", + initial_value="computed::FontVariantAlternates::get_initial_value()", + initial_specified_value="specified::FontVariantAlternates::get_initial_specified_value()", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates")} #[cfg(feature = "gecko")] macro_rules! exclusive_value { diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs index a14d6e19cbae..98c3e014c4b2 100644 --- a/servo/components/style/values/computed/font.rs +++ b/servo/components/style/values/computed/font.rs @@ -265,6 +265,17 @@ impl ToAnimatedValue for FontSizeAdjust { } } +/// Use VariantAlternatesList as computed type of FontVariantAlternates +pub type FontVariantAlternates = specified::VariantAlternatesList; + +impl FontVariantAlternates { + #[inline] + /// Get initial value with VariantAlternatesList + pub fn get_initial_value() -> Self { + specified::VariantAlternatesList(vec![].into_boxed_slice()) + } +} + impl ToComputedValue for specified::MozScriptMinSize { type ComputedValue = MozScriptMinSize; diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 51fe7f209878..7c6f54f58edb 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -36,7 +36,8 @@ pub use self::angle::Angle; pub use self::background::{BackgroundSize, BackgroundRepeat}; pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderCornerRadius, BorderSpacing}; -pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; +pub use self::font::{MozScriptLevel, MozScriptMinSize, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; diff --git a/servo/components/style/values/specified/font.rs b/servo/components/style/values/specified/font.rs index 67c9956d4b89..da2fa9432e8d 100644 --- a/servo/components/style/values/specified/font.rs +++ b/servo/components/style/values/specified/font.rs @@ -10,8 +10,11 @@ use app_units::Au; use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; use properties::longhands::system_font::SystemFont; +#[allow(unused_imports)] +use std::ascii::AsciiExt; use std::fmt; use style_traits::{ToCss, StyleParseErrorKind, ParseError}; +use values::CustomIdent; use values::computed::{font as computed, Context, Length, NonNegativeLength, ToComputedValue}; use values::specified::{AllowQuirks, LengthOrPercentage, NoCalcLength, Number}; use values::specified::length::{AU_PER_PT, AU_PER_PX, FontBaseSize}; @@ -610,6 +613,292 @@ impl Parse for FontSize { } } +bitflags! { + #[cfg_attr(feature = "servo", derive(MallocSizeOf))] + /// Flags of variant alternates in bit + struct VariantAlternatesParsingFlags: u8 { + /// None of variant alternates enabled + const NORMAL = 0; + /// Historical forms + const HISTORICAL_FORMS = 0x01; + /// Stylistic Alternates + const STYLISTIC = 0x02; + /// Stylistic Sets + const STYLESET = 0x04; + /// Character Variant + const CHARACTER_VARIANT = 0x08; + /// Swash glyphs + const SWASH = 0x10; + /// Ornaments glyphs + const ORNAMENTS = 0x20; + /// Annotation forms + const ANNOTATION = 0x40; + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] +/// Set of variant alternates +pub enum VariantAlternates { + /// Enables display of stylistic alternates + Stylistic(CustomIdent), + /// Enables display with stylistic sets + Styleset(Box<[CustomIdent]>), + /// Enables display of specific character variants + CharacterVariant(Box<[CustomIdent]>), + /// Enables display of swash glyphs + Swash(CustomIdent), + /// Enables replacement of default glyphs with ornaments + Ornaments(CustomIdent), + /// Enables display of alternate annotation forms + Annotation(CustomIdent), + /// Enables display of historical forms + HistoricalForms, +} + +impl ToCss for VariantAlternates { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + VariantAlternates::Swash(ref atom) => { + dest.write_str("swash")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Stylistic(ref atom) => { + dest.write_str("stylistic")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Ornaments(ref atom) => { + dest.write_str("ornaments")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Annotation(ref atom) => { + dest.write_str("annotation")?; + dest.write_str("(")?; + atom.to_css(dest)?; + dest.write_str(")") + }, + VariantAlternates::Styleset(ref vec) => { + dest.write_str("styleset")?; + dest.write_str("(")?; + let mut iter = vec.iter(); + iter.next().unwrap().to_css(dest)?; + for c in iter { + dest.write_str(", ")?; + c.to_css(dest)?; + } + dest.write_str(")") + }, + VariantAlternates::CharacterVariant(ref vec) => { + dest.write_str("character-variant")?; + dest.write_str("(")?; + let mut iter = vec.iter(); + iter.next().unwrap().to_css(dest)?; + for c in iter { + dest.write_str(", ")?; + c.to_css(dest)?; + } + dest.write_str(")") + }, + VariantAlternates::HistoricalForms => { + dest.write_str("historical-forms") + }, + } + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq)] +/// List of Variant Alternates +pub struct VariantAlternatesList(pub Box<[VariantAlternates]>); + +impl VariantAlternatesList { + /// Returns the length of all variant alternates. + pub fn len(&self) -> usize { + self.0.iter().fold(0, |acc, alternate| { + match *alternate { + VariantAlternates::Swash(_) | VariantAlternates::Stylistic(_) | + VariantAlternates::Ornaments(_) | VariantAlternates::Annotation(_) => { + acc + 1 + }, + VariantAlternates::Styleset(ref slice) | + VariantAlternates::CharacterVariant(ref slice) => { + acc + slice.len() + }, + _ => acc, + } + }) + } +} + +impl ToCss for VariantAlternatesList { + fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0.is_empty() { + return dest.write_str("normal"); + } + + let mut iter = self.0.iter(); + iter.next().unwrap().to_css(dest)?; + for alternate in iter { + dest.write_str(" ")?; + alternate.to_css(dest)?; + } + Ok(()) + } +} + +#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss)] +/// Control over the selection of these alternate glyphs +pub enum FontVariantAlternates { + /// Use alternative glyph from value + Value(VariantAlternatesList), + /// Use system font glyph + System(SystemFont) +} + +impl FontVariantAlternates { + #[inline] + /// Get initial specified value with VariantAlternatesList + pub fn get_initial_specified_value() -> Self { + FontVariantAlternates::Value(VariantAlternatesList(vec![].into_boxed_slice())) + } + + /// Get FontVariantAlternates with system font + pub fn system_font(f: SystemFont) -> Self { + FontVariantAlternates::System(f) + } + + /// Get SystemFont of FontVariantAlternates + pub fn get_system(&self) -> Option { + if let FontVariantAlternates::System(s) = *self { + Some(s) + } else { + None + } + } +} + +impl ToComputedValue for FontVariantAlternates { + type ComputedValue = computed::FontVariantAlternates; + + fn to_computed_value(&self, _context: &Context) -> computed::FontVariantAlternates { + match *self { + FontVariantAlternates::Value(ref v) => v.clone(), + FontVariantAlternates::System(_) => { + #[cfg(feature = "gecko")] { + _context.cached_system_font.as_ref().unwrap().font_variant_alternates.clone() + } + #[cfg(feature = "servo")] { + unreachable!() + } + } + } + } + + fn from_computed_value(other: &computed::FontVariantAlternates) -> Self { + FontVariantAlternates::Value(other.clone()) + } +} + +impl Parse for FontVariantAlternates { + /// normal | + /// [ stylistic() || + /// historical-forms || + /// styleset( #) || + /// character-variant( #) || + /// swash() || + /// ornaments() || + /// annotation() ] + fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + let mut alternates = Vec::new(); + if input.try(|input| input.expect_ident_matching("normal")).is_ok() { + return Ok(FontVariantAlternates::Value(VariantAlternatesList(alternates.into_boxed_slice()))); + } + + let mut parsed_alternates = VariantAlternatesParsingFlags::empty(); + macro_rules! check_if_parsed( + ($input:expr, $flag:path) => ( + if parsed_alternates.contains($flag) { + return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + parsed_alternates |= $flag; + ) + ); + while let Ok(_) = input.try(|input| { + // FIXME: remove clone() when lifetimes are non-lexical + match input.next()?.clone() { + Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => { + check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS); + alternates.push(VariantAlternates::HistoricalForms); + Ok(()) + }, + Token::Function(ref name) => { + input.parse_nested_block(|i| { + match_ignore_ascii_case! { &name, + "swash" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Swash(ident)); + Ok(()) + }, + "stylistic" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Stylistic(ident)); + Ok(()) + }, + "ornaments" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Ornaments(ident)); + Ok(()) + }, + "annotation" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION); + let location = i.current_source_location(); + let ident = CustomIdent::from_ident(location, i.expect_ident()?, &[])?; + alternates.push(VariantAlternates::Annotation(ident)); + Ok(()) + }, + "styleset" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET); + let idents = i.parse_comma_separated(|i| { + let location = i.current_source_location(); + CustomIdent::from_ident(location, i.expect_ident()?, &[]) + })?; + alternates.push(VariantAlternates::Styleset(idents.into_boxed_slice())); + Ok(()) + }, + "character-variant" => { + check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT); + let idents = i.parse_comma_separated(|i| { + let location = i.current_source_location(); + CustomIdent::from_ident(location, i.expect_ident()?, &[]) + })?; + alternates.push(VariantAlternates::CharacterVariant(idents.into_boxed_slice())); + Ok(()) + }, + _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + }) + }, + _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } + }) { } + + if parsed_alternates.is_empty() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + Ok(FontVariantAlternates::Value(VariantAlternatesList(alternates.into_boxed_slice()))) + } +} + #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)] /// Whether user agents are allowed to synthesize bold or oblique font faces /// when a font family lacks bold or italic faces diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index 5d89190e229d..9a33019d98a9 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -30,7 +30,8 @@ pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, Justify pub use self::background::{BackgroundRepeat, BackgroundSize}; pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSideWidth, BorderRadius, BorderSideWidth, BorderSpacing}; -pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, MozScriptLevel, MozScriptMinSize, XTextZoom}; +pub use self::font::{FontSize, FontSizeAdjust, FontSynthesis, FontWeight, FontVariantAlternates}; +pub use self::font::{MozScriptLevel, MozScriptMinSize, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, ScrollSnapType, VerticalAlign}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; diff --git a/taskcluster/ci/beetmover-cdns/kind.yml b/taskcluster/ci/beetmover-cdns/kind.yml index fa316f94760b..357850256a92 100644 --- a/taskcluster/ci/beetmover-cdns/kind.yml +++ b/taskcluster/ci/beetmover-cdns/kind.yml @@ -19,13 +19,18 @@ job-defaults: mozilla-beta: scriptworker-prov-v1/beetmoverworker-v1 default: scriptworker-prov-v1/beetmoverworker-dev run-on-projects: [] + shipping-phase: publish jobs: fennec-push-to-cdns: name: fennec_push_to_cdns product: fennec - run: - routes: - - index.releases.v1.{branch}.latest.fennec.latest.beetmover_cdns - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.beetmover_cdns + shipping-product: fennec + routes: + - index.releases.v1.{branch}.latest.fennec.latest.beetmover_cdns + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.beetmover_cdns treeherder-platform: Android/opt + index: + type: release + product: fennec + job-name: android-api-16-opt diff --git a/taskcluster/ci/push-apk-breakpoint/kind.yml b/taskcluster/ci/push-apk-breakpoint/kind.yml index a4634a6bc62b..1587ddb7df0e 100644 --- a/taskcluster/ci/push-apk-breakpoint/kind.yml +++ b/taskcluster/ci/push-apk-breakpoint/kind.yml @@ -17,6 +17,8 @@ jobs: attributes: build_platform: android-nightly nightly: true + shipping-phase: ship + shipping-product: fennec worker-type: # see transforms worker: implementation: push-apk-breakpoint diff --git a/taskcluster/ci/push-apk/kind.yml b/taskcluster/ci/push-apk/kind.yml index e6f52385db9e..3a950d2667c5 100644 --- a/taskcluster/ci/push-apk/kind.yml +++ b/taskcluster/ci/push-apk/kind.yml @@ -18,6 +18,8 @@ jobs: attributes: build_platform: android-nightly nightly: true + shipping-phase: ship + shipping-product: fennec worker-type: scriptworker-prov-v1/pushapk-v1 worker: upstream-artifacts: # see transforms diff --git a/taskcluster/ci/release-bouncer-aliases/kind.yml b/taskcluster/ci/release-bouncer-aliases/kind.yml index c32301817446..1d8a09588c57 100644 --- a/taskcluster/ci/release-bouncer-aliases/kind.yml +++ b/taskcluster/ci/release-bouncer-aliases/kind.yml @@ -12,20 +12,22 @@ transforms: kind-dependencies: - release-uptake-monitoring +job-defaults: + shipping-phase: ship + jobs: fennec: name: fennec_release_bouncer_aliases description: Update bouncer aliases job worker-type: buildbot-bridge/buildbot-bridge run-on-projects: [] + shipping-product: fennec run: using: buildbot product: fennec buildername: release-{branch}-fennec_bouncer_aliases release-promotion: true - routes: - - index.releases.v1.{branch}.latest.fennec.latest.bouncer_submitter - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.bouncer_submitter + worker: properties: tuxedo_server_url: by-project: @@ -33,6 +35,13 @@ jobs: mozilla-release: https://bounceradmin.mozilla.com/api maple: https://admin-bouncer.stage.mozaws.net/api/ default: http://localhost/api + routes: + - index.releases.v1.{branch}.latest.fennec.latest.bouncer_submitter + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.bouncer_submitter + index: + type: release + product: fennec + job-name: android-api-16-opt notifications: completed: by-project: diff --git a/taskcluster/ci/release-bouncer-sub/kind.yml b/taskcluster/ci/release-bouncer-sub/kind.yml index ecdbd5b72545..ecc716eeacc0 100644 --- a/taskcluster/ci/release-bouncer-sub/kind.yml +++ b/taskcluster/ci/release-bouncer-sub/kind.yml @@ -8,6 +8,8 @@ transforms: - taskgraph.transforms.job:transforms - taskgraph.transforms.task:transforms +job-defaults: + shipping-phase: promote jobs: fennec: @@ -15,14 +17,19 @@ jobs: description: release bouncer submission job worker-type: buildbot-bridge/buildbot-bridge run-on-projects: [] + shipping-product: fennec run: using: buildbot product: fennec buildername: release-{branch}-fennec_bncr_sub release-promotion: true - routes: - - index.releases.v1.{branch}.latest.fennec.latest.bouncer_submitter - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.bouncer_submitter + routes: + - index.releases.v1.{branch}.latest.fennec.latest.bouncer_submitter + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.bouncer_submitter + index: + type: release + product: fennec + job-name: android-api-16-opt notifications: completed: by-project: diff --git a/taskcluster/ci/release-mark-as-shipped/kind.yml b/taskcluster/ci/release-mark-as-shipped/kind.yml index e0310d5e153c..5f65d981603f 100644 --- a/taskcluster/ci/release-mark-as-shipped/kind.yml +++ b/taskcluster/ci/release-mark-as-shipped/kind.yml @@ -14,6 +14,8 @@ kind-dependencies: - release-bouncer-aliases - release-version-bump +job-defaults: + shipping-phase: ship jobs: fennec: @@ -21,14 +23,19 @@ jobs: description: mark release as shipped in Ship-It worker-type: buildbot-bridge/buildbot-bridge run-on-projects: [] + shipping-product: fennec run: using: buildbot product: fennec buildername: release-{branch}-fennec_mark_as_shipped release-promotion: true - routes: - - index.releases.v1.{branch}.latest.fennec.latest.mark_as_shipped - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.mark_as_shipped + routes: + - index.releases.v1.{branch}.latest.fennec.latest.mark_as_shipped + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.mark_as_shipped + index: + type: release + product: fennec + job-name: android-api-16-opt notifications: completed: by-project: diff --git a/taskcluster/ci/release-notify-promote/kind.yml b/taskcluster/ci/release-notify-promote/kind.yml index a498fb32d3dc..885b38845019 100644 --- a/taskcluster/ci/release-notify-promote/kind.yml +++ b/taskcluster/ci/release-notify-promote/kind.yml @@ -12,11 +12,15 @@ kind-dependencies: - beetmover-checksums - release-bouncer-sub +job-defaults: + shipping-phase: promote + jobs: fennec: name: notify-release-drivers-promote description: Sends email to release-drivers telling release was promoted. run-on-projects: [] + shipping-product: fennec worker-type: aws-provisioner-v1/gecko-{level}-b-linux worker: implementation: docker-worker diff --git a/taskcluster/ci/release-notify-publish/kind.yml b/taskcluster/ci/release-notify-publish/kind.yml index 38e2ed928874..335c2bed9ee9 100644 --- a/taskcluster/ci/release-notify-publish/kind.yml +++ b/taskcluster/ci/release-notify-publish/kind.yml @@ -13,11 +13,15 @@ kind-dependencies: - release-bouncer-aliases - release-version-bump +job-defaults: + shipping-phase: ship + jobs: fennec: name: notify-release-drivers-publish description: Sends email to release-drivers telling release was published. run-on-projects: [] + shipping-product: fennec worker-type: aws-provisioner-v1/gecko-{level}-b-linux worker: implementation: docker-worker diff --git a/taskcluster/ci/release-uptake-monitoring/kind.yml b/taskcluster/ci/release-uptake-monitoring/kind.yml index 488981590649..78bf87886c15 100644 --- a/taskcluster/ci/release-uptake-monitoring/kind.yml +++ b/taskcluster/ci/release-uptake-monitoring/kind.yml @@ -12,29 +12,38 @@ transforms: kind-dependencies: - beetmover-cdns +job-defaults: + shipping-phase: publish + jobs: fennec: name: fennec_release_uptake_monitoring description: Uptake monitoring job worker-type: buildbot-bridge/buildbot-bridge run-on-projects: [] + shipping-product: fennec run: using: buildbot product: fennec buildername: release-{branch}-fennec_uptake_monitoring release-promotion: true - routes: - - index.releases.v1.{branch}.latest.fennec.latest.uptake_monitoring - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.uptake_monitoring + worker: properties: - # TODO: Calculate "platforms" dynamically - platforms: "android-api-16, android-x86" - tuxedo_server_url: - by-project: - mozilla-beta: https://bounceradmin.mozilla.com/api - mozilla-release: https://bounceradmin.mozilla.com/api - maple: https://admin-bouncer.stage.mozaws.net/api/ - default: http://localhost/api + # TODO: Calculate "platforms" dynamically + platforms: "android-api-16, android-x86" + tuxedo_server_url: + by-project: + mozilla-beta: https://bounceradmin.mozilla.com/api + mozilla-release: https://bounceradmin.mozilla.com/api + maple: https://admin-bouncer.stage.mozaws.net/api/ + default: http://localhost/api + routes: + - index.releases.v1.{branch}.latest.fennec.latest.uptake_monitoring + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.uptake_monitoring + index: + type: release + product: fennec + job-name: android-api-16-opt notifications: completed: by-project: diff --git a/taskcluster/ci/release-version-bump/kind.yml b/taskcluster/ci/release-version-bump/kind.yml index 57df96ab0603..f291c05512de 100644 --- a/taskcluster/ci/release-version-bump/kind.yml +++ b/taskcluster/ci/release-version-bump/kind.yml @@ -12,20 +12,28 @@ transforms: kind-dependencies: - beetmover-cdns +job-defaults: + shipping-phase: ship + jobs: fennec: name: fennec-version-bump description: Release Promotion version bump worker-type: buildbot-bridge/buildbot-bridge run-on-projects: [] + shipping-product: fennec run: using: buildbot product: fennec buildername: release-{branch}-fennec_version_bump release-promotion: true - routes: - - index.releases.v1.{branch}.latest.fennec.latest.version_bump - - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.version_bump + routes: + - index.releases.v1.{branch}.latest.fennec.latest.version_bump + - index.releases.v1.{branch}.{revision}.fennec.{underscore_version}.build{build_number}.version_bump + index: + type: release + product: fennec + job-name: android-api-16-opt notifications: completed: by-project: diff --git a/taskcluster/docs/attributes.rst b/taskcluster/docs/attributes.rst index 2b048eb6fa0e..53ae12fb0297 100644 --- a/taskcluster/docs/attributes.rst +++ b/taskcluster/docs/attributes.rst @@ -186,3 +186,11 @@ optimization even if the ``optimize_target_tasks`` parameter is False. This is meant to be used for tasks which a developer would almost always want to run. Typically these tasks will be short running and have a high risk of causing a backout. For example ``lint`` or ``python-unittest`` tasks. + +shipping_product +================ +For release promotion jobs, this is the product we are shipping. + +shipping_phase +============== +For release promotion jobs, this is the shipping phase (promote, publish, ship). diff --git a/taskcluster/docs/parameters.rst b/taskcluster/docs/parameters.rst index 722aae0fc072..12c127fc26e5 100644 --- a/taskcluster/docs/parameters.rst +++ b/taskcluster/docs/parameters.rst @@ -138,6 +138,15 @@ Optimization Any tasks in the graph matching one of the labels will use the previously-run taskId rather than submitting a new task. +Release Promotion +----------------- + +``build_number`` + Specify the release promotion build number. + +``next_version`` + Specify the next version for version bump tasks. + Comm Push Information --------------------- diff --git a/taskcluster/taskgraph/actions/release_promotion.py b/taskcluster/taskgraph/actions/release_promotion.py index 5df031a6b2e5..de62bf9bbf8c 100644 --- a/taskcluster/taskgraph/actions/release_promotion.py +++ b/taskcluster/taskgraph/actions/release_promotion.py @@ -6,8 +6,6 @@ from __future__ import absolute_import, print_function, unicode_literals -import os - from .registry import register_callback_action from .util import (find_decision_task, find_existing_tasks_from_previous_kinds, @@ -142,7 +140,6 @@ def is_release_promotion_available(parameters): } ) def release_promotion_action(parameters, input, task_group_id, task_id, task): - os.environ['BUILD_NUMBER'] = str(input['build_number']) release_promotion_flavor = input['release_promotion_flavor'] if release_promotion_flavor in VERSION_BUMP_FLAVORS: next_version = str(input.get('next_version', '')) @@ -151,7 +148,6 @@ def release_promotion_action(parameters, input, task_group_id, task_id, task): "`next_version` property needs to be provided for %s " "targets." % ', '.join(VERSION_BUMP_FLAVORS) ) - os.environ['NEXT_VERSION'] = next_version promotion_config = RELEASE_PROMOTION_CONFIG[release_promotion_flavor] target_tasks_method = input.get( @@ -185,6 +181,8 @@ def release_promotion_action(parameters, input, task_group_id, task_id, task): ) parameters['do_not_optimize'] = do_not_optimize parameters['target_tasks_method'] = target_tasks_method + parameters['build_number'] = str(input['build_number']) + parameters['next_version'] = next_version # make parameters read-only parameters = Parameters(**parameters) diff --git a/taskcluster/taskgraph/decision.py b/taskcluster/taskgraph/decision.py index ecbccd8f85a9..952dafe368cf 100644 --- a/taskcluster/taskgraph/decision.py +++ b/taskcluster/taskgraph/decision.py @@ -169,6 +169,8 @@ def get_decision_parameters(options): ] parameters['existing_tasks'] = {} parameters['do_not_optimize'] = [] + parameters['build_number'] = 1 + parameters['next_version'] = None # owner must be an email, but sometimes (e.g., for ffxbld) it is not, in which # case, fake it diff --git a/taskcluster/taskgraph/parameters.py b/taskcluster/taskgraph/parameters.py index 3fb60edd06a1..2aca65001bca 100644 --- a/taskcluster/taskgraph/parameters.py +++ b/taskcluster/taskgraph/parameters.py @@ -31,6 +31,7 @@ def get_head_ref(): PARAMETERS = { 'base_repository': 'https://hg.mozilla.org/mozilla-unified', 'build_date': lambda: int(time.time()), + 'build_number': 1, 'do_not_optimize': [], 'existing_tasks': {}, 'filters': ['check_servo', 'target_tasks_method'], @@ -41,6 +42,7 @@ PARAMETERS = { 'level': '3', 'message': '', 'moz_build_date': lambda: datetime.now().strftime("%Y%m%d%H%M%S"), + 'next_version': None, 'optimize_target_tasks': True, 'owner': 'nobody@mozilla.com', 'project': 'mozilla-central', diff --git a/taskcluster/taskgraph/target_tasks.py b/taskcluster/taskgraph/target_tasks.py index 911e95daca71..4254a8d0a7e5 100644 --- a/taskcluster/taskgraph/target_tasks.py +++ b/taskcluster/taskgraph/target_tasks.py @@ -334,6 +334,10 @@ def target_tasks_mozilla_beta_desktop_promotion(full_task_graph, parameters, gra if task.label in beta_tasks: return True + if task.attributes.get('shipping_product') == 'firefox' and \ + task.attributes.get('shipping_phase') == 'promote': + return True + # TODO: partner repacks # TODO: source task # TODO: funsize, all but balrog submission @@ -360,6 +364,9 @@ def target_tasks_publish_firefox(full_task_graph, parameters, graph_config): # Include promotion tasks; these will be optimized out if task.label in filtered_for_candidates: return True + if task.attributes.get('shipping_product') == 'firefox' and \ + task.attributes.get('shipping_phase') in ('publish', 'ship'): + return True # TODO: add beetmover push-to-releases # TODO: tagging / version bumping # TODO: publish to balrog @@ -390,14 +397,9 @@ def target_tasks_candidates_fennec(full_task_graph, parameters, graph_config): if task.kind not in ('balrog', 'push-apk', 'push-apk-breakpoint'): if task.attributes.get('nightly'): return True - if task.task['payload'].get('properties', {}).get('product') == 'fennec': - if task.kind in ('release-bouncer-sub', - ): - return True - if task.task.get('extra', {}).get('product') == 'fennec': - if task.kind in ('release-notify-promote', - ): - return True + if task.attributes.get('shipping_product') == 'fennec' and \ + task.attributes.get('shipping_phase') == 'promote': + return True return [l for l, t in full_task_graph.tasks.iteritems() if filter(full_task_graph[l])] @@ -414,23 +416,8 @@ def target_tasks_publish_fennec(full_task_graph, parameters, graph_config): # Include candidates build tasks; these will be optimized out if task.label in filtered_for_candidates: return True - if task.task['payload'].get('properties', {}).get('product') == 'fennec': - if task.kind in ('release-mark-as-shipped', - 'release-bouncer-aliases', - 'release-uptake-monitoring', - 'release-version-bump', - ): - return True - if task.task.get('extra', {}).get('product') == 'fennec': - if task.kind in ('release-notify-publish', - ): - return True - - if task.task['payload'].get('product') == 'fennec': - if task.kind in ('beetmover-cdns', ): - return True - - if task.kind in ('push-apk', 'push-apk-breakpoint'): + if task.attributes.get('shipping_product') == 'fennec' and \ + task.attributes.get('shipping_phase') in ('ship', 'publish'): return True return [l for l, t in full_task_graph.tasks.iteritems() if filter(full_task_graph[l])] diff --git a/taskcluster/taskgraph/transforms/beetmover_cdns.py b/taskcluster/taskgraph/transforms/beetmover_cdns.py index a4c02d570cf6..d1b5e5970bf2 100644 --- a/taskcluster/taskgraph/transforms/beetmover_cdns.py +++ b/taskcluster/taskgraph/transforms/beetmover_cdns.py @@ -8,7 +8,9 @@ Transform the beetmover-cdns task into a task description. from __future__ import absolute_import, print_function, unicode_literals from taskgraph.transforms.base import TransformSequence -from taskgraph.util.schema import validate_schema, Schema +from taskgraph.util.schema import ( + optionally_keyed_by, resolve_keyed_by, validate_schema, Schema +) from taskgraph.util.scriptworker import ( get_beetmover_bucket_scope, get_beetmover_action_scope ) @@ -35,11 +37,12 @@ beetmover_cdns_description_schema = Schema({ Optional('job-from'): task_description_schema['job-from'], Optional('run'): {basestring: object}, Optional('run-on-projects'): task_description_schema['run-on-projects'], - Required('worker-type'): Any( - job_description_schema['worker-type'], - {'by-project': {basestring: job_description_schema['worker-type']}}, - ), + Required('worker-type'): optionally_keyed_by('project', basestring), Optional('dependencies'): {basestring: taskref_or_string}, + Optional('index'): {basestring: basestring}, + Optional('routes'): [basestring], + Required('shipping-phase'): task_description_schema['shipping-phase'], + Required('shipping-product'): task_description_schema['shipping-product'], }) @@ -68,13 +71,18 @@ def make_beetmover_cdns_description(config, jobs): ) ) + resolve_keyed_by( + job, 'worker-type', item_name=job['name'], + project=config.params['project'] + ) + bucket_scope = get_beetmover_bucket_scope(config) action_scope = get_beetmover_action_scope(config) task = { 'label': label, 'description': description, - 'worker-type': 'scriptworker-prov-v1/beetmoverworker-dev', + 'worker-type': job['worker-type'], 'scopes': [bucket_scope, action_scope], 'product': job['product'], 'dependencies': job['dependencies'], diff --git a/taskcluster/taskgraph/transforms/job/__init__.py b/taskcluster/taskgraph/transforms/job/__init__.py index 871b9c4a4a38..36d3d5b4cbad 100644 --- a/taskcluster/taskgraph/transforms/job/__init__.py +++ b/taskcluster/taskgraph/transforms/job/__init__.py @@ -60,6 +60,8 @@ job_description_schema = Schema({ Optional('treeherder'): task_description_schema['treeherder'], Optional('index'): task_description_schema['index'], Optional('run-on-projects'): task_description_schema['run-on-projects'], + Optional('shipping-phase'): task_description_schema['shipping-phase'], + Optional('shipping-product'): task_description_schema['shipping-product'], Optional('coalesce'): task_description_schema['coalesce'], Optional('always-target'): task_description_schema['always-target'], Exclusive('optimization', 'optimization'): task_description_schema['optimization'], diff --git a/taskcluster/taskgraph/transforms/job/buildbot.py b/taskcluster/taskgraph/transforms/job/buildbot.py index 403d0c9b85cd..dd4de0bd6c80 100644 --- a/taskcluster/taskgraph/transforms/job/buildbot.py +++ b/taskcluster/taskgraph/transforms/job/buildbot.py @@ -8,11 +8,10 @@ Support for running jobs via buildbot. """ from __future__ import absolute_import, print_function, unicode_literals -import copy import slugid from urlparse import urlparse -from taskgraph.util.schema import Schema, optionally_keyed_by, resolve_keyed_by +from taskgraph.util.schema import Schema from taskgraph.util.scriptworker import get_release_config from voluptuous import Optional, Required, Any @@ -30,8 +29,6 @@ buildbot_run_schema = Schema({ Required('product'): Any('firefox', 'mobile', 'fennec', 'devedition', 'thunderbird'), Optional('release-promotion'): bool, - Optional('routes'): [basestring], - Optional('properties'): {basestring: optionally_keyed_by('project', basestring)}, }) @@ -40,9 +37,6 @@ def bb_release_worker(config, worker, run): release_props = get_release_config(config, force=True) repo_path = urlparse(config.params['head_repository']).path.lstrip('/') revision = config.params['head_rev'] - branch = config.params['project'] - buildername = worker['buildername'] - underscore_version = release_props['version'].replace('.', '_') release_props.update({ 'release_promotion': True, 'repo_path': repo_path, @@ -50,22 +44,6 @@ def bb_release_worker(config, worker, run): 'script_repo_revision': revision, }) worker['properties'].update(release_props) - # scopes - worker['scopes'] = [ - "project:releng:buildbot-bridge:builder-name:{}".format(buildername) - ] - # routes - if run.get('routes'): - worker['routes'] = [] - repl_dict = { - 'branch': branch, - 'build_number': str(release_props['build_number']), - 'revision': revision, - 'underscore_version': underscore_version, - } - for route in run['routes']: - route = route.format(**repl_dict) - worker['routes'].append(route) def bb_ci_worker(config, worker): @@ -77,13 +55,6 @@ def bb_ci_worker(config, worker): @run_job_using('buildbot-bridge', 'buildbot', schema=buildbot_run_schema) def mozharness_on_buildbot_bridge(config, job, taskdesc): - # resolve by-* keys first - fields = [ - "run.properties.tuxedo_server_url", - ] - job = copy.deepcopy(job) - for field in fields: - resolve_keyed_by(job, field, field, **config.params) run = job['run'] worker = taskdesc['worker'] branch = config.params['project'] @@ -99,12 +70,8 @@ def mozharness_on_buildbot_bridge(config, job, taskdesc): 'repository': config.params['head_repository'], 'revision': revision, }, - 'properties': { - 'product': product, - }, }) - if run.get('properties'): - worker['properties'].update(run['properties']) + worker.setdefault('properties', {})['product'] = product if run.get('release-promotion'): bb_release_worker(config, worker, run) diff --git a/taskcluster/taskgraph/transforms/job/mozharness.py b/taskcluster/taskgraph/transforms/job/mozharness.py index 063d1a4522db..03f5158f201c 100644 --- a/taskcluster/taskgraph/transforms/job/mozharness.py +++ b/taskcluster/taskgraph/transforms/job/mozharness.py @@ -221,8 +221,12 @@ def mozharness_on_generic_worker(config, job, taskdesc): if run['use-simple-package']: env.update({'MOZ_SIMPLE_PACKAGE_NAME': 'target'}) + # The windows generic worker uses batch files to pass environment variables + # to commands. Setting a variable to empty in a batch file unsets, so if + # there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that + # mozharness doesn't try to find the commit message on its own. if 'try' in config.params['project']: - env['TRY_COMMIT_MSG'] = config.params['message'] + env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message' if not job['attributes']['build_platform'].startswith('win'): raise Exception( diff --git a/taskcluster/taskgraph/transforms/push_apk.py b/taskcluster/taskgraph/transforms/push_apk.py index f58b6efe943f..f9cbb3840cd1 100644 --- a/taskcluster/taskgraph/transforms/push_apk.py +++ b/taskcluster/taskgraph/transforms/push_apk.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, print_function, unicode_literals import functools from taskgraph.transforms.base import TransformSequence +from taskgraph.transforms.task import task_description_schema from taskgraph.util.schema import Schema from taskgraph.util.scriptworker import get_push_apk_scope, get_push_apk_track, \ get_push_apk_dry_run_option, get_push_apk_rollout_percentage @@ -21,6 +22,11 @@ from voluptuous import Optional, Required transforms = TransformSequence() +# Voluptuous uses marker objects as dictionary *keys*, but they are not +# comparable, so we cast all of the keys back to regular strings +task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()} + + push_apk_description_schema = Schema({ # the dependent task (object) for this beetmover job, used to inform beetmover. Required('dependent-tasks'): object, @@ -35,6 +41,8 @@ push_apk_description_schema = Schema({ Required('worker'): object, Required('scopes'): None, Required('deadline-after'): basestring, + Required('shipping-phase'): task_description_schema['shipping-phase'], + Required('shipping-product'): task_description_schema['shipping-product'], Optional('extra'): object, }) diff --git a/taskcluster/taskgraph/transforms/push_apk_breakpoint.py b/taskcluster/taskgraph/transforms/push_apk_breakpoint.py index 1ccf4868a3f6..eb1eb128ba3c 100644 --- a/taskcluster/taskgraph/transforms/push_apk_breakpoint.py +++ b/taskcluster/taskgraph/transforms/push_apk_breakpoint.py @@ -10,6 +10,7 @@ from __future__ import absolute_import, print_function, unicode_literals import functools from taskgraph.transforms.base import TransformSequence +from taskgraph.transforms.task import task_description_schema from taskgraph.util.schema import Schema from taskgraph.util.scriptworker import get_push_apk_breakpoint_worker_type from taskgraph.util.push_apk import fill_labels_tranform, validate_jobs_schema_transform_partial, \ @@ -19,6 +20,10 @@ from voluptuous import Required transforms = TransformSequence() +# Voluptuous uses marker objects as dictionary *keys*, but they are not +# comparable, so we cast all of the keys back to regular strings +task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()} + push_apk_breakpoint_description_schema = Schema({ # the dependent task (object) for this beetmover job, used to inform beetmover. Required('dependent-tasks'): object, @@ -32,6 +37,8 @@ push_apk_breakpoint_description_schema = Schema({ Required('treeherder'): object, Required('run-on-projects'): list, Required('deadline-after'): basestring, + Required('shipping-phase'): task_description_schema['shipping-phase'], + Required('shipping-product'): task_description_schema['shipping-product'], }) validate_jobs_schema_transform = functools.partial( diff --git a/taskcluster/taskgraph/transforms/release_deps.py b/taskcluster/taskgraph/transforms/release_deps.py index 3383f07e1d1a..5fe1c3c66a82 100644 --- a/taskcluster/taskgraph/transforms/release_deps.py +++ b/taskcluster/taskgraph/transforms/release_deps.py @@ -14,8 +14,9 @@ transforms = TransformSequence() def _get_product(job_or_task): # Find the product. - # XXX officially support a product attribute that is consistently set. - product = job_or_task.get('product') + # XXX Once shipping-product is set for nightly builds as well, we can get + # rid of this function. + product = job_or_task.get('shipping-product', job_or_task.get('product')) if 'payload' in job_or_task: product = product or job_or_task['payload'].get( 'product', diff --git a/taskcluster/taskgraph/transforms/task.py b/taskcluster/taskgraph/transforms/task.py index d87ed16b57ee..c2a9267f3728 100755 --- a/taskcluster/taskgraph/transforms/task.py +++ b/taskcluster/taskgraph/transforms/task.py @@ -132,6 +132,7 @@ task_description_schema = Schema({ # the name of the product this build produces 'product': Any( 'firefox', + 'fennec', 'mobile', 'static-analysis', 'devedition', @@ -142,7 +143,7 @@ task_description_schema = Schema({ 'job-name': basestring, # Type of gecko v2 index to use - 'type': Any('generic', 'nightly', 'l10n', 'nightly-with-multi-l10n'), + 'type': Any('generic', 'nightly', 'l10n', 'nightly-with-multi-l10n', 'release'), # The rank that the task will receive in the TaskCluster # index. A newly completed task supercedes the currently @@ -170,6 +171,24 @@ task_description_schema = Schema({ # See the attributes documentation for details. Optional('run-on-projects'): [basestring], + # The `shipping_phase` attribute, defaulting to None. This specifies the + # release promotion phase that this task belongs to. + Required('shipping-phase', default=None): Any( + None, + 'promote', + 'publish', + 'ship', + ), + + # The `shipping_product` attribute, defaulting to None. This specifies the + # release promotion product that this task belongs to. + Required('shipping-product', default=None): Any( + None, + 'devedition', + 'fennec', + 'firefox', + ), + # Coalescing provides the facility for tasks to be superseded by the same # task in a subsequent commit, if the current task backlog reaches an # explicit threshold. Both age and size thresholds need to be met in order @@ -411,8 +430,6 @@ task_description_schema = Schema({ Optional('tuxedo_server_url'): optionally_keyed_by('project', basestring), Extra: taskref_or_string, # additional properties are allowed }, - Optional('scopes'): [basestring], - Optional('routes'): [basestring], }, { Required('implementation'): 'native-engine', Required('os'): Any('macosx', 'linux'), @@ -665,6 +682,12 @@ def superseder_url(config, task): ) +def verify_index_job_name(index): + job_name = index['job-name'] + if job_name not in JOB_NAME_WHITELIST: + raise Exception(JOB_NAME_WHITELIST_ERROR.format(job_name)) + + @payload_builder('docker-worker') def build_docker_worker_payload(config, task, task_def): worker = task['worker'] @@ -1003,13 +1026,23 @@ def build_buildbot_bridge_payload(config, task, task_def): task['extra'].pop('treeherder', None) task['extra'].pop('treeherderEnv', None) worker = task['worker'] + + if worker['properties'].get('tuxedo_server_url'): + resolve_keyed_by( + worker, 'properties.tuxedo_server_url', worker['buildername'], + **config.params + ) + task_def['payload'] = { 'buildername': worker['buildername'], 'sourcestamp': worker['sourcestamp'], 'properties': worker['properties'], } - task_def['scopes'].extend(worker.get('scopes', [])) - task_def['routes'].extend(worker.get('routes', [])) + task_def.setdefault('scopes', []) + if worker['properties'].get('release_promotion'): + task_def['scopes'].append( + "project:releng:buildbot-bridge:builder-name:{}".format(worker['buildername']) + ) transforms = TransformSequence() @@ -1040,12 +1073,10 @@ def add_generic_index_routes(config, task): index = task.get('index') routes = task.setdefault('routes', []) - job_name = index['job-name'] - if job_name not in JOB_NAME_WHITELIST: - raise Exception(JOB_NAME_WHITELIST_ERROR.format(job_name)) + verify_index_job_name(index) subs = config.params.copy() - subs['job-name'] = job_name + subs['job-name'] = index['job-name'] subs['build_date_long'] = time.strftime("%Y.%m.%d.%Y%m%d%H%M%S", time.gmtime(config.params['build_date'])) subs['product'] = index['product'] @@ -1069,12 +1100,10 @@ def add_nightly_index_routes(config, task): index = task.get('index') routes = task.setdefault('routes', []) - job_name = index['job-name'] - if job_name not in JOB_NAME_WHITELIST: - raise Exception(JOB_NAME_WHITELIST_ERROR.format(job_name)) + verify_index_job_name(index) subs = config.params.copy() - subs['job-name'] = job_name + subs['job-name'] = index['job-name'] subs['build_date_long'] = time.strftime("%Y.%m.%d.%Y%m%d%H%M%S", time.gmtime(config.params['build_date'])) subs['build_date'] = time.strftime("%Y.%m.%d", @@ -1090,6 +1119,29 @@ def add_nightly_index_routes(config, task): return task +@index_builder('release') +def add_release_index_routes(config, task): + index = task.get('index') + routes = [] + release_config = get_release_config(config, force=True) + + verify_index_job_name(index) + + subs = config.params.copy() + subs['build_number'] = str(release_config['build_number']) + subs['revision'] = subs['head_rev'] + subs['underscore_version'] = release_config['version'].replace('.', '_') + subs['product'] = index['product'] + subs['branch'] = subs['project'] + + for rt in task.get('routes', []): + routes.append(rt.format(**subs)) + + task['routes'] = routes + + return task + + @index_builder('nightly-with-multi-l10n') def add_nightly_multi_index_routes(config, task): task = add_nightly_index_routes(config, task) @@ -1102,12 +1154,10 @@ def add_l10n_index_routes(config, task, force_locale=None): index = task.get('index') routes = task.setdefault('routes', []) - job_name = index['job-name'] - if job_name not in JOB_NAME_WHITELIST: - raise Exception(JOB_NAME_WHITELIST_ERROR.format(job_name)) + verify_index_job_name(index) subs = config.params.copy() - subs['job-name'] = job_name + subs['job-name'] = index['job-name'] subs['build_date_long'] = time.strftime("%Y.%m.%d.%Y%m%d%H%M%S", time.gmtime(config.params['build_date'])) subs['product'] = index['product'] @@ -1277,6 +1327,8 @@ def build_task(config, tasks): attributes = task.get('attributes', {}) attributes['run_on_projects'] = task.get('run-on-projects', ['all']) attributes['always_target'] = task['always-target'] + attributes['shipping_phase'] = task['shipping-phase'] + attributes['shipping_product'] = task['shipping-product'] # Set MOZ_AUTOMATION on all jobs. if task['worker']['implementation'] in ( diff --git a/taskcluster/taskgraph/util/scriptworker.py b/taskcluster/taskgraph/util/scriptworker.py index 221931eab13d..73f19319a553 100644 --- a/taskcluster/taskgraph/util/scriptworker.py +++ b/taskcluster/taskgraph/util/scriptworker.py @@ -414,23 +414,14 @@ def get_release_config(config, force=False): Args: config (dict): the task config that defines the target task method. - Raises: - ValueError: if a release graph doesn't define a valid - `os.environ['BUILD_NUMBER']` - Returns: dict: containing both `build_number` and `version`. This can be used to update `task.payload`. """ release_config = {} if force or config.params['target_tasks_method'] in BEETMOVER_RELEASE_TARGET_TASKS: - next_version = str(os.environ.get("NEXT_VERSION", "")) - if next_version != "": - release_config['next_version'] = next_version - build_number = str(os.environ.get("BUILD_NUMBER", 1)) - if not build_number.isdigit(): - raise ValueError("Release graphs must specify `BUILD_NUMBER` in the environment!") - release_config['build_number'] = int(build_number) + release_config['next_version'] = str(config.params['next_version']) + release_config['build_number'] = int(config.params['build_number']) with open(VERSION_PATH, "r") as fh: version = fh.readline().rstrip() release_config['version'] = version diff --git a/toolkit/components/extensions/ExtensionParent.jsm b/toolkit/components/extensions/ExtensionParent.jsm index 310c48640919..1f26243167d2 100644 --- a/toolkit/components/extensions/ExtensionParent.jsm +++ b/toolkit/components/extensions/ExtensionParent.jsm @@ -20,7 +20,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { AppConstants: "resource://gre/modules/AppConstants.jsm", - DeferredSave: "resource://gre/modules/DeferredSave.jsm", + AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm", + DeferredTask: "resource://gre/modules/DeferredTask.jsm", E10SUtils: "resource:///modules/E10SUtils.jsm", MessageChannel: "resource://gre/modules/MessageChannel.jsm", OS: "resource://gre/modules/osfile.jsm", @@ -1418,26 +1419,28 @@ StartupCache = { file: OS.Path.join(OS.Constants.Path.localProfileDir, "startupCache", "webext.sc.lz4"), - get saver() { - if (!this._saver) { + async _saveNow() { + let data = new Uint8Array(aomStartup.encodeBlob(this._data)); + await OS.File.writeAtomic(this.file, data, {tmpPath: `${this.file}.tmp`}); + }, + + async save() { + if (!this._saveTask) { OS.File.makeDir(OS.Path.dirname(this.file), { ignoreExisting: true, from: OS.Constants.Path.localProfileDir, }); - this._saver = new DeferredSave(this.file, - () => this.getBlob(), - {delay: 5000}); + this._saveTask = new DeferredTask(() => this._saveNow(), 5000); + + AsyncShutdown.profileBeforeChange.addBlocker( + "Flush WebExtension StartupCache", + async () => { + await this._saveTask.finalize(); + this._saveTask = null; + }); } - return this._saver; - }, - - async save() { - return this.saver.saveChanges(); - }, - - getBlob() { - return new Uint8Array(aomStartup.encodeBlob(this._data)); + return this._saveTask.arm(); }, _data: null, diff --git a/toolkit/mozapps/extensions/DeferredSave.jsm b/toolkit/mozapps/extensions/DeferredSave.jsm deleted file mode 100644 index 88b808f8dcb1..000000000000 --- a/toolkit/mozapps/extensions/DeferredSave.jsm +++ /dev/null @@ -1,299 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; - -Cu.import("resource://gre/modules/osfile.jsm"); -/* globals OS*/ -Cu.import("resource://gre/modules/PromiseUtils.jsm"); - -// Make it possible to mock out timers for testing -var MakeTimer = () => Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - -this.EXPORTED_SYMBOLS = ["DeferredSave"]; - -// If delay parameter is not provided, default is 50 milliseconds. -const DEFAULT_SAVE_DELAY_MS = 50; - -Cu.import("resource://gre/modules/Log.jsm"); -// Configure a logger at the parent 'DeferredSave' level to format -// messages for all the modules under DeferredSave.* -const DEFERREDSAVE_PARENT_LOGGER_ID = "DeferredSave"; -var parentLogger = Log.repository.getLogger(DEFERREDSAVE_PARENT_LOGGER_ID); -parentLogger.level = Log.Level.Warn; -var formatter = new Log.BasicFormatter(); -// Set parent logger (and its children) to append to -// the Javascript section of the Browser Console -parentLogger.addAppender(new Log.ConsoleAppender(formatter)); -// Set parent logger (and its children) to -// also append to standard out -parentLogger.addAppender(new Log.DumpAppender(formatter)); - -// Provide the ability to enable/disable logging -// messages at runtime. -// If the "extensions.logging.enabled" preference is -// missing or 'false', messages at the WARNING and higher -// severity should be logged to the JS console and standard error. -// If "extensions.logging.enabled" is set to 'true', messages -// at DEBUG and higher should go to JS console and standard error. -Cu.import("resource://gre/modules/Services.jsm"); - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown", - "resource://gre/modules/AsyncShutdown.jsm"); - -const PREF_LOGGING_ENABLED = "extensions.logging.enabled"; -const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed"; - -/** -* Preference listener which listens for a change in the -* "extensions.logging.enabled" preference and changes the logging level of the -* parent 'addons' level logger accordingly. -*/ -var PrefObserver = { - init() { - Services.prefs.addObserver(PREF_LOGGING_ENABLED, this); - Services.obs.addObserver(this, "xpcom-shutdown"); - this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED); - }, - - observe(aSubject, aTopic, aData) { - if (aTopic == "xpcom-shutdown") { - Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this); - Services.obs.removeObserver(this, "xpcom-shutdown"); - } else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) { - let debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED, false); - if (debugLogEnabled) { - parentLogger.level = Log.Level.Debug; - } else { - parentLogger.level = Log.Level.Warn; - } - } - } -}; - -PrefObserver.init(); - -/** - * A module to manage deferred, asynchronous writing of data files - * to disk. Writing is deferred by waiting for a specified delay after - * a request to save the data, before beginning to write. If more than - * one save request is received during the delay, all requests are - * fulfilled by a single write. - * - * @constructor - * @param {string} aPath - * String representing the full path of the file where the data - * is to be written. - * @param {function} aDataProvider - * Callback function that takes no argument and returns the data to - * be written. If aDataProvider returns an ArrayBufferView, the - * bytes it contains are written to the file as is. - * If aDataProvider returns a String the data are UTF-8 encoded - * and then written to the file. - * @param {object | integer} [aOptions] - * The delay in milliseconds between the first saveChanges() call - * that marks the data as needing to be saved, and when the DeferredSave - * begins writing the data to disk. Default 50 milliseconds. - * - * Or, an options object containing: - * - delay: A delay in milliseconds. - * - finalizeAt: An AsyncShutdown blocker during which to - * finalize any pending writes. - */ -this.DeferredSave = function(aPath, aDataProvider, aOptions = {}) { - if (typeof aOptions == "number") { - aOptions = {delay: aOptions}; - } - - // Create a new logger (child of 'DeferredSave' logger) - // for use by this particular instance of DeferredSave object - let leafName = OS.Path.basename(aPath); - let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName; - this.logger = Log.repository.getLogger(logger_id); - - // @type {Deferred|null}, null when no data needs to be written - // @resolves with the result of OS.File.writeAtomic when all writes complete - // @rejects with the error from OS.File.writeAtomic if the write fails, - // or with the error from aDataProvider() if that throws. - this._pending = null; - - // @type {Promise}, completes when the in-progress write (if any) completes, - // kept as a resolved promise at other times to simplify logic. - // Because _deferredSave() always uses _writing.then() to execute - // its next action, we don't need a special case for whether a write - // is in progress - if the previous write is complete (and the _writing - // promise is already resolved/rejected), _writing.then() starts - // the next action immediately. - // - // @resolves with the result of OS.File.writeAtomic - // @rejects with the error from OS.File.writeAtomic - this._writing = Promise.resolve(0); - - // Are we currently waiting for a write to complete - this.writeInProgress = false; - - this._path = aPath; - this._dataProvider = aDataProvider; - - this._timer = null; - - // Some counters for telemetry - // The total number of times the file was written - this.totalSaves = 0; - - // The number of times the data became dirty while - // another save was in progress - this.overlappedSaves = 0; - - // Error returned by the most recent write (if any) - this._lastError = null; - - if (aOptions.delay && (aOptions.delay > 0)) - this._delay = aOptions.delay; - else - this._delay = DEFAULT_SAVE_DELAY_MS; - - this._finalizeAt = aOptions.finalizeAt || AsyncShutdown.profileBeforeChange; - this._finalize = this._finalize.bind(this); - this._finalizeAt.addBlocker(`DeferredSave: writing data to ${aPath}`, - this._finalize); -}; - -this.DeferredSave.prototype = { - get dirty() { - return this._pending || this.writeInProgress; - }, - - get lastError() { - return this._lastError; - }, - - get path() { - return this._path; - }, - - // Start the pending timer if data is dirty - _startTimer() { - if (!this._pending) { - return; - } - - this.logger.debug("Starting timer"); - if (!this._timer) - this._timer = MakeTimer(); - this._timer.initWithCallback(() => this._timerCallback(), - this._delay, Ci.nsITimer.TYPE_ONE_SHOT); - }, - - /** - * Mark the current stored data dirty, and schedule a flush to disk - * @return A Promise that will be resolved after the data is written to disk; - * the promise is resolved with the number of bytes written. - */ - saveChanges() { - this.logger.debug("Save changes"); - if (!this._pending) { - if (this.writeInProgress) { - this.logger.debug("Data changed while write in progress"); - this.overlappedSaves++; - } - this._pending = PromiseUtils.defer(); - // Wait until the most recent write completes or fails (if it hasn't already) - // and then restart our timer - this._writing.then(count => this._startTimer(), error => this._startTimer()); - } - return this._pending.promise; - }, - - _timerCallback() { - Services.tm.idleDispatchToMainThread(() => this._deferredSave()); - }, - - _deferredSave() { - let pending = this._pending; - this._pending = null; - let writing = this._writing; - this._writing = pending.promise; - - // In either the success or the exception handling case, we don't need to handle - // the error from _writing here; it's already being handled in another then() - let toSave = null; - try { - toSave = this._dataProvider(); - } catch (e) { - this.logger.error("Deferred save dataProvider failed", e); - writing.catch(error => {}) - .then(count => { - pending.reject(e); - }); - return; - } - - writing.catch(error => { return 0; }) - .then(count => { - this.logger.debug("Starting write"); - this.totalSaves++; - this.writeInProgress = true; - - OS.File.writeAtomic(this._path, toSave, {tmpPath: this._path + ".tmp"}) - .then( - result => { - this._lastError = null; - this.writeInProgress = false; - this.logger.debug("Write succeeded"); - pending.resolve(result); - }, - error => { - this._lastError = error; - this.writeInProgress = false; - this.logger.warn("Write failed", error); - pending.reject(error); - }); - }); - }, - - /** - * Immediately save the dirty data to disk, skipping - * the delay of normal operation. Note that the write - * still happens asynchronously in the worker - * thread from OS.File. - * - * There are four possible situations: - * 1) Nothing to flush - * 2) Data is not currently being written, in-memory copy is dirty - * 3) Data is currently being written, in-memory copy is clean - * 4) Data is being written and in-memory copy is dirty - * - * @return Promise that will resolve when all in-memory data - * has finished being flushed, returning the number of bytes - * written. If all in-memory data is clean, completes with the - * result of the most recent write. - */ - flush() { - // If we have pending changes, cancel our timer and set up the write - // immediately (_deferredSave queues the write for after the most - // recent write completes, if it hasn't already) - if (this._pending) { - this.logger.debug("Flush called while data is dirty"); - if (this._timer) { - this._timer.cancel(); - this._timer = null; - } - this._deferredSave(); - } - - return this._writing; - }, - - _finalize() { - return this.flush().catch(Cu.reportError); - }, - -}; diff --git a/toolkit/mozapps/extensions/internal/AddonRepository.jsm b/toolkit/mozapps/extensions/internal/AddonRepository.jsm index 5a3d0051ee73..7307fb3927c5 100644 --- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm +++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm @@ -9,22 +9,19 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/AddonManager.jsm"); -/* globals AddonManagerPrivate*/ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", - "resource://gre/modules/NetUtil.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave", - "resource://gre/modules/DeferredSave.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Preferences", - "resource://gre/modules/Preferences.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest", - "resource://gre/modules/ServiceRequest.jsm"); - +XPCOMUtils.defineLazyModuleGetters(this, { + AddonManager: "resource://gre/modules/AddonManager.jsm", + AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm", + AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm", + DeferredTask: "resource://gre/modules/DeferredTask.jsm", + Services: "resource://gre/modules/Services.jsm", + ServiceRequest: "resource://gre/modules/ServiceRequest.jsm", + NetUtil: "resource://gre/modules/NetUtil.jsm", + OS: "resource://gre/modules/osfile.jsm", + Preferences: "resource://gre/modules/Preferences.jsm", +}); this.EXPORTED_SYMBOLS = [ "AddonRepository" ]; @@ -1527,6 +1524,9 @@ this.AddonRepository = { var AddonDatabase = { connectionPromise: null, + _saveTask: null, + _blockerAdded: false, + // the in-memory database DB: BLANK_DB(), @@ -1576,7 +1576,7 @@ var AddonDatabase = { } // Create a blank addons.json file - this._saveDBToDisk(); + this.save(); Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA); return this.DB; @@ -1615,10 +1615,13 @@ var AddonDatabase = { this.connectionPromise = null; - if (aSkipFlush) { + if (aSkipFlush || !this._saveTask) { return Promise.resolve(); } - return this.Writer.flush(); + + let promise = this._saveTask.finalize(); + this._saveTask = null; + return promise; }, /** @@ -1632,10 +1635,13 @@ var AddonDatabase = { delete(aCallback) { this.DB = BLANK_DB(); - this._deleting = this.Writer.flush() - .catch(() => {}) - // shutdown(true) never rejects - .then(() => this.shutdown(true)) + if (this._saveTask) { + this._saveTask.disarm(); + this._saveTask = null; + } + + // shutdown(true) never rejects + this._deleting = this.shutdown(true) .then(() => OS.File.remove(this.jsonFile, {})) .catch(error => logger.error("Unable to delete Addon Repository file " + this.jsonFile, error)) @@ -1644,7 +1650,7 @@ var AddonDatabase = { return this._deleting; }, - toJSON() { + async _saveNow() { let json = { schema: this.DB.schema, addons: [] @@ -1653,22 +1659,28 @@ var AddonDatabase = { for (let [, value] of this.DB.addons) json.addons.push(value); - return json; + await OS.File.writeAtomic(this.jsonFile, JSON.stringify(json), + {tmpPath: `${this.jsonFile}.tmp`}); }, - /* - * This is a deferred task writer that is used - * to batch operations done within 50ms of each - * other and thus generating only one write to disk - */ - get Writer() { - delete this.Writer; - this.Writer = new DeferredSave( - this.jsonFile, - () => { return JSON.stringify(this); }, - DB_BATCH_TIMEOUT_MS - ); - return this.Writer; + save() { + if (!this._saveTask) { + this._saveTask = new DeferredTask(() => this._saveNow(), DB_BATCH_TIMEOUT_MS); + + if (!this._blockerAdded) { + AsyncShutdown.profileBeforeChange.addBlocker( + "Flush AddonRepository", + async () => { + if (!this._saveTask) { + return; + } + await this._saveTask.finalize(); + this._saveTask = null; + }); + this._blockerAdded = true; + } + } + this._saveTask.arm(); }, /** @@ -1681,7 +1693,14 @@ var AddonDatabase = { if (this._deleting) { return this._deleting; } - return this.Writer.flush(); + + if (this._saveTask) { + let promise = this._saveTask.finalize(); + this._saveTask = null; + return promise; + } + + return Promise.resolve(); }, /** @@ -1723,15 +1742,12 @@ var AddonDatabase = { */ async insertAddons(aAddons, aCallback) { await this.openConnection(); - await this._insertAddons(aAddons, aCallback); - }, - async _insertAddons(aAddons, aCallback) { for (let addon of aAddons) { this._insertAddon(addon); } - await this._saveDBToDisk(); + this.save(); aCallback && aCallback(); }, @@ -1867,18 +1883,6 @@ var AddonDatabase = { return addon; }, - /** - * Write the in-memory DB to disk, after waiting for - * the DB_BATCH_TIMEOUT_MS timeout. - * - * @return Promise A promise that resolves after the - * write to disk has completed. - */ - _saveDBToDisk() { - return this.Writer.saveChanges().catch( - e => logger.error("SaveDBToDisk failed", e)); - }, - /** * Make a developer object from a vanilla * JS object from the JSON database diff --git a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm index b05f59bf7256..7a5143f9ba14 100644 --- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm +++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm @@ -616,7 +616,7 @@ var AddonTestUtils = { let XPIscope = Cu.import("resource://gre/modules/addons/XPIProvider.jsm", {}); // This would be cleaner if I could get it as the rejection reason from // the AddonManagerInternal.shutdown() promise - let shutdownError = XPIscope.XPIProvider._shutdownError; + let shutdownError = XPIscope.XPIDatabase._saveError; AddonManagerPrivate.unregisterProvider(XPIscope.XPIProvider); Cu.unload("resource://gre/modules/addons/XPIProvider.jsm"); diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.jsm b/toolkit/mozapps/extensions/internal/XPIProvider.jsm index 8eb1be0a150a..272b5b89ec30 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm @@ -2091,8 +2091,7 @@ this.XPIProvider = { this.installs = new Set(); this.installLocations = []; this.installLocationsByName = {}; - // Hook for tests to detect when saving database at shutdown time fails - this._shutdownError = null; + // Clear this at startup for xpcshell test restarts this._telemetryDetails = {}; // Register our details structure with AddonManager @@ -2379,11 +2378,7 @@ this.XPIProvider = { this.extensionsActive = false; this._addonFileMap.clear(); - try { - await XPIDatabase.shutdown(); - } catch (err) { - this._shutdownError = err; - } + await XPIDatabase.shutdown(); }, cleanupTemporaryAddons() { diff --git a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js index 0098cf74e6ce..e163da2e36e1 100644 --- a/toolkit/mozapps/extensions/internal/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/internal/XPIProviderUtils.js @@ -16,18 +16,17 @@ var Cr = Components.results; var Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/AddonManager.jsm"); -/* globals AddonManagerPrivate*/ -XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", - "resource://gre/modules/addons/AddonRepository.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", - "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave", - "resource://gre/modules/DeferredSave.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "OS", - "resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyModuleGetters(this, { + AddonManager: "resource://gre/modules/AddonManager.jsm", + AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm", + AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm", + DeferredTask: "resource://gre/modules/DeferredTask.jsm", + FileUtils: "resource://gre/modules/FileUtils.jsm", + OS: "resource://gre/modules/osfile.jsm", + Services: "resource://gre/modules/Services.jsm", +}); + XPCOMUtils.defineLazyServiceGetter(this, "Blocklist", "@mozilla.org/extensions/blocklist;1", Ci.nsIBlocklistService); @@ -280,18 +279,46 @@ this.XPIDatabase = { // Active add-on directories loaded from extensions.ini and prefs at startup. activeBundles: null, + _saveTask: null, + // Saved error object if we fail to read an existing database _loadError: null, + // Saved error object if we fail to save the database + _saveError: null, + // Error reported by our most recent attempt to read or write the database, if any get lastError() { if (this._loadError) return this._loadError; - if (this._deferredSave) - return this._deferredSave.lastError; + if (this._saveError) + return this._saveError; return null; }, + async _saveNow() { + try { + let json = JSON.stringify(this); + let path = this.jsonFile.path; + await OS.File.writeAtomic(path, json, {tmpPath: `${path}.tmp`}); + + if (!this._schemaVersionSet) { + // Update the XPIDB schema version preference the first time we + // successfully save the database. + logger.debug("XPI Database saved, setting schema version preference to " + DB_SCHEMA); + Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA); + this._schemaVersionSet = true; + + // Reading the DB worked once, so we don't need the load error + this._loadError = null; + } + } catch (error) { + logger.warn("Failed to save XPI database", error); + this._saveError = error; + throw error; + } + }, + /** * Mark the current stored data dirty, and schedule a flush to disk */ @@ -307,47 +334,21 @@ this.XPIDatabase = { AddonManagerPrivate.recordSimpleMeasure("XPIDB_late_stack", Log.stackTrace(err)); } - if (!this._deferredSave) { - this._deferredSave = new DeferredSave(this.jsonFile.path, - () => JSON.stringify(this), - ASYNC_SAVE_DELAY_MS); + if (!this._saveTask) { + this._saveTask = new DeferredTask(() => this._saveNow(), + ASYNC_SAVE_DELAY_MS); } - let promise = this._deferredSave.saveChanges(); - if (!this._schemaVersionSet) { - this._schemaVersionSet = true; - promise = promise.then( - count => { - // Update the XPIDB schema version preference the first time we successfully - // save the database. - logger.debug("XPI Database saved, setting schema version preference to " + DB_SCHEMA); - Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA); - // Reading the DB worked once, so we don't need the load error - this._loadError = null; - }, - error => { - // Need to try setting the schema version again later - this._schemaVersionSet = false; - // this._deferredSave.lastError has the most recent error so we don't - // need this any more - this._loadError = null; - - throw error; - }); - } - - promise.catch(error => { - logger.warn("Failed to save XPI database", error); - }); + this._saveTask.arm(); }, - flush() { + async finalize() { // handle the "in memory only" and "saveChanges never called" cases - if (!this._deferredSave) { - return Promise.resolve(0); + if (!this._saveTask) { + return; } - return this._deferredSave.flush(); + await this._saveTask.finalize(); }, /** @@ -658,39 +659,26 @@ this.XPIDatabase = { this.initialized = false; - if (this._deferredSave) { - AddonManagerPrivate.recordSimpleMeasure( - "XPIDB_saves_total", this._deferredSave.totalSaves); - AddonManagerPrivate.recordSimpleMeasure( - "XPIDB_saves_overlapped", this._deferredSave.overlappedSaves); - AddonManagerPrivate.recordSimpleMeasure( - "XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0); - } - // If we're shutting down while still loading, finish loading // before everything else! if (this._dbPromise) { await this._dbPromise; } - // Await and pending DB writes and finish cleaning up. - try { - await this.flush(); - } catch (error) { - logger.error("Flush of XPI database failed", error); - AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1); + // Await any pending DB writes and finish cleaning up. + await this.finalize(); + + if (this._saveError) { // If our last attempt to read or write the DB failed, force a new // extensions.ini to be written to disk on the next startup Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true); - - throw error; } // Clear out the cached addons data loaded from JSON delete this.addonDB; delete this._dbPromise; // same for the deferred save - delete this._deferredSave; + delete this._saveTask; // re-enable the schema version setter delete this._schemaVersionSet; } diff --git a/toolkit/mozapps/extensions/moz.build b/toolkit/mozapps/extensions/moz.build index 9c6f040f0916..c60e35804ae0 100644 --- a/toolkit/mozapps/extensions/moz.build +++ b/toolkit/mozapps/extensions/moz.build @@ -37,7 +37,6 @@ EXTRA_PP_COMPONENTS += [ EXTRA_JS_MODULES += [ 'AddonManager.jsm', 'ChromeManifestParser.jsm', - 'DeferredSave.jsm', 'LightweightThemeManager.jsm', ] diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js b/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js deleted file mode 100644 index bd9f2de9391e..000000000000 --- a/toolkit/mozapps/extensions/test/xpcshell/test_DeferredSave.js +++ /dev/null @@ -1,545 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -// Test behaviour of module to perform deferred save of data -// files to disk - -"use strict"; - -const testFile = gProfD.clone(); -testFile.append("DeferredSaveTest"); - -Components.utils.import("resource://gre/modules/Promise.jsm"); - -var DSContext = Components.utils.import("resource://gre/modules/DeferredSave.jsm", {}); -var DeferredSave = DSContext.DeferredSave; - -// Test wrapper to let us do promise/task based testing of DeferredSave -function DeferredSaveTester(aDataProvider) { - let tester = { - // Deferred for the promise returned by the mock writeAtomic - waDeferred: null, - - // The most recent data "written" by the mock OS.File.writeAtomic - writtenData: undefined, - - dataToSave: "Data to save", - - save: (aData, aWriteHandler) => { - tester.writeHandler = aWriteHandler || writer; - tester.dataToSave = aData; - return tester.saver.saveChanges(); - }, - - flush: (aWriteHandler) => { - tester.writeHandler = aWriteHandler || writer; - return tester.saver.flush(); - }, - - get lastError() { - return tester.saver.lastError; - } - }; - - // Default write handler for most cases where the test case doesn't need - // to do anything while the write is in progress; just completes the write - // on the next event loop - function writer(aTester) { - do_print("default write callback"); - let length = aTester.writtenData.length; - do_execute_soon(() => aTester.waDeferred.resolve(length)); - } - - if (!aDataProvider) - aDataProvider = () => tester.dataToSave; - - tester.saver = new DeferredSave(testFile.path, aDataProvider); - - // Install a mock for OS.File.writeAtomic to let us control the async - // behaviour of the promise - DSContext.OS.File.writeAtomic = function mock_writeAtomic(aFile, aData, aOptions) { - do_print("writeAtomic: " + aFile + " data: '" + aData + "', " + aOptions.toSource()); - tester.writtenData = aData; - tester.waDeferred = Promise.defer(); - tester.writeHandler(tester); - return tester.waDeferred.promise; - }; - - return tester; -} - -/** - * Install a mock nsITimer factory that triggers on the next spin of - * the event loop after it is scheduled - */ -function setQuickMockTimer() { - let quickTimer = { - initWithCallback(aFunction, aDelay, aType) { - do_print("Starting quick timer, delay = " + aDelay); - do_execute_soon(aFunction); - }, - cancel() { - do_throw("Attempted to cancel a quickMockTimer"); - } - }; - DSContext.MakeTimer = () => { - do_print("Creating quick timer"); - return quickTimer; - }; -} - -/** - * Install a mock nsITimer factory in DeferredSave.jsm, returning a promise that resolves - * when the client code sets the timer. Test cases can use this to wait for client code to - * be ready for a timer event, and then signal the event by calling mockTimer.callback(). - * This could use some enhancement; clients can re-use the returned timer, - * but with this implementation it's not possible for the test to wait for - * a second call to initWithCallback() on the re-used timer. - * @return Promise{mockTimer} that resolves when initWithCallback() - * is called - */ -function setPromiseMockTimer() { - return new Promise(resolve => { - let mockTimer = { - callback: null, - delay: null, - type: null, - isCancelled: false, - - initWithCallback(aFunction, aDelay, aType) { - do_print("Starting timer, delay = " + aDelay); - this.callback = aFunction; - this.delay = aDelay; - this.type = aType; - // cancelled timers can be re-used - this.isCancelled = false; - resolve(this); - }, - cancel() { - do_print("Cancelled mock timer"); - this.callback = null; - this.delay = null; - this.type = null; - this.isCancelled = true; - // If initWithCallback was never called, resolve to let tests check for cancel - resolve(this); - } - }; - DSContext.MakeTimer = () => { - do_print("Creating mock timer"); - return mockTimer; - }; - }); -} - -/** - * Return a Promise that resolves after the specified number of milliseconds - */ -function delay(aDelayMS) { - return new Promise(resolve => { - do_timeout(aDelayMS, () => resolve(null)); - }); -} - -// Modify set data once, ask for save, make sure it saves cleanly -add_task(async function test_basic_save_succeeds() { - setQuickMockTimer(); - let tester = DeferredSaveTester(); - let data = "Test 1 Data"; - - await tester.save(data); - do_check_eq(tester.writtenData, data); - do_check_eq(1, tester.saver.totalSaves); -}); - -// Two saves called during the same event loop, both with callbacks -// Make sure we save only the second version of the data -add_task(async function test_two_saves() { - setQuickMockTimer(); - let tester = DeferredSaveTester(); - let firstCallback_happened = false; - let firstData = "Test first save"; - let secondData = "Test second save"; - - // first save should not resolve until after the second one is called, - // so we can't just yield this promise - tester.save(firstData).then(count => { - do_check_eq(secondData, tester.writtenData); - do_check_false(firstCallback_happened); - firstCallback_happened = true; - }, do_report_unexpected_exception); - - await tester.save(secondData); - do_check_true(firstCallback_happened); - do_check_eq(secondData, tester.writtenData); - do_check_eq(1, tester.saver.totalSaves); -}); - -// Two saves called with a delay in between, both with callbacks -// Make sure we save the second version of the data -add_task(async function test_two_saves_delay() { - let timerPromise = setPromiseMockTimer(); - let tester = DeferredSaveTester(); - let firstCallback_happened = false; - let delayDone = false; - - let firstData = "First data to save with delay"; - let secondData = "Modified data to save with delay"; - - tester.save(firstData).then(count => { - do_check_false(firstCallback_happened); - do_check_true(delayDone); - do_check_eq(secondData, tester.writtenData); - firstCallback_happened = true; - }, do_report_unexpected_exception); - - // Wait a short time to let async events possibly spawned by the - // first tester.save() to run - await delay(2); - delayDone = true; - // request to save modified data - let saving = tester.save(secondData); - // Yield to wait for client code to set the timer - let activeTimer = await timerPromise; - // and then trigger it - activeTimer.callback(); - // now wait for the DeferredSave to finish saving - await saving; - do_check_true(firstCallback_happened); - do_check_eq(secondData, tester.writtenData); - do_check_eq(1, tester.saver.totalSaves); - do_check_eq(0, tester.saver.overlappedSaves); -}); - -// Test case where OS.File immediately reports an error when the write begins -// Also check that the "error" getter correctly returns the error -// Then do a write that succeeds, and make sure the error is cleared -add_task(async function test_error_immediate() { - let tester = DeferredSaveTester(); - let testError = new Error("Forced failure"); - function writeFail(aTester) { - aTester.waDeferred.reject(testError); - } - - setQuickMockTimer(); - await tester.save("test_error_immediate", writeFail).then( - count => do_throw("Did not get expected error"), - error => do_check_eq(testError.message, error.message) - ); - do_check_eq(testError, tester.lastError); - - // This write should succeed and clear the error - await tester.save("test_error_immediate succeeds"); - do_check_eq(null, tester.lastError); - // The failed save attempt counts in our total - do_check_eq(2, tester.saver.totalSaves); -}); - -// Save one set of changes, then while the write is in progress, modify the -// data two more times. Test that we re-write the dirty data exactly once -// after the first write succeeds -add_task(async function dirty_while_writing() { - let tester = DeferredSaveTester(); - let firstData = "First data"; - let secondData = "Second data"; - let thirdData = "Third data"; - let firstCallback_happened = false; - let secondCallback_happened = false; - let writer = await new Promise(resolve => { - - function writeCallback(aTester) { - resolve(aTester.waDeferred); - } - - setQuickMockTimer(); - do_print("First save"); - tester.save(firstData, writeCallback).then( - count => { - do_check_false(firstCallback_happened); - do_check_false(secondCallback_happened); - do_check_eq(tester.writtenData, firstData); - firstCallback_happened = true; - }, do_report_unexpected_exception); - - do_print("waiting for writer"); - }); - do_print("Write started"); - - // Delay a bit, modify the data and call saveChanges, delay a bit more, - // modify the data and call saveChanges again, another delay, - // then complete the in-progress write - await delay(1); - - tester.save(secondData).then( - count => { - do_check_true(firstCallback_happened); - do_check_false(secondCallback_happened); - do_check_eq(tester.writtenData, thirdData); - secondCallback_happened = true; - }, do_report_unexpected_exception); - - // wait and then do the third change - await delay(1); - let thirdWrite = tester.save(thirdData); - - // wait a bit more and then finally finish the first write - await delay(1); - writer.resolve(firstData.length); - - // Now let everything else finish - await thirdWrite; - do_check_true(firstCallback_happened); - do_check_true(secondCallback_happened); - do_check_eq(tester.writtenData, thirdData); - do_check_eq(2, tester.saver.totalSaves); - do_check_eq(1, tester.saver.overlappedSaves); -}); - -// A write callback for the OS.File.writeAtomic mock that rejects write attempts -function disabled_write_callback(aTester) { - do_throw("Should not have written during clean flush"); -} - -// special write callback that disables itself to make sure -// we don't try to write twice -function write_then_disable(aTester) { - do_print("write_then_disable"); - let length = aTester.writtenData.length; - aTester.writeHandler = disabled_write_callback; - do_execute_soon(() => aTester.waDeferred.resolve(length)); -} - -// Flush tests. First, do an ordinary clean save and then call flush; -// there should not be another save -add_task(async function flush_after_save() { - setQuickMockTimer(); - let tester = DeferredSaveTester(); - let dataToSave = "Flush after save"; - - await tester.save(dataToSave); - await tester.flush(disabled_write_callback); - do_check_eq(1, tester.saver.totalSaves); -}); - -// Flush while a write is in progress, but the in-memory data is clean -add_task(async function flush_during_write() { - let tester = DeferredSaveTester(); - let dataToSave = "Flush during write"; - let firstCallback_happened = false; - let writer = await new Promise(resolve => { - - function writeCallback(aTester) { - resolve(aTester.waDeferred); - } - - setQuickMockTimer(); - tester.save(dataToSave, writeCallback).then( - count => { - do_check_false(firstCallback_happened); - firstCallback_happened = true; - }, do_report_unexpected_exception); - - }); - - // call flush with the write callback disabled, delay a bit more, complete in-progress write - let flushing = tester.flush(disabled_write_callback); - await delay(2); - writer.resolve(dataToSave.length); - - // now wait for the flush to finish - await flushing; - do_check_true(firstCallback_happened); - do_check_eq(1, tester.saver.totalSaves); -}); - -// Flush while dirty but write not in progress -// The data written should be the value at the time -// flush() is called, even if it is changed later -add_task(async function flush_while_dirty() { - let timerPromise = setPromiseMockTimer(); - let tester = DeferredSaveTester(); - let firstData = "Flush while dirty, valid data"; - let firstCallback_happened = false; - - tester.save(firstData, write_then_disable).then( - count => { - do_check_false(firstCallback_happened); - firstCallback_happened = true; - do_check_eq(tester.writtenData, firstData); - }, do_report_unexpected_exception); - - // Wait for the timer to be set, but don't trigger it so the write won't start - let activeTimer = await timerPromise; - - let flushing = tester.flush(); - - // Make sure the timer was cancelled - do_check_true(activeTimer.isCancelled); - - // Also make sure that data changed after the flush call - // (even without a saveChanges() call) doesn't get written - tester.dataToSave = "Flush while dirty, invalid data"; - - await flushing; - do_check_true(firstCallback_happened); - do_check_eq(tester.writtenData, firstData); - do_check_eq(1, tester.saver.totalSaves); -}); - -// And the grand finale - modify the data, start writing, -// modify the data again so we're in progress and dirty, -// then flush, then modify the data again -// Data for the second write should be taken at the time -// flush() is called, even if it is modified later -add_task(async function flush_writing_dirty() { - let timerPromise = setPromiseMockTimer(); - let tester = DeferredSaveTester(); - let firstData = "Flush first pass data"; - let secondData = "Flush second pass data"; - let firstCallback_happened = false; - let secondCallback_happened = false; - let writeStarted = Promise.defer(); - - function writeCallback(aTester) { - writeStarted.resolve(aTester.waDeferred); - } - - tester.save(firstData, writeCallback).then( - count => { - do_check_false(firstCallback_happened); - do_check_eq(tester.writtenData, firstData); - firstCallback_happened = true; - }, do_report_unexpected_exception); - - // Trigger the timer callback as soon as the DeferredSave sets it - let activeTimer = await timerPromise; - activeTimer.callback(); - let writer = await writeStarted.promise; - // the first write has started - - // dirty the data and request another save - // after the second save completes, there should not be another write - tester.save(secondData, write_then_disable).then( - count => { - do_check_true(firstCallback_happened); - do_check_false(secondCallback_happened); - do_check_eq(tester.writtenData, secondData); - secondCallback_happened = true; - }, do_report_unexpected_exception); - - let flushing = tester.flush(write_then_disable); - // Flush should have cancelled our timer - do_check_true(activeTimer.isCancelled); - tester.dataToSave = "Flush, invalid data: changed late"; - // complete the first write - writer.resolve(firstData.length); - // now wait for the second write / flush to complete - await flushing; - do_check_true(firstCallback_happened); - do_check_true(secondCallback_happened); - do_check_eq(tester.writtenData, secondData); - do_check_eq(2, tester.saver.totalSaves); - do_check_eq(1, tester.saver.overlappedSaves); -}); - -// A data provider callback that throws an error the first -// time it is called, and a different error the second time -// so that tests can (a) make sure the promise is rejected -// with the error and (b) make sure the provider is only -// called once in case of error -const expectedDataError = "Failed to serialize data"; -var badDataError = null; -function badDataProvider() { - let err = new Error(badDataError); - badDataError = "badDataProvider called twice"; - throw err; -} - -// Handle cases where data provider throws -// First, throws during a normal save -add_task(async function data_throw() { - setQuickMockTimer(); - badDataError = expectedDataError; - let tester = DeferredSaveTester(badDataProvider); - await tester.save("data_throw").then( - count => do_throw("Expected serialization failure"), - error => do_check_eq(error.message, expectedDataError)); -}); - -// Now, throws during flush -add_task(async function data_throw_during_flush() { - badDataError = expectedDataError; - let tester = DeferredSaveTester(badDataProvider); - let firstCallback_happened = false; - - setPromiseMockTimer(); - // Write callback should never be called - tester.save("data_throw_during_flush", disabled_write_callback).then( - count => do_throw("Expected serialization failure"), - error => { - do_check_false(firstCallback_happened); - do_check_eq(error.message, expectedDataError); - firstCallback_happened = true; - }); - - // flush() will cancel the timer - await tester.flush(disabled_write_callback).then( - count => do_throw("Expected serialization failure"), - error => do_check_eq(error.message, expectedDataError) - ); - - do_check_true(firstCallback_happened); -}); - -// Try to reproduce race condition. The observed sequence of events: -// saveChanges -// start writing -// saveChanges -// finish writing (need to restart delayed timer) -// saveChanges -// flush -// write starts -// actually restart timer for delayed write -// write completes -// delayed timer goes off, throws error because DeferredSave has been torn down -add_task(async function delay_flush_race() { - let timerPromise = setPromiseMockTimer(); - let tester = DeferredSaveTester(); - let firstData = "First save"; - let secondData = "Second save"; - let thirdData = "Third save"; - let writeStarted = Promise.defer(); - - function writeCallback(aTester) { - writeStarted.resolve(aTester.waDeferred); - } - - // This promise won't resolve until after writeStarted - let firstSave = tester.save(firstData, writeCallback); - (await timerPromise).callback(); - - let writer = await writeStarted.promise; - // the first write has started - - // dirty the data and request another save - let secondSave = tester.save(secondData); - - // complete the first write - writer.resolve(firstData.length); - await firstSave; - do_check_eq(tester.writtenData, firstData); - - tester.save(thirdData); - let flushing = tester.flush(); - - await secondSave; - do_check_eq(tester.writtenData, thirdData); - - await flushing; - do_check_eq(tester.writtenData, thirdData); - - // Our DeferredSave should not have a _timer here; if it - // does, the bug caused a reschedule - do_check_eq(null, tester.saver._timer); -}); diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini index e35f14808363..1255ebe1070d 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini @@ -17,7 +17,6 @@ tags = blocklist [test_cache_certdb.js] run-if = addon_signing [test_cacheflush.js] -[test_DeferredSave.js] [test_gmpProvider.js] skip-if = appname != "firefox" [test_hotfix_cert.js] diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index efc900086e92..4ec887e179e7 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -3598,7 +3598,15 @@ class ChannelDownloader extends CommonDownloader { this._bkgFileSaver.onStartRequest(request, context); if (request instanceof Ci.nsIResumableChannel) { - this._patch.setProperty("entityID", request.entityID); + // Reading the entityID can throw if the server doesn't allow resuming. + try { + this._patch.setProperty("entityID", request.entityID); + } catch (ex) { + if (!(ex instanceof Components.Exception) || + ex.result != Cr.NS_ERROR_NOT_RESUMABLE) { + throw ex; + } + } } var um = Cc["@mozilla.org/updates/update-manager;1"]. diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 1ca91b52dffc..84a3b78efecb 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -710,7 +710,7 @@ nsXREDirProvider::LoadContentProcessTempDir() static bool IsContentSandboxDisabled() { - return !BrowserTabsRemoteAutostart() || (GetEffectiveContentSandboxLevel() < 1); + return !BrowserTabsRemoteAutostart() || (!IsContentSandboxEnabled()); } // diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 9ecbde6a5ac3..c38c9e1eed3d 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -1172,11 +1172,11 @@ nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, gfxContextAutoSaveRestore autoSR(ctx); gfxMatrix matrix; if (!snapped) { // else rects are in device coords - matrix = ctx->CurrentMatrix(); + matrix = ctx->CurrentMatrixDouble(); } matrix.Translate(origin); matrix.Scale(scaleFactor, scaleFactor); // Draw in GDK coords - ctx->SetMatrix(matrix); + ctx->SetMatrixDouble(matrix); // The gdk_clip is just advisory here, meaning "you don't // need to draw outside this rect if you don't feel like it!" diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 7ec086e6a47e..c3da6dc00b0c 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -3842,10 +3842,11 @@ nsWindow::Create(nsIWidget* aParent, } } break; + case eWindowType_plugin: case eWindowType_plugin_ipc_chrome: case eWindowType_plugin_ipc_content: - MOZ_ASSERT_UNREACHABLE(); + MOZ_ASSERT_UNREACHABLE("Unexpected eWindowType_plugin*"); return NS_ERROR_FAILURE; case eWindowType_child: { diff --git a/xpcom/base/nsDebug.h b/xpcom/base/nsDebug.h index 15ed54416673..77fe2d5be344 100644 --- a/xpcom/base/nsDebug.h +++ b/xpcom/base/nsDebug.h @@ -106,24 +106,9 @@ inline void MOZ_PretendNoReturn() #endif /** - * NS_PRECONDITION/POSTCONDITION are synonyms for NS_ASSERTION. + * NS_PRECONDITION is a synonym for NS_ASSERTION. */ #define NS_PRECONDITION(expr, str) NS_ASSERTION(expr, str) -#define NS_POSTCONDITION(expr, str) NS_ASSERTION(expr, str) - -/** - * This macros triggers a program failure if executed. It indicates that - * an attempt was made to execute some unimplemented functionality. - */ -#ifdef DEBUG -#define NS_NOTYETIMPLEMENTED(str) \ - do { \ - NS_DebugBreak(NS_DEBUG_ASSERTION, str, "NotYetImplemented", __FILE__, __LINE__); \ - MOZ_PretendNoReturn(); \ - } while(0) -#else -#define NS_NOTYETIMPLEMENTED(str) do { /* nothing */ } while(0) -#endif /** * This macros triggers a program failure if executed. It indicates that @@ -162,21 +147,6 @@ inline void MOZ_PretendNoReturn() #define NS_WARNING(str) do { /* nothing */ } while(0) #endif -/** - * Trigger an debug-only abort. - * - * @see MOZ_RELEASE_ASSERT or MOZ_CRASH for release-mode asserts. - */ -#ifdef DEBUG -#define NS_ABORT() \ - do { \ - NS_DebugBreak(NS_DEBUG_ABORT, nullptr, nullptr, __FILE__, __LINE__); \ - MOZ_PretendNoReturn(); \ - } while(0) -#else -#define NS_ABORT() do { /* nothing */ } while(0) -#endif - /** * Trigger a debugger breakpoint, only in debug builds. */ diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index 22b4c813269e..2f25fd2d070f 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -1603,8 +1603,9 @@ nsLocalFile::Normalize() path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\")); } } - NS_POSTCONDITION(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid"); - NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid"); + + MOZ_ASSERT(0 < rootIdx && rootIdx < (int32_t)path.Length(), "rootIdx is invalid"); + MOZ_ASSERT(path.CharAt(rootIdx) == '\\', "rootIdx is invalid"); // if there is nothing following the root path then it is already normalized if (rootIdx + 1 == (int32_t)path.Length()) { diff --git a/xpcom/threads/BlockingResourceBase.cpp b/xpcom/threads/BlockingResourceBase.cpp index 9cf2f6d77827..98f80655645a 100644 --- a/xpcom/threads/BlockingResourceBase.cpp +++ b/xpcom/threads/BlockingResourceBase.cpp @@ -271,7 +271,7 @@ void BlockingResourceBase::CheckAcquire() { if (mType == eCondVar) { - NS_NOTYETIMPLEMENTED( + MOZ_ASSERT_UNREACHABLE( "FIXME bug 456272: annots. to allow CheckAcquire()ing condvars"); return; } @@ -315,7 +315,7 @@ void BlockingResourceBase::Acquire() { if (mType == eCondVar) { - NS_NOTYETIMPLEMENTED( + MOZ_ASSERT_UNREACHABLE( "FIXME bug 456272: annots. to allow Acquire()ing condvars"); return; } @@ -340,7 +340,7 @@ void BlockingResourceBase::Release() { if (mType == eCondVar) { - NS_NOTYETIMPLEMENTED( + MOZ_ASSERT_UNREACHABLE( "FIXME bug 456272: annots. to allow Release()ing condvars"); return; } diff --git a/xpfe/components/directory/nsDirectoryViewer.cpp b/xpfe/components/directory/nsDirectoryViewer.cpp index baf5bc324e1f..cab4f7bfb264 100644 --- a/xpfe/components/directory/nsDirectoryViewer.cpp +++ b/xpfe/components/directory/nsDirectoryViewer.cpp @@ -1371,6 +1371,6 @@ nsDirectoryViewerFactory::CreateInstanceForDocument(nsISupports* aContainer, const char *aCommand, nsIContentViewer** aDocViewerResult) { - NS_NOTYETIMPLEMENTED("didn't expect to get here"); + MOZ_ASSERT_UNREACHABLE("didn't expect to get here"); return NS_ERROR_NOT_IMPLEMENTED; }