From 58325c0ef03f0bff2d7a4421a4eb8a773aa15ee2 Mon Sep 17 00:00:00 2001 From: Olivier Yiptong Date: Tue, 8 Mar 2016 14:20:51 -0500 Subject: [PATCH 01/53] Bug 1239119 - BackgroundPageThumbs captureIfMissing promise resolves when task is truly done r=ursula MozReview-Commit-ID: LmN9Phdrspv --HG-- extra : rebase_source : cd05e2ab6d7dfb5f306f50e2ac92c71bb64aff6d --- .../thumbnails/BackgroundPageThumbs.jsm | 30 +++++++++++++++++-- .../test/browser_thumbnails_bg_bad_url.js | 7 ++++- .../test/browser_thumbnails_bg_basic.js | 7 +++++ ...wser_thumbnails_bg_crash_during_capture.js | 15 +++++++++- .../test/browser_thumbnails_bg_redirect.js | 16 ++++++++++ .../test/browser_thumbnails_bg_timeout.js | 7 ++++- toolkit/components/thumbnails/test/head.js | 21 +++++++++++++ 7 files changed, 98 insertions(+), 5 deletions(-) diff --git a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm index 90f003ef6862..63ecee06a9d5 100644 --- a/toolkit/components/thumbnails/BackgroundPageThumbs.jsm +++ b/toolkit/components/thumbnails/BackgroundPageThumbs.jsm @@ -88,7 +88,7 @@ const BackgroundPageThumbs = { * @param url The URL to capture. * @param options An optional object that configures the capture. See * capture() for description. - * @return {Promise} Promise; + * @return {Promise} A Promise that resolves when this task completes */ captureIfMissing: Task.async(function* (url, options={}) { // The fileExistsForURL call is an optimization, potentially but unlikely @@ -101,10 +101,31 @@ const BackgroundPageThumbs = { } return url; } + let thumbPromise = new Promise((resolve, reject) => { + function observe(subject, topic, data) { // jshint ignore:line + if (data === url) { + switch(topic) { + case "page-thumbnail:create": + resolve(); + break; + case "page-thumbnail:error": + reject(new Error("page-thumbnail:error")); + break; + } + Services.obs.removeObserver(observe, "page-thumbnail:create"); + Services.obs.removeObserver(observe, "page-thumbnail:error"); + } + } + Services.obs.addObserver(observe, "page-thumbnail:create", false); + Services.obs.addObserver(observe, "page-thumbnail:error", false); + }); try{ this.capture(url, options); + yield thumbPromise; } catch (err) { - options.onDone(url); + if (options.onDone) { + options.onDone(url); + } throw err; } return url; @@ -250,6 +271,9 @@ const BackgroundPageThumbs = { throw new Error("The capture should be at the head of the queue."); this._captureQueue.shift(); this._capturesByURL.delete(capture.url); + if (capture.doneReason != TEL_CAPTURE_DONE_OK) { + Services.obs.notifyObservers(null, "page-thumbnail:error", capture.url); + } // Start the destroy-browser timer *before* processing the capture queue. let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); @@ -285,6 +309,7 @@ function Capture(url, captureCallback, options) { this.id = Capture.nextID++; this.creationDate = new Date(); this.doneCallbacks = []; + this.doneReason; if (options.onDone) this.doneCallbacks.push(options.onDone); } @@ -371,6 +396,7 @@ Capture.prototype = { // removes the didCapture message listener. let { captureCallback, doneCallbacks, options } = this; this.destroy(); + this.doneReason = reason; if (typeof(reason) != "number") { throw new Error("A done reason must be given."); diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_bad_url.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_bad_url.js index c6a74a24f6ba..df8ef8d965cc 100644 --- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_bad_url.js +++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_bad_url.js @@ -14,5 +14,10 @@ function* runTests() { next(); }, }); - yield true; + yield new Promise(resolve => { + bgAddPageThumbObserver(url).catch(function(err) { + ok(true, "page-thumbnail error produced"); + resolve(); + }); + }); } diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_basic.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_basic.js index 0a31f5823ba7..027e0bfb7450 100644 --- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_basic.js +++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_basic.js @@ -5,8 +5,15 @@ function* runTests() { let url = "http://www.example.com/"; ok(!thumbnailExists(url), "Thumbnail should not be cached yet."); + let capturePromise = new Promise(resolve => { + bgAddPageThumbObserver(url).then(() => { + ok(true, `page-thumbnail created for ${url}`); + resolve(); + }); + }); let capturedURL = yield bgCapture(url); is(capturedURL, url, "Captured URL should be URL passed to capture"); + yield capturePromise; ok(thumbnailExists(url), "Thumbnail should be cached after capture"); removeThumbnail(url); diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js index 3d97b1413503..db67a04a8987 100644 --- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js +++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_crash_during_capture.js @@ -29,8 +29,21 @@ function* runTests() { ok(crashObserver.crashed, "Saw a crash from this test"); next(); }}); + let crashPromise = new Promise(resolve => { + bgAddPageThumbObserver(waitUrl).catch(function(err) { + ok(true, `page-thumbnail error thrown for ${waitUrl}`); + resolve(); + }); + }); + let capturePromise = new Promise(resolve => { + bgAddPageThumbObserver(goodUrl).then(() => { + ok(true, `page-thumbnail created for ${goodUrl}`); + resolve(); + }); + }); info("Crashing the thumbnail content process."); mm.sendAsyncMessage("thumbnails-test:crash"); - yield true; + yield crashPromise; + yield capturePromise; } diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_redirect.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_redirect.js index 179bdd6f9789..baa1b6d681af 100644 --- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_redirect.js +++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_redirect.js @@ -10,9 +10,25 @@ function* runTests() { ok(!thumbnailExists(finalURL), "Thumbnail file for final URL should not exist yet."); + let captureOriginalPromise = new Promise(resolve => { + bgAddPageThumbObserver(originalURL).then(() => { + ok(true, `page-thumbnail created for ${originalURL}`); + resolve(); + }); + }); + + let captureFinalPromise = new Promise(resolve => { + bgAddPageThumbObserver(finalURL).then(() => { + ok(true, `page-thumbnail created for ${finalURL}`); + resolve(); + }); + }); + let capturedURL = yield bgCapture(originalURL); is(capturedURL, originalURL, "Captured URL should be URL passed to capture"); + yield captureOriginalPromise; + yield captureFinalPromise; ok(thumbnailExists(originalURL), "Thumbnail for original URL should be cached"); ok(thumbnailExists(finalURL), diff --git a/toolkit/components/thumbnails/test/browser_thumbnails_bg_timeout.js b/toolkit/components/thumbnails/test/browser_thumbnails_bg_timeout.js index ba83ea72236b..da05b43553ba 100644 --- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_timeout.js +++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_timeout.js @@ -15,5 +15,10 @@ function* runTests() { next(); }, }); - yield true; + yield new Promise(resolve => { + bgAddPageThumbObserver(url).catch(function(err) { + ok(true, `page-thumbnail error thrown for ${url}`); + resolve(); + }); + }); } diff --git a/toolkit/components/thumbnails/test/head.js b/toolkit/components/thumbnails/test/head.js index 214c788b4eee..a2dce4ad8533 100644 --- a/toolkit/components/thumbnails/test/head.js +++ b/toolkit/components/thumbnails/test/head.js @@ -277,6 +277,27 @@ function bgTestPageURL(aOpts = {}) { return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(aOpts)); } +function bgAddPageThumbObserver(url) { + return new Promise((resolve, reject) => { + function observe(subject, topic, data) { // jshint ignore:line + if (data === url) { + switch(topic) { + case "page-thumbnail:create": + resolve(); + break; + case "page-thumbnail:error": + reject(new Error("page-thumbnail:error")); + break; + } + Services.obs.removeObserver(observe, "page-thumbnail:create"); + Services.obs.removeObserver(observe, "page-thumbnail:error"); + } + } + Services.obs.addObserver(observe, "page-thumbnail:create", false); + Services.obs.addObserver(observe, "page-thumbnail:error", false); + }); +} + function bgAddCrashObserver() { let crashed = false; Services.obs.addObserver(function crashObserver(subject, topic, data) { From ddc2dcdbeb243981fccb01833b2e75a05c50b622 Mon Sep 17 00:00:00 2001 From: Olivier Yiptong Date: Tue, 8 Mar 2016 14:20:56 -0500 Subject: [PATCH 02/53] Bug 1239119 - PreviewProvider module produces thumbnail data URIs given a url r=ursula MozReview-Commit-ID: KD6taVtzJCy --HG-- extra : rebase_source : 5b007224ec4916456c343bcefcad7910e43f2b64 --- browser/components/newtab/PreviewProvider.jsm | 49 ++++++++++ browser/components/newtab/moz.build | 3 +- .../newtab/tests/browser/blue_page.html | 9 ++ .../newtab/tests/browser/browser.ini | 2 + .../tests/browser/browser_PreviewProvider.js | 90 +++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 browser/components/newtab/PreviewProvider.jsm create mode 100644 browser/components/newtab/tests/browser/blue_page.html create mode 100644 browser/components/newtab/tests/browser/browser_PreviewProvider.js diff --git a/browser/components/newtab/PreviewProvider.jsm b/browser/components/newtab/PreviewProvider.jsm new file mode 100644 index 000000000000..8624b8544239 --- /dev/null +++ b/browser/components/newtab/PreviewProvider.jsm @@ -0,0 +1,49 @@ +/* global XPCOMUtils, BackgroundPageThumbs, FileUtils, PageThumbsStorage, Task, MIMEService */ +/* exported PreviewProvider */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["PreviewProvider"]; + +const {utils: Cu} = Components; +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/PageThumbs.jsm"); +Cu.import("resource://gre/modules/FileUtils.jsm"); +const {OS} = Cu.import("resource://gre/modules/osfile.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "BackgroundPageThumbs", + "resource://gre/modules/BackgroundPageThumbs.jsm"); +XPCOMUtils.defineLazyServiceGetter(this, "MIMEService", + "@mozilla.org/mime;1", "nsIMIMEService"); + +let PreviewProvider = { + /** + * Returns a thumbnail as a data URI for a url, creating it if necessary + * + * @param {String} url + * a url to obtain a thumbnail for + * @return {Promise} A Promise that resolves with a base64 encoded thumbnail + */ + getThumbnail: Task.async(function* PreviewProvider_getThumbnail(url) { + try { + yield BackgroundPageThumbs.captureIfMissing(url); + let imgPath = PageThumbsStorage.getFilePathForURL(url); + + // OS.File object used to easily read off-thread + let file = yield OS.File.open(imgPath, {read: true, existing: true}); + + // nsIFile object needed for MIMEService + let nsFile = FileUtils.File(imgPath); + + let contentType = MIMEService.getTypeFromFile(nsFile); + let bytes = yield file.read(); + let encodedData = btoa(String.fromCharCode.apply(null, bytes)); + file.close(); + return `data:${contentType};base64,${encodedData}`; + } catch (err) { + Cu.reportError(`PreviewProvider_getThumbnail error: ${err}`); + throw err; + } + }) +}; diff --git a/browser/components/newtab/moz.build b/browser/components/newtab/moz.build index 501bd9d4da58..efcc23d88831 100644 --- a/browser/components/newtab/moz.build +++ b/browser/components/newtab/moz.build @@ -16,7 +16,8 @@ EXTRA_JS_MODULES += [ 'NewTabRemoteResources.jsm', 'NewTabURL.jsm', 'NewTabWebChannel.jsm', - 'PlacesProvider.jsm' + 'PlacesProvider.jsm', + 'PreviewProvider.jsm' ] XPIDL_SOURCES += [ diff --git a/browser/components/newtab/tests/browser/blue_page.html b/browser/components/newtab/tests/browser/blue_page.html new file mode 100644 index 000000000000..a7f000bfdf9b --- /dev/null +++ b/browser/components/newtab/tests/browser/blue_page.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/browser/components/newtab/tests/browser/browser.ini b/browser/components/newtab/tests/browser/browser.ini index 67d6d3a8a27c..a04ec639b03f 100644 --- a/browser/components/newtab/tests/browser/browser.ini +++ b/browser/components/newtab/tests/browser/browser.ini @@ -3,7 +3,9 @@ support-files = dummy_page.html newtabwebchannel_basic.html newtabmessages_prefs.html + blue_page.html +[browser_PreviewProvider.js] [browser_remotenewtab_pageloads.js] [browser_newtab_overrides.js] [browser_newtabmessages.js] diff --git a/browser/components/newtab/tests/browser/browser_PreviewProvider.js b/browser/components/newtab/tests/browser/browser_PreviewProvider.js new file mode 100644 index 000000000000..b1e3eda9fdc6 --- /dev/null +++ b/browser/components/newtab/tests/browser/browser_PreviewProvider.js @@ -0,0 +1,90 @@ +/* globals XPCOMUtils, Services, PreviewProvider, registerCleanupFunction */ +"use strict"; + +let Cu = Components.utils; +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "PreviewProvider", + "resource:///modules/PreviewProvider.jsm"); + +var oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"); +Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false); + +registerCleanupFunction(function() { + while (gBrowser.tabs.length > 1) { + gBrowser.removeTab(gBrowser.tabs[1]); + } + Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref); +}); + +const TEST_URL = "https://example.com/browser/browser/components/newtab/tests/browser/blue_page.html"; + +function pixelsForDataURI(dataURI, options) { + return new Promise(resolve => { + if (!options) { + options = {}; + } + let {width, height} = options; + if (!width) { + width = 100; + } + if (!height) { + height = 100; + } + + let htmlns = "http://www.w3.org/1999/xhtml"; + let img = document.createElementNS(htmlns, "img"); + img.setAttribute("src", dataURI); + + img.addEventListener("load", function onLoad() { + img.removeEventListener("load", onLoad, true); + let canvas = document.createElementNS(htmlns, "canvas"); + canvas.setAttribute("width", width); + canvas.setAttribute("height", height); + let ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, width, height); + let result = ctx.getImageData(0, 0, width, height).data; + resolve(result); + }); + }); +} + +function* chunk_four(listData) { + let index = 0; + while (index < listData.length) { + yield listData.slice(index, index + 5); + index += 4; + } +} + +add_task(function* open_page() { + let dataURI = yield PreviewProvider.getThumbnail(TEST_URL); + let pixels = yield pixelsForDataURI(dataURI, {width: 10, height: 10}); + let rgbCount = {r: 0, g: 0, b: 0, a: 0}; + for (let [r, g, b, a] of chunk_four(pixels)) { + if (r === 255) { + rgbCount.r += 1; + } + if (g === 255) { + rgbCount.g += 1; + } + if (b === 255) { + rgbCount.b += 1; + } + if (a === 255) { + rgbCount.a += 1; + } + } + Assert.equal(`${rgbCount.r},${rgbCount.g},${rgbCount.b},${rgbCount.a}`, + "0,0,100,100", "there should be 100 blue-only pixels at full opacity"); +}); + +add_task(function* invalid_url() { + try { + yield PreviewProvider.getThumbnail("invalid:URL"); + } catch (err) { + Assert.ok(true, "URL Failed"); + } +}); From e626493cd52f3371cdd99800dee13d8c46ecf299 Mon Sep 17 00:00:00 2001 From: Olivier Yiptong Date: Wed, 16 Mar 2016 16:17:57 -0400 Subject: [PATCH 03/53] Bug 1239119 - Move provider init for newtab to NewTabMessages.jsm r=ursula MozReview-Commit-ID: DE3AqHXF4jl --HG-- extra : rebase_source : dff26cec63c36b3654e64bc8aba6547e154f65a5 --- browser/components/newtab/NewTabMessages.jsm | 6 +++++ .../tests/browser/browser_newtabmessages.js | 1 - .../tests/browser/browser_newtabwebchannel.js | 24 ++++++++++++------- browser/components/nsBrowserGlue.js | 10 -------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/browser/components/newtab/NewTabMessages.jsm b/browser/components/newtab/NewTabMessages.jsm index 560d559b7a31..eb4cfa40e0be 100644 --- a/browser/components/newtab/NewTabMessages.jsm +++ b/browser/components/newtab/NewTabMessages.jsm @@ -68,6 +68,9 @@ let NewTabMessages = { }, init() { + NewTabPrefsProvider.prefs.init(); + NewTabWebChannel.init(); + this._prefs.enabled = Preferences.get(PREF_ENABLED, false); if (this._prefs.enabled) { @@ -91,5 +94,8 @@ let NewTabMessages = { NewTabPrefsProvider.prefs.off(pref, this.handlePrefChange); } } + + NewTabPrefsProvider.prefs.uninit(); + NewTabWebChannel.uninit(); } }; diff --git a/browser/components/newtab/tests/browser/browser_newtabmessages.js b/browser/components/newtab/tests/browser/browser_newtabmessages.js index 52d33aebb4aa..6b39e15df921 100644 --- a/browser/components/newtab/tests/browser/browser_newtabmessages.js +++ b/browser/components/newtab/tests/browser/browser_newtabmessages.js @@ -17,7 +17,6 @@ function setup() { function cleanup() { NewTabMessages.uninit(); - NewTabWebChannel.tearDownState(); Preferences.set("browser.newtabpage.remote", false); Preferences.set("browser.newtabpage.remote.mode", "production"); } diff --git a/browser/components/newtab/tests/browser/browser_newtabwebchannel.js b/browser/components/newtab/tests/browser/browser_newtabwebchannel.js index 94fee6eea35e..69b5e56bf04f 100644 --- a/browser/components/newtab/tests/browser/browser_newtabwebchannel.js +++ b/browser/components/newtab/tests/browser/browser_newtabwebchannel.js @@ -7,12 +7,22 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NewTabWebChannel", "resource:///modules/NewTabWebChannel.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "NewTabMessages", + "resource:///modules/NewTabMessages.jsm"); const TEST_URL = "https://example.com/browser/browser/components/newtab/tests/browser/newtabwebchannel_basic.html"; const TEST_URL_2 = "http://mochi.test:8888/browser/browser/components/newtab/tests/browser/newtabwebchannel_basic.html"; +function setup(mode = "test") { + Preferences.set("browser.newtabpage.remote.mode", mode); + Preferences.set("browser.newtabpage.remote", true); + NewTabWebChannel.init(); + NewTabMessages.init(); +} + function cleanup() { - NewTabWebChannel.tearDownState(); + NewTabMessages.uninit(); + NewTabWebChannel.uninit(); Preferences.set("browser.newtabpage.remote", false); Preferences.set("browser.newtabpage.remote.mode", "production"); } @@ -22,8 +32,7 @@ registerCleanupFunction(cleanup); * Tests flow of messages from newtab to chrome and chrome to newtab */ add_task(function* open_webchannel_basic() { - Preferences.set("browser.newtabpage.remote.mode", "test"); - Preferences.set("browser.newtabpage.remote", true); + setup(); let tabOptions = { gBrowser, @@ -72,8 +81,7 @@ add_task(function* open_webchannel_basic() { * Tests message broadcast reaches all open newtab pages */ add_task(function* webchannel_broadcast() { - Preferences.set("browser.newtabpage.remote.mode", "test"); - Preferences.set("browser.newtabpage.remote", true); + setup(); let countingMessagePromise = new Promise(resolve => { let count = 0; @@ -133,8 +141,7 @@ add_task(function* webchannel_broadcast() { * Tests switching modes */ add_task(function* webchannel_switch() { - Preferences.set("browser.newtabpage.remote.mode", "test"); - Preferences.set("browser.newtabpage.remote", true); + setup(); function newMessagePromise() { return new Promise(resolve => { @@ -195,8 +202,7 @@ add_task(function* webchannel_switch() { }); add_task(function* open_webchannel_reload() { - Preferences.set("browser.newtabpage.remote.mode", "test"); - Preferences.set("browser.newtabpage.remote", true); + setup(); let tabOptions = { gBrowser, diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index e0c347b37275..be5a68e801b4 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -27,12 +27,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "DirectoryLinksProvider", XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", - "resource:///modules/NewTabPrefsProvider.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "NewTabWebChannel", - "resource:///modules/NewTabWebChannel.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "NewTabMessages", "resource:///modules/NewTabMessages.jsm"); @@ -759,8 +753,6 @@ BrowserGlue.prototype = { NewTabUtils.links.addProvider(DirectoryLinksProvider); AboutNewTab.init(); - NewTabPrefsProvider.prefs.init(); - NewTabWebChannel.init(); NewTabMessages.init(); SessionStore.init(); @@ -1066,8 +1058,6 @@ BrowserGlue.prototype = { } SelfSupportBackend.uninit(); - NewTabPrefsProvider.prefs.uninit(); - NewTabWebChannel.uninit(); NewTabMessages.uninit(); AboutNewTab.uninit(); From 10471cf5de834c29ea53057ebb4943d8d8007343 Mon Sep 17 00:00:00 2001 From: Olivier Yiptong Date: Tue, 8 Mar 2016 14:21:07 -0500 Subject: [PATCH 04/53] Bug 1239119 - NewTab webchannel messages for thumbnails r=ursula MozReview-Commit-ID: JyW9kmxY9OE --HG-- extra : rebase_source : 934bcce36e87aca6dbe6d4cbf211f955c15fb21c --- browser/components/newtab/NewTabMessages.jsm | 35 +++++++++++++++--- .../newtab/tests/browser/browser.ini | 3 +- .../tests/browser/browser_newtabmessages.js | 29 +++++++++++++++ .../tests/browser/newtabmessages_preview.html | 37 +++++++++++++++++++ 4 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 browser/components/newtab/tests/browser/newtabmessages_preview.html diff --git a/browser/components/newtab/NewTabMessages.jsm b/browser/components/newtab/NewTabMessages.jsm index eb4cfa40e0be..d52e57eca98c 100644 --- a/browser/components/newtab/NewTabMessages.jsm +++ b/browser/components/newtab/NewTabMessages.jsm @@ -13,6 +13,8 @@ const {utils: Cu} = Components; Cu.import("resource://gre/modules/Preferences.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PreviewProvider", + "resource:///modules/PreviewProvider.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NewTabPrefsProvider", "resource:///modules/NewTabPrefsProvider.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NewTabWebChannel", @@ -28,8 +30,13 @@ const ACTIONS = { prefs: { inPrefs: "REQUEST_PREFS", outPrefs: "RECEIVE_PREFS", - action_types: new Set(["REQUEST_PREFS", "RECEIVE_PREFS"]), - } + action_types: new Set(["REQUEST_PREFS"]), + }, + preview: { + inThumb: "REQUEST_THUMB", + outThumb: "RECEIVE_THUMB", + action_types: new Set(["REQUEST_THUMB"]), + }, }; let NewTabMessages = { @@ -42,12 +49,20 @@ let NewTabMessages = { * Return to the originator all newtabpage prefs. A point-to-point request. */ handlePrefRequest(actionName, {target}) { - if (ACTIONS.prefs.action_types.has(actionName)) { + if (ACTIONS.prefs.inPrefs === actionName) { let results = NewTabPrefsProvider.prefs.newtabPagePrefs; NewTabWebChannel.send(ACTIONS.prefs.outPrefs, results, target); } }, + handlePreviewRequest(actionName, {data, target}) { + if (ACTIONS.preview.inThumb === actionName) { + PreviewProvider.getThumbnail(data).then(imgData => { + NewTabWebChannel.send(ACTIONS.preview.outThumb, {url: data, imgData}, target); + }); + } + }, + /* * Broadcast preference changes to all open newtab pages */ @@ -68,17 +83,24 @@ let NewTabMessages = { }, init() { + this.handlePrefRequest = this.handlePrefRequest.bind(this); + this.handlePreviewRequest = this.handlePreviewRequest.bind(this); + this.handlePrefChange = this.handlePrefChange.bind(this); + this._handleEnabledChange = this._handleEnabledChange.bind(this); + NewTabPrefsProvider.prefs.init(); NewTabWebChannel.init(); this._prefs.enabled = Preferences.get(PREF_ENABLED, false); if (this._prefs.enabled) { - NewTabWebChannel.on(ACTIONS.prefs.inPrefs, this.handlePrefRequest.bind(this)); - NewTabPrefsProvider.prefs.on(PREF_ENABLED, this._handleEnabledChange.bind(this)); + NewTabWebChannel.on(ACTIONS.prefs.inPrefs, this.handlePrefRequest); + NewTabWebChannel.on(ACTIONS.preview.inThumb, this.handlePreviewRequest); + + NewTabPrefsProvider.prefs.on(PREF_ENABLED, this._handleEnabledChange); for (let pref of NewTabPrefsProvider.newtabPagePrefSet) { - NewTabPrefsProvider.prefs.on(pref, this.handlePrefChange.bind(this)); + NewTabPrefsProvider.prefs.on(pref, this.handlePrefChange); } } }, @@ -90,6 +112,7 @@ let NewTabMessages = { NewTabPrefsProvider.prefs.off(PREF_ENABLED, this._handleEnabledChange); NewTabWebChannel.off(ACTIONS.prefs.inPrefs, this.handlePrefRequest); + NewTabWebChannel.off(ACTIONS.prefs.inThumb, this.handlePreviewRequest); for (let pref of NewTabPrefsProvider.newtabPagePrefSet) { NewTabPrefsProvider.prefs.off(pref, this.handlePrefChange); } diff --git a/browser/components/newtab/tests/browser/browser.ini b/browser/components/newtab/tests/browser/browser.ini index a04ec639b03f..17a442399c4d 100644 --- a/browser/components/newtab/tests/browser/browser.ini +++ b/browser/components/newtab/tests/browser/browser.ini @@ -1,9 +1,10 @@ [DEFAULT] support-files = + blue_page.html dummy_page.html newtabwebchannel_basic.html newtabmessages_prefs.html - blue_page.html + newtabmessages_preview.html [browser_PreviewProvider.js] [browser_remotenewtab_pageloads.js] diff --git a/browser/components/newtab/tests/browser/browser_newtabmessages.js b/browser/components/newtab/tests/browser/browser_newtabmessages.js index 6b39e15df921..88dac468e2bf 100644 --- a/browser/components/newtab/tests/browser/browser_newtabmessages.js +++ b/browser/components/newtab/tests/browser/browser_newtabmessages.js @@ -54,3 +54,32 @@ add_task(function* prefMessages_request() { }); cleanup(); }); + +/* + * Sanity tests for preview messages + */ +add_task(function* previewMessages_request() { + setup(); + var oldEnabledPref = Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"); + Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", false); + + let testURL = "https://example.com/browser/browser/components/newtab/tests/browser/newtabmessages_preview.html"; + + let tabOptions = { + gBrowser, + url: testURL + }; + + let previewResponseAck = new Promise(resolve => { + NewTabWebChannel.once("responseAck", () => { + ok(true, "a request response has been received"); + resolve(); + }); + }); + + yield BrowserTestUtils.withNewTab(tabOptions, function*() { + yield previewResponseAck; + }); + cleanup(); + Services.prefs.setBoolPref("browser.pagethumbnails.capturing_disabled", oldEnabledPref); +}); diff --git a/browser/components/newtab/tests/browser/newtabmessages_preview.html b/browser/components/newtab/tests/browser/newtabmessages_preview.html new file mode 100644 index 000000000000..99be6f0cf8fe --- /dev/null +++ b/browser/components/newtab/tests/browser/newtabmessages_preview.html @@ -0,0 +1,37 @@ + + + + Newtab WebChannel test + + + + + From 6ce7b129597e4bf4423480dff4175694c2927fcf Mon Sep 17 00:00:00 2001 From: Steven Low Date: Wed, 2 Mar 2016 19:56:00 -0500 Subject: [PATCH 05/53] Bug 1247548 - Changed nsCookieService::EnsureReadComplete and nsCookieService::PurgeCookeis to allocate nsTArray instead of AutoTArray. r=jdm --- netwerk/cookie/nsCookieService.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp index d0719f90f2b0..a04afc11e052 100644 --- a/netwerk/cookie/nsCookieService.cpp +++ b/netwerk/cookie/nsCookieService.cpp @@ -2697,7 +2697,7 @@ nsCookieService::EnsureReadComplete() nsCString baseDomain, name, value, host, path; bool hasResult; - AutoTArray array; + nsTArray array(kMaxNumberOfCookies); while (1) { rv = stmt->ExecuteStep(&hasResult); if (NS_FAILED(rv)) { @@ -4087,8 +4087,8 @@ nsCookieService::PurgeCookies(int64_t aCurrentTimeInUsec) ("PurgeCookies(): beginning purge with %ld cookies and %lld oldest age", mDBState->cookieCount, aCurrentTimeInUsec - mDBState->cookieOldestTime)); - typedef AutoTArray PurgeList; - PurgeList purgeList; + typedef nsTArray PurgeList; + PurgeList purgeList(kMaxNumberOfCookies); nsCOMPtr removedList = do_CreateInstance(NS_ARRAY_CONTRACTID); From 683c7a418e4e616a233a5abfdc0160e1b4fb806b Mon Sep 17 00:00:00 2001 From: Sebastian Hengst Date: Fri, 1 Apr 2016 20:17:23 +0200 Subject: [PATCH 06/53] Bug 1239119 - NewTab webchannel messages for thumbnails. Follow-up: Move semicolon from end of if block to end of function assigned to document.onreadystatechange (discovered by eslint). r=eslint-fix --- .../newtab/tests/browser/newtabmessages_preview.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/newtab/tests/browser/newtabmessages_preview.html b/browser/components/newtab/tests/browser/newtabmessages_preview.html index 99be6f0cf8fe..dd91976b1beb 100644 --- a/browser/components/newtab/tests/browser/newtabmessages_preview.html +++ b/browser/components/newtab/tests/browser/newtabmessages_preview.html @@ -30,8 +30,8 @@ } }); window.dispatchEvent(msg); - }; - } + } + }; From 2471ddff5c6de2d8d582d88e27bd59dfdff7bfe7 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 30 Mar 2016 15:25:18 -0700 Subject: [PATCH 07/53] Bug 1260908 - Allow keyed histograms to accumulate early in startup (r=gfritzsche) --- toolkit/components/telemetry/Telemetry.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index f7074deafcd5..93a70156bdb4 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -703,6 +703,7 @@ class TelemetryImpl final public: void InitMemoryReporter(); + static bool IsInitialized(); static bool CanRecordBase(); static bool CanRecordExtended(); static already_AddRefed CreateTelemetryInstance(); @@ -3309,6 +3310,12 @@ TelemetryImpl::SetCanRecordBase(bool canRecord) { return NS_OK; } +/* static */ bool +TelemetryImpl::IsInitialized() +{ + return sTelemetry; +} + /** * Indicates if Telemetry can record base data (FHR data). This is true if the * FHR data reporting service or self-support are enabled. @@ -3902,7 +3909,7 @@ Accumulate(ID aHistogram, uint32_t aSample) void Accumulate(ID aID, const nsCString& aKey, uint32_t aSample) { - if (!TelemetryImpl::CanRecordBase()) { + if (!TelemetryImpl::IsInitialized() || !TelemetryImpl::CanRecordBase()) { return; } const TelemetryHistogram& th = gHistograms[aID]; From b3613ec89e2dba3ad10e6e5e53da81e432c1615f Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 30 Mar 2016 15:25:54 -0700 Subject: [PATCH 08/53] Bug 1260908 - Record IPC message sizes (r=dvander) --- dom/base/nsFrameMessageManager.cpp | 14 ++++++++++++++ ipc/glue/MessageChannel.cpp | 10 ++++++++++ toolkit/components/telemetry/Histograms.json | 20 ++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 659051db2772..7dc84105da91 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -69,6 +69,8 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::ipc; +static const int kMinTelemetryMessageSize = 8192; + nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, nsFrameMessageManager* aParentManager, /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags) @@ -730,6 +732,12 @@ nsFrameMessageManager::SendMessage(const nsAString& aMessageName, return NS_ERROR_DOM_DATA_CLONE_ERR; } + if (data.DataLength() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE, + NS_ConvertUTF16toUTF8(aMessageName), + data.DataLength()); + } + JS::Rooted objects(aCx); if (aArgc >= 3 && aObjects.isObject()) { objects = &aObjects.toObject(); @@ -810,6 +818,12 @@ nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName, return NS_ERROR_DOM_DATA_CLONE_ERR; } + if (data.DataLength() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE, + NS_ConvertUTF16toUTF8(aMessageName), + data.DataLength()); + } + JS::Rooted objects(aCx); if (aArgc >= 3 && aObjects.isObject()) { objects = &aObjects.toObject(); diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 4a7f40b26228..ff088587f1b1 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -118,6 +118,8 @@ static MessageChannel* gParentProcessBlocker; namespace mozilla { namespace ipc { +static const int kMinTelemetryMessageSize = 8192; + const int32_t MessageChannel::kNoTimeout = INT32_MIN; // static @@ -749,6 +751,10 @@ MessageChannel::Echo(Message* aMsg) bool MessageChannel::Send(Message* aMsg) { + if (aMsg->size() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size()); + } + CxxStackFrame frame(*this, OUT_MESSAGE, aMsg); nsAutoPtr msg(aMsg); @@ -1045,6 +1051,10 @@ MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction) bool MessageChannel::Send(Message* aMsg, Message* aReply) { + if (aMsg->size() >= kMinTelemetryMessageSize) { + Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size()); + } + nsAutoPtr msg(aMsg); // Sanity checks. diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 8c45af145219..dd67b9328768 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -10495,5 +10495,25 @@ "kind": "count", "bug_numbers": [1237198], "description": "Count tiny plugin content" + }, + "IPC_MESSAGE_SIZE": { + "alert_emails": ["wmccloskey@mozilla.com"], + "bug_numbers": [1260908], + "expires_in_version": "55", + "kind": "exponential", + "high": 8000000, + "n_buckets": 50, + "keyed": true, + "description": "Measures the size of IPC messages by message name" + }, + "MESSAGE_MANAGER_MESSAGE_SIZE": { + "alert_emails": ["wmccloskey@mozilla.com"], + "bug_numbers": [1260908], + "expires_in_version": "55", + "kind": "exponential", + "high": 8000000, + "n_buckets": 50, + "keyed": true, + "description": "Measures the size of message manager messages by message name" } } From e0648b2c3a997160af35a0ca6e20bdf61583d7fb Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 1 Apr 2016 11:28:47 -0700 Subject: [PATCH 09/53] Bug 934889: Use JS_InitStandardClasses everywhere now that it works. r=bz --- dom/bindings/BindingUtils.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 80490a80fa1e..197debcfd8e4 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3003,9 +3003,6 @@ struct CreateGlobalOptions { static MOZ_CONSTEXPR_VAR ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = ProtoAndIfaceCache::NonWindowLike; - // Intl API is broken and makes JS_InitStandardClasses fail intermittently, - // see bug 934889. - static MOZ_CONSTEXPR_VAR bool ForceInitStandardClassesToFalse = true; static void TraceGlobal(JSTracer* aTrc, JSObject* aObj) { mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj); @@ -3023,7 +3020,6 @@ struct CreateGlobalOptions { static MOZ_CONSTEXPR_VAR ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind = ProtoAndIfaceCache::WindowLike; - static MOZ_CONSTEXPR_VAR bool ForceInitStandardClassesToFalse = false; static void TraceGlobal(JSTracer* aTrc, JSObject* aObj); static bool PostCreateGlobal(JSContext* aCx, JS::Handle aGlobal); }; @@ -3074,7 +3070,6 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, } if (aInitStandardClasses && - !CreateGlobalOptions::ForceInitStandardClassesToFalse && !JS_InitStandardClasses(aCx, aGlobal)) { NS_WARNING("Failed to init standard classes"); return nullptr; From 3b346fca73668dbd9d1f40e431958a3f45a312fb Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 1 Apr 2016 11:28:53 -0700 Subject: [PATCH 10/53] Bug 1260806: Remove some more dead MessageLoop code. r=jld --- ipc/chromium/src/base/message_loop.cc | 25 +++------------- ipc/chromium/src/base/message_loop.h | 41 +-------------------------- ipc/chromium/src/base/task.h | 18 ------------ 3 files changed, 5 insertions(+), 79 deletions(-) diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc index fab5a5a6d0d5..3414938f5f12 100644 --- a/ipc/chromium/src/base/message_loop.cc +++ b/ipc/chromium/src/base/message_loop.cc @@ -203,12 +203,6 @@ void MessageLoop::Run() { RunHandler(); } -void MessageLoop::RunAllPending() { - AutoRunState save_state(this); - state_->quit_received = true; // Means run until we would otherwise block. - RunHandler(); -} - // Runs the loop in two different SEH modes: // enable_SEH_restoration_ = false : any unhandled exception goes to the last // one that calls SetUnhandledExceptionFilter(). @@ -266,22 +260,12 @@ void MessageLoop::Quit() { void MessageLoop::PostTask( const tracked_objects::Location& from_here, Task* task) { - PostTask_Helper(from_here, task, 0, true); + PostTask_Helper(from_here, task, 0); } void MessageLoop::PostDelayedTask( const tracked_objects::Location& from_here, Task* task, int delay_ms) { - PostTask_Helper(from_here, task, delay_ms, true); -} - -void MessageLoop::PostNonNestableTask( - const tracked_objects::Location& from_here, Task* task) { - PostTask_Helper(from_here, task, 0, false); -} - -void MessageLoop::PostNonNestableDelayedTask( - const tracked_objects::Location& from_here, Task* task, int delay_ms) { - PostTask_Helper(from_here, task, delay_ms, false); + PostTask_Helper(from_here, task, delay_ms); } void MessageLoop::PostIdleTask( @@ -300,8 +284,7 @@ void MessageLoop::PostIdleTask( // Possibly called on a background thread! void MessageLoop::PostTask_Helper( - const tracked_objects::Location& from_here, Task* task, int delay_ms, - bool nestable) { + const tracked_objects::Location& from_here, Task* task, int delay_ms) { #ifdef MOZ_TASK_TRACER task = mozilla::tasktracer::CreateTracedTask(task); @@ -310,7 +293,7 @@ void MessageLoop::PostTask_Helper( task->SetBirthPlace(from_here); - PendingTask pending_task(task, nestable); + PendingTask pending_task(task, true); if (delay_ms > 0) { pending_task.delayed_run_time = diff --git a/ipc/chromium/src/base/message_loop.h b/ipc/chromium/src/base/message_loop.h index eca36c3f6a3c..c1be6f5f2158 100644 --- a/ipc/chromium/src/base/message_loop.h +++ b/ipc/chromium/src/base/message_loop.h @@ -117,52 +117,13 @@ public: void PostDelayedTask( const tracked_objects::Location& from_here, Task* task, int delay_ms); - void PostNonNestableTask( - const tracked_objects::Location& from_here, Task* task); - - void PostNonNestableDelayedTask( - const tracked_objects::Location& from_here, Task* task, int delay_ms); - // PostIdleTask is not thread safe and should be called on this thread void PostIdleTask( const tracked_objects::Location& from_here, Task* task); - // A variant on PostTask that deletes the given object. This is useful - // if the object needs to live until the next run of the MessageLoop (for - // example, deleting a RenderProcessHost from within an IPC callback is not - // good). - // - // NOTE: This method may be called on any thread. The object will be deleted - // on the thread that executes MessageLoop::Run(). If this is not the same - // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit - // from RefCountedThreadSafe! - template - void DeleteSoon(const tracked_objects::Location& from_here, T* object) { - PostNonNestableTask(from_here, new DeleteTask(object)); - } - - // A variant on PostTask that releases the given reference counted object - // (by calling its Release method). This is useful if the object needs to - // live until the next run of the MessageLoop, or if the object needs to be - // released on a particular thread. - // - // NOTE: This method may be called on any thread. The object will be - // released (and thus possibly deleted) on the thread that executes - // MessageLoop::Run(). If this is not the same as the thread that calls - // PostDelayedTask(FROM_HERE, ), then T MUST inherit from - // RefCountedThreadSafe! - template - void ReleaseSoon(const tracked_objects::Location& from_here, T* object) { - PostNonNestableTask(from_here, new ReleaseTask(object)); - } - // Run the message loop. void Run(); - // Process all pending tasks, windows messages, etc., but don't wait/sleep. - // Return as soon as all items that can be run are taken care of. - void RunAllPending(); - // Signals the Run method to return after it is done processing all pending // messages. This method may only be called on the same thread that called // Run, and Run must still be on the call stack. @@ -395,7 +356,7 @@ public: // Post a task to our incomming queue. void PostTask_Helper(const tracked_objects::Location& from_here, Task* task, - int delay_ms, bool nestable); + int delay_ms); // base::MessagePump::Delegate methods: virtual bool DoWork() override; diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h index d2c475d0aede..ca90436c8868 100644 --- a/ipc/chromium/src/base/task.h +++ b/ipc/chromium/src/base/task.h @@ -214,24 +214,6 @@ class DeleteTask : public CancelableTask { "external factors.") obj_; }; -// Task to Release() an object -template -class ReleaseTask : public CancelableTask { - public: - explicit ReleaseTask(T* obj) : obj_(obj) { - } - virtual void Run() { - if (obj_) - obj_->Release(); - } - virtual void Cancel() { - obj_ = NULL; - } - private: - T* MOZ_UNSAFE_REF("The validity of this pointer must be enforced by " - "external factors.") obj_; -}; - // RunnableMethodTraits -------------------------------------------------------- // // This traits-class is used by RunnableMethod to manage the lifetime of the From 4e1852e788a90e539c1842b0ef08f8ba78582af2 Mon Sep 17 00:00:00 2001 From: Eugen Sawin Date: Thu, 31 Mar 2016 23:28:27 +0200 Subject: [PATCH 11/53] Bug 1257304 - [1.2] Cache screen size and update only on orientation change. r=jchen --- .../java/org/mozilla/gecko/GeckoAppShell.java | 28 ++++++++++++------- .../mozilla/gecko/GeckoScreenOrientation.java | 4 ++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java index 56910f7724de..52480c0f2af4 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java @@ -302,6 +302,7 @@ public class GeckoAppShell public static native void onFullScreenPluginHidden(View view); private static LayerView sLayerView; + private static Rect sScreenSize; public static void setLayerView(LayerView lv) { if (sLayerView == lv) { @@ -1267,7 +1268,7 @@ public class GeckoAppShell if (action.equalsIgnoreCase(Intent.ACTION_SEND)) { Intent shareIntent = getShareIntent(context, targetURI, mimeType, title); return Intent.createChooser(shareIntent, - context.getResources().getString(R.string.share_title)); + context.getResources().getString(R.string.share_title)); } Uri uri = normalizeUriScheme(targetURI.indexOf(':') >= 0 ? Uri.parse(targetURI) : new Uri.Builder().scheme(targetURI).build()); @@ -1733,7 +1734,7 @@ public class GeckoAppShell return true; } }; - + EnumerateGeckoProcesses(visitor); } @@ -1755,7 +1756,7 @@ public class GeckoAppShell // figure out the column offsets. We only care about the pid and user fields StringTokenizer st = new StringTokenizer(headerOutput); - + int tokenSoFar = 0; while (st.hasMoreTokens()) { String next = st.nextToken(); @@ -1888,7 +1889,7 @@ public class GeckoAppShell final MimeTypeMap mtm = MimeTypeMap.getSingleton(); return mtm.getMimeTypeFromExtension(ext); } - + private static Drawable getDrawableForExtension(PackageManager pm, String aExt) { Intent intent = new Intent(Intent.ACTION_VIEW); final String mimeType = getMimeTypeFromExtension(aExt); @@ -2612,7 +2613,7 @@ public class GeckoAppShell if (Proxy.NO_PROXY.equals(proxy)) { return "DIRECT"; } - + switch (proxy.type()) { case HTTP: return "PROXY " + proxy.address().toString(); @@ -2855,11 +2856,18 @@ public class GeckoAppShell return 0; } + public static synchronized Rect getScreenSize(final boolean update) { + if (update || sScreenSize == null) { + final WindowManager wm = (WindowManager) + getApplicationContext().getSystemService(Context.WINDOW_SERVICE); + final Display disp = wm.getDefaultDisplay(); + sScreenSize = new Rect(0, 0, disp.getWidth(), disp.getHeight()); + } + return sScreenSize; + } + @WrapForJNI - static Rect getScreenSize() { - final WindowManager wm = (WindowManager) - getApplicationContext().getSystemService(Context.WINDOW_SERVICE); - final Display disp = wm.getDefaultDisplay(); - return new Rect(0, 0, disp.getWidth(), disp.getHeight()); + public static Rect getScreenSize() { + return getScreenSize(/* update */ false); } } diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java b/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java index daab582c0047..4b9383cdffb6 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java @@ -40,7 +40,7 @@ public class GeckoScreenOrientation { this.value = (short)value; } - private final static ScreenOrientation[] sValues = ScreenOrientation.values(); + private final static ScreenOrientation[] sValues = ScreenOrientation.values(); public static ScreenOrientation get(int value) { for (ScreenOrientation orient: sValues) { @@ -156,6 +156,8 @@ public class GeckoScreenOrientation { GeckoEvent.createScreenOrientationEvent(aScreenOrientation.value, getAngle())); } + // Update screen size. + GeckoAppShell.getScreenSize(/* update */ true); return true; } From 5b07475fcb5e822e9d9a055455dc350e07d3dc14 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 1 Apr 2016 19:58:22 +0100 Subject: [PATCH 12/53] Bug 1246327 - Remove dom.always_allow_move_resize_window preference. r=jst --- dom/base/nsGlobalWindow.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index b3722a0c7d26..db49fcff0c53 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -6450,10 +6450,7 @@ nsGlobalWindow::CanMoveResizeWindows(bool aCallerIsChrome) } } - // The preference is useful for the webapp runtime. Webapps should be able - // to resize or move their window. - if (mDocShell && !Preferences::GetBool("dom.always_allow_move_resize_window", - false)) { + if (mDocShell) { bool allow; nsresult rv = mDocShell->GetAllowWindowControl(&allow); if (NS_SUCCEEDED(rv) && !allow) From ec28626b238e2d46c3333e7418e3e917bdb4a71d Mon Sep 17 00:00:00 2001 From: Randall Barker Date: Tue, 29 Mar 2016 16:46:29 -0700 Subject: [PATCH 13/53] Bug 1260588 - C++ APZ should only allow handoff to ancestor APZC r=botond --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 22 +++++++++---------- gfx/layers/apz/src/InputBlockState.cpp | 12 ++++++++-- gfx/layers/apz/src/InputBlockState.h | 3 ++- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 03c95db4722d..ba3a4f4da32a 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2360,11 +2360,11 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, // a later event in the block could potentially scroll an APZC earlier // in the handoff chain, than an earlier event in the block (because // the earlier APZC was scrolled to its extent in the original direction). - // If immediate handoff is disallowed, we want to disallow this (to - // preserve the property that a single input block only scrolls one APZC), - // so we skip the earlier APZC. - bool scrollThisApzc = gfxPrefs::APZAllowImmediateHandoff() || - (CurrentInputBlock() && (!CurrentInputBlock()->GetScrolledApzc() || this == CurrentInputBlock()->GetScrolledApzc())); + // We want to disallow this. + bool scrollThisApzc = false; + if (InputBlockState* block = CurrentInputBlock()) { + scrollThisApzc = !block->GetScrolledApzc() || block->IsDownchainOfScrolledApzc(this); + } if (scrollThisApzc) { ReentrantMonitorAutoEnter lock(mMonitor); @@ -2383,21 +2383,19 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, if (!IsZero(adjustedDisplacement)) { ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom()); - if (!gfxPrefs::APZAllowImmediateHandoff()) { - if (InputBlockState* block = CurrentInputBlock()) { - block->SetScrolledApzc(this); - } + if (InputBlockState* block = CurrentInputBlock()) { + block->SetScrolledApzc(this); } ScheduleCompositeAndMaybeRepaint(); UpdateSharedCompositorFrameMetrics(); } + + // Adjust the start point to reflect the consumed portion of the scroll. + aStartPoint = aEndPoint + overscroll; } else { overscroll = displacement; } - // Adjust the start point to reflect the consumed portion of the scroll. - aStartPoint = aEndPoint + overscroll; - // If we consumed the entire displacement as a normal scroll, great. if (IsZero(overscroll)) { return true; diff --git a/gfx/layers/apz/src/InputBlockState.cpp b/gfx/layers/apz/src/InputBlockState.cpp index b1552c71135d..0b23ebb337d6 100644 --- a/gfx/layers/apz/src/InputBlockState.cpp +++ b/gfx/layers/apz/src/InputBlockState.cpp @@ -88,7 +88,7 @@ InputBlockState::IsTargetConfirmed() const } bool -InputBlockState::IsAncestorOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) +InputBlockState::IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const { if (aA == aB) { return true; @@ -112,7 +112,7 @@ void InputBlockState::SetScrolledApzc(AsyncPanZoomController* aApzc) { // An input block should only have one scrolled APZC. - MOZ_ASSERT(!mScrolledApzc || mScrolledApzc == aApzc); + MOZ_ASSERT(!mScrolledApzc || (gfxPrefs::APZAllowImmediateHandoff() ? IsDownchainOf(mScrolledApzc, aApzc) : mScrolledApzc == aApzc)); mScrolledApzc = aApzc; } @@ -123,6 +123,14 @@ InputBlockState::GetScrolledApzc() const return mScrolledApzc; } +bool +InputBlockState::IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const +{ + MOZ_ASSERT(aApzc && mScrolledApzc); + + return IsDownchainOf(mScrolledApzc, aApzc); +} + CancelableBlockState::CancelableBlockState(const RefPtr& aTargetApzc, bool aTargetConfirmed) : InputBlockState(aTargetApzc, aTargetConfirmed) diff --git a/gfx/layers/apz/src/InputBlockState.h b/gfx/layers/apz/src/InputBlockState.h index d962c1426f37..4648dfecb028 100644 --- a/gfx/layers/apz/src/InputBlockState.h +++ b/gfx/layers/apz/src/InputBlockState.h @@ -52,6 +52,7 @@ public: void SetScrolledApzc(AsyncPanZoomController* aApzc); AsyncPanZoomController* GetScrolledApzc() const; + bool IsDownchainOfScrolledApzc(AsyncPanZoomController* aApzc) const; protected: virtual void UpdateTargetApzc(const RefPtr& aTargetApzc); @@ -59,7 +60,7 @@ protected: private: // Checks whether |aA| is an ancestor of |aB| (or the same as |aB|) in // |mOverscrollHandoffChain|. - bool IsAncestorOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB); + bool IsDownchainOf(AsyncPanZoomController* aA, AsyncPanZoomController* aB) const; private: RefPtr mTargetApzc; From 8125870e5caca8c725d10fa0fdf9a603a5ef83e2 Mon Sep 17 00:00:00 2001 From: Randall Barker Date: Thu, 24 Mar 2016 15:00:27 -0700 Subject: [PATCH 14/53] Bug 1257269 - Panning up in a scrollable element should not hide the toolbar r=kats,jchen --- gfx/layers/apz/public/GeckoContentController.h | 3 ++- gfx/layers/apz/src/AsyncPanZoomController.cpp | 13 ++++++++++++- .../gecko/gfx/DynamicToolbarAnimator.java | 18 ++++++++++++++++-- .../mozilla/gecko/gfx/GeckoLayerClient.java | 5 +++++ .../gecko/gfx/NativePanZoomController.java | 5 +++++ .../org/mozilla/gecko/gfx/PanZoomTarget.java | 1 + widget/android/AndroidContentController.cpp | 10 +++++++++- widget/android/AndroidContentController.h | 3 ++- widget/android/GeneratedJNIWrappers.cpp | 8 ++++++++ widget/android/GeneratedJNIWrappers.h | 16 ++++++++++++++++ widget/android/nsWindow.cpp | 16 ++++++++++++++++ widget/android/nsWindow.h | 1 + 12 files changed, 93 insertions(+), 6 deletions(-) diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index ecaafe20c559..650b9ce059cc 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -152,7 +152,8 @@ public: virtual void NotifyFlushComplete() = 0; virtual void UpdateOverscrollVelocity(const float aX, const float aY) {} - virtual void UpdateOverscrollOffset(const float aX,const float aY) {} + virtual void UpdateOverscrollOffset(const float aX, const float aY) {} + virtual void SetScrollingRootContent(const bool isRootContent) {} GeckoContentController() {} virtual void ChildAdopted() {} diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index ba3a4f4da32a..ada229fe70aa 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1319,6 +1319,11 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) { APZC_LOG("%p got a touch-end in state %d\n", this, mState); + RefPtr controller = GetGeckoContentController(); + if (controller) { + controller->SetScrollingRootContent(false); + } + OnTouchEndOrCancel(); // In case no touch behavior triggered previously we can avoid sending @@ -2383,7 +2388,13 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, if (!IsZero(adjustedDisplacement)) { ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom()); - if (InputBlockState* block = CurrentInputBlock()) { + if (CancelableBlockState* block = CurrentInputBlock()) { + if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) { + RefPtr controller = GetGeckoContentController(); + if (controller) { + controller->SetScrollingRootContent(IsRootContent()); + } + } block->SetScrolledApzc(this); } ScheduleCompositeAndMaybeRepaint(); diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java index d0b287f4f1ed..eed3e3d9b711 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java @@ -5,6 +5,7 @@ package org.mozilla.gecko.gfx; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.PrefsHelper; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -94,6 +95,9 @@ public class DynamicToolbarAnimator { private PointF mTouchStart; private float mLastTouch; + /* Set to true when root content is being scrolled */ + private boolean mScrollingRootContent; + public DynamicToolbarAnimator(GeckoLayerClient aTarget) { mTarget = aTarget; mListeners = new ArrayList(); @@ -108,6 +112,11 @@ public class DynamicToolbarAnimator { } }; PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver); + + // JPZ doesn't notify when scrolling root content. This maintains existing behaviour. + if (!AppConstants.MOZ_ANDROID_APZ) { + mScrollingRootContent = true; + } } public void destroy() { @@ -185,6 +194,10 @@ public class DynamicToolbarAnimator { animateToolbar(false, immediately); } + public void setScrollingRootContent(boolean isRootContent) { + mScrollingRootContent = isRootContent; + } + private void animateToolbar(final boolean showToolbar, boolean immediately) { ThreadUtils.assertOnUiThread(); @@ -339,11 +352,12 @@ public class DynamicToolbarAnimator { // translation to take effect right away. Or if the user has moved // their finger past the required threshold (and is not trying to // scroll past the bottom of the page) then also we want the touch - // to cause translation. + // to cause translation. If the toolbar is fully visible, we only + // want the toolbar to hide if the user is scrolling the root content. boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation); boolean reachedThreshold = -aTouchTravelDistance >= exposeThreshold; boolean atBottomOfPage = aMetrics.viewportRectBottom() >= aMetrics.pageRectBottom; - if (inBetween || (reachedThreshold && !atBottomOfPage)) { + if (inBetween || (mScrollingRootContent && reachedThreshold && !atBottomOfPage)) { return translation; } } else { // finger moving downwards diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 4b02f34b68e8..0f2493f8b79e 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -1086,6 +1086,11 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return layerPoint; } + @Override + public void setScrollingRootContent(boolean isRootContent) { + mToolbarAnimator.setScrollingRootContent(isRootContent); + } + public void addDrawListener(DrawListener listener) { mDrawListeners.add(listener); } diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java index 7807419eb46c..23fe980d26fb 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -324,6 +324,11 @@ class NativePanZoomController extends JNIObject implements PanZoomController { } } + @WrapForJNI + private void setScrollingRootContent(final boolean isRootContent) { + mTarget.setScrollingRootContent(isRootContent); + } + /** * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned * to avoid unwanted scroll interactions. diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java index 4afe8653a4b8..b276e781e536 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java @@ -28,4 +28,5 @@ public interface PanZoomTarget { public void removeRenderTask(RenderTask task); public Object getLock(); public PointF convertViewPointToLayerPoint(PointF viewPoint); + public void setScrollingRootContent(boolean isRootContent); } diff --git a/widget/android/AndroidContentController.cpp b/widget/android/AndroidContentController.cpp index fb3f305bf5f9..a9dd9aa7babf 100644 --- a/widget/android/AndroidContentController.cpp +++ b/widget/android/AndroidContentController.cpp @@ -100,13 +100,21 @@ AndroidContentController::UpdateOverscrollVelocity(const float aX, const float a } void -AndroidContentController::UpdateOverscrollOffset(const float aX,const float aY) +AndroidContentController::UpdateOverscrollOffset(const float aX, const float aY) { if (mAndroidWindow) { mAndroidWindow->UpdateOverscrollOffset(aX, aY); } } +void +AndroidContentController::SetScrollingRootContent(const bool isRootContent) +{ + if (mAndroidWindow) { + mAndroidWindow->SetScrollingRootContent(isRootContent); + } +} + void AndroidContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, diff --git a/widget/android/AndroidContentController.h b/widget/android/AndroidContentController.h index bb38d56d28d8..273f977794a2 100644 --- a/widget/android/AndroidContentController.h +++ b/widget/android/AndroidContentController.h @@ -39,7 +39,8 @@ public: const ScrollableLayerGuid& aGuid) override; void PostDelayedTask(Task* aTask, int aDelayMs) override; void UpdateOverscrollVelocity(const float aX, const float aY) override; - void UpdateOverscrollOffset(const float aX,const float aY) override; + void UpdateOverscrollOffset(const float aX, const float aY) override; + void SetScrollingRootContent(const bool isRootContent) override; void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) override; diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index eca5b59f842f..87ba7a8d16f2 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1575,6 +1575,14 @@ auto NativePanZoomController::RequestContentRepaintWrapper(float a0, float a1, f return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0, a1, a2, a3, a4); } +constexpr char NativePanZoomController::SetScrollingRootContent_t::name[]; +constexpr char NativePanZoomController::SetScrollingRootContent_t::signature[]; + +auto NativePanZoomController::SetScrollingRootContent(bool a0) const -> void +{ + return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0); +} + constexpr char NativePanZoomController::UpdateOverscrollOffset_t::name[]; constexpr char NativePanZoomController::UpdateOverscrollOffset_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index c57dcadb5881..41ed2d2212f7 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3781,6 +3781,22 @@ public: auto RequestContentRepaintWrapper(float, float, float, float, float) const -> void; + struct SetScrollingRootContent_t { + typedef NativePanZoomController Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + bool> Args; + static constexpr char name[] = "setScrollingRootContent"; + static constexpr char signature[] = + "(Z)V"; + static const bool isStatic = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + auto SetScrollingRootContent(bool) const -> void; + struct UpdateOverscrollOffset_t { typedef NativePanZoomController Owner; typedef void ReturnType; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 8ba270ad251e..0c198f3f5364 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -817,6 +817,11 @@ public: mNPZC->UpdateOverscrollOffset(x, y); } + void SetScrollingRootContent(const bool isRootContent) + { + mNPZC->SetScrollingRootContent(isRootContent); + } + void SetSelectionDragState(const bool aState) { mNPZC->OnSelectionDragState(aState); @@ -1876,6 +1881,17 @@ nsWindow::UpdateOverscrollOffset(const float aX, const float aY) } } +void +nsWindow::SetScrollingRootContent(const bool isRootContent) +{ + // On Android, the Controller thread and UI thread are the same. + MOZ_ASSERT(APZThreadUtils::IsControllerThread(), "nsWindow::SetScrollingRootContent must be called from the controller thread"); + + if (mNPZCSupport) { + mNPZCSupport->SetScrollingRootContent(isRootContent); + } +} + void nsWindow::SetSelectionDragState(bool aState) { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index b053381e0736..cc1e6fdbad00 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -79,6 +79,7 @@ public: void UpdateOverscrollVelocity(const float aX, const float aY); void UpdateOverscrollOffset(const float aX, const float aY); + void SetScrollingRootContent(const bool isRootContent); // // nsIWidget From a74ec8c79b2ea32c8aed04146eb50de30f0fc6c9 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Mon, 28 Mar 2016 19:14:52 -0400 Subject: [PATCH 15/53] Bug 1219296 - Split fields not needed for repaints out from FrameMetrics. r=kats MozReview-Commit-ID: DymHOSI6yYK --HG-- extra : rebase_source : 1996bcb7f005c02b94031fe1c73d5136814b0296 --- gfx/ipc/GfxMessageUtils.h | 24 +++- gfx/layers/FrameMetrics.cpp | 5 +- gfx/layers/FrameMetrics.h | 118 +++++++++++------- gfx/layers/LayerMetricsWrapper.h | 37 +++--- gfx/layers/Layers.cpp | 32 +++-- gfx/layers/Layers.h | 35 +++--- gfx/layers/LayersLogging.cpp | 15 ++- gfx/layers/LayersLogging.h | 4 + gfx/layers/apz/src/APZCTreeManager.cpp | 2 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 13 +- gfx/layers/apz/src/AsyncPanZoomController.h | 11 +- .../apz/test/gtest/APZCTreeManagerTester.h | 19 +-- gfx/layers/apz/test/gtest/TestBasic.cpp | 14 ++- .../composite/AsyncCompositionManager.cpp | 19 +-- .../composite/ContainerLayerComposite.cpp | 4 +- gfx/layers/ipc/LayerTransactionParent.cpp | 4 +- gfx/layers/ipc/LayersMessages.ipdlh | 4 +- gfx/layers/ipc/ShadowLayers.cpp | 2 +- gfx/tests/gtest/TestLayers.cpp | 52 ++++---- layout/base/FrameLayerBuilder.cpp | 36 +++--- layout/base/nsDisplayList.cpp | 32 ++--- layout/base/nsDisplayList.h | 9 +- layout/base/nsLayoutUtils.cpp | 31 ++--- layout/base/nsLayoutUtils.h | 21 ++-- layout/generic/nsFrame.cpp | 2 +- layout/generic/nsGfxScrollFrame.cpp | 12 +- layout/generic/nsGfxScrollFrame.h | 10 +- layout/generic/nsIScrollableFrame.h | 4 +- 28 files changed, 327 insertions(+), 244 deletions(-) diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index f09c9d583bf6..d400c3b55cff 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -703,8 +703,6 @@ struct ParamTraits WriteParam(aMsg, aParam.GetContentDescription()); WriteParam(aMsg, aParam.mLineScrollAmount); WriteParam(aMsg, aParam.mPageScrollAmount); - WriteParam(aMsg, aParam.mClipRect); - WriteParam(aMsg, aParam.mMaskLayerIndex); WriteParam(aMsg, aParam.mPaintRequestTime); WriteParam(aMsg, aParam.mIsRootContent); WriteParam(aMsg, aParam.mHasScrollgrab); @@ -765,8 +763,6 @@ struct ParamTraits ReadContentDescription(aMsg, aIter, aResult) && ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) && ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) && - ReadParam(aMsg, aIter, &aResult->mClipRect) && - ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) && ReadParam(aMsg, aIter, &aResult->mPaintRequestTime) && ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsRootContent) && ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetHasScrollgrab) && @@ -780,6 +776,26 @@ struct ParamTraits } }; +template <> +struct ParamTraits +{ + typedef mozilla::layers::ScrollMetadata paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mMetrics); + WriteParam(aMsg, aParam.mMaskLayerIndex); + WriteParam(aMsg, aParam.mClipRect); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mMetrics) && + ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) && + ReadParam(aMsg, aIter, &aResult->mClipRect)); + } +}; + template<> struct ParamTraits { diff --git a/gfx/layers/FrameMetrics.cpp b/gfx/layers/FrameMetrics.cpp index a4cf2b579a64..2ea0b02172ac 100644 --- a/gfx/layers/FrameMetrics.cpp +++ b/gfx/layers/FrameMetrics.cpp @@ -10,7 +10,6 @@ namespace mozilla { namespace layers { const FrameMetrics::ViewID FrameMetrics::NULL_SCROLL_ID = 0; -const FrameMetrics FrameMetrics::sNullMetrics; void FrameMetrics::SetUsesContainerScrolling(bool aValue) { @@ -18,5 +17,7 @@ FrameMetrics::SetUsesContainerScrolling(bool aValue) { mUsesContainerScrolling = aValue; } +const ScrollMetadata ScrollMetadata::sNullMetadata; + +} } -} \ No newline at end of file diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 556d16572ec3..fca3a0cb893a 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -38,7 +38,6 @@ public: static const ViewID NULL_SCROLL_ID; // This container layer does not scroll. static const ViewID START_SCROLL_ID = 2; // This is the ID that scrolling subframes // will begin at. - static const FrameMetrics sNullMetrics; // We often need an empty metrics FrameMetrics() : mScrollId(NULL_SCROLL_ID) @@ -63,8 +62,6 @@ public: , mContentDescription() , mLineScrollAmount(0, 0) , mPageScrollAmount(0, 0) - , mClipRect() - , mMaskLayerIndex() , mPaintRequestTime() , mIsRootContent(false) , mHasScrollgrab(false) @@ -105,8 +102,6 @@ public: // don't compare mContentDescription mLineScrollAmount == aOther.mLineScrollAmount && mPageScrollAmount == aOther.mPageScrollAmount && - mClipRect == aOther.mClipRect && - mMaskLayerIndex == aOther.mMaskLayerIndex && mPaintRequestTime == aOther.mPaintRequestTime && mIsRootContent == aOther.mIsRootContent && mHasScrollgrab == aOther.mHasScrollgrab && @@ -124,14 +119,6 @@ public: return !operator==(aOther); } - bool IsDefault() const - { - FrameMetrics def; - - def.mPresShellId = mPresShellId; - return (def == *this); - } - bool IsScrollable() const { return mScrollId != NULL_SCROLL_ID; @@ -528,28 +515,6 @@ public: mAllowVerticalScrollWithWheel = aValue; } - void SetClipRect(const Maybe& aClipRect) - { - mClipRect = aClipRect; - } - const Maybe& GetClipRect() const - { - return mClipRect; - } - bool HasClipRect() const { - return mClipRect.isSome(); - } - const ParentLayerIntRect& ClipRect() const { - return mClipRect.ref(); - } - - void SetMaskLayerIndex(const Maybe& aIndex) { - mMaskLayerIndex = aIndex; - } - const Maybe& GetMaskLayerIndex() const { - return mMaskLayerIndex; - } - void SetPaintRequestTime(const TimeStamp& aTime) { mPaintRequestTime = aTime; } @@ -731,14 +696,6 @@ private: // The value of GetPageScrollAmount(), for scroll frames. LayoutDeviceIntSize mPageScrollAmount; - // The clip rect to use when compositing a layer with this FrameMetrics. - Maybe mClipRect; - - // An extra clip mask layer to use when compositing a layer with this - // FrameMetrics. This is an index into the MetricsMaskLayers array on - // the Layer. - Maybe mMaskLayerIndex; - // The time at which the APZC last requested a repaint for this scrollframe. TimeStamp mPaintRequestTime; @@ -794,6 +751,81 @@ private: } }; + +/** + * Metadata about a scroll frame that's stored in the layer tree for use by + * the compositor (including APZ). This includes the scroll frame's FrameMetrics, + * as well as other metadata. We don't put the other metadata into FrameMetrics + * to avoid FrameMetrics becoming too bloated (as a FrameMetrics is e.g. sent + * over IPC for every repaint request for every active scroll frame). + */ +struct ScrollMetadata { + friend struct IPC::ParamTraits; +public: + static const ScrollMetadata sNullMetadata; // We sometimes need an empty metadata + + ScrollMetadata() + : mMetrics() + , mMaskLayerIndex() + , mClipRect() + {} + + bool operator==(const ScrollMetadata& aOther) const + { + return mMetrics == aOther.mMetrics && + mMaskLayerIndex == aOther.mMaskLayerIndex && + mClipRect == aOther.mClipRect; + } + + bool operator!=(const ScrollMetadata& aOther) const + { + return !operator==(aOther); + } + + bool IsDefault() const + { + ScrollMetadata def; + + def.mMetrics.SetPresShellId(mMetrics.GetPresShellId()); + return (def == *this); + } + + FrameMetrics& GetMetrics() { return mMetrics; } + const FrameMetrics& GetMetrics() const { return mMetrics; } + + void SetMaskLayerIndex(const Maybe& aIndex) { + mMaskLayerIndex = aIndex; + } + const Maybe& GetMaskLayerIndex() const { + return mMaskLayerIndex; + } + + void SetClipRect(const Maybe& aClipRect) + { + mClipRect = aClipRect; + } + const Maybe& GetClipRect() const + { + return mClipRect; + } + bool HasClipRect() const { + return mClipRect.isSome(); + } + const ParentLayerIntRect& ClipRect() const { + return mClipRect.ref(); + } +private: + FrameMetrics mMetrics; + + // An extra clip mask layer to use when compositing a layer with this + // FrameMetrics. This is an index into the MetricsMaskLayers array on + // the Layer. + Maybe mMaskLayerIndex; + + // The clip rect to use when compositing a layer with this FrameMetrics. + Maybe mClipRect; +}; + /** * This class allows us to uniquely identify a scrollable layer. The * mLayersId identifies the layer tree (corresponding to a child process diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h index fbda790f0738..5051e125522a 100644 --- a/gfx/layers/LayerMetricsWrapper.h +++ b/gfx/layers/LayerMetricsWrapper.h @@ -142,7 +142,7 @@ public: switch (aStart) { case StartAt::TOP: - mIndex = mLayer->GetFrameMetricsCount(); + mIndex = mLayer->GetScrollMetadataCount(); if (mIndex > 0) { mIndex--; } @@ -161,7 +161,7 @@ public: , mIndex(aMetricsIndex) { MOZ_ASSERT(mLayer); - MOZ_ASSERT(mIndex == 0 || mIndex < mLayer->GetFrameMetricsCount()); + MOZ_ASSERT(mIndex == 0 || mIndex < mLayer->GetScrollMetadataCount()); } bool IsValid() const @@ -240,21 +240,26 @@ public: return LayerMetricsWrapper(nullptr); } - const FrameMetrics& Metrics() const + const ScrollMetadata& Metadata() const { MOZ_ASSERT(IsValid()); - if (mIndex >= mLayer->GetFrameMetricsCount()) { - return FrameMetrics::sNullMetrics; + if (mIndex >= mLayer->GetScrollMetadataCount()) { + return ScrollMetadata::sNullMetadata; } - return mLayer->GetFrameMetrics(mIndex); + return mLayer->GetScrollMetadata(mIndex); + } + + const FrameMetrics& Metrics() const + { + return Metadata().GetMetrics(); } AsyncPanZoomController* GetApzc() const { MOZ_ASSERT(IsValid()); - if (mIndex >= mLayer->GetFrameMetricsCount()) { + if (mIndex >= mLayer->GetScrollMetadataCount()) { return nullptr; } return mLayer->GetAsyncPanZoomController(mIndex); @@ -264,12 +269,12 @@ public: { MOZ_ASSERT(IsValid()); - if (mLayer->GetFrameMetricsCount() == 0) { + if (mLayer->GetScrollMetadataCount() == 0) { MOZ_ASSERT(mIndex == 0); MOZ_ASSERT(aApzc == nullptr); return; } - MOZ_ASSERT(mIndex < mLayer->GetFrameMetricsCount()); + MOZ_ASSERT(mIndex < mLayer->GetScrollMetadataCount()); mLayer->SetAsyncPanZoomController(mIndex, aApzc); } @@ -441,30 +446,30 @@ public: static const FrameMetrics& TopmostScrollableMetrics(Layer* aLayer) { - for (uint32_t i = aLayer->GetFrameMetricsCount(); i > 0; i--) { + for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) { if (aLayer->GetFrameMetrics(i - 1).IsScrollable()) { return aLayer->GetFrameMetrics(i - 1); } } - return FrameMetrics::sNullMetrics; + return ScrollMetadata::sNullMetadata.GetMetrics(); } static const FrameMetrics& BottommostScrollableMetrics(Layer* aLayer) { - for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { if (aLayer->GetFrameMetrics(i).IsScrollable()) { return aLayer->GetFrameMetrics(i); } } - return FrameMetrics::sNullMetrics; + return ScrollMetadata::sNullMetadata.GetMetrics(); } static const FrameMetrics& BottommostMetrics(Layer* aLayer) { - if (aLayer->GetFrameMetricsCount() > 0) { + if (aLayer->GetScrollMetadataCount() > 0) { return aLayer->GetFrameMetrics(0); } - return FrameMetrics::sNullMetrics; + return ScrollMetadata::sNullMetadata.GetMetrics(); } private: @@ -475,7 +480,7 @@ private: bool AtTopLayer() const { - return mLayer->GetFrameMetricsCount() == 0 || mIndex == mLayer->GetFrameMetricsCount() - 1; + return mLayer->GetScrollMetadataCount() == 0 || mIndex == mLayer->GetScrollMetadataCount() - 1; } private: diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index ada472541329..419bf5a170f6 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -538,14 +538,14 @@ Layer::StartPendingAnimations(const TimeStamp& aReadyTime) void Layer::SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller) { - MOZ_ASSERT(aIndex < GetFrameMetricsCount()); + MOZ_ASSERT(aIndex < GetScrollMetadataCount()); mApzcs[aIndex] = controller; } AsyncPanZoomController* Layer::GetAsyncPanZoomController(uint32_t aIndex) const { - MOZ_ASSERT(aIndex < GetFrameMetricsCount()); + MOZ_ASSERT(aIndex < GetScrollMetadataCount()); #ifdef DEBUG if (mApzcs[aIndex]) { MOZ_ASSERT(GetFrameMetrics(aIndex).IsScrollable()); @@ -555,9 +555,9 @@ Layer::GetAsyncPanZoomController(uint32_t aIndex) const } void -Layer::FrameMetricsChanged() +Layer::ScrollMetadataChanged() { - mApzcs.SetLength(GetFrameMetricsCount()); + mApzcs.SetLength(GetScrollMetadataCount()); } void @@ -846,17 +846,23 @@ Layer::CalculateScissorRect(const RenderTargetIntRect& aCurrentScissorRect) return currentClip.Intersect(scissor); } +const ScrollMetadata& +Layer::GetScrollMetadata(uint32_t aIndex) const +{ + MOZ_ASSERT(aIndex < GetScrollMetadataCount()); + return mScrollMetadata[aIndex]; +} + const FrameMetrics& Layer::GetFrameMetrics(uint32_t aIndex) const { - MOZ_ASSERT(aIndex < GetFrameMetricsCount()); - return mFrameMetrics[aIndex]; + return GetScrollMetadata(aIndex).GetMetrics(); } bool Layer::HasScrollableFrameMetrics() const { - for (uint32_t i = 0; i < GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < GetScrollMetadataCount(); i++) { if (GetFrameMetrics(i).IsScrollable()) { return true; } @@ -1088,12 +1094,12 @@ Layer::GetCombinedClipRect() const { Maybe clip = GetClipRect(); - for (size_t i = 0; i < mFrameMetrics.Length(); i++) { - if (!mFrameMetrics[i].HasClipRect()) { + for (size_t i = 0; i < mScrollMetadata.Length(); i++) { + if (!mScrollMetadata[i].HasClipRect()) { continue; } - const ParentLayerIntRect& other = mFrameMetrics[i].ClipRect(); + const ParentLayerIntRect& other = mScrollMetadata[i].ClipRect(); if (clip) { clip = Some(clip.value().Intersect(other)); } else { @@ -1968,10 +1974,10 @@ Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix) if (mMaskLayer) { aStream << nsPrintfCString(" [mMaskLayer=%p]", mMaskLayer.get()).get(); } - for (uint32_t i = 0; i < mFrameMetrics.Length(); i++) { - if (!mFrameMetrics[i].IsDefault()) { + for (uint32_t i = 0; i < mScrollMetadata.Length(); i++) { + if (!mScrollMetadata[i].IsDefault()) { aStream << nsPrintfCString(" [metrics%d=", i).get(); - AppendToString(aStream, mFrameMetrics[i], "", "]"); + AppendToString(aStream, mScrollMetadata[i], "", "]"); } } } diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index a07fe2e63c13..c837d06d36f3 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -855,12 +855,12 @@ public: * them with the provided FrameMetrics. See the documentation for * SetFrameMetrics(const nsTArray&) for more details. */ - void SetFrameMetrics(const FrameMetrics& aFrameMetrics) + void SetScrollMetadata(const ScrollMetadata& aScrollMetadata) { - if (mFrameMetrics.Length() != 1 || mFrameMetrics[0] != aFrameMetrics) { + if (mScrollMetadata.Length() != 1 || mScrollMetadata[0] != aScrollMetadata) { MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this)); - mFrameMetrics.ReplaceElementsAt(0, mFrameMetrics.Length(), aFrameMetrics); - FrameMetricsChanged(); + mScrollMetadata.ReplaceElementsAt(0, mScrollMetadata.Length(), aScrollMetadata); + ScrollMetadataChanged(); Mutated(); } } @@ -871,23 +871,23 @@ public: * rooted at this. There might be multiple metrics on this layer * because the layer may, for example, be contained inside multiple * nested scrolling subdocuments. In general a Layer having multiple - * FrameMetrics objects is conceptually equivalent to having a stack + * ScrollMetadata objects is conceptually equivalent to having a stack * of ContainerLayers that have been flattened into this Layer. * See the documentation in LayerMetricsWrapper.h for a more detailed * explanation of this conceptual equivalence. * * Note also that there is actually a many-to-many relationship between - * Layers and FrameMetrics, because multiple Layers may have identical - * FrameMetrics objects. This happens when those layers belong to the + * Layers and ScrollMetadata, because multiple Layers may have identical + * ScrollMetadata objects. This happens when those layers belong to the * same scrolling subdocument and therefore end up with the same async * transform when they are scrolled by the APZ code. */ - void SetFrameMetrics(const nsTArray& aMetricsArray) + void SetScrollMetadata(const nsTArray& aMetadataArray) { - if (mFrameMetrics != aMetricsArray) { + if (mScrollMetadata != aMetadataArray) { MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) FrameMetrics", this)); - mFrameMetrics = aMetricsArray; - FrameMetricsChanged(); + mScrollMetadata = aMetadataArray; + ScrollMetadataChanged(); Mutated(); } } @@ -1254,9 +1254,10 @@ public: uint32_t GetContentFlags() { return mContentFlags; } const gfx::IntRect& GetLayerBounds() const { return mLayerBounds; } const LayerIntRegion& GetVisibleRegion() const { return mVisibleRegion; } + const ScrollMetadata& GetScrollMetadata(uint32_t aIndex) const; const FrameMetrics& GetFrameMetrics(uint32_t aIndex) const; - uint32_t GetFrameMetricsCount() const { return mFrameMetrics.Length(); } - const nsTArray& GetAllFrameMetrics() { return mFrameMetrics; } + uint32_t GetScrollMetadataCount() const { return mScrollMetadata.Length(); } + const nsTArray& GetAllScrollMetadata() { return mScrollMetadata; } bool HasScrollableFrameMetrics() const; bool IsScrollInfoLayer() const; const EventRegions& GetEventRegions() const { return mEventRegions; } @@ -1667,10 +1668,10 @@ public: // The aIndex for these functions must be less than GetFrameMetricsCount(). void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller); AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const; - // The FrameMetricsChanged function is used internally to ensure the APZC array length + // The ScrollMetadataChanged function is used internally to ensure the APZC array length // matches the frame metrics array length. private: - void FrameMetricsChanged(); + void ScrollMetadataChanged(); public: void ApplyPendingUpdatesForThisTransaction(); @@ -1791,7 +1792,7 @@ protected: gfx::UserData mUserData; gfx::IntRect mLayerBounds; LayerIntRegion mVisibleRegion; - nsTArray mFrameMetrics; + nsTArray mScrollMetadata; EventRegions mEventRegions; gfx::Matrix4x4 mTransform; // A mutation of |mTransform| that we've queued to be applied at the @@ -2498,8 +2499,6 @@ private: virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override { MOZ_CRASH(); return false; } - using Layer::SetFrameMetrics; - public: /** * CONSTRUCTION PHASE ONLY diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index 22fd69c05f1a..1a149f75ef9b 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -144,6 +144,18 @@ AppendToString(std::stringstream& aStream, const EventRegions& e, aStream << "}" << sfx; } +void +AppendToString(std::stringstream& aStream, const ScrollMetadata& m, + const char* pfx, const char* sfx) +{ + aStream << pfx; + AppendToString(aStream, m.GetMetrics(), "{ [metrics=", "]"); + if (m.HasClipRect()) { + AppendToString(aStream, m.ClipRect(), " [clip=", "]"); + } + aStream << "}" << sfx; +} + void AppendToString(std::stringstream& aStream, const FrameMetrics& m, const char* pfx, const char* sfx, bool detailed) @@ -166,9 +178,6 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m, if (m.IsRootContent()) { aStream << "] [rcd"; } - if (m.HasClipRect()) { - AppendToString(aStream, m.ClipRect(), "] [clip="); - } AppendToString(aStream, m.GetZoom(), "] [z=", "] }"); } else { AppendToString(aStream, m.GetDisplayPortMargins(), " [dpm="); diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h index 169acb8f67cb..96229728a728 100644 --- a/gfx/layers/LayersLogging.h +++ b/gfx/layers/LayersLogging.h @@ -114,6 +114,10 @@ void AppendToString(std::stringstream& aStream, const EventRegions& e, const char* pfx="", const char* sfx=""); +void +AppendToString(std::stringstream& aStream, const ScrollMetadata& m, + const char* pfx="", const char* sfx=""); + void AppendToString(std::stringstream& aStream, const FrameMetrics& m, const char* pfx="", const char* sfx="", bool detailed = false); diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 66091805051b..0eac8dac3e26 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -457,7 +457,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId()); - apzc->NotifyLayersUpdated(aMetrics, aState.mIsFirstPaint, + apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint, aLayersId == aState.mOriginatingLayersId); // Since this is the first time we are encountering an APZC with this guid, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index ada229fe70aa..af9b108c3428 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -860,6 +860,7 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId, // mTreeManager must be initialized before GetFrameTime() is called mTreeManager(aTreeManager), mSharingFrameMetricsAcrossProcesses(false), + mFrameMetrics(mScrollMetadata.GetMetrics()), mMonitor("AsyncPanZoomController"), mX(this), mY(this), @@ -3298,14 +3299,16 @@ bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const { return true; } -void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, +void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint, bool aThisLayerTreeUpdated) { APZThreadUtils::AssertOnCompositorThread(); ReentrantMonitorAutoEnter lock(mMonitor); - bool isDefault = mFrameMetrics.IsDefault(); + bool isDefault = mScrollMetadata.IsDefault(); + + const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics(); if ((aLayerMetrics == mLastContentPaintMetrics) && !isDefault) { // No new information here, skip it. Note that this is not just an @@ -3390,7 +3393,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri // that was just painted is something we knew nothing about previously CancelAnimation(); - mFrameMetrics = aLayerMetrics; + mScrollMetadata = aScrollMetadata; if (scrollOffsetUpdated) { AcknowledgeScrollUpdate(); } @@ -3446,8 +3449,8 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab()); mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount()); mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount()); - mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect()); - mFrameMetrics.SetMaskLayerIndex(aLayerMetrics.GetMaskLayerIndex()); + mScrollMetadata.SetClipRect(aScrollMetadata.GetClipRect()); + mScrollMetadata.SetMaskLayerIndex(aScrollMetadata.GetMaskLayerIndex()); mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot()); mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling()); mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer()); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index aa86d188f55a..d812361f6b37 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -178,13 +178,13 @@ public: AsyncTransformComponentMatrix GetOverscrollTransform() const; /** - * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics + * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata * for the container layer corresponding to this APZC. * |aIsFirstPaint| is a flag passed from the shadow - * layers code indicating that the frame metrics being sent with this call are - * the initial metrics and the initial paint of the frame has just happened. + * layers code indicating that the scroll metadata being sent with this call are + * the initial metadata and the initial paint of the frame has just happened. */ - void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint, + void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint, bool aThisLayerTreeUpdated); /** @@ -681,7 +681,8 @@ protected: protected: // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the // monitor. Do not read from or modify either of them without locking. - FrameMetrics mFrameMetrics; + ScrollMetadata mScrollMetadata; + FrameMetrics& mFrameMetrics; // for convenience, refers to mScrollMetadata.mMetrics // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|. // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the diff --git a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h index f21be66bf579..81e9d1aa78e3 100644 --- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h +++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h @@ -57,7 +57,8 @@ protected: protected: static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId, CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) { - FrameMetrics metrics; + ScrollMetadata metadata; + FrameMetrics& metrics = metadata.GetMetrics(); metrics.SetScrollId(aScrollId); // By convention in this test file, START_SCROLL_ID is the root, so mark it as such. if (aScrollId == FrameMetrics::START_SCROLL_ID) { @@ -70,7 +71,7 @@ protected: metrics.SetScrollOffset(CSSPoint(0, 0)); metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100)); metrics.SetAllowVerticalScrollWithWheel(true); - aLayer->SetFrameMetrics(metrics); + aLayer->SetScrollMetadata(metadata); aLayer->SetClipRect(Some(ViewAs(layerBound))); if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) { // The purpose of this is to roughly mimic what layout would do in the @@ -84,13 +85,13 @@ protected: } void SetScrollHandoff(Layer* aChild, Layer* aParent) { - FrameMetrics metrics = aChild->GetFrameMetrics(0); - metrics.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId()); - aChild->SetFrameMetrics(metrics); + ScrollMetadata metadata = aChild->GetScrollMetadata(0); + metadata.GetMetrics().SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId()); + aChild->SetScrollMetadata(metadata); } static TestAsyncPanZoomController* ApzcOf(Layer* aLayer) { - EXPECT_EQ(1u, aLayer->GetFrameMetricsCount()); + EXPECT_EQ(1u, aLayer->GetScrollMetadataCount()); return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(0); } @@ -151,9 +152,9 @@ protected: SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1); // Make layers[1] the root content - FrameMetrics childMetrics = layers[1]->GetFrameMetrics(0); - childMetrics.SetIsRootContent(true); - layers[1]->SetFrameMetrics(childMetrics); + ScrollMetadata childMetadata = layers[1]->GetScrollMetadata(0); + childMetadata.GetMetrics().SetIsRootContent(true); + layers[1]->SetScrollMetadata(childMetadata); // Both layers are fully dispatch-to-content EventRegions regions; diff --git a/gfx/layers/apz/test/gtest/TestBasic.cpp b/gfx/layers/apz/test/gtest/TestBasic.cpp index 1bc9fc9fc1c2..1ee82d0a44cf 100644 --- a/gfx/layers/apz/test/gtest/TestBasic.cpp +++ b/gfx/layers/apz/test/gtest/TestBasic.cpp @@ -78,7 +78,8 @@ TEST_F(APZCBasicTester, ComplexTransform) { RefPtr lm; RefPtr root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers); - FrameMetrics metrics; + ScrollMetadata metadata; + FrameMetrics& metrics = metadata.GetMetrics(); metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24)); metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6)); metrics.SetScrollOffset(CSSPoint(10, 10)); @@ -89,11 +90,12 @@ TEST_F(APZCBasicTester, ComplexTransform) { metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3)); metrics.SetScrollId(FrameMetrics::START_SCROLL_ID); - FrameMetrics childMetrics = metrics; + ScrollMetadata childMetadata = metadata; + FrameMetrics& childMetrics = childMetadata.GetMetrics(); childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1); - layers[0]->SetFrameMetrics(metrics); - layers[1]->SetFrameMetrics(childMetrics); + layers[0]->SetScrollMetadata(metadata); + layers[1]->SetScrollMetadata(childMetadata); ParentLayerPoint pointOut; AsyncTransform viewTransformOut; @@ -103,13 +105,13 @@ TEST_F(APZCBasicTester, ComplexTransform) { // initial transform apzc->SetFrameMetrics(metrics); - apzc->NotifyLayersUpdated(metrics, true, true); + apzc->NotifyLayersUpdated(metadata, true, true); apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut); EXPECT_EQ(ParentLayerPoint(60, 60), pointOut); childApzc->SetFrameMetrics(childMetrics); - childApzc->NotifyLayersUpdated(childMetrics, true, true); + childApzc->NotifyLayersUpdated(childMetadata, true, true); childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut); EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut); EXPECT_EQ(ParentLayerPoint(60, 60), pointOut); diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index b58e6a7c823a..7bb9b52c29c7 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -330,7 +330,7 @@ IntervalOverlap(gfxFloat aTranslation, gfxFloat aMin, gfxFloat aMax) static LayerMetricsWrapper FindMetricsWithScrollId(Layer* aLayer, FrameMetrics::ViewID aScrollId) { - for (uint64_t i = 0; i < aLayer->GetFrameMetricsCount(); ++i) { + for (uint64_t i = 0; i < aLayer->GetScrollMetadataCount(); ++i) { if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollId) { return LayerMetricsWrapper(aLayer, i); } @@ -687,7 +687,7 @@ AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer) RecordShadowTransforms(child); } - for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { AsyncPanZoomController* apzc = aLayer->GetAsyncPanZoomController(i); if (!apzc) { continue; @@ -837,7 +837,7 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, // of all scroll frames inside the current one. nsTArray ancestorMaskLayers; - for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i); if (!controller) { continue; @@ -858,7 +858,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, controller->MarkAsyncTransformAppliedToContent(); } - const FrameMetrics& metrics = aLayer->GetFrameMetrics(i); + const ScrollMetadata& scrollMetadata = aLayer->GetScrollMetadata(i); + const FrameMetrics& metrics = scrollMetadata.GetMetrics(); #if defined(MOZ_ANDROID_APZ) // If we find a metrics which is the root content doc, use that. If not, use @@ -870,7 +871,7 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, if (!(*aOutFoundRoot)) { *aOutFoundRoot = metrics.IsRootContent() || /* RCD */ (aLayer->GetParent() == nullptr && /* rootmost metrics */ - i + 1 >= aLayer->GetFrameMetricsCount()); + i + 1 >= aLayer->GetScrollMetadataCount()); if (*aOutFoundRoot) { mRootScrollableId = metrics.GetScrollId(); CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor(); @@ -939,8 +940,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, // Combine the local clip with the ancestor scrollframe clip. This is not // included in the async transform above, since the ancestor clip should not // move with this APZC. - if (metrics.HasClipRect()) { - ParentLayerIntRect clip = metrics.ClipRect(); + if (scrollMetadata.HasClipRect()) { + ParentLayerIntRect clip = scrollMetadata.ClipRect(); if (aLayer->GetParent() && aLayer->GetParent()->GetTransformIsPerspective()) { // If our parent layer has a perspective transform, we want to apply // our scroll clip to it instead of to this layer (see bug 1168263). @@ -962,8 +963,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, } // Append the ancestor mask layer for this scroll frame to ancestorMaskLayers. - if (metrics.GetMaskLayerIndex()) { - size_t maskLayerIndex = metrics.GetMaskLayerIndex().value(); + if (scrollMetadata.GetMaskLayerIndex()) { + size_t maskLayerIndex = scrollMetadata.GetMaskLayerIndex().value(); Layer* ancestorMaskLayer = aLayer->GetAncestorMaskLayerAt(maskLayerIndex); ancestorMaskLayers.AppendElement(ancestorMaskLayer); } diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index a94f98755e92..59593d00b7a7 100755 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -478,7 +478,7 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager, { Compositor* compositor = aManager->GetCompositor(); - if (aLayer->GetFrameMetricsCount() < 1) { + if (aLayer->GetScrollMetadataCount() < 1) { return; } @@ -654,7 +654,7 @@ RenderLayers(ContainerT* aContainer, // frames higher up, so loop from the top down, and accumulate an async // transform as we go along. Matrix4x4 asyncTransform; - for (uint32_t i = layer->GetFrameMetricsCount(); i > 0; --i) { + for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; --i) { if (layer->GetFrameMetrics(i - 1).IsScrollable()) { // Since the composition bounds are in the parent layer's coordinates, // use the parent's effective transform rather than the layer's own. diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index e60f971b4545..edfa84bab5ee 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -367,7 +367,7 @@ LayerTransactionParent::RecvUpdate(InfallibleTArray&& cset, layer->SetMaskLayer(nullptr); } layer->SetAnimations(common.animations()); - layer->SetFrameMetrics(common.metrics()); + layer->SetScrollMetadata(common.scrollMetadata()); layer->SetDisplayListLog(common.displayListLog().get()); // The updated invalid region is added to the existing one, since we can @@ -792,7 +792,7 @@ LayerTransactionParent::RecvGetAnimationTransform(PLayerParent* aParent, static AsyncPanZoomController* GetAPZCForViewID(Layer* aLayer, FrameMetrics::ViewID aScrollID) { - for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { if (aLayer->GetFrameMetrics(i).GetScrollId() == aScrollID) { return aLayer->GetAsyncPanZoomController(i); } diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh index b60663f93311..a2d150f32b5e 100644 --- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -41,7 +41,7 @@ using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h"; using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h"; using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h"; using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h"; -using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; +using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h"; using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h"; using struct mozilla::layers::FenceHandle from "mozilla/layers/FenceUtils.h"; using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h"; @@ -237,7 +237,7 @@ struct CommonLayerAttributes { // Animated colors will only honored for ColorLayers. Animation[] animations; nsIntRegion invalidRegion; - FrameMetrics[] metrics; + ScrollMetadata[] scrollMetadata; nsCString displayListLog; }; diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 092f1c7faffd..44ad4a67237c 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -844,7 +844,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies, common.maskLayerParent() = nullptr; common.animations() = mutant->GetAnimations(); common.invalidRegion() = mutant->GetInvalidRegion(); - common.metrics() = mutant->GetAllFrameMetrics(); + common.scrollMetadata() = mutant->GetAllScrollMetadata(); for (size_t i = 0; i < mutant->GetAncestorMaskLayerCount(); i++) { auto layer = Shadow(mutant->GetAncestorMaskLayerAt(i)->AsShadowableLayer()); common.ancestorMaskLayersChild().AppendElement(layer); diff --git a/gfx/tests/gtest/TestLayers.cpp b/gfx/tests/gtest/TestLayers.cpp index 47116c2dbdbb..30c150020d82 100644 --- a/gfx/tests/gtest/TestLayers.cpp +++ b/gfx/tests/gtest/TestLayers.cpp @@ -389,11 +389,11 @@ TEST(LayerMetricsWrapper, SimpleTree) { ASSERT_TRUE(rootWrapper == wrapper.GetParent()); } -static FrameMetrics -MakeMetrics(FrameMetrics::ViewID aId) { - FrameMetrics metrics; - metrics.SetScrollId(aId); - return metrics; +static ScrollMetadata +MakeMetadata(FrameMetrics::ViewID aId) { + ScrollMetadata metadata; + metadata.GetMetrics().SetScrollId(aId); + return metadata; } TEST(LayerMetricsWrapper, MultiFramemetricsTree) { @@ -401,31 +401,31 @@ TEST(LayerMetricsWrapper, MultiFramemetricsTree) { RefPtr lm; RefPtr root = CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers); - nsTArray metrics; - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 0)); // topmost of root layer - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::NULL_SCROLL_ID)); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 1)); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 2)); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::NULL_SCROLL_ID)); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::NULL_SCROLL_ID)); // bottom of root layer - root->SetFrameMetrics(metrics); + nsTArray metadata; + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 0)); // topmost of root layer + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID)); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 1)); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 2)); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID)); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID)); // bottom of root layer + root->SetScrollMetadata(metadata); - metrics.Clear(); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 3)); - layers[1]->SetFrameMetrics(metrics); + metadata.Clear(); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 3)); + layers[1]->SetScrollMetadata(metadata); - metrics.Clear(); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::NULL_SCROLL_ID)); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 4)); - layers[2]->SetFrameMetrics(metrics); + metadata.Clear(); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::NULL_SCROLL_ID)); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 4)); + layers[2]->SetScrollMetadata(metadata); - metrics.Clear(); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 5)); - layers[4]->SetFrameMetrics(metrics); + metadata.Clear(); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 5)); + layers[4]->SetScrollMetadata(metadata); - metrics.Clear(); - metrics.InsertElementAt(0, MakeMetrics(FrameMetrics::START_SCROLL_ID + 6)); - layers[5]->SetFrameMetrics(metrics); + metadata.Clear(); + metadata.InsertElementAt(0, MakeMetadata(FrameMetrics::START_SCROLL_ID + 6)); + layers[5]->SetScrollMetadata(metadata); LayerMetricsWrapper wrapper(root, LayerMetricsWrapper::StartAt::TOP); nsTArray expectedLayers; diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 0f112b087b96..cced558f12d5 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -686,9 +686,9 @@ struct NewLayerEntry { RefPtr mLayer; AnimatedGeometryRoot* mAnimatedGeometryRoot; const DisplayItemScrollClip* mScrollClip; - // If non-null, this FrameMetrics is set to the be the first FrameMetrics + // If non-null, this ScrollMetadata is set to the be the first ScrollMetadata // on the layer. - UniquePtr mBaseFrameMetrics; + UniquePtr mBaseScrollMetadata; // The following are only used for retained layers (for occlusion // culling of those layers). These regions are all relative to the // container reference frame. @@ -4160,15 +4160,15 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) if (itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) { nsDisplayScrollInfoLayer* scrollItem = static_cast(item); newLayerEntry->mOpaqueForAnimatedGeometryRootParent = false; - newLayerEntry->mBaseFrameMetrics = - scrollItem->ComputeFrameMetrics(ownLayer, mParameters); + newLayerEntry->mBaseScrollMetadata = + scrollItem->ComputeScrollMetadata(ownLayer, mParameters); } else if ((itemType == nsDisplayItem::TYPE_SUBDOCUMENT || itemType == nsDisplayItem::TYPE_ZOOM || itemType == nsDisplayItem::TYPE_RESOLUTION) && gfxPrefs::LayoutUseContainersForRootFrames()) { - newLayerEntry->mBaseFrameMetrics = - static_cast(item)->ComputeFrameMetrics(ownLayer, mParameters); + newLayerEntry->mBaseScrollMetadata = + static_cast(item)->ComputeScrollMetadata(ownLayer, mParameters); } /** @@ -4714,13 +4714,13 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) return; } - AutoTArray metricsArray; - if (aEntry->mBaseFrameMetrics) { - metricsArray.AppendElement(*aEntry->mBaseFrameMetrics); + AutoTArray metricsArray; + if (aEntry->mBaseScrollMetadata) { + metricsArray.AppendElement(*aEntry->mBaseScrollMetadata); // The base FrameMetrics was not computed by the nsIScrollableframe, so it // should not have a mask layer. - MOZ_ASSERT(!aEntry->mBaseFrameMetrics->GetMaskLayerIndex()); + MOZ_ASSERT(!aEntry->mBaseScrollMetadata->GetMaskLayerIndex()); } // Any extra mask layers we need to attach to FrameMetrics. @@ -4739,9 +4739,9 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) nsIScrollableFrame* scrollFrame = scrollClip->mScrollableFrame; const DisplayItemClip* clip = scrollClip->mClip; - Maybe metrics = - scrollFrame->ComputeFrameMetrics(aEntry->mLayer, mContainerReferenceFrame, mParameters, clip); - if (!metrics) { + Maybe metadata = + scrollFrame->ComputeScrollMetadata(aEntry->mLayer, mContainerReferenceFrame, mParameters, clip); + if (!metadata) { continue; } @@ -4757,16 +4757,16 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry) RefPtr maskLayer = CreateMaskLayer(aEntry->mLayer, *clip, nextIndex, clip->GetRoundedRectCount()); if (maskLayer) { - metrics->SetMaskLayerIndex(nextIndex); + metadata->SetMaskLayerIndex(nextIndex); maskLayers.AppendElement(maskLayer); } } - metricsArray.AppendElement(*metrics); + metricsArray.AppendElement(*metadata); } // Watch out for FrameMetrics copies in profiles - aEntry->mLayer->SetFrameMetrics(metricsArray); + aEntry->mLayer->SetScrollMetadata(metricsArray); aEntry->mLayer->SetAncestorMaskLayers(maskLayers); } @@ -4806,8 +4806,8 @@ InvalidateVisibleBoundsChangesForScrolledLayer(PaintedLayer* aLayer) static inline const Maybe& GetStationaryClipInContainer(Layer* aLayer) { - if (size_t metricsCount = aLayer->GetFrameMetricsCount()) { - return aLayer->GetFrameMetrics(metricsCount - 1).GetClipRect(); + if (size_t metricsCount = aLayer->GetScrollMetadataCount()) { + return aLayer->GetScrollMetadata(metricsCount - 1).GetClipRect(); } return aLayer->GetClipRect(); } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 9b9e23a10e18..cbab3bc6559b 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1691,11 +1691,11 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB props = Move(LayerProperties::CloneFrom(layerManager->GetRoot())); } - // Clear any FrameMetrics that may have been set on the root layer on a + // Clear any ScrollMetadata that may have been set on the root layer on a // previous paint. This paint will set new metrics if necessary, and if we // don't clear the old one here, we may be left with extra metrics. if (Layer* root = layerManager->GetRoot()) { - root->SetFrameMetrics(nsTArray()); + root->SetScrollMetadata(nsTArray()); } ContainerLayerParameters containerParameters @@ -1767,8 +1767,8 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize()); - root->SetFrameMetrics( - nsLayoutUtils::ComputeFrameMetrics(frame, + root->SetScrollMetadata( + nsLayoutUtils::ComputeScrollMetadata(frame, rootScrollFrame, content, aBuilder->FindReferenceFrameFor(frame), root, FrameMetrics::NULL_SCROLL_ID, viewport, Nothing(), @@ -4580,12 +4580,12 @@ nsDisplaySubDocument::BuildLayer(nsDisplayListBuilder* aBuilder, return layer.forget(); } -UniquePtr -nsDisplaySubDocument::ComputeFrameMetrics(Layer* aLayer, - const ContainerLayerParameters& aContainerParameters) +UniquePtr +nsDisplaySubDocument::ComputeScrollMetadata(Layer* aLayer, + const ContainerLayerParameters& aContainerParameters) { if (!(mFlags & GENERATE_SCROLLABLE_LAYER)) { - return UniquePtr(nullptr); + return UniquePtr(nullptr); } nsPresContext* presContext = mFrame->PresContext(); @@ -4606,8 +4606,8 @@ nsDisplaySubDocument::ComputeFrameMetrics(Layer* aLayer, mFrame->GetPosition() + mFrame->GetOffsetToCrossDoc(ReferenceFrame()); - return MakeUnique( - nsLayoutUtils::ComputeFrameMetrics( + return MakeUnique( + nsLayoutUtils::ComputeScrollMetadata( mFrame, rootScrollFrame, rootScrollFrame->GetContent(), ReferenceFrame(), aLayer, mScrollParentId, viewport, Nothing(), isRootContentDocument, params)); @@ -4989,9 +4989,9 @@ nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder, return LAYER_ACTIVE_EMPTY; } -UniquePtr -nsDisplayScrollInfoLayer::ComputeFrameMetrics(Layer* aLayer, - const ContainerLayerParameters& aContainerParameters) +UniquePtr +nsDisplayScrollInfoLayer::ComputeScrollMetadata(Layer* aLayer, + const ContainerLayerParameters& aContainerParameters) { ContainerLayerParameters params = aContainerParameters; if (mScrolledFrame->GetContent() && @@ -5003,13 +5003,13 @@ nsDisplayScrollInfoLayer::ComputeFrameMetrics(Layer* aLayer, mScrollFrame->GetPosition() + mScrollFrame->GetOffsetToCrossDoc(ReferenceFrame()); - FrameMetrics metrics = nsLayoutUtils::ComputeFrameMetrics( + ScrollMetadata metadata = nsLayoutUtils::ComputeScrollMetadata( mScrolledFrame, mScrollFrame, mScrollFrame->GetContent(), ReferenceFrame(), aLayer, mScrollParentId, viewport, Nothing(), false, params); - metrics.SetIsScrollInfoLayer(true); + metadata.GetMetrics().SetIsScrollInfoLayer(true); - return UniquePtr(new FrameMetrics(metrics)); + return UniquePtr(new ScrollMetadata(metadata)); } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 024e199c2a7d..a37c12fa8bc2 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1308,6 +1308,7 @@ public: typedef mozilla::DisplayItemClip DisplayItemClip; typedef mozilla::DisplayItemScrollClip DisplayItemScrollClip; typedef mozilla::layers::FrameMetrics FrameMetrics; + typedef mozilla::layers::ScrollMetadata ScrollMetadata; typedef mozilla::layers::FrameMetrics::ViewID ViewID; typedef mozilla::layers::Layer Layer; typedef mozilla::layers::LayerManager LayerManager; @@ -3550,8 +3551,8 @@ public: NS_DISPLAY_DECL_NAME("SubDocument", TYPE_SUBDOCUMENT) - mozilla::UniquePtr ComputeFrameMetrics(Layer* aLayer, - const ContainerLayerParameters& aContainerParameters); + mozilla::UniquePtr ComputeScrollMetadata(Layer* aLayer, + const ContainerLayerParameters& aContainerParameters); protected: ViewID mScrollParentId; @@ -3691,8 +3692,8 @@ public: virtual void WriteDebugInfo(std::stringstream& aStream) override; - mozilla::UniquePtr ComputeFrameMetrics(Layer* aLayer, - const ContainerLayerParameters& aContainerParameters); + mozilla::UniquePtr ComputeScrollMetadata(Layer* aLayer, + const ContainerLayerParameters& aContainerParameters); protected: nsIFrame* mScrollFrame; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 3755fdb43708..8a7426915bef 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8583,23 +8583,24 @@ nsLayoutUtils::CanScrollOriginClobberApz(nsIAtom* aScrollOrigin) && aScrollOrigin != nsGkAtoms::restore; } -/* static */ FrameMetrics -nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, - nsIFrame* aScrollFrame, - nsIContent* aContent, - const nsIFrame* aReferenceFrame, - Layer* aLayer, - ViewID aScrollParentId, - const nsRect& aViewport, - const Maybe& aClipRect, - bool aIsRootContent, - const ContainerLayerParameters& aContainerParameters) +/* static */ ScrollMetadata +nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame, + nsIFrame* aScrollFrame, + nsIContent* aContent, + const nsIFrame* aReferenceFrame, + Layer* aLayer, + ViewID aScrollParentId, + const nsRect& aViewport, + const Maybe& aClipRect, + bool aIsRootContent, + const ContainerLayerParameters& aContainerParameters) { nsPresContext* presContext = aForFrame->PresContext(); int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel(); nsIPresShell* presShell = presContext->GetPresShell(); - FrameMetrics metrics; + ScrollMetadata metadata; + FrameMetrics& metrics = metadata.GetMetrics(); metrics.SetViewport(CSSRect::FromAppUnits(aViewport)); ViewID scrollId = FrameMetrics::NULL_SCROLL_ID; @@ -8734,7 +8735,7 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, ParentLayerRect rect = LayoutDeviceRect::FromAppUnits(*aClipRect, auPerDevPixel) * metrics.GetCumulativeResolution() * layerToParentLayerScale; - metrics.SetClipRect(Some(RoundedToInt(rect))); + metadata.SetClipRect(Some(RoundedToInt(rect))); } // For the root scroll frame of the root content document (RCD-RSF), the above calculation @@ -8798,13 +8799,13 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, } } - return metrics; + return metadata; } /* static */ bool nsLayoutUtils::ContainsMetricsWithId(const Layer* aLayer, const ViewID& aScrollId) { - for (uint32_t i = aLayer->GetFrameMetricsCount(); i > 0; i--) { + for (uint32_t i = aLayer->GetScrollMetadataCount(); i > 0; i--) { if (aLayer->GetFrameMetrics(i-1).GetScrollId() == aScrollId) { return true; } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index c0acfa62d692..87c438ec41e0 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -143,6 +143,7 @@ class nsLayoutUtils public: typedef mozilla::layers::FrameMetrics FrameMetrics; + typedef mozilla::layers::ScrollMetadata ScrollMetadata; typedef FrameMetrics::ViewID ViewID; typedef mozilla::CSSPoint CSSPoint; typedef mozilla::CSSSize CSSSize; @@ -2721,16 +2722,16 @@ public: */ static bool CanScrollOriginClobberApz(nsIAtom* aScrollOrigin); - static FrameMetrics ComputeFrameMetrics(nsIFrame* aForFrame, - nsIFrame* aScrollFrame, - nsIContent* aContent, - const nsIFrame* aReferenceFrame, - Layer* aLayer, - ViewID aScrollParentId, - const nsRect& aViewport, - const mozilla::Maybe& aClipRect, - bool aIsRoot, - const ContainerLayerParameters& aContainerParameters); + static ScrollMetadata ComputeScrollMetadata(nsIFrame* aForFrame, + nsIFrame* aScrollFrame, + nsIContent* aContent, + const nsIFrame* aReferenceFrame, + Layer* aLayer, + ViewID aScrollParentId, + const nsRect& aViewport, + const mozilla::Maybe& aClipRect, + bool aIsRoot, + const ContainerLayerParameters& aContainerParameters); /** * If the given scroll frame needs an area excluded from its composition diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index d560202db2b6..041d4efe36f4 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5474,7 +5474,7 @@ nsIFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) static bool DoesLayerHaveOutOfDateFrameMetrics(Layer* aLayer) { - for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) { + for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) { const FrameMetrics& metrics = aLayer->GetFrameMetrics(i); if (!metrics.IsScrollable()) { continue; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index d2a97db1c304..1e28d9f99b9d 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3553,11 +3553,11 @@ ScrollFrameHelper::DecideScrollableLayer(nsDisplayListBuilder* aBuilder, } -Maybe -ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, - nsIFrame* aContainerReferenceFrame, - const ContainerLayerParameters& aParameters, - const DisplayItemClip* aClip) const +Maybe +ScrollFrameHelper::ComputeScrollMetadata(Layer* aLayer, + nsIFrame* aContainerReferenceFrame, + const ContainerLayerParameters& aParameters, + const DisplayItemClip* aClip) const { if (!mWillBuildScrollableLayer || mIsScrollableLayerInRootContainer) { return Nothing(); @@ -3609,7 +3609,7 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer, nsRect scrollport = mScrollPort + toReferenceFrame; - return Some(nsLayoutUtils::ComputeFrameMetrics( + return Some(nsLayoutUtils::ComputeScrollMetadata( mScrolledFrame, mOuter, mOuter->GetContent(), aContainerReferenceFrame, aLayer, mScrollParentID, scrollport, parentLayerClip, isRootContent, aParameters)); diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 17baf920492a..761775b40823 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -416,7 +416,7 @@ public: } } bool WantAsyncScroll() const; - Maybe ComputeFrameMetrics( + Maybe ComputeScrollMetadata( Layer* aLayer, nsIFrame* aContainerReferenceFrame, const ContainerLayerParameters& aParameters, const mozilla::DisplayItemClip* aClip) const; @@ -905,12 +905,12 @@ public: virtual bool WantAsyncScroll() const override { return mHelper.WantAsyncScroll(); } - virtual mozilla::Maybe ComputeFrameMetrics( + virtual mozilla::Maybe ComputeScrollMetadata( Layer* aLayer, nsIFrame* aContainerReferenceFrame, const ContainerLayerParameters& aParameters, const mozilla::DisplayItemClip* aClip) const override { - return mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, aParameters, aClip); + return mHelper.ComputeScrollMetadata(aLayer, aContainerReferenceFrame, aParameters, aClip); } virtual bool IsIgnoringViewportClipping() const override { return mHelper.IsIgnoringViewportClipping(); @@ -1316,12 +1316,12 @@ public: virtual bool WantAsyncScroll() const override { return mHelper.WantAsyncScroll(); } - virtual mozilla::Maybe ComputeFrameMetrics( + virtual mozilla::Maybe ComputeScrollMetadata( Layer* aLayer, nsIFrame* aContainerReferenceFrame, const ContainerLayerParameters& aParameters, const mozilla::DisplayItemClip* aClip) const override { - return mHelper.ComputeFrameMetrics(aLayer, aContainerReferenceFrame, aParameters, aClip); + return mHelper.ComputeScrollMetadata(aLayer, aContainerReferenceFrame, aParameters, aClip); } virtual bool IsIgnoringViewportClipping() const override { return mHelper.IsIgnoringViewportClipping(); diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index 6af15e481555..e5cf55dac224 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -400,9 +400,9 @@ public: virtual bool WantAsyncScroll() const = 0; /** * aLayer's animated geometry root is this frame. If there needs to be a - * FrameMetrics contributed by this frame, append it to aOutput. + * ScrollMetadata contributed by this frame, append it to aOutput. */ - virtual mozilla::Maybe ComputeFrameMetrics( + virtual mozilla::Maybe ComputeScrollMetadata( mozilla::layers::Layer* aLayer, nsIFrame* aContainerReferenceFrame, const ContainerLayerParameters& aParameters, From 4690842281c7fb17c6b9eaba945c71e83fc83413 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Fri, 18 Mar 2016 20:07:27 -0400 Subject: [PATCH 16/53] Bug 1219296 - Factor out scroll snap information into a form that's usable by the compositor. r=kats MozReview-Commit-ID: DTvu7UsKsBg --HG-- extra : rebase_source : 71831d884d128bc9e50aac55c7a8302f7ffe74f8 --- gfx/layers/FrameMetrics.h | 24 ++++++ layout/generic/nsGfxScrollFrame.cpp | 119 +++++++++++++++++++++------- layout/generic/nsGfxScrollFrame.h | 3 + 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index fca3a0cb893a..22ea892da98e 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -16,6 +16,7 @@ #include "mozilla/gfx/Logging.h" // for Log #include "mozilla/TimeStamp.h" // for TimeStamp #include "nsString.h" +#include "nsStyleCoord.h" // for nsStyleCoord namespace IPC { template struct ParamTraits; @@ -751,6 +752,29 @@ private: } }; +struct ScrollSnapInfo { + ScrollSnapInfo() + : mScrollSnapTypeX(NS_STYLE_SCROLL_SNAP_TYPE_NONE) + , mScrollSnapTypeY(NS_STYLE_SCROLL_SNAP_TYPE_NONE) + {} + + // The scroll frame's scroll-snap-type. + // One of NS_STYLE_SCROLL_SNAP_{NONE, MANDATORY, PROXIMITY}. + uint8_t mScrollSnapTypeX; + uint8_t mScrollSnapTypeY; + + // The intervals derived from the scroll frame's scroll-snap-points. + Maybe mScrollSnapIntervalX; + Maybe mScrollSnapIntervalY; + + // The scroll frame's scroll-snap-destination, in cooked form (to avoid + // shipping the raw nsStyleCoord::CalcValue over IPC). + nsPoint mScrollSnapDestination; + + // The scroll-snap-coordinates of any descendant frames of the scroll frame, + // relative to the origin of the scrolled frame. + nsTArray mScrollSnapCoordinates; +}; /** * Metadata about a scroll frame that's stored in the layer tree for use by diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 1e28d9f99b9d..f9ca6536c687 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -6010,9 +6010,26 @@ CalcSnapPoints::AddEdgeInterval(nscoord aInterval, nscoord aMinPos, } static void -ScrollSnapHelper(SnappingEdgeCallback& aCallback, nsIFrame* aFrame, - nsIFrame* aScrolledFrame, - const nsPoint &aScrollSnapDestination) { +ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback, + const nsTArray& aScrollSnapCoordinates, + const nsPoint& aScrollSnapDestination) { + for (nsPoint snapCoords : aScrollSnapCoordinates) { + // Make them relative to the scroll snap destination. + snapCoords -= aScrollSnapDestination; + + aCallback.AddVerticalEdge(snapCoords.x); + aCallback.AddHorizontalEdge(snapCoords.y); + } +} + +/** + * Collect the scroll-snap-coordinates of frames in the subtree rooted at + * |aFrame|, relative to |aScrolledFrame|, into |aOutCoords|. + */ +void +CollectScrollSnapCoordinates(nsIFrame* aFrame, nsIFrame* aScrolledFrame, + nsTArray& aOutCoords) +{ nsIFrame::ChildListIterator childLists(aFrame); for (; !childLists.IsDone(); childLists.Next()) { nsFrameList::Enumerator childFrames(childLists.CurrentList()); @@ -6029,7 +6046,7 @@ ScrollSnapHelper(SnappingEdgeCallback& aCallback, nsIFrame* aFrame, for (size_t coordNum = 0; coordNum < coordCount; coordNum++) { const nsStyleImageLayers::Position &coordPosition = f->StyleDisplay()->mScrollSnapCoordinate[coordNum]; - nsPoint coordPoint = edgesRect.TopLeft() - aScrollSnapDestination; + nsPoint coordPoint = edgesRect.TopLeft(); coordPoint += nsPoint(coordPosition.mXPosition.mLength, coordPosition.mYPosition.mLength); if (coordPosition.mXPosition.mHasPercent) { @@ -6041,68 +6058,110 @@ ScrollSnapHelper(SnappingEdgeCallback& aCallback, nsIFrame* aFrame, frameRect.height); } - aCallback.AddVerticalEdge(coordPoint.x); - aCallback.AddHorizontalEdge(coordPoint.y); + aOutCoords.AppendElement(coordPoint); } } - ScrollSnapHelper(aCallback, f, aScrolledFrame, aScrollSnapDestination); + CollectScrollSnapCoordinates(f, aScrolledFrame, aOutCoords); } } } +layers::ScrollSnapInfo +ComputeScrollSnapInfo(const ScrollFrameHelper& aScrollFrame) +{ + ScrollSnapInfo result; + + ScrollbarStyles styles = aScrollFrame.GetScrollbarStylesFromFrame(); + + if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE && + styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) { + // We won't be snapping, short-circuit the computation. + return result; + } + + result.mScrollSnapTypeX = styles.mScrollSnapTypeX; + result.mScrollSnapTypeY = styles.mScrollSnapTypeY; + + nsSize scrollPortSize = aScrollFrame.GetScrollPortRect().Size(); + + result.mScrollSnapDestination = nsPoint(styles.mScrollSnapDestinationX.mLength, + styles.mScrollSnapDestinationY.mLength); + if (styles.mScrollSnapDestinationX.mHasPercent) { + result.mScrollSnapDestination.x += + NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent * + scrollPortSize.width); + } + if (styles.mScrollSnapDestinationY.mHasPercent) { + result.mScrollSnapDestination.y += + NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent * + scrollPortSize.height); + } + + if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) { + result.mScrollSnapIntervalX = Some(nsRuleNode::ComputeCoordPercentCalc( + styles.mScrollSnapPointsX, scrollPortSize.width)); + } + if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) { + result.mScrollSnapIntervalY = Some(nsRuleNode::ComputeCoordPercentCalc( + styles.mScrollSnapPointsY, scrollPortSize.height)); + } + + CollectScrollSnapCoordinates(aScrollFrame.GetScrolledFrame(), + aScrollFrame.GetScrolledFrame(), + result.mScrollSnapCoordinates); + + return result; +} + +layers::ScrollSnapInfo +ScrollFrameHelper::GetScrollSnapInfo() const +{ + // TODO(botond): Should we cache it? + return ComputeScrollSnapInfo(*this); +} + bool ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUnit, nsPoint aStartPos, nsPoint &aDestination) { - ScrollbarStyles styles = GetScrollbarStylesFromFrame(); - if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE && - styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) { + ScrollSnapInfo snapInfo = GetScrollSnapInfo(); + + if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE && + snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) { return false; } nsSize scrollPortSize = mScrollPort.Size(); nsRect scrollRange = GetScrollRangeForClamping(); - nsPoint destPos = nsPoint(styles.mScrollSnapDestinationX.mLength, - styles.mScrollSnapDestinationY.mLength); - if (styles.mScrollSnapDestinationX.mHasPercent) { - destPos.x += NSToCoordFloorClamped(styles.mScrollSnapDestinationX.mPercent - * scrollPortSize.width); - } - - if (styles.mScrollSnapDestinationY.mHasPercent) { - destPos.y += NSToCoordFloorClamped(styles.mScrollSnapDestinationY.mPercent - * scrollPortSize.height); - } + nsPoint destPos = snapInfo.mScrollSnapDestination; CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos); - if (styles.mScrollSnapPointsX.GetUnit() != eStyleUnit_None) { - nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsX, - scrollPortSize.width); + if (snapInfo.mScrollSnapIntervalX.isSome()) { + nscoord interval = snapInfo.mScrollSnapIntervalX.value(); calcSnapPoints.AddVerticalEdgeInterval(scrollRange, interval, destPos.x); } - if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) { - nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsY, - scrollPortSize.height); + if (snapInfo.mScrollSnapIntervalY.isSome()) { + nscoord interval = snapInfo.mScrollSnapIntervalY.value(); calcSnapPoints.AddHorizontalEdgeInterval(scrollRange, interval, destPos.y); } - ScrollSnapHelper(calcSnapPoints, mScrolledFrame, mScrolledFrame, destPos); + ProcessScrollSnapCoordinates(calcSnapPoints, snapInfo.mScrollSnapCoordinates, destPos); bool snapped = false; nsPoint finalPos = calcSnapPoints.GetBestEdge(); nscoord proximityThreshold = Preferences::GetInt("layout.css.scroll-snap.proximity-threshold", 0); proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold); - if (styles.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && + if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && std::abs(aDestination.y - finalPos.y) > proximityThreshold) { finalPos.y = aDestination.y; } else { snapped = true; } - if (styles.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && + if (snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && std::abs(aDestination.x - finalPos.x) > proximityThreshold) { finalPos.x = aDestination.x; } else { diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 761775b40823..e12b6b1e08b3 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -49,6 +49,7 @@ public: typedef mozilla::CSSIntPoint CSSIntPoint; typedef mozilla::layout::ScrollbarActivity ScrollbarActivity; typedef mozilla::layers::FrameMetrics FrameMetrics; + typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo; typedef mozilla::layers::Layer Layer; class AsyncScroll; @@ -390,6 +391,8 @@ public: bool UsesContainerScrolling() const; + ScrollSnapInfo GetScrollSnapInfo() const; + bool DecideScrollableLayer(nsDisplayListBuilder* aBuilder, nsRect* aDirtyRect, bool aAllowCreateDisplayPort); From e33fc6e896cf9e5ffbd30d68b14c0daedb1a7f11 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 16 Mar 2016 21:46:33 -0400 Subject: [PATCH 17/53] Bug 1219296 - Fix an include-what-you-use error. r=kats MozReview-Commit-ID: KBizLnMEXG4 --HG-- extra : rebase_source : 045f571e67b8a1046cbc1c98e9ab2f7716964a96 --- layout/generic/nsRubyTextContainerFrame.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/layout/generic/nsRubyTextContainerFrame.cpp b/layout/generic/nsRubyTextContainerFrame.cpp index d5c80ab42a6b..3c8bd80075e5 100644 --- a/layout/generic/nsRubyTextContainerFrame.cpp +++ b/layout/generic/nsRubyTextContainerFrame.cpp @@ -10,6 +10,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/WritingModes.h" +#include "nsLineLayout.h" #include "nsPresContext.h" #include "nsStyleContext.h" From c2325214db89f716d1f17a12ff004806f89e5674 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Mon, 21 Mar 2016 21:32:28 -0400 Subject: [PATCH 18/53] Bug 1219296 - Factor out the algorithm that computes a scroll snap destination into a reusable form. r=kats MozReview-Commit-ID: KyU67pXEJus --HG-- extra : rebase_source : c1dfbe948dd8d98e549612287ca48e507adf5aee --- layout/generic/ScrollSnap.cpp | 311 ++++++++++++++++++++++++++++ layout/generic/ScrollSnap.h | 37 ++++ layout/generic/moz.build | 2 + layout/generic/nsGfxScrollFrame.cpp | 300 +-------------------------- 4 files changed, 358 insertions(+), 292 deletions(-) create mode 100644 layout/generic/ScrollSnap.cpp create mode 100644 layout/generic/ScrollSnap.h diff --git a/layout/generic/ScrollSnap.cpp b/layout/generic/ScrollSnap.cpp new file mode 100644 index 000000000000..748e30dafc74 --- /dev/null +++ b/layout/generic/ScrollSnap.cpp @@ -0,0 +1,311 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FrameMetrics.h" +#include "ScrollSnap.h" +#include "mozilla/Maybe.h" +#include "mozilla/Preferences.h" +#include "nsLineLayout.h" + +namespace mozilla { + +using layers::ScrollSnapInfo; + +/** + * Stores candidate snapping edges. + */ +class SnappingEdgeCallback { +public: + virtual void AddHorizontalEdge(nscoord aEdge) = 0; + virtual void AddVerticalEdge(nscoord aEdge) = 0; + virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, + nscoord aOffset) = 0; + virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, + nscoord aOffset) = 0; +}; + +/** + * Keeps track of the current best edge to snap to. The criteria for + * adding an edge depends on the scrolling unit. + */ +class CalcSnapPoints : public SnappingEdgeCallback { +public: + CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit, + const nsPoint& aDestination, + const nsPoint& aStartPos); + virtual void AddHorizontalEdge(nscoord aEdge) override; + virtual void AddVerticalEdge(nscoord aEdge) override; + virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, nscoord aOffset) + override; + virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, nscoord aOffset) + override; + void AddEdge(nscoord aEdge, + nscoord aDestination, + nscoord aStartPos, + nscoord aScrollingDirection, + nscoord* aBestEdge, + bool* aEdgeFound); + void AddEdgeInterval(nscoord aInterval, + nscoord aMinPos, + nscoord aMaxPos, + nscoord aOffset, + nscoord aDestination, + nscoord aStartPos, + nscoord aScrollingDirection, + nscoord* aBestEdge, + bool* aEdgeFound); + nsPoint GetBestEdge() const; +protected: + nsIScrollableFrame::ScrollUnit mUnit; + nsPoint mDestination; // gives the position after scrolling but before snapping + nsPoint mStartPos; // gives the position before scrolling + nsIntPoint mScrollingDirection; // always -1, 0, or 1 + nsPoint mBestEdge; // keeps track of the position of the current best edge + bool mHorizontalEdgeFound; // true if mBestEdge.x is storing a valid horizontal edge + bool mVerticalEdgeFound; // true if mBestEdge.y is storing a valid vertical edge +}; + +CalcSnapPoints::CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit, + const nsPoint& aDestination, + const nsPoint& aStartPos) +{ + mUnit = aUnit; + mDestination = aDestination; + mStartPos = aStartPos; + + nsPoint direction = aDestination - aStartPos; + mScrollingDirection = nsIntPoint(0,0); + if (direction.x < 0) { + mScrollingDirection.x = -1; + } + if (direction.x > 0) { + mScrollingDirection.x = 1; + } + if (direction.y < 0) { + mScrollingDirection.y = -1; + } + if (direction.y > 0) { + mScrollingDirection.y = 1; + } + mBestEdge = aDestination; + mHorizontalEdgeFound = false; + mVerticalEdgeFound = false; +} + +nsPoint +CalcSnapPoints::GetBestEdge() const +{ + return nsPoint(mVerticalEdgeFound ? mBestEdge.x : mStartPos.x, + mHorizontalEdgeFound ? mBestEdge.y : mStartPos.y); +} + +void +CalcSnapPoints::AddHorizontalEdge(nscoord aEdge) +{ + AddEdge(aEdge, mDestination.y, mStartPos.y, mScrollingDirection.y, &mBestEdge.y, + &mHorizontalEdgeFound); +} + +void +CalcSnapPoints::AddVerticalEdge(nscoord aEdge) +{ + AddEdge(aEdge, mDestination.x, mStartPos.x, mScrollingDirection.x, &mBestEdge.x, + &mVerticalEdgeFound); +} + +void +CalcSnapPoints::AddHorizontalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, nscoord aOffset) +{ + AddEdgeInterval(aInterval, aScrollRange.y, aScrollRange.YMost(), aOffset, + mDestination.y, mStartPos.y, mScrollingDirection.y, + &mBestEdge.y, &mHorizontalEdgeFound); +} + +void +CalcSnapPoints::AddVerticalEdgeInterval(const nsRect &aScrollRange, + nscoord aInterval, nscoord aOffset) +{ + AddEdgeInterval(aInterval, aScrollRange.x, aScrollRange.XMost(), aOffset, + mDestination.x, mStartPos.x, mScrollingDirection.x, + &mBestEdge.x, &mVerticalEdgeFound); +} + +void +CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination, nscoord aStartPos, + nscoord aScrollingDirection, nscoord* aBestEdge, + bool *aEdgeFound) +{ + // nsIScrollableFrame::DEVICE_PIXELS indicates that we are releasing a drag + // gesture or any other user input event that sets an absolute scroll + // position. In this case, scroll snapping is expected to travel in any + // direction. Otherwise, we will restrict the direction of the scroll + // snapping movement based on aScrollingDirection. + if (mUnit != nsIScrollableFrame::DEVICE_PIXELS) { + // Unless DEVICE_PIXELS, we only want to snap to points ahead of the + // direction we are scrolling + if (aScrollingDirection == 0) { + // The scroll direction is neutral - will not hit a snap point. + return; + } + // nsIScrollableFrame::WHOLE indicates that we are navigating to "home" or + // "end". In this case, we will always select the first or last snap point + // regardless of the direction of the scroll. Otherwise, we will select + // scroll snapping points only in the direction specified by + // aScrollingDirection. + if (mUnit != nsIScrollableFrame::WHOLE) { + // Direction of the edge from the current position (before scrolling) in + // the direction of scrolling + nscoord direction = (aEdge - aStartPos) * aScrollingDirection; + if (direction <= 0) { + // The edge is not in the direction we are scrolling, skip it. + return; + } + } + } + if (!*aEdgeFound) { + *aBestEdge = aEdge; + *aEdgeFound = true; + return; + } + if (mUnit == nsIScrollableFrame::DEVICE_PIXELS || + mUnit == nsIScrollableFrame::LINES) { + if (std::abs(aEdge - aDestination) < std::abs(*aBestEdge - aDestination)) { + *aBestEdge = aEdge; + } + } else if (mUnit == nsIScrollableFrame::PAGES) { + // distance to the edge from the scrolling destination in the direction of scrolling + nscoord overshoot = (aEdge - aDestination) * aScrollingDirection; + // distance to the current best edge from the scrolling destination in the direction of scrolling + nscoord curOvershoot = (*aBestEdge - aDestination) * aScrollingDirection; + + // edges between the current position and the scrolling destination are favoured + // to preserve context + if (overshoot < 0 && (overshoot > curOvershoot || curOvershoot >= 0)) { + *aBestEdge = aEdge; + } + // if there are no edges between the current position and the scrolling destination + // the closest edge beyond the destination is used + if (overshoot > 0 && overshoot < curOvershoot) { + *aBestEdge = aEdge; + } + } else if (mUnit == nsIScrollableFrame::WHOLE) { + // the edge closest to the top/bottom/left/right is used, depending on scrolling direction + if (aScrollingDirection > 0 && aEdge > *aBestEdge) { + *aBestEdge = aEdge; + } else if (aScrollingDirection < 0 && aEdge < *aBestEdge) { + *aBestEdge = aEdge; + } + } else { + NS_ERROR("Invalid scroll mode"); + return; + } +} + +void +CalcSnapPoints::AddEdgeInterval(nscoord aInterval, nscoord aMinPos, + nscoord aMaxPos, nscoord aOffset, + nscoord aDestination, nscoord aStartPos, + nscoord aScrollingDirection, + nscoord* aBestEdge, bool *aEdgeFound) +{ + if (aInterval == 0) { + // When interval is 0, there are no scroll snap points. + // Avoid division by zero and bail. + return; + } + + // The only possible candidate interval snap points are the edges immediately + // surrounding aDestination. + + // aDestination must be clamped to the scroll + // range in order to handle cases where the best matching snap point would + // result in scrolling out of bounds. This clamping must be prior to + // selecting the two interval edges. + nscoord clamped = std::max(std::min(aDestination, aMaxPos), aMinPos); + + // Add each edge in the interval immediately before aTarget and after aTarget + // Do not add edges that are out of range. + nscoord r = (clamped + aOffset) % aInterval; + if (r < aMinPos) { + r += aInterval; + } + nscoord edge = clamped - r; + if (edge >= aMinPos && edge <= aMaxPos) { + AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge, + aEdgeFound); + } + edge += aInterval; + if (edge >= aMinPos && edge <= aMaxPos) { + AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge, + aEdgeFound); + } +} + +static void +ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback, + const nsTArray& aScrollSnapCoordinates, + const nsPoint& aScrollSnapDestination) { + for (nsPoint snapCoords : aScrollSnapCoordinates) { + // Make them relative to the scroll snap destination. + snapCoords -= aScrollSnapDestination; + + aCallback.AddVerticalEdge(snapCoords.x); + aCallback.AddHorizontalEdge(snapCoords.y); + } +} + +Maybe ScrollSnapUtils::GetSnapPointForDestination( + const ScrollSnapInfo& aSnapInfo, + nsIScrollableFrame::ScrollUnit aUnit, + const nsSize& aScrollPortSize, + const nsRect& aScrollRange, + const nsPoint& aStartPos, + const nsPoint& aDestination) +{ + if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE && + aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) { + return Nothing(); + } + + nsPoint destPos = aSnapInfo.mScrollSnapDestination; + + CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos); + + if (aSnapInfo.mScrollSnapIntervalX.isSome()) { + nscoord interval = aSnapInfo.mScrollSnapIntervalX.value(); + calcSnapPoints.AddVerticalEdgeInterval(aScrollRange, interval, destPos.x); + } + if (aSnapInfo.mScrollSnapIntervalY.isSome()) { + nscoord interval = aSnapInfo.mScrollSnapIntervalY.value(); + calcSnapPoints.AddHorizontalEdgeInterval(aScrollRange, interval, destPos.y); + } + + ProcessScrollSnapCoordinates(calcSnapPoints, aSnapInfo.mScrollSnapCoordinates, destPos); + bool snapped = false; + nsPoint finalPos = calcSnapPoints.GetBestEdge(); + nscoord proximityThreshold = + Preferences::GetInt("layout.css.scroll-snap.proximity-threshold", 0); + proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold); + if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && + std::abs(aDestination.y - finalPos.y) > proximityThreshold) { + finalPos.y = aDestination.y; + } else { + snapped = true; + } + if (aSnapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && + std::abs(aDestination.x - finalPos.x) > proximityThreshold) { + finalPos.x = aDestination.x; + } else { + snapped = true; + } + return snapped ? Some(finalPos) : Nothing(); +} + +} // namespace mozilla diff --git a/layout/generic/ScrollSnap.h b/layout/generic/ScrollSnap.h new file mode 100644 index 000000000000..f58df35cd9ef --- /dev/null +++ b/layout/generic/ScrollSnap.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layout_ScrollSnap_h_ +#define mozilla_layout_ScrollSnap_h_ + +namespace mozilla { + +namespace layers { +struct ScrollSnapInfo; +} + +struct ScrollSnapUtils { + /** + * GetSnapPointForDestination determines which point to snap to after + * scrolling. |aStartPos| gives the position before scrolling and + * |aDestination| gives the position after scrolling, with no snapping. + * Behaviour is dependent on the value of |aUnit|. + * |aSnapInfo|, |aScrollPortSize|, and |aScrollRange| are characteristics + * of the scroll frame for which snapping is being performed. + * If a suitable snap point could be found, it is returned. Otherwise, an + * empty Maybe is returned. + */ + static Maybe GetSnapPointForDestination( + const layers::ScrollSnapInfo& aSnapInfo, + nsIScrollableFrame::ScrollUnit aUnit, + const nsSize& aScrollPortSize, + const nsRect& aScrollRange, + const nsPoint& aStartPos, + const nsPoint& aDestination); +}; + +} // namespace mozilla + +#endif // mozilla_layout_ScrollSnap_h_ diff --git a/layout/generic/moz.build b/layout/generic/moz.build index ad33a7c39da8..f04bcb22eb64 100644 --- a/layout/generic/moz.build +++ b/layout/generic/moz.build @@ -96,6 +96,7 @@ EXPORTS += [ 'nsTextRunTransformations.h', 'RubyUtils.h', 'ScrollbarActivity.h', + 'ScrollSnap.h', 'Visibility.h', ] @@ -166,6 +167,7 @@ UNIFIED_SOURCES += [ 'nsViewportFrame.cpp', 'RubyUtils.cpp', 'ScrollbarActivity.cpp', + 'ScrollSnap.cpp', 'ScrollVelocityQueue.cpp', 'StickyScrollContainer.cpp', 'SummaryFrame.cpp', diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index f9ca6536c687..32fff2655576 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -59,6 +59,7 @@ #include "gfxPlatform.h" #include "gfxPrefs.h" #include "AsyncScrollBase.h" +#include "ScrollSnap.h" #include "UnitTransforms.h" #include "nsPluginFrame.h" #include @@ -5774,254 +5775,6 @@ nsIScrollableFrame::GetPerceivedScrollingDirections() const return directions; } -/** - * Stores candidate snapping edges. - */ -class SnappingEdgeCallback { -public: - virtual void AddHorizontalEdge(nscoord aEdge) = 0; - virtual void AddVerticalEdge(nscoord aEdge) = 0; - virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, - nscoord aOffset) = 0; - virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, - nscoord aOffset) = 0; -}; - -/** - * Keeps track of the current best edge to snap to. The criteria for - * adding an edge depends on the scrolling unit. - */ -class CalcSnapPoints : public SnappingEdgeCallback { -public: - CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit, - const nsPoint& aDestination, - const nsPoint& aStartPos); - virtual void AddHorizontalEdge(nscoord aEdge) override; - virtual void AddVerticalEdge(nscoord aEdge) override; - virtual void AddHorizontalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, nscoord aOffset) - override; - virtual void AddVerticalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, nscoord aOffset) - override; - void AddEdge(nscoord aEdge, - nscoord aDestination, - nscoord aStartPos, - nscoord aScrollingDirection, - nscoord* aBestEdge, - bool* aEdgeFound); - void AddEdgeInterval(nscoord aInterval, - nscoord aMinPos, - nscoord aMaxPos, - nscoord aOffset, - nscoord aDestination, - nscoord aStartPos, - nscoord aScrollingDirection, - nscoord* aBestEdge, - bool* aEdgeFound); - nsPoint GetBestEdge() const; -protected: - nsIScrollableFrame::ScrollUnit mUnit; - nsPoint mDestination; // gives the position after scrolling but before snapping - nsPoint mStartPos; // gives the position before scrolling - nsIntPoint mScrollingDirection; // always -1, 0, or 1 - nsPoint mBestEdge; // keeps track of the position of the current best edge - bool mHorizontalEdgeFound; // true if mBestEdge.x is storing a valid horizontal edge - bool mVerticalEdgeFound; // true if mBestEdge.y is storing a valid vertical edge -}; - -CalcSnapPoints::CalcSnapPoints(nsIScrollableFrame::ScrollUnit aUnit, - const nsPoint& aDestination, - const nsPoint& aStartPos) -{ - mUnit = aUnit; - mDestination = aDestination; - mStartPos = aStartPos; - - nsPoint direction = aDestination - aStartPos; - mScrollingDirection = nsIntPoint(0,0); - if (direction.x < 0) { - mScrollingDirection.x = -1; - } - if (direction.x > 0) { - mScrollingDirection.x = 1; - } - if (direction.y < 0) { - mScrollingDirection.y = -1; - } - if (direction.y > 0) { - mScrollingDirection.y = 1; - } - mBestEdge = aDestination; - mHorizontalEdgeFound = false; - mVerticalEdgeFound = false; -} - -nsPoint -CalcSnapPoints::GetBestEdge() const -{ - return nsPoint(mVerticalEdgeFound ? mBestEdge.x : mStartPos.x, - mHorizontalEdgeFound ? mBestEdge.y : mStartPos.y); -} - -void -CalcSnapPoints::AddHorizontalEdge(nscoord aEdge) -{ - AddEdge(aEdge, mDestination.y, mStartPos.y, mScrollingDirection.y, &mBestEdge.y, - &mHorizontalEdgeFound); -} - -void -CalcSnapPoints::AddVerticalEdge(nscoord aEdge) -{ - AddEdge(aEdge, mDestination.x, mStartPos.x, mScrollingDirection.x, &mBestEdge.x, - &mVerticalEdgeFound); -} - -void -CalcSnapPoints::AddHorizontalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, nscoord aOffset) -{ - AddEdgeInterval(aInterval, aScrollRange.y, aScrollRange.YMost(), aOffset, - mDestination.y, mStartPos.y, mScrollingDirection.y, - &mBestEdge.y, &mHorizontalEdgeFound); -} - -void -CalcSnapPoints::AddVerticalEdgeInterval(const nsRect &aScrollRange, - nscoord aInterval, nscoord aOffset) -{ - AddEdgeInterval(aInterval, aScrollRange.x, aScrollRange.XMost(), aOffset, - mDestination.x, mStartPos.x, mScrollingDirection.x, - &mBestEdge.x, &mVerticalEdgeFound); -} - -void -CalcSnapPoints::AddEdge(nscoord aEdge, nscoord aDestination, nscoord aStartPos, - nscoord aScrollingDirection, nscoord* aBestEdge, - bool *aEdgeFound) -{ - // nsIScrollableFrame::DEVICE_PIXELS indicates that we are releasing a drag - // gesture or any other user input event that sets an absolute scroll - // position. In this case, scroll snapping is expected to travel in any - // direction. Otherwise, we will restrict the direction of the scroll - // snapping movement based on aScrollingDirection. - if (mUnit != nsIScrollableFrame::DEVICE_PIXELS) { - // Unless DEVICE_PIXELS, we only want to snap to points ahead of the - // direction we are scrolling - if (aScrollingDirection == 0) { - // The scroll direction is neutral - will not hit a snap point. - return; - } - // nsIScrollableFrame::WHOLE indicates that we are navigating to "home" or - // "end". In this case, we will always select the first or last snap point - // regardless of the direction of the scroll. Otherwise, we will select - // scroll snapping points only in the direction specified by - // aScrollingDirection. - if (mUnit != nsIScrollableFrame::WHOLE) { - // Direction of the edge from the current position (before scrolling) in - // the direction of scrolling - nscoord direction = (aEdge - aStartPos) * aScrollingDirection; - if (direction <= 0) { - // The edge is not in the direction we are scrolling, skip it. - return; - } - } - } - if (!*aEdgeFound) { - *aBestEdge = aEdge; - *aEdgeFound = true; - return; - } - if (mUnit == nsIScrollableFrame::DEVICE_PIXELS || - mUnit == nsIScrollableFrame::LINES) { - if (std::abs(aEdge - aDestination) < std::abs(*aBestEdge - aDestination)) { - *aBestEdge = aEdge; - } - } else if (mUnit == nsIScrollableFrame::PAGES) { - // distance to the edge from the scrolling destination in the direction of scrolling - nscoord overshoot = (aEdge - aDestination) * aScrollingDirection; - // distance to the current best edge from the scrolling destination in the direction of scrolling - nscoord curOvershoot = (*aBestEdge - aDestination) * aScrollingDirection; - - // edges between the current position and the scrolling destination are favoured - // to preserve context - if (overshoot < 0 && (overshoot > curOvershoot || curOvershoot >= 0)) { - *aBestEdge = aEdge; - } - // if there are no edges between the current position and the scrolling destination - // the closest edge beyond the destination is used - if (overshoot > 0 && overshoot < curOvershoot) { - *aBestEdge = aEdge; - } - } else if (mUnit == nsIScrollableFrame::WHOLE) { - // the edge closest to the top/bottom/left/right is used, depending on scrolling direction - if (aScrollingDirection > 0 && aEdge > *aBestEdge) { - *aBestEdge = aEdge; - } else if (aScrollingDirection < 0 && aEdge < *aBestEdge) { - *aBestEdge = aEdge; - } - } else { - NS_ERROR("Invalid scroll mode"); - return; - } -} - -void -CalcSnapPoints::AddEdgeInterval(nscoord aInterval, nscoord aMinPos, - nscoord aMaxPos, nscoord aOffset, - nscoord aDestination, nscoord aStartPos, - nscoord aScrollingDirection, - nscoord* aBestEdge, bool *aEdgeFound) -{ - if (aInterval == 0) { - // When interval is 0, there are no scroll snap points. - // Avoid division by zero and bail. - return; - } - - // The only possible candidate interval snap points are the edges immediately - // surrounding aDestination. - - // aDestination must be clamped to the scroll - // range in order to handle cases where the best matching snap point would - // result in scrolling out of bounds. This clamping must be prior to - // selecting the two interval edges. - nscoord clamped = std::max(std::min(aDestination, aMaxPos), aMinPos); - - // Add each edge in the interval immediately before aTarget and after aTarget - // Do not add edges that are out of range. - nscoord r = (clamped + aOffset) % aInterval; - if (r < aMinPos) { - r += aInterval; - } - nscoord edge = clamped - r; - if (edge >= aMinPos && edge <= aMaxPos) { - AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge, - aEdgeFound); - } - edge += aInterval; - if (edge >= aMinPos && edge <= aMaxPos) { - AddEdge(edge, aDestination, aStartPos, aScrollingDirection, aBestEdge, - aEdgeFound); - } -} - -static void -ProcessScrollSnapCoordinates(SnappingEdgeCallback& aCallback, - const nsTArray& aScrollSnapCoordinates, - const nsPoint& aScrollSnapDestination) { - for (nsPoint snapCoords : aScrollSnapCoordinates) { - // Make them relative to the scroll snap destination. - snapCoords -= aScrollSnapDestination; - - aCallback.AddVerticalEdge(snapCoords.x); - aCallback.AddHorizontalEdge(snapCoords.y); - } -} - /** * Collect the scroll-snap-coordinates of frames in the subtree rooted at * |aFrame|, relative to |aScrolledFrame|, into |aOutCoords|. @@ -6126,51 +5879,14 @@ ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUn nsPoint aStartPos, nsPoint &aDestination) { - ScrollSnapInfo snapInfo = GetScrollSnapInfo(); - - if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_NONE && - snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_NONE) { - return false; + Maybe snapPoint = ScrollSnapUtils::GetSnapPointForDestination( + GetScrollSnapInfo(), aUnit, mScrollPort.Size(), + GetScrollRangeForClamping(), aStartPos, aDestination); + if (snapPoint) { + aDestination = snapPoint.ref(); + return true; } - - nsSize scrollPortSize = mScrollPort.Size(); - nsRect scrollRange = GetScrollRangeForClamping(); - - nsPoint destPos = snapInfo.mScrollSnapDestination; - - CalcSnapPoints calcSnapPoints(aUnit, aDestination, aStartPos); - - if (snapInfo.mScrollSnapIntervalX.isSome()) { - nscoord interval = snapInfo.mScrollSnapIntervalX.value(); - calcSnapPoints.AddVerticalEdgeInterval(scrollRange, interval, destPos.x); - } - if (snapInfo.mScrollSnapIntervalY.isSome()) { - nscoord interval = snapInfo.mScrollSnapIntervalY.value(); - calcSnapPoints.AddHorizontalEdgeInterval(scrollRange, interval, destPos.y); - } - - ProcessScrollSnapCoordinates(calcSnapPoints, snapInfo.mScrollSnapCoordinates, destPos); - bool snapped = false; - nsPoint finalPos = calcSnapPoints.GetBestEdge(); - nscoord proximityThreshold = - Preferences::GetInt("layout.css.scroll-snap.proximity-threshold", 0); - proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold); - if (snapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && - std::abs(aDestination.y - finalPos.y) > proximityThreshold) { - finalPos.y = aDestination.y; - } else { - snapped = true; - } - if (snapInfo.mScrollSnapTypeX == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && - std::abs(aDestination.x - finalPos.x) > proximityThreshold) { - finalPos.x = aDestination.x; - } else { - snapped = true; - } - if (snapped) { - aDestination = finalPos; - } - return snapped; + return false; } bool From 04587ba3aaa47783438dfcd983878b56e25969cc Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Fri, 18 Mar 2016 21:15:45 -0400 Subject: [PATCH 19/53] Bug 1219296 - Move the layout.css.scroll-snap.proximity-threshold pref to gfxPrefs, so it can be queried on the compositor thread. r=kats MozReview-Commit-ID: 6apoF0810Ea --HG-- extra : rebase_source : 7860ad185b7c131ef19faa2befb3a09f75b7ad6e --- gfx/thebes/gfxPrefs.h | 1 + layout/generic/ScrollSnap.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index f2bc80b463c7..ab42c38e957e 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -398,6 +398,7 @@ private: DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f); DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000); DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-sensitivity", ScrollSnapPredictionSensitivity, float, 0.750f); + DECL_GFX_PREF(Live, "layout.css.scroll-snap.proximity-threshold", ScrollSnapProximityThreshold, int32_t, 200); DECL_GFX_PREF(Once, "layout.css.touch_action.enabled", TouchActionEnabled, bool, false); DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false); DECL_GFX_PREF(Live, "layout.event-regions.enabled", LayoutEventRegionsEnabledDoNotUseDirectly, bool, false); diff --git a/layout/generic/ScrollSnap.cpp b/layout/generic/ScrollSnap.cpp index 748e30dafc74..96eb72925908 100644 --- a/layout/generic/ScrollSnap.cpp +++ b/layout/generic/ScrollSnap.cpp @@ -5,6 +5,7 @@ #include "FrameMetrics.h" #include "ScrollSnap.h" +#include "gfxPrefs.h" #include "mozilla/Maybe.h" #include "mozilla/Preferences.h" #include "nsLineLayout.h" @@ -290,8 +291,7 @@ Maybe ScrollSnapUtils::GetSnapPointForDestination( ProcessScrollSnapCoordinates(calcSnapPoints, aSnapInfo.mScrollSnapCoordinates, destPos); bool snapped = false; nsPoint finalPos = calcSnapPoints.GetBestEdge(); - nscoord proximityThreshold = - Preferences::GetInt("layout.css.scroll-snap.proximity-threshold", 0); + nscoord proximityThreshold = gfxPrefs::ScrollSnapProximityThreshold(); proximityThreshold = nsPresContext::CSSPixelsToAppUnits(proximityThreshold); if (aSnapInfo.mScrollSnapTypeY == NS_STYLE_SCROLL_SNAP_TYPE_PROXIMITY && std::abs(aDestination.y - finalPos.y) > proximityThreshold) { From 77cebd9dd20c7ec582f05d984a2b3ffd5057479e Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 30 Mar 2016 17:04:10 -0400 Subject: [PATCH 20/53] Bug 1219296 - Make ScrollMetadata::sNullMetadata a StaticAutoPtr so that ScrollMetadata can admit nsTArray members. r=kats MozReview-Commit-ID: LOZdnAhL5xH --HG-- extra : rebase_source : 39b619a7945e9f3c867dbe67e700e8b83d7c3699 --- gfx/layers/FrameMetrics.cpp | 2 +- gfx/layers/FrameMetrics.h | 3 ++- gfx/layers/LayerMetricsWrapper.h | 8 ++++---- gfx/layers/apz/test/gtest/APZCTreeManagerTester.h | 2 ++ gfx/layers/composite/ContainerLayerComposite.cpp | 3 ++- gfx/tests/gtest/TestLayers.cpp | 12 ++++++++++-- gfx/thebes/gfxPlatform.cpp | 5 +++++ 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/gfx/layers/FrameMetrics.cpp b/gfx/layers/FrameMetrics.cpp index 2ea0b02172ac..3b55cb19736e 100644 --- a/gfx/layers/FrameMetrics.cpp +++ b/gfx/layers/FrameMetrics.cpp @@ -17,7 +17,7 @@ FrameMetrics::SetUsesContainerScrolling(bool aValue) { mUsesContainerScrolling = aValue; } -const ScrollMetadata ScrollMetadata::sNullMetadata; +StaticAutoPtr ScrollMetadata::sNullMetadata; } } diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 22ea892da98e..512e3414b24f 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -14,6 +14,7 @@ #include "mozilla/gfx/Rect.h" // for RoundedIn #include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor #include "mozilla/gfx/Logging.h" // for Log +#include "mozilla/StaticPtr.h" // for StaticAutoPtr #include "mozilla/TimeStamp.h" // for TimeStamp #include "nsString.h" #include "nsStyleCoord.h" // for nsStyleCoord @@ -786,7 +787,7 @@ struct ScrollSnapInfo { struct ScrollMetadata { friend struct IPC::ParamTraits; public: - static const ScrollMetadata sNullMetadata; // We sometimes need an empty metadata + static StaticAutoPtr sNullMetadata; // We sometimes need an empty metadata ScrollMetadata() : mMetrics() diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h index 5051e125522a..e3ddb0a06b0f 100644 --- a/gfx/layers/LayerMetricsWrapper.h +++ b/gfx/layers/LayerMetricsWrapper.h @@ -245,7 +245,7 @@ public: MOZ_ASSERT(IsValid()); if (mIndex >= mLayer->GetScrollMetadataCount()) { - return ScrollMetadata::sNullMetadata; + return *ScrollMetadata::sNullMetadata; } return mLayer->GetScrollMetadata(mIndex); } @@ -451,7 +451,7 @@ public: return aLayer->GetFrameMetrics(i - 1); } } - return ScrollMetadata::sNullMetadata.GetMetrics(); + return ScrollMetadata::sNullMetadata->GetMetrics(); } static const FrameMetrics& BottommostScrollableMetrics(Layer* aLayer) @@ -461,7 +461,7 @@ public: return aLayer->GetFrameMetrics(i); } } - return ScrollMetadata::sNullMetadata.GetMetrics(); + return ScrollMetadata::sNullMetadata->GetMetrics(); } static const FrameMetrics& BottommostMetrics(Layer* aLayer) @@ -469,7 +469,7 @@ public: if (aLayer->GetScrollMetadataCount() > 0) { return aLayer->GetFrameMetrics(0); } - return ScrollMetadata::sNullMetadata.GetMetrics(); + return ScrollMetadata::sNullMetadata->GetMetrics(); } private: diff --git a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h index 81e9d1aa78e3..4a51ed4dbda4 100644 --- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h +++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h @@ -13,11 +13,13 @@ */ #include "APZTestCommon.h" +#include "gfxPlatform.h" class APZCTreeManagerTester : public ::testing::Test { protected: virtual void SetUp() { gfxPrefs::GetSingleton(); + gfxPlatform::GetPlatform(); APZThreadUtils::SetThreadAssertionsEnabled(false); APZThreadUtils::SetControllerThread(MessageLoop::current()); diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index 59593d00b7a7..82b3d66f356c 100755 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -360,7 +360,8 @@ ContainerRenderVR(ContainerT* aContainer, static bool NeedToDrawCheckerboardingForLayer(Layer* aLayer, Color* aOutCheckerboardingColor) { - return (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && + return (aLayer->Manager()->AsyncPanZoomEnabled() && + aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) && aLayer->IsOpaqueForVisibility() && LayerHasCheckerboardingAPZC(aLayer, aOutCheckerboardingColor); } diff --git a/gfx/tests/gtest/TestLayers.cpp b/gfx/tests/gtest/TestLayers.cpp index 30c150020d82..9c98d82c964f 100644 --- a/gfx/tests/gtest/TestLayers.cpp +++ b/gfx/tests/gtest/TestLayers.cpp @@ -352,7 +352,15 @@ TEST(Layers, RepositionChild) { ASSERT_EQ(nullptr, layers[1]->GetNextSibling()); } -TEST(LayerMetricsWrapper, SimpleTree) { +class LayerMetricsWrapperTester : public ::testing::Test { +protected: + virtual void SetUp() { + // This ensures ScrollMetadata::sNullMetadata is initialized. + gfxPlatform::GetPlatform(); + } +}; + +TEST_F(LayerMetricsWrapperTester, SimpleTree) { nsTArray > layers; RefPtr lm; RefPtr root = CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers); @@ -396,7 +404,7 @@ MakeMetadata(FrameMetrics::ViewID aId) { return metadata; } -TEST(LayerMetricsWrapper, MultiFramemetricsTree) { +TEST_F(LayerMetricsWrapperTester, MultiFramemetricsTree) { nsTArray > layers; RefPtr lm; RefPtr root = CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index a759676b7157..b720ed980a4f 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -9,6 +9,7 @@ #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/SharedBufferManagerChild.h" #include "mozilla/layers/ISurfaceAllocator.h" // for GfxMemoryImageReporter +#include "mozilla/ClearOnShutdown.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" @@ -67,6 +68,7 @@ #include "nsILocaleService.h" #include "nsIObserverService.h" #include "nsIScreenManager.h" +#include "FrameMetrics.h" #include "MainThreadUtils.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" @@ -757,6 +759,9 @@ gfxPlatform::Init() SkGraphics::SetFontCacheLimit(skiaCacheSize); } #endif + + ScrollMetadata::sNullMetadata = new ScrollMetadata(); + ClearOnShutdown(&ScrollMetadata::sNullMetadata); } static bool sLayersIPCIsUp = false; From 895380f52c71e2770736320aa0f1a2e5825cacee Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Fri, 11 Mar 2016 22:04:53 -0500 Subject: [PATCH 21/53] Bug 1219296 - Ship scroll snap information to the compositor. r=kats MozReview-Commit-ID: 2aCaAEC5Csu --HG-- extra : rebase_source : 840fa9478c32932c40cf3f9222d14da567f9d41b --- gfx/ipc/GfxMessageUtils.h | 28 +++++++++++++++++++ gfx/layers/FrameMetrics.h | 10 +++++++ gfx/layers/apz/src/AsyncPanZoomController.cpp | 1 + layout/base/nsLayoutUtils.cpp | 2 ++ layout/generic/nsGfxScrollFrame.h | 8 ++++++ layout/generic/nsIScrollableFrame.h | 6 ++++ 6 files changed, 55 insertions(+) diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index d400c3b55cff..857211830633 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -776,6 +776,32 @@ struct ParamTraits } }; +template <> +struct ParamTraits +{ + typedef mozilla::layers::ScrollSnapInfo paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mScrollSnapTypeX); + WriteParam(aMsg, aParam.mScrollSnapTypeY); + WriteParam(aMsg, aParam.mScrollSnapIntervalX); + WriteParam(aMsg, aParam.mScrollSnapIntervalY); + WriteParam(aMsg, aParam.mScrollSnapDestination); + WriteParam(aMsg, aParam.mScrollSnapCoordinates); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeX) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapTypeY) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalX) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapIntervalY) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapDestination) && + ReadParam(aMsg, aIter, &aResult->mScrollSnapCoordinates)); + } +}; + template <> struct ParamTraits { @@ -784,6 +810,7 @@ struct ParamTraits static void Write(Message* aMsg, const paramType& aParam) { WriteParam(aMsg, aParam.mMetrics); + WriteParam(aMsg, aParam.mSnapInfo); WriteParam(aMsg, aParam.mMaskLayerIndex); WriteParam(aMsg, aParam.mClipRect); } @@ -791,6 +818,7 @@ struct ParamTraits static bool Read(const Message* aMsg, void** aIter, paramType* aResult) { return (ReadParam(aMsg, aIter, &aResult->mMetrics) && + ReadParam(aMsg, aIter, &aResult->mSnapInfo) && ReadParam(aMsg, aIter, &aResult->mMaskLayerIndex) && ReadParam(aMsg, aIter, &aResult->mClipRect)); } diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 512e3414b24f..9c5f727fb866 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -791,12 +791,14 @@ public: ScrollMetadata() : mMetrics() + , mSnapInfo() , mMaskLayerIndex() , mClipRect() {} bool operator==(const ScrollMetadata& aOther) const { + // TODO(botond): Should we include mSnapInfo in the comparison? return mMetrics == aOther.mMetrics && mMaskLayerIndex == aOther.mMaskLayerIndex && mClipRect == aOther.mClipRect; @@ -818,6 +820,11 @@ public: FrameMetrics& GetMetrics() { return mMetrics; } const FrameMetrics& GetMetrics() const { return mMetrics; } + void SetSnapInfo(ScrollSnapInfo&& aSnapInfo) { + mSnapInfo = Move(aSnapInfo); + } + const ScrollSnapInfo& GetSnapInfo() const { return mSnapInfo; } + void SetMaskLayerIndex(const Maybe& aIndex) { mMaskLayerIndex = aIndex; } @@ -842,6 +849,9 @@ public: private: FrameMetrics mMetrics; + // Information used to determine where to snap to for a given scroll. + ScrollSnapInfo mSnapInfo; + // An extra clip mask layer to use when compositing a layer with this // FrameMetrics. This is an index into the MetricsMaskLayers array on // the Layer. diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index af9b108c3428..278cfa289e5b 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -3449,6 +3449,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab()); mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount()); mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount()); + mScrollMetadata.SetSnapInfo(ScrollSnapInfo(aScrollMetadata.GetSnapInfo())); mScrollMetadata.SetClipRect(aScrollMetadata.GetClipRect()); mScrollMetadata.SetMaskLayerIndex(aScrollMetadata.GetMaskLayerIndex()); mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot()); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 8a7426915bef..0b891f47d36b 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8668,6 +8668,8 @@ nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame, } metrics.SetUsesContainerScrolling(scrollableFrame->UsesContainerScrolling()); + + metadata.SetSnapInfo(scrollableFrame->GetScrollSnapInfo()); } // If we have the scrollparent being the same as the scroll id, the diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index e12b6b1e08b3..a1452af3c3ab 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -1013,6 +1013,10 @@ public: mHelper.SetZoomableByAPZ(aZoomable); } + ScrollSnapInfo GetScrollSnapInfo() const override { + return mHelper.GetScrollSnapInfo(); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif @@ -1432,6 +1436,10 @@ public: mHelper.TriggerDisplayPortExpiration(); } + ScrollSnapInfo GetScrollSnapInfo() const override { + return mHelper.GetScrollSnapInfo(); + } + #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; #endif diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index e5cf55dac224..efc575170257 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -48,6 +48,7 @@ public: typedef mozilla::CSSIntPoint CSSIntPoint; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; typedef mozilla::layers::FrameMetrics FrameMetrics; + typedef mozilla::layers::ScrollSnapInfo ScrollSnapInfo; NS_DECL_QUERYFRAME_TARGET(nsIScrollableFrame) @@ -470,6 +471,11 @@ public: * own displayport and schedule a timer to do that if it is safe. */ virtual void TriggerDisplayPortExpiration() = 0; + + /** + * Returns information required to determine where to snap to after a scroll. + */ + virtual ScrollSnapInfo GetScrollSnapInfo() const = 0; }; #endif From 3721e5c1ce7c39db53cd36647e9a83090d9af9c7 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 16 Mar 2016 20:58:08 -0400 Subject: [PATCH 22/53] Bug 1219296 - Remove StartSmoothScroll()'s argument, which is no longer used. r=kats MozReview-Commit-ID: DlnaEQLCvlf --HG-- extra : rebase_source : 95cc626bb6260fc11efa71db4e1e66a01d770508 --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 6 ++---- gfx/layers/apz/src/AsyncPanZoomController.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 278cfa289e5b..d273d5cb2ea7 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -709,7 +709,6 @@ private: class SmoothScrollAnimation : public AsyncPanZoomAnimation { public: SmoothScrollAnimation(AsyncPanZoomController& aApzc, - ScrollSource aSource, const nsPoint &aInitialPosition, const nsPoint &aInitialVelocity, const nsPoint& aDestination, double aSpringConstant, @@ -2607,7 +2606,7 @@ void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr); } -void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) { +void AsyncPanZoomController::StartSmoothScroll() { SetState(SMOOTH_SCROLL); nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to @@ -2617,7 +2616,6 @@ void AsyncPanZoomController::StartSmoothScroll(ScrollSource aSource) { nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset()); StartAnimation(new SmoothScrollAnimation(*this, - aSource, initialPosition, initialVelocity, destination, gfxPrefs::ScrollBehaviorSpringConstant(), @@ -3512,7 +3510,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe CSSPoint::ToAppUnits(aLayerMetrics.GetSmoothScrollOffset())); } else { CancelAnimation(); - StartSmoothScroll(ScrollSource::DOM); + StartSmoothScroll(); } } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index d812361f6b37..7f0babc34a46 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -879,7 +879,7 @@ private: // Start an overscroll animation with the given initial velocity. void StartOverscrollAnimation(const ParentLayerPoint& aVelocity); - void StartSmoothScroll(ScrollSource aSource); + void StartSmoothScroll(); // Returns whether overscroll is allowed during an event. bool AllowScrollHandoffInCurrentBlock() const; From 80ebd436cbcde492cc46f985a5f1dcfdad7d51d7 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 16 Mar 2016 21:04:04 -0400 Subject: [PATCH 23/53] Bug 1219296 - Light refactoring to how a smooth scroll is launched inside APZC. r=kats MozReview-Commit-ID: Ay5XXT1808Z --HG-- extra : rebase_source : 1ed2b6c0087b499a077a23116dc477997d13503d --- gfx/layers/apz/src/AsyncPanZoomController.cpp | 45 +++++++++---------- gfx/layers/apz/src/AsyncPanZoomController.h | 2 +- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index d273d5cb2ea7..d11a334a4d82 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -2606,20 +2606,28 @@ void AsyncPanZoomController::HandleSmoothScrollOverscroll(const ParentLayerPoint HandleFlingOverscroll(aVelocity, BuildOverscrollHandoffChain(), nullptr); } -void AsyncPanZoomController::StartSmoothScroll() { - SetState(SMOOTH_SCROLL); - nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); - // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to - // appunits/second - nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), - mY.GetVelocity())) * 1000.0f; - nsPoint destination = CSSPoint::ToAppUnits(mFrameMetrics.GetSmoothScrollOffset()); +void AsyncPanZoomController::SmoothScrollTo(const CSSPoint& aDestination) { + if (mState == SMOOTH_SCROLL && mAnimation) { + APZC_LOG("%p updating destination on existing animation\n", this); + RefPtr animation( + static_cast(mAnimation.get())); + animation->SetDestination(CSSPoint::ToAppUnits(aDestination)); + } else { + CancelAnimation(); + SetState(SMOOTH_SCROLL); + nsPoint initialPosition = CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()); + // Cast velocity from ParentLayerPoints/ms to CSSPoints/ms then convert to + // appunits/second + nsPoint initialVelocity = CSSPoint::ToAppUnits(CSSPoint(mX.GetVelocity(), + mY.GetVelocity())) * 1000.0f; + nsPoint destination = CSSPoint::ToAppUnits(aDestination); - StartAnimation(new SmoothScrollAnimation(*this, - initialPosition, initialVelocity, - destination, - gfxPrefs::ScrollBehaviorSpringConstant(), - gfxPrefs::ScrollBehaviorDampingRatio())); + StartAnimation(new SmoothScrollAnimation(*this, + initialPosition, initialVelocity, + destination, + gfxPrefs::ScrollBehaviorSpringConstant(), + gfxPrefs::ScrollBehaviorDampingRatio())); + } } void AsyncPanZoomController::StartOverscrollAnimation(const ParentLayerPoint& aVelocity) { @@ -3502,16 +3510,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe AcknowledgeScrollUpdate(); mExpectedGeckoMetrics = aLayerMetrics; - if (mState == SMOOTH_SCROLL && mAnimation) { - APZC_LOG("%p updating destination on existing animation\n", this); - RefPtr animation( - static_cast(mAnimation.get())); - animation->SetDestination( - CSSPoint::ToAppUnits(aLayerMetrics.GetSmoothScrollOffset())); - } else { - CancelAnimation(); - StartSmoothScroll(); - } + SmoothScrollTo(mFrameMetrics.GetSmoothScrollOffset()); } if (needContentRepaint) { diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 7f0babc34a46..9fd611976180 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -879,7 +879,7 @@ private: // Start an overscroll animation with the given initial velocity. void StartOverscrollAnimation(const ParentLayerPoint& aVelocity); - void StartSmoothScroll(); + void SmoothScrollTo(const CSSPoint& aDestination); // Returns whether overscroll is allowed during an event. bool AllowScrollHandoffInCurrentBlock() const; From a2ee8407e4d0657210ebf429ed7928b0c1d22ff3 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Mon, 28 Mar 2016 18:36:02 -0400 Subject: [PATCH 24/53] Bug 1219296 - Scroll snap directly in APZ instead of going through the main thread. r=kats MozReview-Commit-ID: 3qAdSWXwOsu --HG-- extra : rebase_source : 1c746af11c620e4bc27d1163980db361c239ae5e --- gfx/layers/FrameMetrics.h | 9 + gfx/layers/apz/src/APZUtils.h | 2 +- gfx/layers/apz/src/AsyncPanZoomController.cpp | 173 ++++++++++-------- gfx/layers/apz/src/AsyncPanZoomController.h | 15 +- layout/base/Units.h | 5 + layout/generic/ScrollSnap.h | 4 + 6 files changed, 126 insertions(+), 82 deletions(-) diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index 9c5f727fb866..b034cb231cb2 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -194,6 +194,15 @@ public: return size; } + CSSRect CalculateScrollRange() const + { + CSSSize scrollPortSize = CalculateCompositedSizeInCssPixels(); + CSSRect scrollRange = mScrollableRect; + scrollRange.width = std::max(scrollRange.width - scrollPortSize.width, 0.0f); + scrollRange.height = std::max(scrollRange.height - scrollPortSize.height, 0.0f); + return scrollRange; + } + void ScrollBy(const CSSPoint& aPoint) { mScrollOffset += aPoint; diff --git a/gfx/layers/apz/src/APZUtils.h b/gfx/layers/apz/src/APZUtils.h index 75565ca06752..03a3ac2622da 100644 --- a/gfx/layers/apz/src/APZUtils.h +++ b/gfx/layers/apz/src/APZUtils.h @@ -25,7 +25,7 @@ enum HitTestResult { enum CancelAnimationFlags : uint32_t { Default = 0x0, /* Cancel all animations */ ExcludeOverscroll = 0x1, /* Don't clear overscroll */ - RequestSnap = 0x2 /* Request snapping to snap points */ + ScrollSnap = 0x2 /* Snap to snap points */ }; inline CancelAnimationFlags diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index d11a334a4d82..bee08b48bcf8 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -70,6 +70,7 @@ #include "nsThreadUtils.h" // for NS_IsMainThread #include "prsystem.h" // for PR_GetPhysicalMemorySize #include "SharedMemoryBasic.h" // for SharedMemoryBasic +#include "ScrollSnap.h" // for ScrollSnapUtils #include "WheelScrollAnimation.h" #define ENABLE_APZC_LOGGING 0 @@ -691,7 +692,13 @@ public: // snap point, so we request one now. If there are no snap points, this will // do nothing. If there are snap points, we'll get a scrollTo that snaps us // back to the nearest valid snap point. - mApzc.RequestSnap(); + // The scroll snapping is done in a deferred task, otherwise the state + // change to NOTHING caused by the overscroll animation ending would + // clobber a possible state change to SMOOTH_SCROLL in ScrollSnap(). + if (!mDeferredTasks.append(NewRunnableMethod(&mApzc, + &AsyncPanZoomController::ScrollSnap))) { + MOZ_CRASH(); + } return false; } return true; @@ -924,7 +931,7 @@ AsyncPanZoomController::Destroy() { APZThreadUtils::AssertOnCompositorThread(); - CancelAnimation(CancelAnimationFlags::RequestSnap); + CancelAnimation(CancelAnimationFlags::ScrollSnap); { // scope the lock MonitorAutoLock lock(mRefPtrMonitor); @@ -1550,6 +1557,20 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent { ReentrantMonitorAutoEnter lock(mMonitor); + ScheduleComposite(); + RequestContentRepaint(); + UpdateSharedCompositorFrameMetrics(); + } + + // Non-negative focus point would indicate that one finger is still down + if (aEvent.mFocusPoint.x != -1 && aEvent.mFocusPoint.y != -1) { + mPanDirRestricted = false; + mX.StartTouch(aEvent.mFocusPoint.x, aEvent.mTime); + mY.StartTouch(aEvent.mFocusPoint.y, aEvent.mTime); + SetState(TOUCHING); + } else { + // Otherwise, handle the fingers being lifted. + ReentrantMonitorAutoEnter lock(mMonitor); // We can get into a situation where we are overscrolled at the end of a // pinch if we go into overscroll with a two-finger pan, and then turn @@ -1565,21 +1586,8 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent ClearOverscroll(); } // Along with clearing the overscroll, we also want to snap to the nearest - // snap point as appropriate, so ask the main thread (which knows about such - // things) to handle it. - RequestSnap(); - - ScheduleComposite(); - RequestContentRepaint(); - UpdateSharedCompositorFrameMetrics(); - } - - // Non-negative focus point would indicate that one finger is still down - if (aEvent.mFocusPoint.x != -1 && aEvent.mFocusPoint.y != -1) { - mPanDirRestricted = false; - mX.StartTouch(aEvent.mFocusPoint.x, aEvent.mTime); - mY.StartTouch(aEvent.mFocusPoint.y, aEvent.mTime); - SetState(TOUCHING); + // snap point as appropriate. + ScrollSnap(); } return nsEventStatus_eConsumeNoDefault; @@ -2003,7 +2011,7 @@ nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) { RequestContentRepaint(); if (!aEvent.mFollowedByMomentum) { - RequestSnap(); + ScrollSnap(); } return nsEventStatus_eConsumeNoDefault; @@ -2018,7 +2026,7 @@ nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& } SetState(PAN_MOMENTUM); - RequestSnapToDestination(); + ScrollSnapToDestination(); // Call into OnPan in order to process any delta included in this event. OnPan(aEvent, false); @@ -2519,51 +2527,18 @@ void AsyncPanZoomController::AcceptFling(FlingHandoffState& aHandoffState) { mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y); aHandoffState.mVelocity.y = 0; } - SetState(FLING); - FlingAnimation *fling = new FlingAnimation(*this, - aHandoffState.mChain, - !aHandoffState.mIsHandoff, // only apply acceleration if this is an initial fling - aHandoffState.mScrolledApzc); - RequestSnapToDestination(); - StartAnimation(fling); -} -void -AsyncPanZoomController::RequestSnapToDestination() -{ - ReentrantMonitorAutoEnter lock(mMonitor); - - float friction = gfxPrefs::APZFlingFriction(); - ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity()); - ParentLayerPoint predictedDelta; - // "-velocity / log(1.0 - friction)" is the integral of the deceleration - // curve modeled for flings in the "Axis" class. - if (velocity.x != 0.0f) { - predictedDelta.x = -velocity.x / log(1.0 - friction); - } - if (velocity.y != 0.0f) { - predictedDelta.y = -velocity.y / log(1.0 - friction); - } - CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom(); - - // If the fling will overscroll, don't request a fling snap, because the - // resulting content scrollTo() would unnecessarily cancel the overscroll - // animation. - bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) || - (velocity.y * mY.GetOverscroll() >= 0)); - if (!flingWillOverscroll) { - RefPtr controller = GetGeckoContentController(); - if (controller) { - APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f " - "predictedDelta: %f, %f position: %f, %f " - "predictedDestination: %f, %f\n", - this, friction, velocity.x, velocity.y, (float)predictedDelta.x, - (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x, - (float)mFrameMetrics.GetScrollOffset().y, - (float)predictedDestination.x, (float)predictedDestination.y); - controller->RequestFlingSnap(mFrameMetrics.GetScrollId(), - predictedDestination); - } + // If there's a scroll snap point near the predicted fling destination, + // scroll there using a smooth scroll animation. Otherwise, start a + // fling animation. + ScrollSnapToDestination(); + if (mState != SMOOTH_SCROLL) { + SetState(FLING); + FlingAnimation *fling = new FlingAnimation(*this, + aHandoffState.mChain, + !aHandoffState.mIsHandoff, // only apply acceleration if this is an initial fling + aHandoffState.mScrolledApzc); + StartAnimation(fling); } } @@ -2708,9 +2683,9 @@ void AsyncPanZoomController::CancelAnimation(CancelAnimationFlags aFlags) { repaint = true; } // Similar to relieving overscroll, we also need to snap to any snap points - // if appropriate, so ask the main thread to do that. - if (aFlags & CancelAnimationFlags::RequestSnap) { - RequestSnap(); + // if appropriate. + if (aFlags & CancelAnimationFlags::ScrollSnap) { + ScrollSnap(); } if (repaint) { RequestContentRepaint(); @@ -2910,7 +2885,7 @@ bool AsyncPanZoomController::SnapBackIfOverscrolled() { // main thread to snap to any nearby snap points, assuming we haven't already // done so when we started this fling if (mState != FLING) { - RequestSnap(); + ScrollSnap(); } return false; } @@ -3752,7 +3727,7 @@ AsyncPanZoomController::CancelAnimationAndGestureState() { mX.CancelGesture(); mY.CancelGesture(); - CancelAnimation(CancelAnimationFlags::RequestSnap); + CancelAnimation(CancelAnimationFlags::ScrollSnap); } bool @@ -3929,12 +3904,62 @@ void AsyncPanZoomController::ShareCompositorFrameMetrics() { } } -void AsyncPanZoomController::RequestSnap() { - if (RefPtr controller = GetGeckoContentController()) { - APZC_LOG("%p requesting snap near %s\n", this, - Stringify(mFrameMetrics.GetScrollOffset()).c_str()); - controller->RequestFlingSnap(mFrameMetrics.GetScrollId(), - mFrameMetrics.GetScrollOffset()); +void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) { + mMonitor.AssertCurrentThreadIn(); + APZC_LOG("%p scroll snapping near %s\n", this, Stringify(aDestination).c_str()); + CSSRect scrollRange = mFrameMetrics.CalculateScrollRange(); + if (Maybe snapPoint = ScrollSnapUtils::GetSnapPointForDestination( + mScrollMetadata.GetSnapInfo(), + nsIScrollableFrame::DEVICE_PIXELS, + CSSSize::ToAppUnits(mFrameMetrics.CalculateCompositedSizeInCssPixels()), + CSSRect::ToAppUnits(scrollRange), + CSSPoint::ToAppUnits(mFrameMetrics.GetScrollOffset()), + CSSPoint::ToAppUnits(aDestination))) { + CSSPoint cssSnapPoint = CSSPoint::FromAppUnits(snapPoint.ref()); + // GetSnapPointForDestination() can produce a destination that's outside + // of the scroll frame's scroll range. Clamp it here (this matches the + // behaviour of the main-thread code path, which clamps it in + // nsGfxScrollFrame::ScrollTo()). + cssSnapPoint = scrollRange.ClampPoint(cssSnapPoint); + SmoothScrollTo(cssSnapPoint); + } +} + +void AsyncPanZoomController::ScrollSnap() { + ReentrantMonitorAutoEnter lock(mMonitor); + ScrollSnapNear(mFrameMetrics.GetScrollOffset()); +} + +void AsyncPanZoomController::ScrollSnapToDestination() { + ReentrantMonitorAutoEnter lock(mMonitor); + + float friction = gfxPrefs::APZFlingFriction(); + ParentLayerPoint velocity(mX.GetVelocity(), mY.GetVelocity()); + ParentLayerPoint predictedDelta; + // "-velocity / log(1.0 - friction)" is the integral of the deceleration + // curve modeled for flings in the "Axis" class. + if (velocity.x != 0.0f) { + predictedDelta.x = -velocity.x / log(1.0 - friction); + } + if (velocity.y != 0.0f) { + predictedDelta.y = -velocity.y / log(1.0 - friction); + } + CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom(); + + // If the fling will overscroll, don't scroll snap, because then the user + // user would not see any overscroll animation. + bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) || + (velocity.y * mY.GetOverscroll() >= 0)); + if (!flingWillOverscroll) { + APZC_LOG("%p fling snapping. friction: %f velocity: %f, %f " + "predictedDelta: %f, %f position: %f, %f " + "predictedDestination: %f, %f\n", + this, friction, velocity.x, velocity.y, (float)predictedDelta.x, + (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x, + (float)mFrameMetrics.GetScrollOffset().y, + (float)predictedDestination.x, (float)predictedDestination.y); + + ScrollSnapNear(predictedDestination); } } diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 9fd611976180..1ebed90d5f26 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -643,13 +643,14 @@ protected: // Common processing at the end of a touch block. void OnTouchEndOrCancel(); - // This is called to request that the main thread snap the scroll position - // to a nearby snap position if appropriate. The current scroll position is - // used as the final destination. - void RequestSnap(); - // Same as above, but takes into account the current velocity to find a - // predicted destination. - void RequestSnapToDestination(); + // Snap to a snap position nearby the current scroll position, if appropriate. + void ScrollSnap(); + // Snap to a snap position nearby the destination predicted based on the + // current velocity, if appropriate. + void ScrollSnapToDestination(); + + // Helper function for ScrollSnap() and ScrollSnapToDestination(). + void ScrollSnapNear(const CSSPoint& aDestination); uint64_t mLayersId; RefPtr mCompositorBridgeParent; diff --git a/layout/base/Units.h b/layout/base/Units.h index 90cc41d9d8db..52c25f3452da 100644 --- a/layout/base/Units.h +++ b/layout/base/Units.h @@ -248,6 +248,11 @@ struct CSSPixel { NSToCoordRoundWithClamp(float(aPoint.y) * float(AppUnitsPerCSSPixel()))); } + static nsSize ToAppUnits(const CSSSize& aSize) { + return nsSize(NSToCoordRoundWithClamp(aSize.width * float(AppUnitsPerCSSPixel())), + NSToCoordRoundWithClamp(aSize.height * float(AppUnitsPerCSSPixel()))); + } + static nsSize ToAppUnits(const CSSIntSize& aSize) { return nsSize(NSToCoordRoundWithClamp(float(aSize.width) * float(AppUnitsPerCSSPixel())), NSToCoordRoundWithClamp(float(aSize.height) * float(AppUnitsPerCSSPixel()))); diff --git a/layout/generic/ScrollSnap.h b/layout/generic/ScrollSnap.h index f58df35cd9ef..a2877564dc96 100644 --- a/layout/generic/ScrollSnap.h +++ b/layout/generic/ScrollSnap.h @@ -22,6 +22,10 @@ struct ScrollSnapUtils { * of the scroll frame for which snapping is being performed. * If a suitable snap point could be found, it is returned. Otherwise, an * empty Maybe is returned. + * IMPORTANT NOTE: This function is designed to be called both on and off + * the main thread. If modifying its implementation, be sure + * not to touch main-thread-only data structures without + * appropriate locking. */ static Maybe GetSnapPointForDestination( const layers::ScrollSnapInfo& aSnapInfo, From 0a249045c18af79aaf7afef1867d6ff19770663f Mon Sep 17 00:00:00 2001 From: Rail Aliiev Date: Wed, 30 Mar 2016 21:26:14 -0400 Subject: [PATCH 25/53] Bug 1260946 - Fix SDK file names r=jlund a=release DONTBUILD MozReview-Commit-ID: HX16hI6slSJ --HG-- extra : rebase_source : 6168e30cfb983e41b10ebf642a94e913fbfc2791 --- testing/mozharness/configs/beetmover/en_us.yml.tmpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/testing/mozharness/configs/beetmover/en_us.yml.tmpl b/testing/mozharness/configs/beetmover/en_us.yml.tmpl index 9be7f95e465c..35b06d873222 100644 --- a/testing/mozharness/configs/beetmover/en_us.yml.tmpl +++ b/testing/mozharness/configs/beetmover/en_us.yml.tmpl @@ -51,7 +51,7 @@ mapping: s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/win32_info.txt sdk: artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.zip - s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.zip + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ version }}.{{ platform }}.sdk.zip mar_tools_mar: artifact: {{ artifact_base_url }}/mar.exe s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/mar-tools/win32/mar.exe @@ -75,7 +75,7 @@ mapping: s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/win64_info.txt sdk: artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.zip - s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.zip + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ version }}.{{ platform }}.sdk.zip mar_tools_mar: artifact: {{ artifact_base_url }}/mar.exe s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/mar-tools/win64/mar.exe @@ -96,7 +96,7 @@ mapping: s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/linux_info.txt sdk: artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.tar.bz2 - s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.tar.bz2 + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ version }}.{{ platform }}.sdk.tar.bz2 mar_tools_mar: artifact: {{ artifact_base_url }}/mar s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/mar-tools/linux/mar @@ -117,7 +117,7 @@ mapping: s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/linux64_info.txt sdk: artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.tar.bz2 - s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}.sdk.tar.bz2 + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ version }}.{{ platform }}.sdk.tar.bz2 mar_tools_mar: artifact: {{ artifact_base_url }}/mar s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/mar-tools/linux64/mar @@ -138,7 +138,7 @@ mapping: s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/macosx64_info.txt sdk: artifact: {{ artifact_base_url }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}-x86_64.sdk.tar.bz2 - s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ app_version }}.{{ locale }}.{{ platform }}-x86_64.sdk.tar.bz2 + s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/firefox-{{ version }}.{{ platform }}-x86_64.sdk.tar.bz2 mar_tools_mar: artifact: {{ artifact_base_url }}/mar s3_key: {{ s3_prefix }}/{{ version }}-candidates/{{ build_num }}/mar-tools/macosx64/mar From 52158769650781df090d16ffc842d4ec3c6577ec Mon Sep 17 00:00:00 2001 From: Rail Aliiev Date: Wed, 30 Mar 2016 10:51:29 -0400 Subject: [PATCH 26/53] Bug 1260415 - Schedule "partner repacks" builder r=coop a=release DONTBUILD Allow passing version and build_number via buildbot properties. This will allow us add buildbot builders without hardcoded parameters depending on release configs. MozReview-Commit-ID: 3zGiCJ5z36X --HG-- extra : rebase_source : 95004a869b40dd72a643c4dbc0b02e96409fb7d8 --- .../scripts/desktop_partner_repacks.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/testing/mozharness/scripts/desktop_partner_repacks.py b/testing/mozharness/scripts/desktop_partner_repacks.py index e2f57ff344ed..0fd034e43837 100755 --- a/testing/mozharness/scripts/desktop_partner_repacks.py +++ b/testing/mozharness/scripts/desktop_partner_repacks.py @@ -21,11 +21,6 @@ from mozharness.mozilla.release import ReleaseMixin from mozharness.base.python import VirtualenvMixin from mozharness.base.log import FATAL -try: - import simplejson as json - assert json -except ImportError: - import json # DesktopPartnerRepacks {{{1 class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin, @@ -45,7 +40,7 @@ class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin, "help": "Version of Firefox to repack", }], [["--build-number", "-n"], { - "dest": "buildnumber", + "dest": "build_number", "help": "Build number of Firefox to repack", }], [["--platform"], { @@ -103,18 +98,23 @@ class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin, **buildscript_kwargs ) - if 'version' not in self.config: - self.fatal("Version (-v) not supplied.") - if 'buildnumber' not in self.config: - self.fatal("Build number (-n) not supplied.") if 'repo_file' not in self.config: self.fatal("repo_file not supplied.") if 'repack_manifests_url' not in self.config: self.fatal("repack_manifests_url not supplied.") def _pre_config_lock(self, rw_config): + self.read_buildbot_config() + if not self.buildbot_config: + self.warning("Skipping buildbot properties overrides") + else: + props = self.buildbot_config["properties"] + for prop in ['version', 'build_number']: + if props.get(prop): + self.info("Overriding %s with %s" % (prop, props[prop])) + self.config[prop] = props.get(prop) + if self.config.get('require_buildprops', False) is True: - self.read_buildbot_config() if not self.buildbot_config: self.fatal("Unable to load properties from file: %s" % self.config.get('buildbot_json_path')) buildbot_props = self.buildbot_config.get('properties', {}) @@ -123,6 +123,11 @@ class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin, self.fatal("No partner specified in buildprops.json.") self.config['partner'] = partner + if 'version' not in self.config: + self.fatal("Version (-v) not supplied.") + if 'build_number' not in self.config: + self.fatal("Build number (-n) not supplied.") + def query_abs_dirs(self): if self.abs_dirs: return self.abs_dirs @@ -176,7 +181,7 @@ class DesktopPartnerRepacks(ReleaseMixin, BuildbotMixin, PurgeMixin, python = self.query_exe("python2.7") repack_cmd = [python, "partner-repacks.py", "-v", self.config['version'], - "-n", self.config['buildnumber']] + "-n", self.config['build_number']] if self.config.get('platform'): repack_cmd.extend(["--platform", self.config['platform']]) if self.config.get('partner'): From eaf4ca74c0735a64cf470e39e2cf84f88e1b616b Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Mon, 14 Mar 2016 17:00:40 -0400 Subject: [PATCH 27/53] Bug 1245092 - Fix beta-only non-local network bustage on reftest, r=me, a=test-only MozReview-Commit-ID: 1N6lsrbMNyI --HG-- extra : commitid : MnX4BePWDx extra : source : f902121e4d1b1c612b6a270743bf4830c8c8dcbf extra : amend_source : 035d59b83e35cd72256ae079cdea5212273c7b82 --- layout/tools/reftest/reftest-preferences.js | 1 + 1 file changed, 1 insertion(+) diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index a3278b027907..29cd882fb8bc 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -109,6 +109,7 @@ user_pref("browser.tabs.remote.autostart.1", false); user_pref("browser.tabs.remote.autostart.2", false); user_pref("startup.homepage_welcome_url", ""); +user_pref("startup.homepage_welcome_url.additional", ""); user_pref("startup.homepage_override_url", ""); user_pref("browser.usedOnWindows10.introURL", ""); From 5753e3da8314bb0522bdbf92819beb6d89faeb06 Mon Sep 17 00:00:00 2001 From: Botond Ballo Date: Wed, 30 Mar 2016 21:11:00 -0400 Subject: [PATCH 28/53] Bug 1259301 - Remove GeckoContentController::RequestFlingSnap(). r=kats MozReview-Commit-ID: DRntzo1hohv --HG-- extra : rebase_source : 5e9c5c43202fb48c5cad5ab95b05fd0bc1be021e --- .../apz/public/GeckoContentController.h | 8 ---- gfx/layers/apz/util/APZCCallbackHelper.cpp | 40 ------------------- gfx/layers/apz/util/APZCCallbackHelper.h | 8 ---- .../apz/util/ChromeProcessController.cpp | 7 ---- gfx/layers/apz/util/ChromeProcessController.h | 2 - gfx/layers/ipc/APZChild.cpp | 8 ---- gfx/layers/ipc/APZChild.h | 3 -- gfx/layers/ipc/PAPZ.ipdl | 1 - gfx/layers/ipc/RemoteContentController.cpp | 18 --------- gfx/layers/ipc/RemoteContentController.h | 3 -- layout/generic/nsGfxScrollFrame.cpp | 6 --- layout/generic/nsGfxScrollFrame.h | 7 ---- layout/generic/nsIScrollableFrame.h | 10 ----- 13 files changed, 121 deletions(-) diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index 650b9ce059cc..6d8023fe9fff 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -36,14 +36,6 @@ public: */ virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) = 0; - /** - * Requests handling of a scroll snapping at the end of a fling gesture for - * the scrollable frame with the given scroll id. aDestination specifies the - * expected landing position of the fling if no snapping were to be performed. - */ - virtual void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) = 0; - /** * Acknowledges the recipt of a scroll offset update for the scrollable * frame with the given scroll id. This is used to maintain consistency diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index d6ed4a53b9d8..87b82b31b065 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -331,46 +331,6 @@ APZCCallbackHelper::InitializeRootDisplayport(nsIPresShell* aPresShell) } } -class FlingSnapEvent : public nsRunnable -{ - typedef mozilla::layers::FrameMetrics::ViewID ViewID; - -public: - FlingSnapEvent(const ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) - : mScrollId(aScrollId) - , mDestination(aDestination) - { - } - - NS_IMETHOD Run() { - MOZ_ASSERT(NS_IsMainThread()); - - nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(mScrollId); - if (sf) { - sf->FlingSnap(mDestination); - } - - return NS_OK; - } - -protected: - ViewID mScrollId; - mozilla::CSSPoint mDestination; -}; - -void -APZCCallbackHelper::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) -{ - nsCOMPtr r1 = new FlingSnapEvent(aScrollId, aDestination); - if (!NS_IsMainThread()) { - NS_DispatchToMainThread(r1); - } else { - r1->Run(); - } -} - class AcknowledgeScrollUpdateEvent : public nsRunnable { typedef mozilla::layers::FrameMetrics::ViewID ViewID; diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h index fd3782cc4307..46dd93ac84be 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -66,14 +66,6 @@ public: given presShell. */ static void InitializeRootDisplayport(nsIPresShell* aPresShell); - /* Tell layout to perform scroll snapping for the scrollable frame with the - * given scroll id. aDestination specifies the expected landing position of - * a current fling or scrolling animation that should be used to select - * the scroll snap point. - */ - static void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination); - /* Tell layout that we received the scroll offset update for the given view ID, so that it accepts future scroll offset updates from APZ. */ static void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp index 14f0faa48004..281f37c8535a 100644 --- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -68,13 +68,6 @@ ChromeProcessController::PostDelayedTask(Task* aTask, int aDelayMs) MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs); } -void -ChromeProcessController::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) -{ - APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination); -} - void ChromeProcessController::AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, const uint32_t& aScrollGeneration) diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h index e6f64130d67f..47b3d3535b8b 100644 --- a/gfx/layers/apz/util/ChromeProcessController.h +++ b/gfx/layers/apz/util/ChromeProcessController.h @@ -40,8 +40,6 @@ public: // GeckoContentController interface virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override; virtual void PostDelayedTask(Task* aTask, int aDelayMs) override; - virtual void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) override; virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, const uint32_t& aScrollGeneration) override; diff --git a/gfx/layers/ipc/APZChild.cpp b/gfx/layers/ipc/APZChild.cpp index f7e00448f846..a8ca20940dad 100644 --- a/gfx/layers/ipc/APZChild.cpp +++ b/gfx/layers/ipc/APZChild.cpp @@ -94,14 +94,6 @@ APZChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) return mBrowser->UpdateFrame(aFrameMetrics); } -bool -APZChild::RecvRequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) -{ - APZCCallbackHelper::RequestFlingSnap(aScrollId, aDestination); - return true; -} - bool APZChild::RecvAcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration) diff --git a/gfx/layers/ipc/APZChild.h b/gfx/layers/ipc/APZChild.h index 1c01c0370232..38c0e48a4f3c 100644 --- a/gfx/layers/ipc/APZChild.h +++ b/gfx/layers/ipc/APZChild.h @@ -28,9 +28,6 @@ public: virtual bool RecvUpdateFrame(const FrameMetrics& frame) override; - virtual bool RecvRequestFlingSnap(const ViewID& aScrollID, - const CSSPoint& aDestination) override; - virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId, const uint32_t& aScrollGeneration) override; diff --git a/gfx/layers/ipc/PAPZ.ipdl b/gfx/layers/ipc/PAPZ.ipdl index 35de6602a8c2..541d27748ba0 100644 --- a/gfx/layers/ipc/PAPZ.ipdl +++ b/gfx/layers/ipc/PAPZ.ipdl @@ -89,7 +89,6 @@ child: // The following methods correspond to functions on the GeckoContentController // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation // in that file for these functions. - async RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination); async AcknowledgeScrollUpdate(ViewID aScrollId, uint32_t aScrollGeneration); async HandleDoubleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid); async HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid, bool aCallTakeFocusForClickFromTap); diff --git a/gfx/layers/ipc/RemoteContentController.cpp b/gfx/layers/ipc/RemoteContentController.cpp index 1710d3c4ecaa..191e5de58e4e 100644 --- a/gfx/layers/ipc/RemoteContentController.cpp +++ b/gfx/layers/ipc/RemoteContentController.cpp @@ -51,24 +51,6 @@ RemoteContentController::RequestContentRepaint(const FrameMetrics& aFrameMetrics } } -void -RemoteContentController::RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) -{ - if (MessageLoop::current() != mUILoop) { - // We have to send this message from the "UI thread" (main - // thread). - mUILoop->PostTask( - FROM_HERE, - NewRunnableMethod(this, &RemoteContentController::RequestFlingSnap, - aScrollId, aDestination)); - return; - } - if (CanSend()) { - Unused << SendRequestFlingSnap(aScrollId, aDestination); - } -} - void RemoteContentController::AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, const uint32_t& aScrollGeneration) diff --git a/gfx/layers/ipc/RemoteContentController.h b/gfx/layers/ipc/RemoteContentController.h index 471104d505f5..56000da0eb02 100644 --- a/gfx/layers/ipc/RemoteContentController.h +++ b/gfx/layers/ipc/RemoteContentController.h @@ -42,9 +42,6 @@ public: // Needs to be called on the main thread. virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics) override; - virtual void RequestFlingSnap(const FrameMetrics::ViewID& aScrollId, - const mozilla::CSSPoint& aDestination) override; - virtual void AcknowledgeScrollUpdate(const FrameMetrics::ViewID& aScrollId, const uint32_t& aScrollGeneration) override; diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index 32fff2655576..da250cf87aad 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3915,12 +3915,6 @@ ScrollFrameHelper::ScrollSnap(nsIScrollableFrame::ScrollMode aMode) ScrollSnap(destinationPos, aMode); } -void -ScrollFrameHelper::FlingSnap(const mozilla::CSSPoint& aDestination) -{ - ScrollSnap(CSSPoint::ToAppUnits(aDestination)); -} - void ScrollFrameHelper::ScrollSnap(const nsPoint &aDestination, nsIScrollableFrame::ScrollMode aMode) diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index a1452af3c3ab..1d217bba1978 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -204,7 +204,6 @@ public: // Get the scroll range assuming the scrollport has size (aWidth, aHeight). nsRect GetScrollRange(nscoord aWidth, nscoord aHeight) const; nsSize GetScrollPositionClampingScrollPortSize() const; - void FlingSnap(const mozilla::CSSPoint& aDestination); void ScrollSnap(nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD); void ScrollSnap(const nsPoint &aDestination, nsIScrollableFrame::ScrollMode aMode = nsIScrollableFrame::SMOOTH_MSD); @@ -841,9 +840,6 @@ public: override { mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum, aSnap); } - virtual void FlingSnap(const mozilla::CSSPoint& aDestination) override { - mHelper.FlingSnap(aDestination); - } virtual void ScrollSnap() override { mHelper.ScrollSnap(); } @@ -1256,9 +1252,6 @@ public: override { mHelper.ScrollBy(aDelta, aUnit, aMode, aOverflow, aOrigin, aMomentum, aSnap); } - virtual void FlingSnap(const mozilla::CSSPoint& aDestination) override { - mHelper.FlingSnap(aDestination); - } virtual void ScrollSnap() override { mHelper.ScrollSnap(); } diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index efc575170257..e45a67640afc 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -270,16 +270,6 @@ public: nsIScrollbarMediator::ScrollSnapMode aSnap = nsIScrollbarMediator::DISABLE_SNAP) = 0; - /** - * Perform scroll snapping, possibly resulting in a smooth scroll to - * maintain the scroll snap position constraints. A predicted landing - * position determined by the APZC is used to select the best matching - * snap point, allowing touchscreen fling gestures to navigate between - * snap points. - * @param aDestination The desired landing position of the fling, which - * is used to select the best matching snap point. - */ - virtual void FlingSnap(const mozilla::CSSPoint& aDestination) = 0; /** * Perform scroll snapping, possibly resulting in a smooth scroll to * maintain the scroll snap position constraints. Velocity sampled from From 6e4140d766943e93f746c30504492c6024752e00 Mon Sep 17 00:00:00 2001 From: David Keeler Date: Tue, 9 Feb 2016 10:14:27 -0800 Subject: [PATCH 29/53] bug 1245280 - add policy mechanism to optionally enforce BRs for falling back to subject CN r=Cykesiopka,mgoodwin MozReview-Commit-ID: 7xT6JGpOH1g --HG-- extra : rebase_source : 0def29e8be898a2d975ee4390b3bc6a193766b1b --- netwerk/base/security-prefs.js | 14 ++ .../certverifier/BRNameMatchingPolicy.cpp | 38 +++++ security/certverifier/BRNameMatchingPolicy.h | 56 ++++++++ security/certverifier/CertVerifier.cpp | 16 ++- security/certverifier/CertVerifier.h | 9 +- security/certverifier/moz.build | 2 + .../manager/ssl/SSLServerCertVerification.cpp | 66 +++++---- security/manager/ssl/SharedCertVerifier.h | 5 +- security/manager/ssl/nsNSSComponent.cpp | 21 ++- security/manager/ssl/tests/unit/moz.build | 1 + security/manager/ssl/tests/unit/pycert.py | 19 ++- .../unit/test_baseline_requirements/ca.pem | 17 +++ .../ca.pem.certspec | 4 + .../unit/test_baseline_requirements/moz.build | 17 +++ .../test_baseline_requirements/no-san-old.pem | 17 +++ .../no-san-old.pem.certspec | 3 + .../no-san-recent.pem | 17 +++ .../no-san-recent.pem.certspec | 3 + .../san-contains-no-hostnames-old.pem | 18 +++ ...san-contains-no-hostnames-old.pem.certspec | 4 + .../san-contains-no-hostnames-recent.pem | 18 +++ ...-contains-no-hostnames-recent.pem.certspec | 4 + ...seline_requirements_subject_common_name.js | 131 ++++++++++++++++++ security/manager/ssl/tests/unit/xpcshell.ini | 2 + security/pkix/include/pkix/pkix.h | 3 +- security/pkix/include/pkix/pkixtypes.h | 24 ++++ security/pkix/lib/pkixnames.cpp | 23 ++- security/pkix/test/gtest/pkixgtest.h | 11 ++ security/pkix/test/gtest/pkixnames_tests.cpp | 45 ++++-- 29 files changed, 549 insertions(+), 59 deletions(-) create mode 100644 security/certverifier/BRNameMatchingPolicy.cpp create mode 100644 security/certverifier/BRNameMatchingPolicy.h create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/moz.build create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec create mode 100644 security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js diff --git a/netwerk/base/security-prefs.js b/netwerk/base/security-prefs.js index bac723d1507d..33de760c7859 100644 --- a/netwerk/base/security-prefs.js +++ b/netwerk/base/security-prefs.js @@ -49,6 +49,20 @@ pref("security.pki.cert_short_lifetime_in_days", 10); // 3 = allow SHA-1 for certificates issued before 2016 or by an imported root. pref("security.pki.sha1_enforcement_level", 3); +// security.pki.name_matching_mode controls how the platform matches hostnames +// to name information in TLS certificates. The possible values are: +// 0: always fall back to the subject common name if necessary (as in, if the +// subject alternative name extension is either not present or does not +// contain any DNS names or IP addresses) +// 1: fall back to the subject common name for certificates valid before 23 +// August 2016 if necessary +// 2: only use name information from the subject alternative name extension +#ifdef RELEASE_BUILD +pref("security.pki.name_matching_mode", 1); +#else +pref("security.pki.name_matching_mode", 2); +#endif + pref("security.webauth.u2f", false); pref("security.webauth.u2f.softtoken", false); pref("security.webauth.u2f.usbtoken", false); diff --git a/security/certverifier/BRNameMatchingPolicy.cpp b/security/certverifier/BRNameMatchingPolicy.cpp new file mode 100644 index 000000000000..47e6a1c2b974 --- /dev/null +++ b/security/certverifier/BRNameMatchingPolicy.cpp @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BRNameMatchingPolicy.h" + +#include "mozilla/Assertions.h" + +using namespace mozilla::psm; +using namespace mozilla::pkix; + +Result +BRNameMatchingPolicy::FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) +{ + // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000 + static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400); + switch (mMode) + { + case Mode::Enforce: + fallBackToCommonName = FallBackToSearchWithinSubject::No; + break; + case Mode::EnforceAfter23August2016: + fallBackToCommonName = notBefore > AUGUST_23_2016 + ? FallBackToSearchWithinSubject::No + : FallBackToSearchWithinSubject::Yes; + break; + case Mode::DoNotEnforce: + fallBackToCommonName = FallBackToSearchWithinSubject::Yes; + break; + default: + MOZ_CRASH("Unexpected Mode"); + } + return Success; +} diff --git a/security/certverifier/BRNameMatchingPolicy.h b/security/certverifier/BRNameMatchingPolicy.h new file mode 100644 index 000000000000..686a1122ad1a --- /dev/null +++ b/security/certverifier/BRNameMatchingPolicy.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRNameMatchingPolicy_h +#define BRNameMatchingPolicy_h + +#include "pkix/pkixtypes.h" + +namespace mozilla { namespace psm { + +// According to the Baseline Requirements version 1.3.3 section 7.1.4.2.2.a, +// the requirements of the subject common name field are as follows: +// "If present, this field MUST contain a single IP address or Fully‐Qualified +// Domain Name that is one of the values contained in the Certificate’s +// subjectAltName extension". Consequently, since any name information present +// in the common name must be present in the subject alternative name extension, +// when performing name matching, it should not be necessary to fall back to the +// common name. Because this consequence has not commonly been enforced, this +// implementation provides a mechanism to start enforcing it gradually while +// maintaining some backwards compatibility. If configured with the mode +// "EnforceAfter23August2016", name matching will only fall back to using the +// subject common name for certificates where the notBefore field is before 23 +// August 2016. +// Note that this implementation does not actually directly enforce that if the +// subject common name is present, its value corresponds to a dNSName or +// iPAddress entry in the subject alternative name extension. + +class BRNameMatchingPolicy : public mozilla::pkix::NameMatchingPolicy +{ +public: + enum class Mode { + DoNotEnforce = 0, + EnforceAfter23August2016 = 1, + Enforce = 2, + }; + + explicit BRNameMatchingPolicy(Mode mode) + : mMode(mode) + { + } + + virtual mozilla::pkix::Result FallBackToCommonName( + mozilla::pkix::Time notBefore, + /*out*/ mozilla::pkix::FallBackToSearchWithinSubject& fallBacktoCommonName) + override; + +private: + Mode mMode; +}; + +} } // namespace mozilla::psm + +#endif // BRNameMatchingPolicy_h diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp index a9ff038b21a6..2f53aa56c23d 100644 --- a/security/certverifier/CertVerifier.cpp +++ b/security/certverifier/CertVerifier.cpp @@ -8,6 +8,7 @@ #include +#include "BRNameMatchingPolicy.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "NSSErrorsService.h" @@ -38,13 +39,15 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, PinningMode pinningMode, - SHA1Mode sha1Mode) + SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode) : mOCSPDownloadConfig(odc) , mOCSPStrict(osc == ocspStrict) , mOCSPGETEnabled(ogc == ocspGetEnabled) , mCertShortLifetimeInDays(certShortLifetimeInDays) , mPinningMode(pinningMode) , mSHA1Mode(sha1Mode) + , mNameMatchingMode(nameMatchingMode) { } @@ -716,7 +719,16 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert, PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } - result = CheckCertHostname(peerCertInput, hostnameInput); + bool isBuiltInRoot; + result = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot); + if (result != Success) { + PR_SetError(MapResultToPRErrorCode(result), 0); + return SECFailure; + } + BRNameMatchingPolicy nameMatchingPolicy( + isBuiltInRoot ? mNameMatchingMode + : BRNameMatchingPolicy::Mode::DoNotEnforce); + result = CheckCertHostname(peerCertInput, hostnameInput, nameMatchingPolicy); if (result != Success) { // Treat malformed name information as a domain mismatch. if (result == Result::ERROR_BAD_DER) { diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h index 8edb1f7422af..d4cbd1db7110 100644 --- a/security/certverifier/CertVerifier.h +++ b/security/certverifier/CertVerifier.h @@ -7,10 +7,11 @@ #ifndef mozilla_psm__CertVerifier_h #define mozilla_psm__CertVerifier_h -#include "mozilla/Telemetry.h" -#include "pkix/pkixtypes.h" +#include "BRNameMatchingPolicy.h" #include "OCSPCache.h" #include "ScopedNSSTypes.h" +#include "mozilla/Telemetry.h" +#include "pkix/pkixtypes.h" namespace mozilla { namespace psm { @@ -121,7 +122,8 @@ public: CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, - PinningMode pinningMode, SHA1Mode sha1Mode); + PinningMode pinningMode, SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode); ~CertVerifier(); void ClearOCSPCache() { mOCSPCache.Clear(); } @@ -132,6 +134,7 @@ public: const uint32_t mCertShortLifetimeInDays; const PinningMode mPinningMode; const SHA1Mode mSHA1Mode; + const BRNameMatchingPolicy::Mode mNameMatchingMode; private: OCSPCache mOCSPCache; diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build index be3548360e92..5cb28c820006 100644 --- a/security/certverifier/moz.build +++ b/security/certverifier/moz.build @@ -5,11 +5,13 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS += [ + 'BRNameMatchingPolicy.h', 'CertVerifier.h', 'OCSPCache.h', ] UNIFIED_SOURCES += [ + 'BRNameMatchingPolicy.cpp', 'CertVerifier.cpp', 'NSSCertDBTrustDomain.cpp', 'OCSPCache.cpp', diff --git a/security/manager/ssl/SSLServerCertVerification.cpp b/security/manager/ssl/SSLServerCertVerification.cpp index 22d0c0631209..57b1a076535b 100644 --- a/security/manager/ssl/SSLServerCertVerification.cpp +++ b/security/manager/ssl/SSLServerCertVerification.cpp @@ -96,41 +96,40 @@ #include -#include "pkix/pkix.h" -#include "pkix/pkixnss.h" +#include "BRNameMatchingPolicy.h" #include "CertVerifier.h" #include "CryptoTask.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" -#include "nsIBadCertListener2.h" -#include "nsICertOverrideService.h" -#include "nsISiteSecurityService.h" -#include "nsNSSComponent.h" -#include "nsNSSIOLayer.h" -#include "nsNSSShutDown.h" - -#include "mozilla/Assertions.h" -#include "mozilla/Mutex.h" -#include "mozilla/Telemetry.h" -#include "mozilla/net/DNS.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/unused.h" -#include "nsIThreadPool.h" -#include "nsISocketProvider.h" -#include "nsXPCOMCIDInternal.h" -#include "nsComponentManagerUtils.h" -#include "nsServiceManagerUtils.h" #include "PSMRunnable.h" #include "RootCertificateTelemetryUtils.h" #include "SharedSSLState.h" -#include "nsContentUtils.h" -#include "nsURLHelper.h" - -#include "ssl.h" #include "cert.h" +#include "mozilla/Assertions.h" +#include "mozilla/Mutex.h" +#include "mozilla/Telemetry.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/net/DNS.h" +#include "mozilla/unused.h" +#include "nsComponentManagerUtils.h" +#include "nsContentUtils.h" +#include "nsIBadCertListener2.h" +#include "nsICertOverrideService.h" +#include "nsISiteSecurityService.h" +#include "nsISocketProvider.h" +#include "nsIThreadPool.h" +#include "nsNSSComponent.h" +#include "nsNSSIOLayer.h" +#include "nsNSSShutDown.h" +#include "nsServiceManagerUtils.h" +#include "nsURLHelper.h" +#include "nsXPCOMCIDInternal.h" +#include "pkix/pkix.h" +#include "pkix/pkixnss.h" #include "secerr.h" #include "secoidt.h" #include "secport.h" +#include "ssl.h" #include "sslerr.h" extern mozilla::LazyLogModule gPIPNSSLog; @@ -432,13 +431,28 @@ DetermineCertOverrideErrors(CERTCertificate* cert, const char* hostName, PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } - result = CheckCertHostname(certInput, hostnameInput); + // Use a lax policy so as to not generate potentially spurious name + // mismatch "hints". + BRNameMatchingPolicy nameMatchingPolicy( + BRNameMatchingPolicy::Mode::DoNotEnforce); + // CheckCertHostname expects that its input represents a certificate that + // has already been successfully validated by BuildCertChain. This is + // obviously not the case, however, because we're in the error path of + // certificate verification. Thus, this is problematic. In the future, it + // would be nice to remove this optimistic additional error checking and + // simply punt to the front-end, which can more easily (and safely) perform + // extra checks to give the user hints as to why verification failed. + result = CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy); // Treat malformed name information as a domain mismatch. if (result == Result::ERROR_BAD_DER || result == Result::ERROR_BAD_CERT_DOMAIN) { collectedErrors |= nsICertOverrideService::ERROR_MISMATCH; errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN; - } else if (result != Success) { + } else if (IsFatalError(result)) { + // Because its input has not been validated by BuildCertChain, + // CheckCertHostname can return an error that is less important than the + // original certificate verification error. Only return an error result + // from this function if we've encountered a fatal error. PR_SetError(MapResultToPRErrorCode(result), 0); return SECFailure; } diff --git a/security/manager/ssl/SharedCertVerifier.h b/security/manager/ssl/SharedCertVerifier.h index 10241d991f47..04957ca3b6da 100644 --- a/security/manager/ssl/SharedCertVerifier.h +++ b/security/manager/ssl/SharedCertVerifier.h @@ -21,9 +21,10 @@ public: SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, OcspGetConfig ogc, uint32_t certShortLifetimeInDays, - PinningMode pinningMode, SHA1Mode sha1Mode) + PinningMode pinningMode, SHA1Mode sha1Mode, + BRNameMatchingPolicy::Mode nameMatchingMode) : mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays, - pinningMode, sha1Mode) + pinningMode, sha1Mode, nameMatchingMode) { } }; diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index ab42b91432a0..4b92283e420d 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -872,6 +872,21 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting, break; default: sha1Mode = CertVerifier::SHA1Mode::Allowed; + break; + } + + BRNameMatchingPolicy::Mode nameMatchingMode = + static_cast + (Preferences::GetInt("security.pki.name_matching_mode", + static_cast(BRNameMatchingPolicy::Mode::DoNotEnforce))); + switch (nameMatchingMode) { + case BRNameMatchingPolicy::Mode::Enforce: + case BRNameMatchingPolicy::Mode::EnforceAfter23August2016: + case BRNameMatchingPolicy::Mode::DoNotEnforce: + break; + default: + nameMatchingMode = BRNameMatchingPolicy::Mode::DoNotEnforce; + break; } CertVerifier::OcspDownloadConfig odc; @@ -883,7 +898,8 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting, lock); mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc, certShortLifetimeInDays, - pinningMode, sha1Mode); + pinningMode, sha1Mode, + nameMatchingMode); } // Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and @@ -1321,7 +1337,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") || prefName.EqualsLiteral("security.ssl.enable_ocsp_must_staple") || prefName.EqualsLiteral("security.cert_pinning.enforcement_level") || - prefName.EqualsLiteral("security.pki.sha1_enforcement_level")) { + prefName.EqualsLiteral("security.pki.sha1_enforcement_level") || + prefName.EqualsLiteral("security.pki.name_matching_mode")) { MutexAutoLock lock(mutex); setValidationOptions(false, lock); #ifdef DEBUG diff --git a/security/manager/ssl/tests/unit/moz.build b/security/manager/ssl/tests/unit/moz.build index 386bb4f556fa..d1d73d2ca8b1 100644 --- a/security/manager/ssl/tests/unit/moz.build +++ b/security/manager/ssl/tests/unit/moz.build @@ -12,6 +12,7 @@ if not CONFIG['MOZ_NO_SMART_CARDS']: TEST_DIRS += [ 'bad_certs', 'ocsp_certs', + 'test_baseline_requirements', 'test_cert_eku', 'test_cert_embedded_null', 'test_cert_keyUsage', diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py index f7aefff0d27e..d5584b00f511 100755 --- a/security/manager/ssl/tests/unit/pycert.py +++ b/security/manager/ssl/tests/unit/pycert.py @@ -29,7 +29,7 @@ keyUsage:[digitalSignature,nonRepudiation,keyEncipherment, extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection nsSGC, # Netscape Server Gated Crypto OCSPSigning,timeStamping] -subjectAlternativeName:[,...] +subjectAlternativeName:[,...] authorityInformationAccess: certificatePolicies: nameConstraints:{permitted,excluded}:[,...] @@ -531,14 +531,19 @@ class Certificate(object): extKeyUsageExtension.setComponentByPosition(count, self.keyPurposeToOID(keyPurpose)) self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension, critical) - def addSubjectAlternativeName(self, dNSNames, critical): + def addSubjectAlternativeName(self, names, critical): subjectAlternativeName = rfc2459.SubjectAltName() - for count, dNSName in enumerate(dNSNames.split(',')): + for count, name in enumerate(names.split(',')): generalName = rfc2459.GeneralName() - # The string may have things like '\0' (i.e. a slash - # followed by the number zero) that have to be decoded into - # the resulting '\x00' (i.e. a byte with value zero). - generalName.setComponentByName('dNSName', dNSName.decode(encoding='string_escape')) + if '/' in name: + directoryName = stringToDN(name, + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4)) + generalName.setComponentByName('directoryName', directoryName) + else: + # The string may have things like '\0' (i.e. a slash + # followed by the number zero) that have to be decoded into + # the resulting '\x00' (i.e. a byte with value zero). + generalName.setComponentByName('dNSName', name.decode(encoding='string_escape')) subjectAlternativeName.setComponentByPosition(count, generalName) self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical) diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem new file mode 100644 index 000000000000..62ff96cb9111 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICxTCCAa+gAwIBAgIUQy+m6w0ZtMTfbmtELQQz8zwqCAowCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw +MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu +Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO +7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf +qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt +HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx +uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud +DwQEAwIBBjALBgkqhkiG9w0BAQsDggEBAJQcekrdR+S6U0I3owUQxVOoUJMzHdTj +u562Ra7cOiJQwe1OQZbvo6rQkQWPrpuDOGpwwr1+HBMGb8mjUqeFo5wIinU003TC +UYYEpDCbPwXOKDkDUukKd1aO4wpJc/v8YIiCz7aCRj9HQ3L5YO5JsgMNSCXKKoUm +ILcz2V+IQZ6lePzFfd2aO3zLMDPwEOyujYYtQnBVZIT4F/x/6nU8E6bkbDSGPjQW +CSVhwa0YQ9lCRSM6e//wGry4i8X8718t1V+Nqh7y6u7UlOrXbNEA4pR6mvJsqPhF +Mj82We4OGNBxXbyuGJObQgLBfmRuwKQT9SNtKWEifiaTw8apT/fBagc= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec new file mode 100644 index 000000000000..6660f5d4783c --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/ca.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:ca +extension:basicConstraints:cA, +extension:keyUsage:cRLSign,keyCertSign diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build new file mode 100644 index 000000000000..a66a115c7530 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Temporarily disabled. See bug 1256495. +#test_certificates = ( +# 'ca.pem', +# 'no-san-old.pem', +# 'no-san-recent.pem', +# 'san-contains-no-hostnames-old.pem', +# 'san-contains-no-hostnames-recent.pem', +#) +# +#for test_certificate in test_certificates: +# GeneratedTestCertificate(test_certificate) diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem new file mode 100644 index 000000000000..4a0032f43419 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrzCCAZmgAwIBAgIUS00fexo4Y4FagP1oiKQiGCJKd/swCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOC +AQEAnooFCIG4D5EQxpe6EcB3gXE2Cj5kMBvO5H+OobaVRd4kIxET+MChwsN2nuwy +xooA/P2M+9W/S3KP/K8L1tlyf/8J2xlr349MhULhCKG7XxpEQZl6sVjaeDLwWrEK +2Ts1jPgCTufdfPYBcO3rb4HSdHNfFlhB98dfgSakXqXzUZ9sz2VxmAeL6tLvspG9 +tH5viWFc+lv1J3/Jtzk79hleDsiIdcjTjC/bM/Y49jkNOBxru4Qrw5AZuveoVy/G +2axz89wBpjLYjI0KtVxCf8dutcVW7UwFKQywmo7QDqha61f0f8I6oCYXsDPK9FPf +WLIrK1TT3YxdhN7QIRu6J8ttBQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec new file mode 100644 index 000000000000..7a34d0758f51 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-old.pem.certspec @@ -0,0 +1,3 @@ +issuer:ca +subject:example.com +validity:20160724-20160924 diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem new file mode 100644 index 000000000000..f901f613bf51 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrzCCAZmgAwIBAgIUWxGwhSb8roUQoLNpJajl0X8jk10wCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwODI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABMAsGCSqGSIb3DQEBCwOC +AQEADs0POeb1wJthEcg1nRYRcPgcNHsV1yVCkHMyfmssA1rgWXsp93PRVaCyZYgI +Dq+8QJtAYpdcChFcEYeGjcjLv49Dh+yiiZPPbRWKJB0y3v+13A74M1q+IQFoQWAh +L5GzAEjApsB1j/csRfDNjIwcP1WApN1iZ2NPxFU+PAIFhcmm+uD2doDtQfpMN9vi +HEg5H1x7JOufOZlN+zbnPK+Ob7N13pFd/P/IO8XhA/X8by4G45oh0deyELf9zVcW +4flslHPYthp4LDEyPvTP52jHn/yTO/m8cxKmCZOuwiw4DWfSVUy6irUs8W5SlfS8 +gI2Ctb+G5YvSffsPYwkUg3Hg7g== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec new file mode 100644 index 000000000000..e38478165c5d --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/no-san-recent.pem.certspec @@ -0,0 +1,3 @@ +issuer:ca +subject:example.com +validity:20160824-20160924 diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem new file mode 100644 index 000000000000..2d6821a7cdad --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC4TCCAcugAwIBAgIUL/Gibj3iILbh9idTh3u9XTSwzqIwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAsBgNVHREEJTAj +pCEwHzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24wCwYJKoZIhvcNAQEL +A4IBAQAtCjzHSdXdAyaC5Qyw77gFwQKECHDAimgze1Nvgkyiv4LJLSuFZ84jnLIL +PM+iRxrxeBOdNy8PNIaDadFb5NoovmdLTG08ZjNjJoXOt5JufIHQrUzrcZy1aP7z +rWXED1QcwyKkoOAOqr5hOZ3pmu67a1vJgjZ8H4dVhfFkmSVGPG/6mTvn9H4N/AEo +K+M7BW1WMnNexsV5mMvlUUdfZP0J3o9oI9msH6uH92xU6jIHpgcm6plXfpOBGQfB +g6EUGD4znDe24ljbaohFATWw5c09kkOQNg/H6DQpb1Vi6k+X62Zgj5UR79zx53e+ +3Wo3Iu+hJUfNwyNk7KVF+r1wsUdA +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec new file mode 100644 index 000000000000..41817bde7514 --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-old.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:example.com +validity:20160724-20160924 +extension:subjectAlternativeName:/O=Example Organization diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem new file mode 100644 index 000000000000..fc8b5b48a37e --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC4TCCAcugAwIBAgIUUwz+d++GVy8L6Lxi9GVnzG6yUCEwCwYJKoZIhvcNAQEL +MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwODI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw +MDBaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo +4NgfvbGcBptuGobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDD +SeTbuUzCa2wO7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFX +kD3SO8XguEgfqDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUx +owyR3bTK9/ytHSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/ +Fosv5fGPmRcxuLP+SSP6clHEMdUDrNoYCjXtjQIDAQABozAwLjAsBgNVHREEJTAj +pCEwHzEdMBsGA1UECgwURXhhbXBsZSBPcmdhbml6YXRpb24wCwYJKoZIhvcNAQEL +A4IBAQCdKXA+1XhcpKdyjeJjEEUm9ptoS8tKJt/vdGUPCByzlD71OJGsiXorTcGY +V2sgbGCmxA8dnxG8bPQMFdAZ2hRjWjZ/Hs18SDbMAONjzgiPlwUWRZldb2Th7WX3 +7a+1uMsT1rEsgmip7FuJjqW0qEyuHFRTt47aK0GJRX42VC5kJVMX8ujl8ucqSSNa +PRh6IPQgIxSchu79weP+YIxMz3GDvNuu6z4QWdkzQrnYqSJpLgNGPAdAxFCgsok3 +5rFNhadGNv6RqrG00HmXPBTTq6T8rQEsbQZQZ4eensb0HxdiQyX4XGnMeEpElTwO +LHziGIW2jBA9v5FJVBCC9QQysNby +-----END CERTIFICATE----- \ No newline at end of file diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec new file mode 100644 index 000000000000..140c2014344c --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements/san-contains-no-hostnames-recent.pem.certspec @@ -0,0 +1,4 @@ +issuer:ca +subject:example.com +validity:20160824-20160924 +extension:subjectAlternativeName:/O=Example Organization diff --git a/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js new file mode 100644 index 000000000000..d85bce740f9d --- /dev/null +++ b/security/manager/ssl/tests/unit/test_baseline_requirements_subject_common_name.js @@ -0,0 +1,131 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 2 -*- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// The preference security.pki.name_matching_mode controls whether or not +// mozilla::pkix will fall back to using a certificate's subject common name +// during name matching. If the Baseline Requirements are followed, fallback +// should not be necessary (because any name information in the subject common +// name should be present in the subject alternative name extension). Due to +// compatibility concerns, the platform can be configured to fall back for +// certificates that are valid before 23 August 2016. Note that for certificates +// issued by an imported root, the platform will fall back if necessary, +// regardless of the value of the preference. + +"use strict"; + +do_get_profile(); // must be called before getting nsIX509CertDB +const gCertDB = Cc["@mozilla.org/security/x509certdb;1"] + .getService(Ci.nsIX509CertDB); + +function certFromFile(certName) { + return constructCertFromFile(`test_baseline_requirements/${certName}.pem`); +} + +function loadCertWithTrust(certName, trustString) { + addCertFromFile(gCertDB, `test_baseline_requirements/${certName}.pem`, + trustString); +} + +function checkCertOn25August2016(cert, expectedResult) { + // (new Date("2016-08-25T00:00:00Z")).getTime() / 1000 + const VALIDATION_TIME = 1472083200; + checkCertErrorGenericAtTime(gCertDB, cert, expectedResult, + certificateUsageSSLServer, VALIDATION_TIME, {}, + "example.com"); +} + +function run_test() { + do_register_cleanup(() => { + Services.prefs.clearUserPref("security.pki.name_matching_mode"); + Services.prefs.clearUserPref("security.test.built_in_root_hash"); + }); + + loadCertWithTrust("ca", "CTu,,"); + + // When verifying a certificate, if the trust anchor is not a built-in root, + // name matching will fall back to using the subject common name if necessary + // (i.e. if there is no subject alternative name extension or it does not + // contain any dNSName or iPAddress entries). Thus, since imported roots are + // not in general treated as built-ins, these should all successfully verify + // regardless of the value of the pref. + Services.prefs.setIntPref("security.pki.name_matching_mode", 0); + do_print("current mode: always fall back, root not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + Services.prefs.setIntPref("security.pki.name_matching_mode", 1); + do_print("current mode: fall back for notBefore < August 23, 2016, root " + + "not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + Services.prefs.setIntPref("security.pki.name_matching_mode", 2); + do_print("current mode: never fall back, root not built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // In debug builds, we can treat an imported root as a built-in, and thus we + // can actually test the different values of the pref. + if (isDebugBuild) { + let root = certFromFile("ca"); + Services.prefs.setCharPref("security.test.built_in_root_hash", + root.sha256Fingerprint); + + // Always fall back if necessary. + Services.prefs.setIntPref("security.pki.name_matching_mode", 0); + do_print("current mode: always fall back, root built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // Only fall back if notBefore < 23 August 2016 + Services.prefs.setIntPref("security.pki.name_matching_mode", 1); + do_print("current mode: fall back for notBefore < August 23, 2016, root " + + "built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("no-san-old"), + PRErrorCodeSuccess); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + PRErrorCodeSuccess); + + // Never fall back. + Services.prefs.setIntPref("security.pki.name_matching_mode", 2); + do_print("current mode: never fall back, root built-in"); + checkCertOn25August2016(certFromFile("no-san-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("no-san-old"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-recent"), + SSL_ERROR_BAD_CERT_DOMAIN); + checkCertOn25August2016(certFromFile("san-contains-no-hostnames-old"), + SSL_ERROR_BAD_CERT_DOMAIN); + } +} diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini index 438ed16585ce..0eb1b02c5be1 100644 --- a/security/manager/ssl/tests/unit/xpcshell.ini +++ b/security/manager/ssl/tests/unit/xpcshell.ini @@ -5,6 +5,7 @@ tags = psm support-files = bad_certs/** ocsp_certs/** + test_baseline_requirements/** test_cert_eku/** test_cert_embedded_null/** test_cert_keyUsage/** @@ -29,6 +30,7 @@ support-files = tlsserver/** [test_add_preexisting_cert.js] +[test_baseline_requirements_subject_common_name.js] [test_cert_blocklist.js] skip-if = buildapp == "b2g" tags = addons psm diff --git a/security/pkix/include/pkix/pkix.h b/security/pkix/include/pkix/pkix.h index ee6746b0fca5..da4f636157bd 100644 --- a/security/pkix/include/pkix/pkix.h +++ b/security/pkix/include/pkix/pkix.h @@ -116,7 +116,8 @@ Result BuildCertChain(TrustDomain& trustDomain, Input cert, // - IP addresses are out of scope of RFC 6125, but this method accepts them for // backward compatibility (see SearchNames in pkixnames.cpp) // - A wildcard in a DNS-ID may only appear as the entirety of the first label. -Result CheckCertHostname(Input cert, Input hostname); +Result CheckCertHostname(Input cert, Input hostname, + NameMatchingPolicy& nameMatchingPolicy); // Construct an RFC-6960-encoded OCSP request, ready for submission to a // responder, for the provided CertID. The request has no extensions. diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h index 1ea9535a1598..3c92b3a8a666 100644 --- a/security/pkix/include/pkix/pkixtypes.h +++ b/security/pkix/include/pkix/pkixtypes.h @@ -349,6 +349,30 @@ protected: void operator=(const TrustDomain&) = delete; }; +enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; + +// Applications control the behavior of matching presented name information from +// a certificate against a reference hostname by implementing the +// NameMatchingPolicy interface. Used in concert with CheckCertHostname. +class NameMatchingPolicy +{ +public: + virtual ~NameMatchingPolicy() { } + + // Given that the certificate in question has a notBefore field with the given + // value, should name matching fall back to searching within the subject + // common name field? + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) = 0; + +protected: + NameMatchingPolicy() { } + + NameMatchingPolicy(const NameMatchingPolicy&) = delete; + void operator=(const NameMatchingPolicy&) = delete; +}; + } } // namespace mozilla::pkix #endif // mozilla_pkix_pkixtypes_h diff --git a/security/pkix/lib/pkixnames.cpp b/security/pkix/lib/pkixnames.cpp index 01e36517342e..ca9b8d7d3770 100644 --- a/security/pkix/lib/pkixnames.cpp +++ b/security/pkix/lib/pkixnames.cpp @@ -114,8 +114,6 @@ ReadGeneralName(Reader& reader, return Success; } -enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; - enum class MatchResult { NoNamesOfGivenType = 0, @@ -219,7 +217,8 @@ MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID, // assumed to be a string representation of an IPv4 address, an IPv6 addresss, // or a normalized ASCII (possibly punycode) DNS name. Result -CheckCertHostname(Input endEntityCertDER, Input hostname) +CheckCertHostname(Input endEntityCertDER, Input hostname, + NameMatchingPolicy& nameMatchingPolicy) { BackCert cert(endEntityCertDER, EndEntityOrCA::MustBeEndEntity, nullptr); Result rv = cert.Init(); @@ -227,10 +226,22 @@ CheckCertHostname(Input endEntityCertDER, Input hostname) return rv; } + Time notBefore(Time::uninitialized); + rv = ParseValidity(cert.GetValidity(), ¬Before); + if (rv != Success) { + return rv; + } + FallBackToSearchWithinSubject fallBackToSearchWithinSubject; + rv = nameMatchingPolicy.FallBackToCommonName(notBefore, + fallBackToSearchWithinSubject); + if (rv != Success) { + return rv; + } + const Input* subjectAltName(cert.GetSubjectAltName()); Input subject(cert.GetSubject()); - // For backward compatibility with legacy certificates, we fall back to + // For backward compatibility with legacy certificates, we may fall back to // searching for a name match in the subject common name for DNS names and // IPv4 addresses. We don't do so for IPv6 addresses because we do not think // there are many certificates that would need such fallback, and because @@ -244,13 +255,13 @@ CheckCertHostname(Input endEntityCertDER, Input hostname) uint8_t ipv4[4]; if (IsValidReferenceDNSID(hostname)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::dNSName, - hostname, FallBackToSearchWithinSubject::Yes, match); + hostname, fallBackToSearchWithinSubject, match); } else if (ParseIPv6Address(hostname, ipv6)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, Input(ipv6), FallBackToSearchWithinSubject::No, match); } else if (ParseIPv4Address(hostname, ipv4)) { rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, - Input(ipv4), FallBackToSearchWithinSubject::Yes, match); + Input(ipv4), fallBackToSearchWithinSubject, match); } else { return Result::ERROR_BAD_CERT_DOMAIN; } diff --git a/security/pkix/test/gtest/pkixgtest.h b/security/pkix/test/gtest/pkixgtest.h index 5e7fa5a55545..77dba8e35804 100644 --- a/security/pkix/test/gtest/pkixgtest.h +++ b/security/pkix/test/gtest/pkixgtest.h @@ -217,6 +217,17 @@ class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain } }; +class DefaultNameMatchingPolicy : public NameMatchingPolicy +{ +public: + virtual Result FallBackToCommonName( + Time, /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override + { + fallBackToCommonName = FallBackToSearchWithinSubject::Yes; + return Success; + } +}; + } } } // namespace mozilla::pkix::test #endif // mozilla_pkix_pkixgtest_h diff --git a/security/pkix/test/gtest/pkixnames_tests.cpp b/security/pkix/test/gtest/pkixnames_tests.cpp index 7935032c1809..9e2303ad0e15 100644 --- a/security/pkix/test/gtest/pkixnames_tests.cpp +++ b/security/pkix/test/gtest/pkixnames_tests.cpp @@ -902,6 +902,8 @@ class pkixnames_MatchPresentedDNSIDWithReferenceDNSID : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_MatchPresentedDNSIDWithReferenceDNSID, @@ -937,6 +939,8 @@ class pkixnames_Turkish_I_Comparison : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_Turkish_I_Comparison, MatchPresentedDNSIDWithReferenceDNSID) @@ -982,6 +986,8 @@ class pkixnames_IsValidReferenceDNSID : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_IsValidReferenceDNSID, IsValidReferenceDNSID) @@ -1006,6 +1012,8 @@ class pkixnames_ParseIPv4Address : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_ParseIPv4Address, ParseIPv4Address) @@ -1032,6 +1040,8 @@ class pkixnames_ParseIPv6Address : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_ParseIPv6Address, ParseIPv6Address) @@ -1074,6 +1084,8 @@ class pkixnames_CheckCertHostname : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; #define WITH_SAN(r, ps, psan, result) \ @@ -1578,7 +1590,8 @@ TEST_P(pkixnames_CheckCertHostname, CheckCertHostname) ASSERT_EQ(Success, hostnameInput.Init(param.hostname.data(), param.hostname.length())); - ASSERT_EQ(param.result, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(param.result, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname, @@ -1610,13 +1623,15 @@ TEST_F(pkixnames_CheckCertHostname, SANWithoutSequence) static const uint8_t a[] = { 'a' }; ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID, - CheckCertHostname(certInput, Input(a))); + CheckCertHostname(certInput, Input(a), mNameMatchingPolicy)); } class pkixnames_CheckCertHostname_PresentedMatchesReference : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, CN_NoSAN) @@ -1636,7 +1651,7 @@ TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, CN_NoSAN) param.referenceDNSID.length())); ASSERT_EQ(param.expectedMatches ? Success : Result::ERROR_BAD_CERT_DOMAIN, - CheckCertHostname(certInput, hostnameInput)); + CheckCertHostname(certInput, hostnameInput, mNameMatchingPolicy)); } TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, @@ -1660,7 +1675,8 @@ TEST_P(pkixnames_CheckCertHostname_PresentedMatchesReference, = param.expectedResult != Success ? param.expectedResult : param.expectedMatches ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname_DNSID_MATCH_PARAMS, @@ -1689,8 +1705,10 @@ TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_CN_NoSAN) ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I)); - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I, + mNameMatchingPolicy)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I, + mNameMatchingPolicy)); } TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_SAN) @@ -1716,14 +1734,18 @@ TEST_P(pkixnames_Turkish_I_Comparison, CheckCertHostname_SAN) InputsAreEqual(UPPERCASE_I, input)) ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I)); - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, UPPERCASE_I, + mNameMatchingPolicy)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, LOWERCASE_I, + mNameMatchingPolicy)); } class pkixnames_CheckCertHostname_IPV4_Addresses : public ::testing::Test , public ::testing::WithParamInterface> { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, @@ -1745,7 +1767,7 @@ TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, param.input.length())); ASSERT_EQ(param.isValid ? Success : Result::ERROR_BAD_CERT_DOMAIN, - CheckCertHostname(certInput, hostnameInput)); + CheckCertHostname(certInput, hostnameInput, mNameMatchingPolicy)); } TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, @@ -1772,7 +1794,8 @@ TEST_P(pkixnames_CheckCertHostname_IPV4_Addresses, ? Success : Result::ERROR_BAD_CERT_DOMAIN; - ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput)); + ASSERT_EQ(expectedResult, CheckCertHostname(certInput, hostnameInput, + mNameMatchingPolicy)); } INSTANTIATE_TEST_CASE_P(pkixnames_CheckCertHostname_IPV4_ADDRESSES, @@ -2572,6 +2595,8 @@ class pkixnames_CheckNameConstraints : public ::testing::Test , public ::testing::WithParamInterface { +public: + DefaultNameMatchingPolicy mNameMatchingPolicy; }; TEST_P(pkixnames_CheckNameConstraints, From 481a70b111d93410ee97fdcd9faad0d9f873d529 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Fri, 1 Apr 2016 22:08:04 +0100 Subject: [PATCH 30/53] Bug 1158500 - make writing-mode a mapped CSS property. r=cam --- dom/base/nsGkAtomList.h | 8 +------- dom/base/nsTreeSanitizer.cpp | 14 +++++++------- dom/svg/nsSVGElement.cpp | 4 +--- layout/reftests/svg/text/reftest.list | 3 +++ layout/reftests/svg/text/vertical-01-ref.svg | 12 ++++++++++++ layout/reftests/svg/text/vertical-01.svg | 10 ++++++++++ 6 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 layout/reftests/svg/text/vertical-01-ref.svg create mode 100644 layout/reftests/svg/text/vertical-01.svg diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index b9fd89fa4c02..c68f0f419513 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -1364,7 +1364,6 @@ GK_ATOM(cy, "cy") GK_ATOM(d, "d") GK_ATOM(darken, "darken") GK_ATOM(defs, "defs") -GK_ATOM(definition_src, "definition-src") GK_ATOM(deg, "deg") GK_ATOM(desc, "desc") GK_ATOM(diffuseConstant, "diffuseConstant") @@ -1438,15 +1437,11 @@ GK_ATOM(g, "g") GK_ATOM(gamma, "gamma") // 'generic' conflicts with msvc11 winrt compiler extensions GK_ATOM(generic_, "generic") -GK_ATOM(glyph, "glyph") GK_ATOM(glyphRef, "glyphRef") -GK_ATOM(glyph_orientation_horizontal, "glyph-orientation-horizontal") -GK_ATOM(glyph_orientation_vertical, "glyph-orientation-vertical") GK_ATOM(grad, "grad") GK_ATOM(gradientTransform, "gradientTransform") GK_ATOM(gradientUnits, "gradientUnits") GK_ATOM(hardLight, "hard-light") -GK_ATOM(hkern, "hkern") GK_ATOM(hue, "hue") GK_ATOM(hueRotate, "hueRotate") GK_ATOM(identity, "identity") @@ -1458,7 +1453,6 @@ GK_ATOM(k1, "k1") GK_ATOM(k2, "k2") GK_ATOM(k3, "k3") GK_ATOM(k4, "k4") -GK_ATOM(kerning, "kerning") GK_ATOM(kernelMatrix, "kernelMatrix") GK_ATOM(kernelUnitLength, "kernelUnitLength") GK_ATOM(lengthAdjust, "lengthAdjust") @@ -1587,9 +1581,9 @@ GK_ATOM(userSpaceOnUse, "userSpaceOnUse") GK_ATOM(view, "view") GK_ATOM(viewBox, "viewBox") GK_ATOM(viewTarget, "viewTarget") -GK_ATOM(vkern, "vkern") GK_ATOM(white_space, "white-space") GK_ATOM(word_spacing, "word-spacing") +GK_ATOM(writing_mode, "writing-mode") GK_ATOM(x, "x") GK_ATOM(x1, "x1") GK_ATOM(x2, "x2") diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index 44faeb2cdd08..7ce087b44fe3 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -330,9 +330,9 @@ nsIAtom** const kElementsSVG[] = { &nsGkAtoms::font_face_uri, // font-face-uri &nsGkAtoms::foreignObject, // foreignObject &nsGkAtoms::g, // g - &nsGkAtoms::glyph, // glyph + // glyph &nsGkAtoms::glyphRef, // glyphRef - &nsGkAtoms::hkern, // hkern + // hkern &nsGkAtoms::image, // image &nsGkAtoms::line, // line &nsGkAtoms::linearGradient, // linearGradient @@ -358,7 +358,7 @@ nsIAtom** const kElementsSVG[] = { &nsGkAtoms::tspan, // tspan &nsGkAtoms::use, // use &nsGkAtoms::view, // view - &nsGkAtoms::vkern, // vkern + // vkern nullptr }; @@ -431,8 +431,8 @@ nsIAtom** const kAttributesSVG[] = { // g2 // glyph-name // glyphRef - &nsGkAtoms::glyph_orientation_horizontal, // glyph-orientation-horizontal - &nsGkAtoms::glyph_orientation_vertical, // glyph-orientation-vertical + // glyph-orientation-horizontal + // glyph-orientation-vertical &nsGkAtoms::gradientTransform, // gradientTransform &nsGkAtoms::gradientUnits, // gradientUnits &nsGkAtoms::height, // height @@ -450,7 +450,7 @@ nsIAtom** const kAttributesSVG[] = { &nsGkAtoms::k2, // k2 &nsGkAtoms::k3, // k3 &nsGkAtoms::k4, // k4 - &nsGkAtoms::kerning, // kerning + // kerning &nsGkAtoms::kernelMatrix, // kernelMatrix &nsGkAtoms::kernelUnitLength, // kernelUnitLength &nsGkAtoms::keyPoints, // keyPoints @@ -580,7 +580,7 @@ nsIAtom** const kAttributesSVG[] = { &nsGkAtoms::width, // width // widths &nsGkAtoms::word_spacing, // word-spacing - // writing-mode + &nsGkAtoms::writing_mode, // writing-mode &nsGkAtoms::x, // x // x-height &nsGkAtoms::x1, // x1 diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index 720d0bb0d370..1605719f75a6 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -995,14 +995,12 @@ nsSVGElement::sTextContentElementsMap[] = { // { &nsGkAtoms::baseline_shift }, { &nsGkAtoms::direction }, { &nsGkAtoms::dominant_baseline }, - // { &nsGkAtoms::glyph_orientation_horizontal }, - // { &nsGkAtoms::glyph_orientation_vertical }, - // { &nsGkAtoms::kerning }, { &nsGkAtoms::letter_spacing }, { &nsGkAtoms::text_anchor }, { &nsGkAtoms::text_decoration }, { &nsGkAtoms::unicode_bidi }, { &nsGkAtoms::word_spacing }, + { &nsGkAtoms::writing_mode }, { nullptr } }; diff --git a/layout/reftests/svg/text/reftest.list b/layout/reftests/svg/text/reftest.list index 370734127c02..1ecae3151ce4 100644 --- a/layout/reftests/svg/text/reftest.list +++ b/layout/reftests/svg/text/reftest.list @@ -130,6 +130,9 @@ fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.[12]/ # text-shadow == text-shadow.svg text-shadow-ref.svg +# vertical text +pref(layout.css.vertical-text.enabled,true) == vertical-01.svg vertical-01-ref.svg + # tests for ignoring various properties == ignore-border.svg ignore-prop-ref.svg == ignore-display.svg ignore-display-ref.svg diff --git a/layout/reftests/svg/text/vertical-01-ref.svg b/layout/reftests/svg/text/vertical-01-ref.svg new file mode 100644 index 000000000000..31989c426592 --- /dev/null +++ b/layout/reftests/svg/text/vertical-01-ref.svg @@ -0,0 +1,12 @@ + + + + + + + A B C + + diff --git a/layout/reftests/svg/text/vertical-01.svg b/layout/reftests/svg/text/vertical-01.svg new file mode 100644 index 000000000000..a5fe02af0ac8 --- /dev/null +++ b/layout/reftests/svg/text/vertical-01.svg @@ -0,0 +1,10 @@ + + + + A B C + + From 569b24a16f44e82d7d34097593b2bf9e0d6aeae4 Mon Sep 17 00:00:00 2001 From: gasolin Date: Tue, 29 Mar 2016 15:55:58 +0800 Subject: [PATCH 31/53] Bug 969443 - Update CustomizableUI tests to yield on CustomizeMode.reset; r=Gijs MozReview-Commit-ID: 93fLK4thrgH --HG-- extra : rebase_source : 8db50229beb2e099f1e47169a53d4e7107dbef0f --- .../browser_1089591_still_customizable_after_reset.js | 3 +-- ..._923857_customize_mode_event_wrapping_during_reset.js | 3 +-- .../test/browser_938980_navbar_collapsed.js | 9 +++------ .../test/browser_970511_undo_restore_default.js | 6 +++--- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js b/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js index a18ff826a337..1f502e8e25a3 100644 --- a/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js +++ b/browser/components/customizableui/test/browser_1089591_still_customizable_after_reset.js @@ -8,8 +8,7 @@ add_task(function* () { ok(historyButton && devButton, "Draggable elements should exist"); simulateItemDrag(historyButton, devButton); - gCustomizeMode.reset(); - yield waitForCondition(() => !gCustomizeMode.resetting); + yield gCustomizeMode.reset(); ok(CustomizableUI.inDefaultState, "Should be back in default state"); historyButton = document.getElementById("wrapper-history-panelmenu"); diff --git a/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js index 468627808ded..87aca51eb4fc 100644 --- a/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js +++ b/browser/components/customizableui/test/browser_923857_customize_mode_event_wrapping_during_reset.js @@ -14,8 +14,7 @@ add_task(function*() { ok(devButton && downloadsButton && searchBox && palette, "Stuff should exist"); simulateItemDrag(devButton, downloadsButton); simulateItemDrag(searchBox, palette); - gCustomizeMode.reset(); - yield waitForCondition(() => !gCustomizeMode.resetting); + yield gCustomizeMode.reset(); ok(CustomizableUI.inDefaultState, "Should be back in default state"); yield endCustomizing(); }); diff --git a/browser/components/customizableui/test/browser_938980_navbar_collapsed.js b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js index 18b71ec02132..fc7fa1a0a865 100644 --- a/browser/components/customizableui/test/browser_938980_navbar_collapsed.js +++ b/browser/components/customizableui/test/browser_938980_navbar_collapsed.js @@ -37,8 +37,7 @@ add_task(function*() { is(CustomizableUI.inDefaultState, false, "Should no longer be in default state"); yield startCustomizing(); - gCustomizeMode.reset(); - yield waitForCondition(() => !gCustomizeMode.resetting); + yield gCustomizeMode.reset(); yield endCustomizing(); is(bookmarksToolbar.collapsed, true, "Customization reset should restore collapsed-state to the bookmarks toolbar"); @@ -61,8 +60,7 @@ add_task(function*() { isnot(menubar.getBoundingClientRect().height, 0, "menubar should be visible now"); yield startCustomizing(); - gCustomizeMode.reset(); - yield waitForCondition(() => !gCustomizeMode.resetting); + yield gCustomizeMode.reset(); is(menubar.getAttribute("autohide"), "true", "The menubar should have autohide=true after reset in customization mode"); is(menubar.getBoundingClientRect().height, 0, "The menubar should have height=0 after reset in customization mode"); @@ -89,8 +87,7 @@ add_task(function*() { ok(!navbar.collapsed, "The navbar should be visible before reset"); ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed"); - gCustomizeMode.reset(); - yield waitForCondition(() => !gCustomizeMode.resetting); + yield gCustomizeMode.reset(); ok(bookmarksToolbar.collapsed, "The bookmarksToolbar should be collapsed after reset"); ok(!tabsToolbar.collapsed, "TabsToolbar should not be collapsed"); diff --git a/browser/components/customizableui/test/browser_970511_undo_restore_default.js b/browser/components/customizableui/test/browser_970511_undo_restore_default.js index 88157e92e493..cf32546804e3 100644 --- a/browser/components/customizableui/test/browser_970511_undo_restore_default.js +++ b/browser/components/customizableui/test/browser_970511_undo_restore_default.js @@ -21,9 +21,9 @@ add_task(function*() { ok(CustomizableUI.inDefaultState, "In default state after reset"); is(undoResetButton.hidden, false, "The undo button is visible after reset"); - undoResetButton.click(); - yield waitForCondition(() => !gCustomizeMode.resetting); - ok(!CustomizableUI.inDefaultState, "Not in default state after reset-undo"); + yield gCustomizeMode.undoReset() + + ok(!CustomizableUI.inDefaultState, "Not in default state after undo-reset"); is(undoResetButton.hidden, true, "The undo button is hidden after clicking on the undo button"); is(CustomizableUI.getPlacementOfWidget(homeButtonId), null, "Home button is in palette"); From 9825c57bc32c275bec813481468aded9e513d37f Mon Sep 17 00:00:00 2001 From: David Keeler Date: Tue, 12 Jan 2016 15:39:43 -0800 Subject: [PATCH 32/53] bug 1239166 - platform work to support Microsoft Family Safety functionality r=froydnj,mgoodwin,mhowell,rbarnes,vladan MozReview-Commit-ID: GhpJqJB97r9 --HG-- extra : rebase_source : e943c1e4d0f008ffd6b6bb4bb63e1daf27ae2c96 --- mfbt/WindowsVersion.h | 6 + security/manager/ssl/nsNSSCertificate.cpp | 17 +- security/manager/ssl/nsNSSCertificate.h | 4 + security/manager/ssl/nsNSSCertificateDB.cpp | 39 +- security/manager/ssl/nsNSSCertificateDB.h | 5 + security/manager/ssl/nsNSSComponent.cpp | 445 ++++++++++++++++++- toolkit/components/telemetry/Histograms.json | 8 + 7 files changed, 504 insertions(+), 20 deletions(-) diff --git a/mfbt/WindowsVersion.h b/mfbt/WindowsVersion.h index 72eda2c48ded..4156f4f64baa 100644 --- a/mfbt/WindowsVersion.h +++ b/mfbt/WindowsVersion.h @@ -171,6 +171,12 @@ IsWin8OrLater() return IsWindowsVersionOrLater(0x06020000ul); } +MOZ_ALWAYS_INLINE bool +IsWin8Point1OrLater() +{ + return IsWindowsVersionOrLater(0x06030000ul); +} + MOZ_ALWAYS_INLINE bool IsWin10OrLater() { diff --git a/security/manager/ssl/nsNSSCertificate.cpp b/security/manager/ssl/nsNSSCertificate.cpp index 3346aadf73de..f584cc223d49 100644 --- a/security/manager/ssl/nsNSSCertificate.cpp +++ b/security/manager/ssl/nsNSSCertificate.cpp @@ -513,7 +513,12 @@ nsNSSCertificate::GetDbKey(nsACString& aDbKey) if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } + return GetDbKey(mCert, aDbKey); +} +nsresult +nsNSSCertificate::GetDbKey(CERTCertificate* cert, nsACString& aDbKey) +{ static_assert(sizeof(uint64_t) == 8, "type size sanity check"); static_assert(sizeof(uint32_t) == 4, "type size sanity check"); // The format of the key is the base64 encoding of the following: @@ -528,14 +533,14 @@ nsNSSCertificate::GetDbKey(nsACString& aDbKey) nsAutoCString buf; const char leadingZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0}; buf.Append(leadingZeroes, sizeof(leadingZeroes)); - uint32_t serialNumberLen = htonl(mCert->serialNumber.len); + uint32_t serialNumberLen = htonl(cert->serialNumber.len); buf.Append(reinterpret_cast(&serialNumberLen), sizeof(uint32_t)); - uint32_t issuerLen = htonl(mCert->derIssuer.len); + uint32_t issuerLen = htonl(cert->derIssuer.len); buf.Append(reinterpret_cast(&issuerLen), sizeof(uint32_t)); - buf.Append(reinterpret_cast(mCert->serialNumber.data), - mCert->serialNumber.len); - buf.Append(reinterpret_cast(mCert->derIssuer.data), - mCert->derIssuer.len); + buf.Append(reinterpret_cast(cert->serialNumber.data), + cert->serialNumber.len); + buf.Append(reinterpret_cast(cert->derIssuer.data), + cert->derIssuer.len); return Base64Encode(buf, aDbKey); } diff --git a/security/manager/ssl/nsNSSCertificate.h b/security/manager/ssl/nsNSSCertificate.h index 621df9a3fbdc..519f26327826 100644 --- a/security/manager/ssl/nsNSSCertificate.h +++ b/security/manager/ssl/nsNSSCertificate.h @@ -53,6 +53,10 @@ public: ev_status_unknown = 2 }; + // This is a separate static method so nsNSSComponent can use it during NSS + // initialization. Other code should probably not use it. + static nsresult GetDbKey(CERTCertificate* cert, nsACString& aDbKey); + private: virtual ~nsNSSCertificate(); diff --git a/security/manager/ssl/nsNSSCertificateDB.cpp b/security/manager/ssl/nsNSSCertificateDB.cpp index 2a31ba5932cb..c45bb45a2b7e 100644 --- a/security/manager/ssl/nsNSSCertificateDB.cpp +++ b/security/manager/ssl/nsNSSCertificateDB.cpp @@ -128,10 +128,10 @@ nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname, } NS_IMETHODIMP -nsNSSCertificateDB::FindCertByDBKey(const char* aDBkey,nsIX509Cert** _cert) +nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert) { - NS_ENSURE_ARG_POINTER(aDBkey); - NS_ENSURE_ARG(aDBkey[0]); + NS_ENSURE_ARG_POINTER(aDBKey); + NS_ENSURE_ARG(aDBKey[0]); NS_ENSURE_ARG_POINTER(_cert); *_cert = nullptr; @@ -140,6 +140,27 @@ nsNSSCertificateDB::FindCertByDBKey(const char* aDBkey,nsIX509Cert** _cert) return NS_ERROR_NOT_AVAILABLE; } + UniqueCERTCertificate cert; + nsresult rv = FindCertByDBKey(aDBKey, cert); + if (NS_FAILED(rv)) { + return rv; + } + // If we can't find the certificate, that's not an error. Just return null. + if (!cert) { + return NS_OK; + } + nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); + if (!nssCert) { + return NS_ERROR_OUT_OF_MEMORY; + } + nssCert.forget(_cert); + return NS_OK; +} + +nsresult +nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey, + UniqueCERTCertificate& cert) +{ static_assert(sizeof(uint64_t) == 8, "type size sanity check"); static_assert(sizeof(uint32_t) == 4, "type size sanity check"); // (From nsNSSCertificate::GetDbKey) @@ -153,7 +174,7 @@ nsNSSCertificateDB::FindCertByDBKey(const char* aDBkey,nsIX509Cert** _cert) // n bytes: // m bytes: nsAutoCString decoded; - nsAutoCString tmpDBKey(aDBkey); + nsAutoCString tmpDBKey(aDBKey); // Filter out any whitespace for backwards compatibility. tmpDBKey.StripWhitespace(); nsresult rv = Base64Decode(tmpDBKey, decoded); @@ -185,15 +206,7 @@ nsNSSCertificateDB::FindCertByDBKey(const char* aDBkey,nsIX509Cert** _cert) reader += issuerLen; MOZ_ASSERT(reader == decoded.EndReading()); - ScopedCERTCertificate cert( - CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN)); - if (cert) { - nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); - if (!nssCert) { - return NS_ERROR_OUT_OF_MEMORY; - } - nssCert.forget(_cert); - } + cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN)); return NS_OK; } diff --git a/security/manager/ssl/nsNSSCertificateDB.h b/security/manager/ssl/nsNSSCertificateDB.h index 5a9588b190f6..4258a153bfd6 100644 --- a/security/manager/ssl/nsNSSCertificateDB.h +++ b/security/manager/ssl/nsNSSCertificateDB.h @@ -35,6 +35,11 @@ public: ImportValidCACerts(int numCACerts, SECItem *CACerts, nsIInterfaceRequestor *ctx, const nsNSSShutDownPreventionLock &proofOfLock); + // This is a separate static method so nsNSSComponent can use it during NSS + // initialization. Other code should probably not use it. + static nsresult + FindCertByDBKey(const char* aDBKey, mozilla::UniqueCERTCertificate& cert); + protected: virtual ~nsNSSCertificateDB(); diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index 4b92283e420d..cebbf3170e79 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -4,16 +4,21 @@ * 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/. */ +#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm + #include "nsNSSComponent.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "SharedSSLState.h" +#include "cert.h" +#include "certdb.h" #include "mozilla/Preferences.h" #include "mozilla/PublicSSL.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/Telemetry.h" +#include "mozilla/UniquePtr.h" #include "nsAppDirectoryServiceDefs.h" #include "nsCRT.h" #include "nsCertVerificationThread.h" @@ -30,6 +35,7 @@ #include "nsITokenPasswordDialogs.h" #include "nsIWindowWatcher.h" #include "nsIXULRuntime.h" +#include "nsNSSCertificateDB.h" #include "nsNSSHelper.h" #include "nsNSSShutDown.h" #include "nsServiceManagerUtils.h" @@ -49,7 +55,14 @@ #endif #ifdef XP_WIN +#include "mozilla/WindowsVersion.h" #include "nsILocalFileWin.h" + +#include "windows.h" // this needs to be before the following includes +#include "Lmcons.h" +#include "Sddl.h" +#include "Wincrypt.h" +#include "nsIWindowsRegKey.h" #endif using namespace mozilla; @@ -394,6 +407,433 @@ nsNSSComponent::ShutdownSmartCardThreads() } #endif // MOZ_NO_SMART_CARDS +#ifdef XP_WIN +static bool +GetUserSid(nsAString& sidString) +{ + // UNLEN is the maximum user name length (see Lmcons.h). +1 for the null + // terminator. + WCHAR lpAccountName[UNLEN + 1]; + DWORD lcAccountName = sizeof(lpAccountName) / sizeof(lpAccountName[0]); + BOOL success = GetUserName(lpAccountName, &lcAccountName); + if (!success) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetUserName failed")); + return false; + } + char sid_buffer[SECURITY_MAX_SID_SIZE]; + SID* sid = reinterpret_cast(sid_buffer); + DWORD cbSid = MOZ_ARRAY_LENGTH(sid_buffer); + SID_NAME_USE eUse; + // There doesn't appear to be a defined maximum length for the domain name + // here. To deal with this, we start with a reasonable buffer length and + // see if that works. If it fails and the error indicates insufficient length, + // we use the indicated required length and try again. + DWORD cchReferencedDomainName = 128; + auto ReferencedDomainName(MakeUnique(cchReferencedDomainName)); + success = LookupAccountName(nullptr, lpAccountName, sid, &cbSid, + ReferencedDomainName.get(), + &cchReferencedDomainName, &eUse); + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("LookupAccountName failed")); + return false; + } + if (!success) { + ReferencedDomainName = MakeUnique(cchReferencedDomainName); + success = LookupAccountName(nullptr, lpAccountName, sid, &cbSid, + ReferencedDomainName.get(), + &cchReferencedDomainName, &eUse); + } + if (!success) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("LookupAccountName failed")); + return false; + } + LPTSTR StringSid; + success = ConvertSidToStringSid(sid, &StringSid); + if (!success) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("ConvertSidToStringSid failed")); + return false; + } + sidString.Assign(StringSid); + LocalFree(StringSid); + return true; +} + +// This is a specialized helper function to read the value of a registry key +// that might not be present. If it is present, returns (via the output +// parameter) its value. Otherwise, returns the given default value. +// This function handles one level of nesting. That is, if the desired value +// is actually in a direct child of the given registry key (where the child +// and/or the value being sought may not actually be present), this function +// will handle that. In the normal case, though, optionalChildName will be +// null. +static nsresult +ReadRegKeyValueWithDefault(nsCOMPtr regKey, + uint32_t flags, + wchar_t* optionalChildName, + wchar_t* valueName, + uint32_t defaultValue, + uint32_t& valueOut) +{ + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("ReadRegKeyValueWithDefault")); + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("attempting to read '%S%s%S' with default '%u'", + optionalChildName ? optionalChildName : L"", + optionalChildName ? "\\" : "", valueName, defaultValue)); + if (optionalChildName) { + nsDependentString childNameString(optionalChildName); + bool hasChild; + nsresult rv = regKey->HasChild(childNameString, &hasChild); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("failed to determine if child key is present")); + return rv; + } + if (!hasChild) { + valueOut = defaultValue; + return NS_OK; + } + nsCOMPtr childRegKey; + rv = regKey->OpenChild(childNameString, flags, + getter_AddRefs(childRegKey)); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open child key")); + return rv; + } + return ReadRegKeyValueWithDefault(childRegKey, flags, nullptr, valueName, + defaultValue, valueOut); + } + nsDependentString valueNameString(valueName); + bool hasValue; + nsresult rv = regKey->HasValue(valueNameString, &hasValue); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("failed to determine if value is present")); + return rv; + } + if (!hasValue) { + valueOut = defaultValue; + return NS_OK; + } + rv = regKey->ReadIntValue(valueNameString, &valueOut); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to read value")); + return rv; + } + return NS_OK; +} + +static nsresult +AccountHasFamilySafetyEnabled(bool& enabled) +{ + enabled = false; + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("AccountHasFamilySafetyEnabled?")); + nsCOMPtr parentalControlsKey( + do_CreateInstance("@mozilla.org/windows-registry-key;1")); + if (!parentalControlsKey) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't create nsIWindowsRegKey")); + return NS_ERROR_FAILURE; + } + uint32_t flags = nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::WOW64_64; + NS_NAMED_LITERAL_STRING(familySafetyPath, + "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Parental Controls"); + nsresult rv = parentalControlsKey->Open( + nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, familySafetyPath, flags); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open parentalControlsKey")); + return rv; + } + NS_NAMED_LITERAL_STRING(usersString, "Users"); + bool hasUsers; + rv = parentalControlsKey->HasChild(usersString, &hasUsers); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("HasChild(Users) failed")); + return rv; + } + if (!hasUsers) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("Users subkey not present - Parental Controls not enabled")); + return NS_OK; + } + nsCOMPtr usersKey; + rv = parentalControlsKey->OpenChild(usersString, flags, + getter_AddRefs(usersKey)); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to open Users subkey")); + return rv; + } + nsAutoString sid; + if (!GetUserSid(sid)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get sid")); + return NS_ERROR_FAILURE; + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("our sid is '%S'", sid.get())); + bool hasSid; + rv = usersKey->HasChild(sid, &hasSid); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("HasChild(sid) failed")); + return rv; + } + if (!hasSid) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("sid not present in Family Safety Users")); + return NS_OK; + } + nsCOMPtr sidKey; + rv = usersKey->OpenChild(sid, flags, getter_AddRefs(sidKey)); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open sid key")); + return rv; + } + // There are three keys we're interested in: "Parental Controls On", + // "Logging Required", and "Web\\Filter On". These keys will have value 0 + // or 1, indicating a particular feature is disabled or enabled, + // respectively. So, if "Parental Controls On" is not 1, Family Safety is + // disabled and we don't care about anything else. If both "Logging + // Required" and "Web\\Filter On" are 0, the proxy will not be running, + // so for our purposes we can consider Family Safety disabled in that + // case. + // By default, "Logging Required" is 1 and "Web\\Filter On" is 0, + // reflecting the initial settings when Family Safety is enabled for an + // account for the first time, However, these sub-keys are not created + // unless they are switched away from the default value. + uint32_t parentalControlsOn; + rv = sidKey->ReadIntValue(NS_LITERAL_STRING("Parental Controls On"), + &parentalControlsOn); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("couldn't read Parental Controls On")); + return rv; + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("Parental Controls On: %u", parentalControlsOn)); + if (parentalControlsOn != 1) { + return NS_OK; + } + uint32_t loggingRequired; + rv = ReadRegKeyValueWithDefault(sidKey, flags, nullptr, L"Logging Required", + 1, loggingRequired); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("failed to read value of Logging Required")); + return rv; + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("Logging Required: %u", loggingRequired)); + uint32_t webFilterOn; + rv = ReadRegKeyValueWithDefault(sidKey, flags, L"Web", L"Filter On", 0, + webFilterOn); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("failed to read value of Web\\Filter On")); + return rv; + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Web\\Filter On: %u", webFilterOn)); + enabled = loggingRequired == 1 || webFilterOn == 1; + return NS_OK; +} + +const char* kImportedFamilySafetyRootPref = + "security.family_safety.imported_root.db_key"; + +static nsresult +MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate, + bool& wasFamilySafetyRoot) +{ + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot")); + wasFamilySafetyRoot = false; + + // It would be convenient to just use nsIX509CertDB here. However, since + // nsIX509CertDB depends on nsNSSComponent initialization, we can't use it. + // Instead, we can use NSS APIs directly (as long as we're called late enough + // in nsNSSComponent initialization such that those APIs are safe to use). + + SECItem derCert = { + siBuffer, + certificate->pbCertEncoded, + certificate->cbCertEncoded + }; + UniqueCERTCertificate nssCertificate( + CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, + nullptr, // nickname unnecessary + false, // not permanent + true)); // copy DER + if (!nssCertificate) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate")); + return NS_ERROR_FAILURE; + } + // Looking for a certificate with the common name 'Microsoft Family Safety' + UniquePtr subjectName( + CERT_GetCommonName(&nssCertificate->subject), PORT_Free); + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("subject name is '%s'", subjectName.get())); + if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 0) { + wasFamilySafetyRoot = true; + CERTCertTrust trust = { + CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER, + 0, + 0 + }; + SECStatus srv = __CERT_AddTempCertToPerm( + nssCertificate.get(), "Microsoft Family Safety", &trust); + if (srv != SECSuccess) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("couldn't permanently add certificate")); + return NS_ERROR_FAILURE; + } + nsAutoCString dbKey; + nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate.get(), dbKey); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed")); + return rv; + } + Preferences::SetCString(kImportedFamilySafetyRootPref, dbKey); + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root")); + } + return NS_OK; +} + +// Because HCERTSTORE is just a typedef void*, we can't use any of the nice +// scoped pointer templates. +class ScopedCertStore final +{ +public: + explicit ScopedCertStore(HCERTSTORE certstore) : certstore(certstore) {} + + ~ScopedCertStore() + { + CertCloseStore(certstore, 0); + } + + HCERTSTORE get() + { + return certstore; + } + +private: + ScopedCertStore(const ScopedCertStore&) = delete; + ScopedCertStore& operator=(const ScopedCertStore&) = delete; + HCERTSTORE certstore; +}; + +static const wchar_t* WindowsDefaultRootStoreName = L"ROOT"; + +static nsresult +LoadFamilySafetyRoot() +{ + ScopedCertStore certstore( + CertOpenSystemStore(0, WindowsDefaultRootStoreName)); + if (!certstore.get()) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("couldn't get certstore '%S'", WindowsDefaultRootStoreName)); + return NS_ERROR_FAILURE; + } + // Any resources held by the certificate are released by the next call to + // CertFindCertificateInStore. + PCCERT_CONTEXT certificate = nullptr; + while (certificate = CertFindCertificateInStore(certstore.get(), + X509_ASN_ENCODING, 0, + CERT_FIND_ANY, nullptr, + certificate)) { + bool wasFamilySafetyRoot = false; + nsresult rv = MaybeImportFamilySafetyRoot(certificate, + wasFamilySafetyRoot); + if (NS_SUCCEEDED(rv) && wasFamilySafetyRoot) { + return NS_OK; // We're done (we're only expecting one root). + } + } + return NS_ERROR_FAILURE; +} + +static void +UnloadFamilySafetyRoot() +{ + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot")); + nsAdoptingCString dbKey = Preferences::GetCString( + kImportedFamilySafetyRootPref); + if (!dbKey || dbKey.IsEmpty()) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("Family Safety root wasn't previously imported")); + return; + } + UniqueCERTCertificate cert; + nsresult rv = nsNSSCertificateDB::FindCertByDBKey(dbKey, cert); + if (NS_FAILED(rv)) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("finding previously-imported Family Safety root failed")); + return; + } + if (!cert) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("previously-imported Family Safety root not found")); + return; + } + SECStatus srv = SEC_DeletePermCertificate(cert.get()); + if (srv != SECSuccess) { + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("couldn't delete previously-imported Family Safety root")); + return; + } + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("deleted previously-imported Family Safety root")); + Preferences::ClearUser(kImportedFamilySafetyRootPref); +} + +#endif // XP_WIN + +// The supported values of this pref are: +// 0: disable detecting Family Safety mode and importing the root +// 1: only attempt to detect Family Safety mode (don't import the root) +// 2: detect Family Safety mode and import the root +const char* kFamilySafetyModePref = "security.family_safety.mode"; + +// The telemetry gathered by this function is as follows: +// 0-2: the value of the Family Safety mode pref +// 3: detecting Family Safety mode failed +// 4: Family Safety was not enabled +// 5: Family Safety was enabled +// 6: failed to import the Family Safety root +// 7: successfully imported the root +static void +MaybeEnableFamilySafetyCompatibility() +{ +#ifdef XP_WIN + UnloadFamilySafetyRoot(); + if (!(IsWin8Point1OrLater() && !IsWin10OrLater())) { + return; + } + // Detect but don't import by default. + uint32_t familySafetyMode = Preferences::GetUint(kFamilySafetyModePref, 1); + if (familySafetyMode > 2) { + familySafetyMode = 0; + } + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, familySafetyMode); + if (familySafetyMode == 0) { + return; + } + bool familySafetyEnabled; + nsresult rv = AccountHasFamilySafetyEnabled(familySafetyEnabled); + if (NS_FAILED(rv)) { + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 3); + return; + } + if (!familySafetyEnabled) { + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 4); + return; + } + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 5); + if (familySafetyMode == 2) { + rv = LoadFamilySafetyRoot(); + if (NS_FAILED(rv)) { + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 6); + MOZ_LOG(gPIPNSSLog, LogLevel::Debug, + ("failed to load Family Safety root")); + } else { + Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 7); + } + } +#endif // XP_WIN +} + void nsNSSComponent::LoadLoadableRoots() { @@ -1079,6 +1519,8 @@ nsNSSComponent::InitializeNSS() InitCertVerifierLog(); LoadLoadableRoots(); + MaybeEnableFamilySafetyCompatibility(); + ConfigureTLSSessionIdentifiers(); bool requireSafeNegotiation = @@ -1144,7 +1586,6 @@ nsNSSComponent::InitializeNSS() if (PK11_IsFIPS()) { Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true); } - MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS Initialization done\n")); return NS_OK; } @@ -1346,6 +1787,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic, MutexAutoLock lock(mutex); mTestBuiltInRootHash = Preferences::GetString("security.test.built_in_root_hash"); #endif // DEBUG + } else if (prefName.Equals(kFamilySafetyModePref)) { + MaybeEnableFamilySafetyCompatibility(); } else { clearSessionCache = false; } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index dd67b9328768..5ee7643533a5 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -291,6 +291,14 @@ "n_values": 10, "description": "GPU Device Reset Reason (ok, hung, removed, reset, internal error, invalid call, out of memory)" }, + "FAMILY_SAFETY": { + "alert_emails": ["seceng@mozilla.org"], + "expires_in_version": "55", + "kind": "enumerated", + "n_values": 16, + "bug_numbers": [1239166], + "description": "Status of Family Safety detection and remediation. See nsNSSComponent.cpp." + }, "FETCH_IS_MAINTHREAD": { "expires_in_version": "50", "kind": "boolean", From 4eeb180418f877cedbbbd1b8d889fc37a3954ac6 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 1 Apr 2016 16:04:48 -0700 Subject: [PATCH 33/53] Backed out changeset 288b6ba19e64 (bug 1257304) because it caused merge conflicts with m-c/fx-team MozReview-Commit-ID: HuO0AUpagld --- .../java/org/mozilla/gecko/GeckoAppShell.java | 28 +++++++------------ .../mozilla/gecko/GeckoScreenOrientation.java | 4 +-- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java index 52480c0f2af4..56910f7724de 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java @@ -302,7 +302,6 @@ public class GeckoAppShell public static native void onFullScreenPluginHidden(View view); private static LayerView sLayerView; - private static Rect sScreenSize; public static void setLayerView(LayerView lv) { if (sLayerView == lv) { @@ -1268,7 +1267,7 @@ public class GeckoAppShell if (action.equalsIgnoreCase(Intent.ACTION_SEND)) { Intent shareIntent = getShareIntent(context, targetURI, mimeType, title); return Intent.createChooser(shareIntent, - context.getResources().getString(R.string.share_title)); + context.getResources().getString(R.string.share_title)); } Uri uri = normalizeUriScheme(targetURI.indexOf(':') >= 0 ? Uri.parse(targetURI) : new Uri.Builder().scheme(targetURI).build()); @@ -1734,7 +1733,7 @@ public class GeckoAppShell return true; } }; - + EnumerateGeckoProcesses(visitor); } @@ -1756,7 +1755,7 @@ public class GeckoAppShell // figure out the column offsets. We only care about the pid and user fields StringTokenizer st = new StringTokenizer(headerOutput); - + int tokenSoFar = 0; while (st.hasMoreTokens()) { String next = st.nextToken(); @@ -1889,7 +1888,7 @@ public class GeckoAppShell final MimeTypeMap mtm = MimeTypeMap.getSingleton(); return mtm.getMimeTypeFromExtension(ext); } - + private static Drawable getDrawableForExtension(PackageManager pm, String aExt) { Intent intent = new Intent(Intent.ACTION_VIEW); final String mimeType = getMimeTypeFromExtension(aExt); @@ -2613,7 +2612,7 @@ public class GeckoAppShell if (Proxy.NO_PROXY.equals(proxy)) { return "DIRECT"; } - + switch (proxy.type()) { case HTTP: return "PROXY " + proxy.address().toString(); @@ -2856,18 +2855,11 @@ public class GeckoAppShell return 0; } - public static synchronized Rect getScreenSize(final boolean update) { - if (update || sScreenSize == null) { - final WindowManager wm = (WindowManager) - getApplicationContext().getSystemService(Context.WINDOW_SERVICE); - final Display disp = wm.getDefaultDisplay(); - sScreenSize = new Rect(0, 0, disp.getWidth(), disp.getHeight()); - } - return sScreenSize; - } - @WrapForJNI - public static Rect getScreenSize() { - return getScreenSize(/* update */ false); + static Rect getScreenSize() { + final WindowManager wm = (WindowManager) + getApplicationContext().getSystemService(Context.WINDOW_SERVICE); + final Display disp = wm.getDefaultDisplay(); + return new Rect(0, 0, disp.getWidth(), disp.getHeight()); } } diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java b/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java index 4b9383cdffb6..daab582c0047 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoScreenOrientation.java @@ -40,7 +40,7 @@ public class GeckoScreenOrientation { this.value = (short)value; } - private final static ScreenOrientation[] sValues = ScreenOrientation.values(); + private final static ScreenOrientation[] sValues = ScreenOrientation.values(); public static ScreenOrientation get(int value) { for (ScreenOrientation orient: sValues) { @@ -156,8 +156,6 @@ public class GeckoScreenOrientation { GeckoEvent.createScreenOrientationEvent(aScreenOrientation.value, getAngle())); } - // Update screen size. - GeckoAppShell.getScreenSize(/* update */ true); return true; } From 81af293e823078c0c74495f622fc4f40e34ecbeb Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 1 Apr 2016 16:05:07 -0700 Subject: [PATCH 34/53] Backed out changeset 95e61ede373d (bug 1257269) because it caused merge conflicts with m-c/fx-team MozReview-Commit-ID: 2Zt4WJqmlzu --- gfx/layers/apz/public/GeckoContentController.h | 3 +-- gfx/layers/apz/src/AsyncPanZoomController.cpp | 13 +------------ .../gecko/gfx/DynamicToolbarAnimator.java | 18 ++---------------- .../mozilla/gecko/gfx/GeckoLayerClient.java | 5 ----- .../gecko/gfx/NativePanZoomController.java | 5 ----- .../org/mozilla/gecko/gfx/PanZoomTarget.java | 1 - widget/android/AndroidContentController.cpp | 10 +--------- widget/android/AndroidContentController.h | 3 +-- widget/android/GeneratedJNIWrappers.cpp | 8 -------- widget/android/GeneratedJNIWrappers.h | 16 ---------------- widget/android/nsWindow.cpp | 16 ---------------- widget/android/nsWindow.h | 1 - 12 files changed, 6 insertions(+), 93 deletions(-) diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index 6d8023fe9fff..c9bfaa2feeda 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -144,8 +144,7 @@ public: virtual void NotifyFlushComplete() = 0; virtual void UpdateOverscrollVelocity(const float aX, const float aY) {} - virtual void UpdateOverscrollOffset(const float aX, const float aY) {} - virtual void SetScrollingRootContent(const bool isRootContent) {} + virtual void UpdateOverscrollOffset(const float aX,const float aY) {} GeckoContentController() {} virtual void ChildAdopted() {} diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index bee08b48bcf8..e82ef21f2b2a 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1326,11 +1326,6 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) { APZC_LOG("%p got a touch-end in state %d\n", this, mState); - RefPtr controller = GetGeckoContentController(); - if (controller) { - controller->SetScrollingRootContent(false); - } - OnTouchEndOrCancel(); // In case no touch behavior triggered previously we can avoid sending @@ -2396,13 +2391,7 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, if (!IsZero(adjustedDisplacement)) { ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom()); - if (CancelableBlockState* block = CurrentInputBlock()) { - if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) { - RefPtr controller = GetGeckoContentController(); - if (controller) { - controller->SetScrollingRootContent(IsRootContent()); - } - } + if (InputBlockState* block = CurrentInputBlock()) { block->SetScrolledApzc(this); } ScheduleCompositeAndMaybeRepaint(); diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java index eed3e3d9b711..d0b287f4f1ed 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java @@ -5,7 +5,6 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.PrefsHelper; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -95,9 +94,6 @@ public class DynamicToolbarAnimator { private PointF mTouchStart; private float mLastTouch; - /* Set to true when root content is being scrolled */ - private boolean mScrollingRootContent; - public DynamicToolbarAnimator(GeckoLayerClient aTarget) { mTarget = aTarget; mListeners = new ArrayList(); @@ -112,11 +108,6 @@ public class DynamicToolbarAnimator { } }; PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver); - - // JPZ doesn't notify when scrolling root content. This maintains existing behaviour. - if (!AppConstants.MOZ_ANDROID_APZ) { - mScrollingRootContent = true; - } } public void destroy() { @@ -194,10 +185,6 @@ public class DynamicToolbarAnimator { animateToolbar(false, immediately); } - public void setScrollingRootContent(boolean isRootContent) { - mScrollingRootContent = isRootContent; - } - private void animateToolbar(final boolean showToolbar, boolean immediately) { ThreadUtils.assertOnUiThread(); @@ -352,12 +339,11 @@ public class DynamicToolbarAnimator { // translation to take effect right away. Or if the user has moved // their finger past the required threshold (and is not trying to // scroll past the bottom of the page) then also we want the touch - // to cause translation. If the toolbar is fully visible, we only - // want the toolbar to hide if the user is scrolling the root content. + // to cause translation. boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation); boolean reachedThreshold = -aTouchTravelDistance >= exposeThreshold; boolean atBottomOfPage = aMetrics.viewportRectBottom() >= aMetrics.pageRectBottom; - if (inBetween || (mScrollingRootContent && reachedThreshold && !atBottomOfPage)) { + if (inBetween || (reachedThreshold && !atBottomOfPage)) { return translation; } } else { // finger moving downwards diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 0f2493f8b79e..4b02f34b68e8 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -1086,11 +1086,6 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return layerPoint; } - @Override - public void setScrollingRootContent(boolean isRootContent) { - mToolbarAnimator.setScrollingRootContent(isRootContent); - } - public void addDrawListener(DrawListener listener) { mDrawListeners.add(listener); } diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java index 23fe980d26fb..7807419eb46c 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -324,11 +324,6 @@ class NativePanZoomController extends JNIObject implements PanZoomController { } } - @WrapForJNI - private void setScrollingRootContent(final boolean isRootContent) { - mTarget.setScrollingRootContent(isRootContent); - } - /** * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned * to avoid unwanted scroll interactions. diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java index b276e781e536..4afe8653a4b8 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java @@ -28,5 +28,4 @@ public interface PanZoomTarget { public void removeRenderTask(RenderTask task); public Object getLock(); public PointF convertViewPointToLayerPoint(PointF viewPoint); - public void setScrollingRootContent(boolean isRootContent); } diff --git a/widget/android/AndroidContentController.cpp b/widget/android/AndroidContentController.cpp index a9dd9aa7babf..fb3f305bf5f9 100644 --- a/widget/android/AndroidContentController.cpp +++ b/widget/android/AndroidContentController.cpp @@ -100,21 +100,13 @@ AndroidContentController::UpdateOverscrollVelocity(const float aX, const float a } void -AndroidContentController::UpdateOverscrollOffset(const float aX, const float aY) +AndroidContentController::UpdateOverscrollOffset(const float aX,const float aY) { if (mAndroidWindow) { mAndroidWindow->UpdateOverscrollOffset(aX, aY); } } -void -AndroidContentController::SetScrollingRootContent(const bool isRootContent) -{ - if (mAndroidWindow) { - mAndroidWindow->SetScrollingRootContent(isRootContent); - } -} - void AndroidContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, diff --git a/widget/android/AndroidContentController.h b/widget/android/AndroidContentController.h index 273f977794a2..bb38d56d28d8 100644 --- a/widget/android/AndroidContentController.h +++ b/widget/android/AndroidContentController.h @@ -39,8 +39,7 @@ public: const ScrollableLayerGuid& aGuid) override; void PostDelayedTask(Task* aTask, int aDelayMs) override; void UpdateOverscrollVelocity(const float aX, const float aY) override; - void UpdateOverscrollOffset(const float aX, const float aY) override; - void SetScrollingRootContent(const bool isRootContent) override; + void UpdateOverscrollOffset(const float aX,const float aY) override; void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) override; diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index 87ba7a8d16f2..eca5b59f842f 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1575,14 +1575,6 @@ auto NativePanZoomController::RequestContentRepaintWrapper(float a0, float a1, f return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0, a1, a2, a3, a4); } -constexpr char NativePanZoomController::SetScrollingRootContent_t::name[]; -constexpr char NativePanZoomController::SetScrollingRootContent_t::signature[]; - -auto NativePanZoomController::SetScrollingRootContent(bool a0) const -> void -{ - return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0); -} - constexpr char NativePanZoomController::UpdateOverscrollOffset_t::name[]; constexpr char NativePanZoomController::UpdateOverscrollOffset_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 41ed2d2212f7..c57dcadb5881 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3781,22 +3781,6 @@ public: auto RequestContentRepaintWrapper(float, float, float, float, float) const -> void; - struct SetScrollingRootContent_t { - typedef NativePanZoomController Owner; - typedef void ReturnType; - typedef void SetterType; - typedef mozilla::jni::Args< - bool> Args; - static constexpr char name[] = "setScrollingRootContent"; - static constexpr char signature[] = - "(Z)V"; - static const bool isStatic = false; - static const mozilla::jni::ExceptionMode exceptionMode = - mozilla::jni::ExceptionMode::ABORT; - }; - - auto SetScrollingRootContent(bool) const -> void; - struct UpdateOverscrollOffset_t { typedef NativePanZoomController Owner; typedef void ReturnType; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 0c198f3f5364..8ba270ad251e 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -817,11 +817,6 @@ public: mNPZC->UpdateOverscrollOffset(x, y); } - void SetScrollingRootContent(const bool isRootContent) - { - mNPZC->SetScrollingRootContent(isRootContent); - } - void SetSelectionDragState(const bool aState) { mNPZC->OnSelectionDragState(aState); @@ -1881,17 +1876,6 @@ nsWindow::UpdateOverscrollOffset(const float aX, const float aY) } } -void -nsWindow::SetScrollingRootContent(const bool isRootContent) -{ - // On Android, the Controller thread and UI thread are the same. - MOZ_ASSERT(APZThreadUtils::IsControllerThread(), "nsWindow::SetScrollingRootContent must be called from the controller thread"); - - if (mNPZCSupport) { - mNPZCSupport->SetScrollingRootContent(isRootContent); - } -} - void nsWindow::SetSelectionDragState(bool aState) { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index cc1e6fdbad00..b053381e0736 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -79,7 +79,6 @@ public: void UpdateOverscrollVelocity(const float aX, const float aY); void UpdateOverscrollOffset(const float aX, const float aY); - void SetScrollingRootContent(const bool isRootContent); // // nsIWidget From 3eeb55a5ff20910053519157d1f495039d8e04a8 Mon Sep 17 00:00:00 2001 From: Randall Barker Date: Thu, 24 Mar 2016 15:00:27 -0700 Subject: [PATCH 35/53] Bug 1257269 - Panning up in a scrollable element should not hide the toolbar r=kats,jchen --- gfx/layers/apz/public/GeckoContentController.h | 3 ++- gfx/layers/apz/src/AsyncPanZoomController.cpp | 13 ++++++++++++- .../gecko/gfx/DynamicToolbarAnimator.java | 18 ++++++++++++++++-- .../mozilla/gecko/gfx/GeckoLayerClient.java | 5 +++++ .../gecko/gfx/NativePanZoomController.java | 5 +++++ .../org/mozilla/gecko/gfx/PanZoomTarget.java | 1 + widget/android/AndroidContentController.cpp | 10 +++++++++- widget/android/AndroidContentController.h | 3 ++- widget/android/GeneratedJNIWrappers.cpp | 8 ++++++++ widget/android/GeneratedJNIWrappers.h | 16 ++++++++++++++++ widget/android/nsWindow.cpp | 16 ++++++++++++++++ widget/android/nsWindow.h | 1 + 12 files changed, 93 insertions(+), 6 deletions(-) diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index c9bfaa2feeda..6d8023fe9fff 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -144,7 +144,8 @@ public: virtual void NotifyFlushComplete() = 0; virtual void UpdateOverscrollVelocity(const float aX, const float aY) {} - virtual void UpdateOverscrollOffset(const float aX,const float aY) {} + virtual void UpdateOverscrollOffset(const float aX, const float aY) {} + virtual void SetScrollingRootContent(const bool isRootContent) {} GeckoContentController() {} virtual void ChildAdopted() {} diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index e82ef21f2b2a..bee08b48bcf8 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1326,6 +1326,11 @@ nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) { APZC_LOG("%p got a touch-end in state %d\n", this, mState); + RefPtr controller = GetGeckoContentController(); + if (controller) { + controller->SetScrollingRootContent(false); + } + OnTouchEndOrCancel(); // In case no touch behavior triggered previously we can avoid sending @@ -2391,7 +2396,13 @@ bool AsyncPanZoomController::AttemptScroll(ParentLayerPoint& aStartPoint, if (!IsZero(adjustedDisplacement)) { ScrollBy(adjustedDisplacement / mFrameMetrics.GetZoom()); - if (InputBlockState* block = CurrentInputBlock()) { + if (CancelableBlockState* block = CurrentInputBlock()) { + if (block->AsTouchBlock() && (block->GetScrolledApzc() != this)) { + RefPtr controller = GetGeckoContentController(); + if (controller) { + controller->SetScrollingRootContent(IsRootContent()); + } + } block->SetScrolledApzc(this); } ScheduleCompositeAndMaybeRepaint(); diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java index d0b287f4f1ed..eed3e3d9b711 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java @@ -5,6 +5,7 @@ package org.mozilla.gecko.gfx; +import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.PrefsHelper; import org.mozilla.gecko.util.FloatUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -94,6 +95,9 @@ public class DynamicToolbarAnimator { private PointF mTouchStart; private float mLastTouch; + /* Set to true when root content is being scrolled */ + private boolean mScrollingRootContent; + public DynamicToolbarAnimator(GeckoLayerClient aTarget) { mTarget = aTarget; mListeners = new ArrayList(); @@ -108,6 +112,11 @@ public class DynamicToolbarAnimator { } }; PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver); + + // JPZ doesn't notify when scrolling root content. This maintains existing behaviour. + if (!AppConstants.MOZ_ANDROID_APZ) { + mScrollingRootContent = true; + } } public void destroy() { @@ -185,6 +194,10 @@ public class DynamicToolbarAnimator { animateToolbar(false, immediately); } + public void setScrollingRootContent(boolean isRootContent) { + mScrollingRootContent = isRootContent; + } + private void animateToolbar(final boolean showToolbar, boolean immediately) { ThreadUtils.assertOnUiThread(); @@ -339,11 +352,12 @@ public class DynamicToolbarAnimator { // translation to take effect right away. Or if the user has moved // their finger past the required threshold (and is not trying to // scroll past the bottom of the page) then also we want the touch - // to cause translation. + // to cause translation. If the toolbar is fully visible, we only + // want the toolbar to hide if the user is scrolling the root content. boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation); boolean reachedThreshold = -aTouchTravelDistance >= exposeThreshold; boolean atBottomOfPage = aMetrics.viewportRectBottom() >= aMetrics.pageRectBottom; - if (inBetween || (reachedThreshold && !atBottomOfPage)) { + if (inBetween || (mScrollingRootContent && reachedThreshold && !atBottomOfPage)) { return translation; } } else { // finger moving downwards diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java index 4b02f34b68e8..0f2493f8b79e 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -1086,6 +1086,11 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget return layerPoint; } + @Override + public void setScrollingRootContent(boolean isRootContent) { + mToolbarAnimator.setScrollingRootContent(isRootContent); + } + public void addDrawListener(DrawListener listener) { mDrawListeners.add(listener); } diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java index 7807419eb46c..23fe980d26fb 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -324,6 +324,11 @@ class NativePanZoomController extends JNIObject implements PanZoomController { } } + @WrapForJNI + private void setScrollingRootContent(final boolean isRootContent) { + mTarget.setScrollingRootContent(isRootContent); + } + /** * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned * to avoid unwanted scroll interactions. diff --git a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java index 4afe8653a4b8..b276e781e536 100644 --- a/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java +++ b/mobile/android/base/java/org/mozilla/gecko/gfx/PanZoomTarget.java @@ -28,4 +28,5 @@ public interface PanZoomTarget { public void removeRenderTask(RenderTask task); public Object getLock(); public PointF convertViewPointToLayerPoint(PointF viewPoint); + public void setScrollingRootContent(boolean isRootContent); } diff --git a/widget/android/AndroidContentController.cpp b/widget/android/AndroidContentController.cpp index fb3f305bf5f9..a9dd9aa7babf 100644 --- a/widget/android/AndroidContentController.cpp +++ b/widget/android/AndroidContentController.cpp @@ -100,13 +100,21 @@ AndroidContentController::UpdateOverscrollVelocity(const float aX, const float a } void -AndroidContentController::UpdateOverscrollOffset(const float aX,const float aY) +AndroidContentController::UpdateOverscrollOffset(const float aX, const float aY) { if (mAndroidWindow) { mAndroidWindow->UpdateOverscrollOffset(aX, aY); } } +void +AndroidContentController::SetScrollingRootContent(const bool isRootContent) +{ + if (mAndroidWindow) { + mAndroidWindow->SetScrollingRootContent(isRootContent); + } +} + void AndroidContentController::NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, diff --git a/widget/android/AndroidContentController.h b/widget/android/AndroidContentController.h index bb38d56d28d8..273f977794a2 100644 --- a/widget/android/AndroidContentController.h +++ b/widget/android/AndroidContentController.h @@ -39,7 +39,8 @@ public: const ScrollableLayerGuid& aGuid) override; void PostDelayedTask(Task* aTask, int aDelayMs) override; void UpdateOverscrollVelocity(const float aX, const float aY) override; - void UpdateOverscrollOffset(const float aX,const float aY) override; + void UpdateOverscrollOffset(const float aX, const float aY) override; + void SetScrollingRootContent(const bool isRootContent) override; void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg) override; diff --git a/widget/android/GeneratedJNIWrappers.cpp b/widget/android/GeneratedJNIWrappers.cpp index fb7689113743..677d34174457 100644 --- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -1556,6 +1556,14 @@ auto NativePanZoomController::RequestContentRepaintWrapper(float a0, float a1, f return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0, a1, a2, a3, a4); } +constexpr char NativePanZoomController::SetScrollingRootContent_t::name[]; +constexpr char NativePanZoomController::SetScrollingRootContent_t::signature[]; + +auto NativePanZoomController::SetScrollingRootContent(bool a0) const -> void +{ + return mozilla::jni::Method::Call(NativePanZoomController::mCtx, nullptr, a0); +} + constexpr char NativePanZoomController::UpdateOverscrollOffset_t::name[]; constexpr char NativePanZoomController::UpdateOverscrollOffset_t::signature[]; diff --git a/widget/android/GeneratedJNIWrappers.h b/widget/android/GeneratedJNIWrappers.h index 5a87e1d3e337..97f0f656ccb4 100644 --- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -3740,6 +3740,22 @@ public: auto RequestContentRepaintWrapper(float, float, float, float, float) const -> void; + struct SetScrollingRootContent_t { + typedef NativePanZoomController Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args< + bool> Args; + static constexpr char name[] = "setScrollingRootContent"; + static constexpr char signature[] = + "(Z)V"; + static const bool isStatic = false; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + }; + + auto SetScrollingRootContent(bool) const -> void; + struct UpdateOverscrollOffset_t { typedef NativePanZoomController Owner; typedef void ReturnType; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 8ba270ad251e..0c198f3f5364 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -817,6 +817,11 @@ public: mNPZC->UpdateOverscrollOffset(x, y); } + void SetScrollingRootContent(const bool isRootContent) + { + mNPZC->SetScrollingRootContent(isRootContent); + } + void SetSelectionDragState(const bool aState) { mNPZC->OnSelectionDragState(aState); @@ -1876,6 +1881,17 @@ nsWindow::UpdateOverscrollOffset(const float aX, const float aY) } } +void +nsWindow::SetScrollingRootContent(const bool isRootContent) +{ + // On Android, the Controller thread and UI thread are the same. + MOZ_ASSERT(APZThreadUtils::IsControllerThread(), "nsWindow::SetScrollingRootContent must be called from the controller thread"); + + if (mNPZCSupport) { + mNPZCSupport->SetScrollingRootContent(isRootContent); + } +} + void nsWindow::SetSelectionDragState(bool aState) { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index b053381e0736..cc1e6fdbad00 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -79,6 +79,7 @@ public: void UpdateOverscrollVelocity(const float aX, const float aY); void UpdateOverscrollOffset(const float aX, const float aY); + void SetScrollingRootContent(const bool isRootContent); // // nsIWidget From 9bf9d2c2c995b975f93e5463bdc887f294f95bad Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Thu, 31 Mar 2016 10:49:07 -0700 Subject: [PATCH 36/53] Bug 1257401 - Remove the worker descriptor for `PushSubscription`. r=khuey MozReview-Commit-ID: 9rTJn4KU2Es --HG-- extra : rebase_source : 446483edea7d5a08c4accf3c4787f08bc3a94cd5 --- dom/bindings/Bindings.conf | 9 - dom/push/Push.js | 1 - dom/push/PushManager.cpp | 436 +---------------------------- dom/push/PushManager.h | 117 -------- dom/push/PushSubscription.cpp | 372 ++++++++++++++++++++++++ dom/push/PushSubscription.h | 95 +++++++ dom/push/moz.build | 2 + dom/webidl/PushSubscription.webidl | 4 - 8 files changed, 473 insertions(+), 563 deletions(-) create mode 100644 dom/push/PushSubscription.cpp create mode 100644 dom/push/PushSubscription.h diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 5d5b7fe2640e..0c243f60ead5 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -965,15 +965,6 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::WorkerPushManager', }], -'PushSubscription': [{ - 'workers': False, - 'headerFile': 'mozilla/dom/PushManager.h', -}, { - 'workers': True, - 'headerFile': 'mozilla/dom/PushManager.h', - 'nativeType': 'mozilla::dom::WorkerPushSubscription', -}], - 'Range': { 'nativeType': 'nsRange', 'binaryNames': { diff --git a/dom/push/Push.js b/dom/push/Push.js index af7377898a80..30c4dbc88d8f 100644 --- a/dom/push/Push.js +++ b/dom/push/Push.js @@ -209,7 +209,6 @@ PushSubscriptionCallback.prototype = { pushManager._scope, publicKey, authSecret); - sub.setPrincipal(pushManager._principal); this.resolve(sub); }, diff --git a/dom/push/PushManager.cpp b/dom/push/PushManager.cpp index 4f501614a80c..01eb684148a9 100644 --- a/dom/push/PushManager.cpp +++ b/dom/push/PushManager.cpp @@ -11,7 +11,7 @@ #include "mozilla/Services.h" #include "mozilla/unused.h" #include "mozilla/dom/PushManagerBinding.h" -#include "mozilla/dom/PushSubscriptionBinding.h" +#include "mozilla/dom/PushSubscription.h" #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" #include "mozilla/dom/Promise.h" @@ -66,183 +66,8 @@ GetPermissionState(nsIPrincipal* aPrincipal, return NS_OK; } -void -SubscriptionToJSON(PushSubscriptionJSON& aJSON, const nsString& aEndpoint, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret) -{ - aJSON.mEndpoint.Construct(); - aJSON.mEndpoint.Value() = aEndpoint; - - aJSON.mKeys.mP256dh.Construct(); - nsresult rv = Base64URLEncode(aRawP256dhKey.Length(), - aRawP256dhKey.Elements(), - aJSON.mKeys.mP256dh.Value()); - Unused << NS_WARN_IF(NS_FAILED(rv)); - - aJSON.mKeys.mAuth.Construct(); - rv = Base64URLEncode(aAuthSecret.Length(), aAuthSecret.Elements(), - aJSON.mKeys.mAuth.Value()); - Unused << NS_WARN_IF(NS_FAILED(rv)); -} - } // anonymous namespace -class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback -{ -public: - NS_DECL_ISUPPORTS - - explicit UnsubscribeResultCallback(Promise* aPromise) - : mPromise(aPromise) - { - AssertIsOnMainThread(); - } - - NS_IMETHOD - OnUnsubscribe(nsresult aStatus, bool aSuccess) override - { - if (NS_SUCCEEDED(aStatus)) { - mPromise->MaybeResolve(aSuccess); - } else { - mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); - } - - return NS_OK; - } - -private: - ~UnsubscribeResultCallback() - {} - - RefPtr mPromise; -}; - -NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback) - -already_AddRefed -PushSubscription::Unsubscribe(ErrorResult& aRv) -{ - MOZ_ASSERT(mPrincipal); - - nsCOMPtr service = - do_GetService("@mozilla.org/push/Service;1"); - if (NS_WARN_IF(!service)) { - aRv = NS_ERROR_FAILURE; - return nullptr; - } - - RefPtr p = Promise::Create(mGlobal, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr callback = - new UnsubscribeResultCallback(p); - Unused << NS_WARN_IF(NS_FAILED( - service->Unsubscribe(mScope, mPrincipal, callback))); - return p.forget(); -} - -void -PushSubscription::ToJSON(PushSubscriptionJSON& aJSON) -{ - SubscriptionToJSON(aJSON, mEndpoint, mRawP256dhKey, mAuthSecret); -} - -PushSubscription::PushSubscription(nsIGlobalObject* aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret) - : mGlobal(aGlobal) - , mEndpoint(aEndpoint) - , mScope(aScope) - , mRawP256dhKey(aRawP256dhKey) - , mAuthSecret(aAuthSecret) -{ -} - -PushSubscription::~PushSubscription() -{ -} - -JSObject* -PushSubscription::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto); -} - -void -PushSubscription::GetKey(JSContext* aCx, - PushEncryptionKeyName aType, - JS::MutableHandle aKey) -{ - if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) { - aKey.set(ArrayBuffer::Create(aCx, - mRawP256dhKey.Length(), - mRawP256dhKey.Elements())); - } else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) { - aKey.set(ArrayBuffer::Create(aCx, - mAuthSecret.Length(), - mAuthSecret.Elements())); - } else { - aKey.set(nullptr); - } -} - -void -PushSubscription::SetPrincipal(nsIPrincipal* aPrincipal) -{ - MOZ_ASSERT(!mPrincipal); - mPrincipal = aPrincipal; -} - -// static -already_AddRefed -PushSubscription::Constructor(GlobalObject& aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const Nullable& aP256dhKey, - const Nullable& aAuthSecret, - ErrorResult& aRv) -{ - MOZ_ASSERT(!aEndpoint.IsEmpty()); - MOZ_ASSERT(!aScope.IsEmpty()); - - nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - - nsTArray rawKey; - if (!aP256dhKey.IsNull()) { - const ArrayBuffer& key = aP256dhKey.Value(); - key.ComputeLengthAndData(); - rawKey.InsertElementsAt(0, key.Data(), key.Length()); - } - - nsTArray authSecret; - if (!aAuthSecret.IsNull()) { - const ArrayBuffer& sekrit = aAuthSecret.Value(); - sekrit.ComputeLengthAndData(); - authSecret.InsertElementsAt(0, sekrit.Data(), sekrit.Length()); - } - RefPtr sub = new PushSubscription(global, - aEndpoint, - aScope, - rawKey, - authSecret); - - return sub.forget(); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mPrincipal) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope) : mGlobal(aGlobal), mScope(aScope) { @@ -298,259 +123,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -// WorkerPushSubscription - -WorkerPushSubscription::WorkerPushSubscription(const nsAString& aEndpoint, - const nsAString& aScope, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret) - : mEndpoint(aEndpoint) - , mScope(aScope) - , mRawP256dhKey(aRawP256dhKey) - , mAuthSecret(aAuthSecret) -{ - MOZ_ASSERT(!aScope.IsEmpty()); - MOZ_ASSERT(!aEndpoint.IsEmpty()); -} - -WorkerPushSubscription::~WorkerPushSubscription() -{} - -JSObject* -WorkerPushSubscription::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return PushSubscriptionBinding_workers::Wrap(aCx, this, aGivenProto); -} - -// static -already_AddRefed -WorkerPushSubscription::Constructor(GlobalObject& aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const Nullable& aP256dhKey, - const Nullable& aAuthSecret, - ErrorResult& aRv) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - nsTArray rawKey; - if (!aP256dhKey.IsNull()) { - const ArrayBuffer& key = aP256dhKey.Value(); - key.ComputeLengthAndData(); - rawKey.SetLength(key.Length()); - rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length()); - } - - nsTArray authSecret; - if (!aAuthSecret.IsNull()) { - const ArrayBuffer& sekrit = aAuthSecret.Value(); - sekrit.ComputeLengthAndData(); - authSecret.SetLength(sekrit.Length()); - authSecret.ReplaceElementsAt(0, sekrit.Length(), - sekrit.Data(), sekrit.Length()); - } - RefPtr sub = new WorkerPushSubscription(aEndpoint, - aScope, - rawKey, - authSecret); - - return sub.forget(); -} - -void -WorkerPushSubscription::GetKey(JSContext* aCx, - PushEncryptionKeyName aType, - JS::MutableHandle aKey) -{ - if (aType == mozilla::dom::PushEncryptionKeyName::P256dh && - !mRawP256dhKey.IsEmpty()) { - aKey.set(ArrayBuffer::Create(aCx, - mRawP256dhKey.Length(), - mRawP256dhKey.Elements())); - } else if (aType == mozilla::dom::PushEncryptionKeyName::Auth && - !mAuthSecret.IsEmpty()) { - aKey.set(ArrayBuffer::Create(aCx, - mAuthSecret.Length(), - mAuthSecret.Elements())); - } else { - aKey.set(nullptr); - } -} - -class UnsubscribeResultRunnable final : public WorkerRunnable -{ -public: - UnsubscribeResultRunnable(PromiseWorkerProxy* aProxy, - nsresult aStatus, - bool aSuccess) - : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount) - , mProxy(aProxy) - , mStatus(aStatus) - , mSuccess(aSuccess) - { - AssertIsOnMainThread(); - } - - bool - WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override - { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); - - RefPtr promise = mProxy->WorkerPromise(); - if (NS_SUCCEEDED(mStatus)) { - promise->MaybeResolve(mSuccess); - } else { - promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); - } - - mProxy->CleanUp(); - return true; - } -private: - ~UnsubscribeResultRunnable() - {} - - RefPtr mProxy; - nsresult mStatus; - bool mSuccess; -}; - -class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback -{ -public: - NS_DECL_ISUPPORTS - - explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy) - : mProxy(aProxy) - { - AssertIsOnMainThread(); - } - - NS_IMETHOD - OnUnsubscribe(nsresult aStatus, bool aSuccess) override - { - AssertIsOnMainThread(); - MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?"); - - RefPtr proxy = mProxy.forget(); - - MutexAutoLock lock(proxy->Lock()); - if (proxy->CleanedUp()) { - return NS_OK; - } - - RefPtr r = - new UnsubscribeResultRunnable(proxy, aStatus, aSuccess); - r->Dispatch(); - return NS_OK; - } - -private: - ~WorkerUnsubscribeResultCallback() - { - } - - RefPtr mProxy; -}; - -NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback) - -class UnsubscribeRunnable final : public nsRunnable -{ -public: - UnsubscribeRunnable(PromiseWorkerProxy* aProxy, - const nsAString& aScope) - : mProxy(aProxy) - , mScope(aScope) - { - MOZ_ASSERT(aProxy); - MOZ_ASSERT(!aScope.IsEmpty()); - } - - NS_IMETHOD - Run() override - { - AssertIsOnMainThread(); - - nsCOMPtr principal; - { - MutexAutoLock lock(mProxy->Lock()); - if (mProxy->CleanedUp()) { - return NS_OK; - } - principal = mProxy->GetWorkerPrivate()->GetPrincipal(); - } - MOZ_ASSERT(principal); - - RefPtr callback = - new WorkerUnsubscribeResultCallback(mProxy); - - nsCOMPtr service = - do_GetService("@mozilla.org/push/Service;1"); - if (!service) { - callback->OnUnsubscribe(NS_ERROR_FAILURE, false); - return NS_OK; - } - - if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) { - callback->OnUnsubscribe(NS_ERROR_FAILURE, false); - return NS_OK; - } - return NS_OK; - } - -private: - ~UnsubscribeRunnable() - {} - - RefPtr mProxy; - nsString mScope; -}; - -already_AddRefed -WorkerPushSubscription::Unsubscribe(ErrorResult &aRv) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - nsCOMPtr global = worker->GlobalScope(); - RefPtr p = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr proxy = PromiseWorkerProxy::Create(worker, p); - if (!proxy) { - p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); - return p.forget(); - } - - RefPtr r = - new UnsubscribeRunnable(proxy, mScope); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); - - return p.forget(); -} - -void -WorkerPushSubscription::ToJSON(PushSubscriptionJSON& aJSON) -{ - SubscriptionToJSON(aJSON, mEndpoint, mRawP256dhKey, mAuthSecret); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushSubscription) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushSubscription) -NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushSubscription) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushSubscription) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - // WorkerPushManager WorkerPushManager::WorkerPushManager(const nsAString& aScope) @@ -590,9 +162,9 @@ public: if (mEndpoint.IsEmpty()) { promise->MaybeResolve(JS::NullHandleValue); } else { - RefPtr sub = - new WorkerPushSubscription(mEndpoint, mScope, - mRawP256dhKey, mAuthSecret); + RefPtr sub = + new PushSubscription(nullptr, mEndpoint, mScope, mRawP256dhKey, + mAuthSecret); promise->MaybeResolve(sub); } } else if (NS_ERROR_GET_MODULE(mStatus) == NS_ERROR_MODULE_DOM_PUSH ) { diff --git a/dom/push/PushManager.h b/dom/push/PushManager.h index 2870bca29711..53d99c573fcc 100644 --- a/dom/push/PushManager.h +++ b/dom/push/PushManager.h @@ -45,8 +45,6 @@ class nsIGlobalObject; class nsIPrincipal; -#include "mozilla/dom/PushSubscriptionBinding.h" - namespace mozilla { namespace dom { @@ -57,67 +55,7 @@ class WorkerPrivate; class Promise; class PushManagerImpl; -class PushSubscription final : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscription) - explicit PushSubscription(nsIGlobalObject* aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const nsTArray& aP256dhKey, - const nsTArray& aAuthSecret); - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - nsIGlobalObject* - GetParentObject() const - { - return mGlobal; - } - - void - GetEndpoint(nsAString& aEndpoint) const - { - aEndpoint = mEndpoint; - } - - void - GetKey(JSContext* cx, - PushEncryptionKeyName aType, - JS::MutableHandle aKey); - - static already_AddRefed - Constructor(GlobalObject& aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const Nullable& aP256dhKey, - const Nullable& aAuthSecret, - ErrorResult& aRv); - - void - SetPrincipal(nsIPrincipal* aPrincipal); - - already_AddRefed - Unsubscribe(ErrorResult& aRv); - - void - ToJSON(PushSubscriptionJSON& aJSON); - -protected: - ~PushSubscription(); - -private: - nsCOMPtr mGlobal; - nsCOMPtr mPrincipal; - nsString mEndpoint; - nsString mScope; - nsTArray mRawP256dhKey; - nsTArray mAuthSecret; -}; class PushManager final : public nsISupports , public nsWrapperCache @@ -158,61 +96,6 @@ private: nsString mScope; }; -class WorkerPushSubscription final : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushSubscription) - - explicit WorkerPushSubscription(const nsAString& aEndpoint, - const nsAString& aScope, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret); - - nsIGlobalObject* - GetParentObject() const - { - return nullptr; - } - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - static already_AddRefed - Constructor(GlobalObject& aGlobal, - const nsAString& aEndpoint, - const nsAString& aScope, - const Nullable& aP256dhKey, - const Nullable& aAuthSecret, - ErrorResult& aRv); - - void - GetEndpoint(nsAString& aEndpoint) const - { - aEndpoint = mEndpoint; - } - - void - GetKey(JSContext* cx, PushEncryptionKeyName aType, - JS::MutableHandle aP256dhKey); - - already_AddRefed - Unsubscribe(ErrorResult& aRv); - - void - ToJSON(PushSubscriptionJSON& aJSON); - -protected: - ~WorkerPushSubscription(); - -private: - nsString mEndpoint; - nsString mScope; - nsTArray mRawP256dhKey; - nsTArray mAuthSecret; -}; - class WorkerPushManager final : public nsISupports , public nsWrapperCache { diff --git a/dom/push/PushSubscription.cpp b/dom/push/PushSubscription.cpp new file mode 100644 index 000000000000..98b10e2631c0 --- /dev/null +++ b/dom/push/PushSubscription.cpp @@ -0,0 +1,372 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/PushSubscription.h" + +#include "nsIPushService.h" +#include "nsIScriptObjectPrincipal.h" + +#include "mozilla/Base64.h" +#include "mozilla/unused.h" + +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseWorkerProxy.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerScope.h" +#include "mozilla/dom/workers/Workers.h" + +namespace mozilla { +namespace dom { + +using namespace workers; + +namespace { + +class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback +{ +public: + NS_DECL_ISUPPORTS + + explicit UnsubscribeResultCallback(Promise* aPromise) + : mPromise(aPromise) + { + AssertIsOnMainThread(); + } + + NS_IMETHOD + OnUnsubscribe(nsresult aStatus, bool aSuccess) override + { + if (NS_SUCCEEDED(aStatus)) { + mPromise->MaybeResolve(aSuccess); + } else { + mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); + } + + return NS_OK; + } + +private: + ~UnsubscribeResultCallback() + {} + + RefPtr mPromise; +}; + +NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback) + +class UnsubscribeResultRunnable final : public WorkerRunnable +{ +public: + UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate, + already_AddRefed&& aProxy, + nsresult aStatus, + bool aSuccess) + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) + , mProxy(Move(aProxy)) + , mStatus(aStatus) + , mSuccess(aSuccess) + { + AssertIsOnMainThread(); + } + + bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + RefPtr promise = mProxy->WorkerPromise(); + if (NS_SUCCEEDED(mStatus)) { + promise->MaybeResolve(mSuccess); + } else { + promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); + } + + mProxy->CleanUp(); + + return true; + } +private: + ~UnsubscribeResultRunnable() + {} + + RefPtr mProxy; + nsresult mStatus; + bool mSuccess; +}; + +class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback +{ +public: + NS_DECL_ISUPPORTS + + explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy) + : mProxy(aProxy) + { + AssertIsOnMainThread(); + } + + NS_IMETHOD + OnUnsubscribe(nsresult aStatus, bool aSuccess) override + { + AssertIsOnMainThread(); + MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?"); + + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { + return NS_OK; + } + + WorkerPrivate* worker = mProxy->GetWorkerPrivate(); + RefPtr r = + new UnsubscribeResultRunnable(worker, mProxy.forget(), aStatus, aSuccess); + MOZ_ALWAYS_TRUE(r->Dispatch()); + + return NS_OK; + } + +private: + ~WorkerUnsubscribeResultCallback() + { + } + + RefPtr mProxy; +}; + +NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback) + +class UnsubscribeRunnable final : public nsRunnable +{ +public: + UnsubscribeRunnable(PromiseWorkerProxy* aProxy, + const nsAString& aScope) + : mProxy(aProxy) + , mScope(aScope) + { + MOZ_ASSERT(aProxy); + MOZ_ASSERT(!aScope.IsEmpty()); + } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + + nsCOMPtr principal; + + { + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { + return NS_OK; + } + principal = mProxy->GetWorkerPrivate()->GetPrincipal(); + } + + MOZ_ASSERT(principal); + + RefPtr callback = + new WorkerUnsubscribeResultCallback(mProxy); + + nsCOMPtr service = + do_GetService("@mozilla.org/push/Service;1"); + if (NS_WARN_IF(!service)) { + callback->OnUnsubscribe(NS_ERROR_FAILURE, false); + return NS_OK; + } + + if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) { + callback->OnUnsubscribe(NS_ERROR_FAILURE, false); + return NS_OK; + } + + return NS_OK; + } + +private: + ~UnsubscribeRunnable() + {} + + RefPtr mProxy; + nsString mScope; +}; + +} // anonymous namespace + +PushSubscription::PushSubscription(nsIGlobalObject* aGlobal, + const nsAString& aEndpoint, + const nsAString& aScope, + const nsTArray& aRawP256dhKey, + const nsTArray& aAuthSecret) + : mEndpoint(aEndpoint) + , mScope(aScope) + , mRawP256dhKey(aRawP256dhKey) + , mAuthSecret(aAuthSecret) +{ + if (NS_IsMainThread()) { + mGlobal = aGlobal; + } else { +#ifdef DEBUG + // There's only one global on a worker, so we don't need to pass a global + // object to the constructor. + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); +#endif + } +} + +PushSubscription::~PushSubscription() +{} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +PushSubscription::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto); +} + +// static +already_AddRefed +PushSubscription::Constructor(GlobalObject& aGlobal, + const nsAString& aEndpoint, + const nsAString& aScope, + const Nullable& aP256dhKey, + const Nullable& aAuthSecret, + ErrorResult& aRv) +{ + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + + nsTArray rawKey; + if (!aP256dhKey.IsNull()) { + const ArrayBuffer& key = aP256dhKey.Value(); + key.ComputeLengthAndData(); + rawKey.SetLength(key.Length()); + rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length()); + } + + nsTArray authSecret; + if (!aAuthSecret.IsNull()) { + const ArrayBuffer& sekrit = aAuthSecret.Value(); + sekrit.ComputeLengthAndData(); + authSecret.SetLength(sekrit.Length()); + authSecret.ReplaceElementsAt(0, sekrit.Length(), + sekrit.Data(), sekrit.Length()); + } + RefPtr sub = new PushSubscription(global, + aEndpoint, + aScope, + rawKey, + authSecret); + + return sub.forget(); +} + +already_AddRefed +PushSubscription::Unsubscribe(ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + RefPtr p = UnsubscribeFromWorker(aRv); + return p.forget(); + } + + MOZ_ASSERT(mGlobal); + + nsCOMPtr service = + do_GetService("@mozilla.org/push/Service;1"); + if (NS_WARN_IF(!service)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr sop = do_QueryInterface(mGlobal); + if (!sop) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr p = Promise::Create(mGlobal, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr callback = + new UnsubscribeResultCallback(p); + Unused << NS_WARN_IF(NS_FAILED( + service->Unsubscribe(mScope, sop->GetPrincipal(), callback))); + + return p.forget(); +} + +void +PushSubscription::GetKey(JSContext* aCx, + PushEncryptionKeyName aType, + JS::MutableHandle aKey) +{ + if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) { + aKey.set(ArrayBuffer::Create(aCx, + mRawP256dhKey.Length(), + mRawP256dhKey.Elements())); + } else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) { + aKey.set(ArrayBuffer::Create(aCx, + mAuthSecret.Length(), + mAuthSecret.Elements())); + } else { + aKey.set(nullptr); + } +} + +void +PushSubscription::ToJSON(PushSubscriptionJSON& aJSON) +{ + aJSON.mEndpoint.Construct(); + aJSON.mEndpoint.Value() = mEndpoint; + + aJSON.mKeys.mP256dh.Construct(); + nsresult rv = Base64URLEncode(mRawP256dhKey.Length(), + mRawP256dhKey.Elements(), + aJSON.mKeys.mP256dh.Value()); + Unused << NS_WARN_IF(NS_FAILED(rv)); + + aJSON.mKeys.mAuth.Construct(); + rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(), + aJSON.mKeys.mAuth.Value()); + Unused << NS_WARN_IF(NS_FAILED(rv)); +} + +already_AddRefed +PushSubscription::UnsubscribeFromWorker(ErrorResult& aRv) +{ + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + nsCOMPtr global = worker->GlobalScope(); + RefPtr p = Promise::Create(global, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr proxy = PromiseWorkerProxy::Create(worker, p); + if (!proxy) { + p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE); + return p.forget(); + } + + RefPtr r = + new UnsubscribeRunnable(proxy, mScope); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); + + return p.forget(); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/push/PushSubscription.h b/dom/push/PushSubscription.h new file mode 100644 index 000000000000..f80a39ed5729 --- /dev/null +++ b/dom/push/PushSubscription.h @@ -0,0 +1,95 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PushSubscription_h +#define mozilla_dom_PushSubscription_h + +#include "jsapi.h" +#include "nsCOMPtr.h" +#include "nsWrapperCache.h" + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/RefPtr.h" + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/PushSubscriptionBinding.h" +#include "mozilla/dom/TypedArray.h" + +class nsIGlobalObject; + +namespace mozilla { +namespace dom { + +namespace workers { +class WorkerPrivate; +} + +class Promise; + +class PushSubscription final : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscription) + + PushSubscription(nsIGlobalObject* aGlobal, + const nsAString& aEndpoint, + const nsAString& aScope, + const nsTArray& aP256dhKey, + const nsTArray& aAuthSecret); + + JSObject* + WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + nsIGlobalObject* + GetParentObject() const + { + return mGlobal; + } + + void + GetEndpoint(nsAString& aEndpoint) const + { + aEndpoint = mEndpoint; + } + + void + GetKey(JSContext* cx, + PushEncryptionKeyName aType, + JS::MutableHandle aKey); + + static already_AddRefed + Constructor(GlobalObject& aGlobal, + const nsAString& aEndpoint, + const nsAString& aScope, + const Nullable& aP256dhKey, + const Nullable& aAuthSecret, + ErrorResult& aRv); + + already_AddRefed + Unsubscribe(ErrorResult& aRv); + + void + ToJSON(PushSubscriptionJSON& aJSON); + +protected: + ~PushSubscription(); + +private: + already_AddRefed + UnsubscribeFromWorker(ErrorResult& aRv); + + nsString mEndpoint; + nsString mScope; + nsTArray mRawP256dhKey; + nsTArray mAuthSecret; + nsCOMPtr mGlobal; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_PushSubscription_h diff --git a/dom/push/moz.build b/dom/push/moz.build index ea6bfcd608b2..06830e6b7217 100644 --- a/dom/push/moz.build +++ b/dom/push/moz.build @@ -39,11 +39,13 @@ XPCSHELL_TESTS_MANIFESTS += [ EXPORTS.mozilla.dom += [ 'PushManager.h', 'PushNotifier.h', + 'PushSubscription.h', ] UNIFIED_SOURCES += [ 'PushManager.cpp', 'PushNotifier.cpp', + 'PushSubscription.cpp', ] TEST_DIRS += ['test/xpcshell'] diff --git a/dom/webidl/PushSubscription.webidl b/dom/webidl/PushSubscription.webidl index e8536a58f188..725f6cc248df 100644 --- a/dom/webidl/PushSubscription.webidl +++ b/dom/webidl/PushSubscription.webidl @@ -39,8 +39,4 @@ interface PushSubscription // Implements the custom serializer specified in Push API, section 9. PushSubscriptionJSON toJSON(); - - // Used to set the principal from the JS implemented PushManager. - [Exposed=Window,ChromeOnly] - void setPrincipal(Principal principal); }; From 2bfd46b8600edcea25ff120e150a788a7e17beed Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Fri, 1 Apr 2016 15:25:49 -0700 Subject: [PATCH 37/53] Bug 1257401 - Remove the worker descriptor for `PushManager`. r=khuey MozReview-Commit-ID: 4nZElH1K3W5 --HG-- extra : rebase_source : d325e2fc44124acc0bd04b133605bf04a99ab906 --- dom/bindings/Bindings.conf | 11 +- dom/push/Push.js | 3 +- dom/push/PushManager.cpp | 302 +++++++++++----------- dom/push/PushManager.h | 95 +++---- dom/push/PushSubscription.cpp | 46 ++-- dom/push/PushSubscription.h | 4 +- dom/webidl/PushManager.webidl | 27 +- dom/workers/ServiceWorkerRegistration.cpp | 32 +-- dom/workers/ServiceWorkerRegistration.h | 22 +- 9 files changed, 236 insertions(+), 306 deletions(-) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 0c243f60ead5..47d1e0fb75cf 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -955,16 +955,6 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::workers::PushMessageData', }, -'PushManager': [{ - 'workers': False, - 'headerFile': 'mozilla/dom/PushManager.h', - 'nativeType': 'mozilla::dom::PushManager', -}, { - 'workers': True, - 'headerFile': 'mozilla/dom/PushManager.h', - 'nativeType': 'mozilla::dom::WorkerPushManager', -}], - 'Range': { 'nativeType': 'nsRange', 'binaryNames': { @@ -1008,6 +998,7 @@ DOMInterfaces = { 'ServiceWorkerRegistration': [{ 'nativeType': 'mozilla::dom::ServiceWorkerRegistrationMainThread', 'headerFile': 'mozilla/dom/ServiceWorkerRegistration.h', + 'implicitJSContext': [ 'pushManager' ], }, { 'workers': True, 'nativeType': 'mozilla::dom::ServiceWorkerRegistrationWorkerThread', diff --git a/dom/push/Push.js b/dom/push/Push.js index 30c4dbc88d8f..532303b13366 100644 --- a/dom/push/Push.js +++ b/dom/push/Push.js @@ -56,8 +56,7 @@ Push.prototype = { this._principal = aWindow.document.nodePrincipal; }, - setScope: function(scope){ - console.debug("setScope()", scope); + __init: function(scope) { this._scope = scope; }, diff --git a/dom/push/PushManager.cpp b/dom/push/PushManager.cpp index 01eb684148a9..cbeecb977285 100644 --- a/dom/push/PushManager.cpp +++ b/dom/push/PushManager.cpp @@ -6,13 +6,10 @@ #include "mozilla/dom/PushManager.h" -#include "mozilla/Base64.h" -#include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/unused.h" #include "mozilla/dom/PushManagerBinding.h" #include "mozilla/dom/PushSubscription.h" -#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" @@ -23,8 +20,7 @@ #include "nsIPushService.h" #include "nsComponentManagerUtils.h" -#include "nsFrameMessageManager.h" -#include "nsContentCID.h" +#include "nsContentUtils.h" #include "WorkerRunnable.h" #include "WorkerPrivate.h" @@ -39,7 +35,7 @@ namespace { nsresult GetPermissionState(nsIPrincipal* aPrincipal, - PushPermissionState& aState) + PushPermissionState& aState) { nsCOMPtr permManager = mozilla::services::GetPermissionManager(); @@ -63,95 +59,27 @@ GetPermissionState(nsIPrincipal* aPrincipal, } else { aState = PushPermissionState::Prompt; } + return NS_OK; } -} // anonymous namespace - -PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope) - : mGlobal(aGlobal), mScope(aScope) -{ - AssertIsOnMainThread(); -} - -PushManager::~PushManager() -{} - -JSObject* -PushManager::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - // XXXnsm I don't know if this is the right way to do it, but I want to assert - // that an implementation has been set before this object gets exposed to JS. - MOZ_ASSERT(mImpl); - return PushManagerBinding::Wrap(aCx, this, aGivenProto); -} - -void -PushManager::SetPushManagerImpl(PushManagerImpl& foo, ErrorResult& aRv) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mImpl); - mImpl = &foo; -} - -already_AddRefed -PushManager::Subscribe(ErrorResult& aRv) -{ - MOZ_ASSERT(mImpl); - return mImpl->Subscribe(aRv); -} - -already_AddRefed -PushManager::GetSubscription(ErrorResult& aRv) -{ - MOZ_ASSERT(mImpl); - return mImpl->GetSubscription(aRv); -} - -already_AddRefed -PushManager::PermissionState(ErrorResult& aRv) -{ - MOZ_ASSERT(mImpl); - return mImpl->PermissionState(aRv); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushManager, mGlobal, mImpl) -NS_IMPL_CYCLE_COLLECTING_ADDREF(PushManager) -NS_IMPL_CYCLE_COLLECTING_RELEASE(PushManager) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -// WorkerPushManager - -WorkerPushManager::WorkerPushManager(const nsAString& aScope) - : mScope(aScope) -{ -} - -JSObject* -WorkerPushManager::WrapObject(JSContext* aCx, JS::Handle aGivenProto) -{ - return PushManagerBinding_workers::Wrap(aCx, this, aGivenProto); -} - class GetSubscriptionResultRunnable final : public WorkerRunnable { public: - GetSubscriptionResultRunnable(PromiseWorkerProxy* aProxy, + GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate, + already_AddRefed&& aProxy, nsresult aStatus, const nsAString& aEndpoint, const nsAString& aScope, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret) - : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount) - , mProxy(aProxy) + nsTArray&& aRawP256dhKey, + nsTArray&& aAuthSecret) + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) + , mProxy(Move(aProxy)) , mStatus(aStatus) , mEndpoint(aEndpoint) , mScope(aScope) - , mRawP256dhKey(aRawP256dhKey) - , mAuthSecret(aAuthSecret) + , mRawP256dhKey(Move(aRawP256dhKey)) + , mAuthSecret(Move(aAuthSecret)) { } bool @@ -163,8 +91,8 @@ public: promise->MaybeResolve(JS::NullHandleValue); } else { RefPtr sub = - new PushSubscription(nullptr, mEndpoint, mScope, mRawP256dhKey, - mAuthSecret); + new PushSubscription(nullptr, mEndpoint, mScope, + Move(mRawP256dhKey), Move(mAuthSecret)); promise->MaybeResolve(sub); } } else if (NS_ERROR_GET_MODULE(mStatus) == NS_ERROR_MODULE_DOM_PUSH ) { @@ -174,6 +102,7 @@ public: } mProxy->CleanUp(); + return true; } private: @@ -206,10 +135,8 @@ public: AssertIsOnMainThread(); MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?"); - RefPtr proxy = mProxy.forget(); - - MutexAutoLock lock(proxy->Lock()); - if (proxy->CleanedUp()) { + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { return NS_OK; } @@ -220,14 +147,17 @@ public: authSecret); } + WorkerPrivate* worker = mProxy->GetWorkerPrivate(); RefPtr r = - new GetSubscriptionResultRunnable(proxy, + new GetSubscriptionResultRunnable(worker, + mProxy.forget(), aStatus, endpoint, mScope, - rawP256dhKey, - authSecret); - r->Dispatch(); + Move(rawP256dhKey), + Move(authSecret)); + MOZ_ALWAYS_TRUE(r->Dispatch()); + return NS_OK; } @@ -249,6 +179,7 @@ private: { NS_Free(aKey); NS_Free(aAuthSecret); + return aStatus; } @@ -306,7 +237,7 @@ class GetSubscriptionRunnable final : public nsRunnable public: GetSubscriptionRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope, - WorkerPushManager::SubscriptionAction aAction) + PushManager::SubscriptionAction aAction) : mProxy(aProxy) , mScope(aScope), mAction(aAction) {} @@ -317,6 +248,7 @@ public: AssertIsOnMainThread(); nsCOMPtr principal; + { // Bug 1228723: If permission is revoked or an error occurs, the // subscription callback will be called synchronously. This causes @@ -328,6 +260,7 @@ public: } principal = mProxy->GetWorkerPrivate()->GetPrincipal(); } + MOZ_ASSERT(principal); RefPtr callback = new GetSubscriptionCallback(mProxy, mScope); @@ -340,7 +273,7 @@ public: } if (state != PushPermissionState::Granted) { - if (mAction == WorkerPushManager::GetSubscriptionAction) { + if (mAction == PushManager::GetSubscriptionAction) { callback->OnPushSubscriptionError(NS_OK); return NS_OK; } @@ -350,15 +283,15 @@ public: nsCOMPtr service = do_GetService("@mozilla.org/push/Service;1"); - if (!service) { + if (NS_WARN_IF(!service)) { callback->OnPushSubscriptionError(NS_ERROR_FAILURE); return NS_OK; } - if (mAction == WorkerPushManager::SubscribeAction) { + if (mAction == PushManager::SubscribeAction) { rv = service->Subscribe(mScope, principal, callback); } else { - MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction); + MOZ_ASSERT(mAction == PushManager::GetSubscriptionAction); rv = service->GetSubscription(mScope, principal, callback); } @@ -376,47 +309,9 @@ private: RefPtr mProxy; nsString mScope; - WorkerPushManager::SubscriptionAction mAction; + PushManager::SubscriptionAction mAction; }; -already_AddRefed -WorkerPushManager::PerformSubscriptionAction(SubscriptionAction aAction, ErrorResult& aRv) -{ - WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(worker); - worker->AssertIsOnWorkerThread(); - - nsCOMPtr global = worker->GlobalScope(); - RefPtr p = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - RefPtr proxy = PromiseWorkerProxy::Create(worker, p); - if (!proxy) { - p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR); - return p.forget(); - } - - RefPtr r = - new GetSubscriptionRunnable(proxy, mScope, aAction); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); - - return p.forget(); -} - -already_AddRefed -WorkerPushManager::Subscribe(ErrorResult& aRv) -{ - return PerformSubscriptionAction(SubscribeAction, aRv); -} - -already_AddRefed -WorkerPushManager::GetSubscription(ErrorResult& aRv) -{ - return PerformSubscriptionAction(GetSubscriptionAction, aRv); -} - class PermissionResultRunnable final : public WorkerRunnable { public: @@ -445,6 +340,7 @@ public: } mProxy->CleanUp(); + return true; } @@ -481,7 +377,8 @@ public: RefPtr r = new PermissionResultRunnable(mProxy, rv, state); - r->Dispatch(); + MOZ_ALWAYS_TRUE(r->Dispatch()); + return NS_OK; } @@ -492,9 +389,99 @@ private: RefPtr mProxy; }; -already_AddRefed -WorkerPushManager::PermissionState(ErrorResult& aRv) +} // anonymous namespace + +PushManager::PushManager(nsIGlobalObject* aGlobal, PushManagerImpl* aImpl) + : mGlobal(aGlobal) + , mImpl(aImpl) { + AssertIsOnMainThread(); + MOZ_ASSERT(aImpl); +} + +PushManager::PushManager(const nsAString& aScope) + : mScope(aScope) +{ +#ifdef DEBUG + // There's only one global on a worker, so we don't need to pass a global + // object to the constructor. + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); +#endif +} + +PushManager::~PushManager() +{} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushManager, mGlobal, mImpl) +NS_IMPL_CYCLE_COLLECTING_ADDREF(PushManager) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PushManager) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* +PushManager::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return PushManagerBinding::Wrap(aCx, this, aGivenProto); +} + +// static +already_AddRefed +PushManager::Constructor(GlobalObject& aGlobal, + const nsAString& aScope, + ErrorResult& aRv) +{ + if (!NS_IsMainThread()) { + RefPtr ret = new PushManager(aScope); + return ret.forget(); + } + + RefPtr impl = PushManagerImpl::Constructor(aGlobal, + aGlobal.Context(), + aScope, aRv); + if (aRv.Failed()) { + return nullptr; + } + + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr ret = new PushManager(global, impl); + + return ret.forget(); +} + +already_AddRefed +PushManager::Subscribe(ErrorResult& aRv) +{ + if (mImpl) { + MOZ_ASSERT(NS_IsMainThread()); + return mImpl->Subscribe(aRv); + } + + return PerformSubscriptionActionFromWorker(SubscribeAction, aRv); +} + +already_AddRefed +PushManager::GetSubscription(ErrorResult& aRv) +{ + if (mImpl) { + MOZ_ASSERT(NS_IsMainThread()); + return mImpl->GetSubscription(aRv); + } + + return PerformSubscriptionActionFromWorker(GetSubscriptionAction, aRv); +} + +already_AddRefed +PushManager::PermissionState(ErrorResult& aRv) +{ + if (mImpl) { + MOZ_ASSERT(NS_IsMainThread()); + return mImpl->PermissionState(aRv); + } + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); @@ -518,15 +505,32 @@ WorkerPushManager::PermissionState(ErrorResult& aRv) return p.forget(); } -WorkerPushManager::~WorkerPushManager() -{} +already_AddRefed +PushManager::PerformSubscriptionActionFromWorker( + SubscriptionAction aAction, ErrorResult& aRv) +{ + WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); + + nsCOMPtr global = worker->GlobalScope(); + RefPtr p = Promise::Create(global, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + RefPtr proxy = PromiseWorkerProxy::Create(worker, p); + if (!proxy) { + p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR); + return p.forget(); + } + + RefPtr r = + new GetSubscriptionRunnable(proxy, mScope, aAction); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); + + return p.forget(); +} -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushManager) -NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushManager) -NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushManager) -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushManager) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END } // namespace dom } // namespace mozilla diff --git a/dom/push/PushManager.h b/dom/push/PushManager.h index 53d99c573fcc..8433c5eda27d 100644 --- a/dom/push/PushManager.h +++ b/dom/push/PushManager.h @@ -5,24 +5,19 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * We would like to expose PushManager and PushSubscription on window and - * workers. Parts of the Push API is implemented in JS out of necessity due to: - * 1) Using frame message managers, in which - * nsIMessageListener::receiveMessage() must be in JS. - * 2) It is easier to use certain APIs like the permission prompt and Promises - * from JS. + * PushManager and PushSubscription are exposed on the main and worker threads. + * The main thread version is implemented in Push.js. The JS implementation + * makes it easier to use certain APIs like the permission prompt and Promises. * - * Unfortunately, JS-implemented WebIDL is not supported off the main thread. To - * aid in fixing this, the nsIPushClient is introduced which deals with part (1) - * above. Part (2) is handled by PushManagerImpl on the main thread. PushManager - * wraps this in C++ since our bindings code cannot accomodate "JS-implemented - * on the main thread, C++ on the worker" bindings. PushManager simply forwards - * the calls to the JS component. + * Unfortunately, JS-implemented WebIDL is not supported off the main thread. + * To work around this, we use a chain of runnables to query the JS-implemented + * nsIPushService component for subscription information, and return the + * results to the worker. We don't have to deal with permission prompts, since + * we just reject calls if the principal does not have permission. * - * On the worker threads, we don't have to deal with permission prompts, instead - * we just reject calls if the principal does not have permission. On workers - * WorkerPushManager dispatches runnables to the main thread which directly call - * nsIPushClient. + * On the main thread, PushManager wraps a JS-implemented PushManagerImpl + * instance. The C++ wrapper is necessary because our bindings code cannot + * accomodate "JS-implemented on the main thread, C++ on the worker" bindings. * * PushSubscription is in C++ on both threads since it isn't particularly * verbose to implement in C++ compared to JS. @@ -40,7 +35,6 @@ #include "nsCOMPtr.h" #include "mozilla/RefPtr.h" -#include "jsapi.h" class nsIGlobalObject; class nsIPrincipal; @@ -55,8 +49,6 @@ class WorkerPrivate; class Promise; class PushManagerImpl; - - class PushManager final : public nsISupports , public nsWrapperCache { @@ -64,7 +56,16 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushManager) - explicit PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope); + enum SubscriptionAction { + SubscribeAction, + GetSubscriptionAction, + }; + + // The main thread constructor. + PushManager(nsIGlobalObject* aGlobal, PushManagerImpl* aImpl); + + // The worker thread constructor. + explicit PushManager(const nsAString& aScope); nsIGlobalObject* GetParentObject() const @@ -75,6 +76,14 @@ public: JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + static already_AddRefed + Constructor(GlobalObject& aGlobal, const nsAString& aScope, + ErrorResult& aRv); + + already_AddRefed + PerformSubscriptionActionFromWorker(SubscriptionAction aAction, + ErrorResult& aRv); + already_AddRefed Subscribe(ErrorResult& aRv); @@ -84,57 +93,15 @@ public: already_AddRefed PermissionState(ErrorResult& aRv); - void - SetPushManagerImpl(PushManagerImpl& foo, ErrorResult& aRv); - protected: ~PushManager(); private: + // The following are only set and accessed on the main thread. nsCOMPtr mGlobal; RefPtr mImpl; - nsString mScope; -}; -class WorkerPushManager final : public nsISupports - , public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushManager) - - enum SubscriptionAction { - SubscribeAction, - GetSubscriptionAction, - }; - - explicit WorkerPushManager(const nsAString& aScope); - - nsIGlobalObject* - GetParentObject() const - { - return nullptr; - } - - JSObject* - WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - - already_AddRefed - PerformSubscriptionAction(SubscriptionAction aAction, ErrorResult& aRv); - - already_AddRefed - Subscribe(ErrorResult& aRv); - - already_AddRefed - GetSubscription(ErrorResult& aRv); - - already_AddRefed - PermissionState(ErrorResult& aRv); - -protected: - ~WorkerPushManager(); - -private: + // Only used on the worker thread. nsString mScope; }; } // namespace dom diff --git a/dom/push/PushSubscription.cpp b/dom/push/PushSubscription.cpp index 98b10e2631c0..5af0523242c6 100644 --- a/dom/push/PushSubscription.cpp +++ b/dom/push/PushSubscription.cpp @@ -191,17 +191,30 @@ private: nsString mScope; }; +bool +CopyArrayBufferToArray(const ArrayBuffer& aBuffer, + nsTArray& aArray) +{ + aBuffer.ComputeLengthAndData(); + if (!aArray.SetLength(aBuffer.Length(), fallible) || + !aArray.ReplaceElementsAt(0, aBuffer.Length(), aBuffer.Data(), + aBuffer.Length(), fallible)) { + return false; + } + return true; +} + } // anonymous namespace PushSubscription::PushSubscription(nsIGlobalObject* aGlobal, const nsAString& aEndpoint, const nsAString& aScope, - const nsTArray& aRawP256dhKey, - const nsTArray& aAuthSecret) + nsTArray&& aRawP256dhKey, + nsTArray&& aAuthSecret) : mEndpoint(aEndpoint) , mScope(aScope) - , mRawP256dhKey(aRawP256dhKey) - , mAuthSecret(aAuthSecret) + , mRawP256dhKey(Move(aRawP256dhKey)) + , mAuthSecret(Move(aAuthSecret)) { if (NS_IsMainThread()) { mGlobal = aGlobal; @@ -245,27 +258,20 @@ PushSubscription::Constructor(GlobalObject& aGlobal, { nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - nsTArray rawKey; - if (!aP256dhKey.IsNull()) { - const ArrayBuffer& key = aP256dhKey.Value(); - key.ComputeLengthAndData(); - rawKey.SetLength(key.Length()); - rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length()); + nsTArray rawKey, authSecret; + if ((!aP256dhKey.IsNull() && !CopyArrayBufferToArray(aP256dhKey.Value(), + rawKey)) || + (!aAuthSecret.IsNull() && !CopyArrayBufferToArray(aAuthSecret.Value(), + authSecret))) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return nullptr; } - nsTArray authSecret; - if (!aAuthSecret.IsNull()) { - const ArrayBuffer& sekrit = aAuthSecret.Value(); - sekrit.ComputeLengthAndData(); - authSecret.SetLength(sekrit.Length()); - authSecret.ReplaceElementsAt(0, sekrit.Length(), - sekrit.Data(), sekrit.Length()); - } RefPtr sub = new PushSubscription(global, aEndpoint, aScope, - rawKey, - authSecret); + Move(rawKey), + Move(authSecret)); return sub.forget(); } diff --git a/dom/push/PushSubscription.h b/dom/push/PushSubscription.h index f80a39ed5729..c374c5c77bc0 100644 --- a/dom/push/PushSubscription.h +++ b/dom/push/PushSubscription.h @@ -38,8 +38,8 @@ public: PushSubscription(nsIGlobalObject* aGlobal, const nsAString& aEndpoint, const nsAString& aScope, - const nsTArray& aP256dhKey, - const nsTArray& aAuthSecret); + nsTArray&& aP256dhKey, + nsTArray&& aAuthSecret); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/webidl/PushManager.webidl b/dom/webidl/PushManager.webidl index 2a28fb6fc4b4..7e3404db74ef 100644 --- a/dom/webidl/PushManager.webidl +++ b/dom/webidl/PushManager.webidl @@ -7,30 +7,23 @@ * https://w3c.github.io/push-api/ */ -// Please see comments in dom/push/PushManager.h for the split between -// PushManagerImpl and PushManager. +// The main thread JS implementation. Please see comments in +// dom/push/PushManager.h for the split between PushManagerImpl and PushManager. [JSImplementation="@mozilla.org/push/PushManager;1", - NoInterfaceObject] + ChromeOnly, Constructor(DOMString scope)] interface PushManagerImpl { - Promise subscribe(); - Promise getSubscription(); - Promise permissionState(); - - // We need a setter in the bindings so that the C++ can use it, - // but we don't want it exposed to client JS. WebPushMethodHider - // always returns false. - [Func="ServiceWorkerRegistration::WebPushMethodHider"] void setScope(DOMString scope); + Promise subscribe(); + Promise getSubscription(); + Promise permissionState(); }; -[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"] +[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled", + ChromeConstructor(DOMString scope)] interface PushManager { - [ChromeOnly, Throws, Exposed=Window] - void setPushManagerImpl(PushManagerImpl store); - [Throws, UseCounter] - Promise subscribe(); + Promise subscribe(); [Throws] - Promise getSubscription(); + Promise getSubscription(); [Throws] Promise permissionState(); }; diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index 9fe3d859512e..2f939919bb80 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -752,7 +752,8 @@ ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptio } already_AddRefed -ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv) +ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx, + ErrorResult& aRv) { AssertIsOnMainThread(); @@ -768,32 +769,17 @@ ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv) return nullptr; } - // TODO: bug 1148117. This will fail when swr is exposed on workers - JS::Rooted jsImplObj(nsContentUtils::RootingCxForThread()); - ConstructJSImplementation("@mozilla.org/push/PushManager;1", - globalObject, &jsImplObj, aRv); + GlobalObject global(aCx, globalObject->GetGlobalJSObject()); + mPushManager = PushManager::Constructor(global, mScope, aRv); if (aRv.Failed()) { return nullptr; } - mPushManager = new PushManager(globalObject, mScope); - - RefPtr impl = new PushManagerImpl(jsImplObj, globalObject); - impl->SetScope(mScope, aRv); - if (aRv.Failed()) { - mPushManager = nullptr; - return nullptr; - } - mPushManager->SetPushManagerImpl(*impl, aRv); - if (aRv.Failed()) { - mPushManager = nullptr; - return nullptr; - } } RefPtr ret = mPushManager; return ret.forget(); - #endif /* ! MOZ_SIMPLEPUSH */ +#endif /* ! MOZ_SIMPLEPUSH */ } //////////////////////////////////////////////////// @@ -1217,7 +1203,7 @@ ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOpt return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv); } -already_AddRefed +already_AddRefed ServiceWorkerRegistrationWorkerThread::GetPushManager(ErrorResult& aRv) { #ifdef MOZ_SIMPLEPUSH @@ -1225,13 +1211,13 @@ ServiceWorkerRegistrationWorkerThread::GetPushManager(ErrorResult& aRv) #else if (!mPushManager) { - mPushManager = new WorkerPushManager(mScope); + mPushManager = new PushManager(mScope); } - RefPtr ret = mPushManager; + RefPtr ret = mPushManager; return ret.forget(); - #endif /* ! MOZ_SIMPLEPUSH */ +#endif /* ! MOZ_SIMPLEPUSH */ } } // dom namespace diff --git a/dom/workers/ServiceWorkerRegistration.h b/dom/workers/ServiceWorkerRegistration.h index 949b2b678d95..b37503f96f54 100644 --- a/dom/workers/ServiceWorkerRegistration.h +++ b/dom/workers/ServiceWorkerRegistration.h @@ -22,7 +22,6 @@ namespace dom { class Promise; class PushManager; -class WorkerPushManager; class WorkerListener; namespace workers { @@ -36,21 +35,6 @@ ServiceWorkerRegistrationVisible(JSContext* aCx, JSObject* aObj); bool ServiceWorkerNotificationAPIVisible(JSContext* aCx, JSObject* aObj); -// This class exists solely so that we can satisfy some WebIDL Func= attribute -// constraints. Func= converts the function name to a header file to include, in -// this case "ServiceWorkerRegistration.h". -class ServiceWorkerRegistration final -{ -public: - // Something that we can feed into the Func webidl property to ensure that - // SetScope is never exposed to the user. - static bool - WebPushMethodHider(JSContext* unusedContext, JSObject* unusedObject) { - return false; - } - -}; - // Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of // updatefound event and invalidating ServiceWorker instances. class ServiceWorkerRegistrationListener @@ -139,7 +123,7 @@ public: GetActive() override; already_AddRefed - GetPushManager(ErrorResult& aRv); + GetPushManager(JSContext* aCx, ErrorResult& aRv); // DOMEventTargethelper void DisconnectFromOwner() override @@ -241,7 +225,7 @@ public: bool Notify(workers::Status aStatus) override; - already_AddRefed + already_AddRefed GetPushManager(ErrorResult& aRv); private: @@ -263,7 +247,7 @@ private: RefPtr mListener; #ifndef MOZ_SIMPLEPUSH - RefPtr mPushManager; + RefPtr mPushManager; #endif }; From 814c90a39d96757eecb32ba00db22155ff1370e0 Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Fri, 1 Apr 2016 10:55:10 -0700 Subject: [PATCH 38/53] Bug 1250531 - Unconditionally sync the clients collection. r=markh MozReview-Commit-ID: 4RHolqewNmx --HG-- extra : rebase_source : fdde3ebdc10018c00c065fbef364f4f6fb160048 --- services/sync/modules/engines/clients.js | 71 ++++- .../sync/tests/unit/test_clients_engine.js | 259 +++++++++++++++++- .../sync/tests/unit/test_interval_triggers.js | 5 + .../sync/tests/unit/test_syncscheduler.js | 6 + 4 files changed, 321 insertions(+), 20 deletions(-) diff --git a/services/sync/modules/engines/clients.js b/services/sync/modules/engines/clients.js index 50cd94cc8982..c3ac36f6d40b 100644 --- a/services/sync/modules/engines/clients.js +++ b/services/sync/modules/engines/clients.js @@ -25,6 +25,11 @@ const CLIENTS_TTL_REFRESH = 604800; // 7 days const SUPPORTED_PROTOCOL_VERSIONS = ["1.1", "1.5"]; +function hasDupeCommand(commands, action) { + return commands.some(other => other.command == action.command && + Utils.deepEquals(other.args, action.args)); +} + this.ClientsRec = function ClientsRec(collection, id) { CryptoWrapper.call(this, collection, id); } @@ -150,6 +155,27 @@ ClientEngine.prototype = { SyncEngine.prototype._syncStartup.call(this); }, + _processIncoming() { + // Fetch all records from the server. + this.lastSync = 0; + this._incomingClients = []; + try { + SyncEngine.prototype._processIncoming.call(this); + // Since clients are synced unconditionally, any records in the local store + // that don't exist on the server must be for disconnected clients. Remove + // them, so that we don't upload records with commands for clients that will + // never see them. We also do this to filter out stale clients from the + // tabs collection, since showing their list of tabs is confusing. + let remoteClientIDs = Object.keys(this._store._remoteClients); + let staleIDs = Utils.arraySub(remoteClientIDs, this._incomingClients); + for (let staleID of staleIDs) { + this._removeRemoteClient(staleID); + } + } finally { + this._incomingClients = null; + } + }, + _syncFinish() { // Record telemetry for our device types. for (let [deviceType, count] of this.deviceTypes) { @@ -170,9 +196,22 @@ ClientEngine.prototype = { SyncEngine.prototype._syncFinish.call(this); }, - // Always process incoming items because they might have commands - _reconcile: function _reconcile() { - return true; + _reconcile: function _reconcile(item) { + // Every incoming record is reconciled, so we use this to track the + // contents of the collection on the server. + this._incomingClients.push(item.id); + + if (!this._store.itemExists(item.id)) { + return true; + } + // Clients are synced unconditionally, so we'll always have new records. + // Unfortunately, this will cause the scheduler to use the immediate sync + // interval for the multi-device case, instead of the active interval. We + // work around this by updating the record during reconciliation, and + // returning false to indicate that the record doesn't need to be applied + // later. + this._store.update(item); + return false; }, // Treat reset the same as wiping for locally cached clients @@ -243,11 +282,6 @@ ClientEngine.prototype = { throw new Error("Unknown remote client ID: '" + clientId + "'."); } - // notDupe compares two commands and returns if they are not equal. - let notDupe = function(other) { - return other.command != command || !Utils.deepEquals(other.args, args); - }; - let action = { command: command, args: args, @@ -257,7 +291,7 @@ ClientEngine.prototype = { client.commands = [action]; } // Add the new action if there are no duplicates. - else if (client.commands.every(notDupe)) { + else if (!hasDupeCommand(client.commands, action)) { client.commands.push(action); } // It must be a dupe. Skip. @@ -409,7 +443,12 @@ ClientEngine.prototype = { let subject = {uri: uri, client: clientId, title: title}; Svc.Obs.notify("weave:engine:clients:display-uri", subject); - } + }, + + _removeRemoteClient(id) { + delete this._store._remoteClients[id]; + this._tracker.removeChangedID(id); + }, }; function ClientStore(name, engine) { @@ -426,8 +465,18 @@ ClientStore.prototype = { // Only grab commands from the server; local name/type always wins if (record.id == this.engine.localID) this.engine.localCommands = record.commands; - else + else { + let currentRecord = this._remoteClients[record.id]; + if (currentRecord && currentRecord.commands) { + // Merge commands. + for (let action of currentRecord.commands) { + if (!hasDupeCommand(record.cleartext.commands, action)) { + record.cleartext.commands.push(action); + } + } + } this._remoteClients[record.id] = record.cleartext; + } }, createRecord: function createRecord(id, collection) { diff --git a/services/sync/tests/unit/test_clients_engine.js b/services/sync/tests/unit/test_clients_engine.js index e571be724f19..1c793a05f5eb 100644 --- a/services/sync/tests/unit/test_clients_engine.js +++ b/services/sync/tests/unit/test_clients_engine.js @@ -193,6 +193,79 @@ add_test(function test_properties() { } }); +add_test(function test_full_sync() { + _("Ensure that Clients engine fetches all records for each sync."); + + let now = Date.now() / 1000; + let contents = { + meta: {global: {engines: {clients: {version: engine.version, + syncID: engine.syncID}}}}, + clients: {}, + crypto: {} + }; + let server = serverForUsers({"foo": "password"}, contents); + let user = server.user("foo"); + + new SyncTestingInfrastructure(server.server); + generateNewKeys(Service.collectionKeys); + + let activeID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(activeID, encryptPayload({ + id: activeID, + name: "Active client", + type: "desktop", + commands: [], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + let deletedID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(deletedID, encryptPayload({ + id: deletedID, + name: "Client to delete", + type: "desktop", + commands: [], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + try { + let store = engine._store; + + _("First sync. 2 records downloaded; our record uploaded."); + strictEqual(engine.lastRecordUpload, 0); + engine._sync(); + ok(engine.lastRecordUpload > 0); + deepEqual(user.collection("clients").keys().sort(), + [activeID, deletedID, engine.localID].sort(), + "Our record should be uploaded on first sync"); + deepEqual(Object.keys(store.getAllIDs()).sort(), + [activeID, deletedID, engine.localID].sort(), + "Other clients should be downloaded on first sync"); + + _("Delete a record, then sync again"); + let collection = server.getCollection("foo", "clients"); + collection.remove(deletedID); + // Simulate a timestamp update in info/collections. + engine.lastModified = now; + engine._sync(); + + _("Record should be updated"); + deepEqual(Object.keys(store.getAllIDs()).sort(), + [activeID, engine.localID].sort(), + "Deleted client should be removed on next sync"); + } finally { + Svc.Prefs.resetBranch(""); + Service.recordManager.clearCache(); + + try { + server.deleteCollections("foo"); + } finally { + server.stop(run_next_test); + } + } +}); + add_test(function test_sync() { _("Ensure that Clients engine uploads a new client record once a week."); @@ -454,18 +527,29 @@ add_test(function test_command_sync() { } _("Create remote client record"); - let rec = new ClientsRec("clients", remoteId); - engine._store.create(rec); - let remoteRecord = engine._store.createRecord(remoteId, "clients"); - engine.sendCommand("wipeAll", []); - - let clientRecord = engine._store._remoteClients[remoteId]; - do_check_neq(clientRecord, undefined); - do_check_eq(clientRecord.commands.length, 1); + server.insertWBO("foo", "clients", new ServerWBO(remoteId, encryptPayload({ + id: remoteId, + name: "Remote client", + type: "desktop", + commands: [], + version: "48", + protocols: ["1.5"], + }), Date.now() / 1000)); try { _("Syncing."); engine._sync(); + + _("Checking remote record was downloaded."); + let clientRecord = engine._store._remoteClients[remoteId]; + do_check_neq(clientRecord, undefined); + do_check_eq(clientRecord.commands.length, 0); + + _("Send a command to the remote client."); + engine.sendCommand("wipeAll", []); + do_check_eq(clientRecord.commands.length, 1); + engine._sync(); + _("Checking record was uploaded."); do_check_neq(clientWBO(engine.localID).payload, undefined); do_check_true(engine.lastRecordUpload > 0); @@ -487,7 +571,13 @@ add_test(function test_command_sync() { } finally { Svc.Prefs.resetBranch(""); Service.recordManager.clearCache(); - server.stop(run_next_test); + + try { + let collection = server.getCollection("foo", "clients"); + collection.remove(remoteId); + } finally { + server.stop(run_next_test); + } } }); @@ -575,6 +665,9 @@ add_test(function test_receive_display_uri() { Svc.Obs.add(ev, handler); do_check_true(engine.processIncomingCommands()); + + engine._resetClient(); + run_next_test(); }); add_test(function test_optional_client_fields() { @@ -603,6 +696,154 @@ add_test(function test_optional_client_fields() { run_next_test(); }); +add_test(function test_merge_commands() { + _("Verifies local commands for remote clients are merged with the server's"); + + let now = Date.now() / 1000; + let contents = { + meta: {global: {engines: {clients: {version: engine.version, + syncID: engine.syncID}}}}, + clients: {}, + crypto: {} + }; + let server = serverForUsers({"foo": "password"}, contents); + let user = server.user("foo"); + + new SyncTestingInfrastructure(server.server); + generateNewKeys(Service.collectionKeys); + + let desktopID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(desktopID, encryptPayload({ + id: desktopID, + name: "Desktop client", + type: "desktop", + commands: [{ + command: "displayURI", + args: ["https://example.com", engine.localID, "Yak Herders Anonymous"], + }], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + let mobileID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(mobileID, encryptPayload({ + id: mobileID, + name: "Mobile client", + type: "mobile", + commands: [{ + command: "logout", + args: [], + }], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + try { + let store = engine._store; + + _("First sync. 2 records downloaded."); + strictEqual(engine.lastRecordUpload, 0); + engine._sync(); + + _("Broadcast logout to all clients"); + engine.sendCommand("logout", []); + engine._sync(); + + let collection = server.getCollection("foo", "clients"); + let desktopPayload = JSON.parse(JSON.parse(collection.payload(desktopID)).ciphertext); + deepEqual(desktopPayload.commands, [{ + command: "displayURI", + args: ["https://example.com", engine.localID, "Yak Herders Anonymous"], + }, { + command: "logout", + args: [], + }], "Should send the logout command to the desktop client"); + + let mobilePayload = JSON.parse(JSON.parse(collection.payload(mobileID)).ciphertext); + deepEqual(mobilePayload.commands, [{ command: "logout", args: [] }], + "Should not send a duplicate logout to the mobile client"); + } finally { + Svc.Prefs.resetBranch(""); + Service.recordManager.clearCache(); + engine._resetClient(); + + try { + server.deleteCollections("foo"); + } finally { + server.stop(run_next_test); + } + } +}); + +add_test(function test_deleted_commands() { + _("Verifies commands for a deleted client are discarded"); + + let now = Date.now() / 1000; + let contents = { + meta: {global: {engines: {clients: {version: engine.version, + syncID: engine.syncID}}}}, + clients: {}, + crypto: {} + }; + let server = serverForUsers({"foo": "password"}, contents); + let user = server.user("foo"); + + new SyncTestingInfrastructure(server.server); + generateNewKeys(Service.collectionKeys); + + let activeID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(activeID, encryptPayload({ + id: activeID, + name: "Active client", + type: "desktop", + commands: [], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + let deletedID = Utils.makeGUID(); + server.insertWBO("foo", "clients", new ServerWBO(deletedID, encryptPayload({ + id: deletedID, + name: "Client to delete", + type: "desktop", + commands: [], + version: "48", + protocols: ["1.5"], + }), now - 10)); + + try { + let store = engine._store; + + _("First sync. 2 records downloaded."); + engine._sync(); + + _("Delete a record on the server."); + let collection = server.getCollection("foo", "clients"); + collection.remove(deletedID); + + _("Broadcast a command to all clients"); + engine.sendCommand("logout", []); + engine._sync(); + + deepEqual(collection.keys().sort(), [activeID, engine.localID].sort(), + "Should not reupload deleted clients"); + + let activePayload = JSON.parse(JSON.parse(collection.payload(activeID)).ciphertext); + deepEqual(activePayload.commands, [{ command: "logout", args: [] }], + "Should send the command to the active client"); + } finally { + Svc.Prefs.resetBranch(""); + Service.recordManager.clearCache(); + engine._resetClient(); + + try { + server.deleteCollections("foo"); + } finally { + server.stop(run_next_test); + } + } +}); + function run_test() { initTestLogging("Trace"); Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace; diff --git a/services/sync/tests/unit/test_interval_triggers.js b/services/sync/tests/unit/test_interval_triggers.js index 071ba63d380c..eca5ec2897b4 100644 --- a/services/sync/tests/unit/test_interval_triggers.js +++ b/services/sync/tests/unit/test_interval_triggers.js @@ -13,6 +13,11 @@ Cu.import("resource://services-sync/service.js"); var scheduler = Service.scheduler; var clientsEngine = Service.clientsEngine; +// Don't remove stale clients when syncing. This is a test-only workaround +// that lets us add clients directly to the store, without losing them on +// the next sync. +clientsEngine._removeRemoteClient = id => {}; + function promiseStopServer(server) { let deferred = Promise.defer(); server.stop(deferred.resolve); diff --git a/services/sync/tests/unit/test_syncscheduler.js b/services/sync/tests/unit/test_syncscheduler.js index cb04c926a772..a275182d7c5b 100644 --- a/services/sync/tests/unit/test_syncscheduler.js +++ b/services/sync/tests/unit/test_syncscheduler.js @@ -29,6 +29,11 @@ Service.engineManager.register(CatapultEngine); var scheduler = new SyncScheduler(Service); var clientsEngine = Service.clientsEngine; +// Don't remove stale clients when syncing. This is a test-only workaround +// that lets us add clients directly to the store, without losing them on +// the next sync. +clientsEngine._removeRemoteClient = id => {}; + function sync_httpd_setup() { let global = new ServerWBO("global", { syncID: Service.syncID, @@ -69,6 +74,7 @@ function setUp(server) { function cleanUpAndGo(server) { let deferred = Promise.defer(); Utils.nextTick(function () { + clientsEngine._store.wipe(); Service.startOver(); if (server) { server.stop(deferred.resolve); From d1de4b6824755dd0727714b092ca7fc129a21a00 Mon Sep 17 00:00:00 2001 From: Kit Cambridge Date: Thu, 31 Mar 2016 14:36:40 -0700 Subject: [PATCH 39/53] Bug 1250531 - Only show existing remote clients in the Synced Tabs UI. r=markh MozReview-Commit-ID: LQw7TinhIfE --HG-- extra : rebase_source : 6090764abd0aa3d7422677c04d8d8cf242cec793 --- services/sync/modules/SyncedTabs.jsm | 3 ++ services/sync/modules/engines/clients.js | 4 +++ services/sync/tests/unit/test_syncedtabs.js | 38 +++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/services/sync/modules/SyncedTabs.jsm b/services/sync/modules/SyncedTabs.jsm index 3b3791d59dde..14e69e2f6503 100644 --- a/services/sync/modules/SyncedTabs.jsm +++ b/services/sync/modules/SyncedTabs.jsm @@ -121,6 +121,9 @@ let SyncedTabsInternal = { let ntabs = 0; for (let [guid, client] in Iterator(engine.getAllClients())) { + if (!Weave.Service.clientsEngine.remoteClientExists(client.id)) { + continue; + } let clientRepr = yield this._makeClient(client); log.debug("Processing client", clientRepr); diff --git a/services/sync/modules/engines/clients.js b/services/sync/modules/engines/clients.js index c3ac36f6d40b..5486b70dc1bd 100644 --- a/services/sync/modules/engines/clients.js +++ b/services/sync/modules/engines/clients.js @@ -140,6 +140,10 @@ ClientEngine.prototype = { Svc.Prefs.set("client.type", value); }, + remoteClientExists(id) { + return !!this._store._remoteClients[id]; + }, + isMobile: function isMobile(id) { if (this._store._remoteClients[id]) return this._store._remoteClients[id].type == DEVICE_TYPE_MOBILE; diff --git a/services/sync/tests/unit/test_syncedtabs.js b/services/sync/tests/unit/test_syncedtabs.js index e10f71ac69b4..4ba904ac4d59 100644 --- a/services/sync/tests/unit/test_syncedtabs.js +++ b/services/sync/tests/unit/test_syncedtabs.js @@ -39,6 +39,9 @@ let MockClientsEngine = { } return guid.endsWith("mobile"); }, + remoteClientExists(id) { + return !id.startsWith("guid_stale"); + }, } // Configure Sync with our mock tabs engine and force it to become initialized. @@ -101,6 +104,41 @@ add_task(function* test_clientWithTabs() { equal(clients[1].tabs.length, 0); }); +add_task(function* test_staleClientWithTabs() { + yield configureClients({ + guid_desktop: { + clientName: "My Desktop", + tabs: [ + { + urlHistory: ["http://foo.com/"], + icon: "http://foo.com/favicon", + }], + }, + guid_mobile: { + clientName: "My Phone", + tabs: [], + }, + guid_stale_mobile: { + clientName: "My Deleted Phone", + tabs: [], + }, + guid_stale_desktop: { + clientName: "My Deleted Laptop", + tabs: [ + { + urlHistory: ["https://bar.com/"], + icon: "https://bar.com/favicon", + }], + }, + }); + let clients = yield SyncedTabs.getTabClients(); + clients.sort((a, b) => { return a.name.localeCompare(b.name);}); + equal(clients.length, 2); + equal(clients[0].tabs.length, 1); + equal(clients[0].tabs[0].url, "http://foo.com/"); + equal(clients[1].tabs.length, 0); +}); + add_task(function* test_clientWithTabsIconsDisabled() { Services.prefs.setBoolPref("services.sync.syncedTabs.showRemoteIcons", false); yield configureClients({ From af75ceece1d13e4626ac7729de7858b489cbb880 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Wed, 23 Mar 2016 14:54:00 -0400 Subject: [PATCH 40/53] Bug 1258172 - Use C-style for loops instead of for/of to cut down on the number of CCWs when using the Tree component. r=jimb --- devtools/client/shared/components/tree.js | 43 +++++++++++++++-------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/devtools/client/shared/components/tree.js b/devtools/client/shared/components/tree.js index bb6cfb38912f..5396467cc97e 100644 --- a/devtools/client/shared/components/tree.js +++ b/devtools/client/shared/components/tree.js @@ -218,13 +218,17 @@ const Tree = module.exports = createClass({ this.props.onExpand(item); this.state.seen.add(item); - for (let child of this.props.getChildren(item)) { - autoExpand(child, currentDepth + 1); + const children = this.props.getChildren(item); + const length = children.length; + for (let i = 0; i < length; i++) { + autoExpand(children[i], currentDepth + 1); } }; - for (let root of this.props.getRoots()) { - autoExpand(root, 0); + const roots = this.props.getRoots(); + const length = roots.length; + for (let i = 0; i < length; i++) { + autoExpand(roots[i], 0); } }, @@ -315,8 +319,10 @@ const Tree = module.exports = createClass({ return traversal; } - for (let child of this.props.getChildren(item)) { - this._dfs(child, maxDepth, traversal, nextDepth); + const children = this.props.getChildren(item); + const length = children.length; + for (let i = 0; i < length; i++) { + this._dfs(children[i], maxDepth, traversal, nextDepth); } return traversal; @@ -328,8 +334,10 @@ const Tree = module.exports = createClass({ _dfsFromRoots(maxDepth = Infinity) { const traversal = []; - for (let root of this.props.getRoots()) { - this._dfs(root, maxDepth, traversal); + const roots = this.props.getRoots(); + const length = roots.length; + for (let i = 0; i < length; i++) { + this._dfs(roots[i], maxDepth, traversal); } return traversal; @@ -346,8 +354,10 @@ const Tree = module.exports = createClass({ this.props.onExpand(item); if (expandAllChildren) { - for (let { item: child } of this._dfs(item)) { - this.props.onExpand(child); + const children = this._dfs(item); + const length = children.length; + for (let i = 0; i < length; i++) { + this.props.onExpand(children[i].item); } } } @@ -453,7 +463,11 @@ const Tree = module.exports = createClass({ // doesn't exist, we're at the first node already. let prev; - for (let { item } of this._dfsFromRoots()) { + + const traversal = this._dfsFromRoots(); + const length = traversal.length; + for (let i = 0; i < length; i++) { + const item = traversal[i].item; if (item === this.props.focused) { break; } @@ -477,10 +491,11 @@ const Tree = module.exports = createClass({ // doesn't exist, we're at the last node already. const traversal = this._dfsFromRoots(); - + const length = traversal.length; let i = 0; - for (let { item } of traversal) { - if (item === this.props.focused) { + + while (i < length) { + if (traversal[i].item === this.props.focused) { break; } i++; From 63fe89bd2e1d31636adb8ac12458f0f6edfbd71d Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 25 Mar 2016 17:02:38 -0400 Subject: [PATCH 41/53] Bug 1259428 - part 1 - don't call Log methods of generated method classes; r=jld The first step to eliminating all the generated Message subclasses the IPDL compiler spits out is to move the functionality of their Log methods someplace else. In addition to eliminating the need for the Log methods, this change has the welcome effect of moving a bunch of code that would be generated hundreds of times into a single place, which should reduce code size a bit (debug builds only). We don't actually remove the generation of the Log methods; that change will be done for a future patch. --- ipc/glue/ProtocolUtils.cpp | 18 ++++++++++++++++++ ipc/glue/ProtocolUtils.h | 11 +++++++++++ ipc/ipdl/ipdl/lower.py | 16 ++++++++++------ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/ipc/glue/ProtocolUtils.cpp b/ipc/glue/ProtocolUtils.cpp index e359170e3bdd..ff979b451383 100644 --- a/ipc/glue/ProtocolUtils.cpp +++ b/ipc/glue/ProtocolUtils.cpp @@ -315,6 +315,24 @@ void AnnotateSystemError() } #endif +void +LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid, + const char* aContextDescription, + const char* aMessageDescription, + MessageDirection aDirection) +{ + nsPrintfCString logMessage("[time: %" PRId64 "][%d%s%d] [%s] %s %s\n", + PR_Now(), base::GetCurrentProcId(), + aDirection == MessageDirection::eReceiving ? "<-" : "->", + aOtherPid, aTopLevelProtocol, + aContextDescription, + aMessageDescription); +#ifdef ANDROID + __android_log_write(ANDROID_LOG_INFO, "GeckoIPC", logMessage.get()); +#endif + fputs(logMessage.get(), stderr); +} + void ProtocolErrorBreakpoint(const char* aMsg) { diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index cdd7498aeb80..0935fb65c607 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -296,6 +296,17 @@ LoggingEnabledFor(const char *aTopLevelProtocol) #endif } +enum class MessageDirection { + eSending, + eReceiving, +}; + +MOZ_NEVER_INLINE void +LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid, + const char* aContextDescription, + const char* aMessageDescription, + MessageDirection aDirection); + MOZ_NEVER_INLINE void ProtocolErrorBreakpoint(const char* aMsg); diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 619663d5f377..018cc3888f91 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -5380,12 +5380,16 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): def logMessage(self, md, msgptr, pfx, actor=None, receiving=False): actorname = _actorName(self.protocol.name, self.side) - topLevel = self.protocol.decl.type.toplevel().name() - return _ifLogging(ExprLiteral.String(topLevel), [ StmtExpr(ExprCall( - ExprSelect(msgptr, '->', 'Log'), - args=[ ExprLiteral.String('['+ actorname +'] '+ pfx), - self.protocol.callOtherPid(actor), - ExprLiteral.TRUE if receiving else ExprLiteral.FALSE ])) ]) + return _ifLogging(ExprLiteral.String(actorname), + [ StmtExpr(ExprCall( + ExprVar('mozilla::ipc::LogMessageForProtocol'), + args=[ ExprLiteral.String(actorname), + self.protocol.callOtherPid(actor), + ExprLiteral.String(pfx), + ExprCall(ExprSelect(msgptr, '->', 'name')), + ExprVar('mozilla::ipc::MessageDirection::eReceiving' + if receiving + else 'mozilla::ipc::MessageDirection::eSending') ])) ]) def profilerLabel(self, tag, msgname): return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'), From 4a4de50da705064e97db498736acb0d2c1caf52b Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 25 Mar 2016 17:07:26 -0400 Subject: [PATCH 42/53] Bug 1259428 - part 2 - remove dodgy static_cast downcasts from logging statements; r=jld Various bits of IPDL code would do something like: Message* m = ...; if (m.type() == particular message type) { static_cast(m)->name(); } The static_cast is a remnant of having to do the downcast to access the Log() method on the concrete subclass. Since name() is defined on Message, there's no need for these casts anymore, so let's remove them. --- ipc/ipdl/ipdl/lower.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 018cc3888f91..a7e0978fb745 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -5194,7 +5194,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): + [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar)) for r in md.returns ] + self.setMessageFlags(md, replyvar, reply=1) - + [ self.logMessage(md, md.replyCast(replyvar), 'Sending reply ') ]) + + [ self.logMessage(md, replyvar, 'Sending reply ') ]) def setMessageFlags(self, md, var, reply): @@ -5229,7 +5229,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): '.', 'set_name'), args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name +'::')) ])), - self.logMessage(md, md.msgCast(msgexpr), 'Received ', + self.logMessage(md, msgexpr, 'Received ', receiving=True), self.profilerLabel('Recv', md.decl.progname), Whitespace.NL @@ -5267,7 +5267,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): def deserializeReply(self, md, replyexpr, side, errfn, actor=None): stmts = [ Whitespace.NL, - self.logMessage(md, md.replyCast(replyexpr), + self.logMessage(md, replyexpr, 'Received reply ', actor, receiving=True) ] if 0 == len(md.returns): return stmts From 9cbbaad4ad191afb50b50c40cacc9874fa274ce2 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 25 Mar 2016 17:09:41 -0400 Subject: [PATCH 43/53] Bug 1259428 - part 3 - remove Log() methods from generated message subclasses; r=jld These are no longer called by anything. The generated Message subclasses now have no behavior of their own, and can be removed in subsequent patches. --- ipc/ipdl/ipdl/lower.py | 58 +----------------------------------------- 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index a7e0978fb745..ea654eda6374 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -1929,63 +1929,7 @@ def _generateMessageClass(clsname, msgid, priority, prettyName, compress): ExprVar(priorityEnum), compression, ExprLiteral.String(prettyName) ]) ]) - cls.addstmts([ ctor, Whitespace.NL ]) - - # generate a logging function - # 'pfx' will be something like "[FooParent] sent" - pfxvar = ExprVar('pfx__') - otherpid = ExprVar('otherPid__') - receiving = ExprVar('receiving__') - logger = MethodDefn(MethodDecl( - 'Log', - params=([ Decl(Type('std::string', const=1, ref=1), pfxvar.name), - Decl(Type('base::ProcessId'), otherpid.name), - Decl(Type('bool'), receiving.name) ]), - const=1)) - # TODO/cjones: allow selecting what information is printed to - # the log - msgvar = ExprVar('logmsg__') - logger.addstmt(StmtDecl(Decl(Type('std::string'), msgvar.name))) - - def appendToMsg(thing): - return StmtExpr(ExprCall(ExprSelect(msgvar, '.', 'append'), - args=[ thing ])) - logger.addstmts([ - StmtExpr(ExprCall( - ExprVar('StringAppendF'), - args=[ ExprAddrOf(msgvar), - ExprLiteral.String('[time:%" PRId64 "][%d%s%d]'), - ExprCall(ExprVar('PR_Now')), - ExprCall(ExprVar('base::GetCurrentProcId')), - ExprConditional(receiving, ExprLiteral.String('<-'), - ExprLiteral.String('->')), - otherpid ])), - appendToMsg(pfxvar), - appendToMsg(ExprLiteral.String(clsname +'(')), - Whitespace.NL - ]) - - # TODO turn this back on when string stuff is sorted - - logger.addstmt(appendToMsg(ExprLiteral.String('[TODO])\\n'))) - - logger.addstmts([ - CppDirective('ifdef', 'ANDROID'), - StmtExpr(ExprCall( - ExprVar('__android_log_write'), - args=[ ExprVar('ANDROID_LOG_INFO'), - ExprLiteral.String('GeckoIPC'), - ExprCall(ExprSelect(msgvar, '.', 'c_str')) ])), - CppDirective('endif') - ]) - - # and actually print the log message - logger.addstmt(StmtExpr(ExprCall( - ExprVar('fputs'), - args=[ ExprCall(ExprSelect(msgvar, '.', 'c_str')), - ExprVar('stderr') ]))) - - cls.addstmt(logger) + cls.addstmts([ ctor ]) return cls From 4d7242a2c4b0dc48cbf4c083d989ee0bd97c7cdc Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 25 Mar 2016 18:27:11 -0400 Subject: [PATCH 44/53] Bug 1259428 - part 4 - remove prtime.h from generated protocol headers; r=jld This include was only needed for PR_Now(), which is no longer called by the headers. --- ipc/ipdl/ipdl/builtin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ipc/ipdl/ipdl/builtin.py b/ipc/ipdl/ipdl/builtin.py index 0b23d045ae65..46d5f1622833 100644 --- a/ipc/ipdl/ipdl/builtin.py +++ b/ipc/ipdl/ipdl/builtin.py @@ -43,7 +43,6 @@ Types = ( HeaderIncludes = ( 'mozilla/Attributes.h', - 'prtime.h', 'IPCMessageStart.h', 'ipc/IPCMessageUtils.h', 'mozilla/RefPtr.h', From ebe8d018956c7e08159ae2cf6bb88ed1cc2a8b15 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Mon, 28 Mar 2016 11:42:47 -0400 Subject: [PATCH 45/53] Bug 1259428 - part 5 - convert Message subclasses to constructor functions; r=jld All we use our Message subclasses for nowadays is the constructor, so we might as well strip the class away and just have functions that perform the construction for us. This change eliminates unnecessary vtables as well as making the included headers somewhat smaller, which is always nice. --- ipc/ipdl/ipdl/lower.py | 75 ++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index ea654eda6374..dd175d24f62b 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -1662,20 +1662,28 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): self.funcDefns.append(tfDefn) for md in p.messageDecls: - ns.addstmts([ - _generateMessageClass(md.msgClass(), md.msgId(), - md.decl.type.priority, - md.prettyMsgName(p.name+'::'), - md.decl.type.compress), - Whitespace.NL ]) + decls = [] + + mfDecl, mfDefn = _splitFuncDeclDefn( + _generateMessageConstructor(md.msgClass(), md.msgId(), + md.decl.type.priority, + md.prettyMsgName(p.name+'::'), + md.decl.type.compress)) + decls.append(mfDecl) + self.funcDefns.append(mfDefn) + if md.hasReply(): - ns.addstmts([ - _generateMessageClass( + rfDecl, rfDefn = _splitFuncDeclDefn( + _generateMessageConstructor( md.replyClass(), md.replyId(), md.decl.type.priority, md.prettyReplyName(p.name+'::'), - md.decl.type.compress), - Whitespace.NL ]) + md.decl.type.compress)) + decls.append(rfDecl) + self.funcDefns.append(rfDefn) + + decls.append(Whitespace.NL) + ns.addstmts(decls) ns.addstmts([ Whitespace.NL, Whitespace.NL ]) @@ -1897,22 +1905,21 @@ class _GenerateProtocolCode(ipdl.ast.Visitor): ##-------------------------------------------------- -def _generateMessageClass(clsname, msgid, priority, prettyName, compress): - cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ]) - cls.addstmt(Label.PUBLIC) +def _generateMessageConstructor(clsname, msgid, priority, prettyName, compress): + routingId = ExprVar('routingId') - idenum = TypeEnum() - idenum.addId('ID', msgid) - cls.addstmt(StmtDecl(Decl(idenum, ''))) + func = FunctionDefn(FunctionDecl( + clsname, + params=[ Decl(Type('int32_t'), routingId.name) ], + ret=Type('IPC::Message', ptr=1))) - # make the message constructor if compress == 'compress': - compression = ExprVar('COMPRESSION_ENABLED') + compression = ExprVar('IPC::Message::COMPRESSION_ENABLED') elif compress: assert compress == 'compressall' - compression = ExprVar('COMPRESSION_ALL') + compression = ExprVar('IPC::Message::COMPRESSION_ALL') else: - compression = ExprVar('COMPRESSION_NONE') + compression = ExprVar('IPC::Message::COMPRESSION_NONE') if priority == ipdl.ast.NORMAL_PRIORITY: priorityEnum = 'IPC::Message::PRIORITY_NORMAL' elif priority == ipdl.ast.HIGH_PRIORITY: @@ -1920,18 +1927,16 @@ def _generateMessageClass(clsname, msgid, priority, prettyName, compress): else: assert priority == ipdl.ast.URGENT_PRIORITY priorityEnum = 'IPC::Message::PRIORITY_URGENT' - routingId = ExprVar('routingId') - ctor = ConstructorDefn( - ConstructorDecl(clsname, params=[ Decl(Type('int32_t'), routingId.name) ]), - memberinits=[ ExprMemberInit(ExprVar('IPC::Message'), - [ routingId, - ExprVar('ID'), - ExprVar(priorityEnum), - compression, - ExprLiteral.String(prettyName) ]) ]) - cls.addstmts([ ctor ]) - return cls + func.addstmt( + StmtReturn(ExprNew(Type('IPC::Message'), + args=[ routingId, + ExprVar(msgid), + ExprVar(priorityEnum), + compression, + ExprLiteral.String(prettyName) ]))) + + return func ##-------------------------------------------------- @@ -5112,9 +5117,9 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): this = None if md.decl.type.isDtor(): this = md.actorDecl().var() - stmts = ([ StmtDecl(Decl(Type(md.pqMsgClass(), ptr=1), msgvar.name), - init=ExprNew(Type(md.pqMsgClass()), - args=[ routingId ])) ] + stmts = ([ StmtDecl(Decl(Type('IPC::Message', ptr=1), msgvar.name), + init=ExprCall(ExprVar(md.pqMsgClass()), + args=[ routingId ])) ] + [ Whitespace.NL ] + [ StmtExpr(self.write(p.ipdltype, p.var(), msgvar, this)) for p in md.params ] @@ -5133,7 +5138,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor): replyvar = self.replyvar return ( [ StmtExpr(ExprAssn( - replyvar, ExprNew(Type(md.pqReplyClass()), args=[ routingId ]))), + replyvar, ExprCall(ExprVar(md.pqReplyClass()), args=[ routingId ]))), Whitespace.NL ] + [ StmtExpr(self.write(r.ipdltype, r.var(), replyvar)) for r in md.returns ] From 697425fd51219adcc701192ef76724d0d55f212d Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Mon, 28 Mar 2016 11:43:54 -0400 Subject: [PATCH 46/53] Bug 1259428 - part 6 - remove unneeded MessageDecl methods; r=jld msgCast and replyCast were only used for the dodgy casts we removed in part 2; the msgCxxType was only called by msgCast. --- ipc/ipdl/ipdl/lower.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index dd175d24f62b..7e36891bb065 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -924,12 +924,6 @@ class MessageDecl(ipdl.ast.MessageDecl): def pqMsgClass(self): return '%s::%s'% (self.namespace, self.msgClass()) - def msgCast(self, msgexpr): - return ExprCast(msgexpr, self.msgCxxType(const=1, ptr=1), static=1) - - def msgCxxType(self, const=0, ref=0, ptr=0): - return Type(self.pqMsgClass(), const=const, ref=ref, ptr=ptr) - def msgId(self): return self.msgClass()+ '__ID' def pqMsgId(self): return '%s::%s'% (self.namespace, self.msgId()) @@ -940,10 +934,6 @@ class MessageDecl(ipdl.ast.MessageDecl): def pqReplyClass(self): return '%s::%s'% (self.namespace, self.replyClass()) - def replyCast(self, replyexpr): - return ExprCast(replyexpr, Type(self.pqReplyClass(), const=1, ptr=1), - static=1) - def replyId(self): return self.replyClass()+ '__ID' def pqReplyId(self): return '%s::%s'% (self.namespace, self.replyId()) From 276caac4761f06bac2df7bd0b3d7d400503b7da5 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Mar 2016 10:46:14 -0400 Subject: [PATCH 47/53] Bug 1260749 - quiet unpacking of Android SDK and NDK downloads; r=nalexander We send the output to /dev/null or equivalent, but there's no need to generate a bunch of extraneous output and consume cycles unnecessarily. --- python/mozboot/mozboot/android.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/mozboot/mozboot/android.py b/python/mozboot/mozboot/android.py index 3991446894f3..0956f52ebf1a 100644 --- a/python/mozboot/mozboot/android.py +++ b/python/mozboot/mozboot/android.py @@ -167,11 +167,11 @@ def install_mobile_android_sdk_or_ndk(url, path): os.chdir(path) abspath = os.path.join(download_path, file) if file.endswith('.tar.gz') or file.endswith('.tgz'): - cmd = ['tar', 'zvxf', abspath] + cmd = ['tar', 'zxf', abspath] elif file.endswith('.tar.bz2'): - cmd = ['tar', 'jvxf', abspath] + cmd = ['tar', 'jxf', abspath] elif file.endswith('.zip'): - cmd = ['unzip', abspath] + cmd = ['unzip', '-q', abspath] elif file.endswith('.bin'): # Execute the .bin file, which unpacks the content. mode = os.stat(path).st_mode From 5790b832fae8c47419812b4ad0e7fcf80fc5d32b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 1 Apr 2016 12:43:40 -0700 Subject: [PATCH 48/53] Bug 1261452 - do_AddRef shouldn't require an rvalue-reference. r=froydnj --- gfx/src/nsDeviceContext.cpp | 2 +- layout/base/nsCSSFrameConstructor.cpp | 4 ++-- mfbt/RefPtr.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gfx/src/nsDeviceContext.cpp b/gfx/src/nsDeviceContext.cpp index ae58fe2d9b34..58d2d9aa96cd 100644 --- a/gfx/src/nsDeviceContext.cpp +++ b/gfx/src/nsDeviceContext.cpp @@ -144,7 +144,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont, mFontMetrics.AppendElement(fm); } fm->GetThebesFontGroup()->UpdateUserFonts(); - return do_AddRef(Move(fm)); + return do_AddRef(fm); } } diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 306cdab32895..4cde8b771625 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2366,7 +2366,7 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS), nullptr, - nullptr, do_AddRef(Move(aFrameState))); + nullptr, do_AddRef(aFrameState)); // Initialize the ancestor filter with null for now; we'll push // aDocElement once we finish resolving style for it. state.mTreeMatchContext.InitAncestors(nullptr); @@ -7779,7 +7779,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer, GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS), GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS), GetFloatContainingBlock(insertion.mParentFrame), - do_AddRef(Move(aFrameState))); + do_AddRef(aFrameState)); state.mTreeMatchContext.InitAncestors(aContainer ? aContainer->AsElement() : nullptr); diff --git a/mfbt/RefPtr.h b/mfbt/RefPtr.h index 20688545f0c9..d17266f6c63a 100644 --- a/mfbt/RefPtr.h +++ b/mfbt/RefPtr.h @@ -596,7 +596,7 @@ operator!=(decltype(nullptr), const RefPtr& aRhs) template inline already_AddRefed -do_AddRef(T*&& aObj) +do_AddRef(T* aObj) { RefPtr ref(aObj); return ref.forget(); From 2b4a73dcb51fff050dc4af89b0c005ffa55229e2 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 1 Apr 2016 20:36:41 -0700 Subject: [PATCH 49/53] Bug 1261284: Don't include + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/forms/button/overflow-areas-1.html b/layout/reftests/forms/button/overflow-areas-1.html new file mode 100644 index 000000000000..b1a34dfe403e --- /dev/null +++ b/layout/reftests/forms/button/overflow-areas-1.html @@ -0,0 +1,51 @@ + + + + + + Testcase for bug 1261284 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/forms/button/reftest.list b/layout/reftests/forms/button/reftest.list index 8611d525f971..dc7a7945d0fe 100644 --- a/layout/reftests/forms/button/reftest.list +++ b/layout/reftests/forms/button/reftest.list @@ -2,6 +2,7 @@ != first-letter-1.html first-letter-1-noref.html == max-height.html max-height-ref.html == min-height.html min-height-ref.html +== overflow-areas-1.html overflow-areas-1-ref.html # The buttons in these tests have some fancy shading applied to their corners # on B2G, despite their "-moz-appearance: none; background: gray", so they From 0479f9126892b3a2b8ebd890f94d2acc36518632 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Sun, 27 Mar 2016 15:29:57 -0700 Subject: [PATCH 50/53] Bug 1260065 - Extend PLUGIN_ACTIVATION_COUNT telemetry probe expiration from FF 48 to 53. r=bsmedberg Also link to bug 722110 which originally added the PLUGIN_ACTIVATION_COUNT telemetry probe. --- toolkit/components/telemetry/Histograms.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 5ee7643533a5..e04045cf8b79 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -10247,10 +10247,11 @@ }, "PLUGIN_ACTIVATION_COUNT": { "alert_emails": ["cpeterson@mozilla.com"], - "expires_in_version": "48", + "expires_in_version": "53", "kind": "count", "keyed": true, "releaseChannelCollection": "opt-out", + "bug_numbers": [722110,1260065], "description": "Counts number of times a certain plugin has been activated." }, "SCROLL_LINKED_EFFECT_FOUND": { From eda8fbb9eac6dbb47f72fec0d64329dd61db9c67 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sat, 2 Apr 2016 01:54:00 -0500 Subject: [PATCH 51/53] Bug 1261230. r=mats --- layout/generic/nsSubDocumentFrame.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 9082709049fc..9b972918b9a0 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -135,6 +135,7 @@ nsSubDocumentFrame::Init(nsIContent* aContent, nsCOMPtr oldContainerDoc; nsView* detachedViews = frameloader->GetDetachedSubdocView(getter_AddRefs(oldContainerDoc)); + frameloader->SetDetachedSubdocView(nullptr, nullptr); if (detachedViews) { if (oldContainerDoc == aContent->OwnerDoc()) { // Restore stashed presentation. @@ -145,7 +146,6 @@ nsSubDocumentFrame::Init(nsIContent* aContent, frameloader->Hide(); } } - frameloader->SetDetachedSubdocView(nullptr, nullptr); } nsContentUtils::AddScriptRunner(new AsyncFrameInit(this)); @@ -945,13 +945,16 @@ public: if (!mPresShell->IsDestroying()) { mPresShell->FlushPendingNotifications(Flush_Frames); } + + // Either the frame has been constructed by now, or it never will be, + // either way we want to clear the stashed views. + mFrameLoader->SetDetachedSubdocView(nullptr, nullptr); + nsSubDocumentFrame* frame = do_QueryFrame(mFrameElement->GetPrimaryFrame()); if ((!frame && mHideViewerIfFrameless) || mPresShell->IsDestroying()) { // Either the frame element has no nsIFrame or the presshell is being - // destroyed. Hide the nsFrameLoader, which destroys the presentation, - // and clear our references to the stashed presentation. - mFrameLoader->SetDetachedSubdocView(nullptr, nullptr); + // destroyed. Hide the nsFrameLoader, which destroys the presentation. mFrameLoader->Hide(); } return NS_OK; @@ -977,7 +980,7 @@ nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot) // Detach the subdocument's views and stash them in the frame loader. // We can then reattach them if we're being reframed (for example if // the frame has been made position:fixed). - nsFrameLoader* frameloader = FrameLoader(); + RefPtr frameloader = FrameLoader(); if (frameloader) { nsView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild()); frameloader->SetDetachedSubdocView(detachedViews, mContent->OwnerDoc()); @@ -986,7 +989,7 @@ nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot) // safely determine whether the frame is being reframed or destroyed. nsContentUtils::AddScriptRunner( new nsHideViewer(mContent, - mFrameLoader, + frameloader, PresContext()->PresShell(), (mDidCreateDoc || mCallingShow))); } From a44e8e3375f91dee81b86d3a2dade00948132f16 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Thu, 31 Mar 2016 13:14:47 -0400 Subject: [PATCH 52/53] Bug 1261122 - don't needlessly construct nsAutoCString temporaries in dom/indexedDB; r=khuey We can make ScriptErrorRunnable::DumpLocalized take an nsACString instead. --- dom/indexedDB/ScriptErrorHelper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/indexedDB/ScriptErrorHelper.cpp b/dom/indexedDB/ScriptErrorHelper.cpp index 13db32f35dbe..44f64dc359d0 100644 --- a/dom/indexedDB/ScriptErrorHelper.cpp +++ b/dom/indexedDB/ScriptErrorHelper.cpp @@ -67,7 +67,7 @@ public: } static void - DumpLocalizedMessage(const nsCString& aMessageName, + DumpLocalizedMessage(const nsACString& aMessageName, const nsAString& aFilename, uint32_t aLineNumber, uint32_t aColumnNumber, @@ -81,7 +81,7 @@ public: nsXPIDLString localizedMessage; if (NS_WARN_IF(NS_FAILED( nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, - aMessageName.get(), + aMessageName.BeginReading(), localizedMessage)))) { return; } @@ -224,7 +224,7 @@ ScriptErrorHelper::DumpLocalizedMessage(const nsACString& aMessageName, uint64_t aInnerWindowID) { if (NS_IsMainThread()) { - ScriptErrorRunnable::DumpLocalizedMessage(nsAutoCString(aMessageName), + ScriptErrorRunnable::DumpLocalizedMessage(aMessageName, aFilename, aLineNumber, aColumnNumber, From a13a3ea88a52ab84884eddd09e6ffcb44d3e3139 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Thu, 31 Mar 2016 13:16:07 -0400 Subject: [PATCH 53/53] Bug 1261123 - don't needlessly construct nsAutoCString temporaries in nsDefaultURIFixup; r=smaug We can make the non-virtual overload of IsDomainWhitelisted take an nsACString instead of an nsAutoCString and avoid some unnecessary copying. --- docshell/base/nsDefaultURIFixup.cpp | 4 ++-- docshell/base/nsDefaultURIFixup.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index e73649759996..09a3e89f4188 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -1071,7 +1071,7 @@ nsDefaultURIFixup::KeywordURIFixup(const nsACString& aURIString, } bool -nsDefaultURIFixup::IsDomainWhitelisted(const nsAutoCString aAsciiHost, +nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aAsciiHost, const uint32_t aDotLoc) { if (sDNSFirstForSingleWords) { @@ -1098,7 +1098,7 @@ nsDefaultURIFixup::IsDomainWhitelisted(const nsACString& aDomain, const uint32_t aDotLoc, bool* aResult) { - *aResult = IsDomainWhitelisted(nsAutoCString(aDomain), aDotLoc); + *aResult = IsDomainWhitelisted(aDomain, aDotLoc); return NS_OK; } diff --git a/docshell/base/nsDefaultURIFixup.h b/docshell/base/nsDefaultURIFixup.h index 4a37438e9394..9616281f8eaf 100644 --- a/docshell/base/nsDefaultURIFixup.h +++ b/docshell/base/nsDefaultURIFixup.h @@ -40,7 +40,7 @@ private: bool PossiblyHostPortUrl(const nsACString& aUrl); bool MakeAlternateURI(nsIURI* aURI); bool IsLikelyFTP(const nsCString& aHostSpec); - bool IsDomainWhitelisted(const nsAutoCString aAsciiHost, + bool IsDomainWhitelisted(const nsACString& aAsciiHost, const uint32_t aDotLoc); };