From 27b6de8e73f2d72aa1262d6285fe487e4a1b051d Mon Sep 17 00:00:00 2001 From: Rutuja Surve Date: Fri, 2 Sep 2016 00:21:00 -0400 Subject: [PATCH 001/102] Bug 1255843 - Add process memory reporting tool to about:performance. r=mconley MozReview-Commit-ID: EHCkl6G3bTT --HG-- extra : rebase_source : 2e9d711aa977e6d0313f07d28e1a2a4861b501c0 extra : source : 5ad925dd2e4e9f6943b228f0173d01278a74c2a8 --- .../content/aboutPerformance.js | 118 ++++++++++++++++++ .../content/aboutPerformance.xhtml | 62 +++++++++ toolkit/content/process-content.js | 20 +++ toolkit/modules/Memory.jsm | 77 ++++++++++++ toolkit/modules/moz.build | 1 + 5 files changed, 278 insertions(+) create mode 100644 toolkit/modules/Memory.jsm diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.js b/toolkit/components/aboutperformance/content/aboutPerformance.js index 0693af318b11..e0d34128a39c 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.js +++ b/toolkit/components/aboutperformance/content/aboutPerformance.js @@ -14,6 +14,8 @@ const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats. const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); const { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); const { ObjectUtils } = Cu.import("resource://gre/modules/ObjectUtils.jsm", {}); +const { Memory } = Cu.import("resource://gre/modules/Memory.jsm"); +const { DownloadUtils } = Cu.import("resource://gre/modules/DownloadUtils.jsm"); // about:performance observes notifications on this topic. // if a notification is sent, this causes the page to be updated immediately, @@ -942,7 +944,123 @@ var Control = { _displayMode: MODE_GLOBAL, }; +/** + * This functionality gets memory related information of sub-processes and + * updates the performance table regularly. + * If the page goes hidden, it also handles visibility change by not + * querying the content processes unnecessarily. + */ +var SubprocessMonitor = { + _timeout: null, + + /** + * Init will start the process of updating the table if the page is not hidden, + * and set up an event listener for handling visibility changes. + */ + init: function() { + if (!document.hidden) { + SubprocessMonitor.updateTable(); + } + document.addEventListener("visibilitychange", SubprocessMonitor.handleVisibilityChange); + }, + + /** + * This function updates the table after an interval if the page is visible + * and clears the interval otherwise. + */ + handleVisibilityChange: function() { + if (!document.hidden) { + SubprocessMonitor.queueUpdate(); + } else { + clearTimeout(this._timeout); + this._timeout = null; + } + }, + + /** + * This function queues a timer to request the next summary using updateTable + * after some delay. + */ + queueUpdate: function() { + this._timeout = setTimeout(() => this.updateTable(), UPDATE_INTERVAL_MS); + }, + + /** + * This is a helper function for updateTable, which updates a particular row. + * @param { node} row The row to be updated. + * @param {object} summaries The object with the updated RSS and USS values. + * @param {string} pid The pid represented by the row for which we update. + */ + updateRow: function(row, summaries, pid) { + row.cells[0].textContent = pid; + let RSSval = DownloadUtils.convertByteUnits(summaries[pid].rss); + row.cells[1].textContent = RSSval.join(" "); + let USSval = DownloadUtils.convertByteUnits(summaries[pid].uss); + row.cells[2].textContent = USSval.join(" "); + }, + + /** + * This function adds a row to the subprocess-performance table for every new pid + * and populates and regularly updates it with RSS/USS measurements. + */ + updateTable: function() { + if (!document.hidden) { + Memory.summary().then((summaries) => { + if (!(Object.keys(summaries).length)) { + // The summaries list was empty, which means we timed out getting + // the memory reports. We'll try again later. + SubprocessMonitor.queueUpdate(); + return; + } + let resultTable = document.getElementById("subprocess-reports"); + let recycle = []; + // We first iterate the table to check if summaries exist for rowPids, + // if yes, update them and delete the pid's summary or else hide the row + // for recycling it. Start at row 1 instead of 0 (to skip the header row). + for (let i = 1, row; row = resultTable.rows[i]; i++) { + let rowPid = row.dataset.pid; + let summary = summaries[rowPid]; + if (summary) { + // Now we update the values in the row, which is hardcoded for now, + // but we might want to make this more adaptable in the future. + SubprocessMonitor.updateRow(row, summaries, rowPid); + delete summaries[rowPid]; + } else { + // Take this unnecessary row, hide it and stash it for potential re-use. + row.hidden = true; + recycle.push(row); + } + } + // For the remaining pids in summaries, we choose from the recyclable + // (hidden) nodes, and if they get exhausted, append a row to the table. + for (let pid in summaries) { + let row = recycle.pop(); + if (row) { + row.hidden = false; + } else { + // We create a new row here, and set it to row + row = document.createElement("tr"); + // Insert cell for pid + row.insertCell(); + // Insert a cell for USS. + row.insertCell(); + // Insert another cell for RSS. + row.insertCell(); + } + row.dataset.pid = pid; + // Update the row and put it at the bottom + SubprocessMonitor.updateRow(row, summaries, pid); + resultTable.appendChild(row); + } + }); + SubprocessMonitor.queueUpdate(); + } + }, +}; + var go = Task.async(function*() { + + SubprocessMonitor.init(); Control.init(); // Setup a hook to allow tests to configure and control this page diff --git a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml index 048b0905c106..721320534b48 100644 --- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml +++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml @@ -7,9 +7,21 @@ about:performance + + +
+

Memory usage of Subprocesses

+ + + + + + +
Process IDResident Set SizeUnique Set Size
+
diff --git a/toolkit/content/process-content.js b/toolkit/content/process-content.js index 9262d0b7f90b..2ff8f908a956 100644 --- a/toolkit/content/process-content.js +++ b/toolkit/content/process-content.js @@ -31,15 +31,35 @@ if (gInContentProcess) { init() { for (let topic of this.TOPICS) { Services.obs.addObserver(this, topic, false); + Services.cpmm.addMessageListener("Memory:GetSummary", this); } }, uninit() { for (let topic of this.TOPICS) { Services.obs.removeObserver(this, topic); + Services.cpmm.removeMessageListener("Memory:GetSummary", this); } }, + receiveMessage(msg) { + if (msg.name != "Memory:GetSummary") { + return; + } + let pid = Services.appinfo.processID; + let memMgr = Cc["@mozilla.org/memory-reporter-manager;1"] + .getService(Ci.nsIMemoryReporterManager); + let rss = memMgr.resident; + let uss = memMgr.residentUnique; + Services.cpmm.sendAsyncMessage("Memory:Summary", { + pid, + summary: { + uss, + rss, + } + }); + }, + observe(subject, topic, data) { switch (topic) { case "inner-window-destroyed": { diff --git a/toolkit/modules/Memory.jsm b/toolkit/modules/Memory.jsm new file mode 100644 index 000000000000..bb8e331c649f --- /dev/null +++ b/toolkit/modules/Memory.jsm @@ -0,0 +1,77 @@ +/* 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/. */ + +this.EXPORTED_SYMBOLS = ["Memory"]; + +const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +// How long we should wait for the Promise to resolve. +const TIMEOUT_INTERVAL = 2000; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Timer.jsm"); + +this.Memory = { + /** + * This function returns a Promise that resolves with an Object that + * describes basic memory usage for each content process and the parent + * process. + * @returns Promise + * @resolves JS Object + * An Object in the following format: + * { + * "parent": { + * uss: , + * rss: , + * }, + * : { + * uss: , + * rss: , + * }, + * ... + * } + */ + summary() { + if (!this._pendingPromise) { + this._pendingPromise = new Promise((resolve) => { + this._pendingResolve = resolve; + this._summaries = {}; + Services.ppmm.broadcastAsyncMessage("Memory:GetSummary"); + Services.ppmm.addMessageListener("Memory:Summary", this); + this._pendingTimeout = setTimeout(() => { this.finish(); }, TIMEOUT_INTERVAL); + }); + } + return this._pendingPromise; + }, + + receiveMessage(msg) { + if (msg.name != "Memory:Summary" || !this._pendingResolve) { + return; + } + this._summaries[msg.data.pid] = msg.data.summary; + // Now we check if we are done for all content processes. + // Services.ppmm.childCount is a count of how many processes currently + // exist that might respond to messages sent through the ppmm, including + // the parent process. So we subtract the parent process with the "- 1", + // and that’s how many content processes we’re waiting for. + if (Object.keys(this._summaries).length >= Services.ppmm.childCount - 1) { + this.finish(); + } + }, + + finish() { + // Code to gather the USS and RSS values for the parent process. This + // functions the same way as in process-content.js. + let memMgr = Cc["@mozilla.org/memory-reporter-manager;1"] + .getService(Ci.nsIMemoryReporterManager); + let rss = memMgr.resident; + let uss = memMgr.residentUnique; + this._summaries["Parent"] = { uss, rss }; + this._pendingResolve(this._summaries); + this._pendingResolve = null; + this._summaries = null; + this._pendingPromise = null; + clearTimeout(this._pendingTimeout); + Services.ppmm.removeMessageListener("Memory:Summary", this); + } +}; diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 677c80e7ea69..886a91f12dd3 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -52,6 +52,7 @@ EXTRA_JS_MODULES += [ 'LoadContextInfo.jsm', 'Locale.jsm', 'Log.jsm', + 'Memory.jsm', 'NewTabUtils.jsm', 'NLP.jsm', 'ObjectUtils.jsm', From 525b93bdcd7eaa40fb0989fef4fd83785e4bb519 Mon Sep 17 00:00:00 2001 From: Milan Sreckovic Date: Fri, 2 Sep 2016 11:34:45 -0400 Subject: [PATCH 002/102] Bug 1299606: Diagnostic, trivial patch. Our messages were not unique. r=me --- gfx/layers/client/TextureClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index 4aba61fc4f79..ebb6d3b43244 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -883,7 +883,7 @@ TextureClient::InitIPDLActor(CompositableForwarder* aForwarder) // It's Ok for a texture to move from a ShadowLayerForwarder to another, but // not form a CompositorBridgeChild to another (they use different channels). if (currentTexFwd && currentTexFwd != aForwarder->AsTextureForwarder()) { - gfxCriticalError() << "Attempt to move a texture to a different channel."; + gfxCriticalError() << "Attempt to move a texture to a different channel CF."; return false; } if (currentFwd && currentFwd->GetCompositorBackendType() != aForwarder->GetCompositorBackendType()) { @@ -940,7 +940,7 @@ TextureClient::InitIPDLActor(TextureForwarder* aForwarder, LayersBackend aBacken } if (currentTexFwd && currentTexFwd != aForwarder) { - gfxCriticalError() << "Attempt to move a texture to a different channel."; + gfxCriticalError() << "Attempt to move a texture to a different channel TF."; return false; } mActor->mTextureForwarder = aForwarder; From 272a50201ec39c8ed931aeb65939dbea1659a219 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 2 Sep 2016 12:03:20 -0400 Subject: [PATCH 003/102] Bug 1298459 - Only include AccessibleNode.webidl if accessibility is enabled. r=smaug --- dom/webidl/Node.webidl | 2 ++ dom/webidl/moz.build | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dom/webidl/Node.webidl b/dom/webidl/Node.webidl index 87e08bd68621..3c4059800a87 100644 --- a/dom/webidl/Node.webidl +++ b/dom/webidl/Node.webidl @@ -108,6 +108,8 @@ interface Node : EventTarget { [ChromeOnly] sequence getBoundMutationObservers(); +#ifdef ACCESSIBILITY [Pref="accessibility.AOM.enabled"] readonly attribute AccessibleNode? accessibleNode; +#endif }; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 8620de4d4e32..45a3967fe508 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -11,6 +11,7 @@ GENERATED_WEBIDL_FILES = [ PREPROCESSED_WEBIDL_FILES = [ 'HTMLMediaElement.webidl', 'Navigator.webidl', + 'Node.webidl', 'Promise.webidl', 'PromiseDebugging.webidl', 'Window.webidl', @@ -18,7 +19,6 @@ PREPROCESSED_WEBIDL_FILES = [ WEBIDL_FILES = [ 'AbstractWorker.webidl', - 'AccessibleNode.webidl', 'AddonManager.webidl', 'AnalyserNode.webidl', 'Animatable.webidl', @@ -350,7 +350,6 @@ WEBIDL_FILES = [ 'NetDashboard.webidl', 'NetworkInformation.webidl', 'NetworkOptions.webidl', - 'Node.webidl', 'NodeFilter.webidl', 'NodeIterator.webidl', 'NodeList.webidl', @@ -925,3 +924,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': WEBIDL_FILES += [ 'AlarmsManager.webidl', ] + +if CONFIG['ACCESSIBILITY']: + WEBIDL_FILES += [ + 'AccessibleNode.webidl', + ] From 303eae9cecfd76668b07f9206fbdc048b1d0ef31 Mon Sep 17 00:00:00 2001 From: Alessio Placitelli Date: Fri, 2 Sep 2016 03:44:00 -0400 Subject: [PATCH 004/102] Bug 1288709 - Split the browser_UsageTelemetry.js test. r=gijs MozReview-Commit-ID: IykhSB7RtSH --HG-- rename : browser/modules/test/browser_UsageTelemetry.js => browser/modules/test/browser_UsageTelemetry_private_and_restore.js --- browser/modules/test/browser.ini | 1 + .../modules/test/browser_UsageTelemetry.js | 80 ----------------- ...wser_UsageTelemetry_private_and_restore.js | 88 +++++++++++++++++++ 3 files changed, 89 insertions(+), 80 deletions(-) create mode 100644 browser/modules/test/browser_UsageTelemetry_private_and_restore.js diff --git a/browser/modules/test/browser.ini b/browser/modules/test/browser.ini index fb926c445c07..13cb18d719eb 100644 --- a/browser/modules/test/browser.ini +++ b/browser/modules/test/browser.ini @@ -24,4 +24,5 @@ support-files = [browser_taskbar_preview.js] skip-if = os != "win" [browser_UsageTelemetry.js] +[browser_UsageTelemetry_private_and_restore.js] [browser_urlBar_zoom.js] diff --git a/browser/modules/test/browser_UsageTelemetry.js b/browser/modules/test/browser_UsageTelemetry.js index ff437c92a1ee..ab172fdbea68 100644 --- a/browser/modules/test/browser_UsageTelemetry.js +++ b/browser/modules/test/browser_UsageTelemetry.js @@ -44,15 +44,6 @@ function browserLocationChanged(browser) { }); } -function promiseBrowserStateRestored() { - return new Promise(resolve => { - Services.obs.addObserver(function observer(aSubject, aTopic) { - Services.obs.removeObserver(observer, "sessionstore-browser-state-restored"); - resolve(); - }, "sessionstore-browser-state-restored", false); - }); -} - /** * An helper that checks the value of a scalar if it's expected to be > 0, * otherwise makes sure that the scalar it's not reported. @@ -251,74 +242,3 @@ add_task(function* test_URIAndDomainCounts() { yield BrowserTestUtils.removeTab(firstTab); yield BrowserTestUtils.closeWindow(newWin); }); - -add_task(function* test_privateMode() { - // Let's reset the counts. - Services.telemetry.clearScalars(); - - // Open a private window and load a website in it. - let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true}); - yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/"); - yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser); - - // Check that tab and window count is recorded. - const scalars = - Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs in private mode."); - ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains in private mode."); - is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value."); - is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value."); - is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value."); - is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value."); - - // Clean up. - yield BrowserTestUtils.closeWindow(privateWin); -}); - -add_task(function* test_sessionRestore() { - const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand"; - Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false); - registerCleanupFunction(() => { - Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND); - }); - - // Let's reset the counts. - Services.telemetry.clearScalars(); - - // The first window will be put into the already open window and the second - // window will be opened with _openWindowWithState, which is the source of the problem. - const state = { - windows: [ - { - tabs: [ - { entries: [{ url: "http://example.org" }], extData: { "uniq": 3785 } } - ], - selected: 1 - } - ] - }; - - // Save the current session. - let SessionStore = - Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {}).SessionStore; - let backupState = SessionStore.getBrowserState(); - - // Load the custom state and wait for SSTabRestored, as we want to make sure - // that the URI counting code was hit. - let tabRestored = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored"); - SessionStore.setBrowserState(JSON.stringify(state)); - yield tabRestored; - - // Check that the URI is not recorded. - const scalars = - Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); - - ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs from restored sessions."); - ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains from restored sessions."); - - // Restore the original session and cleanup. - let sessionRestored = promiseBrowserStateRestored(); - SessionStore.setBrowserState(JSON.stringify(state)); - yield sessionRestored; -}); diff --git a/browser/modules/test/browser_UsageTelemetry_private_and_restore.js b/browser/modules/test/browser_UsageTelemetry_private_and_restore.js new file mode 100644 index 000000000000..2b9c2e9b316e --- /dev/null +++ b/browser/modules/test/browser_UsageTelemetry_private_and_restore.js @@ -0,0 +1,88 @@ +"use strict"; + +const MAX_CONCURRENT_TABS = "browser.engagement.max_concurrent_tab_count"; +const TAB_EVENT_COUNT = "browser.engagement.tab_open_event_count"; +const MAX_CONCURRENT_WINDOWS = "browser.engagement.max_concurrent_window_count"; +const WINDOW_OPEN_COUNT = "browser.engagement.window_open_event_count"; +const TOTAL_URI_COUNT = "browser.engagement.total_uri_count"; +const UNIQUE_DOMAINS_COUNT = "browser.engagement.unique_domains_count"; + +function promiseBrowserStateRestored() { + return new Promise(resolve => { + Services.obs.addObserver(function observer(aSubject, aTopic) { + Services.obs.removeObserver(observer, "sessionstore-browser-state-restored"); + resolve(); + }, "sessionstore-browser-state-restored", false); + }); +} + +add_task(function* test_privateMode() { + // Let's reset the counts. + Services.telemetry.clearScalars(); + + // Open a private window and load a website in it. + let privateWin = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + yield BrowserTestUtils.loadURI(privateWin.gBrowser.selectedBrowser, "http://example.com/"); + yield BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser); + + // Check that tab and window count is recorded. + const scalars = + Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); + + ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs in private mode."); + ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains in private mode."); + is(scalars[TAB_EVENT_COUNT], 1, "The number of open tab event count must match the expected value."); + is(scalars[MAX_CONCURRENT_TABS], 2, "The maximum tab count must match the expected value."); + is(scalars[WINDOW_OPEN_COUNT], 1, "The number of window open event count must match the expected value."); + is(scalars[MAX_CONCURRENT_WINDOWS], 2, "The maximum window count must match the expected value."); + + // Clean up. + yield BrowserTestUtils.closeWindow(privateWin); +}); + +add_task(function* test_sessionRestore() { + const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand"; + Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false); + registerCleanupFunction(() => { + Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND); + }); + + // Let's reset the counts. + Services.telemetry.clearScalars(); + + // The first window will be put into the already open window and the second + // window will be opened with _openWindowWithState, which is the source of the problem. + const state = { + windows: [ + { + tabs: [ + { entries: [{ url: "http://example.org" }], extData: { "uniq": 3785 } } + ], + selected: 1 + } + ] + }; + + // Save the current session. + let SessionStore = + Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {}).SessionStore; + let backupState = SessionStore.getBrowserState(); + + // Load the custom state and wait for SSTabRestored, as we want to make sure + // that the URI counting code was hit. + let tabRestored = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "SSTabRestored"); + SessionStore.setBrowserState(JSON.stringify(state)); + yield tabRestored; + + // Check that the URI is not recorded. + const scalars = + Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN); + + ok(!(TOTAL_URI_COUNT in scalars), "We should not track URIs from restored sessions."); + ok(!(UNIQUE_DOMAINS_COUNT in scalars), "We should not track unique domains from restored sessions."); + + // Restore the original session and cleanup. + let sessionRestored = promiseBrowserStateRestored(); + SessionStore.setBrowserState(JSON.stringify(state)); + yield sessionRestored; +}); From 64a98fc3b7a60080367935533af0471125b8abf5 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 26 Aug 2016 18:04:47 -0400 Subject: [PATCH 005/102] Bug 1264192 - Adjust cookie eviction heuristics when exceeding the maximum cookies allowed per host. r=ehsan If no expired cookies exist, in order of preference, evict the oldest: * session cookie with a non-matching path * session cookie with a matching path * non-session cookie with a non-matching path * non-session cookie with a matching path This replaces the previous heuristic of evicting the oldest cookie, irregardless of any other attributes, if no expired cookies were present. This ensures that cookies that are already considered transient by web applications will be removed first, followed by cookies that are unrelated to the response that is adding new cookies. --HG-- extra : amend_source : f79ba9dd393a3f37760e643b10b7137e37a6397a --- netwerk/cookie/nsCookie.cpp | 4 +- netwerk/cookie/nsCookie.h | 4 + netwerk/cookie/nsCookieService.cpp | 96 ++++++++- netwerk/cookie/nsCookieService.h | 2 +- netwerk/cookie/test/unit/test_eviction.js | 252 ++++++++++++++++++++++ netwerk/cookie/test/unit/xpcshell.ini | 1 + 6 files changed, 347 insertions(+), 12 deletions(-) create mode 100644 netwerk/cookie/test/unit/test_eviction.js diff --git a/netwerk/cookie/nsCookie.cpp b/netwerk/cookie/nsCookie.cpp index f864d9e8649f..5afe6fe80bb5 100644 --- a/netwerk/cookie/nsCookie.cpp +++ b/netwerk/cookie/nsCookie.cpp @@ -9,8 +9,6 @@ #include "nsUTF8ConverterService.h" #include -static const int64_t kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds - /****************************************************************************** * nsCookie: * string helper impl @@ -130,7 +128,7 @@ nsCookie::IsStale() const { int64_t currentTimeInUsec = PR_Now(); - return currentTimeInUsec - LastAccessed() > kCookieStaleThreshold; + return currentTimeInUsec - LastAccessed() > mCookieStaleThreshold * PR_USEC_PER_SEC; } /****************************************************************************** diff --git a/netwerk/cookie/nsCookie.h b/netwerk/cookie/nsCookie.h index 58154b0fa0c9..812db3f32859 100644 --- a/netwerk/cookie/nsCookie.h +++ b/netwerk/cookie/nsCookie.h @@ -12,6 +12,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/BasePrincipal.h" +#include "mozilla/Preferences.h" using mozilla::OriginAttributes; @@ -56,6 +57,8 @@ class nsCookie : public nsICookie2 , mExpiry(aExpiry) , mLastAccessed(aLastAccessed) , mCreationTime(aCreationTime) + // Defaults to 60s + , mCookieStaleThreshold(mozilla::Preferences::GetInt("network.cookie.staleThreshold", 60)) , mIsSession(aIsSession) , mIsSecure(aIsSecure) , mIsHttpOnly(aIsHttpOnly) @@ -127,6 +130,7 @@ class nsCookie : public nsICookie2 int64_t mExpiry; int64_t mLastAccessed; int64_t mCreationTime; + int64_t mCookieStaleThreshold; bool mIsSession; bool mIsSecure; bool mIsHttpOnly; diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp index 891b55726630..f106539e484d 100644 --- a/netwerk/cookie/nsCookieService.cpp +++ b/netwerk/cookie/nsCookieService.cpp @@ -3493,7 +3493,7 @@ nsCookieService::AddInternal(const nsCookieKey &aKey, nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey); if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) { nsListIter iter; - FindStaleCookie(entry, currentTime, iter); + FindStaleCookie(entry, currentTime, aHostURI, iter); oldCookie = iter.Cookie(); // remove the oldest cookie from the domain @@ -4361,12 +4361,35 @@ nsCookieService::CookieExists(nsICookie2 *aCookie, void nsCookieService::FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, + nsIURI* aSource, nsListIter &aIter) { - aIter.entry = nullptr; + bool requireHostMatch = true; + nsAutoCString baseDomain, sourceHost, sourcePath; + if (aSource) { + GetBaseDomain(aSource, baseDomain, requireHostMatch); + aSource->GetAsciiHost(sourceHost); + aSource->GetPath(sourcePath); + } - int64_t oldestTime = 0; const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies(); + + int64_t oldestNonMatchingSessionCookieTime = 0; + nsListIter oldestNonMatchingSessionCookie; + oldestNonMatchingSessionCookie.entry = nullptr; + + int64_t oldestSessionCookieTime = 0; + nsListIter oldestSessionCookie; + oldestSessionCookie.entry = nullptr; + + int64_t oldestNonMatchingNonSessionCookieTime = 0; + nsListIter oldestNonMatchingNonSessionCookie; + oldestNonMatchingNonSessionCookie.entry = nullptr; + + int64_t oldestCookieTime = 0; + nsListIter oldestCookie; + oldestCookie.entry = nullptr; + for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) { nsCookie *cookie = cookies[i]; @@ -4377,12 +4400,69 @@ nsCookieService::FindStaleCookie(nsCookieEntry *aEntry, return; } - // Check if we've found the oldest cookie so far. - if (!aIter.entry || oldestTime > cookie->LastAccessed()) { - oldestTime = cookie->LastAccessed(); - aIter.entry = aEntry; - aIter.index = i; + // Update our various records of oldest cookies fitting several restrictions: + // * session cookies + // * non-session cookies + // * cookies with paths and domains that don't match the cookie triggering this purge + + uint32_t cookiePathLen = cookie->Path().Length(); + if (cookiePathLen > 0 && cookie->Path().Last() == '/') + --cookiePathLen; + + // This cookie is a candidate for eviction if we have no information about + // the source request, or if it is not a path or domain match against the + // source request. + bool isPrimaryEvictionCandidate = true; + if (aSource) { + bool pathMatches = StringBeginsWith(sourcePath, Substring(cookie->Path(), 0, cookiePathLen)); + bool domainMatches = cookie->RawHost() == sourceHost || + (cookie->IsDomain() && StringEndsWith(sourceHost, cookie->Host())); + isPrimaryEvictionCandidate = !pathMatches || !domainMatches; } + + int64_t lastAccessed = cookie->LastAccessed(); + if (cookie->IsSession()) { + if (!oldestSessionCookie.entry || oldestSessionCookieTime > lastAccessed) { + oldestSessionCookieTime = lastAccessed; + oldestSessionCookie.entry = aEntry; + oldestSessionCookie.index = i; + } + + if (isPrimaryEvictionCandidate && + (!oldestNonMatchingSessionCookie.entry || + oldestNonMatchingSessionCookieTime > lastAccessed)) { + oldestNonMatchingSessionCookieTime = lastAccessed; + oldestNonMatchingSessionCookie.entry = aEntry; + oldestNonMatchingSessionCookie.index = i; + } + } else if (isPrimaryEvictionCandidate && + (!oldestNonMatchingNonSessionCookie.entry || + oldestNonMatchingNonSessionCookieTime > lastAccessed)) { + oldestNonMatchingNonSessionCookieTime = lastAccessed; + oldestNonMatchingNonSessionCookie.entry = aEntry; + oldestNonMatchingNonSessionCookie.index = i; + } + + // Check if we've found the oldest cookie so far. + if (!oldestCookie.entry || oldestCookieTime > lastAccessed) { + oldestCookieTime = lastAccessed; + oldestCookie.entry = aEntry; + oldestCookie.index = i; + } + } + + // Prefer to evict the oldest session cookies with a non-matching path/domain, + // followed by the oldest session cookie with a matching path/domain, + // followed by the oldest non-session cookie with a non-matching path/domain, + // resorting to the oldest non-session cookie with a matching path/domain. + if (oldestNonMatchingSessionCookie.entry) { + aIter = oldestNonMatchingSessionCookie; + } else if (oldestSessionCookie.entry) { + aIter = oldestSessionCookie; + } else if (oldestNonMatchingNonSessionCookie.entry) { + aIter = oldestNonMatchingNonSessionCookie; + } else { + aIter = oldestCookie; } } diff --git a/netwerk/cookie/nsCookieService.h b/netwerk/cookie/nsCookieService.h index 1aba062f01af..12a527a5226e 100644 --- a/netwerk/cookie/nsCookieService.h +++ b/netwerk/cookie/nsCookieService.h @@ -312,7 +312,7 @@ class nsCookieService final : public nsICookieService void RemoveAllFromMemory(); already_AddRefed PurgeCookies(int64_t aCurrentTimeInUsec); bool FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter); - static void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter); + void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsIURI* aSource, nsListIter &aIter); void NotifyRejected(nsIURI *aHostURI); void NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel); void NotifyChanged(nsISupports *aSubject, const char16_t *aData); diff --git a/netwerk/cookie/test/unit/test_eviction.js b/netwerk/cookie/test/unit/test_eviction.js new file mode 100644 index 000000000000..ab29ddb0a277 --- /dev/null +++ b/netwerk/cookie/test/unit/test_eviction.js @@ -0,0 +1,252 @@ +var {utils: Cu, interfaces: Ci, classes: Cc} = Components; + +Cu.import("resource://gre/modules/Services.jsm"); + +const BASE_HOSTNAMES = ["example.org", "example.co.uk"]; +const SUBDOMAINS = ["", "pub.", "www.", "other."]; + +const cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService); +const cm = cs.QueryInterface(Ci.nsICookieManager2); + +function run_test() { + Services.prefs.setIntPref("network.cookie.staleThreshold", 0); + for (var host of BASE_HOSTNAMES) { + do_print('testing with host ' + host); + var base = SUBDOMAINS[0] + host; + var sub = SUBDOMAINS[1] + host; + var other = SUBDOMAINS[2] + host; + var another = SUBDOMAINS[3] + host; + test_basic_eviction(base, sub, other, another); + cm.removeAll(); + test_domain_or_path_matches_not_both(base, sub, other, another); + cm.removeAll(); + } + test_localdomain(); +} + +// Verify that subdomains of localhost are treated as separate hosts and aren't considered +// candidates for eviction. +function test_localdomain() { + Services.prefs.setIntPref("network.cookie.maxPerHost", 2); + + const BASE_URI = Services.io.newURI("http://localhost", null, null); + const BASE_BAR = Services.io.newURI("http://localhost/bar", null, null); + const OTHER_URI = Services.io.newURI("http://other.localhost", null, null); + const OTHER_BAR = Services.io.newURI("http://other.localhost/bar", null, null); + + setCookie("session_no_path", null, null, null, BASE_URI); + setCookie("session_bar_path", null, "/bar", null, BASE_BAR); + + setCookie("session_no_path", null, null, null, OTHER_URI); + setCookie("session_bar_path", null, "/bar", null, OTHER_BAR); + + verifyCookies(['session_no_path', + 'session_bar_path'], BASE_URI); + verifyCookies(['session_no_path', + 'session_bar_path'], OTHER_URI); + + setCookie("session_another_no_path", null, null, null, BASE_URI); + verifyCookies(['session_no_path', + 'session_another_no_path'], BASE_URI); + + setCookie("session_another_no_path", null, null, null, OTHER_URI); + verifyCookies(['session_no_path', + 'session_another_no_path'], OTHER_URI); +} + +// Ensure that cookies are still considered candidates for eviction if either the domain +// or path matches, but not both. +function test_domain_or_path_matches_not_both(base_host, + subdomain_host, + other_subdomain_host, + another_subdomain_host) { + Services.prefs.setIntPref("network.cookie.maxPerHost", 2); + + const BASE_URI = Services.io.newURI("http://" + base_host, null, null); + const PUB_FOO_PATH = Services.io.newURI("http://" + subdomain_host + "/foo", null, null); + const WWW_BAR_PATH = Services.io.newURI("http://" + other_subdomain_host + "/bar", null, null); + const OTHER_BAR_PATH = Services.io.newURI("http://" + another_subdomain_host + "/bar", null, null); + const PUB_BAR_PATH = Services.io.newURI("http://" + subdomain_host + "/bar", null, null); + const WWW_FOO_PATH = Services.io.newURI("http://" + other_subdomain_host + "/foo", null, null); + + setCookie("session_pub_with_foo_path", subdomain_host, "/foo", null, PUB_FOO_PATH); + setCookie("session_www_with_bar_path", other_subdomain_host, "/bar", null, WWW_BAR_PATH); + verifyCookies(['session_pub_with_foo_path', + 'session_www_with_bar_path'], BASE_URI); + + setCookie("session_pub_with_bar_path", subdomain_host, "/bar", null, PUB_BAR_PATH); + verifyCookies(['session_www_with_bar_path', + 'session_pub_with_bar_path'], BASE_URI); + + setCookie("session_other_with_bar_path", another_subdomain_host, "/bar", null, OTHER_BAR_PATH); + verifyCookies(['session_pub_with_bar_path', + 'session_other_with_bar_path'], BASE_URI); +} + +function test_basic_eviction(base_host, subdomain_host, other_subdomain_host) { + Services.prefs.setIntPref("network.cookie.maxPerHost", 5); + + const BASE_URI = Services.io.newURI("http://" + base_host, null, null); + const SUBDOMAIN_URI = Services.io.newURI("http://" + subdomain_host, null, null); + const OTHER_SUBDOMAIN_URI = Services.io.newURI("http://" + other_subdomain_host, null, null); + const FOO_PATH = Services.io.newURI("http://" + base_host + "/foo", null, null); + const BAR_PATH = Services.io.newURI("http://" + base_host + "/bar", null, null); + const ALL_SUBDOMAINS = '.' + base_host; + const OTHER_SUBDOMAIN = other_subdomain_host; + + // Initialize the set of cookies with a mix of non-session cookies with no path, + // and session cookies with explicit paths. Any subsequent cookies added will cause + // existing cookies to be evicted. + setCookie("non_session_non_path_non_domain", null, null, 10000, BASE_URI); + setCookie("non_session_non_path_subdomain", ALL_SUBDOMAINS, null, 10000, SUBDOMAIN_URI); + setCookie("session_non_path_pub_domain", OTHER_SUBDOMAIN, null, null, OTHER_SUBDOMAIN_URI); + setCookie("session_foo_path", null, "/foo", null, FOO_PATH); + setCookie("session_bar_path", null, "/bar", null, BAR_PATH); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'session_non_path_pub_domain', + 'session_foo_path', + 'session_bar_path'], BASE_URI); + + // Ensure that cookies set for the / path appear more recent. + cs.getCookieString(OTHER_SUBDOMAIN_URI, null) + verifyCookies(['non_session_non_path_non_domain', + 'session_foo_path', + 'session_bar_path', + 'non_session_non_path_subdomain', + 'session_non_path_pub_domain'], BASE_URI); + + // Evict oldest session cookie that does not match example.org/foo (session_bar_path) + setCookie("session_foo_path_2", null, "/foo", null, FOO_PATH); + verifyCookies(['non_session_non_path_non_domain', + 'session_foo_path', + 'non_session_non_path_subdomain', + 'session_non_path_pub_domain', + 'session_foo_path_2'], BASE_URI); + + // Evict oldest session cookie that does not match example.org/bar (session_foo_path) + setCookie("session_bar_path_2", null, "/bar", null, BAR_PATH); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'session_non_path_pub_domain', + 'session_foo_path_2', + 'session_bar_path_2'], BASE_URI); + + // Evict oldest session cookie that does not match example.org/ (session_non_path_pub_domain) + setCookie("non_session_non_path_non_domain_2", null, null, 10000, BASE_URI); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'session_foo_path_2', + 'session_bar_path_2', + 'non_session_non_path_non_domain_2'], BASE_URI); + + // Evict oldest session cookie that does not match example.org/ (session_foo_path_2) + setCookie("session_non_path_non_domain_3", null, null, null, BASE_URI); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'session_bar_path_2', + 'non_session_non_path_non_domain_2', + 'session_non_path_non_domain_3'], BASE_URI); + + // Evict oldest session cookie; all such cookies match example.org/bar (session_bar_path_2) + setCookie("non_session_non_path_non_domain_3", null, null, 10000, BAR_PATH); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'non_session_non_path_non_domain_2', + 'session_non_path_non_domain_3', + 'non_session_non_path_non_domain_3'], BASE_URI); + + // Evict oldest session cookie, even though it matches pub.example.org (session_non_path_non_domain_3) + setCookie("non_session_non_path_pub_domain", null, null, 10000, OTHER_SUBDOMAIN_URI); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'non_session_non_path_non_domain_2', + 'non_session_non_path_non_domain_3', + 'non_session_non_path_pub_domain'], BASE_URI); + + // All session cookies have been evicted. + // Evict oldest non-session non-domain-matching cookie (non_session_non_path_pub_domain) + setCookie("non_session_bar_path_non_domain", null, '/bar', 10000, BAR_PATH); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'non_session_non_path_non_domain_2', + 'non_session_non_path_non_domain_3', + 'non_session_bar_path_non_domain'], BASE_URI); + + // Evict oldest non-session non-path-matching cookie (non_session_bar_path_non_domain) + setCookie("non_session_non_path_non_domain_4", null, null, 10000, BASE_URI); + verifyCookies(['non_session_non_path_non_domain', + 'non_session_non_path_subdomain', + 'non_session_non_path_non_domain_2', + 'non_session_non_path_non_domain_3', + 'non_session_non_path_non_domain_4'], BASE_URI); + + // At this point all remaining cookies are non-session cookies, have a path of /, + // and either don't have a domain or have one that matches subdomains. + // They will therefore be evicted from oldest to newest if all new cookies added share + // similar characteristics. +} + +// Verify that the given cookie names exist, and are ordered from least to most recently accessed +function verifyCookies(names, uri) { + do_check_eq(cm.countCookiesFromHost(uri.host), names.length); + let cookies = cm.getCookiesFromHost(uri.host, {}); + let actual_cookies = []; + while (cookies.hasMoreElements()) { + let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2); + actual_cookies.push(cookie); + } + if (names.length != actual_cookies.length) { + let left = names.filter(function(n) { + return actual_cookies.findIndex(function(c) { + return c.name == n; + }) == -1; + }); + let right = actual_cookies.filter(function(c) { + return names.findIndex(function(n) { + return c.name == n; + }) == -1; + }).map(function(c) { return c.name }); + if (left.length) { + do_print("unexpected cookies: " + left); + } + if (right.length) { + do_print("expected cookies: " + right); + } + } + do_check_eq(names.length, actual_cookies.length); + actual_cookies.sort(function(a, b) { + if (a.lastAccessed < b.lastAccessed) + return -1; + if (a.lastAccessed > b.lastAccessed) + return 1; + return 0; + }); + for (var i = 0; i < names.length; i++) { + do_check_eq(names[i], actual_cookies[i].name); + do_check_eq(names[i].startsWith('session'), actual_cookies[i].isSession); + } +} + +var lastValue = 0 +function setCookie(name, domain, path, maxAge, url) { + let value = name + "=" + ++lastValue; + var s = 'setting cookie ' + value; + if (domain) { + value += "; Domain=" + domain; + s += ' (d=' + domain + ')'; + } + if (path) { + value += "; Path=" + path; + s += ' (p=' + path + ')'; + } + if (maxAge) { + value += "; Max-Age=" + maxAge; + s += ' (non-session)'; + } else { + s += ' (session)'; + } + s += ' for ' + url.spec; + do_print(s); + cs.setCookieStringFromHttp(url, null, null, value, null, null); +} diff --git a/netwerk/cookie/test/unit/xpcshell.ini b/netwerk/cookie/test/unit/xpcshell.ini index 78f40fd14d61..e95ea3965bcf 100644 --- a/netwerk/cookie/test/unit/xpcshell.ini +++ b/netwerk/cookie/test/unit/xpcshell.ini @@ -8,3 +8,4 @@ skip-if = toolkit == 'gonk' [test_bug1267910.js] [test_parser_0001.js] [test_parser_0019.js] +[test_eviction.js] \ No newline at end of file From 4cd9e8a154d7eededab447a2add1f71cee999a75 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 006/102] Bug 1298541: Tracelogger: Part 1: Add debugging to check start and stop correspond, r=bbouvier --- js/src/vm/TraceLogging.cpp | 25 +++++++++++++++++++++++++ js/src/vm/TraceLogging.h | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index b1286e2feb1a..a8a0a4a23d7b 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -491,6 +491,14 @@ TraceLoggerThread::startEvent(uint32_t id) if (!traceLoggerState->isTextIdEnabled(id)) return; +#ifdef DEBUG + if (enabled > 0) { + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!graphStack.append(id)) + oomUnsafe.crash("Could not add item to debug stack."); + } +#endif + log(id); } @@ -516,6 +524,23 @@ TraceLoggerThread::stopEvent(uint32_t id) if (!traceLoggerState->isTextIdEnabled(id)) return; +#ifdef DEBUG + if (enabled > 0) { + uint32_t prev = graphStack.popCopy(); + if (id == TraceLogger_Engine) { + MOZ_ASSERT(prev == TraceLogger_IonMonkey || prev == TraceLogger_Baseline || + prev == TraceLogger_Interpreter); + } else if (id == TraceLogger_Scripts) { + MOZ_ASSERT(prev >= TraceLogger_Last); + } else if (id >= TraceLogger_Last) { + MOZ_ASSERT(prev >= TraceLogger_Last); + MOZ_ASSERT_IF(prev != id, strcmp(eventText(id), eventText(prev)) == 0); + } else { + MOZ_ASSERT(id == prev); + } + } +#endif + log(TraceLogger_Stop); } diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index f41737aac93c..3f7003ba93ac 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -183,6 +183,11 @@ class TraceLoggerThread // event. uint32_t iteration_; +#ifdef DEBUG + typedef Vector GraphStack; + GraphStack graphStack; +#endif + public: AutoTraceLog* top; From ce00bf219ef0a2896e125b1f9c0fa0d4f31f0f5e Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 007/102] Bug 1298541: Tracelogger: Part 2: Debugger::drainTraceLoggerScriptCalls should only return Script events, r=bbouvier --- js/src/vm/Debugger.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 9a86cae8c7e9..41bbfb2d5864 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -5056,7 +5056,14 @@ Debugger::drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp) if (!item) return false; + // Filter out internal time. uint32_t textId = eventItem->textId; + if (textId == TraceLogger_Internal) { + eventItem++; + MOZ_ASSERT(eventItem->textId == TraceLogger_Stop); + continue; + } + if (textId != TraceLogger_Stop && !logger->textIdIsScriptEvent(textId)) continue; From 12fecc29227bc7fe587a658f69cafd5ba6aa37f0 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 008/102] Bug 1298541: Tracelogger: Part 3: Trace generator resume in the Interpreter, r=bbouvier --- .../jit-test/tests/tracelogger/bug1298541.js | 41 +++++++++++++++++++ js/src/vm/Interpreter.cpp | 6 +++ 2 files changed, 47 insertions(+) create mode 100644 js/src/jit-test/tests/tracelogger/bug1298541.js diff --git a/js/src/jit-test/tests/tracelogger/bug1298541.js b/js/src/jit-test/tests/tracelogger/bug1298541.js new file mode 100644 index 000000000000..1a484d87234a --- /dev/null +++ b/js/src/jit-test/tests/tracelogger/bug1298541.js @@ -0,0 +1,41 @@ +/* -*- 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/. */ + +// Flags: -e "version(170)" + +//----------------------------------------------------------------------------- + +version(170) + +//----------------------------------------------------------------------------- + +var du = new Debugger(); +if (typeof du.drainTraceLoggerScriptCalls == "function") { + du.setupTraceLoggerScriptCalls(); + + du.startTraceLogger(); + test(); + du.endTraceLogger(); + + var objs = du.drainTraceLoggerScriptCalls(); + var scripts = 0; + var stops = 0; + for (var i = 0; i < objs.length; i++) { + if (objs[i].logType == "Script") { + scripts++; + } else if (objs[i].logType == "Stop") { + stops++; + } else { + throw "We shouldn't receive non-script events."; + } + } + assertEq(scripts, stops + 1); + // "+ 1" because we get a start for bug1298541.js:1, but not the stop. +} + +function test() +{ + for (var i in (function(){ for (var j=0;j<4;++j) { yield ""; } })()); +} diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 6d93b3c42680..ae99fe3bc5b9 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3897,6 +3897,12 @@ CASE(JSOP_RESUME) GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(REGS.pc); bool ok = GeneratorObject::resume(cx, activation, gen, val, resumeKind); SET_SCRIPT(REGS.fp()->script()); + + TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); + TraceLoggerEvent scriptEvent(logger, TraceLogger_Scripts, script); + TraceLogStartEvent(logger, scriptEvent); + TraceLogStartEvent(logger, TraceLogger_Interpreter); + if (!ok) goto error; } From 78bda3423e26ee4dab24e63c78a23da46d4d4338 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 009/102] Bug 1298541: Tracelogger: Part 4: Enable jit-tests/tests/tracelogger/drainTraceLogger.js again, r=bbouvier --- .../tests/tracelogger/drainTraceLogger.js | 40 +++++++++---------- js/src/vm/Debugger.cpp | 11 +++++ js/src/vm/TraceLogging.h | 8 +++- js/src/vm/TraceLoggingTypes.h | 8 ++-- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/js/src/jit-test/tests/tracelogger/drainTraceLogger.js b/js/src/jit-test/tests/tracelogger/drainTraceLogger.js index 80b96b341322..e394074bdd63 100644 --- a/js/src/jit-test/tests/tracelogger/drainTraceLogger.js +++ b/js/src/jit-test/tests/tracelogger/drainTraceLogger.js @@ -2,22 +2,24 @@ function TestDrainTraceLoggerInvariants(obj) { var scripts = 0; var stops = 0; for (var i = 0; i < objs.length; i++) { - if (objs[i].logType == "Scripts") { + if (objs[i].logType == "Script") { scripts++; - assertEq("fileName" in objs[i], true); - assertEq("lineNumber" in objs[i], true); - assertEq("columnNumber" in objs[i], true); + assertEq("fileName" in objs[i], true); + assertEq("lineNumber" in objs[i], true); + assertEq("columnNumber" in objs[i], true); } else if (objs[i].logType == "Stop") { stops++; } else { assertEq(true, false); } } - assertEq(scripts, stops); + assertEq(scripts, stops + 1); + // "+ 1" because we get a start for drainTraceLogger.js:1, but not the stop. } function GetMaxScriptDepth(obj) { var max_depth = 0; + var depth = 0; for (var i = 0; i < objs.length; i++) { if (objs[i].logType == "Stop") depth--; @@ -38,42 +40,39 @@ function foo2() { } var du = new Debugger(); -if (typeof du.drainTraceLoggerTraces == "function") { -print(1); +if (typeof du.drainTraceLoggerScriptCalls == "function") { // Test normal setup. du = new Debugger(); - du.setupTraceLoggerForTraces(); + du.setupTraceLoggerScriptCalls(); du.startTraceLogger(); du.endTraceLogger(); - var objs = du.drainTraceLoggerTraces(); + var objs = du.drainTraceLoggerScriptCalls(); TestDrainTraceLoggerInvariants(objs); - var empty_depth = GetMaxScriptDepth(objs); - var empty_length = objs.length; // Test basic script. for (var i=0; i<20; i++) foo1(); du = new Debugger(); - du.setupTraceLoggerTraces(); + du.setupTraceLoggerScriptCalls(); du.startTraceLogger(); foo1(); du.endTraceLogger(); - var objs = du.drainTraceLoggerTraces(); + var objs = du.drainTraceLoggerScriptCalls(); TestDrainTraceLoggerInvariants(objs); - assertEq(empty_depth + 2 == GetMaxScriptDepth(objs)); - assertEq(empty_length + 4 == GetMaxScriptDepth(objs)); - + assertEq(3, GetMaxScriptDepth(objs), "drainTraceLogger.js:0 + foo1 + foo2"); + assertEq(5, objs.length, "drainTraceLogger.js:0 + foo1 + foo2 + stop + stop"); + // Test basic script. for (var i=0; i<20; i++) foo1(); du = new Debugger(); - du.setupTraceLoggerForTraces(); + du.setupTraceLoggerScriptCalls(); du.startTraceLogger(); for (var i=0; i<100; i++) { @@ -81,8 +80,9 @@ print(1); } du.endTraceLogger(); - var objs = du.drainTraceLoggerTraces(); + var objs = du.drainTraceLoggerScriptCalls(); TestDrainTraceLoggerInvariants(objs); - assertEq(empty_depth + 2 == GetMaxScriptDepth(objs)); - assertEq(empty_length + 4*100 == GetMaxScriptDepth(objs)); + assertEq(3, GetMaxScriptDepth(objs), "drainTraceLogger.js:0 + foo1 + foo2"); + assertEq(4*100 + 1, objs.length); + assertEq(1 + 4*100, objs.length, "drainTraceLogger.js:0 + 4 * ( foo1 + foo2 + stop + stop )"); } diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 41bbfb2d5864..52c582967a1b 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -650,6 +650,17 @@ Debugger::Debugger(JSContext* cx, NativeObject* dbg) JS_INIT_CLIST(&breakpoints); JS_INIT_CLIST(&onNewGlobalObjectWatchersLink); + +#ifdef JS_TRACE_LOGGING + TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); + if (logger) { +#ifdef NIGHTLY_BUILD + logger->getIterationAndSize(&traceLoggerLastDrainedIteration, &traceLoggerLastDrainedSize); +#endif + logger->getIterationAndSize(&traceLoggerScriptedCallsLastDrainedIteration, + &traceLoggerScriptedCallsLastDrainedSize); + } +#endif } Debugger::~Debugger() diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 3f7003ba93ac..1edafa85d5e3 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -228,11 +228,15 @@ class TraceLoggerThread start = events.data(); } - *lastIteration = iteration_; - *lastSize = events.size(); + getIterationAndSize(lastIteration, lastSize); return start; } + void getIterationAndSize(uint32_t* iteration, uint32_t* size) const { + *iteration = iteration_; + *size = events.size(); + } + // Extract the details filename, lineNumber and columnNumber out of a event // containing script information. void extractScriptDetails(uint32_t textId, const char** filename, size_t* filename_len, diff --git a/js/src/vm/TraceLoggingTypes.h b/js/src/vm/TraceLoggingTypes.h index 58f8a5646fa3..44ec386c8147 100644 --- a/js/src/vm/TraceLoggingTypes.h +++ b/js/src/vm/TraceLoggingTypes.h @@ -178,19 +178,19 @@ class ContinuousSpace { return data_; } - uint32_t capacity() { + uint32_t capacity() const { return capacity_; } - uint32_t size() { + uint32_t size() const { return size_; } - bool empty() { + bool empty() const { return size_ == 0; } - uint32_t lastEntryId() { + uint32_t lastEntryId() const { MOZ_ASSERT(!empty()); return size_ - 1; } From 0bf1786e8367bf3cf2fd2331461330515c1faa59 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 010/102] Bug 1298541: Tracelogger: Part 5: Trace generator resume in Baseline, r=bbouvier --- js/src/jit/BaselineCompiler.cpp | 45 ++++++++++-- js/src/jit/BaselineCompiler.h | 1 + js/src/jit/BaselineJIT.cpp | 68 +++++++++---------- js/src/jit/BaselineJIT.h | 22 +++--- js/src/jit/VMFunctions.cpp | 5 ++ js/src/jit/shared/BaselineCompiler-shared.cpp | 3 +- js/src/jit/shared/BaselineCompiler-shared.h | 4 +- 7 files changed, 94 insertions(+), 54 deletions(-) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index cd420d3760ab..f52642a23037 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -196,20 +196,18 @@ BaselineCompiler::compile() // Note: There is an extra entry in the bytecode type map for the search hint, see below. size_t bytecodeTypeMapEntries = script->nTypeSets() + 1; - UniquePtr baselineScript( BaselineScript::New(script, prologueOffset_.offset(), epilogueOffset_.offset(), profilerEnterFrameToggleOffset_.offset(), profilerExitFrameToggleOffset_.offset(), - traceLoggerEnterToggleOffset_.offset(), - traceLoggerExitToggleOffset_.offset(), postDebugPrologueOffset_.offset(), icEntries_.length(), pcMappingIndexEntries.length(), pcEntries.length(), bytecodeTypeMapEntries, - yieldOffsets_.length()), + yieldOffsets_.length(), + traceLoggerToggleOffsets_.length()), JS::DeletePolicy(cx->runtime())); if (!baselineScript) { ReportOutOfMemory(cx); @@ -263,7 +261,7 @@ BaselineCompiler::compile() #ifdef JS_TRACE_LOGGING // Initialize the tracelogger instrumentation. - baselineScript->initTraceLogger(cx->runtime(), script); + baselineScript->initTraceLogger(cx->runtime(), script, traceLoggerToggleOffsets_); #endif uint32_t* bytecodeMap = baselineScript->bytecodeTypeMap(); @@ -842,7 +840,8 @@ BaselineCompiler::emitTraceLoggerEnter() Register scriptReg = regs.takeAnyGeneral(); Label noTraceLogger; - traceLoggerEnterToggleOffset_ = masm.toggledJump(&noTraceLogger); + if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) + return false; masm.Push(loggerReg); masm.Push(scriptReg); @@ -875,7 +874,8 @@ BaselineCompiler::emitTraceLoggerExit() Register loggerReg = regs.takeAnyGeneral(); Label noTraceLogger; - traceLoggerExitToggleOffset_ = masm.toggledJump(&noTraceLogger); + if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) + return false; masm.Push(loggerReg); masm.movePtr(ImmPtr(logger), loggerReg); @@ -889,6 +889,32 @@ BaselineCompiler::emitTraceLoggerExit() return true; } + +bool +BaselineCompiler::emitTraceLoggerResume(Register baselineScript, AllocatableGeneralRegisterSet& regs) +{ + Register scriptId = regs.takeAny(); + Register loggerReg = regs.takeAny(); + + Label noTraceLogger; + if (!traceLoggerToggleOffsets_.append(masm.toggledJump(&noTraceLogger))) + return false; + + TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); + masm.movePtr(ImmPtr(logger), loggerReg); + + Address scriptEvent(baselineScript, BaselineScript::offsetOfTraceLoggerScriptEvent()); + masm.computeEffectiveAddress(scriptEvent, scriptId); + masm.tracelogStartEvent(loggerReg, scriptId); + masm.tracelogStartId(loggerReg, TraceLogger_Baseline, /* force = */ true); + + regs.add(loggerReg); + regs.add(scriptId); + + masm.bind(&noTraceLogger); + + return true; +} #endif void @@ -4223,6 +4249,11 @@ BaselineCompiler::emit_JSOP_RESUME() masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1); masm.branchPtr(Assembler::BelowOrEqual, scratch1, ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret); +#ifdef JS_TRACE_LOGGING + if (!emitTraceLoggerResume(scratch1, regs)) + return false; +#endif + Register constructing = regs.takeAny(); ValueOperand newTarget = regs.takeAnyValue(); masm.loadValue(Address(genObj, GeneratorObject::offsetOfNewTargetSlot()), newTarget); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 1a057176c847..630e5e1a81f8 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -300,6 +300,7 @@ class BaselineCompiler : public BaselineCompilerSpecific MOZ_MUST_USE bool emitDebugTrap(); MOZ_MUST_USE bool emitTraceLoggerEnter(); MOZ_MUST_USE bool emitTraceLoggerExit(); + MOZ_MUST_USE bool emitTraceLoggerResume(Register script, AllocatableGeneralRegisterSet& regs); void emitProfilerEnterFrame(); void emitProfilerExitFrame(); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index cd5a29f973b8..df49bd72ea92 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -54,8 +54,6 @@ ICStubSpace::freeAllAfterMinorGC(JSRuntime* rt) BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, - uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset) : method_(nullptr), templateEnv_(nullptr), @@ -70,8 +68,6 @@ BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, traceLoggerScriptsEnabled_(false), traceLoggerEngineEnabled_(false), # endif - traceLoggerEnterToggleOffset_(traceLoggerEnterToggleOffset), - traceLoggerExitToggleOffset_(traceLoggerExitToggleOffset), traceLoggerScriptEvent_(), #endif postDebugPrologueOffset_(postDebugPrologueOffset), @@ -404,13 +400,12 @@ BaselineScript::New(JSScript* jsscript, uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, - uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset, size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, size_t bytecodeTypeMapEntries, - size_t yieldEntries) + size_t yieldEntries, + size_t traceLoggerToggleOffsetEntries) { static const unsigned DataAlignment = sizeof(uintptr_t); @@ -418,25 +413,27 @@ BaselineScript::New(JSScript* jsscript, size_t pcMappingIndexEntriesSize = pcMappingIndexEntries * sizeof(PCMappingIndexEntry); size_t bytecodeTypeMapSize = bytecodeTypeMapEntries * sizeof(uint32_t); size_t yieldEntriesSize = yieldEntries * sizeof(uintptr_t); + size_t tlEntriesSize = traceLoggerToggleOffsetEntries * sizeof(uint32_t); size_t paddedICEntriesSize = AlignBytes(icEntriesSize, DataAlignment); size_t paddedPCMappingIndexEntriesSize = AlignBytes(pcMappingIndexEntriesSize, DataAlignment); size_t paddedPCMappingSize = AlignBytes(pcMappingSize, DataAlignment); size_t paddedBytecodeTypesMapSize = AlignBytes(bytecodeTypeMapSize, DataAlignment); size_t paddedYieldEntriesSize = AlignBytes(yieldEntriesSize, DataAlignment); + size_t paddedTLEntriesSize = AlignBytes(tlEntriesSize, DataAlignment); size_t allocBytes = paddedICEntriesSize + paddedPCMappingIndexEntriesSize + paddedPCMappingSize + paddedBytecodeTypesMapSize + - paddedYieldEntriesSize; + paddedYieldEntriesSize + + paddedTLEntriesSize; BaselineScript* script = jsscript->zone()->pod_malloc_with_extra(allocBytes); if (!script) return nullptr; new (script) BaselineScript(prologueOffset, epilogueOffset, profilerEnterToggleOffset, profilerExitToggleOffset, - traceLoggerEnterToggleOffset, traceLoggerExitToggleOffset, postDebugPrologueOffset); size_t offsetCursor = sizeof(BaselineScript); @@ -460,6 +457,10 @@ BaselineScript::New(JSScript* jsscript, script->yieldEntriesOffset_ = yieldEntries ? offsetCursor : 0; offsetCursor += paddedYieldEntriesSize; + script->traceLoggerToggleOffsetsOffset_ = tlEntriesSize ? offsetCursor : 0; + script->numTraceLoggerToggleOffsets_ = traceLoggerToggleOffsetEntries; + offsetCursor += paddedTLEntriesSize; + MOZ_ASSERT(offsetCursor == sizeof(BaselineScript) + allocBytes); return script; } @@ -968,7 +969,8 @@ BaselineScript::toggleDebugTraps(JSScript* script, jsbytecode* pc) #ifdef JS_TRACE_LOGGING void -BaselineScript::initTraceLogger(JSRuntime* runtime, JSScript* script) +BaselineScript::initTraceLogger(JSRuntime* runtime, JSScript* script, + const Vector& offsets) { #ifdef DEBUG traceLoggerScriptsEnabled_ = TraceLogTextIdEnabled(TraceLogger_Scripts); @@ -978,11 +980,15 @@ BaselineScript::initTraceLogger(JSRuntime* runtime, JSScript* script) TraceLoggerThread* logger = TraceLoggerForMainThread(runtime); traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script); + MOZ_ASSERT(offsets.length() == numTraceLoggerToggleOffsets_); + for (size_t i = 0; i < offsets.length(); i++) + traceLoggerToggleOffsets()[i] = offsets[i].offset(); + if (TraceLogTextIdEnabled(TraceLogger_Engine) || TraceLogTextIdEnabled(TraceLogger_Scripts)) { - CodeLocationLabel enter(method_, CodeOffset(traceLoggerEnterToggleOffset_)); - CodeLocationLabel exit(method_, CodeOffset(traceLoggerExitToggleOffset_)); - Assembler::ToggleToCmp(enter); - Assembler::ToggleToCmp(exit); + for (size_t i = 0; i < numTraceLoggerToggleOffsets_; i++) { + CodeLocationLabel label(method_, CodeOffset(traceLoggerToggleOffsets()[i])); + Assembler::ToggleToCmp(label); + } } } @@ -1004,17 +1010,13 @@ BaselineScript::toggleTraceLoggerScripts(JSRuntime* runtime, JSScript* script, b AutoWritableJitCode awjc(method()); - // Enable/Disable the traceLogger prologue and epilogue. - CodeLocationLabel enter(method_, CodeOffset(traceLoggerEnterToggleOffset_)); - CodeLocationLabel exit(method_, CodeOffset(traceLoggerExitToggleOffset_)); - if (!engineEnabled) { - if (enable) { - Assembler::ToggleToCmp(enter); - Assembler::ToggleToCmp(exit); - } else { - Assembler::ToggleToJmp(enter); - Assembler::ToggleToJmp(exit); - } + // Enable/Disable the traceLogger. + for (size_t i = 0; i < numTraceLoggerToggleOffsets_; i++) { + CodeLocationLabel label(method_, CodeOffset(traceLoggerToggleOffsets()[i])); + if (enable) + Assembler::ToggleToCmp(label); + else + Assembler::ToggleToJmp(label); } #if DEBUG @@ -1033,16 +1035,12 @@ BaselineScript::toggleTraceLoggerEngine(bool enable) AutoWritableJitCode awjc(method()); // Enable/Disable the traceLogger prologue and epilogue. - CodeLocationLabel enter(method_, CodeOffset(traceLoggerEnterToggleOffset_)); - CodeLocationLabel exit(method_, CodeOffset(traceLoggerExitToggleOffset_)); - if (!scriptsEnabled) { - if (enable) { - Assembler::ToggleToCmp(enter); - Assembler::ToggleToCmp(exit); - } else { - Assembler::ToggleToJmp(enter); - Assembler::ToggleToJmp(exit); - } + for (size_t i = 0; i < numTraceLoggerToggleOffsets_; i++) { + CodeLocationLabel label(method_, CodeOffset(traceLoggerToggleOffsets()[i])); + if (enable) + Assembler::ToggleToCmp(label); + else + Assembler::ToggleToJmp(label); } #if DEBUG diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 7b03ad4d291a..81d595e10b21 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -158,8 +158,6 @@ struct BaselineScript bool traceLoggerScriptsEnabled_; bool traceLoggerEngineEnabled_; # endif - uint32_t traceLoggerEnterToggleOffset_; - uint32_t traceLoggerExitToggleOffset_; TraceLoggerEvent traceLoggerScriptEvent_; #endif @@ -221,6 +219,11 @@ struct BaselineScript // instruction. uint32_t yieldEntriesOffset_; + // By default tracelogger is disabled. Therefore we disable the logging code + // by default. We store the offsets we must patch to enable the logging. + uint32_t traceLoggerToggleOffsetsOffset_; + uint32_t numTraceLoggerToggleOffsets_; + // The total bytecode length of all scripts we inlined when we Ion-compiled // this script. 0 if Ion did not compile this script or if we didn't inline // anything. @@ -241,8 +244,6 @@ struct BaselineScript BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, - uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset); ~BaselineScript() { @@ -255,13 +256,12 @@ struct BaselineScript uint32_t prologueOffset, uint32_t epilogueOffset, uint32_t profilerEnterToggleOffset, uint32_t profilerExitToggleOffset, - uint32_t traceLoggerEnterToggleOffset, - uint32_t traceLoggerExitToggleOffset, uint32_t postDebugPrologueOffset, size_t icEntries, size_t pcMappingIndexEntries, size_t pcMappingSize, size_t bytecodeTypeMapEntries, - size_t yieldEntries); + size_t yieldEntries, + size_t traceLoggerToggleOffsetEntries); static void Trace(JSTracer* trc, BaselineScript* script); static void Destroy(FreeOp* fop, BaselineScript* script); @@ -433,13 +433,19 @@ struct BaselineScript } #ifdef JS_TRACE_LOGGING - void initTraceLogger(JSRuntime* runtime, JSScript* script); + void initTraceLogger(JSRuntime* runtime, JSScript* script, const Vector& offsets); void toggleTraceLoggerScripts(JSRuntime* runtime, JSScript* script, bool enable); void toggleTraceLoggerEngine(bool enable); static size_t offsetOfTraceLoggerScriptEvent() { return offsetof(BaselineScript, traceLoggerScriptEvent_); } + + uint32_t* traceLoggerToggleOffsets() { + MOZ_ASSERT(traceLoggerToggleOffsetsOffset_); + return reinterpret_cast(reinterpret_cast(this) + + traceLoggerToggleOffsetsOffset_); + } #endif void noteAccessedGetter(uint32_t pcOffset); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index c21c86eda88e..054750d83b8e 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -770,6 +770,11 @@ FinalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* MOZ_ASSERT(*pc == JSOP_FINALYIELDRVAL); if (!GeneratorObject::finalSuspend(cx, obj)) { + + TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); + TraceLogStopEvent(logger, TraceLogger_Engine); + TraceLogStopEvent(logger, TraceLogger_Scripts); + // Leave this frame and propagate the exception to the caller. return DebugEpilogue(cx, frame, pc, /* ok = */ false); } diff --git a/js/src/jit/shared/BaselineCompiler-shared.cpp b/js/src/jit/shared/BaselineCompiler-shared.cpp index b52777b6131f..5342eeb3f08f 100644 --- a/js/src/jit/shared/BaselineCompiler-shared.cpp +++ b/js/src/jit/shared/BaselineCompiler-shared.cpp @@ -36,8 +36,7 @@ BaselineCompilerShared::BaselineCompilerShared(JSContext* cx, TempAllocator& all spsPushToggleOffset_(), profilerEnterFrameToggleOffset_(), profilerExitFrameToggleOffset_(), - traceLoggerEnterToggleOffset_(), - traceLoggerExitToggleOffset_(), + traceLoggerToggleOffsets_(cx), traceLoggerScriptTextIdOffset_() { } diff --git a/js/src/jit/shared/BaselineCompiler-shared.h b/js/src/jit/shared/BaselineCompiler-shared.h index 6094367f193a..56f9166cf214 100644 --- a/js/src/jit/shared/BaselineCompiler-shared.h +++ b/js/src/jit/shared/BaselineCompiler-shared.h @@ -65,8 +65,8 @@ class BaselineCompilerShared CodeOffset spsPushToggleOffset_; CodeOffset profilerEnterFrameToggleOffset_; CodeOffset profilerExitFrameToggleOffset_; - CodeOffset traceLoggerEnterToggleOffset_; - CodeOffset traceLoggerExitToggleOffset_; + + Vector traceLoggerToggleOffsets_; CodeOffset traceLoggerScriptTextIdOffset_; BaselineCompilerShared(JSContext* cx, TempAllocator& alloc, JSScript* script); From 67f478581c6191fcfa0dbbef49ce617b66547a3f Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:27 +0200 Subject: [PATCH 011/102] Bug 1299839: TraceLogger - Disable on throwing an exception in IonMonkey with Debugger enabled, r=bbouvier --- js/src/jit/JitFrames.cpp | 9 +++++++++ js/src/vm/TraceLogging.cpp | 32 +++++++++++++++++++------------- js/src/vm/TraceLogging.h | 17 ++++++++++++----- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp index 114fffe6173e..5c6a3e80b2eb 100644 --- a/js/src/jit/JitFrames.cpp +++ b/js/src/jit/JitFrames.cpp @@ -787,6 +787,15 @@ HandleException(ResumeFromException* rfe) IonScript* ionScript = nullptr; bool invalidated = iter.checkInvalidation(&ionScript); +#ifdef JS_TRACE_LOGGING + if (logger && cx->compartment()->isDebuggee() && logger->enabled()) { + logger->disable(/* force = */ true, + "Forcefully disabled tracelogger, due to " + "throwing an exception with an active Debugger " + "in IonMonkey."); + } +#endif + for (;;) { HandleExceptionIon(cx, frames, rfe, &overrecursed); diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index a8a0a4a23d7b..962d688aa74d 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -176,15 +176,15 @@ TraceLoggerThread::~TraceLoggerThread() bool TraceLoggerThread::enable() { - if (enabled > 0) { - enabled++; + if (enabled_ > 0) { + enabled_++; return true; } if (failed) return false; - enabled = 1; + enabled_ = 1; logTimestamp(TraceLogger_Enable); return true; @@ -195,7 +195,7 @@ TraceLoggerThread::fail(JSContext* cx, const char* error) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TRACELOGGER_ENABLE_FAIL, error); failed = true; - enabled = 0; + enabled_ = 0; return false; } @@ -206,7 +206,7 @@ TraceLoggerThread::enable(JSContext* cx) if (!enable()) return fail(cx, "internal error"); - if (enabled == 1) { + if (enabled_ == 1) { // Get the top Activation to log the top script/pc (No inlined frames). ActivationIterator iter(cx->runtime()); Activation* act = iter.activation(); @@ -252,21 +252,24 @@ TraceLoggerThread::enable(JSContext* cx) } bool -TraceLoggerThread::disable() +TraceLoggerThread::disable(bool force, const char* error) { if (failed) return false; - if (enabled == 0) + if (enabled_ == 0) return true; - if (enabled > 1) { - enabled--; + if (enabled_ > 1 && !force) { + enabled_--; return true; } + if (force) + traceLoggerState->maybeSpewError(error); + logTimestamp(TraceLogger_Disable); - enabled = 0; + enabled_ = 0; return true; } @@ -492,7 +495,7 @@ TraceLoggerThread::startEvent(uint32_t id) return; #ifdef DEBUG - if (enabled > 0) { + if (enabled_ > 0) { AutoEnterOOMUnsafeRegion oomUnsafe; if (!graphStack.append(id)) oomUnsafe.crash("Could not add item to debug stack."); @@ -525,7 +528,7 @@ TraceLoggerThread::stopEvent(uint32_t id) return; #ifdef DEBUG - if (enabled > 0) { + if (enabled_ > 0) { uint32_t prev = graphStack.popCopy(); if (id == TraceLogger_Engine) { MOZ_ASSERT(prev == TraceLogger_IonMonkey || prev == TraceLogger_Baseline || @@ -560,7 +563,7 @@ TraceLoggerThread::logTimestamp(uint32_t id) void TraceLoggerThread::log(uint32_t id) { - if (enabled == 0) + if (enabled_ == 0) return; MOZ_ASSERT(traceLoggerState); @@ -784,6 +787,7 @@ TraceLoggerThreadState::init() " EnableMainThread Start logging the main thread immediately.\n" " EnableOffThread Start logging helper threads immediately.\n" " EnableGraph Enable spewing the tracelogging graph to a file.\n" + " Errors Report errors during tracing to stderr.\n" ); printf("\n"); exit(0); @@ -796,6 +800,8 @@ TraceLoggerThreadState::init() offThreadEnabled = true; if (strstr(options, "EnableGraph")) graphSpewingEnabled = true; + if (strstr(options, "Errors")) + spewErrors = true; } startupTime = rdtsc(); diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 1edafa85d5e3..b16412c80d56 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -167,7 +167,7 @@ class TraceLoggerThread DefaultHasher, SystemAllocPolicy> TextIdHashMap; - uint32_t enabled; + uint32_t enabled_; bool failed; UniquePtr graph; @@ -192,7 +192,7 @@ class TraceLoggerThread AutoTraceLog* top; TraceLoggerThread() - : enabled(0), + : enabled_(0), failed(false), graph(), nextTextId(TraceLogger_Last), @@ -208,7 +208,8 @@ class TraceLoggerThread bool enable(); bool enable(JSContext* cx); - bool disable(); + bool disable(bool force = false, const char* = ""); + bool enabled() { return enabled_ > 0; } private: bool fail(JSContext* cx, const char* error); @@ -296,7 +297,7 @@ class TraceLoggerThread public: static unsigned offsetOfEnabled() { - return offsetof(TraceLoggerThread, enabled); + return offsetof(TraceLoggerThread, enabled_); } #endif }; @@ -318,6 +319,7 @@ class TraceLoggerThreadState bool mainThreadEnabled; bool offThreadEnabled; bool graphSpewingEnabled; + bool spewErrors; ThreadLoggerHashMap threadLoggers; MainThreadLoggers mainThreadLoggers; @@ -332,7 +334,8 @@ class TraceLoggerThreadState #endif mainThreadEnabled(false), offThreadEnabled(false), - graphSpewingEnabled(false) + graphSpewingEnabled(false), + spewErrors(false) { } bool init(); @@ -349,6 +352,10 @@ class TraceLoggerThreadState } void enableTextId(JSContext* cx, uint32_t textId); void disableTextId(JSContext* cx, uint32_t textId); + void maybeSpewError(const char* text) { + if (spewErrors) + fprintf(stderr, "%s\n", text); + } private: TraceLoggerThread* forMainThread(PerThreadData* mainThread); From 901986bdeecf865fa783321323cd5ad65f742c85 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:19:28 +0200 Subject: [PATCH 012/102] Bug 1299841: TraceLogger - Fail gracefully on OOM, r=bbouvier --- js/src/jit/BaselineJIT.cpp | 6 ++---- js/src/vm/TraceLogging.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index df49bd72ea92..9f553d35695e 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -978,13 +978,13 @@ BaselineScript::initTraceLogger(JSRuntime* runtime, JSScript* script, #endif TraceLoggerThread* logger = TraceLoggerForMainThread(runtime); - traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script); MOZ_ASSERT(offsets.length() == numTraceLoggerToggleOffsets_); for (size_t i = 0; i < offsets.length(); i++) traceLoggerToggleOffsets()[i] = offsets[i].offset(); if (TraceLogTextIdEnabled(TraceLogger_Engine) || TraceLogTextIdEnabled(TraceLogger_Scripts)) { + traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script); for (size_t i = 0; i < numTraceLoggerToggleOffsets_; i++) { CodeLocationLabel label(method_, CodeOffset(traceLoggerToggleOffsets()[i])); Assembler::ToggleToCmp(label); @@ -1003,10 +1003,8 @@ BaselineScript::toggleTraceLoggerScripts(JSRuntime* runtime, JSScript* script, b // Patch the logging script textId to be correct. // When logging log the specific textId else the global Scripts textId. TraceLoggerThread* logger = TraceLoggerForMainThread(runtime); - if (enable) + if (enable && !traceLoggerScriptEvent_.hasPayload()) traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts, script); - else - traceLoggerScriptEvent_ = TraceLoggerEvent(logger, TraceLogger_Scripts); AutoWritableJitCode awjc(method()); diff --git a/js/src/vm/TraceLogging.cpp b/js/src/vm/TraceLogging.cpp index 962d688aa74d..23728efd091b 100644 --- a/js/src/vm/TraceLogging.cpp +++ b/js/src/vm/TraceLogging.cpp @@ -480,7 +480,12 @@ TraceLoggerThread::startEvent(TraceLoggerTextId id) { void TraceLoggerThread::startEvent(const TraceLoggerEvent& event) { if (!event.hasPayload()) { + if (!enabled()) + return; startEvent(TraceLogger_Error); + disable(/* force = */ true, "TraceLogger encountered an empty event. " + "Potentially due to OOM during creation of " + "this event. Disabling TraceLogger."); return; } startEvent(event.payload()->textId()); @@ -776,6 +781,8 @@ TraceLoggerThreadState::init() enabledTextIds[TraceLogger_Baseline] = enabledTextIds[TraceLogger_Engine]; enabledTextIds[TraceLogger_IonMonkey] = enabledTextIds[TraceLogger_Engine]; + enabledTextIds[TraceLogger_Error] = true; + const char* options = getenv("TLOPTIONS"); if (options) { if (strstr(options, "help")) { From b2447f075e5f6056a6f412f813cf67bea8712d2a Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Fri, 2 Sep 2016 18:39:50 +0200 Subject: [PATCH 013/102] Bug 1299321 - Implement string values for JSPropertySpec. r=Waldo --- js/src/jsapi.cpp | 32 +++++++++++----- js/src/jsapi.h | 92 ++++++++++++++++++++++++++++----------------- js/src/shell/js.cpp | 6 ++- 3 files changed, 85 insertions(+), 45 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index e144fd521321..8b71d1c9df05 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3138,17 +3138,31 @@ JS_DefineProperties(JSContext* cx, HandleObject obj, const JSPropertySpec* ps) if (!PropertySpecNameToId(cx, ps->name, &id)) return false; - if (ps->isSelfHosted()) { - if (!DefineSelfHostedProperty(cx, obj, id, - ps->getter.selfHosted.funname, - ps->setter.selfHosted.funname, - ps->flags, 0)) - { - return false; + if (ps->isAccessor()) { + if (ps->isSelfHosted()) { + if (!DefineSelfHostedProperty(cx, obj, id, + ps->accessors.getter.selfHosted.funname, + ps->accessors.setter.selfHosted.funname, + ps->flags, 0)) + { + return false; + } + } else { + if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, + ps->accessors.getter.native, ps->accessors.setter.native, + ps->flags, 0)) + { + return false; + } } } else { - if (!DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue, - ps->getter.native, ps->setter.native, ps->flags, 0)) + RootedAtom atom(cx, Atomize(cx, ps->string.value, strlen(ps->string.value))); + if (!atom) + return false; + + RootedValue v(cx, StringValue(atom)); + if (!DefinePropertyById(cx, obj, id, v, NativeOpWrapper(nullptr), + NativeOpWrapper(nullptr), ps->flags & ~JSPROP_INTERNAL_USE_BIT, 0)) { return false; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 2d88b98cead8..9c2a633b8fb3 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -843,7 +843,6 @@ class MOZ_STACK_CLASS SourceBufferHolder final object that delegates to a prototype containing this property */ #define JSPROP_INTERNAL_USE_BIT 0x80 /* internal JS engine use only */ -// 0x100 /* Unused */ #define JSFUN_STUB_GSOPS 0x200 /* use JS_PropertyStub getter/setter instead of defaulting to class gsops for property holding function */ @@ -1870,22 +1869,38 @@ typedef struct JSNativeWrapper { */ struct JSPropertySpec { struct SelfHostedWrapper { - void* unused; + void* unused; const char* funname; }; + struct StringValueWrapper { + void* unused; + const char* value; + }; + const char* name; uint8_t flags; union { - JSNativeWrapper native; - SelfHostedWrapper selfHosted; - } getter; - union { - JSNativeWrapper native; - SelfHostedWrapper selfHosted; - } setter; + struct { + union { + JSNativeWrapper native; + SelfHostedWrapper selfHosted; + } getter; + union { + JSNativeWrapper native; + SelfHostedWrapper selfHosted; + } setter; + } accessors; + StringValueWrapper string; + }; + + bool isAccessor() const { + return !(flags & JSPROP_INTERNAL_USE_BIT); + } bool isSelfHosted() const { + MOZ_ASSERT(isAccessor()); + #ifdef DEBUG // Verify that our accessors match our JSPROP_GETTER flag. if (flags & JSPROP_GETTER) @@ -1904,17 +1919,17 @@ struct JSPropertySpec { "JSNativeWrapper::info"); private: void checkAccessorsAreNative() const { - MOZ_ASSERT(getter.native.op); + MOZ_ASSERT(accessors.getter.native.op); // We may not have a setter at all. So all we can assert here, for the // native case is that if we have a jitinfo for the setter then we have // a setter op too. This is good enough to make sure we don't have a // SelfHostedWrapper for the setter. - MOZ_ASSERT_IF(setter.native.info, setter.native.op); + MOZ_ASSERT_IF(accessors.setter.native.info, accessors.setter.native.op); } void checkAccessorsAreSelfHosted() const { - MOZ_ASSERT(!getter.selfHosted.unused); - MOZ_ASSERT(!setter.selfHosted.unused); + MOZ_ASSERT(!accessors.getter.selfHosted.unused); + MOZ_ASSERT(!accessors.setter.selfHosted.unused); } }; @@ -1963,37 +1978,46 @@ inline int CheckIsSetterOp(JSSetterOp op); #define JS_STUBSETTER JS_PROPERTYOP_SETTER(JS_StrictPropertyStub) +#define JS_PS_ACCESSOR_SPEC(name, getter, setter, flags, extraFlags) \ + { name, uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | extraFlags), \ + { { getter, setter } } } +#define JS_PS_STRINGVALUE_SPEC(name, value, flags) \ + { name, uint8_t(flags | JSPROP_INTERNAL_USE_BIT), \ + { { STRINGVALUE_WRAPPER(value), JSNATIVE_WRAPPER(nullptr) } } } + +#define SELFHOSTED_WRAPPER(name) \ + { { nullptr, JS_CAST_STRING_TO(name, const JSJitInfo*) } } +#define STRINGVALUE_WRAPPER(value) \ + { { nullptr, JS_CAST_STRING_TO(value, const JSJitInfo*) } } + /* * JSPropertySpec uses JSNativeWrapper. These macros encapsulate the definition * of JSNative-backed JSPropertySpecs, by defining the JSNativeWrappers for * them. */ #define JS_PSG(name, getter, flags) \ - {name, \ - uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED), \ - JSNATIVE_WRAPPER(getter), \ - JSNATIVE_WRAPPER(nullptr)} + JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, \ + JSPROP_SHARED) #define JS_PSGS(name, getter, setter, flags) \ - {name, \ - uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED), \ - JSNATIVE_WRAPPER(getter), \ - JSNATIVE_WRAPPER(setter)} + JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(setter), flags, \ + JSPROP_SHARED) #define JS_SELF_HOSTED_GET(name, getterName, flags) \ - {name, \ - uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_GETTER), \ - { { nullptr, JS_CAST_STRING_TO(getterName, const JSJitInfo*) } }, \ - JSNATIVE_WRAPPER(nullptr) } + JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags, \ + JSPROP_SHARED | JSPROP_GETTER) #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \ - {name, \ - uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER), \ - { nullptr, JS_CAST_STRING_TO(getterName, const JSJitInfo*) }, \ - { nullptr, JS_CAST_STRING_TO(setterName, const JSJitInfo*) } } -#define JS_PS_END { nullptr, 0, JSNATIVE_WRAPPER(nullptr), JSNATIVE_WRAPPER(nullptr) } + JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), SELFHOSTED_WRAPPER(setterName), \ + flags, JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER) #define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags) \ - {reinterpret_cast(uint32_t(::JS::SymbolCode::symbol) + 1), \ - uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | JSPROP_SHARED | JSPROP_GETTER), \ - { { nullptr, JS_CAST_STRING_TO(getterName, const JSJitInfo*) } }, \ - JSNATIVE_WRAPPER(nullptr) } + JS_PS_ACCESSOR_SPEC(reinterpret_cast(uint32_t(::JS::SymbolCode::symbol) + 1), \ + SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags, \ + JSPROP_SHARED | JSPROP_GETTER) +#define JS_STRING_PS(name, string, flags) \ + JS_PS_STRINGVALUE_SPEC(name, string, flags) +#define JS_STRING_SYM_PS(symbol, string, flags) \ + JS_PS_STRINGVALUE_SPEC(reinterpret_cast(uint32_t(::JS::SymbolCode::symbol) + 1), \ + string, flags) +#define JS_PS_END \ + JS_PS_ACCESSOR_SPEC(nullptr, JSNATIVE_WRAPPER(nullptr), JSNATIVE_WRAPPER(nullptr), 0, 0) /** * To define a native function, set call to a JSNativeWrapper. To define a diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 378610daad5c..9d24295a499f 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -6360,8 +6360,10 @@ static const JSJitInfo doFoo_methodinfo = { static const JSPropertySpec dom_props[] = { {"x", JSPROP_SHARED | JSPROP_ENUMERATE, - { { dom_genericGetter, &dom_x_getterinfo } }, - { { dom_genericSetter, &dom_x_setterinfo } } + { { + { { dom_genericGetter, &dom_x_getterinfo } }, + { { dom_genericSetter, &dom_x_setterinfo } } + } }, }, JS_PS_END }; From 55a65363a9ba7f9ed938197c3bd36b887ef675cb Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Fri, 2 Sep 2016 18:39:50 +0200 Subject: [PATCH 014/102] Bug 1299321 - Make DOM/Xray handle string values in JSPropertySpec. r=peterv --- dom/bindings/BindingUtils.cpp | 6 ++-- dom/bindings/Codegen.py | 2 +- js/xpconnect/wrappers/XrayWrapper.cpp | 40 ++++++++++++++++----------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 57028d0c8d44..0201f871b7fe 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -1292,15 +1292,15 @@ XrayResolveAttribute(JSContext* cx, JS::Handle wrapper, desc.setAttributes(attrSpec.flags); // They all have getters, so we can just make it. JS::Rooted funobj(cx, - XrayCreateFunction(cx, wrapper, attrSpec.getter.native, 0, id)); + XrayCreateFunction(cx, wrapper, attrSpec.accessors.getter.native, 0, id)); if (!funobj) return false; desc.setGetterObject(funobj); desc.attributesRef() |= JSPROP_GETTER; - if (attrSpec.setter.native.op) { + if (attrSpec.accessors.setter.native.op) { // We have a setter! Make it. funobj = - XrayCreateFunction(cx, wrapper, attrSpec.setter.native, 1, id); + XrayCreateFunction(cx, wrapper, attrSpec.accessors.setter.native, 1, id); if (!funobj) return false; desc.setSetterObject(funobj); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index fca423ba3123..513f4bc1eadc 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2527,7 +2527,7 @@ class AttrDefiner(PropertyDefiner): return self.generatePrefableArray( array, name, - lambda fields: ' { "%s", %s, %s, %s}' % fields, + lambda fields: ' { "%s", %s, { { %s, %s } } }' % fields, ' JS_PS_END', 'JSPropertySpec', PropertyDefiner.getControllingCondition, specData, doIdArrays) diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 8fc4227630a5..4c0fedbe5b4d 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -424,25 +424,33 @@ TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder, RootedFunction getterObj(cx); RootedFunction setterObj(cx); unsigned flags = psMatch->flags; - if (psMatch->isSelfHosted()) { - getterObj = JS::GetSelfHostedFunction(cx, psMatch->getter.selfHosted.funname, id, 0); - if (!getterObj) - return false; - desc.setGetterObject(JS_GetFunctionObject(getterObj)); - if (psMatch->setter.selfHosted.funname) { - MOZ_ASSERT(flags & JSPROP_SETTER); - setterObj = JS::GetSelfHostedFunction(cx, psMatch->setter.selfHosted.funname, id, 0); - if (!setterObj) + if (psMatch->isAccessor()) { + if (psMatch->isSelfHosted()) { + getterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.getter.selfHosted.funname, id, 0); + if (!getterObj) return false; - desc.setSetterObject(JS_GetFunctionObject(setterObj)); + desc.setGetterObject(JS_GetFunctionObject(getterObj)); + if (psMatch->accessors.setter.selfHosted.funname) { + MOZ_ASSERT(flags & JSPROP_SETTER); + setterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.setter.selfHosted.funname, id, 0); + if (!setterObj) + return false; + desc.setSetterObject(JS_GetFunctionObject(setterObj)); + } + } else { + desc.setGetter(JS_CAST_NATIVE_TO(psMatch->accessors.getter.native.op, + JSGetterOp)); + desc.setSetter(JS_CAST_NATIVE_TO(psMatch->accessors.setter.native.op, + JSSetterOp)); } + desc.setAttributes(flags); } else { - desc.setGetter(JS_CAST_NATIVE_TO(psMatch->getter.native.op, - JSGetterOp)); - desc.setSetter(JS_CAST_NATIVE_TO(psMatch->setter.native.op, - JSSetterOp)); + RootedString atom(cx, JS_AtomizeString(cx, psMatch->string.value)); + if (!atom) + return false; + desc.value().setString(atom); + desc.setAttributes(flags & ~JSPROP_INTERNAL_USE_BIT); } - desc.setAttributes(flags); // The generic Xray machinery only defines non-own properties on the holder. // This is broken, and will be fixed at some point, but for now we need to @@ -453,7 +461,7 @@ TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder, // pass along JITInfo. It's probably ok though, since Xrays are already // pretty slow. return JS_DefinePropertyById(cx, holder, id, - UndefinedHandleValue, + desc.value(), // This particular descriptor, unlike most, // actually stores JSNatives directly, // since we just set it up. Do NOT pass From 0cbb71f632643b306724e7840f38d0c9d290a785 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Fri, 2 Sep 2016 18:39:50 +0200 Subject: [PATCH 015/102] Bug 1299321 - Add @@toStringTag to Promise. r=till --- js/src/builtin/Promise.cpp | 8 +++++++- js/src/tests/ecma_6/Symbol/toStringTag.js | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index a1066295508c..ffaef583299d 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -1397,6 +1397,11 @@ static const JSFunctionSpec promise_methods[] = { JS_FS_END }; +static const JSPropertySpec promise_properties[] = { + JS_STRING_SYM_PS(toStringTag, "Promise", JSPROP_READONLY), + JS_PS_END +}; + static const JSFunctionSpec promise_static_methods[] = { JS_SELF_HOSTED_FN("all", "Promise_static_all", 1, 0), JS_SELF_HOSTED_FN("race", "Promise_static_race", 1, 0), @@ -1415,7 +1420,8 @@ static const ClassSpec PromiseObjectClassSpec = { CreatePromisePrototype, promise_static_methods, promise_static_properties, - promise_methods + promise_methods, + promise_properties }; const Class PromiseObject::class_ = { diff --git a/js/src/tests/ecma_6/Symbol/toStringTag.js b/js/src/tests/ecma_6/Symbol/toStringTag.js index 7be04e298ff5..9ff0d7fefa81 100644 --- a/js/src/tests/ecma_6/Symbol/toStringTag.js +++ b/js/src/tests/ecma_6/Symbol/toStringTag.js @@ -147,7 +147,6 @@ testDefault(function* () {}.constructor.prototype, "GeneratorFunction"); testDefault(function* () {}().__proto__.__proto__, "Generator"); // ES6 25.4.5.4 Promise.prototype [ @@toStringTag ] -// testDefault(Promise.prototype, "Promise"); -// Promise is not yet implemented. +testDefault(Promise.prototype, "Promise"); reportCompare(true, true); From b84fb2a9193b1810a2b8dbbed646ec3abadf9c48 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Fri, 2 Sep 2016 18:39:50 +0200 Subject: [PATCH 016/102] Bug 1299321 - DOM test changes for Promise @@toStringTag. r=bz --- dom/promise/tests/test_promise_xrays.html | 10 ++++++++++ js/xpconnect/tests/chrome/test_xrayToJS.xul | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dom/promise/tests/test_promise_xrays.html b/dom/promise/tests/test_promise_xrays.html index 2fe52df3417a..f261ba0fbc58 100644 --- a/dom/promise/tests/test_promise_xrays.html +++ b/dom/promise/tests/test_promise_xrays.html @@ -288,6 +288,15 @@ function testCatch1() { ).then(nextTest); } +function testToStringTag1() { + is(win.Promise.prototype[Symbol.toStringTag], "Promise", "@@toStringTag was incorrect"); + var p = win.Promise.resolve(); + is(String(p), "[object Promise]", "String() result was incorrect"); + is(p.toString(), "[object Promise]", "toString result was incorrect"); + is(Object.prototype.toString.call(p), "[object Promise]", "second toString result was incorrect"); + nextTest(); +} + var tests = [ testLoadComplete, testHaveXray, @@ -309,6 +318,7 @@ var tests = [ testThen1, testThen2, testCatch1, + testToStringTag1, ]; function nextTest() { diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xul b/js/xpconnect/tests/chrome/test_xrayToJS.xul index 64c10f201835..efab4257069d 100644 --- a/js/xpconnect/tests/chrome/test_xrayToJS.xul +++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul @@ -243,7 +243,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681 "$`", "$'", Symbol.species]) gPrototypeProperties['Promise'] = - ["constructor", "catch", "then"]; + ["constructor", "catch", "then", Symbol.toStringTag]; gConstructorProperties['Promise'] = constructorProps(["resolve", "reject", "all", "race", Symbol.species]); From 3257d27f5ac18c2aad001fb09c7b0cfd38211d40 Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 2 Sep 2016 18:54:54 +0200 Subject: [PATCH 017/102] Bug 1299841: Fix bustage on opt, r=bustage ON CLOSED TREE --- js/src/jit/BaselineJIT.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 9f553d35695e..341a2cb9b884 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -7,6 +7,7 @@ #include "jit/BaselineJIT.h" #include "mozilla/BinarySearch.h" +#include "mozilla/DebugOnly.h" #include "mozilla/MemoryReporting.h" #include "asmjs/WasmInstance.h" @@ -28,6 +29,7 @@ #include "vm/Stack-inl.h" using mozilla::BinarySearchIf; +using mozilla::DebugOnly; using namespace js; using namespace js::jit; @@ -995,8 +997,7 @@ BaselineScript::initTraceLogger(JSRuntime* runtime, JSScript* script, void BaselineScript::toggleTraceLoggerScripts(JSRuntime* runtime, JSScript* script, bool enable) { - bool engineEnabled = TraceLogTextIdEnabled(TraceLogger_Engine); - + DebugOnly engineEnabled = TraceLogTextIdEnabled(TraceLogger_Engine); MOZ_ASSERT(enable == !traceLoggerScriptsEnabled_); MOZ_ASSERT(engineEnabled == traceLoggerEngineEnabled_); @@ -1025,8 +1026,7 @@ BaselineScript::toggleTraceLoggerScripts(JSRuntime* runtime, JSScript* script, b void BaselineScript::toggleTraceLoggerEngine(bool enable) { - bool scriptsEnabled = TraceLogTextIdEnabled(TraceLogger_Scripts); - + DebugOnly scriptsEnabled = TraceLogTextIdEnabled(TraceLogger_Scripts); MOZ_ASSERT(enable == !traceLoggerEngineEnabled_); MOZ_ASSERT(scriptsEnabled == traceLoggerScriptsEnabled_); From a5114317a50a4c14640a1c304367a5c1e40d90ec Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Fri, 2 Sep 2016 10:29:07 -0700 Subject: [PATCH 018/102] Bug 1277595 - Add a --triggered-by=nightly flag to taskgraph, r=dustin MozReview-Commit-ID: 3ipPgeFTWFu --HG-- extra : rebase_source : 7908cf9138b924e32dd1e72fe2af43eba0366179 --- taskcluster/mach_commands.py | 6 ++++++ taskcluster/taskgraph/decision.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/taskcluster/mach_commands.py b/taskcluster/mach_commands.py index 2cb72ccf19ff..bdfa96a3a325 100644 --- a/taskcluster/mach_commands.py +++ b/taskcluster/mach_commands.py @@ -144,6 +144,12 @@ class MachCommands(MachCommandBase): @CommandArgument('--level', required=True, help='SCM level of this repository') + @CommandArgument('--triggered-by', + choices=['nightly', 'push'], + default='push', + help='Source of execution of the decision graph') + @CommandArgument('--target-tasks-method', + help='method for selecting the target tasks to generate') def taskgraph_decision(self, **options): """Run the decision task: generate a task graph and submit to TaskCluster. This is only meant to be called within decision tasks, diff --git a/taskcluster/taskgraph/decision.py b/taskcluster/taskgraph/decision.py index bf76daad9742..d39e4584943f 100644 --- a/taskcluster/taskgraph/decision.py +++ b/taskcluster/taskgraph/decision.py @@ -110,6 +110,7 @@ def get_decision_parameters(options): 'pushdate', 'owner', 'level', + 'triggered_by', 'target_tasks_method', ] if n in options} @@ -122,6 +123,10 @@ def get_decision_parameters(options): "for this project".format(project, __file__)) parameters.update(PER_PROJECT_PARAMETERS['default']) + # `target_tasks_method` has higher precedence than `project` parameters + if options.get('target_tasks_method'): + parameters['target_tasks_method'] = options['target_tasks_method'] + return Parameters(parameters) From 4d4662c7dcfe7c1a47ee28fc6f900d1e236cb7c2 Mon Sep 17 00:00:00 2001 From: "amiyaguchi@mozilla.com" Date: Thu, 1 Sep 2016 09:21:29 -0700 Subject: [PATCH 019/102] Bug 1277595 - Enable multil10n fennec builds on taskcluster, r=jlund MozReview-Commit-ID: B2A3Wg6DBlt --HG-- extra : rebase_source : fdf5f59f805a84a2d0572596bc807bf45cf9a4af extra : source : c1aa2a15b4eb6c7b3bb66488541931412b159702 --- .../mozharness/configs/taskcluster_nightly.py | 5 ++++ .../mozharness/mozilla/building/buildbase.py | 25 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 testing/mozharness/configs/taskcluster_nightly.py diff --git a/testing/mozharness/configs/taskcluster_nightly.py b/testing/mozharness/configs/taskcluster_nightly.py new file mode 100644 index 000000000000..6c4e4a754940 --- /dev/null +++ b/testing/mozharness/configs/taskcluster_nightly.py @@ -0,0 +1,5 @@ +config = { + 'nightly_build': True, + 'taskcluster_nightly': True, +} + diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index a257c3c7493f..dceb4d52384f 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -918,6 +918,14 @@ or run without that action (ie: --no-{action})" 'branch': self.branch } + # this prevents taskcluster from overwriting the target files with + # the multilocale files. Put everything from the en-US build in a + # separate folder. + if multiLocale and self.config.get('taskcluster_nightly'): + if 'UPLOAD_PATH' in mach_env: + mach_env['UPLOAD_PATH'] = os.path.join(mach_env['UPLOAD_PATH'], + 'en-US') + # _query_post_upload_cmd returns a list (a cmd list), for env sake here # let's make it a string if c.get('is_automation'): @@ -1635,17 +1643,28 @@ or run without that action (ie: --no-{action})" dirs = self.query_abs_dirs() base_work_dir = dirs['base_work_dir'] objdir = dirs['abs_obj_dir'] - branch = self.buildbot_config['properties']['branch'] + branch = self.branch # Some android versions share the same .json config - if # multi_locale_config_platform is set, use that the .json name; # otherwise, use the buildbot platform. + default_platform = self.buildbot_config['properties'].get('platform', + 'android') + multi_config_pf = self.config.get('multi_locale_config_platform', - self.buildbot_config['properties']['platform']) + default_platform) + + # The l10n script location differs on buildbot and taskcluster + if self.config.get('taskcluster_nightly'): + multil10n_path = \ + 'build/src/testing/mozharness/scripts/multil10n.py' + base_work_dir = os.path.join(base_work_dir, 'workspace') + else: + multil10n_path = '%s/scripts/scripts/multil10n.py' % base_work_dir, cmd = [ self.query_exe('python'), - '%s/scripts/scripts/multil10n.py' % base_work_dir, + multil10n_path, '--config-file', 'multi_locale/%s_%s.json' % (branch, multi_config_pf), '--config-file', From ef1fdc6d588a16ed6fb0b2c657b3e79a26a41768 Mon Sep 17 00:00:00 2001 From: "amiyaguchi@mozilla.com" Date: Thu, 1 Sep 2016 09:22:10 -0700 Subject: [PATCH 020/102] Bug 1277595 - Use mozilla-central config-file on try, r=jlund MozReview-Commit-ID: J9GQWV4krX --HG-- extra : rebase_source : a232062232426277a6643a52e0908c1ff5c92d49 extra : source : 42ab74605817379b68227dbaf4cc12614db8278d --- testing/mozharness/mozharness/mozilla/building/buildbase.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index dceb4d52384f..587d5efc7311 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -1645,6 +1645,12 @@ or run without that action (ie: --no-{action})" objdir = dirs['abs_obj_dir'] branch = self.branch + # Building a nightly with the try repository fails because a + # config-file does not exist for try. Default to mozilla-central + # settings (arbitrarily). + if branch == 'try': + branch = 'mozilla-central' + # Some android versions share the same .json config - if # multi_locale_config_platform is set, use that the .json name; # otherwise, use the buildbot platform. From 7168c88c0465fd345282a650d3b790caed8db40d Mon Sep 17 00:00:00 2001 From: "amiyaguchi@mozilla.com" Date: Thu, 1 Sep 2016 09:22:45 -0700 Subject: [PATCH 021/102] Bug 1277595 - Generate balrog properties as a taskcluster artifact, r=jlund MozReview-Commit-ID: Bzx5yfVfZcw --HG-- extra : rebase_source : e3f2144cac9bd2d8102645d6912edb209a161cc8 extra : source : d5d26aa0f369f9c34b45c30dc732dd6cf0653ffc --- .../mozharness/mozilla/building/buildbase.py | 9 +++++ .../mozharness/mozilla/updates/balrog.py | 39 +++++++++++-------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index 587d5efc7311..f75f8a923e51 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -2040,6 +2040,15 @@ or run without that action (ie: --no-{action})" # grab any props available from this or previous unclobbered runs self.generate_build_props(console_output=False, halt_on_failure=False) + + # generate balrog props as artifacts + if self.config.get('taskcluster_nightly'): + env = self.query_mach_build_env(multiLocale=False) + props_path = os.path.join(env["UPLOAD_PATH"], + 'balrog_props.json') + self.generate_balrog_props(props_path) + return + if not self.config.get("balrog_servers"): self.fatal("balrog_servers not set; skipping balrog submission.") return diff --git a/testing/mozharness/mozharness/mozilla/updates/balrog.py b/testing/mozharness/mozharness/mozilla/updates/balrog.py index f3d08d652b30..26253283cfd0 100644 --- a/testing/mozharness/mozharness/mozilla/updates/balrog.py +++ b/testing/mozharness/mozharness/mozilla/updates/balrog.py @@ -14,6 +14,27 @@ class BalrogMixin(object): else: raise KeyError("Couldn't find balrog username.") + def generate_balrog_props(self, props_path): + self.set_buildbot_property( + "hashType", self.config.get("hash_type", "sha512"), write_to_file=True + ) + + if self.buildbot_config and "properties" in self.buildbot_config: + buildbot_properties = self.buildbot_config["properties"].items() + else: + buildbot_properties = [] + + balrog_props = dict(properties=dict(chain( + buildbot_properties, + self.buildbot_properties.items(), + ))) + if self.config.get('balrog_platform'): + balrog_props["properties"]["platform"] = self.config['balrog_platform'] + if "branch" not in balrog_props["properties"]: + balrog_props["properties"]["branch"] = self.branch + + self.dump_config(props_path, balrog_props) + def submit_balrog_updates(self, release_type="nightly", product=None): c = self.config dirs = self.query_abs_dirs() @@ -31,25 +52,9 @@ class BalrogMixin(object): submitter_script = os.path.join( dirs["abs_tools_dir"], "scripts", "updates", "balrog-submitter.py" ) - self.set_buildbot_property( - "hashType", c.get("hash_type", "sha512"), write_to_file=True - ) - if self.buildbot_config and "properties" in self.buildbot_config: - buildbot_properties = self.buildbot_config["properties"].items() - else: - buildbot_properties = [] + self.generate_balrog_props(props_path) - balrog_props = dict(properties=dict(chain( - buildbot_properties, - self.buildbot_properties.items(), - ))) - if self.config.get('balrog_platform'): - balrog_props["properties"]["platform"] = self.config['balrog_platform'] - if "branch" not in balrog_props["properties"]: - balrog_props["properties"]["branch"] = self.query_branch() - - self.dump_config(props_path, balrog_props) cmd = [ self.query_exe("python"), submitter_script, From a829379d159567304b2a67a057c82b4cd5bf1733 Mon Sep 17 00:00:00 2001 From: "amiyaguchi@mozilla.com" Date: Thu, 1 Sep 2016 09:23:14 -0700 Subject: [PATCH 022/102] Bug 1277595 - Create initial nightly graph, r=dustin MozReview-Commit-ID: 2rZYddnGdvh --HG-- rename : taskcluster/ci/legacy/tasks/build.yml => taskcluster/ci/nightly-fennec/build.yml rename : taskcluster/ci/legacy/tasks/docker_build.yml => taskcluster/ci/nightly-fennec/docker_build.yml rename : taskcluster/ci/legacy/tasks/builds/mobile_base.yml => taskcluster/ci/nightly-fennec/mobile_base.yml rename : taskcluster/ci/legacy/routes.json => taskcluster/ci/nightly-fennec/routes.json extra : rebase_source : 4267c027ca71c1c69be1fa8b99c6437c082a2186 extra : source : 5809a56922edab81abacd5de166d462998faa0e7 --- .../android-api-15-nightly-build.yml | 66 ++++++++++ taskcluster/ci/nightly-fennec/build.yml | 46 +++++++ .../ci/nightly-fennec/docker_build.yml | 27 +++++ taskcluster/ci/nightly-fennec/kind.yml | 7 ++ taskcluster/ci/nightly-fennec/mobile_base.yml | 13 ++ taskcluster/ci/nightly-fennec/routes.json | 18 +++ taskcluster/ci/signing/kind.yml | 8 ++ taskcluster/ci/signing/signing.yml | 22 ++++ taskcluster/taskgraph/target_tasks.py | 10 ++ taskcluster/taskgraph/task/nightly_fennec.py | 113 ++++++++++++++++++ taskcluster/taskgraph/task/signing.py | 48 ++++++++ 11 files changed, 378 insertions(+) create mode 100644 taskcluster/ci/nightly-fennec/android-api-15-nightly-build.yml create mode 100644 taskcluster/ci/nightly-fennec/build.yml create mode 100644 taskcluster/ci/nightly-fennec/docker_build.yml create mode 100644 taskcluster/ci/nightly-fennec/kind.yml create mode 100644 taskcluster/ci/nightly-fennec/mobile_base.yml create mode 100644 taskcluster/ci/nightly-fennec/routes.json create mode 100644 taskcluster/ci/signing/kind.yml create mode 100644 taskcluster/ci/signing/signing.yml create mode 100644 taskcluster/taskgraph/task/nightly_fennec.py create mode 100644 taskcluster/taskgraph/task/signing.py diff --git a/taskcluster/ci/nightly-fennec/android-api-15-nightly-build.yml b/taskcluster/ci/nightly-fennec/android-api-15-nightly-build.yml new file mode 100644 index 000000000000..4e885cf3feae --- /dev/null +++ b/taskcluster/ci/nightly-fennec/android-api-15-nightly-build.yml @@ -0,0 +1,66 @@ +$inherits: + from: 'mobile_base.yml' + variables: + build_name: 'android' + build_type: 'opt' +task: + metadata: + name: '[TC] Android armv7 API 15+' + description: 'Android armv7 API 15+' + + workerType: android-api-15 + + routes: + - 'index.buildbot.branches.{{project}}.android-api-15' + - 'index.buildbot.revisions.{{head_rev}}.{{project}}.android-api-15' + + scopes: + - 'docker-worker:cache:level-{{level}}-{{project}}-build-android-api-15-workspace' + - 'docker-worker:cache:tooltool-cache' + - 'docker-worker:relengapi-proxy:tooltool.download.internal' + - 'docker-worker:relengapi-proxy:tooltool.download.public' + + payload: + cache: + level-{{level}}-{{project}}-build-android-api-15-workspace: '/home/worker/workspace' + tooltool-cache: '/home/worker/tooltool-cache' + + features: + relengAPIProxy: true + + env: + # inputs to mozharness + MOZHARNESS_SCRIPT: 'mozharness/scripts/fx_desktop_build.py' + # TODO: make these additional configuration files go away + MOZHARNESS_CONFIG: > + builds/releng_base_android_64_builds.py + disable_signing.py + platform_supports_post_upload_to_latest.py + taskcluster_nightly.py + MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update" + MH_CUSTOM_BUILD_VARIANT_CFG: api-15 + MH_BRANCH: {{project}} + MH_BUILD_POOL: taskcluster + TOOLTOOL_CACHE: '/home/worker/tooltool-cache' + + command: ["/bin/bash", "bin/build.sh"] + + extra: + treeherderEnv: + - production + - staging + treeherder: + machine: + # see https://github.com/mozilla/treeherder/blob/master/ui/js/values.js + platform: android-4-0-armv7-api15 + groupSymbol: tc + groupName: Submitted by taskcluster + symbol: B + tier: 2 + # Rather then enforcing particular conventions we require that all build + # tasks provide the "build" extra field to specify where the build and tests + # files are located. + locations: + build: 'public/build/target.apk' + mozharness: 'public/build/mozharness.zip' + test_packages: 'public/build/target.test_packages.json' diff --git a/taskcluster/ci/nightly-fennec/build.yml b/taskcluster/ci/nightly-fennec/build.yml new file mode 100644 index 000000000000..4f179b2f3e8e --- /dev/null +++ b/taskcluster/ci/nightly-fennec/build.yml @@ -0,0 +1,46 @@ +# This is the "base" task which contains the common values all builds must +# provide. +--- +taskId: {{build_slugid}} + +task: + created: + relative-datestamp: "0 seconds" + deadline: + relative-datestamp: "24 hours" + metadata: + source: '{{source}}' + owner: mozilla-taskcluster-maintenance@mozilla.com + + tags: + createdForUser: {{owner}} + + provisionerId: aws-provisioner-v1 + schedulerId: task-graph-scheduler + + payload: + # Two hours is long but covers edge cases (and matches bb based infra) + maxRunTime: 7200 + + env: + # Common environment variables for checking out gecko + GECKO_BASE_REPOSITORY: '{{base_repository}}' + GECKO_HEAD_REPOSITORY: '{{head_repository}}' + GECKO_HEAD_REV: '{{head_rev}}' + GECKO_HEAD_REF: '{{head_ref}}' + TOOLTOOL_REPO: 'https://github.com/mozilla/build-tooltool' + TOOLTOOL_REV: 'master' + MOZ_BUILD_DATE: '{{pushdate}}' + MOZ_SCM_LEVEL: '{{level}}' + + extra: + build_product: '{{build_product}}' + build_name: '{{build_name}}' + build_type: '{{build_type}}' + index: + rank: {{rank}} + treeherder: + jobKind: build + groupSymbol: tc + groupName: Submitted by taskcluster + symbol: B diff --git a/taskcluster/ci/nightly-fennec/docker_build.yml b/taskcluster/ci/nightly-fennec/docker_build.yml new file mode 100644 index 000000000000..72bc5575099d --- /dev/null +++ b/taskcluster/ci/nightly-fennec/docker_build.yml @@ -0,0 +1,27 @@ +$inherits: + from: 'build.yml' + + +task: + workerType: b2gbuild + + routes: + - 'index.gecko.v1.{{project}}.revision.linux.{{head_rev}}.{{build_name}}.{{build_type}}' + - 'index.gecko.v1.{{project}}.latest.linux.{{build_name}}.{{build_type}}' + + scopes: + # docker build tasks use tc-vcs so include the scope. + - 'docker-worker:cache:level-{{level}}-{{project}}-tc-vcs' + + payload: + + cache: + level-{{level}}-{{project}}-tc-vcs: '/home/worker/.tc-vcs' + + # All docker builds share a common artifact directory for ease of uploading. + artifacts: + 'public/build': + type: directory + path: '/home/worker/artifacts/' + expires: + relative-datestamp: '1 year' diff --git a/taskcluster/ci/nightly-fennec/kind.yml b/taskcluster/ci/nightly-fennec/kind.yml new file mode 100644 index 000000000000..fa7e4cec0360 --- /dev/null +++ b/taskcluster/ci/nightly-fennec/kind.yml @@ -0,0 +1,7 @@ +# 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/. + +implementation: 'taskgraph.task.nightly_fennec:NightlyFennecTask' +nightly_fennec_path: '.' + diff --git a/taskcluster/ci/nightly-fennec/mobile_base.yml b/taskcluster/ci/nightly-fennec/mobile_base.yml new file mode 100644 index 000000000000..a3e6df48f7a4 --- /dev/null +++ b/taskcluster/ci/nightly-fennec/mobile_base.yml @@ -0,0 +1,13 @@ +$inherits: + from: 'docker_build.yml' + variables: + build_product: 'mobile' +docker-image: desktop-build +task: + payload: + image: + type: 'task-image' + path: 'public/image.tar' + taskId: + task-reference: "" + diff --git a/taskcluster/ci/nightly-fennec/routes.json b/taskcluster/ci/nightly-fennec/routes.json new file mode 100644 index 000000000000..9596f4c97418 --- /dev/null +++ b/taskcluster/ci/nightly-fennec/routes.json @@ -0,0 +1,18 @@ +{ + "routes": [ + "{index}.gecko.v2.{project}.revision.{head_rev}.{build_product}.{build_name}-{build_type}", + "{index}.gecko.v2.{project}.pushdate.{year}.{month}.{day}.{pushdate}.{build_product}.{build_name}-{build_type}", + "{index}.gecko.v2.{project}.latest.{build_product}.{build_name}-{build_type}" + ], + "nightly": [ + "{index}.gecko.v2.{project}.nightly.{year}.{month}.{day}.revision.{head_rev}.{build_product}.{build_name}-{build_type}", + "{index}.gecko.v2.{project}.nightly.{year}.{month}.{day}.latest.{build_product}.{build_name}-{build_type}", + "{index}.gecko.v2.{project}.nightly.revision.{head_rev}.{build_product}.{build_name}-{build_type}", + "{index}.gecko.v2.{project}.nightly.latest.{build_product}.{build_name}-{build_type}" + ], + "l10n": [ + "{index}.gecko.v2.{project}.revision.{head_rev}.{build_product}-l10n.{build_name}-{build_type}.{locale}", + "{index}.gecko.v2.{project}.pushdate.{year}.{month}.{day}.{pushdate}.{build_product}-l10n.{build_name}-{build_type}.{locale}", + "{index}.gecko.v2.{project}.latest.{build_product}-l10n.{build_name}-{build_type}.{locale}" + ] +} diff --git a/taskcluster/ci/signing/kind.yml b/taskcluster/ci/signing/kind.yml new file mode 100644 index 000000000000..76571a5aa7e2 --- /dev/null +++ b/taskcluster/ci/signing/kind.yml @@ -0,0 +1,8 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +implementation: 'taskgraph.task.signing:SigningTask' +signing_path: '.' +kind-dependencies: + - nightly-fennec diff --git a/taskcluster/ci/signing/signing.yml b/taskcluster/ci/signing/signing.yml new file mode 100644 index 000000000000..f7d731550ddf --- /dev/null +++ b/taskcluster/ci/signing/signing.yml @@ -0,0 +1,22 @@ +task: + provisionerId: "aws-provisioner-v1" + workerType: "desktop-test" + schedulerId: task-graph-scheduler + + created: + relative-datestamp: "0 seconds" + deadline: + relative-datestamp: "24 hours" + payload: + image: "ubuntu:13.10" + command: + - "/bin/bash" + - "-c" + - "echo \"hello World\"" + maxRunTime: 600 + metadata: + name: "Signing Fennec Nightly Task" + description: "Markdown description of **what** this task does" + owner: "amiyaguchi@mozilla.com" + source: "https://tools.taskcluster.net/task-creator/" + diff --git a/taskcluster/taskgraph/target_tasks.py b/taskcluster/taskgraph/target_tasks.py index b7f4294fa378..817b9563362a 100644 --- a/taskcluster/taskgraph/target_tasks.py +++ b/taskcluster/taskgraph/target_tasks.py @@ -84,3 +84,13 @@ def target_tasks_ash_tasks(full_task_graph, parameters): return False return True return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)] + + +@_target_task('nightly_fennec') +def target_tasks_nightly(full_task_graph, parameters): + """Select the set of tasks required for a nightly build of fennec. The + nightly build process involves a pipeline of builds, signing, + and, eventually, uploading the tasks to balrog.""" + return [t.label for t in full_task_graph.tasks.itervalues() + if t.attributes.get('kind') in ['nightly-fennec', 'signing']] + diff --git a/taskcluster/taskgraph/task/nightly_fennec.py b/taskcluster/taskgraph/task/nightly_fennec.py new file mode 100644 index 000000000000..4af19940cf83 --- /dev/null +++ b/taskcluster/taskgraph/task/nightly_fennec.py @@ -0,0 +1,113 @@ +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import json +import os +import time + +from . import base +from .legacy import query_vcs_info, decorate_task_json_routes, gaia_info, \ + mklabel +from taskgraph.util.templates import Templates +from taskgraph.util.docker import docker_image + + +logger = logging.getLogger(__name__) +GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) +ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' +INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' + +class NightlyFennecTask(base.Task): + + def __init__(self, *args, **kwargs): + self.task_dict = kwargs.pop('task_dict') + super(NightlyFennecTask, self).__init__(*args, **kwargs) + + @classmethod + def load_tasks(cls, kind, path, config, params, loaded_tasks): + root = os.path.abspath(os.path.join(path, config[ + 'nightly_fennec_path'])) + + project = params['project'] + + # Set up the parameters, including the time of the build + push_epoch = int(time.time()) + vcs_info = query_vcs_info(params['head_repository'], params['head_rev']) + changed_files = set() + if vcs_info: + push_epoch = vcs_info.pushdate + + logger.debug( + '{} commits influencing task scheduling:'.format(len(vcs_info.changesets))) + for c in vcs_info.changesets: + logger.debug("{cset} {desc}".format( + cset=c['node'][0:12], + desc=c['desc'].splitlines()[0].encode('ascii', 'ignore'))) + changed_files |= set(c['files']) + + pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(push_epoch)) + + # Template parameters used when expanding the graph + parameters = dict(gaia_info().items() + { + 'index': 'index', + 'project': project, + 'pushlog_id': params.get('pushlog_id', 0), + 'base_repository': params['base_repository'] or + params['head_repository'], + 'docker_image': docker_image, + 'head_repository': params['head_repository'], + 'head_ref': params['head_ref'] or params['head_rev'], + 'head_rev': params['head_rev'], + 'pushdate': pushdate, + 'pushtime': pushdate[8:], + 'year': pushdate[0:4], + 'month': pushdate[4:6], + 'day': pushdate[6:8], + 'rank': push_epoch, + 'owner': params['owner'], + 'level': params['level'], + 'build_slugid': mklabel(), + 'source': '{repo}file/{rev}/taskcluster/ci/nightly-fennec'.format( + repo=params['head_repository'], rev=params['head_rev']), + 'build_name': 'android', + 'build_type': 'opt', + 'build_product': 'mobile' + }.items()) + + routes_file = os.path.join(root, 'routes.json') + with open(routes_file) as f: + contents = json.load(f) + json_routes = contents['routes'] + + tasks = [] + templates = Templates(root) + + task = templates.load('android-api-15-nightly-build.yml', parameters) + decorate_task_json_routes(task['task'], json_routes, parameters) + + attributes = {'kind': 'nightly-fennec'} + tasks.append(cls(kind, 'build-nightly-fennec', + task=task['task'], attributes=attributes, + task_dict=task)) + + # Convert to a dictionary of tasks. The process above has invented a + # taskId for each task, and we use those as the *labels* for the tasks; + # taskgraph will later assign them new taskIds. + return tasks + + def get_dependencies(self, taskgraph): + deps = [(label, label) for label in self.task_dict.get('requires', [])] + + # add a dependency on an image task, if needed + if 'docker-image' in self.task_dict: + deps.append(('build-docker-image-{docker-image}'.format(**self.task_dict), + 'docker-image')) + + return deps + + def optimize(self): + return False, None diff --git a/taskcluster/taskgraph/task/signing.py b/taskcluster/taskgraph/task/signing.py new file mode 100644 index 000000000000..7f0ea39e7c13 --- /dev/null +++ b/taskcluster/taskgraph/task/signing.py @@ -0,0 +1,48 @@ +# 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/. + +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import json +import os +import time + +from . import base +from taskgraph.util.templates import Templates + + +logger = logging.getLogger(__name__) +GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) +ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' +INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' + +class SigningTask(base.Task): + + def __init__(self, *args, **kwargs): + super(SigningTask, self).__init__(*args, **kwargs) + + @classmethod + def load_tasks(cls, kind, path, config, params, loaded_tasks): + root = os.path.abspath(os.path.join(path, config['signing_path'])) + + # get each nightly-fennec and add its name to this task + fennec_tasks = [t for t in loaded_tasks if t.attributes.get('kind') + == 'nightly-fennec'] + + tasks = [] + for fennec_task in fennec_tasks: + templates = Templates(root) + task = templates.load('signing.yml', {}) + attributes = {'kind': 'signing'} + tasks.append(cls(kind, 'signing-nightly-fennec', task=task['task'], + attributes=attributes)) + + return tasks + + def get_dependencies(self, taskgraph): + return [('build-nightly-fennec', 'build-nightly-fennec')] + + def optimize(self): + return False, None From bc736d60c9f9368a2d2e97fb707ff3986afea208 Mon Sep 17 00:00:00 2001 From: "amiyaguchi@mozilla.com" Date: Thu, 1 Sep 2016 09:24:01 -0700 Subject: [PATCH 023/102] Bug 1277595 - Update signing task to use signing-linux-v1 worker, r=aki MozReview-Commit-ID: EJJgZjooj2d --HG-- extra : rebase_source : 841cd7bc0aead15f0258de0a01c46187d76f4328 extra : source : dee2532bdc95a0f03ccfd6e678e13fedc0e719ba --- taskcluster/ci/signing/signing.yml | 20 ++++++++------------ taskcluster/taskgraph/task/signing.py | 9 +++++++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/taskcluster/ci/signing/signing.yml b/taskcluster/ci/signing/signing.yml index f7d731550ddf..6653ed041a4e 100644 --- a/taskcluster/ci/signing/signing.yml +++ b/taskcluster/ci/signing/signing.yml @@ -1,22 +1,18 @@ task: - provisionerId: "aws-provisioner-v1" - workerType: "desktop-test" - schedulerId: task-graph-scheduler - + provisionerId: "scriptworker-prov-v1" + workerType: "signing-linux-v1" + scopes: + - "project:releng:signing:cert:dep-signing" + - "project:releng:signing:format:jar" created: relative-datestamp: "0 seconds" deadline: relative-datestamp: "24 hours" payload: - image: "ubuntu:13.10" - command: - - "/bin/bash" - - "-c" - - "echo \"hello World\"" + unsignedArtifacts: [] maxRunTime: 600 metadata: - name: "Signing Fennec Nightly Task" - description: "Markdown description of **what** this task does" + name: "Signing Scriptworker Task" + description: "Testing the signing scriptworker" owner: "amiyaguchi@mozilla.com" source: "https://tools.taskcluster.net/task-creator/" - diff --git a/taskcluster/taskgraph/task/signing.py b/taskcluster/taskgraph/task/signing.py index 7f0ea39e7c13..ea3178db8aac 100644 --- a/taskcluster/taskgraph/task/signing.py +++ b/taskcluster/taskgraph/task/signing.py @@ -35,6 +35,15 @@ class SigningTask(base.Task): for fennec_task in fennec_tasks: templates = Templates(root) task = templates.load('signing.yml', {}) + + artifacts = ['public/build/target.apk', + 'public/build/en-US/target.apk'] + for artifact in artifacts: + url = ARTIFACT_URL.format('', artifact) + task['task']['payload']['unsignedArtifacts'].append({ + 'task-reference': url + }) + attributes = {'kind': 'signing'} tasks.append(cls(kind, 'signing-nightly-fennec', task=task['task'], attributes=attributes)) From 643af168e01e08fd10dd60c364c1b752be75f1d7 Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Thu, 1 Sep 2016 21:03:36 -0700 Subject: [PATCH 024/102] Bug 1047105 - Disable AsmJS caching while in Private Browsing Mode; r=janv MozReview-Commit-ID: 17DJMAZTaGW --- dom/asmjscache/AsmJSCache.cpp | 13 +++++++++++++ js/src/asmjs/AsmJS.cpp | 3 +++ js/src/jsapi.h | 1 + 3 files changed, 17 insertions(+) diff --git a/dom/asmjscache/AsmJSCache.cpp b/dom/asmjscache/AsmJSCache.cpp index 107aa5199c5f..2e7d9dba68f1 100644 --- a/dom/asmjscache/AsmJSCache.cpp +++ b/dom/asmjscache/AsmJSCache.cpp @@ -1548,6 +1548,19 @@ OpenFile(nsIPrincipal* aPrincipal, return JS::AsmJSCache_SynchronousScript; } + // Check to see whether the principal reflects a private browsing session. + // Since AsmJSCache requires disk access at the moment, caching should be + // disabled in private browsing situations. Failing here will cause later + // read/write requests to also fail. + uint32_t pbId; + if (NS_WARN_IF(NS_FAILED(aPrincipal->GetPrivateBrowsingId(&pbId)))) { + return JS::AsmJSCache_InternalError; + } + + if (pbId > 0) { + return JS::AsmJSCache_Disabled_PrivateBrowsing; + } + // We need to synchronously call into the parent to open the file and // interact with the QuotaManager. The child can then map the file into its // address space to perform I/O. diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 30ffdb51d737..9eb23c471927 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -8504,6 +8504,9 @@ BuildConsoleMessage(ExclusiveContext* cx, unsigned time, JS::AsmJSCacheResult ca case JS::AsmJSCache_InternalError: cacheString = "unable to store in cache due to internal error (consider filing a bug)"; break; + case JS::AsmJSCache_Disabled_PrivateBrowsing: + cacheString = "caching disabled by private browsing mode"; + break; case JS::AsmJSCache_LIMIT: MOZ_CRASH("bad AsmJSCacheResult"); break; diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 9c2a633b8fb3..7ba059eb3d82 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5827,6 +5827,7 @@ enum AsmJSCacheResult AsmJSCache_Disabled_ShellFlags, AsmJSCache_Disabled_JitInspector, AsmJSCache_InternalError, + AsmJSCache_Disabled_PrivateBrowsing, AsmJSCache_LIMIT }; From 5c55e4636620bb3d3e9e9195308794454307b6fd Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 2 Sep 2016 11:02:59 -0700 Subject: [PATCH 025/102] Backed out changeset d332de446548 (bug 1294650) --- browser/installer/package-manifest.in | 7 ------- toolkit/library/dummydll/dummydll.cpp | 17 ----------------- toolkit/library/dummydll/moz.build | 19 ------------------- toolkit/library/moz.build | 3 --- 4 files changed, 46 deletions(-) delete mode 100644 toolkit/library/dummydll/dummydll.cpp delete mode 100644 toolkit/library/dummydll/moz.build diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 99db6e4aec76..821983afc43f 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -141,13 +141,6 @@ @RESPATH@/run-mozilla.sh #endif #endif -#ifdef XP_WIN -#ifdef _AMD64_ -@BINPATH@/@DLL_PREFIX@qipcap64@DLL_SUFFIX@ -#else -@BINPATH@/@DLL_PREFIX@qipcap@DLL_SUFFIX@ -#endif -#endif ; [Components] #ifdef MOZ_ARTIFACT_BUILDS diff --git a/toolkit/library/dummydll/dummydll.cpp b/toolkit/library/dummydll/dummydll.cpp deleted file mode 100644 index 5e1c04bd39c0..000000000000 --- a/toolkit/library/dummydll/dummydll.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include - -BOOL WINAPI DllMain( - HANDLE hModule, - DWORD dwReason, - LPVOID lpvReserved -) -{ - if (dwReason == DLL_PROCESS_ATTACH) { - ::DisableThreadLibraryCalls((HMODULE)hModule); - } - return TRUE; -} diff --git a/toolkit/library/dummydll/moz.build b/toolkit/library/dummydll/moz.build deleted file mode 100644 index 01a0ddba8890..000000000000 --- a/toolkit/library/dummydll/moz.build +++ /dev/null @@ -1,19 +0,0 @@ -# -*- 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/. - -# Bug 1294650 - populate our install with a shim dll to work around a -# 3rd party code injection crash. - -SOURCES += [ - 'dummydll.cpp', -] - -if CONFIG['CPU_ARCH'] == 'x86_64': - GeckoSharedLibrary('qipcap64') -else: - GeckoSharedLibrary('qipcap') - -NO_VISIBILITY_FLAGS = True diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build index 6aab7cb2949c..09354859dd1a 100644 --- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -102,9 +102,6 @@ if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']: DIRS += ['gtest'] -if CONFIG['OS_ARCH'] == 'WINNT': - DIRS += ['dummydll'] - # js needs to come after xul for now, because it is an archive and its content # is discarded when it comes first. USE_LIBS += [ From b1b7e86e2f9e50578bbc2293da50dc10bceac232 Mon Sep 17 00:00:00 2001 From: Jordan Lund Date: Fri, 2 Sep 2016 11:34:37 -0700 Subject: [PATCH 026/102] Bug 1300168 - clean up fennec nightly and signing flake errors, r=callek MozReview-Commit-ID: 9Agn9Dhh9jw --HG-- extra : amend_source : 927f332173b45788bff835d5b0f72ee04baecbf7 --- taskcluster/taskgraph/target_tasks.py | 1 - taskcluster/taskgraph/task/nightly_fennec.py | 4 ++-- taskcluster/taskgraph/task/signing.py | 6 ++---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/taskcluster/taskgraph/target_tasks.py b/taskcluster/taskgraph/target_tasks.py index 817b9563362a..8c8cf74e6122 100644 --- a/taskcluster/taskgraph/target_tasks.py +++ b/taskcluster/taskgraph/target_tasks.py @@ -93,4 +93,3 @@ def target_tasks_nightly(full_task_graph, parameters): and, eventually, uploading the tasks to balrog.""" return [t.label for t in full_task_graph.tasks.itervalues() if t.attributes.get('kind') in ['nightly-fennec', 'signing']] - diff --git a/taskcluster/taskgraph/task/nightly_fennec.py b/taskcluster/taskgraph/task/nightly_fennec.py index 4af19940cf83..16e960a3cc85 100644 --- a/taskcluster/taskgraph/task/nightly_fennec.py +++ b/taskcluster/taskgraph/task/nightly_fennec.py @@ -21,6 +21,7 @@ GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' + class NightlyFennecTask(base.Task): def __init__(self, *args, **kwargs): @@ -56,8 +57,7 @@ class NightlyFennecTask(base.Task): 'index': 'index', 'project': project, 'pushlog_id': params.get('pushlog_id', 0), - 'base_repository': params['base_repository'] or - params['head_repository'], + 'base_repository': params['base_repository'] or params['head_repository'], 'docker_image': docker_image, 'head_repository': params['head_repository'], 'head_ref': params['head_ref'] or params['head_rev'], diff --git a/taskcluster/taskgraph/task/signing.py b/taskcluster/taskgraph/task/signing.py index ea3178db8aac..90c25dfecf25 100644 --- a/taskcluster/taskgraph/task/signing.py +++ b/taskcluster/taskgraph/task/signing.py @@ -5,9 +5,7 @@ from __future__ import absolute_import, print_function, unicode_literals import logging -import json import os -import time from . import base from taskgraph.util.templates import Templates @@ -18,6 +16,7 @@ GECKO = os.path.realpath(os.path.join(__file__, '..', '..', '..', '..')) ARTIFACT_URL = 'https://queue.taskcluster.net/v1/task/{}/artifacts/{}' INDEX_URL = 'https://index.taskcluster.net/v1/task/{}' + class SigningTask(base.Task): def __init__(self, *args, **kwargs): @@ -28,8 +27,7 @@ class SigningTask(base.Task): root = os.path.abspath(os.path.join(path, config['signing_path'])) # get each nightly-fennec and add its name to this task - fennec_tasks = [t for t in loaded_tasks if t.attributes.get('kind') - == 'nightly-fennec'] + fennec_tasks = [t for t in loaded_tasks if t.attributes.get('kind') == 'nightly-fennec'] tasks = [] for fennec_task in fennec_tasks: From 40a6cd7a777945a0768225712d0d61d923c55d89 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Fri, 2 Sep 2016 16:13:44 -0400 Subject: [PATCH 027/102] Bug 1297211 - treat an empty src as available in image requests for canvas2d drawImage. r=mchang MozReview-Commit-ID: GwB2oQHy7Ex --- layout/base/nsLayoutUtils.cpp | 14 +++++++++++++- .../2d.drawImage.incomplete.emptysrc.html.ini | 5 ----- .../2d.drawImage.incomplete.nosrc.html.ini | 5 ----- .../2d.drawImage.incomplete.removedsrc.html.ini | 5 ----- 4 files changed, 13 insertions(+), 16 deletions(-) delete mode 100644 testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html.ini delete mode 100644 testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html.ini delete mode 100644 testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html.ini diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 6bd9f8cdfd7f..a8ade69689e5 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -7337,7 +7337,19 @@ nsLayoutUtils::SurfaceFromElement(nsIImageLoadingContent* aElement, nsCOMPtr imgRequest; rv = aElement->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(imgRequest)); - if (NS_FAILED(rv) || !imgRequest) { + if (NS_FAILED(rv)) { + return result; + } + + if (!imgRequest) { + // There's no image request. This is either because a request for + // a non-empty URI failed, or the URI is the empty string. + nsCOMPtr currentURI; + aElement->GetCurrentURI(getter_AddRefs(currentURI)); + if (!currentURI) { + // Treat the empty URI as available instead of broken state. + result.mHasSize = true; + } return result; } diff --git a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html.ini b/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html.ini deleted file mode 100644 index d47648d6d4ac..000000000000 --- a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.emptysrc.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.drawImage.incomplete.emptysrc.html] - type: testharness - [Canvas test: 2d.drawImage.incomplete.emptysrc] - expected: FAIL - diff --git a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html.ini b/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html.ini deleted file mode 100644 index 4b9cc677204f..000000000000 --- a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.nosrc.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.drawImage.incomplete.nosrc.html] - type: testharness - [Canvas test: 2d.drawImage.incomplete.nosrc] - expected: FAIL - diff --git a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html.ini b/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html.ini deleted file mode 100644 index 0813bec1e54a..000000000000 --- a/testing/web-platform/meta/2dcontext/drawing-images-to-the-canvas/2d.drawImage.incomplete.removedsrc.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[2d.drawImage.incomplete.removedsrc.html] - type: testharness - [Canvas test: 2d.drawImage.incomplete.removedsrc] - expected: FAIL - From b1f6796732bd719a7c70bacf88bbc361f772f2e3 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Fri, 2 Sep 2016 23:19:56 +0300 Subject: [PATCH 028/102] bug 1300129, Ensure devtools get the right event listener data, r=mccr8 --- dom/events/EventListenerManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 11380ef879ee..fa235d8e6b26 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -1544,9 +1544,9 @@ EventListenerManager::GetListenerInfo(nsCOMArray* aList) nsCOMPtr target = do_QueryInterface(mTarget); NS_ENSURE_STATE(target); aList->Clear(); - uint32_t count = mListeners.Length(); - for (uint32_t i = 0; i < count; ++i) { - const Listener& listener = mListeners.ElementAt(i); + nsAutoTObserverArray::ForwardIterator iter(mListeners); + while (iter.HasMore()) { + const Listener& listener = iter.GetNext(); // If this is a script handler and we haven't yet // compiled the event handler itself go ahead and compile it if (listener.mListenerType == Listener::eJSEventListener && From 627e13a274521cc74f2c62f38e80c5fa512d2843 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Fri, 2 Sep 2016 13:30:13 -0700 Subject: [PATCH 029/102] Bug 1299645 - Fix browser_tab_close_dependent_window.js test. r=Gijs After closing the tab it's possible the it's now a dead object, so check for that first. --- .../content/test/general/browser_tab_close_dependent_window.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/test/general/browser_tab_close_dependent_window.js b/browser/base/content/test/general/browser_tab_close_dependent_window.js index b1cf0190d4d3..ab8a960ac1ad 100644 --- a/browser/base/content/test/general/browser_tab_close_dependent_window.js +++ b/browser/base/content/test/general/browser_tab_close_dependent_window.js @@ -17,7 +17,7 @@ add_task(function* closing_tab_with_dependents_should_close_window() { let windowClosedPromise = BrowserTestUtils.windowClosed(win); yield BrowserTestUtils.removeTab(tab); - is(openedTab.linkedBrowser, null, "Opened tab should also have closed"); + is(Cu.isDeadWrapper(openedTab) || openedTab.linkedBrowser == null, true, "Opened tab should also have closed"); info("If we timeout now, the window failed to close - that shouldn't happen!"); yield windowClosedPromise; }); From d7ccc9771a21c7bc66b9f60a2dac18553847ef10 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 2 Sep 2016 16:49:11 -0400 Subject: [PATCH 030/102] Bug 1291383 - Let Java code dispatch events through EventDispatcher; r=sebastian Add dispatch() methods to EventDispatcher that allow Java code to dispatch events to Bundle listeners (currently either UI or background thread listeners). --- .../org/mozilla/gecko/EventDispatcher.java | 98 +++++++++++++++++-- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java index 42cb43342abf..40ab5cfed481 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java @@ -235,11 +235,7 @@ public final class EventDispatcher { // Check for thread event listeners before checking for JSON event listeners, // because checking for thread listeners is very fast and doesn't require us to // serialize into JSON and construct a JSONObject. - if (dispatchToThread(type, message, callback, - mUiThreadListeners, ThreadUtils.getUiHandler()) || - dispatchToThread(type, message, callback, - mBackgroundThreadListeners, ThreadUtils.getBackgroundHandler())) { - + if (dispatchToThreads(type, message, /* bundle */ null, callback)) { // If we found thread listeners, we assume we don't have any other types of listeners // and return early. This assumption is checked when registering listeners. return; @@ -255,8 +251,96 @@ public final class EventDispatcher { } } + /** + * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners). + * + * @param message Bundle message with "type" value specifying the event type. + */ + public void dispatch(final Bundle message) { + dispatch(message, /* callback */ null); + } + + /** + * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners). + * + * @param message Bundle message with "type" value specifying the event type. + * @param callback Optional object for callbacks from events. + */ + public void dispatch(final Bundle message, final EventCallback callback) { + if (message == null) { + throw new IllegalArgumentException("Null message"); + } + + final String type = message.getCharSequence("type").toString(); + if (type == null) { + Log.e(LOGTAG, "Bundle message must have a type property"); + return; + } + dispatchToThreads(type, /* js */ null, message, /* callback */ callback); + } + + /** + * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners). + * + * @param type Event type + * @param message Bundle message + */ + public void dispatch(final String type, final Bundle message) { + dispatch(type, message, /* callback */ null); + } + + /** + * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners). + * + * @param type Event type + * @param message Bundle message + * @param callback Optional object for callbacks from events. + */ + public void dispatch(final String type, final Bundle message, final EventCallback callback) { + dispatchToThreads(type, /* js */ null, message, /* callback */ callback); + } + + private boolean dispatchToThreads(final String type, + final NativeJSObject jsMessage, + final Bundle bundleMessage, + final EventCallback callback) { + if (dispatchToThread(type, jsMessage, bundleMessage, callback, + mUiThreadListeners, ThreadUtils.getUiHandler())) { + return true; + } + + if (dispatchToThread(type, jsMessage, bundleMessage, callback, + mBackgroundThreadListeners, ThreadUtils.getBackgroundHandler())) { + return true; + } + + if (jsMessage == null) { + Log.w(LOGTAG, "No listeners for " + type); + } + + if (!AppConstants.RELEASE_BUILD && jsMessage == null) { + // We're dispatching a Bundle message. Because Gecko thread listeners are not + // supported for Bundle messages, do a sanity check to make sure we don't have + // matching Gecko thread listeners. + boolean hasGeckoListener = false; + synchronized (mGeckoThreadNativeListeners) { + hasGeckoListener |= mGeckoThreadNativeListeners.containsKey(type); + } + synchronized (mGeckoThreadJSONListeners) { + hasGeckoListener |= mGeckoThreadJSONListeners.containsKey(type); + } + if (hasGeckoListener) { + throw new IllegalStateException( + "Dispatching Bundle message to Gecko listener " + type); + } + } + + return false; + } + private boolean dispatchToThread(final String type, - final NativeJSObject message, + final NativeJSObject jsMessage, + final Bundle bundleMessage, final EventCallback callback, final Map> listenersMap, final Handler thread) { @@ -283,7 +367,7 @@ public final class EventDispatcher { final Bundle messageAsBundle; try { - messageAsBundle = message.toBundle(); + messageAsBundle = jsMessage != null ? jsMessage.toBundle() : bundleMessage; } catch (final NativeJSObject.InvalidPropertyException e) { Log.e(LOGTAG, "Exception occurred while handling " + type, e); return true; From 8f665ca47e9a98f6d07236d14df6921662788d2e Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 2 Sep 2016 16:49:11 -0400 Subject: [PATCH 031/102] Bug 1291383 - Dispatch Profile:Create event when creating profile directory; r=nalexander Dispatch a Profile:Create event when creating a profile directory, so we can use it to initialize distributions. --- .../java/org/mozilla/gecko/GeckoProfile.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java index 582809ef1ac4..1301917dadd3 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java @@ -9,6 +9,7 @@ import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; +import android.os.Bundle; import android.support.annotation.WorkerThread; import android.text.TextUtils; import android.util.Log; @@ -392,7 +393,7 @@ public final class GeckoProfile { */ @RobocopTarget public synchronized File getDir() { - forceCreate(); + forceCreateLocked(); return mProfileDir; } @@ -400,9 +401,9 @@ public final class GeckoProfile { * Forces profile creation. Consider using {@link #getDir()} to initialize the profile instead - it is the * lazy initializer and, for our code reasoning abilities, we should initialize the profile in one place. */ - private synchronized GeckoProfile forceCreate() { + private void forceCreateLocked() { if (mProfileDir != null) { - return this; + return; } try { @@ -417,7 +418,6 @@ public final class GeckoProfile { } catch (IOException ioe) { Log.e(LOGTAG, "Error getting profile dir", ioe); } - return this; } public File getFile(String aFile) { @@ -898,12 +898,12 @@ public final class GeckoProfile { INIParser parser = GeckoProfileDirectories.getProfilesINI(mMozillaDir); // Salt the name of our requested profile - String saltedName = GeckoProfileDirectories.saltProfileName(mName); - File profileDir = new File(mMozillaDir, saltedName); - while (profileDir.exists()) { + String saltedName; + File profileDir; + do { saltedName = GeckoProfileDirectories.saltProfileName(mName); profileDir = new File(mMozillaDir, saltedName); - } + } while (profileDir.exists()); // Attempt to create the salted profile dir if (!profileDir.mkdirs()) { @@ -988,6 +988,12 @@ public final class GeckoProfile { @RobocopTarget public void enqueueInitialization(final File profileDir) { Log.i(LOGTAG, "Enqueuing profile init."); + + final Bundle message = new Bundle(2); + message.putCharSequence("name", getName()); + message.putCharSequence("path", profileDir.getAbsolutePath()); + EventDispatcher.getInstance().dispatch("Profile:Create", message); + final Context context = mApplicationContext; // Add everything when we're done loading the distribution. From cf1e61cb8d7bfd45f3a97693e041fb848a5109f0 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Fri, 2 Sep 2016 16:49:11 -0400 Subject: [PATCH 032/102] Bug 1291383 - Move Distribution initialization out of GeckoProfile; r=nalexander Move Distribution initialization (on profile directory creation) from GeckoProfile to GeckoApplication, by listening to the Profile:Create event. --- .../org/mozilla/gecko/GeckoApplication.java | 95 +++++++++++++++++++ .../java/org/mozilla/gecko/GeckoProfile.java | 74 ++------------- 2 files changed, 103 insertions(+), 66 deletions(-) diff --git a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java index d0e45add29e7..44246955c864 100644 --- a/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApplication.java @@ -5,9 +5,11 @@ package org.mozilla.gecko; import android.app.Application; +import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.os.Bundle; import android.os.SystemClock; import android.util.Log; @@ -17,13 +19,17 @@ import com.squareup.leakcanary.RefWatcher; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.LocalBrowserDB; +import org.mozilla.gecko.distribution.Distribution; import org.mozilla.gecko.dlc.DownloadContentService; import org.mozilla.gecko.home.HomePanelsManager; import org.mozilla.gecko.lwt.LightweightTheme; import org.mozilla.gecko.mdns.MulticastDNSManager; import org.mozilla.gecko.media.AudioFocusAgent; import org.mozilla.gecko.notifications.NotificationHelper; +import org.mozilla.gecko.preferences.DistroSharedPrefsImport; +import org.mozilla.gecko.util.BundleEventListener; import org.mozilla.gecko.util.Clipboard; +import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.ThreadUtils; @@ -183,6 +189,9 @@ public class GeckoApplication extends Application GeckoService.register(); + EventDispatcher.getInstance().registerBackgroundThreadListener(new EventListener(), + "Profile:Create"); + super.onCreate(); } @@ -216,6 +225,92 @@ public class GeckoApplication extends Application AudioFocusAgent.getInstance().attachToContext(this); } + private class EventListener implements BundleEventListener + { + private void onProfileCreate(final String name, final String path) { + // Add everything when we're done loading the distribution. + final Context context = GeckoApplication.this; + final GeckoProfile profile = GeckoProfile.get(context, name); + final Distribution distribution = Distribution.getInstance(context); + + distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() { + @Override + public void distributionNotFound() { + this.distributionFound(null); + } + + @Override + public void distributionFound(final Distribution distribution) { + Log.d(LOG_TAG, "Running post-distribution task: bookmarks."); + // Because we are running in the background, we want to synchronize on the + // GeckoProfile instance so that we don't race with main thread operations + // such as locking/unlocking/removing the profile. + synchronized (profile.getLock()) { + distributionFoundLocked(distribution); + } + } + + @Override + public void distributionArrivedLate(final Distribution distribution) { + Log.d(LOG_TAG, "Running late distribution task: bookmarks."); + // Recover as best we can. + synchronized (profile.getLock()) { + distributionArrivedLateLocked(distribution); + } + } + + private void distributionFoundLocked(final Distribution distribution) { + // Skip initialization if the profile directory has been removed. + if (!(new File(path)).exists()) { + return; + } + + final ContentResolver cr = context.getContentResolver(); + final LocalBrowserDB db = new LocalBrowserDB(profile.getName()); + + // We pass the number of added bookmarks to ensure that the + // indices of the distribution and default bookmarks are + // contiguous. Because there are always at least as many + // bookmarks as there are favicons, we can also guarantee that + // the favicon IDs won't overlap. + final int offset = distribution == null ? 0 : + db.addDistributionBookmarks(cr, distribution, 0); + db.addDefaultBookmarks(context, cr, offset); + + Log.d(LOG_TAG, "Running post-distribution task: android preferences."); + DistroSharedPrefsImport.importPreferences(context, distribution); + } + + private void distributionArrivedLateLocked(final Distribution distribution) { + // Skip initialization if the profile directory has been removed. + if (!(new File(path)).exists()) { + return; + } + + final ContentResolver cr = context.getContentResolver(); + final LocalBrowserDB db = new LocalBrowserDB(profile.getName()); + + // We assume we've been called very soon after startup, and so our offset + // into "Mobile Bookmarks" is the number of bookmarks in the DB. + final int offset = db.getCount(cr, "bookmarks"); + db.addDistributionBookmarks(cr, distribution, offset); + + Log.d(LOG_TAG, "Running late distribution task: android preferences."); + DistroSharedPrefsImport.importPreferences(context, distribution); + } + }); + } + + @Override // BundleEventListener + public void handleMessage(final String event, final Bundle message, + final EventCallback callback) { + if ("Profile:Create".equals(event)) { + onProfileCreate(message.getCharSequence("name").toString(), + message.getCharSequence("path").toString()); + } + } + } + public boolean isApplicationInBackground() { return mInBackground; } diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java index 1301917dadd3..5ae731be4474 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java @@ -6,7 +6,6 @@ package org.mozilla.gecko; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; @@ -21,11 +20,8 @@ import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException; import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException; import org.mozilla.gecko.annotation.RobocopTarget; import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.db.LocalBrowserDB; import org.mozilla.gecko.db.StubBrowserDB; -import org.mozilla.gecko.distribution.Distribution; import org.mozilla.gecko.firstrun.FirstrunAnimationContainer; -import org.mozilla.gecko.preferences.DistroSharedPrefsImport; import org.mozilla.gecko.util.FileUtils; import org.mozilla.gecko.util.INIParser; import org.mozilla.gecko.util.INISection; @@ -387,6 +383,14 @@ public final class GeckoProfile { return mInGuestMode; } + /** + * Return an Object that can be used with a synchronized statement to allow + * exclusive access to the profile. + */ + public Object getLock() { + return this; + } + /** * Retrieves the directory backing the profile. This method acts * as a lazy initializer for the GeckoProfile instance. @@ -993,67 +997,5 @@ public final class GeckoProfile { message.putCharSequence("name", getName()); message.putCharSequence("path", profileDir.getAbsolutePath()); EventDispatcher.getInstance().dispatch("Profile:Create", message); - - final Context context = mApplicationContext; - - // Add everything when we're done loading the distribution. - final Distribution distribution = Distribution.getInstance(context); - distribution.addOnDistributionReadyCallback(new Distribution.ReadyCallback() { - @Override - public void distributionNotFound() { - this.distributionFound(null); - } - - @Override - public void distributionFound(Distribution distribution) { - Log.d(LOGTAG, "Running post-distribution task: bookmarks."); - - final ContentResolver cr = context.getContentResolver(); - - // Because we are running in the background, we want to synchronize on the - // GeckoProfile instance so that we don't race with main thread operations - // such as locking/unlocking/removing the profile. - synchronized (GeckoProfile.this) { - // Skip initialization if the profile directory has been removed. - if (!profileDir.exists()) { - return; - } - - // We pass the number of added bookmarks to ensure that the - // indices of the distribution and default bookmarks are - // contiguous. Because there are always at least as many - // bookmarks as there are favicons, we can also guarantee that - // the favicon IDs won't overlap. - final LocalBrowserDB db = new LocalBrowserDB(getName()); - final int offset = distribution == null ? 0 : db.addDistributionBookmarks(cr, distribution, 0); - db.addDefaultBookmarks(context, cr, offset); - - Log.d(LOGTAG, "Running post-distribution task: android preferences."); - DistroSharedPrefsImport.importPreferences(context, distribution); - } - } - - @Override - public void distributionArrivedLate(Distribution distribution) { - Log.d(LOGTAG, "Running late distribution task: bookmarks."); - // Recover as best we can. - synchronized (GeckoProfile.this) { - // Skip initialization if the profile directory has been removed. - if (!profileDir.exists()) { - return; - } - - final LocalBrowserDB db = new LocalBrowserDB(getName()); - // We assume we've been called very soon after startup, and so our offset - // into "Mobile Bookmarks" is the number of bookmarks in the DB. - final ContentResolver cr = context.getContentResolver(); - final int offset = db.getCount(cr, "bookmarks"); - db.addDistributionBookmarks(cr, distribution, offset); - - Log.d(LOGTAG, "Running late distribution task: android preferences."); - DistroSharedPrefsImport.importPreferences(context, distribution); - } - } - }); } } From a2597fa145659bd5177c239367b3b34e69bf405b Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Wed, 31 Aug 2016 11:47:44 +0100 Subject: [PATCH 033/102] Bug 1299391 - fix decimal IP detection for 'did you mean this host instead' when we fix up URIs for search, r=jaws MozReview-Commit-ID: JTwxbvkATm0 --HG-- extra : rebase_source : 701a353f9a4a22dbb1aba8fbdca0dd502649e0aa --- browser/base/content/browser.js | 2 +- ...wser_urlbarSearchSingleWordNotification.js | 44 +++++++++++++++---- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 1602785bd467..39e3b23cbf95 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -691,7 +691,7 @@ function gKeywordURIFixup({ target: browser, data: fixupInfo }) { } // Ignore number-only things entirely (no decimal IPs for you!) - if (/^\d+$/.test(asciiHost)) + if (/^\d+$/.test(fixupInfo.originalInput.trim())) return; let onLookupComplete = (request, record, status) => { diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js index 57b07be6dd25..6cf1a304d936 100644 --- a/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js +++ b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js @@ -38,7 +38,7 @@ function promiseNotificationForTab(aBrowser, value, expected, tab=aBrowser.selec return deferred.promise; } -function* runURLBarSearchTest(valueToOpen, expectSearch, expectNotification, aWindow=window) { +function* runURLBarSearchTest({valueToOpen, expectSearch, expectNotification, aWindow=window}) { aWindow.gURLBar.value = valueToOpen; let expectedURI; if (!expectSearch) { @@ -62,21 +62,33 @@ function* runURLBarSearchTest(valueToOpen, expectSearch, expectNotification, aWi add_task(function* test_navigate_full_domain() { let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); - yield* runURLBarSearchTest("www.mozilla.org", false, false); + yield* runURLBarSearchTest({ + valueToOpen: "www.mozilla.org", + expectSearch: false, + expectNotification: false, + }); gBrowser.removeTab(tab); }); -add_task(function* test_navigate_valid_numbers() { +add_task(function* test_navigate_decimal_ip() { let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); - yield* runURLBarSearchTest("1234", true, true); + yield* runURLBarSearchTest({ + valueToOpen: "1234", + expectSearch: true, + expectNotification: false, + }); gBrowser.removeTab(tab); }); -add_task(function* test_navigate_invalid_numbers() { +add_task(function* test_navigate_large_number() { let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); - yield* runURLBarSearchTest("123456789012345", true, false); + yield* runURLBarSearchTest({ + valueToOpen: "123456789012345", + expectSearch: true, + expectNotification: false + }); gBrowser.removeTab(tab); }); function get_test_function_for_localhost_with_hostname(hostName, isPrivate) { @@ -96,7 +108,12 @@ function get_test_function_for_localhost_with_hostname(hostName, isPrivate) { yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); Services.prefs.setBoolPref(pref, false); - yield* runURLBarSearchTest(hostName, true, true, win); + yield* runURLBarSearchTest({ + valueToOpen: hostName, + expectSearch: true, + expectNotification: true, + aWindow: win, + }); let notificationBox = browser.getNotificationBox(tab.linkedBrowser); let notification = notificationBox.getNotificationWithValue("keyword-uri-fixup"); @@ -114,7 +131,12 @@ function get_test_function_for_localhost_with_hostname(hostName, isPrivate) { tab = browser.selectedTab = browser.addTab("about:blank"); yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); // In a private window, the notification should appear again. - yield* runURLBarSearchTest(hostName, isPrivate, isPrivate, win); + yield* runURLBarSearchTest({ + valueToOpen: hostName, + expectSearch: isPrivate, + expectNotification: isPrivate, + aWindow: win, + }); browser.removeTab(tab); if (isPrivate) { info("Waiting for private window to close"); @@ -134,6 +156,10 @@ add_task(get_test_function_for_localhost_with_hostname("localhost", true)); add_task(function* test_navigate_invalid_url() { let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank"); yield BrowserTestUtils.browserLoaded(tab.linkedBrowser); - yield* runURLBarSearchTest("mozilla is awesome", true, false); + yield* runURLBarSearchTest({ + valueToOpen: "mozilla is awesome", + expectSearch: true, + expectNotification: false, + }); gBrowser.removeTab(tab); }); From 24da61daeaa8d0358ce9a66ba963704866794694 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Tue, 19 Jul 2016 15:56:15 +0100 Subject: [PATCH 034/102] Bug 1286389 - fix focusing if nothing is focused when panel closes, r=Enn MozReview-Commit-ID: DzNnQZjuXK2 --HG-- extra : rebase_source : d84d49f06a275a4ab664996e144211d3cc588e3e extra : source : 3696a22ff7a09bcc27296c0a5648e2e9afd32f67 --- toolkit/content/widgets/popup.xml | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/toolkit/content/widgets/popup.xml b/toolkit/content/widgets/popup.xml index 3292c876ddfd..590a39c79e54 100644 --- a/toolkit/content/widgets/popup.xml +++ b/toolkit/content/widgets/popup.xml @@ -316,27 +316,35 @@ } ]]> Date: Thu, 1 Sep 2016 15:31:10 -0400 Subject: [PATCH 035/102] Bug 1299164: Part 1. Add BufferSizeFromDimensions method to complement BufferSizeFromStrideAndHeight. r=bas MozReview-Commit-ID: Ef0RhnM7UJa --HG-- extra : rebase_source : 65c0311d548f92ace59f2d6bca5f533d060d1881 --- gfx/2d/DataSurfaceHelpers.cpp | 26 ++++++++++++++++++++++++-- gfx/2d/DataSurfaceHelpers.h | 16 ++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/gfx/2d/DataSurfaceHelpers.cpp b/gfx/2d/DataSurfaceHelpers.cpp index 7c42afc4e861..87ef00fcd205 100644 --- a/gfx/2d/DataSurfaceHelpers.cpp +++ b/gfx/2d/DataSurfaceHelpers.cpp @@ -265,7 +265,7 @@ BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight, int32_t aExtraBytes) { - if (MOZ_UNLIKELY(aHeight <= 0)) { + if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) { return 0; } @@ -279,7 +279,29 @@ BufferSizeFromStrideAndHeight(int32_t aStride, CheckedInt32 requiredBytes = CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); if (MOZ_UNLIKELY(!requiredBytes.isValid())) { - gfxWarning() << "Buffer size too big; returning zero"; + gfxWarning() << "Buffer size too big; returning zero " << aStride << ", " << aHeight << ", " << aExtraBytes; + return 0; + } + return requiredBytes.value(); +} + +size_t +BufferSizeFromDimensions(int32_t aWidth, + int32_t aHeight, + int32_t aDepth, + int32_t aExtraBytes) +{ + if (MOZ_UNLIKELY(aHeight <= 0) || + MOZ_UNLIKELY(aWidth <= 0) || + MOZ_UNLIKELY(aDepth <= 0)) { + return 0; + } + + // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter. + + CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes); + if (MOZ_UNLIKELY(!requiredBytes.isValid())) { + gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes; return 0; } return requiredBytes.value(); diff --git a/gfx/2d/DataSurfaceHelpers.h b/gfx/2d/DataSurfaceHelpers.h index a0172e3ad744..b0fdf2983cbf 100644 --- a/gfx/2d/DataSurfaceHelpers.h +++ b/gfx/2d/DataSurfaceHelpers.h @@ -92,6 +92,22 @@ BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight, int32_t aExtraBytes = 0); +/** + * Multiplies aWidth, aHeight, aDepth and makes sure the result is limited to + * something sane. To keep things consistent, this should always be used + * wherever we allocate a buffer based on surface dimensions. + * + * @param aExtra Optional argument to specify an additional number of trailing + * bytes (useful for creating intermediate surfaces for filters, for + * example). + * + * @return The result of the multiplication if it is acceptable, or else zero. + */ +size_t +BufferSizeFromDimensions(int32_t aWidth, + int32_t aHeight, + int32_t aDepth, + int32_t aExtraBytes = 0); /** * Copy aSrcRect from aSrc to aDest starting at aDestPoint. * @returns false if the copy is not successful or the aSrc's size is empty. From cd425fcdf7bd1a6e55c16d020f7fbc0494d46675 Mon Sep 17 00:00:00 2001 From: Milan Sreckovic Date: Thu, 1 Sep 2016 15:31:16 -0400 Subject: [PATCH 036/102] Bug 1299164: Part 2. Use BufferSizeFromDimensions method, as well as some of the others that check for valid size. r=bas MozReview-Commit-ID: FLBV5Aex3ga --HG-- extra : rebase_source : 739a0c5052eaae4ed7f9d21cbbbe465dd10cb94a --- dom/base/ImageEncoder.cpp | 12 +++++++++--- dom/canvas/CanvasRenderingContext2D.cpp | 13 ++++++++++++- dom/canvas/ImageBitmapRenderingContext.cpp | 3 ++- dom/plugins/ipc/PluginInstanceChild.cpp | 10 +++++++--- gfx/2d/Factory.cpp | 15 +++++++++++++-- gfx/2d/ImageScaling.cpp | 16 ++++++++-------- gfx/2d/SourceSurfaceRawData.h | 3 ++- gfx/layers/TextureDIB.cpp | 19 +++++++++++++------ gfx/layers/basic/GrallocTextureHostBasic.cpp | 10 +++++++--- gfx/layers/composite/CanvasLayerComposite.cpp | 4 +++- gfx/layers/composite/ImageLayerComposite.cpp | 4 +++- gfx/thebes/gfxAndroidPlatform.cpp | 4 ++++ gfx/thebes/gfxPlatformGtk.cpp | 4 ++++ gfx/thebes/gfxPlatformMac.cpp | 4 ++++ gfx/thebes/gfxWindowsPlatform.cpp | 4 ++++ 15 files changed, 95 insertions(+), 30 deletions(-) diff --git a/dom/base/ImageEncoder.cpp b/dom/base/ImageEncoder.cpp index e9d24844b31d..a41dee080989 100644 --- a/dom/base/ImageEncoder.cpp +++ b/dom/base/ImageEncoder.cpp @@ -369,6 +369,10 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, // get image bytes nsresult rv; if (aImageBuffer) { + if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { + return NS_ERROR_INVALID_ARG; + } + rv = ImageEncoder::GetInputStream( aSize.width, aSize.height, @@ -417,6 +421,10 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions); } else { + if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { + return NS_ERROR_INVALID_ARG; + } + RefPtr dataSurface; RefPtr image(aImage); dataSurface = GetBRGADataSourceSurfaceSync(image.forget()); @@ -439,12 +447,10 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, imgStream = do_QueryInterface(aEncoder); } } else { - CheckedInt32 requiredBytes = CheckedInt32(aSize.width) * CheckedInt32(aSize.height) * 4; - if (MOZ_UNLIKELY(!requiredBytes.isValid())) { + if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { return NS_ERROR_INVALID_ARG; } - // no context, so we have to encode an empty image // note that if we didn't have a current context, the spec says we're // supposed to just return transparent black pixels of the canvas diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 6633e2175c66..4a2392fa30f1 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5243,8 +5243,13 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32)); } else { + IntSize dtSize = IntSize::Ceil(sw, sh); + if (!Factory::AllowedSurfaceSize(dtSize)) { + aError.Throw(NS_ERROR_FAILURE); + return; + } drawDT = - gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize::Ceil(sw, sh), + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(dtSize, SurfaceFormat::B8G8R8A8); if (!drawDT || !drawDT->IsValid()) { aError.Throw(NS_ERROR_FAILURE); @@ -5270,6 +5275,12 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, return; } RefPtr data = snapshot->GetDataSurface(); + if (!data || !Factory::AllowedSurfaceSize(data->GetSize())) { + gfxCriticalError() << "Unexpected invalid data source surface " << + (data ? data->GetSize() : IntSize(0,0)); + aError.Throw(NS_ERROR_FAILURE); + return; + } DataSourceSurface::MappedSurface rawData; if (NS_WARN_IF(!data->Map(DataSourceSurface::READ, &rawData))) { diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index 0238928386f8..33845264e536 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -109,7 +109,8 @@ ImageBitmapRenderingContext::MatchWithIntrinsicSize() temp->GetSize(), map.GetStride(), temp->GetFormat()); - if (!dt) { + if (!dt || !dt->IsValid()) { + gfxWarning() << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed"; return nullptr; } diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index f51fbac8c7c7..44a93574fbf0 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -3786,9 +3786,13 @@ PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect, // Copy helper surface content to target dt = CreateDrawTargetForSurface(aSurface); } - RefPtr surface = - gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface); - dt->CopySurface(surface, aRect, aRect.TopLeft()); + if (dt && dt->IsValid()) { + RefPtr surface = + gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface); + dt->CopySurface(surface, aRect, aRect.TopLeft()); + } else { + gfxWarning() << "PluginInstanceChild::PaintRectToSurface failure"; + } } } diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index e791c953d16b..92be0f08cf10 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -735,6 +735,11 @@ Factory::PurgeAllCaches() already_AddRefed Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { + if (!AllowedSurfaceSize(aSize)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (Cairo) " << aSize; + return nullptr; + } + RefPtr retVal; #ifdef USE_CAIRO @@ -770,6 +775,11 @@ Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const Int already_AddRefed Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize) { + if (!AllowedSurfaceSize(aSize)) { + gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (CG) " << aSize; + return nullptr; + } + RefPtr retVal; RefPtr newTarget = new DrawTargetCG(); @@ -799,7 +809,7 @@ Factory::CreateWrappingDataSourceSurface(uint8_t *aData, SourceSurfaceDeallocator aDeallocator /* = nullptr */, void* aClosure /* = nullptr */) { - if (aSize.width <= 0 || aSize.height <= 0) { + if (!AllowedSurfaceSize(aSize) || aStride <= 0) { return nullptr; } if (!aDeallocator && aClosure) { @@ -843,7 +853,8 @@ Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize, int32_t aStride, bool aZero) { - if (aStride < aSize.width * BytesPerPixel(aFormat)) { + if (!AllowedSurfaceSize(aSize) || + aStride < aSize.width * BytesPerPixel(aFormat)) { gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat; return nullptr; } diff --git a/gfx/2d/ImageScaling.cpp b/gfx/2d/ImageScaling.cpp index 85e99522bd4f..190b7a7b95a7 100644 --- a/gfx/2d/ImageScaling.cpp +++ b/gfx/2d/ImageScaling.cpp @@ -65,20 +65,20 @@ ImageHalfScaler::ScaleForSize(const IntSize &aSize) return; } - IntSize internalSurfSize; + delete [] mDataStorage; + IntSize internalSurfSize; internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2); internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2); - mStride = internalSurfSize.width * 4; - if (mStride % 16) { - mStride += 16 - (mStride % 16); + size_t bufLen = 0; + mStride = GetAlignedStride<16>(internalSurfSize.width, 4); + if (mStride > 0) { + // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We + // should add tools for this, see bug 751696. + bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); } - delete [] mDataStorage; - // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We - // should add tools for this, see bug 751696. - size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); if (bufLen == 0) { mSize.SizeTo(0, 0); mDataStorage = nullptr; diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h index 27c8c24d6ec5..f4c707198b34 100644 --- a/gfx/2d/SourceSurfaceRawData.h +++ b/gfx/2d/SourceSurfaceRawData.h @@ -80,7 +80,8 @@ private: friend class Factory; // If we have a custom deallocator, the |aData| will be released using the - // custom deallocator and |aClosure| in dtor. + // custom deallocator and |aClosure| in dtor. The assumption is that the + // caller will check for valid size and stride before making this call. void InitWrappingData(unsigned char *aData, const IntSize &aSize, int32_t aStride, diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 9031b52d782d..6f3739d85bb0 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -5,6 +5,7 @@ #include "TextureDIB.h" #include "gfx2DGlue.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/ipc/ProtocolUtils.h" @@ -433,7 +434,7 @@ DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion) RefPtr surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat); - if (!mTextureSource->Update(surf, const_cast(aRegion))) { + if (!surf || !mTextureSource->Update(surf, const_cast(aRegion))) { mTextureSource = nullptr; } @@ -473,14 +474,20 @@ TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) mTextureSource = mCompositor->CreateDataTextureSource(mFlags); } - uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, mSize.width * mSize.height * BytesPerPixel(mFormat)); + uint8_t* data = nullptr; + int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height, BytesPerPixel(mFormat)); + if (totalBytes > 0) { + data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, totalBytes); + } if (data) { RefPtr surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat); - - surf->AddUserData(&kFileMappingKey, data, UnmapFileData); - - if (!mTextureSource->Update(surf, const_cast(aRegion))) { + if (surf) { + surf->AddUserData(&kFileMappingKey, data, UnmapFileData); + if (!mTextureSource->Update(surf, const_cast(aRegion))) { + mTextureSource = nullptr; + } + } else { mTextureSource = nullptr; } } else { diff --git a/gfx/layers/basic/GrallocTextureHostBasic.cpp b/gfx/layers/basic/GrallocTextureHostBasic.cpp index f7d061785fac..a157eecacec4 100644 --- a/gfx/layers/basic/GrallocTextureHostBasic.cpp +++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp @@ -140,9 +140,13 @@ GrallocTextureHostBasic::Lock() mCropSize, mFormat); } - mTextureSource = mCompositor->CreateDataTextureSource(mFlags); - mTextureSource->Update(surf, nullptr); - return true; + if (surf) { + mTextureSource = mCompositor->CreateDataTextureSource(mFlags); + mTextureSource->Update(surf, nullptr); + return true; + } + mMappedBuffer = nullptr; + return false; } bool diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index 53bfbc672a93..3c8299e05f72 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -89,7 +89,9 @@ CanvasLayerComposite::RenderLayer(const IntRect& aClipRect) #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { RefPtr surf = mCompositableHost->GetAsSurface(); - WriteSnapshotToDumpFile(this, surf); + if (surf) { + WriteSnapshotToDumpFile(this, surf); + } } #endif diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index 7f3c7756f89f..bac9f37909d9 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -99,7 +99,9 @@ ImageLayerComposite::RenderLayer(const IntRect& aClipRect) #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { RefPtr surf = mImageHost->GetAsSurface(); - WriteSnapshotToDumpFile(this, surf); + if (surf) { + WriteSnapshotToDumpFile(this, surf); + } } #endif diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 85297e1e740d..d39ba46661aa 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -123,6 +123,10 @@ already_AddRefed gfxAndroidPlatform::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + RefPtr newSurface; newSurface = new gfxImageSurface(aSize, aFormat); diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 05537d68650f..9d7f512f27a7 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -142,6 +142,10 @@ already_AddRefed gfxPlatformGtk::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + RefPtr newSurface; bool needsClear = true; #ifdef MOZ_X11 diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index 29bdd8f6d19d..f578e5fe9f9d 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -116,6 +116,10 @@ already_AddRefed gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + RefPtr newSurface = new gfxQuartzSurface(aSize, aFormat); return newSurface.forget(); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 8e2e37bb2f90..3133cc0886d2 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -585,6 +585,10 @@ already_AddRefed gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { + if (!Factory::AllowedSurfaceSize(aSize)) { + return nullptr; + } + RefPtr surf = nullptr; #ifdef CAIRO_HAS_WIN32_SURFACE From 8d3badbf14b2fdad9ebda19028d9f0031a3601f7 Mon Sep 17 00:00:00 2001 From: cku Date: Sat, 27 Aug 2016 02:49:41 +0800 Subject: [PATCH 037/102] Bug 1289011 - Part 1. Implement ComputeHTMLReferenceRect. r=heycam MozReview-Commit-ID: DvlSF3L8yE0 --HG-- extra : rebase_source : 7f7742737f43133fbbb32019b4131d169310eed8 --- layout/svg/nsCSSClipPathInstance.cpp | 14 ++++++++++++-- layout/svg/nsCSSClipPathInstance.h | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index d34b10196bdc..9ed557df2221 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -62,8 +62,9 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); } -already_AddRefed -nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) + +nsRect +nsCSSClipPathInstance::ComputeHTMLReferenceRect() { nsRect r; // XXXkrit SVG needs to use different boxes. @@ -79,8 +80,17 @@ nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) break; default: // Use the border box r = mTargetFrame->GetRectRelativeToSelf(); + break; } + return r; +} + +already_AddRefed +nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) +{ + nsRect r = ComputeHTMLReferenceRect(); + if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) { // TODO Clip to border-radius/reference box if no shape // was specified. diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h index 5da658cff99c..6e6f02d0fa52 100644 --- a/layout/svg/nsCSSClipPathInstance.h +++ b/layout/svg/nsCSSClipPathInstance.h @@ -47,6 +47,10 @@ private: already_AddRefed CreateClipPathInset(DrawTarget* aDrawTarget, const nsRect& aRefBox); + + + nsRect ComputeHTMLReferenceRect(); + /** * The frame for the element that is currently being clipped. */ From 3db06f80955e133319bd1a19c545ef646480074a Mon Sep 17 00:00:00 2001 From: cku Date: Sun, 28 Aug 2016 02:06:04 +0800 Subject: [PATCH 038/102] Bug 1289011 - Part 2. Implement ComputeSVGReferenceRect. r=heycam MozReview-Commit-ID: G98lmo59AuB --HG-- extra : rebase_source : fa5a8ffaebb8dcc8f908ba5ade2f703c38fec03f --- layout/svg/nsCSSClipPathInstance.cpp | 54 ++++++++++++++++++++++++++-- layout/svg/nsCSSClipPathInstance.h | 1 + 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 9ed557df2221..108aea4c98a0 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -62,12 +62,47 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); } +nsRect +nsCSSClipPathInstance::ComputeSVGReferenceRect() +{ + nsRect r; + + // For SVG elements without associated CSS layout box, the used value for + // content-box, padding-box, border-box and margin-box is fill-box. + switch (mClipPathStyle.GetReferenceBox()) { + + case StyleClipPathGeometryBox::NoBox: + case StyleClipPathGeometryBox::Border: + case StyleClipPathGeometryBox::Content: + case StyleClipPathGeometryBox::Padding: + case StyleClipPathGeometryBox::Margin: + case StyleClipPathGeometryBox::Fill: { + gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, + nsSVGUtils::eBBoxIncludeFill); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } + default:{ + MOZ_ASSERT_UNREACHABLE("unknown StyleClipPathGeometryBox type"); + gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, + nsSVGUtils::eBBoxIncludeFill); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } + } + + return r; +} nsRect nsCSSClipPathInstance::ComputeHTMLReferenceRect() { nsRect r; - // XXXkrit SVG needs to use different boxes. + + // For elements with associated CSS layout box, the used value for fill-box, + // stroke-box and view-box is border-box. switch (mClipPathStyle.GetReferenceBox()) { case StyleClipPathGeometryBox::Content: r = mTargetFrame->GetContentRectRelativeToSelf(); @@ -78,7 +113,15 @@ nsCSSClipPathInstance::ComputeHTMLReferenceRect() case StyleClipPathGeometryBox::Margin: r = mTargetFrame->GetMarginRectRelativeToSelf(); break; - default: // Use the border box + case StyleClipPathGeometryBox::NoBox: + case StyleClipPathGeometryBox::Border: + case StyleClipPathGeometryBox::Fill: + case StyleClipPathGeometryBox::Stroke: + case StyleClipPathGeometryBox::View: + r = mTargetFrame->GetRectRelativeToSelf(); + break; + default: + MOZ_ASSERT_UNREACHABLE("unknown StyleClipPathGeometryBox type"); r = mTargetFrame->GetRectRelativeToSelf(); break; } @@ -89,7 +132,12 @@ nsCSSClipPathInstance::ComputeHTMLReferenceRect() already_AddRefed nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) { - nsRect r = ComputeHTMLReferenceRect(); + // We use ComputeSVGReferenceRect for all SVG elements, except + // element, which does have an associated CSS layout box. In this case we + // should still use ComputeHTMLReferenceRect for region computing. + nsRect r = mTargetFrame->IsFrameOfType(nsIFrame::eSVG) && + (mTargetFrame->GetType() != nsGkAtoms::svgOuterSVGFrame) + ? ComputeSVGReferenceRect() : ComputeHTMLReferenceRect(); if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) { // TODO Clip to border-radius/reference box if no shape diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h index 6e6f02d0fa52..3b0724dbd098 100644 --- a/layout/svg/nsCSSClipPathInstance.h +++ b/layout/svg/nsCSSClipPathInstance.h @@ -50,6 +50,7 @@ private: nsRect ComputeHTMLReferenceRect(); + nsRect ComputeSVGReferenceRect(); /** * The frame for the element that is currently being clipped. From 235e174b8f8623cb0e65bea96b9a148fa10df3f5 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 30 Aug 2016 10:27:30 +0800 Subject: [PATCH 039/102] Bug 1289011 - Part 3. Handle view-box. r=heycam MozReview-Commit-ID: 3GsivsTYPYK --HG-- extra : rebase_source : da1fff2bb43f85b8d6e55ce4d490f1d872ddea45 --- dom/svg/SVGSVGElement.cpp | 2 +- dom/svg/SVGSVGElement.h | 2 +- layout/svg/nsCSSClipPathInstance.cpp | 30 ++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp index b347cbbecd6c..90c3c3fff372 100644 --- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -1022,7 +1022,7 @@ SVGSVGElement::GetEnumInfo() ArrayLength(sEnumInfo)); } -nsSVGViewBox * +nsSVGViewBox* SVGSVGElement::GetViewBox() { return &mViewBox; diff --git a/dom/svg/SVGSVGElement.h b/dom/svg/SVGSVGElement.h index f0a056e69fc1..da08ad770c65 100644 --- a/dom/svg/SVGSVGElement.h +++ b/dom/svg/SVGSVGElement.h @@ -288,6 +288,7 @@ public: already_AddRefed PreserveAspectRatio(); uint16_t ZoomAndPan(); void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv); + virtual nsSVGViewBox* GetViewBox() override; private: // nsSVGElement overrides @@ -378,7 +379,6 @@ private: static nsSVGEnumMapping sZoomAndPanMap[]; static EnumInfo sEnumInfo[1]; - virtual nsSVGViewBox *GetViewBox() override; virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; nsSVGViewBox mViewBox; diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 108aea4c98a0..6b733fe65e47 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -65,12 +65,42 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, nsRect nsCSSClipPathInstance::ComputeSVGReferenceRect() { + MOZ_ASSERT(mTargetFrame->GetContent()->IsSVGElement()); nsRect r; // For SVG elements without associated CSS layout box, the used value for // content-box, padding-box, border-box and margin-box is fill-box. switch (mClipPathStyle.GetReferenceBox()) { + case StyleClipPathGeometryBox::View: { + nsIContent* content = mTargetFrame->GetContent(); + nsSVGElement* element = static_cast(content); + SVGSVGElement* svgElement = element->GetCtx(); + MOZ_ASSERT(svgElement); + if (svgElement && svgElement->HasViewBoxRect()) { + // If a ‘viewBox‘ attribute is specified for the SVG viewport creating + // element: + // 1. The reference box is positioned at the origin of the coordinate + // system established by the ‘viewBox‘ attribute. + // 2. The dimension of the reference box is set to the width and height + // values of the ‘viewBox‘ attribute. + nsSVGViewBox* viewBox = svgElement->GetViewBox(); + const nsSVGViewBoxRect& value = viewBox->GetAnimValue(); + r = nsRect(nsPresContext::CSSPixelsToAppUnits(value.x), + nsPresContext::CSSPixelsToAppUnits(value.y), + nsPresContext::CSSPixelsToAppUnits(value.width), + nsPresContext::CSSPixelsToAppUnits(value.height)); + } else { + // No viewBox is specified, uses the nearest SVG viewport as reference + // box. + svgFloatSize viewportSize = svgElement->GetViewportSize(); + r = nsRect(0, 0, + nsPresContext::CSSPixelsToAppUnits(viewportSize.width), + nsPresContext::CSSPixelsToAppUnits(viewportSize.height)); + } + + break; + } case StyleClipPathGeometryBox::NoBox: case StyleClipPathGeometryBox::Border: case StyleClipPathGeometryBox::Content: From f2373417b486d0adc611cff1a99d0e2dbe25d8ec Mon Sep 17 00:00:00 2001 From: cku Date: Sun, 28 Aug 2016 02:36:20 +0800 Subject: [PATCH 040/102] Bug 1289011 - Part 4. Handle stroke-box. r=heycam MozReview-Commit-ID: BImaXBGds6l --HG-- extra : rebase_source : 58a95532c74c007bbfb7b3ea1b1f83a534be2210 --- layout/svg/nsCSSClipPathInstance.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 6b733fe65e47..8e266fbb1f6a 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -71,6 +71,16 @@ nsCSSClipPathInstance::ComputeSVGReferenceRect() // For SVG elements without associated CSS layout box, the used value for // content-box, padding-box, border-box and margin-box is fill-box. switch (mClipPathStyle.GetReferenceBox()) { + case StyleClipPathGeometryBox::Stroke: { + // XXX Bug 1299876 + // The size of srtoke-box is not correct if this graphic element has + // specific stroke-linejoin or stroke-linecap. + gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, + nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeStroke); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } case StyleClipPathGeometryBox::View: { nsIContent* content = mTargetFrame->GetContent(); nsSVGElement* element = static_cast(content); From 6363ec70cdd86127b306b4197a0dfd9353fe622d Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 30 Aug 2016 00:44:43 +0800 Subject: [PATCH 041/102] Bug 1289011 - Part 5. shape-box reftest for elements with associated CSS layout box. r=heycam MozReview-Commit-ID: GnDA3cqO3QL --HG-- extra : rebase_source : a864a8ab5f42033b39c3e595c1baaa7297ed8fe8 --- .../masking/clip-path-borderBox-1a.html | 28 +++++++++++++++++++ .../masking/clip-path-contentBox-1a.html | 27 ++++++++++++++++++ .../masking/clip-path-geometryBox-1-ref.html | 14 ++++++++++ .../masking/clip-path-geometryBox-2-ref.html | 14 ++++++++++ .../masking/clip-path-geometryBox-2a.html | 27 ++++++++++++++++++ .../masking/clip-path-geometryBox-2b.html | 27 ++++++++++++++++++ .../masking/clip-path-geometryBox-2c.html | 27 ++++++++++++++++++ .../masking/clip-path-geometryBox-2d.html | 27 ++++++++++++++++++ .../masking/clip-path-paddingBox-1a.html | 27 ++++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 11 ++++++++ 10 files changed, 229 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-1-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2-ref.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2d.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1a.html diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1a.html new file mode 100644 index 000000000000..ee48c8001527 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1a.html @@ -0,0 +1,28 @@ + + + + + CSS Masking: clip-path: clip path border-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1a.html new file mode 100644 index 000000000000..fcbae31ac9fd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1a.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path content-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-1-ref.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-1-ref.html new file mode 100644 index 000000000000..837bf0b06b3e --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-1-ref.html @@ -0,0 +1,14 @@ + + + + + CSS Masking: clip-path: clip path geometry box + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2-ref.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2-ref.html new file mode 100644 index 000000000000..b9ee9ab63b09 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2-ref.html @@ -0,0 +1,14 @@ + + + + + CSS Masking: clip-path: clip path geometry box + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2a.html new file mode 100644 index 000000000000..b071ffe07774 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2a.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path content-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2b.html new file mode 100644 index 000000000000..d292bdfc5535 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2b.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path padding-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2c.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2c.html new file mode 100644 index 000000000000..c2c44ec2dfc6 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2c.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path border-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2d.html b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2d.html new file mode 100644 index 000000000000..7344b827e1ad --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-geometryBox-2d.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path margin-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1a.html new file mode 100644 index 000000000000..4dc5e1714e38 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1a.html @@ -0,0 +1,27 @@ + + + + + CSS Masking: clip-path: clip path padding-box + + + + + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 1a1ea0a4c1ac..4e89e1aaeac7 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -77,3 +77,14 @@ fails == mask-origin-2.html mask-origin-2-ref.html # bug 1260094 == mask-size-percent-length.html mask-size-percent-percent-ref.html == mask-size-percent-percent.html mask-size-percent-percent-ref.html == mask-size-percent-percent-stretch.html mask-size-percent-percent-stretch-ref.html + +default-preferences pref(layout.css.clip-path-shapes.enabled,true) + +== clip-path-contentBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-paddingBox-1a.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-geometryBox-2a.html clip-path-geometryBox-2-ref.html +== clip-path-geometryBox-2b.html clip-path-geometryBox-2-ref.html +== clip-path-geometryBox-2c.html clip-path-geometryBox-2-ref.html +== clip-path-geometryBox-2d.html clip-path-geometryBox-2-ref.html + From f4ff1b6e18947614fad7a9245d80837c654e20e8 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 30 Aug 2016 10:28:27 +0800 Subject: [PATCH 042/102] Bug 1289011 - Part 6. shape-box reftest for SVG elements without associated CSS layout box. r=heycam MozReview-Commit-ID: LDQsymuMWqd --HG-- extra : rebase_source : 361d77140b915a4af4c034b8a54900d6a75131d7 --- .../masking/clip-path-borderBox-1b.html | 18 ++++++++++++++++++ .../masking/clip-path-contentBox-1b.html | 18 ++++++++++++++++++ .../masking/clip-path-marginBox-1a.html | 18 ++++++++++++++++++ .../masking/clip-path-paddingBox-1b.html | 18 ++++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 4 ++++ 5 files changed, 76 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1b.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-marginBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1b.html diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1b.html new file mode 100644 index 000000000000..2c18099ababb --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1b.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path border-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1b.html new file mode 100644 index 000000000000..eab70f987e4b --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1b.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path content-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-marginBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-marginBox-1a.html new file mode 100644 index 000000000000..6634c93f64bf --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-marginBox-1a.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path margin-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1b.html new file mode 100644 index 000000000000..717a6bfe12b3 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1b.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path padding-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 4e89e1aaeac7..6b92d16aa06e 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -81,8 +81,12 @@ fails == mask-origin-2.html mask-origin-2-ref.html # bug 1260094 default-preferences pref(layout.css.clip-path-shapes.enabled,true) == clip-path-contentBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-contentBox-1b.html clip-path-geometryBox-1-ref.html == clip-path-paddingBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-paddingBox-1b.html clip-path-geometryBox-1-ref.html fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-borderBox-1b.html clip-path-geometryBox-1-ref.html +== clip-path-marginBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-geometryBox-2a.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2b.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2c.html clip-path-geometryBox-2-ref.html From fe9175a1c25f315f271dd7688e88658f72b482b8 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 30 Aug 2016 10:33:34 +0800 Subject: [PATCH 043/102] Bug 1289011 - Part 7. view-box reftest. r=heycam MozReview-Commit-ID: HkBCSdhIyqr --HG-- extra : rebase_source : 266accb09502105cf78997b66f8f6fdf031ad8f7 --- .../masking/clip-path-viewBox-1a.html | 18 ++++++++++++++++++ .../masking/clip-path-viewBox-1b.html | 18 ++++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 2 ++ 3 files changed, 38 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1b.html diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1a.html new file mode 100644 index 000000000000..f09b36f48700 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1a.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path view-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1b.html new file mode 100644 index 000000000000..a80004f55eb9 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1b.html @@ -0,0 +1,18 @@ + + + + + CSS Masking: clip-path: clip path view-box with viewbox + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 6b92d16aa06e..7f6f6e8fd780 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -87,6 +87,8 @@ default-preferences pref(layout.css.clip-path-shapes.enabled,true) fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-borderBox-1b.html clip-path-geometryBox-1-ref.html == clip-path-marginBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-viewBox-1a.html clip-path-geometryBox-1-ref.html +== clip-path-viewBox-1b.html clip-path-geometryBox-1-ref.html == clip-path-geometryBox-2a.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2b.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2c.html clip-path-geometryBox-2-ref.html From e4713e9c1d6997a1a8ed026b242d5fa10659cfbf Mon Sep 17 00:00:00 2001 From: cku Date: Thu, 1 Sep 2016 17:11:59 +0800 Subject: [PATCH 044/102] Bug 1289011 - Part 8. shape-box reftest for SVG outter element. r=heycam MozReview-Commit-ID: 651vKL0Q1YF --HG-- extra : rebase_source : 6e4d3d63b069f18bb1bdd2199882fa06d4bb8b9f --- .../masking/clip-path-borderBox-1c.html | 30 ++++++++++++++++++ .../masking/clip-path-contentBox-1c.html | 28 +++++++++++++++++ .../masking/clip-path-fillBox-1a.html | 31 +++++++++++++++++++ .../masking/clip-path-paddingBox-1c.html | 29 +++++++++++++++++ .../masking/clip-path-strokeBox-1a.html | 31 +++++++++++++++++++ .../masking/clip-path-viewBox-1c.html | 31 +++++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 6 ++++ 7 files changed, 186 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-fillBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1c.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1a.html create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1c.html diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1c.html b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1c.html new file mode 100644 index 000000000000..e5fb81e169d9 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-borderBox-1c.html @@ -0,0 +1,30 @@ + + + + + CSS Masking: clip-path: clip path border-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1c.html b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1c.html new file mode 100644 index 000000000000..bddc3a536993 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-contentBox-1c.html @@ -0,0 +1,28 @@ + + + + + CSS Masking: clip-path: clip path content-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-fillBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-fillBox-1a.html new file mode 100644 index 000000000000..ca850ec8b9af --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-fillBox-1a.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: clip-path: clip path fill-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1c.html b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1c.html new file mode 100644 index 000000000000..781fa6569d8e --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-paddingBox-1c.html @@ -0,0 +1,29 @@ + + + + + CSS Masking: clip-path: clip path padding-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1a.html b/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1a.html new file mode 100644 index 000000000000..4ab2501d69ba --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1a.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: clip-path: clip path stroke-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1c.html b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1c.html new file mode 100644 index 000000000000..3a9d12615dd7 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-viewBox-1c.html @@ -0,0 +1,31 @@ + + + + + CSS Masking: clip-path: clip path view-box + + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 7f6f6e8fd780..1ae57895aaa0 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -82,13 +82,19 @@ default-preferences pref(layout.css.clip-path-shapes.enabled,true) == clip-path-contentBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-contentBox-1b.html clip-path-geometryBox-1-ref.html +== clip-path-contentBox-1c.html clip-path-geometryBox-1-ref.html == clip-path-paddingBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-paddingBox-1b.html clip-path-geometryBox-1-ref.html +== clip-path-paddingBox-1c.html clip-path-geometryBox-1-ref.html fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-borderBox-1b.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html == clip-path-marginBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-viewBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-viewBox-1b.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html == clip-path-geometryBox-2a.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2b.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2c.html clip-path-geometryBox-2-ref.html From 01253cd8d0f409deed7a75b1978de210b55d6848 Mon Sep 17 00:00:00 2001 From: cku Date: Fri, 2 Sep 2016 01:20:22 +0800 Subject: [PATCH 045/102] Bug 1289011 - Part 9. stroke-box reftest. r=heycam MozReview-Commit-ID: 3LJsppqLKMv --HG-- extra : rebase_source : 7e4b2219380aa145bf23bd5fa64b3f2ce3de39f3 --- .../masking/clip-path-strokeBox-1b.html | 17 +++++++++++++++++ .../w3c-css/submitted/masking/reftest.list | 1 + 2 files changed, 18 insertions(+) create mode 100644 layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1b.html diff --git a/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1b.html b/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1b.html new file mode 100644 index 000000000000..ba81b5df7363 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/masking/clip-path-strokeBox-1b.html @@ -0,0 +1,17 @@ + + + + + CSS Masking: clip-path: clip path stroke-box + + + + + + + + + + + + diff --git a/layout/reftests/w3c-css/submitted/masking/reftest.list b/layout/reftests/w3c-css/submitted/masking/reftest.list index 1ae57895aaa0..4541b4997064 100644 --- a/layout/reftests/w3c-css/submitted/masking/reftest.list +++ b/layout/reftests/w3c-css/submitted/masking/reftest.list @@ -95,6 +95,7 @@ fuzzy(64,311) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html fuzzy(64,311) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html fuzzy(64,311) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html fuzzy(64,311) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html +fuzzy(64,311) == clip-path-strokeBox-1b.html clip-path-geometryBox-1-ref.html == clip-path-geometryBox-2a.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2b.html clip-path-geometryBox-2-ref.html == clip-path-geometryBox-2c.html clip-path-geometryBox-2-ref.html From a1bd244c061db752ecef2882609171a84821dcf9 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Thu, 1 Sep 2016 15:33:06 -0400 Subject: [PATCH 046/102] Bug 1299882 - Intermittent tests | Timed out while polling clipboard for pasted data. r=RyanVM MozReview-Commit-ID: 3S0vvyix6JN --HG-- extra : rebase_source : f9ceb0b813adc6dca9a1f8323446ade4dd2c1a6e --- devtools/client/inspector/test/browser.ini | 1 + dom/browser-element/mochitest/mochitest-oop.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/devtools/client/inspector/test/browser.ini b/devtools/client/inspector/test/browser.ini index b3cc6dc0d29c..543f5eb79cc6 100644 --- a/devtools/client/inspector/test/browser.ini +++ b/devtools/client/inspector/test/browser.ini @@ -147,6 +147,7 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo [browser_inspector_search-08.js] [browser_inspector_search-clear.js] [browser_inspector_search-filter_context-menu.js] +subsuite = clipboard [browser_inspector_search_keyboard_trap.js] [browser_inspector_search-label.js] [browser_inspector_search-reserved.js] diff --git a/dom/browser-element/mochitest/mochitest-oop.ini b/dom/browser-element/mochitest/mochitest-oop.ini index 147d0b72daf3..c869a79e8789 100644 --- a/dom/browser-element/mochitest/mochitest-oop.ini +++ b/dom/browser-element/mochitest/mochitest-oop.ini @@ -45,6 +45,7 @@ skip-if = (toolkit == 'gonk' && !debug) [test_browserElement_oop_Close.html] [test_browserElement_oop_CookiesNotThirdParty.html] [test_browserElement_oop_CopyPaste.html] +subsuite = clipboard [test_browserElement_oop_DOMRequestError.html] disabled = Disabling some OOP tests for WebIDL scope changes [test_browserElement_oop_DataURI.html] From 9ac4909e0ac1cb0fa1e39b870a8d28d616520774 Mon Sep 17 00:00:00 2001 From: cku Date: Mon, 15 Aug 2016 23:04:50 +0800 Subject: [PATCH 047/102] Bug 1295094 - Part 1. Simplify the logic in nsSVGIntegrationUtils::PaintFramesWithEffects. r=mstange MozReview-Commit-ID: HbQIrhtChCC --HG-- extra : rebase_source : 3bea6d260495c87291142249d9dc12571ee62899 --- layout/svg/nsSVGIntegrationUtils.cpp | 88 +++++++++++++--------------- 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 2f09a6b54e80..e6fc88c1e887 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -718,19 +718,46 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) bool shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0]; #endif + bool shouldGenerateClipMaskLayer = clipPathFrame && !isTrivialClip; + bool shouldApplyClipPath = clipPathFrame && isTrivialClip; + bool shouldApplyBasicShape = !clipPathFrame && svgReset->HasClipPath(); + MOZ_ASSERT_IF(shouldGenerateClipMaskLayer, + !shouldApplyClipPath && !shouldApplyBasicShape); + // These are used if we require a temporary surface for a custom blend mode. RefPtr target = &aParams.ctx; IntPoint targetOffset; - bool complexEffects = false; + if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + // Create a temporary context to draw to so we can blend it back with + // another operator. + gfxRect clipRect; + { + gfxContextMatrixAutoSaveRestore matRestore(&context); + + context.SetMatrix(gfxMatrix()); + clipRect = context.GetClipExtents(); + } + + IntRect drawRect = RoundedOut(ToRect(clipRect)); + + RefPtr targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); + if (!targetDT || !targetDT->IsValid()) { + return DrawResult::TEMPORARY_ERROR; + } + target = gfxContext::CreateOrNull(targetDT); + MOZ_ASSERT(target); // already checked the draw target above + target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); + targetOffset = drawRect.TopLeft(); + } + DrawResult result = DrawResult::SUCCESS; + bool shouldGenerateMask = (opacity != 1.0f || shouldGenerateClipMaskLayer || + shouldGenerateMaskLayer); + /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ - if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) - || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL - || shouldGenerateMaskLayer) { - complexEffects = true; - + if (shouldGenerateMask) { context.Save(); nsRect clipRect = frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; @@ -752,31 +779,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) return result; } - if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { - // Create a temporary context to draw to so we can blend it back with - // another operator. - gfxRect clipRect; - { - gfxContextMatrixAutoSaveRestore matRestore(&context); - - context.SetMatrix(gfxMatrix()); - clipRect = context.GetClipExtents(); - } - - IntRect drawRect = RoundedOut(ToRect(clipRect)); - - RefPtr targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); - if (!targetDT || !targetDT->IsValid()) { - context.Restore(); - return result; - } - target = gfxContext::CreateOrNull(targetDT); - MOZ_ASSERT(target); // already checked the draw target above - target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); - targetOffset = drawRect.TopLeft(); - } - - if (clipPathFrame && !isTrivialClip) { + if (shouldGenerateClipMaskLayer) { Matrix clippedMaskTransform; RefPtr clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, @@ -789,19 +792,16 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) } } - if (opacity != 1.0f || shouldGenerateMaskLayer || - (clipPathFrame && !isTrivialClip)) { - target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); - } + target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ - if (clipPathFrame && isTrivialClip) { + if (shouldApplyClipPath) { context.Save(); clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); - } else if (!clipPathFrame && svgReset->HasClipPath()) { + } else if (shouldApplyBasicShape) { context.Save(); nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame); } @@ -825,19 +825,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) basic->SetTarget(oldCtx); } - if ((clipPathFrame && isTrivialClip) || - (!clipPathFrame && svgReset->HasClipPath())) { + if (shouldApplyClipPath || shouldApplyBasicShape) { context.Restore(); } - /* No more effects, we're done. */ - if (!complexEffects) { - return result; - } - - if (opacity != 1.0f || shouldGenerateMaskLayer || - (clipPathFrame && !isTrivialClip)) { + if (shouldGenerateMask) { target->PopGroupAndBlend(); + context.Restore(); } if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { @@ -845,13 +839,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) target = nullptr; RefPtr targetSurf = targetDT->Snapshot(); + gfxContextAutoSaveRestore save(&context); context.SetMatrix(gfxMatrix()); // This will be restored right after. RefPtr pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); context.SetPattern(pattern); context.Paint(); } - context.Restore(); return result; } From 4dd64993c5e2be2b439509da260e8dda88b48f21 Mon Sep 17 00:00:00 2001 From: cku Date: Mon, 15 Aug 2016 23:49:21 +0800 Subject: [PATCH 048/102] Bug 1295094 - Part 2. Implement ComputeClipExtsInDeviceSpace r=mstange MozReview-Commit-ID: 8BFy0y83MeG --HG-- extra : rebase_source : 59b1fa5eabcbf64b32ef90821a5458e12501335b --- layout/svg/nsSVGIntegrationUtils.cpp | 37 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index e6fc88c1e887..137b0e691e31 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -410,6 +410,22 @@ private: nsPoint mOffset; }; +static IntRect +ComputeClipExtsInDeviceSpace(gfxContext& aCtx) +{ + gfxContextMatrixAutoSaveRestore matRestore(&aCtx); + + // Get the clip extents in device space. + aCtx.SetMatrix(gfxMatrix()); + gfxRect clippedFrameSurfaceRect = aCtx.GetClipExtents(); + clippedFrameSurfaceRect.RoundOut(); + + IntRect result; + ToRect(clippedFrameSurfaceRect).ToIntRect(&result); + return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result + : IntRect(); +} + static IntRect ComputeMaskGeometry(const nsSVGIntegrationUtils::PaintFramesParams& aParams, const nsStyleSVGReset *svgReset, @@ -463,17 +479,10 @@ ComputeMaskGeometry(const nsSVGIntegrationUtils::PaintFramesParams& aParams, ctx.Clip(maskInUserSpace); } - // Get the clip extents in device space. - ctx.SetMatrix(gfxMatrix()); - gfxRect clippedFrameSurfaceRect = ctx.GetClipExtents(); - clippedFrameSurfaceRect.RoundOut(); - + IntRect result = ComputeClipExtsInDeviceSpace(ctx); ctx.Restore(); - IntRect result; - ToRect(clippedFrameSurfaceRect).ToIntRect(&result); - return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result - : IntRect(); + return result; } static DrawResult @@ -731,15 +740,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. - gfxRect clipRect; - { - gfxContextMatrixAutoSaveRestore matRestore(&context); - - context.SetMatrix(gfxMatrix()); - clipRect = context.GetClipExtents(); - } - - IntRect drawRect = RoundedOut(ToRect(clipRect)); + IntRect drawRect = ComputeClipExtsInDeviceSpace(context); RefPtr targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); if (!targetDT || !targetDT->IsValid()) { From 33f7856d090dd7edf1cddeed50c13c3d7058ebcc Mon Sep 17 00:00:00 2001 From: cku Date: Mon, 15 Aug 2016 23:57:45 +0800 Subject: [PATCH 049/102] Bug 1295094 - Part 3. Implement ComputeOpacity r=mstange MozReview-Commit-ID: ILdIFIvXFyi --HG-- extra : rebase_source : 57f7c2a7594c487dbb9adca84146d19d01bcb33d --- layout/svg/nsSVGIntegrationUtils.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 137b0e691e31..b129aaa28566 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -604,6 +604,25 @@ GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams, return DrawResult::SUCCESS; } +static float +ComputeOpacity(const nsSVGIntegrationUtils::PaintFramesParams& aParams) +{ + nsIFrame* frame = aParams.frame; + + MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || + !aParams.callerPaintsOpacity, + "How can we be optimizing the opacity into the svg as well as having the caller paint it?"); + + float opacity = frame->StyleEffects()->mOpacity; + + if (opacity != 1.0f && + (nsSVGUtils::CanOptimizeOpacity(frame) || aParams.callerPaintsOpacity)) { + return 1.0f; + } + + return opacity; +} + DrawResult nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) { @@ -641,17 +660,10 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) } } - float opacity = frame->StyleEffects()->mOpacity; - if (opacity != 1.0f && - (nsSVGUtils::CanOptimizeOpacity(frame) || - aParams.callerPaintsOpacity)) { - opacity = 1.0f; - } + float opacity = ComputeOpacity(aParams); if (opacity == 0.0f) { return DrawResult::SUCCESS; } - MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.callerPaintsOpacity, - "How can we be optimizing the opacity into the svg as well as having the caller paint it?"); /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ From 1843b41cceb535b0f33e9bd8b687a4d5bd4e7584 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 00:05:10 +0800 Subject: [PATCH 050/102] Bug 1295094 - Part 4. Implement ValidateSVGFrame r=mstange MozReview-Commit-ID: GdO647a09Xo --HG-- extra : rebase_source : cda0319ed0728fb705658055c9386eb108debef8 --- layout/svg/nsSVGIntegrationUtils.cpp | 41 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index b129aaa28566..cf92cbacf8c3 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -623,8 +623,9 @@ ComputeOpacity(const nsSVGIntegrationUtils::PaintFramesParams& aParams) return opacity; } -DrawResult -nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) +static bool +ValidateSVGFrame(const nsSVGIntegrationUtils::PaintFramesParams& aParams, + bool aHasSVGLayout, DrawResult* aResult) { #ifdef DEBUG NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || @@ -633,6 +634,28 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) "Should not use nsSVGIntegrationUtils on this SVG frame"); #endif + nsIFrame* frame = aParams.frame; + const nsIContent* content = frame->GetContent(); + if (aHasSVGLayout) { + nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame); + if (!svgChildFrame || !frame->GetContent()->IsSVGElement()) { + NS_ASSERTION(false, "why?"); + *aResult = DrawResult::BAD_ARGS; + return false; + } + if (!static_cast(content)->HasValidDimensions()) { + // The SVG spec says not to draw _anything_ + *aResult = DrawResult::SUCCESS; + return false; + } + } + + return true; +} + +DrawResult +nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) +{ /* SVG defines the following rendering model: * * 1. Render geometry @@ -647,17 +670,10 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) * + Merge opacity and masking if both used together. */ nsIFrame* frame = aParams.frame; - const nsIContent* content = frame->GetContent(); + DrawResult result = DrawResult::SUCCESS; bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); - if (hasSVGLayout) { - nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame); - if (!svgChildFrame || !frame->GetContent()->IsSVGElement()) { - NS_ASSERTION(false, "why?"); - return DrawResult::BAD_ARGS; - } - if (!static_cast(content)->HasValidDimensions()) { - return DrawResult::SUCCESS; // The SVG spec says not to draw _anything_ - } + if (!ValidateSVGFrame(aParams, hasSVGLayout, &result)) { + return result; } float opacity = ComputeOpacity(aParams); @@ -764,7 +780,6 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) targetOffset = drawRect.TopLeft(); } - DrawResult result = DrawResult::SUCCESS; bool shouldGenerateMask = (opacity != 1.0f || shouldGenerateClipMaskLayer || shouldGenerateMaskLayer); From 8afbe7e2c0be8e37a8946dea823e43af58d81ae8 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 00:33:35 +0800 Subject: [PATCH 051/102] Bug 1295094 - Part 5. Implement SetupContextMatrix r=mstange MozReview-Commit-ID: AOPyRwUSr0A --HG-- extra : rebase_source : 8f9eb6aad0ac0d4399000e7c9d0a548bb5df9a00 --- layout/svg/nsSVGIntegrationUtils.cpp | 98 +++++++++++++++++----------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index cf92cbacf8c3..f060c199a5f8 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -653,6 +653,56 @@ ValidateSVGFrame(const nsSVGIntegrationUtils::PaintFramesParams& aParams, return true; } +static void +SetupContextMatrix(const nsSVGIntegrationUtils::PaintFramesParams& aParams, + nsPoint& aOffsetToBoundingBox, + nsPoint& aToUserSpace, + nsPoint& aOffsetToUserSpace) +{ + nsIFrame* frame = aParams.frame; + nsIFrame* firstFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); + + nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); + aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset; + if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { + /* Snap the offset if the reference frame is not a SVG frame, + * since other frames will be snapped to pixel when rendering. */ + aOffsetToBoundingBox = nsPoint( + frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x), + frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y)); + } + + // After applying only "aOffsetToBoundingBox", aCtx would have its origin at + // the top left corner of frame's bounding box (over all continuations). + // However, SVG painting needs the origin to be located at the origin of the + // SVG frame's "user space", i.e. the space in which, for example, the + // frame's BBox lives. + // SVG geometry frames and foreignObject frames apply their own offsets, so + // their position is relative to their user space. So for these frame types, + // if we want aCtx to be in user space, we first need to subtract the + // frame's position so that SVG painting can later add it again and the + // frame is painted in the right place. + + gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(frame); + aToUserSpace = + nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), + nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); + + aOffsetToUserSpace = aOffsetToBoundingBox - aToUserSpace; + +#ifdef DEBUG + bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + NS_ASSERTION(hasSVGLayout || aOffsetToBoundingBox == aOffsetToUserSpace, + "For non-SVG frames there shouldn't be any additional offset"); +#endif + + gfxPoint devPixelOffsetToUserSpace = + nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace, + frame->PresContext()->AppUnitsPerDevPixel()); + aParams.ctx.SetMatrix(aParams.ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); +} + DrawResult nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) { @@ -681,6 +731,14 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) return DrawResult::SUCCESS; } + gfxContext& context = aParams.ctx; + gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); + nsPoint offsetToBoundingBox; + nsPoint toUserSpace; + nsPoint offsetToUserSpace; + SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace, + offsetToUserSpace); + /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ nsIFrame* firstFrame = @@ -692,46 +750,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; - gfxContext& context = aParams.ctx; - DrawTarget* drawTarget = context.GetDrawTarget(); - gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); - - nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); - nsPoint offsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset; - if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { - /* Snap the offset if the reference frame is not a SVG frame, - * since other frames will be snapped to pixel when rendering. */ - offsetToBoundingBox = nsPoint( - frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), - frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); - } - - // After applying only "offsetToBoundingBox", aCtx would have its origin at - // the top left corner of frame's bounding box (over all continuations). - // However, SVG painting needs the origin to be located at the origin of the - // SVG frame's "user space", i.e. the space in which, for example, the - // frame's BBox lives. - // SVG geometry frames and foreignObject frames apply their own offsets, so - // their position is relative to their user space. So for these frame types, - // if we want aCtx to be in user space, we first need to subtract the - // frame's position so that SVG painting can later add it again and the - // frame is painted in the right place. - - gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(frame); - nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), - nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); - nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace; - - NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace, - "For non-SVG frames there shouldn't be any additional offset"); - - gfxPoint devPixelOffsetToUserSpace = - nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, - frame->PresContext()->AppUnitsPerDevPixel()); - context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame); - const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); nsTArray maskFrames = effectProperties.GetMaskFrames(); @@ -791,7 +811,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; context.Clip(NSRectToSnappedRect(clipRect, frame->PresContext()->AppUnitsPerDevPixel(), - *drawTarget)); + *context.GetDrawTarget())); Matrix maskTransform; RefPtr maskSurface; if (shouldGenerateMaskLayer) { From 9bf4d9c5cdff9cc2a07430a6e8b705237e8f5806 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 01:28:07 +0800 Subject: [PATCH 052/102] Bug 1295094 - Part 6. Implement CreateBlendTarget and BlendToTarget. r=mstange MozReview-Commit-ID: 3C3hlje0QEw --HG-- extra : rebase_source : aaf3527cd3e3fcf12efa20d91a5233c9828d2f1d --- layout/svg/nsSVGIntegrationUtils.cpp | 77 +++++++++++++++++++--------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index f060c199a5f8..61330393f9fe 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -703,6 +703,49 @@ SetupContextMatrix(const nsSVGIntegrationUtils::PaintFramesParams& aParams, aParams.ctx.SetMatrix(aParams.ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); } +static already_AddRefed +CreateBlendTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams, + IntPoint& aTargetOffset) +{ + MOZ_ASSERT(aParams.frame->StyleEffects()->mMixBlendMode != + NS_STYLE_BLEND_NORMAL); + + // Create a temporary context to draw to so we can blend it back with + // another operator. + IntRect drawRect = ComputeClipExtsInDeviceSpace(aParams.ctx); + + RefPtr targetDT = aParams.ctx.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); + if (!targetDT || !targetDT->IsValid()) { + return nullptr; + } + + RefPtr target = gfxContext::CreateOrNull(targetDT); + MOZ_ASSERT(target); // already checked the draw target above + target->SetMatrix(aParams.ctx.CurrentMatrix() * + gfxMatrix::Translation(-drawRect.TopLeft())); + aTargetOffset = drawRect.TopLeft(); + + return target.forget(); +} + +static void +BlendToTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams, + gfxContext* aTarget, const IntPoint& aTargetOffset) +{ + MOZ_ASSERT(aParams.frame->StyleEffects()->mMixBlendMode != + NS_STYLE_BLEND_NORMAL); + + RefPtr targetDT = aTarget->GetDrawTarget(); + RefPtr targetSurf = targetDT->Snapshot(); + + gfxContext& context = aParams.ctx; + gfxContextAutoSaveRestore save(&context); + context.SetMatrix(gfxMatrix()); // This will be restored right after. + RefPtr pattern = new gfxPattern(targetSurf, Matrix::Translation(aTargetOffset.x, aTargetOffset.y)); + context.SetPattern(pattern); + context.Paint(); +} + DrawResult nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) { @@ -782,22 +825,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) !shouldApplyClipPath && !shouldApplyBasicShape); // These are used if we require a temporary surface for a custom blend mode. - RefPtr target = &aParams.ctx; IntPoint targetOffset; - - if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { - // Create a temporary context to draw to so we can blend it back with - // another operator. - IntRect drawRect = ComputeClipExtsInDeviceSpace(context); - - RefPtr targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); - if (!targetDT || !targetDT->IsValid()) { - return DrawResult::TEMPORARY_ERROR; - } - target = gfxContext::CreateOrNull(targetDT); - MOZ_ASSERT(target); // already checked the draw target above - target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); - targetOffset = drawRect.TopLeft(); + RefPtr target = + (aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL) + ? RefPtr(&aParams.ctx).forget() + : CreateBlendTarget(aParams, targetOffset); + if (!target) { + return DrawResult::TEMPORARY_ERROR; } bool shouldGenerateMask = (opacity != 1.0f || shouldGenerateClipMaskLayer || @@ -882,16 +916,9 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) context.Restore(); } - if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { - RefPtr targetDT = target->GetDrawTarget(); - target = nullptr; - RefPtr targetSurf = targetDT->Snapshot(); - - gfxContextAutoSaveRestore save(&context); - context.SetMatrix(gfxMatrix()); // This will be restored right after. - RefPtr pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); - context.SetPattern(pattern); - context.Paint(); + if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + MOZ_ASSERT(target != &aParams.ctx); + BlendToTarget(aParams, target, targetOffset); } return result; From 949553410df1d7c5e4867df8bd854b38773538ab Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 00:59:35 +0800 Subject: [PATCH 053/102] Bug 1295094 - Part 7. Implement nsSVGIntegrationUtils::PaintFilter. r=mstange MozReview-Commit-ID: DTcNsc2UNdu --HG-- extra : rebase_source : 1370d3aa9f4d23086c0bc17d14a9c1fadb4d4db8 --- layout/svg/nsSVGIntegrationUtils.cpp | 71 ++++++++++++++++++++++++++++ layout/svg/nsSVGIntegrationUtils.h | 3 ++ 2 files changed, 74 insertions(+) diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 61330393f9fe..7335ee110c1d 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -924,6 +924,77 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) return result; } +DrawResult +nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams) +{ + nsIFrame* frame = aParams.frame; + DrawResult result = DrawResult::SUCCESS; + bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + if (!ValidateSVGFrame(aParams, hasSVGLayout, &result)) { + return result; + } + + float opacity = ComputeOpacity(aParams); + if (opacity == 0.0f) { + return DrawResult::SUCCESS; + } + + gfxContext& context = aParams.ctx; + gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); + nsPoint offsetToBoundingBox; + nsPoint toUserSpace; + nsPoint offsetToUserSpace; + SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace, + offsetToUserSpace); + + /* Properties are added lazily and may have been removed by a restyle, + so make sure all applicable ones are set again. */ + nsIFrame* firstFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + + if (!effectProperties.HasValidFilter()) { + return DrawResult::NOT_READY; + } + + // These are used if we require a temporary surface for a custom blend mode. + IntPoint targetOffset; + RefPtr target = + (aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL) + ? RefPtr(&aParams.ctx).forget() + : CreateBlendTarget(aParams, targetOffset); + if (!target) { + return DrawResult::TEMPORARY_ERROR; + } + + /* Paint the child and apply filters */ + if (!aParams.builder->IsForGenerateGlyphMask()) { + RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, + offsetToUserSpace); + + nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; + gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); + nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), + tm, &callback, &dirtyRegion); + } else { + target->SetMatrix(matrixAutoSaveRestore.Matrix()); + BasicLayerManager* basic = static_cast(aParams.layerManager); + RefPtr oldCtx = basic->GetTarget(); + basic->SetTarget(target); + aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, + aParams.builder); + basic->SetTarget(oldCtx); + } + + if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { + MOZ_ASSERT(target != &aParams.ctx); + BlendToTarget(aParams, target, targetOffset); + } + + return result; +} + gfxMatrix nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) { diff --git a/layout/svg/nsSVGIntegrationUtils.h b/layout/svg/nsSVGIntegrationUtils.h index 8454ae0dbc5c..0b4045d37dc0 100644 --- a/layout/svg/nsSVGIntegrationUtils.h +++ b/layout/svg/nsSVGIntegrationUtils.h @@ -149,6 +149,9 @@ public: static DrawResult PaintFramesWithEffects(const PaintFramesParams& aParams); + static DrawResult + PaintFilter(const PaintFramesParams& aParams); + /** * SVG frames expect to paint in SVG user units, which are equal to CSS px * units. This method provides a transform matrix to multiply onto a From 40076dcfa1219abe1a586c0bdc1c330c6d88f27b Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 01:07:57 +0800 Subject: [PATCH 054/102] Bug 1295094 - Part 8. Implement nsSVGIntegrationUtils::PaintMaskAndClipPath r=mstange MozReview-Commit-ID: EbIUVDXuHa0 --HG-- extra : rebase_source : f9c76291cfc78352268f9cf3a32376fcac96d018 --- layout/base/nsDisplayList.cpp | 2 +- layout/svg/nsSVGIntegrationUtils.cpp | 28 +++++++++------------------- layout/svg/nsSVGIntegrationUtils.h | 2 +- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 94a2d14ec438..db583d189a47 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -6689,7 +6689,7 @@ nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder, aManager, mOpacityItemCreated); image::DrawResult result = - nsSVGIntegrationUtils::PaintFramesWithEffects(params); + nsSVGIntegrationUtils::PaintMaskAndClipPath(params); nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result); } diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 7335ee110c1d..77071662a1ad 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -747,7 +747,7 @@ BlendToTarget(const nsSVGIntegrationUtils::PaintFramesParams& aParams, } DrawResult -nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) +nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) { /* SVG defines the following rendering model: * @@ -755,7 +755,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) * 2. Apply filter * 3. Apply clipping, masking, group opacity * - * We follow this, but perform a couple of optimizations: + * We handle #3 here and perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). @@ -889,23 +889,13 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) } /* Paint the child */ - if (effectProperties.HasValidFilter() && !aParams.builder->IsForGenerateGlyphMask()) { - RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, - offsetToUserSpace); - - nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; - gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); - nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), - tm, &callback, &dirtyRegion); - } else { - target->SetMatrix(matrixAutoSaveRestore.Matrix()); - BasicLayerManager* basic = static_cast(aParams.layerManager); - RefPtr oldCtx = basic->GetTarget(); - basic->SetTarget(target); - aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, - aParams.builder); - basic->SetTarget(oldCtx); - } + target->SetMatrix(matrixAutoSaveRestore.Matrix()); + BasicLayerManager* basic = static_cast(aParams.layerManager); + RefPtr oldCtx = basic->GetTarget(); + basic->SetTarget(target); + aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, + aParams.builder); + basic->SetTarget(oldCtx); if (shouldApplyClipPath || shouldApplyBasicShape) { context.Restore(); diff --git a/layout/svg/nsSVGIntegrationUtils.h b/layout/svg/nsSVGIntegrationUtils.h index 0b4045d37dc0..c4c592798a93 100644 --- a/layout/svg/nsSVGIntegrationUtils.h +++ b/layout/svg/nsSVGIntegrationUtils.h @@ -147,7 +147,7 @@ public: * Paint non-SVG frame with SVG effects. */ static DrawResult - PaintFramesWithEffects(const PaintFramesParams& aParams); + PaintMaskAndClipPath(const PaintFramesParams& aParams); static DrawResult PaintFilter(const PaintFramesParams& aParams); From 40235c5c4753570f5d6d1a4d8e42be6c1a2dd503 Mon Sep 17 00:00:00 2001 From: cku Date: Wed, 10 Aug 2016 03:02:45 +0800 Subject: [PATCH 055/102] Bug 1295094 - Part 9. Implement nsDisplayMask. r=mstange MozReview-Commit-ID: CBszApBehRE --HG-- extra : rebase_source : 94a1aee20a569e0e061d20cae81b1112275cd1a6 --- layout/base/FrameLayerBuilder.cpp | 4 +- layout/base/nsDisplayItemTypesList.h | 2 +- layout/base/nsDisplayList.cpp | 227 +++++++++++++-------------- layout/base/nsDisplayList.h | 61 ++++--- layout/base/nsLayoutDebugger.cpp | 4 +- layout/generic/nsFrame.cpp | 4 +- 6 files changed, 156 insertions(+), 146 deletions(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 67ab34e5f3f3..ae0e45451892 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -3589,8 +3589,8 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder, basic->BeginTransaction(); basic->SetTarget(context); - if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) { - static_cast(aItem)->PaintAsLayer(aBuilder, aCtx, basic); + if (aItem->GetType() == nsDisplayItem::TYPE_MASK) { + static_cast(aItem)->PaintAsLayer(aBuilder, aCtx, basic); if (basic->InTransaction()) { basic->AbortTransaction(); } diff --git a/layout/base/nsDisplayItemTypesList.h b/layout/base/nsDisplayItemTypesList.h index a53bed88ff43..7aaa9b1df8a2 100644 --- a/layout/base/nsDisplayItemTypesList.h +++ b/layout/base/nsDisplayItemTypesList.h @@ -51,7 +51,7 @@ DECLARE_DISPLAY_ITEM_TYPE(SCROLL_INFO_LAYER) DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY) DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR) DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT) -DECLARE_DISPLAY_ITEM_TYPE(SVG_EFFECTS) +DECLARE_DISPLAY_ITEM_TYPE(MASK) DECLARE_DISPLAY_ITEM_TYPE(SVG_GLYPHS) DECLARE_DISPLAY_ITEM_TYPE(SVG_OUTER_SVG) DECLARE_DISPLAY_ITEM_TYPE(SVG_PATH_GEOMETRY) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index db583d189a47..fa8b8b1e1bba 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -6677,92 +6677,6 @@ nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect } } -void -nsDisplaySVGEffects::PaintAsLayer(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx, - LayerManager* aManager) -{ - nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); - nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(), - mFrame, mVisibleRect, - borderArea, aBuilder, - aManager, mOpacityItemCreated); - - image::DrawResult result = - nsSVGIntegrationUtils::PaintMaskAndClipPath(params); - - nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result); -} - -LayerState -nsDisplaySVGEffects::GetLayerState(nsDisplayListBuilder* aBuilder, - LayerManager* aManager, - const ContainerLayerParameters& aParameters) -{ - return LAYER_SVG_EFFECTS; -} - -already_AddRefed -nsDisplaySVGEffects::BuildLayer(nsDisplayListBuilder* aBuilder, - LayerManager* aManager, - const ContainerLayerParameters& aContainerParameters) -{ - const nsIContent* content = mFrame->GetContent(); - bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); - if (hasSVGLayout) { - nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame); - if (!svgChildFrame || !mFrame->GetContent()->IsSVGElement()) { - NS_ASSERTION(false, "why?"); - return nullptr; - } - if (!static_cast(content)->HasValidDimensions()) { - return nullptr; // The SVG spec says not to draw filters for this - } - } - - if (mFrame->StyleEffects()->mOpacity == 0.0f && - !mOpacityItemCreated) - return nullptr; - - nsIFrame* firstFrame = - nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); - nsSVGEffects::EffectProperties effectProperties = - nsSVGEffects::GetEffectProperties(firstFrame); - - bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); - effectProperties.GetClipPathFrame(&isOK); - - if (!isOK) { - return nullptr; - } - - ContainerLayerParameters newContainerParameters = aContainerParameters; - if (effectProperties.HasValidFilter()) { - newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; - } - - RefPtr container = aManager->GetLayerBuilder()-> - BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, - newContainerParameters, nullptr); - - return container.forget(); -} - -nsRect -nsDisplaySVGEffects::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) -{ - nsIFrame* firstFrame = - nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); - nsSVGEffects::EffectProperties effectProperties = - nsSVGEffects::GetEffectProperties(firstFrame); - - if (effectProperties.HasValidFilter()) { - return nsRect(); - } - - return nsDisplayWrapList::GetComponentAlphaBounds(aBuilder); -} - bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) { nsPoint offset = ToReferenceFrame(); @@ -6779,26 +6693,6 @@ bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, return true; } -bool nsDisplaySVGEffects::TryMerge(nsDisplayItem* aItem) -{ - if (aItem->GetType() != TYPE_SVG_EFFECTS) - return false; - // items for the same content element should be merged into a single - // compositing group - // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects - if (aItem->Frame()->GetContent() != mFrame->GetContent()) - return false; - if (aItem->GetClip() != GetClip()) - return false; - if (aItem->ScrollClip() != ScrollClip()) - return false; - nsDisplaySVGEffects* other = static_cast(aItem); - MergeFromTrackingMergedFrames(other); - mEffectsBounds.UnionRect(mEffectsBounds, - other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); - return true; -} - gfxRect nsDisplaySVGEffects::BBoxInUserSpace() const { @@ -6838,9 +6732,120 @@ nsDisplaySVGEffects::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, } } +bool nsDisplaySVGEffects::ValidateSVGFrame() +{ + const nsIContent* content = mFrame->GetContent(); + bool hasSVGLayout = (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + if (hasSVGLayout) { + nsISVGChildFrame *svgChildFrame = do_QueryFrame(mFrame); + if (!svgChildFrame || !mFrame->GetContent()->IsSVGElement()) { + NS_ASSERTION(false, "why?"); + return false; + } + if (!static_cast(content)->HasValidDimensions()) { + return false; // The SVG spec says not to draw filters for this + } + } + + return true; +} + +nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, nsDisplayList* aList, + bool aOpacityItemCreated) + : nsDisplaySVGEffects(aBuilder, aFrame, aList, aOpacityItemCreated) +{ + MOZ_COUNT_CTOR(nsDisplayMask); +} + +#ifdef NS_BUILD_REFCNT_LOGGING +nsDisplayMask::~nsDisplayMask() +{ + MOZ_COUNT_DTOR(nsDisplayMask); +} +#endif + +bool nsDisplayMask::TryMerge(nsDisplayItem* aItem) +{ + if (aItem->GetType() != TYPE_MASK) + return false; + + // items for the same content element should be merged into a single + // compositing group + // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects + if (aItem->Frame()->GetContent() != mFrame->GetContent()) + return false; + if (aItem->GetClip() != GetClip()) + return false; + if (aItem->ScrollClip() != ScrollClip()) + return false; + nsDisplayMask* other = static_cast(aItem); + MergeFromTrackingMergedFrames(other); + mEffectsBounds.UnionRect(mEffectsBounds, + other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); + return true; +} + +already_AddRefed +nsDisplayMask::BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) +{ + if (!ValidateSVGFrame()) { + return nullptr; + } + + if (mFrame->StyleEffects()->mOpacity == 0.0f && + !mOpacityItemCreated) + return nullptr; + + nsIFrame* firstFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + + bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); + effectProperties.GetClipPathFrame(&isOK); + + if (!isOK) { + return nullptr; + } + + RefPtr container = aManager->GetLayerBuilder()-> + BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, + aContainerParameters, nullptr); + + return container.forget(); +} + +LayerState +nsDisplayMask::GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) +{ + return LAYER_SVG_EFFECTS; +} + +void +nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx, + LayerManager* aManager) +{ + nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); + nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(), + mFrame, mVisibleRect, + borderArea, aBuilder, + aManager, mOpacityItemCreated); + + image::DrawResult result = + nsSVGIntegrationUtils::PaintMaskAndClipPath(params); + + nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result); +} + #ifdef MOZ_DUMP_PAINTING void -nsDisplaySVGEffects::PrintEffects(nsACString& aTo) +nsDisplayMask::PrintEffects(nsACString& aTo) { nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); @@ -6869,13 +6874,7 @@ nsDisplaySVGEffects::PrintEffects(nsACString& aTo) aTo += "clip(basic-shape)"; first = false; } - if (effectProperties.HasValidFilter()) { - if (!first) { - aTo += ", "; - } - aTo += "filter"; - first = false; - } + if (effectProperties.GetFirstMaskFrame()) { if (!first) { aTo += ", "; diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 05d37187de11..bb592d562f58 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3782,18 +3782,14 @@ private: int32_t mAPD, mParentAPD; }; -/** - * A display item to paint a stacking context with effects - * set by the stacking context root frame's style. - */ -class nsDisplaySVGEffects : public nsDisplayWrapList { +class nsDisplaySVGEffects: public nsDisplayWrapList { public: nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, bool aOpacityItemCreated); #ifdef NS_BUILD_REFCNT_LOGGING virtual ~nsDisplaySVGEffects(); #endif - + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap) override; virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, @@ -3804,22 +3800,12 @@ public: *aSnap = false; return mEffectsBounds + ToReferenceFrame(); } - virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override; + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) override; - virtual bool TryMerge(nsDisplayItem* aItem) override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } - NS_DISPLAY_DECL_NAME("SVGEffects", TYPE_SVG_EFFECTS) - - virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, - LayerManager* aManager, - const ContainerLayerParameters& aParameters) override; - - virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, - LayerManager* aManager, - const ContainerLayerParameters& aContainerParameters) override; gfxRect BBoxInUserSpace() const; gfxPoint UserSpaceOffset() const; @@ -3832,15 +3818,9 @@ public: const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) override; - void PaintAsLayer(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx, - LayerManager* aManager); +protected: + bool ValidateSVGFrame(); -#ifdef MOZ_DUMP_PAINTING - void PrintEffects(nsACString& aTo); -#endif - -private: // relative to mFrame nsRect mEffectsBounds; // True if the caller also created an nsDisplayOpacity item, and we should tell @@ -3848,6 +3828,37 @@ private: bool mOpacityItemCreated; }; +/** + * A display item to paint a stacking context with mask and clip effects + * set by the stacking context root frame's style. + */ +class nsDisplayMask : public nsDisplaySVGEffects { +public: + nsDisplayMask(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + nsDisplayList* aList, bool aOpacityItemCreated); +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayMask(); +#endif + + NS_DISPLAY_DECL_NAME("Mask", TYPE_MASK) + + virtual bool TryMerge(nsDisplayItem* aItem) override; + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) override; + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) override; + +#ifdef MOZ_DUMP_PAINTING + void PrintEffects(nsACString& aTo); +#endif + + void PaintAsLayer(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx, + LayerManager* aManager); +}; + /* A display item that applies a transformation to all of its descendant * elements. This wrapper should only be used if there is a transform applied * to the root element. diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 87468771793e..9becf23b5003 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -196,9 +196,9 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, } } #ifdef MOZ_DUMP_PAINTING - if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) { + if (aItem->GetType() == nsDisplayItem::TYPE_MASK) { nsCString str; - (static_cast(aItem))->PrintEffects(str); + (static_cast(aItem))->PrintEffects(str); aStream << str.get(); } #endif diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 842a715e2e5b..5fb20fa91c4e 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2448,9 +2448,9 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); /* List now emptied, so add the new list to the top. */ resultList.AppendNewToTop( - new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList, useOpacity)); + new (aBuilder) nsDisplayMask(aBuilder, this, &resultList, useOpacity)); // Also add the hoisted scroll info items. We need those for APZ scrolling - // because nsDisplaySVGEffects items can't build active layers. + // because nsDisplayMask items can't build active layers. aBuilder->ExitSVGEffectsContents(); resultList.AppendToTop(&hoistedScrollInfoItemsStorage); } From 634a5a654d9a2299b7d97d081871c16beb022b30 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 2 Sep 2016 15:09:02 -0600 Subject: [PATCH 056/102] Bug 1300191 - Move machine-configuration.json into mozharness; r=jmaher --HG-- rename : testing/machine-configuration.json => testing/mozharness/external_tools/machine-configuration.json --- .../configs/unittests/linux_unittest.py | 5 +- .../configs/unittests/mac_unittest.py | 5 +- .../configs/unittests/win_unittest.py | 5 +- .../machine-configuration.json | 0 .../mouse_and_screen_resolution.py | 59 +++++++++++-------- 5 files changed, 39 insertions(+), 35 deletions(-) rename testing/{ => mozharness/external_tools}/machine-configuration.json (100%) diff --git a/testing/mozharness/configs/unittests/linux_unittest.py b/testing/mozharness/configs/unittests/linux_unittest.py index 51b15de14959..ac8789d390ea 100644 --- a/testing/mozharness/configs/unittests/linux_unittest.py +++ b/testing/mozharness/configs/unittests/linux_unittest.py @@ -313,9 +313,8 @@ config = { # when configs are consolidated this python path will only show # for windows. "python", "../scripts/external_tools/mouse_and_screen_resolution.py", - "--configuration-url", - "https://hg.mozilla.org/%(branch)s/raw-file/%(revision)s/" + - "testing/machine-configuration.json"], + "--configuration-file", + "../scripts/external_tools/machine-configuration.json"], "architectures": ["32bit"], "halt_on_failure": True, "enabled": ADJUST_MOUSE_AND_SCREEN diff --git a/testing/mozharness/configs/unittests/mac_unittest.py b/testing/mozharness/configs/unittests/mac_unittest.py index 693f76fda383..96e39c11facc 100644 --- a/testing/mozharness/configs/unittests/mac_unittest.py +++ b/testing/mozharness/configs/unittests/mac_unittest.py @@ -241,9 +241,8 @@ config = { # when configs are consolidated this python path will only show # for windows. "python", "../scripts/external_tools/mouse_and_screen_resolution.py", - "--configuration-url", - "https://hg.mozilla.org/%(branch)s/raw-file/%(revision)s/" + - "testing/machine-configuration.json"], + "--configuration-file", + "../scripts/external_tools/machine-configuration.json"], "architectures": ["32bit"], "halt_on_failure": True, "enabled": ADJUST_MOUSE_AND_SCREEN diff --git a/testing/mozharness/configs/unittests/win_unittest.py b/testing/mozharness/configs/unittests/win_unittest.py index 9aef03369f8d..2dc975cd579b 100644 --- a/testing/mozharness/configs/unittests/win_unittest.py +++ b/testing/mozharness/configs/unittests/win_unittest.py @@ -261,9 +261,8 @@ config = { # for windows. sys.executable, "../scripts/external_tools/mouse_and_screen_resolution.py", - "--configuration-url", - "https://hg.mozilla.org/%(repo_path)s/raw-file/%(revision)s/" + - "testing/machine-configuration.json"], + "--configuration-file", + "../scripts/external_tools/machine-configuration.json"], "architectures": ["32bit"], "halt_on_failure": True, "enabled": ADJUST_MOUSE_AND_SCREEN diff --git a/testing/machine-configuration.json b/testing/mozharness/external_tools/machine-configuration.json similarity index 100% rename from testing/machine-configuration.json rename to testing/mozharness/external_tools/machine-configuration.json diff --git a/testing/mozharness/external_tools/mouse_and_screen_resolution.py b/testing/mozharness/external_tools/mouse_and_screen_resolution.py index 70daf4008ef3..29e46e1bc071 100755 --- a/testing/mozharness/external_tools/mouse_and_screen_resolution.py +++ b/testing/mozharness/external_tools/mouse_and_screen_resolution.py @@ -44,40 +44,47 @@ def wfetch(url, retries=5): time.sleep(60) def main(): - ''' - We load the configuration file from: - https://hg.mozilla.org/mozilla-central/raw-file/default/build/machine-configuration.json - ''' - parser = OptionParser() - parser.add_option( - "--configuration-url", dest="configuration_url", type="string", - help="It indicates from where to download the configuration file.") - (options, args) = parser.parse_args() - - if options.configuration_url == None: - print "You need to specify --configuration-url." - return 1 if not (platform.version().startswith('6.1.760') and not 'PROGRAMFILES(X86)' in os.environ): # We only want to run this for Windows 7 32-bit print "INFO: This script was written to be used with Windows 7 32-bit machines." return 0 - try: - conf_dict = json.loads(wfetch(options.configuration_url)) + parser = OptionParser() + parser.add_option( + "--configuration-url", dest="configuration_url", type="string", + help="Specifies the url of the configuration file.") + parser.add_option( + "--configuration-file", dest="configuration_file", type="string", + help="Specifies the path to the configuration file.") + (options, args) = parser.parse_args() + + if (options.configuration_url == None and + options.configuration_file == None): + print "You must specify --configuration-url or --configuration-file." + return 1 + + if options.configuration_file: + with open(options.configuration_file) as f: + conf_dict = json.load(f) new_screen_resolution = conf_dict["win7"]["screen_resolution"] new_mouse_position = conf_dict["win7"]["mouse_position"] - except urllib2.HTTPError, e: - print "This branch does not seem to have the configuration file %s" % str(e) - print "Let's fail over to 1024x768." - new_screen_resolution = default_screen_resolution - new_mouse_position = default_mouse_position - except urllib2.URLError, e: - print "INFRA-ERROR: We couldn't reach hg.mozilla.org: %s" % str(e) - return 1 - except Exception, e: - print "ERROR: We were not expecting any more exceptions: %s" % str(e) - return 1 + else: + try: + conf_dict = json.loads(wfetch(options.configuration_url)) + new_screen_resolution = conf_dict["win7"]["screen_resolution"] + new_mouse_position = conf_dict["win7"]["mouse_position"] + except urllib2.HTTPError, e: + print "This branch does not seem to have the configuration file %s" % str(e) + print "Let's fail over to 1024x768." + new_screen_resolution = default_screen_resolution + new_mouse_position = default_mouse_position + except urllib2.URLError, e: + print "INFRA-ERROR: We couldn't reach hg.mozilla.org: %s" % str(e) + return 1 + except Exception, e: + print "ERROR: We were not expecting any more exceptions: %s" % str(e) + return 1 current_screen_resolution = queryScreenResolution() print "Screen resolution (current): (%(x)s, %(y)s)" % (current_screen_resolution) From 5c628bb103921c72fde62da3d4a2b199a2581899 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 2 Sep 2016 15:09:03 -0600 Subject: [PATCH 057/102] Bug 1298023 - Remove some redundant android reftest prefs; r=jmaher --- layout/tools/reftest/remotereftest.py | 34 --------------------------- 1 file changed, 34 deletions(-) diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py index 5511013fae29..a469345f31c1 100644 --- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -229,48 +229,14 @@ class RemoteReftest(RefTest): prefs = {} prefs["app.update.url.android"] = "" prefs["browser.firstrun.show.localepicker"] = False - prefs["font.size.inflation.emPerLine"] = 0 - prefs["font.size.inflation.minTwips"] = 0 prefs["reftest.remote"] = True prefs["datareporting.policy.dataSubmissionPolicyBypassAcceptance"] = True - # Point the url-classifier to the local testing server for fast failures - prefs["browser.safebrowsing.provider.google.gethashURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/gethash" - prefs["browser.safebrowsing.provider.google.updateURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/update" - prefs["browser.safebrowsing.provider.mozilla.gethashURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/gethash" - prefs["browser.safebrowsing.provider.mozilla.updateURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/update" - # Point update checks to the local testing server for fast failures - prefs["extensions.update.url"] = "http://127.0.0.1:8888/extensions-dummy/updateURL" - prefs["extensions.update.background.url"] = "http://127.0.0.1:8888/extensions-dummy/updateBackgroundURL" - prefs["extensions.blocklist.url"] = "http://127.0.0.1:8888/extensions-dummy/blocklistURL" - prefs["extensions.hotfix.url"] = "http://127.0.0.1:8888/extensions-dummy/hotfixURL" - # Turn off extension updates so they don't bother tests - prefs["extensions.update.enabled"] = False - # Make sure opening about:addons won't hit the network - prefs["extensions.webservice.discoverURL"] = "http://127.0.0.1:8888/extensions-dummy/discoveryURL" - # Make sure AddonRepository won't hit the network - prefs["extensions.getAddons.maxResults"] = 0 - prefs["extensions.getAddons.get.url"] = "http://127.0.0.1:8888/extensions-dummy/repositoryGetURL" - prefs["extensions.getAddons.getWithPerformance.url"] = "http://127.0.0.1:8888/extensions-dummy/repositoryGetWithPerformanceURL" - prefs["extensions.getAddons.search.browseURL"] = "http://127.0.0.1:8888/extensions-dummy/repositoryBrowseURL" - prefs["extensions.getAddons.search.url"] = "http://127.0.0.1:8888/extensions-dummy/repositorySearchURL" - # Make sure the GMPInstallManager won't hit the network - prefs["media.gmp-manager.url.override"] = "http://127.0.0.1:8888/dummy-gmp-manager.xml"; prefs["layout.css.devPixelsPerPx"] = "1.0" # Because Fennec is a little wacky (see bug 1156817) we need to load the # reftest pages at 1.0 zoom, rather than zooming to fit the CSS viewport. prefs["apz.allow_zooming"] = False - # Disable skia-gl: see bug 907351 - prefs["gfx.canvas.azure.accelerated"] = False - - prefs["media.autoplay.enabled"] = True - - # Debug reftests have problems with large tile size on pandaboards - if mozinfo.info['debug'] and self._devicemanager.shellCheckOutput(['getprop', 'ro.product.name']) == 'pandaboard': - prefs["layers.tiles.adjust"] = False - prefs["layers.single-tile.enabled"] = False - # Set the extra prefs. profile.set_preferences(prefs) From 058c0dac3f90798e90f841b206f8374657e335c4 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:37 -0400 Subject: [PATCH 058/102] Bug 1300005. Make sure that our PositionOptions structs are always owned by someone in geolocation code. r=kanru --- dom/geolocation/nsGeolocation.cpp | 38 ++++++++++--------- dom/geolocation/nsGeolocation.h | 4 +- .../geolocation/nsIDOMGeoGeolocation.idl | 8 ++-- dom/ipc/ContentParent.cpp | 4 +- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp index 5b17e632f94c..0fed7d38cbff 100644 --- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -85,7 +85,7 @@ class nsGeolocationRequest final nsGeolocationRequest(Geolocation* aLocator, GeoPositionCallback aCallback, GeoPositionErrorCallback aErrorCallback, - PositionOptions* aOptions, + nsAutoPtr&& aOptions, uint8_t aProtocolType, bool aWatchPositionRequest = false, int32_t aWatchId = 0); @@ -138,7 +138,7 @@ class nsGeolocationRequest final uint8_t mProtocolType; }; -static PositionOptions* +static nsAutoPtr CreatePositionOptionsCopy(const PositionOptions& aOptions) { nsAutoPtr geoOptions(new PositionOptions()); @@ -147,7 +147,7 @@ CreatePositionOptionsCopy(const PositionOptions& aOptions) geoOptions->mMaximumAge = aOptions.mMaximumAge; geoOptions->mTimeout = aOptions.mTimeout; - return geoOptions.forget(); + return geoOptions; } class GeolocationSettingsCallback : public nsISettingsServiceCallback @@ -349,14 +349,14 @@ PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback) nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator, GeoPositionCallback aCallback, GeoPositionErrorCallback aErrorCallback, - PositionOptions* aOptions, + nsAutoPtr&& aOptions, uint8_t aProtocolType, bool aWatchPositionRequest, int32_t aWatchId) : mIsWatchPositionRequest(aWatchPositionRequest), mCallback(Move(aCallback)), mErrorCallback(Move(aErrorCallback)), - mOptions(aOptions), + mOptions(Move(aOptions)), mLocator(aLocator), mWatchId(aWatchId), mShutdown(false), @@ -1392,7 +1392,7 @@ Geolocation::GetCurrentPosition(PositionCallback& aCallback, { nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback), GeoPositionErrorCallback(aErrorCallback), - CreatePositionOptionsCopy(aOptions)); + Move(CreatePositionOptionsCopy(aOptions))); if (NS_FAILED(rv)) { aRv.Throw(rv); @@ -1404,30 +1404,34 @@ Geolocation::GetCurrentPosition(PositionCallback& aCallback, NS_IMETHODIMP Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback, nsIDOMGeoPositionErrorCallback* aErrorCallback, - PositionOptions* aOptions) + nsAutoPtr&& aOptions) { NS_ENSURE_ARG_POINTER(aCallback); return GetCurrentPosition(GeoPositionCallback(aCallback), - GeoPositionErrorCallback(aErrorCallback), aOptions); + GeoPositionErrorCallback(aErrorCallback), + Move(aOptions)); } nsresult Geolocation::GetCurrentPosition(GeoPositionCallback callback, GeoPositionErrorCallback errorCallback, - PositionOptions *options) + nsAutoPtr&& options) { if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) { return NS_ERROR_NOT_AVAILABLE; } + // After this we hand over ownership of options to our nsGeolocationRequest. + // Count the number of requests per protocol/scheme. Telemetry::Accumulate(Telemetry::GEOLOCATION_GETCURRENTPOSITION_SECURE_ORIGIN, static_cast(mProtocolType)); RefPtr request = - new nsGeolocationRequest(this, Move(callback), Move(errorCallback), options, - static_cast(mProtocolType), false); + new nsGeolocationRequest(this, Move(callback), Move(errorCallback), + Move(options), static_cast(mProtocolType), + false); if (!sGeoEnabled) { nsCOMPtr ev = new RequestAllowEvent(false, request); @@ -1477,7 +1481,7 @@ Geolocation::WatchPosition(PositionCallback& aCallback, int32_t ret = 0; nsresult rv = WatchPosition(GeoPositionCallback(&aCallback), GeoPositionErrorCallback(aErrorCallback), - CreatePositionOptionsCopy(aOptions), &ret); + Move(CreatePositionOptionsCopy(aOptions)), &ret); if (NS_FAILED(rv)) { aRv.Throw(rv); @@ -1489,20 +1493,20 @@ Geolocation::WatchPosition(PositionCallback& aCallback, NS_IMETHODIMP Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback, nsIDOMGeoPositionErrorCallback *aErrorCallback, - PositionOptions *aOptions, + nsAutoPtr&& aOptions, int32_t* aRv) { NS_ENSURE_ARG_POINTER(aCallback); return WatchPosition(GeoPositionCallback(aCallback), GeoPositionErrorCallback(aErrorCallback), - aOptions, aRv); + Move(aOptions), aRv); } nsresult Geolocation::WatchPosition(GeoPositionCallback aCallback, GeoPositionErrorCallback aErrorCallback, - PositionOptions* aOptions, + nsAutoPtr&& aOptions, int32_t* aRv) { if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) { @@ -1518,8 +1522,8 @@ Geolocation::WatchPosition(GeoPositionCallback aCallback, RefPtr request = new nsGeolocationRequest(this, Move(aCallback), Move(aErrorCallback), - aOptions, static_cast(mProtocolType), - true, *aRv); + Move(aOptions), + static_cast(mProtocolType), true, *aRv); if (!sGeoEnabled) { nsCOMPtr ev = new RequestAllowEvent(false, request); diff --git a/dom/geolocation/nsGeolocation.h b/dom/geolocation/nsGeolocation.h index 96ad3bfc9709..97b1313dc478 100644 --- a/dom/geolocation/nsGeolocation.h +++ b/dom/geolocation/nsGeolocation.h @@ -191,10 +191,10 @@ private: nsresult GetCurrentPosition(GeoPositionCallback aCallback, GeoPositionErrorCallback aErrorCallback, - PositionOptions* aOptions); + nsAutoPtr&& aOptions); nsresult WatchPosition(GeoPositionCallback aCallback, GeoPositionErrorCallback aErrorCallback, - PositionOptions* aOptions, int32_t* aRv); + nsAutoPtr&& aOptions, int32_t* aRv); bool RegisterRequestWithPrompt(nsGeolocationRequest* request); diff --git a/dom/interfaces/geolocation/nsIDOMGeoGeolocation.idl b/dom/interfaces/geolocation/nsIDOMGeoGeolocation.idl index c75d93f2d4bf..ef1e863f1ec3 100644 --- a/dom/interfaces/geolocation/nsIDOMGeoGeolocation.idl +++ b/dom/interfaces/geolocation/nsIDOMGeoGeolocation.idl @@ -13,18 +13,20 @@ namespace dom { struct PositionOptions; } } + +template class nsAutoPtr; %} -[ptr] native NamespacedPositionOptions(mozilla::dom::PositionOptions); +native PositionOptionsRef(nsAutoPtr&&); [builtinclass, uuid(9142ab45-0ab5-418c-9bab-338a6d271d4f)] interface nsIDOMGeoGeolocation : nsISupports { int32_t watchPosition(in nsIDOMGeoPositionCallback callback, in nsIDOMGeoPositionErrorCallback errorCallback, - in NamespacedPositionOptions options); + in PositionOptionsRef options); void getCurrentPosition(in nsIDOMGeoPositionCallback callback, in nsIDOMGeoPositionErrorCallback errorCallback, - in NamespacedPositionOptions options); + in PositionOptionsRef options); void clearWatch(in long watchId); }; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 26088e63f936..c554bb31c9db 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4061,12 +4061,12 @@ AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, return -1; } - PositionOptions* options = new PositionOptions(); + nsAutoPtr options(new PositionOptions()); options->mTimeout = 0; options->mMaximumAge = 0; options->mEnableHighAccuracy = highAccuracy; int32_t retval = 1; - geo->WatchPosition(watcher, errorCallBack, options, &retval); + geo->WatchPosition(watcher, errorCallBack, Move(options), &retval); return retval; } From 363a5143aa4d809de77455c95ecb5a065b48afca Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 059/102] Bug 1297125. Make sure the parser insertion point is defined when firing the load event for external or firing the error event on a failed external script load (but not other cases, like bogus script URL). r=hsivonen If we have a creator parser, then we were a parser-inserted script and should presumably be able to set a valid insertion point when we run or fire our load/error events. For the error event case, we do this in nsScriptElement::ScriptAvailable, so that async error events due to things like bogus script URLs do not end up with a valid insertion point. For the load event case, we just do this in ScriptEvaluated directly. ScriptEvaluated is called while the scriptloader has our script set as the current parser-inserted script. But for the error event case we need to maintain that state around the ScriptAvailable call that will fire the event. --- dom/base/nsIScriptElement.h | 4 ++-- dom/base/nsScriptElement.cpp | 10 +++++++++- dom/base/nsScriptLoader.cpp | 8 ++++++++ parser/html/nsHtml5Parser.cpp | 10 +++++----- parser/html/nsHtml5Parser.h | 16 ++++++++++------ parser/htmlparser/nsIParser.h | 11 +++++++---- parser/htmlparser/nsParser.cpp | 4 ++-- parser/htmlparser/nsParser.h | 4 ++-- .../tests/reftest/bug592656-1-ref.html | 2 +- .../htmlparser/tests/reftest/bug592656-1.html | 10 ++++++++-- testing/web-platform/meta/MANIFEST.json | 18 ++++++++++++++++++ .../script-onerror-insertion-point-1.html | 12 ++++++++++++ .../script-onerror-insertion-point-2.html | 13 +++++++++++++ .../script-onload-insertion-point.html | 12 ++++++++++++ ...cript-onerror-insertion-point-1-helper.html | 2 ++ ...cript-onerror-insertion-point-2-helper.html | 2 ++ .../script-onload-insertion-point-helper.html | 2 ++ .../script-onload-insertion-point-helper.js | 1 + 18 files changed, 116 insertions(+), 25 deletions(-) create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html create mode 100644 testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js diff --git a/dom/base/nsIScriptElement.h b/dom/base/nsIScriptElement.h index e163928af3fc..470d51c94827 100644 --- a/dom/base/nsIScriptElement.h +++ b/dom/base/nsIScriptElement.h @@ -185,7 +185,7 @@ public: { nsCOMPtr parser = do_QueryReferent(mCreatorParser); if (parser) { - parser->BeginEvaluatingParserInsertedScript(); + parser->PushDefinedInsertionPoint(); } } @@ -196,7 +196,7 @@ public: { nsCOMPtr parser = do_QueryReferent(mCreatorParser); if (parser) { - parser->EndEvaluatingParserInsertedScript(); + parser->PopDefinedInsertionPoint(); } } diff --git a/dom/base/nsScriptElement.cpp b/dom/base/nsScriptElement.cpp index 5e0334fa1efd..ebeb18f81cb2 100644 --- a/dom/base/nsScriptElement.cpp +++ b/dom/base/nsScriptElement.cpp @@ -26,7 +26,15 @@ nsScriptElement::ScriptAvailable(nsresult aResult, int32_t aLineNo) { if (!aIsInline && NS_FAILED(aResult)) { - return FireErrorEvent(); + nsCOMPtr parser = do_QueryReferent(mCreatorParser); + if (parser) { + parser->PushDefinedInsertionPoint(); + } + nsresult rv = FireErrorEvent(); + if (parser) { + parser->PopDefinedInsertionPoint(); + } + return rv; } return NS_OK; } diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 04028b3459d4..8ddfbc930bbb 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -2524,8 +2524,16 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, MOZ_ASSERT(!request->isInList()); mParserBlockingRequest = nullptr; UnblockParser(request); + + // Ensure that we treat request->mElement as our current parser-inserted + // script while firing onerror on it. + MOZ_ASSERT(request->mElement->GetParserCreated()); + nsCOMPtr oldParserInsertedScript = + mCurrentParserInsertedScript; + mCurrentParserInsertedScript = request->mElement; FireScriptAvailable(rv, request); ContinueParserAsync(request); + mCurrentParserInsertedScript = oldParserInsertedScript; } else { mPreloads.RemoveElement(request, PreloadRequestComparator()); } diff --git a/parser/html/nsHtml5Parser.cpp b/parser/html/nsHtml5Parser.cpp index c28b12c43868..8d3a6e6648bb 100644 --- a/parser/html/nsHtml5Parser.cpp +++ b/parser/html/nsHtml5Parser.cpp @@ -553,19 +553,19 @@ bool nsHtml5Parser::IsInsertionPointDefined() { return !mExecutor->IsFlushing() && - (!GetStreamParser() || mParserInsertedScriptsBeingEvaluated); + (!GetStreamParser() || mInsertionPointPushLevel); } void -nsHtml5Parser::BeginEvaluatingParserInsertedScript() +nsHtml5Parser::PushDefinedInsertionPoint() { - ++mParserInsertedScriptsBeingEvaluated; + ++mInsertionPointPushLevel; } void -nsHtml5Parser::EndEvaluatingParserInsertedScript() +nsHtml5Parser::PopDefinedInsertionPoint() { - --mParserInsertedScriptsBeingEvaluated; + --mInsertionPointPushLevel; } void diff --git a/parser/html/nsHtml5Parser.h b/parser/html/nsHtml5Parser.h index 0d4d4a9b8d2b..9d88adb29d85 100644 --- a/parser/html/nsHtml5Parser.h +++ b/parser/html/nsHtml5Parser.h @@ -188,14 +188,17 @@ class nsHtml5Parser final : public nsIParser, virtual bool IsInsertionPointDefined() override; /** - * Call immediately before starting to evaluate a parser-inserted script. + * Call immediately before starting to evaluate a parser-inserted script or + * in general when the spec says to define an insertion point. */ - virtual void BeginEvaluatingParserInsertedScript() override; + virtual void PushDefinedInsertionPoint() override; /** - * Call immediately after having evaluated a parser-inserted script. + * Call immediately after having evaluated a parser-inserted script or + * generally want to restore to the state before the last + * PushDefinedInsertionPoint call. */ - virtual void EndEvaluatingParserInsertedScript() override; + virtual void PopDefinedInsertionPoint() override; /** * Marks the HTML5 parser as not a script-created parser: Prepares the @@ -286,9 +289,10 @@ class nsHtml5Parser final : public nsIParser, bool mDocWriteSpeculatorActive; /** - * The number of parser-inserted script currently being evaluated. + * The number of PushDefinedInsertionPoint calls we've seen without a + * matching PopDefinedInsertionPoint. */ - int32_t mParserInsertedScriptsBeingEvaluated; + int32_t mInsertionPointPushLevel; /** * True if document.close() has been called. diff --git a/parser/htmlparser/nsIParser.h b/parser/htmlparser/nsIParser.h index 8e33f5bf0875..31666cadbabe 100644 --- a/parser/htmlparser/nsIParser.h +++ b/parser/htmlparser/nsIParser.h @@ -213,14 +213,17 @@ class nsIParser : public nsParserBase { virtual bool IsInsertionPointDefined() = 0; /** - * Call immediately before starting to evaluate a parser-inserted script. + * Call immediately before starting to evaluate a parser-inserted script or + * in general when the spec says to define an insertion point. */ - virtual void BeginEvaluatingParserInsertedScript() = 0; + virtual void PushDefinedInsertionPoint() = 0; /** - * Call immediately after having evaluated a parser-inserted script. + * Call immediately after having evaluated a parser-inserted script or + * generally want to restore to the state before the last + * PushDefinedInsertionPoint call. */ - virtual void EndEvaluatingParserInsertedScript() = 0; + virtual void PopDefinedInsertionPoint() = 0; /** * Marks the HTML5 parser as not a script-created parser. diff --git a/parser/htmlparser/nsParser.cpp b/parser/htmlparser/nsParser.cpp index 2520d81fcc10..4b86b236e637 100644 --- a/parser/htmlparser/nsParser.cpp +++ b/parser/htmlparser/nsParser.cpp @@ -1135,12 +1135,12 @@ nsParser::IsInsertionPointDefined() } void -nsParser::BeginEvaluatingParserInsertedScript() +nsParser::PushDefinedInsertionPoint() { } void -nsParser::EndEvaluatingParserInsertedScript() +nsParser::PopDefinedInsertionPoint() { } diff --git a/parser/htmlparser/nsParser.h b/parser/htmlparser/nsParser.h index 748d2578c7ac..39bfe03b8493 100644 --- a/parser/htmlparser/nsParser.h +++ b/parser/htmlparser/nsParser.h @@ -258,12 +258,12 @@ class nsParser final : public nsIParser, /** * No-op. */ - virtual void BeginEvaluatingParserInsertedScript() override; + virtual void PushDefinedInsertionPoint() override; /** * No-op. */ - virtual void EndEvaluatingParserInsertedScript() override; + virtual void PopDefinedInsertionPoint() override; /** * No-op. diff --git a/parser/htmlparser/tests/reftest/bug592656-1-ref.html b/parser/htmlparser/tests/reftest/bug592656-1-ref.html index c149e762b872..824d8156335c 100644 --- a/parser/htmlparser/tests/reftest/bug592656-1-ref.html +++ b/parser/htmlparser/tests/reftest/bug592656-1-ref.html @@ -4,6 +4,6 @@ document.write() from script-inserted inline scripts and script@onload -1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 diff --git a/parser/htmlparser/tests/reftest/bug592656-1.html b/parser/htmlparser/tests/reftest/bug592656-1.html index 02a4fc754cf6..769f62f648a8 100644 --- a/parser/htmlparser/tests/reftest/bug592656-1.html +++ b/parser/htmlparser/tests/reftest/bug592656-1.html @@ -10,8 +10,12 @@ function write(num) { document.write(num + " "); } function scriptload() { -document.write("\u003Cscript src='data:text/javascript,write(9)'>\u003C/script> 10 \u003Cscript>write(11)\u003C/script>"); -write(12); + document.write("\u003Cscript src='data:text/javascript,write(9)'>\u003C/script> 10 \u003Cscript>write(11)\u003C/script>"); + write(12); +} +function scripterror() { + document.write("\u003Cscript src='data:text/javascript,write(16)'>\u003C/script> 17 \u003Cscript>write(18)\u003C/script>"); + write(19); } write(2); document.write("\u003Cscript src='data:text/javascript,write(3)'>\u003C/script> 4 \u003Cscript>write(5)\u003C/script>"); @@ -21,6 +25,8 @@ document.body.appendChild(s); write(7); document.write("\u003Cscript src='data:text/javascript,write(8)' onload='scriptload()'>\u003C/script> 13 \u003Cscript>write(14)\u003C/script>"); write(15); +document.write(`\u003Cscript src='nosuchscriptoutthere.js' onload='write("fail")' onerror='scripterror()'>\u003C/script> 20 \u003Cscript>write(21)\u003C/script>`); +write(22); diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 482528f28c85..132e95933e41 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -37826,6 +37826,24 @@ "url": "/svg/linking/scripted/href-script-element.html" } ], + "html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html": [ + { + "path": "html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html", + "url": "/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html" + } + ], + "html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html": [ + { + "path": "html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html", + "url": "/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html" + } + ], + "html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html": [ + { + "path": "html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html", + "url": "/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html" + } + ], "web-animations/interfaces/Animation/effect.html": [ { "path": "web-animations/interfaces/Animation/effect.html", diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html new file mode 100644 index 000000000000..0fe39b11a817 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-1.html @@ -0,0 +1,12 @@ + + +Test that the insertion point is defined in the error event of a parser-inserted script that actually started a fetch (but just had it fail). + + + + diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html new file mode 100644 index 000000000000..6d3f3ef09eed --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onerror-insertion-point-2.html @@ -0,0 +1,13 @@ + + +Test that the insertion point is not defined in the error event of a + parser-inserted script that has an unparseable URL + + + + diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html new file mode 100644 index 000000000000..ce3ddeee65ff --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-onload-insertion-point.html @@ -0,0 +1,12 @@ + + +Test that the insertion point is defined in the load event of a parser-inserted script. + + + + diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html new file mode 100644 index 000000000000..d9b0c84ca4f1 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-1-helper.html @@ -0,0 +1,2 @@ +Some diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html new file mode 100644 index 000000000000..7a1739815626 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onerror-insertion-point-2-helper.html @@ -0,0 +1,2 @@ +Some diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html new file mode 100644 index 000000000000..f0236b4fbb32 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.html @@ -0,0 +1,2 @@ +Some diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js new file mode 100644 index 000000000000..8a96a0b783e1 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/support/script-onload-insertion-point-helper.js @@ -0,0 +1 @@ +document.write("te"); From 8ff5499fbb62056b16e0ea586ef6fcd513920d25 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 060/102] Bug 1292159. Be more careful about removing the inherit principal bit on base channel redirects. r=ckerschb --- netwerk/base/nsBaseChannel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index 5873ad02a6fe..200804c1e6ba 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -84,8 +84,8 @@ nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags, // make a copy of the loadinfo, append to the redirectchain // and set it on the new channel if (mLoadInfo) { - nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() ^ - nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; + nsSecurityFlags secFlags = mLoadInfo->GetSecurityFlags() & + ~nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; nsCOMPtr newLoadInfo = static_cast(mLoadInfo.get())->CloneWithNewSecFlags(secFlags); From 10a9e4c62066639909c5f87a655ee96b74afe121 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 061/102] Bug 1297717. Stop using an unforgeable holder for global objects with unforgeable properties, since it's just pure slowdown in that case. r=peterv --- dom/bindings/BindingUtils.h | 18 ++++----- dom/bindings/Codegen.py | 81 ++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 50 deletions(-) diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 4260da050c51..a80a21d9144f 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -3063,15 +3063,15 @@ struct CreateGlobalOptions nsresult RegisterDOMNames(); -// The return value is whatever the ProtoHandleGetter we used -// returned. This should be the DOM prototype for the global. +// The return value is true if we created and successfully performed our part of +// the setup for the global, false otherwise. // // Typically this method's caller will want to ensure that // xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is // called after, this method, to ensure that this global object and its // compartment are consistent with other global objects. template -JS::Handle +bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, const JSClass* aClass, JS::CompartmentOptions& aOptions, JSPrincipals* aPrincipal, bool aInitStandardClasses, @@ -3086,7 +3086,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, JS::DontFireOnNewGlobalHook, aOptions)); if (!aGlobal) { NS_WARNING("Failed to create global"); - return nullptr; + return false; } JSAutoCompartment ac(aCx, aGlobal); @@ -3101,31 +3101,31 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, CreateGlobalOptions::ProtoAndIfaceCacheKind); if (!CreateGlobalOptions::PostCreateGlobal(aCx, aGlobal)) { - return nullptr; + return false; } } if (aInitStandardClasses && !JS_InitStandardClasses(aCx, aGlobal)) { NS_WARNING("Failed to init standard classes"); - return nullptr; + return false; } JS::Handle proto = GetProto(aCx); if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) { NS_WARNING("Failed to set proto"); - return nullptr; + return false; } bool succeeded; if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) { - return nullptr; + return false; } MOZ_ASSERT(succeeded, "making a fresh global object's [[Prototype]] immutable can " "internally fail, but it should never be unsuccessful"); - return proto; + return true; } /* diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 513f4bc1eadc..c12d6065a12c 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -586,7 +586,10 @@ class CGPrototypeJSClass(CGThing): def define(self): prototypeID, depth = PrototypeIDAndDepth(self.descriptor) slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" - if self.descriptor.hasUnforgeableMembers: + # Globals handle unforgeables directly in Wrap() instead of + # via a holder. + if (self.descriptor.hasUnforgeableMembers and + not self.descriptor.isGlobal()): slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */" (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor) type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype" @@ -2930,7 +2933,9 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): else: defineAliases = None - if self.descriptor.hasUnforgeableMembers: + # Globals handle unforgeables directly in Wrap() instead of + # via a holder. + if self.descriptor.hasUnforgeableMembers and not self.descriptor.isGlobal(): assert needInterfacePrototypeObject # We want to use the same JSClass and prototype as the object we'll @@ -2939,12 +2944,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): # a fast copy. In the case of proxies that's null, because the # expando object is a vanilla object, but in the case of other DOM # objects it's whatever our class is. - # - # Also, for a global we can't use the global's class; just use - # nullpr and when we do the copy off the holder we'll take a slower - # path. This also means that we don't need to worry about matching - # the prototype. - if self.descriptor.proxy or self.descriptor.isGlobal(): + if self.descriptor.proxy: holderClass = "nullptr" holderProto = "nullptr" else: @@ -3334,7 +3334,8 @@ def CreateBindingJSObject(descriptor, properties): return objDecl + create -def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode): +def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode, + holderName="unforgeableHolder"): """ Define the unforgeable properties on the unforgeable holder for the interface represented by descriptor. @@ -3351,18 +3352,20 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode): defineUnforgeableAttrs = fill( """ - if (!DefineUnforgeableAttributes(aCx, unforgeableHolder, %s)) { + if (!DefineUnforgeableAttributes(aCx, ${holderName}, %s)) { $*{failureCode} } """, - failureCode=failureCode) + failureCode=failureCode, + holderName=holderName) defineUnforgeableMethods = fill( """ - if (!DefineUnforgeableMethods(aCx, unforgeableHolder, %s)) { + if (!DefineUnforgeableMethods(aCx, ${holderName}, %s)) { $*{failureCode} } """, - failureCode=failureCode) + failureCode=failureCode, + holderName=holderName) unforgeableMembers = [ (defineUnforgeableAttrs, properties.unforgeableAttrs), @@ -3384,16 +3387,17 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode): """ JS::RootedId toPrimitive(aCx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive))); - if (!JS_DefinePropertyById(aCx, unforgeableHolder, toPrimitive, + if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive, JS::UndefinedHandleValue, JSPROP_READONLY | JSPROP_PERMANENT) || - !JS_DefineProperty(aCx, unforgeableHolder, "toJSON", + !JS_DefineProperty(aCx, ${holderName}, "toJSON", JS::UndefinedHandleValue, JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) { $*{failureCode} } """, - failureCode=failureCode))) + failureCode=failureCode, + holderName=holderName))) return CGWrapper(CGList(unforgeables), pre="\n") @@ -3403,6 +3407,8 @@ def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): Copy the unforgeable properties from the unforgeable holder for this interface to the instance object we have. """ + assert not descriptor.isGlobal(); + if not descriptor.hasUnforgeableMembers: return "" @@ -3442,23 +3448,15 @@ def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): else: obj = "aReflector" - # We can't do the fast copy for globals, because we can't allocate the - # unforgeable holder for those with the right JSClass. Luckily, there - # aren't too many globals being created. - if descriptor.isGlobal(): - copyFunc = "JS_CopyPropertiesFrom" - else: - copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject" copyCode.append(CGGeneric(fill( """ JS::Rooted unforgeableHolder(aCx, &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject()); - if (!${copyFunc}(aCx, ${obj}, unforgeableHolder)) { + if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) { $*{cleanup} return false; } """, - copyFunc=copyFunc, obj=obj, cleanup=cleanup))) @@ -3709,14 +3707,19 @@ class CGWrapGlobalMethod(CGAbstractMethod): else: chromeProperties = "nullptr" + failureCode = dedent( + """ + aCache->ReleaseWrapper(aObject); + aCache->ClearWrapper(); + return false; + """); + if self.descriptor.hasUnforgeableMembers: - declareProto = "JS::Handle canonicalProto =\n" - assertProto = ( - "MOZ_ASSERT(canonicalProto &&\n" - " IsDOMIfaceAndProtoClass(js::GetObjectClass(canonicalProto)));\n") + unforgeable = InitUnforgeablePropertiesOnHolder( + self.descriptor, self.properties, failureCode, + "aReflector").define(); else: - declareProto = "" - assertProto = "" + unforgeable = "" return fill( """ @@ -3724,26 +3727,23 @@ class CGWrapGlobalMethod(CGAbstractMethod): MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), "nsISupports must be on our primary inheritance chain"); - $*{declareProto} - CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx, + if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx, aObject, aCache, sClass.ToJSClass(), aOptions, aPrincipal, aInitStandardClasses, - aReflector); - if (!aReflector) { - return false; + aReflector)) { + $*{failureCode} } - $*{assertProto} // aReflector is a new global, so has a new compartment. Enter it // before doing anything with it. JSAutoCompartment ac(aCx, aReflector); if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) { - return false; + $*{failureCode} } $*{unforgeable} @@ -3753,11 +3753,10 @@ class CGWrapGlobalMethod(CGAbstractMethod): """, assertions=AssertInheritanceChain(self.descriptor), nativeType=self.descriptor.nativeType, - declareProto=declareProto, - assertProto=assertProto, properties=properties, chromeProperties=chromeProperties, - unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, True), + failureCode=failureCode, + unforgeable=unforgeable, slots=InitMemberSlots(self.descriptor, True)) From 8aef9d0ce9969e46eda1fb3155ef0e557e434749 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 062/102] Bug 1299306 part 1. Refactor the error handling in CGWrapNonWrapperCacheMethod and CGWrapWithCacheMethod to have less duplication. r=peterv --- dom/bindings/Codegen.py | 58 ++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index c12d6065a12c..1524f7f19944 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3402,7 +3402,7 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode, return CGWrapper(CGList(unforgeables), pre="\n") -def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): +def CopyUnforgeablePropertiesToInstance(descriptor, failureCode): """ Copy the unforgeable properties from the unforgeable holder for this interface to the instance object we have. @@ -3422,15 +3422,6 @@ def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): """)) ] - if wrapperCache: - cleanup = dedent( - """ - aCache->ReleaseWrapper(aObject); - aCache->ClearWrapper(); - """) - else: - cleanup = "" - # For proxies, we want to define on the expando object, not directly on the # reflector, so we can make sure we don't get confused by named getters. if descriptor.proxy: @@ -3439,11 +3430,10 @@ def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): JS::Rooted expando(aCx, DOMProxyHandler::EnsureExpandoObject(aCx, aReflector)); if (!expando) { - $*{cleanup} - return false; + $*{failureCode} } """, - cleanup=cleanup))) + failureCode=failureCode))) obj = "expando" else: obj = "aReflector" @@ -3453,12 +3443,11 @@ def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache): JS::Rooted unforgeableHolder(aCx, &js::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject()); if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) { - $*{cleanup} - return false; + $*{failureCode} } """, obj=obj, - cleanup=cleanup))) + failureCode=failureCode))) return CGWrapper(CGList(copyCode), pre="\n").define() @@ -3478,7 +3467,7 @@ def AssertInheritanceChain(descriptor): return asserts -def InitMemberSlots(descriptor, wrapperCache): +def InitMemberSlots(descriptor, failureCode): """ Initialize member slots on our JS object if we're supposed to have some. @@ -3489,22 +3478,13 @@ def InitMemberSlots(descriptor, wrapperCache): """ if not descriptor.interface.hasMembersInSlots(): return "" - if wrapperCache: - clearWrapper = dedent( - """ - aCache->ReleaseWrapper(aObject); - aCache->ClearWrapper(); - """) - else: - clearWrapper = "" return fill( """ if (!UpdateMemberSlots(aCx, aReflector, aObject)) { - $*{clearWrapper} - return false; + $*{failureCode} } """, - clearWrapper=clearWrapper) + failureCode=failureCode) def DeclareProto(): @@ -3561,6 +3541,14 @@ class CGWrapWithCacheMethod(CGAbstractMethod): """) else: preserveWrapper = "PreserveWrapper(aObject);\n" + + failureCode = dedent( + """ + aCache->ReleaseWrapper(aObject); + aCache->ClearWrapper(); + return false; + """) + return fill( """ $*{assertInheritance} @@ -3615,8 +3603,9 @@ class CGWrapWithCacheMethod(CGAbstractMethod): assertInheritance=AssertInheritanceChain(self.descriptor), declareProto=DeclareProto(), createObject=CreateBindingJSObject(self.descriptor, self.properties), - unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, True), - slots=InitMemberSlots(self.descriptor, True), + unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, + failureCode), + slots=InitMemberSlots(self.descriptor, failureCode), preserveWrapper=preserveWrapper) @@ -3655,6 +3644,8 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod): self.properties = properties def definition_body(self): + failureCode = "return false;\n" + return fill( """ $*{assertions} @@ -3673,8 +3664,9 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod): assertions=AssertInheritanceChain(self.descriptor), declareProto=DeclareProto(), createObject=CreateBindingJSObject(self.descriptor, self.properties), - unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, False), - slots=InitMemberSlots(self.descriptor, False)) + unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, + failureCode), + slots=InitMemberSlots(self.descriptor, failureCode)) class CGWrapGlobalMethod(CGAbstractMethod): @@ -3757,7 +3749,7 @@ class CGWrapGlobalMethod(CGAbstractMethod): chromeProperties=chromeProperties, failureCode=failureCode, unforgeable=unforgeable, - slots=InitMemberSlots(self.descriptor, True)) + slots=InitMemberSlots(self.descriptor, failureCode)) class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod): From 40a43f8a0044b32c651f0c39c98afdf4c27e9c92 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 063/102] Bug 1299306 part 2. Call JS_SetImmutablePrototype on Location instances to make their prototype immutable in a more spec-compliant way. r=peterv --- dom/bindings/Codegen.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 1524f7f19944..38768f68d7f8 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3487,6 +3487,24 @@ def InitMemberSlots(descriptor, failureCode): failureCode=failureCode) +def SetImmutablePrototype(descriptor, failureCode): + if not descriptor.hasNonOrdinaryGetPrototypeOf(): + return "" + + return fill( + """ + bool succeeded; + if (!JS_SetImmutablePrototype(aCx, aReflector, &succeeded)) { + ${failureCode} + } + MOZ_ASSERT(succeeded, + "Making a fresh reflector instance have an immutable " + "prototype can internally fail, but it should never be " + "unsuccessful"); + """, + failureCode=failureCode) + + def DeclareProto(): """ Declare the canonicalProto and proto we have for our wrapping operation. @@ -3584,6 +3602,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod): aCache->SetWrapper(aReflector); $*{unforgeable} $*{slots} + $*{setImmutablePrototype} creator.InitializationSucceeded(); MOZ_ASSERT(aCache->GetWrapperPreserveColor() && @@ -3606,6 +3625,8 @@ class CGWrapWithCacheMethod(CGAbstractMethod): unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, failureCode), slots=InitMemberSlots(self.descriptor, failureCode), + setImmutablePrototype=SetImmutablePrototype(self.descriptor, + failureCode), preserveWrapper=preserveWrapper) @@ -3658,6 +3679,8 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod): $*{unforgeable} $*{slots} + + $*{setImmutablePrototype} creator.InitializationSucceeded(); return true; """, @@ -3666,7 +3689,9 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod): createObject=CreateBindingJSObject(self.descriptor, self.properties), unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, failureCode), - slots=InitMemberSlots(self.descriptor, failureCode)) + slots=InitMemberSlots(self.descriptor, failureCode), + setImmutablePrototype=SetImmutablePrototype(self.descriptor, + failureCode)) class CGWrapGlobalMethod(CGAbstractMethod): From f795c235383a1f4c00a635cc07bb4e4adf4828ed Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 064/102] Bug 1299306 part 3. Always support immutable prototypes. r=waldo --- js/src/builtin/TestingFunctions.cpp | 14 -------------- js/src/jit-test/lib/immutable-prototype.js | 5 +---- js/src/jsfriendapi.h | 3 --- js/src/jsobj.cpp | 16 +--------------- .../ecma_6/extensions/setImmutablePrototype.js | 11 +---------- 5 files changed, 3 insertions(+), 46 deletions(-) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 59439381da57..7965c78bae57 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -3112,15 +3112,6 @@ ByteSizeOfScript(JSContext*cx, unsigned argc, Value* vp) return true; } -static bool -ImmutablePrototypesEnabled(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - - args.rval().setBoolean(JS_ImmutablePrototypesEnabled()); - return true; -} - static bool SetImmutablePrototype(JSContext* cx, unsigned argc, Value* vp) { @@ -3991,11 +3982,6 @@ gc::ZealModeHelpText), "byteSizeOfScript(f)", " Return the size in bytes occupied by the function |f|'s JSScript.\n"), - JS_FN_HELP("immutablePrototypesEnabled", ImmutablePrototypesEnabled, 0, 0, -"immutablePrototypesEnabled()", -" Returns true if immutable-prototype behavior (triggered by setImmutablePrototype)\n" -" is enabled, such that modifying an immutable prototype will fail."), - JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0, "setImmutablePrototype(obj)", " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n" diff --git a/js/src/jit-test/lib/immutable-prototype.js b/js/src/jit-test/lib/immutable-prototype.js index ced47ecdac5e..75c1f4e3ca94 100644 --- a/js/src/jit-test/lib/immutable-prototype.js +++ b/js/src/jit-test/lib/immutable-prototype.js @@ -1,7 +1,4 @@ function globalPrototypeChainIsMutable() { - if (typeof immutablePrototypesEnabled !== "function") - return true; - - return !immutablePrototypesEnabled(); + return false; } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index dce1e203d3a6..1fc55819dd22 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -74,9 +74,6 @@ JS_ObjectCountDynamicSlots(JS::HandleObject obj); extern JS_FRIEND_API(size_t) JS_SetProtoCalled(JSContext* cx); -extern JS_FRIEND_API(bool) -JS_ImmutablePrototypesEnabled(); - extern JS_FRIEND_API(size_t) JS_GetCustomIteratorCount(JSContext* cx); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 4ac90cfcd339..c96fa7c000f6 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2493,20 +2493,6 @@ js::GetPrototypeIfOrdinary(JSContext* cx, HandleObject obj, bool* isOrdinary, return true; } -// Our immutable-prototype behavior is non-standard, and it's unclear whether -// it's shippable. (Or at least it's unclear whether it's shippable with any -// provided-by-default uses exposed to script.) If this bool is true, -// immutable-prototype behavior is enforced; if it's false, behavior is not -// enforced, and immutable-prototype bits stored on objects are completely -// ignored. -static const bool ImmutablePrototypesEnabled = true; - -JS_FRIEND_API(bool) -JS_ImmutablePrototypesEnabled() -{ - return ImmutablePrototypesEnabled; -} - /*** ES6 standard internal methods ***************************************************************/ bool @@ -2527,7 +2513,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object return result.succeed(); /* Disallow mutation of immutable [[Prototype]]s. */ - if (obj->staticPrototypeIsImmutable() && ImmutablePrototypesEnabled) + if (obj->staticPrototypeIsImmutable()) return result.fail(JSMSG_CANT_SET_PROTO); /* diff --git a/js/src/tests/ecma_6/extensions/setImmutablePrototype.js b/js/src/tests/ecma_6/extensions/setImmutablePrototype.js index ef89d320e8e3..9226dcce2d74 100644 --- a/js/src/tests/ecma_6/extensions/setImmutablePrototype.js +++ b/js/src/tests/ecma_6/extensions/setImmutablePrototype.js @@ -36,13 +36,6 @@ if (typeof setImmutablePrototype !== "function") } } -if (typeof immutablePrototypesEnabled !== "function" && - typeof SpecialPowers !== "undefined") -{ - immutablePrototypesEnabled = - SpecialPowers.Cu.getJSTestingFunctions().immutablePrototypesEnabled; -} - if (typeof wrap !== "function") { // good enough @@ -93,9 +86,7 @@ function checkPrototypeMutationFailure(obj, desc) function runNormalTests(global) { - if (typeof setImmutablePrototype !== "function" || - typeof immutablePrototypesEnabled !== "function" || - !immutablePrototypesEnabled()) + if (typeof setImmutablePrototype !== "function") { print("no testable setImmutablePrototype function available, skipping tests"); return; From 45276cf8ca346cff8147a1df6ca006383057fe10 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Fri, 2 Sep 2016 17:55:38 -0400 Subject: [PATCH 065/102] Bug 1299306 part 4. Remove the existing Location hardcoding in js::SetPrototype now that Location handles that itself. r=waldo --- js/src/jsobj.cpp | 10 ---------- testing/web-platform/meta/MANIFEST.json | 6 ++++++ .../location-prototype-setting.html | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting.html diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index c96fa7c000f6..3efd26151f4e 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2536,16 +2536,6 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object return false; } - /* - * Explicitly disallow mutating the [[Prototype]] of Location objects - * for flash-related security reasons. - */ - if (!strcmp(obj->getClass()->name, "Location")) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_SET_PROTO_OF, - "incompatible Location object"); - return false; - } - /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ bool extensible; if (!IsExtensible(cx, obj, &extensible)) diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 132e95933e41..9534de50da1e 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -37796,6 +37796,12 @@ "url": "/editing/other/delete.html" } ], + "html/browsers/history/the-location-interface/location-prototype-setting.html": [ + { + "path": "html/browsers/history/the-location-interface/location-prototype-setting.html", + "url": "/html/browsers/history/the-location-interface/location-prototype-setting.html" + } + ], "html/semantics/forms/the-form-element/form-submission-sandbox.html": [ { "path": "html/semantics/forms/the-form-element/form-submission-sandbox.html", diff --git a/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting.html b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting.html new file mode 100644 index 000000000000..726aaea23ab4 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/history/the-location-interface/location-prototype-setting.html @@ -0,0 +1,18 @@ + + +[[SetPrototypeOf]] on a location object should return false + + + From e6c63d0931f7a251a1c21b1cd2eeb04200d4ab50 Mon Sep 17 00:00:00 2001 From: Mason Chang Date: Fri, 2 Sep 2016 15:00:29 -0700 Subject: [PATCH 066/102] Bug 1298484. Use cleartype and convert to grayscale AA for skia windows fonts if cleartype is disabled system wide. r=lsalzman --- gfx/2d/2D.h | 1 + gfx/2d/DrawTargetSkia.cpp | 42 ++++++++++++++++++--------- gfx/2d/HelpersWinFonts.h | 58 +++++++++++++++++++++++++++++++++++++ gfx/2d/ScaledFontDWrite.cpp | 42 ++------------------------- gfx/2d/ScaledFontDWrite.h | 2 +- gfx/2d/ScaledFontWin.cpp | 8 +++++ gfx/2d/ScaledFontWin.h | 1 + 7 files changed, 101 insertions(+), 53 deletions(-) create mode 100644 gfx/2d/HelpersWinFonts.h diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index b89aa24bf9fa..518134962a10 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -672,6 +672,7 @@ public: typedef void (*FontDescriptorOutput)(const uint8_t *aData, uint32_t aLength, Float aFontSize, void *aBaton); virtual FontType GetType() const = 0; + virtual AntialiasMode GetDefaultAAMode() { return AntialiasMode::DEFAULT; } /** This allows getting a path that describes the outline of a set of glyphs. * A target is passed in so that the guarantee is made the returned path diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 1f7b9fb74c78..6dfe95374277 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -1183,12 +1183,8 @@ ShouldUseCGToFillGlyphs(const GlyphRenderingOptions* aOptions, const Pattern& aP #endif -void -DrawTargetSkia::FillGlyphs(ScaledFont *aFont, - const GlyphBuffer &aBuffer, - const Pattern &aPattern, - const DrawOptions &aOptions, - const GlyphRenderingOptions *aRenderingOptions) +static bool +CanDrawFont(ScaledFont* aFont) { switch (aFont->GetType()) { case FontType::SKIA: @@ -1197,8 +1193,20 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, case FontType::MAC: case FontType::GDI: case FontType::DWRITE: - break; + return true; default: + return false; + } +} + +void +DrawTargetSkia::FillGlyphs(ScaledFont *aFont, + const GlyphBuffer &aBuffer, + const Pattern &aPattern, + const DrawOptions &aOptions, + const GlyphRenderingOptions *aRenderingOptions) +{ + if (!CanDrawFont(aFont)) { return; } @@ -1219,11 +1227,18 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, } AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern); + AntialiasMode aaMode = aFont->GetDefaultAAMode(); + if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { + aaMode = aOptions.mAntialiasMode; + } + bool aaEnabled = aaMode != AntialiasMode::NONE; + + paint.mPaint.setAntiAlias(aaEnabled); paint.mPaint.setTypeface(typeface); paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize)); paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aOptions.mAntialiasMode); + bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aaMode); paint.mPaint.setLCDRenderText(shouldLCDRenderText); bool useSubpixelText = true; @@ -1237,7 +1252,7 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, useSubpixelText = false; break; case FontType::MAC: - if (aOptions.mAntialiasMode == AntialiasMode::GRAY) { + if (aaMode == AntialiasMode::GRAY) { // Normally, Skia enables LCD FontSmoothing which creates thicker fonts // and also enables subpixel AA. CoreGraphics without font smoothing // explicitly creates thinner fonts and grayscale AA. @@ -1257,14 +1272,15 @@ DrawTargetSkia::FillGlyphs(ScaledFont *aFont, } break; case FontType::GDI: + { if (!shouldLCDRenderText) { - // If we have non LCD GDI text, Cairo currently always uses cleartype fonts and - // converts them to grayscale. Force Skia to do the same, otherwise we use - // GDI fonts with the ANTIALIASED_QUALITY which is generally bolder than - // Cleartype fonts. + // If we have non LCD GDI text, render the fonts as cleartype and convert them + // to grayscale. This seems to be what Chrome and IE are doing on Windows 7. + // This also applies if cleartype is disabled system wide. paint.mPaint.setFlags(paint.mPaint.getFlags() | SkPaint::kGenA8FromLCD_Flag); } break; + } default: break; } diff --git a/gfx/2d/HelpersWinFonts.h b/gfx/2d/HelpersWinFonts.h new file mode 100644 index 000000000000..6f6a5fda7533 --- /dev/null +++ b/gfx/2d/HelpersWinFonts.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 20; 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/. */ + +namespace mozilla { +namespace gfx { + +// Cleartype can be dynamically enabled/disabled, so we have to check it +// everytime we want to render some text. +static BYTE +GetSystemTextQuality() +{ + BOOL font_smoothing; + UINT smoothing_type; + + if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { + return DEFAULT_QUALITY; + } + + if (font_smoothing) { + if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, + 0, &smoothing_type, 0)) { + return DEFAULT_QUALITY; + } + + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { + return CLEARTYPE_QUALITY; + } + + return ANTIALIASED_QUALITY; + } + + return DEFAULT_QUALITY; +} + +static AntialiasMode +GetSystemDefaultAAMode() +{ + AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; + + switch (GetSystemTextQuality()) { + case CLEARTYPE_QUALITY: + defaultMode = AntialiasMode::SUBPIXEL; + break; + case ANTIALIASED_QUALITY: + defaultMode = AntialiasMode::GRAY; + break; + case DEFAULT_QUALITY: + defaultMode = AntialiasMode::NONE; + break; + } + + return defaultMode; +} + +} // namespace gfx +} // namespace mozilla diff --git a/gfx/2d/ScaledFontDWrite.cpp b/gfx/2d/ScaledFontDWrite.cpp index 9441b4b4e977..2c233f4e25f6 100644 --- a/gfx/2d/ScaledFontDWrite.cpp +++ b/gfx/2d/ScaledFontDWrite.cpp @@ -22,35 +22,11 @@ using namespace std; #include "cairo-win32.h" #endif +#include "HelpersWinFonts.h" + namespace mozilla { namespace gfx { -static BYTE -GetSystemTextQuality() -{ - BOOL font_smoothing; - UINT smoothing_type; - - if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { - return DEFAULT_QUALITY; - } - - if (font_smoothing) { - if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, - 0, &smoothing_type, 0)) { - return DEFAULT_QUALITY; - } - - if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { - return CLEARTYPE_QUALITY; - } - - return ANTIALIASED_QUALITY; - } - - return DEFAULT_QUALITY; -} - #define GASP_TAG 0x70736167 #define GASP_DOGRAY 0x2 @@ -315,19 +291,7 @@ ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton AntialiasMode ScaledFontDWrite::GetDefaultAAMode() { - AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; - - switch (GetSystemTextQuality()) { - case CLEARTYPE_QUALITY: - defaultMode = AntialiasMode::SUBPIXEL; - break; - case ANTIALIASED_QUALITY: - defaultMode = AntialiasMode::GRAY; - break; - case DEFAULT_QUALITY: - defaultMode = AntialiasMode::NONE; - break; - } + AntialiasMode defaultMode = GetSystemDefaultAAMode(); if (defaultMode == AntialiasMode::GRAY) { if (!DoGrayscale(mFontFace, mSize)) { diff --git a/gfx/2d/ScaledFontDWrite.h b/gfx/2d/ScaledFontDWrite.h index 7e72dbbf0e89..8e9c6c8e152e 100644 --- a/gfx/2d/ScaledFontDWrite.h +++ b/gfx/2d/ScaledFontDWrite.h @@ -44,7 +44,7 @@ public: virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton); - virtual AntialiasMode GetDefaultAAMode(); + virtual AntialiasMode GetDefaultAAMode() override; #ifdef USE_SKIA virtual SkTypeface* GetSkTypeface(); diff --git a/gfx/2d/ScaledFontWin.cpp b/gfx/2d/ScaledFontWin.cpp index 5bf29a79076a..8ce370c960b3 100644 --- a/gfx/2d/ScaledFontWin.cpp +++ b/gfx/2d/ScaledFontWin.cpp @@ -18,6 +18,8 @@ #include "cairo-win32.h" #endif +#include "HelpersWinFonts.h" + namespace mozilla { namespace gfx { @@ -86,6 +88,12 @@ ScaledFontWin::GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) return true; } +AntialiasMode +ScaledFontWin::GetDefaultAAMode() +{ + return GetSystemDefaultAAMode(); +} + #ifdef USE_SKIA SkTypeface* ScaledFontWin::GetSkTypeface() { diff --git a/gfx/2d/ScaledFontWin.h b/gfx/2d/ScaledFontWin.h index e5b55d630a5f..f2f6072ae4fc 100644 --- a/gfx/2d/ScaledFontWin.h +++ b/gfx/2d/ScaledFontWin.h @@ -22,6 +22,7 @@ public: bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) override; virtual bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override; + virtual AntialiasMode GetDefaultAAMode() override; #ifdef USE_SKIA virtual SkTypeface* GetSkTypeface(); From 9a68e5763ce869ca815860fed4eb97daa89d7ba1 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 2 Sep 2016 15:30:48 -0700 Subject: [PATCH 067/102] Bug 1298640 - Track top-level functions in eval/global bodies for all-or-nothing redeclaration checks. (r=Waldo) --- js/src/frontend/BytecodeEmitter.cpp | 21 ++--- js/src/frontend/Parser.cpp | 24 ++--- js/src/frontend/Parser.h | 6 +- js/src/frontend/SharedContext.h | 19 ---- js/src/js.msg | 1 + .../eval-nondefinable-function.js | 10 +++ js/src/vm/EnvironmentObject.cpp | 88 ++++++++++++++++++- js/src/vm/EnvironmentObject.h | 4 + js/src/vm/Interpreter.cpp | 12 +-- js/src/vm/Scope.cpp | 24 +++-- js/src/vm/Scope.h | 43 ++++++--- 11 files changed, 164 insertions(+), 88 deletions(-) create mode 100644 js/src/tests/ecma_6/LexicalEnvironment/eval-nondefinable-function.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 4426366f167e..1631909412cf 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1170,22 +1170,15 @@ BytecodeEmitter::EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce, F class DynamicBindingIter : public BindingIter { - uint32_t functionEnd_; - public: explicit DynamicBindingIter(GlobalSharedContext* sc) - : BindingIter(*sc->bindings), - functionEnd_(sc->functionBindingEnd) - { - MOZ_ASSERT(functionEnd_ >= varStart_ && functionEnd_ <= letStart_); - } + : BindingIter(*sc->bindings) + { } explicit DynamicBindingIter(EvalSharedContext* sc) - : BindingIter(*sc->bindings, /* strict = */ false), - functionEnd_(sc->functionBindingEnd) + : BindingIter(*sc->bindings, /* strict = */ false) { MOZ_ASSERT(!sc->strict()); - MOZ_ASSERT(functionEnd_ >= varStart_ && functionEnd_ <= letStart_); } JSOp bindingOp() const { @@ -1200,10 +1193,6 @@ class DynamicBindingIter : public BindingIter MOZ_CRASH("Bad BindingKind"); } } - - bool isBodyLevelFunction() const { - return index_ < functionEnd_; - } }; bool @@ -1243,7 +1232,7 @@ BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedCon // Define the name in the prologue. Do not emit DEFVAR for // functions that we'll emit DEFFUN for. - if (bi.isBodyLevelFunction()) + if (bi.isTopLevelFunction()) continue; if (!bce->emitAtomOp(name, bi.bindingOp())) @@ -1303,7 +1292,7 @@ BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext for (DynamicBindingIter bi(evalsc); bi; bi++) { MOZ_ASSERT(bi.bindingOp() == JSOP_DEFVAR); - if (bi.isBodyLevelFunction()) + if (bi.isTopLevelFunction()) continue; if (!bce->emitAtomOp(bi.name(), JSOP_DEFVAR)) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index acff9da5a478..67e8290a9f2e 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -275,7 +275,6 @@ EvalSharedContext::EvalSharedContext(ExclusiveContext* cx, JSObject* enclosingEn bool extraWarnings) : SharedContext(cx, Kind::Eval, directives, extraWarnings), enclosingScope_(cx, enclosingScope), - functionBindingEnd(0), bindings(cx) { computeAllowSyntax(enclosingScope); @@ -1374,8 +1373,7 @@ NewEmptyBindingData(ExclusiveContext* cx, LifoAlloc& alloc, uint32_t numBindings template <> Maybe -Parser::newGlobalScopeData(ParseContext::Scope& scope, - uint32_t* functionBindingEnd) +Parser::newGlobalScopeData(ParseContext::Scope& scope) { Vector funs(context); Vector vars(context); @@ -1420,12 +1418,10 @@ Parser::newGlobalScopeData(ParseContext::Scope& scope, BindingName* start = bindings->names; BindingName* cursor = start; - // Keep track of what vars are functions. This is only used in BCE to omit - // superfluous DEFVARs. PodCopy(cursor, funs.begin(), funs.length()); cursor += funs.length(); - *functionBindingEnd = cursor - start; + bindings->varStart = cursor - start; PodCopy(cursor, vars.begin(), vars.length()); cursor += vars.length(); @@ -1510,8 +1506,7 @@ Parser::newModuleScopeData(ParseContext::Scope& scope) template <> Maybe -Parser::newEvalScopeData(ParseContext::Scope& scope, - uint32_t* functionBindingEnd) +Parser::newEvalScopeData(ParseContext::Scope& scope) { Vector funs(context); Vector vars(context); @@ -1545,8 +1540,8 @@ Parser::newEvalScopeData(ParseContext::Scope& scope, // superfluous DEFVARs. PodCopy(cursor, funs.begin(), funs.length()); cursor += funs.length(); - *functionBindingEnd = cursor - start; + bindings->varStart = cursor - start; PodCopy(cursor, vars.begin(), vars.length()); bindings->length = numBindings; } @@ -1830,14 +1825,10 @@ Parser::evalBody(EvalSharedContext* evalsc) if (!FoldConstants(context, &body, this)) return nullptr; - uint32_t functionBindingEnd = 0; - Maybe bindings = - newEvalScopeData(pc->varScope(), &functionBindingEnd); + Maybe bindings = newEvalScopeData(pc->varScope()); if (!bindings) return nullptr; - evalsc->bindings = *bindings; - evalsc->functionBindingEnd = functionBindingEnd; return body; } @@ -1864,13 +1855,10 @@ Parser::globalBody(GlobalSharedContext* globalsc) if (!FoldConstants(context, &body, this)) return nullptr; - uint32_t functionBindingEnd = 0; - Maybe bindings = newGlobalScopeData(pc->varScope(), &functionBindingEnd); + Maybe bindings = newGlobalScopeData(pc->varScope()); if (!bindings) return nullptr; - globalsc->bindings = *bindings; - globalsc->functionBindingEnd = functionBindingEnd; return body; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 759ec1756a09..a08ae0f0c049 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1290,11 +1290,9 @@ class Parser final : private JS::AutoGCRooter, public StrictModeGetter // Required on Scope exit. bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope); - mozilla::Maybe newGlobalScopeData(ParseContext::Scope& scope, - uint32_t* functionBindingEnd); + mozilla::Maybe newGlobalScopeData(ParseContext::Scope& scope); mozilla::Maybe newModuleScopeData(ParseContext::Scope& scope); - mozilla::Maybe newEvalScopeData(ParseContext::Scope& scope, - uint32_t* functionBindingEnd); + mozilla::Maybe newEvalScopeData(ParseContext::Scope& scope); mozilla::Maybe newFunctionScopeData(ParseContext::Scope& scope, bool hasParameterExprs); mozilla::Maybe newVarScopeData(ParseContext::Scope& scope); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index cd9619b9cf1e..069d48414c6e 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -380,22 +380,12 @@ class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext ScopeKind scopeKind_; public: - // We omit DEFFVAR in the prologue for global functions since we emit - // DEFFUN for them. In order to distinguish function vars, functions are - // ordered before vars. See Parser::newGlobalScopeData and - // EmitterScope::enterGlobal. - // - // This is only used in BytecodeEmitter, and is thus not kept in - // GlobalScope::Data. - uint32_t functionBindingEnd; - Rooted bindings; GlobalSharedContext(ExclusiveContext* cx, ScopeKind scopeKind, Directives directives, bool extraWarnings) : SharedContext(cx, Kind::Global, directives, extraWarnings), scopeKind_(scopeKind), - functionBindingEnd(0), bindings(cx) { MOZ_ASSERT(scopeKind == ScopeKind::Global || scopeKind == ScopeKind::NonSyntactic); @@ -423,15 +413,6 @@ class MOZ_STACK_CLASS EvalSharedContext : public SharedContext RootedScope enclosingScope_; public: - // We omit DEFFVAR in the prologue for body-level functions since we emit - // DEFFUN for them. In order to distinguish function vars, functions are - // ordered before vars. See Parser::newEvalScopeData and - // EmitterScope::enterEval. - // - // This is only used in BytecodeEmitter, and is thus not kept in - // EvalScope::Data. - uint32_t functionBindingEnd; - Rooted bindings; EvalSharedContext(ExclusiveContext* cx, JSObject* enclosingEnv, Scope* enclosingScope, diff --git a/js/src/js.msg b/js/src/js.msg index 0362ff4f134e..f086de8d5134 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -118,6 +118,7 @@ MSG_DEF(JSMSG_TOO_MANY_CON_SPREADARGS, 0, JSEXN_RANGEERR, "too many constructor MSG_DEF(JSMSG_TOO_MANY_FUN_SPREADARGS, 0, JSEXN_RANGEERR, "too many function arguments") MSG_DEF(JSMSG_UNINITIALIZED_LEXICAL, 1, JSEXN_REFERENCEERR, "can't access lexical declaration `{0}' before initialization") MSG_DEF(JSMSG_BAD_CONST_ASSIGN, 1, JSEXN_TYPEERR, "invalid assignment to const `{0}'") +MSG_DEF(JSMSG_CANT_DECLARE_GLOBAL_BINDING, 2, JSEXN_TYPEERR, "cannot declare global binding `{0}': {1}") // Date MSG_DEF(JSMSG_INVALID_DATE, 0, JSEXN_RANGEERR, "invalid date") diff --git a/js/src/tests/ecma_6/LexicalEnvironment/eval-nondefinable-function.js b/js/src/tests/ecma_6/LexicalEnvironment/eval-nondefinable-function.js new file mode 100644 index 000000000000..bd5dcf79787f --- /dev/null +++ b/js/src/tests/ecma_6/LexicalEnvironment/eval-nondefinable-function.js @@ -0,0 +1,10 @@ +try { + eval("var shouldNotBeDefined1; function NaN(){}; var shouldNotBeDefined2;"); +} catch (e) { +} + +assertEq(Object.getOwnPropertyDescriptor(this, 'shouldNotBeDefined2'), undefined); +assertEq(Object.getOwnPropertyDescriptor(this, 'shouldNotBeDefined1'), undefined); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index e762d084cc33..9ac758e1ffad 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -3182,6 +3182,59 @@ js::CheckVarNameConflict(JSContext* cx, Handle lexica return true; } +static void +ReportCannotDeclareGlobalBinding(JSContext* cx, HandlePropertyName name, const char* reason) +{ + JSAutoByteString printable; + if (AtomToPrintableString(cx, name, &printable)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, + JSMSG_CANT_DECLARE_GLOBAL_BINDING, + printable.ptr(), reason); + } +} + +bool +js::CheckCanDeclareGlobalBinding(JSContext* cx, Handle global, + HandlePropertyName name, bool isFunction) +{ + RootedId id(cx, NameToId(name)); + Rooted desc(cx); + if (!GetOwnPropertyDescriptor(cx, global, id, &desc)) + return false; + + // ES 8.1.14.15 CanDeclareGlobalVar + // ES 8.1.14.16 CanDeclareGlobalFunction + + // Step 4. + if (!desc.object()) { + // 8.1.14.15 step 6. + // 8.1.14.16 step 5. + if (global->nonProxyIsExtensible()) + return true; + + ReportCannotDeclareGlobalBinding(cx, name, "global is non-extensible"); + return false; + } + + // Global functions have additional restrictions. + if (isFunction) { + // 8.1.14.16 step 6. + if (desc.configurable()) + return true; + + // 8.1.14.16 step 7. + if (desc.isDataDescriptor() && desc.writable() && desc.enumerable()) + return true; + + ReportCannotDeclareGlobalBinding(cx, name, + "property must be configurable or " + "both writable and enumerable"); + return false; + } + + return true; +} + bool js::CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script, Handle lexicalEnv, @@ -3196,14 +3249,32 @@ js::CheckGlobalDeclarationConflicts(JSContext* cx, HandleScript script, RootedPropertyName name(cx); Rooted bi(cx, BindingIter(script)); + // ES 15.1.11 GlobalDeclarationInstantiation + + // Step 6. + // + // Check 'var' declarations do not conflict with existing bindings in the + // global lexical environment. for (; bi; bi++) { if (bi.kind() != BindingKind::Var) break; name = bi.name()->asPropertyName(); if (!CheckVarNameConflict(cx, lexicalEnv, name)) return false; + + // Step 10 and 12. + // + // Check that global functions and vars may be declared. + if (varObj->is()) { + Handle global = varObj.as(); + if (!CheckCanDeclareGlobalBinding(cx, global, name, bi.isTopLevelFunction())) + return false; + } } + // Step 5. + // + // Check that lexical bindings do not conflict. for (; bi; bi++) { name = bi.name()->asPropertyName(); if (!CheckLexicalNameConflict(cx, lexicalEnv, varObj, name)) @@ -3256,7 +3327,9 @@ js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script, RootedObject obj(cx, scopeChain); - // ES6 18.2.1.2 step d + // ES 18.2.1.3. + + // Step 5. // // Check that a direct eval will not hoist 'var' bindings over lexical // bindings with the same name. @@ -3266,6 +3339,19 @@ js::CheckEvalDeclarationConflicts(JSContext* cx, HandleScript script, obj = obj->enclosingEnvironment(); } + // Step 8. + // + // Check that global functions may be declared. + if (varObj->is()) { + Handle global = varObj.as(); + RootedPropertyName name(cx); + for (Rooted bi(cx, BindingIter(script)); bi; bi++) { + name = bi.name()->asPropertyName(); + if (!CheckCanDeclareGlobalBinding(cx, global, name, bi.isTopLevelFunction())) + return false; + } + } + return true; } diff --git a/js/src/vm/EnvironmentObject.h b/js/src/vm/EnvironmentObject.h index 710326689769..f97ff1f9f586 100644 --- a/js/src/vm/EnvironmentObject.h +++ b/js/src/vm/EnvironmentObject.h @@ -1082,6 +1082,10 @@ MOZ_MUST_USE bool CheckVarNameConflict(JSContext* cx, Handle lexicalEnv, HandlePropertyName name); +MOZ_MUST_USE bool +CheckCanDeclareGlobalBinding(JSContext* cx, Handle global, + HandlePropertyName name, bool isFunction); + MOZ_MUST_USE bool CheckLexicalNameConflict(JSContext* cx, Handle lexicalEnv, HandleObject varObj, HandlePropertyName name); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index ae99fe3bc5b9..d8b133feed0c 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -4396,15 +4396,9 @@ js::DefFunOperation(JSContext* cx, HandleScript script, HandleObject envChain, if (!DefineProperty(cx, parent, name, rval, nullptr, nullptr, attrs)) return false; } else { - if (shape->isAccessorDescriptor() || !shape->writable() || !shape->enumerable()) { - JSAutoByteString bytes; - if (AtomToPrintableString(cx, name, &bytes)) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_REDEFINE_PROP, - bytes.ptr()); - } - - return false; - } + MOZ_ASSERT(shape->isDataDescriptor()); + MOZ_ASSERT(shape->writable()); + MOZ_ASSERT(shape->enumerable()); } // Careful: the presence of a shape, even one appearing to derive from diff --git a/js/src/vm/Scope.cpp b/js/src/vm/Scope.cpp index ef509c67db8e..11b40cafe1dd 100644 --- a/js/src/vm/Scope.cpp +++ b/js/src/vm/Scope.cpp @@ -1205,7 +1205,7 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla if (flags & IsNamedLambda) { // Named lambda binding is weird. Normal BindingKind ordering rules // don't apply. - init(0, 0, 0, 0, 0, + init(0, 0, 0, 0, 0, 0, CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), data.names, data.length); @@ -1213,10 +1213,11 @@ BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t fla // imports - [0, 0) // positional formals - [0, 0) // other formals - [0, 0) + // top-level funcs - [0, 0) // vars - [0, 0) // lets - [0, data.constStart) // consts - [data.constStart, data.length) - init(0, 0, 0, 0, data.constStart, + init(0, 0, 0, 0, 0, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots | flags, firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_), data.names, data.length); @@ -1233,10 +1234,11 @@ BindingIter::init(FunctionScope::Data& data, uint8_t flags) // imports - [0, 0) // positional formals - [0, data.nonPositionalFormalStart) // other formals - [data.nonPositionalParamStart, data.varStart) + // top-level funcs - [data.varStart, data.varStart) // vars - [data.varStart, data.length) // lets - [data.length, data.length) // consts - [data.length, data.length) - init(0, data.nonPositionalFormalStart, data.varStart, data.length, data.length, + init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length, flags, 0, JSSLOT_FREE(&CallObject::class_), data.names, data.length); @@ -1248,10 +1250,11 @@ BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot) // imports - [0, 0) // positional formals - [0, 0) // other formals - [0, 0) + // top-level funcs - [0, 0) // vars - [0, data.length) // lets - [data.length, data.length) // consts - [data.length, data.length) - init(0, 0, 0, data.length, data.length, + init(0, 0, 0, 0, data.length, data.length, CanHaveFrameSlots | CanHaveEnvironmentSlots, firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_), data.names, data.length); @@ -1263,10 +1266,11 @@ BindingIter::init(GlobalScope::Data& data) // imports - [0, 0) // positional formals - [0, 0) // other formals - [0, 0) - // vars - [0, data.letStart) + // top-level funcs - [0, data.varStart) + // vars - [data.varStart, data.letStart) // lets - [data.letStart, data.constStart) // consts - [data.constStart, data.length) - init(0, 0, 0, data.letStart, data.constStart, + init(0, 0, 0, data.varStart, data.letStart, data.constStart, CannotHaveSlots, UINT32_MAX, UINT32_MAX, data.names, data.length); @@ -1291,10 +1295,11 @@ BindingIter::init(EvalScope::Data& data, bool strict) // imports - [0, 0) // positional formals - [0, 0) // other formals - [0, 0) - // vars - [0, data.length) + // top-level funcs - [0, data.varStart) + // vars - [data.varStart, data.length) // lets - [data.length, data.length) // consts - [data.length, data.length) - init(0, 0, 0, data.length, data.length, + init(0, 0, 0, data.varStart, data.length, data.length, flags, firstFrameSlot, firstEnvironmentSlot, data.names, data.length); } @@ -1305,10 +1310,11 @@ BindingIter::init(ModuleScope::Data& data) // imports - [0, data.varStart) // positional formals - [data.varStart, data.varStart) // other formals - [data.varStart, data.varStart) + // top-level funcs - [data.varStart, data.varStart) // vars - [data.varStart, data.letStart) // lets - [data.letStart, data.constStart) // consts - [data.constStart, data.length) - init(data.varStart, data.varStart, data.varStart, data.letStart, data.constStart, + init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart, CanHaveFrameSlots | CanHaveEnvironmentSlots, 0, JSSLOT_FREE(&ModuleEnvironmentObject::class_), data.names, data.length); diff --git a/js/src/vm/Scope.h b/js/src/vm/Scope.h index f8180f4dac5d..665ee5c02597 100644 --- a/js/src/vm/Scope.h +++ b/js/src/vm/Scope.h @@ -628,9 +628,11 @@ class GlobalScope : public Scope { // Bindings are sorted by kind. // - // vars - [0, letStart) - // lets - [letStart, constStart) - // consts - [constStart, length) + // top-level funcs - [0, varStart) + // vars - [varStart, letStart) + // lets - [letStart, constStart) + // consts - [constStart, length) + uint32_t varStart; uint32_t letStart; uint32_t constStart; uint32_t length; @@ -722,7 +724,12 @@ class EvalScope : public Scope { // All bindings in an eval script are 'var' bindings. The implicit // lexical scope around the eval is present regardless of strictness - // and is its own LexicalScope. + // and is its own LexicalScope. However, we need to track top-level + // functions specially for redeclaration checks. + // + // top-level funcs - [0, varStart) + // vars - [varStart, length) + uint32_t varStart; uint32_t length; // Frame slots [0, nextFrameSlot) are live when this is the innermost @@ -892,7 +899,8 @@ class BindingIter // // imports - [0, positionalFormalStart) // positional formals - [positionalFormalStart, nonPositionalFormalStart) - // other formals - [nonPositionalParamStart, varStart) + // other formals - [nonPositionalParamStart, topLevelFunctionStart) + // top-level funcs - [topLevelFunctionStart, varStart) // vars - [varStart, letStart) // lets - [letStart, constStart) // consts - [constStart, length) @@ -902,6 +910,7 @@ class BindingIter // imports - name // positional formals - argument slot // other formals - frame slot + // top-level funcs - frame slot // vars - frame slot // lets - frame slot // consts - frame slot @@ -909,13 +918,15 @@ class BindingIter // Access method when closed over: // // imports - name - // positional formals - environment slot - // other formals - environment slot - // vars - environment slot - // lets - environment slot - // consts - environment slot + // positional formals - environment slot or name + // other formals - environment slot or name + // top-level funcs - environment slot or name + // vars - environment slot or name + // lets - environment slot or name + // consts - environment slot or name uint32_t positionalFormalStart_; uint32_t nonPositionalFormalStart_; + uint32_t topLevelFunctionStart_; uint32_t varStart_; uint32_t letStart_; uint32_t constStart_; @@ -947,12 +958,14 @@ class BindingIter BindingName* names_; void init(uint32_t positionalFormalStart, uint32_t nonPositionalFormalStart, - uint32_t varStart, uint32_t letStart, uint32_t constStart, + uint32_t topLevelFunctionStart, uint32_t varStart, + uint32_t letStart, uint32_t constStart, uint8_t flags, uint32_t firstFrameSlot, uint32_t firstEnvironmentSlot, BindingName* names, uint32_t length) { positionalFormalStart_ = positionalFormalStart; nonPositionalFormalStart_ = nonPositionalFormalStart; + topLevelFunctionStart_ = topLevelFunctionStart; varStart_ = varStart; letStart_ = letStart; constStart_ = constStart; @@ -1113,7 +1126,7 @@ class BindingIter MOZ_ASSERT(!done()); if (index_ < positionalFormalStart_) return BindingKind::Import; - if (index_ < varStart_) { + if (index_ < topLevelFunctionStart_) { // When the parameter list has expressions, the parameters act // like lexical bindings and have TDZ. if (hasFormalParameterExprs()) @@ -1129,6 +1142,11 @@ class BindingIter return BindingKind::Const; } + bool isTopLevelFunction() const { + MOZ_ASSERT(!done()); + return index_ >= topLevelFunctionStart_ && index_ < varStart_; + } + bool hasArgumentSlot() const { MOZ_ASSERT(!done()); if (hasFormalParameterExprs()) @@ -1267,6 +1285,7 @@ class BindingIterOperations bool closedOver() const { return iter().closedOver(); } BindingLocation location() const { return iter().location(); } BindingKind kind() const { return iter().kind(); } + bool isTopLevelFunction() const { return iter().isTopLevelFunction(); } bool hasArgumentSlot() const { return iter().hasArgumentSlot(); } uint16_t argumentSlot() const { return iter().argumentSlot(); } uint32_t nextFrameSlot() const { return iter().nextFrameSlot(); } From 2ba570bd26a15ff5e5c72474d0ad06f2bc328dfe Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 2 Sep 2016 15:30:48 -0700 Subject: [PATCH 068/102] Bug 1298570 - Check result of getArg when decompiling. (r=efaust) --- js/src/jsopcode.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index df9764bab696..229aec9fe715 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1179,6 +1179,8 @@ ExpressionDecompiler::decompilePC(jsbytecode* pc) case JSOP_GETARG: { unsigned slot = GET_ARGNO(pc); JSAtom* atom = getArg(slot); + if (!atom) + return false; return write(atom); } case JSOP_GETLOCAL: { From 52683a208a53835df7d520f0a23adf091c19c977 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Fri, 2 Sep 2016 15:30:48 -0700 Subject: [PATCH 069/102] Bug 1299121 - Only report ACCESS_LOST for named lambda callees when getting. (r=jimb) --- js/src/jit-test/tests/debug/bug1299121.js | 10 ++++++ js/src/vm/EnvironmentObject.cpp | 41 +++++++++++------------ 2 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 js/src/jit-test/tests/debug/bug1299121.js diff --git a/js/src/jit-test/tests/debug/bug1299121.js b/js/src/jit-test/tests/debug/bug1299121.js new file mode 100644 index 000000000000..4139c977276b --- /dev/null +++ b/js/src/jit-test/tests/debug/bug1299121.js @@ -0,0 +1,10 @@ +var g = newGlobal(); +g.parent = this; +g.eval("(" + function() { + var dbg = new Debugger(parent); + dbg.onExceptionUnwind = function(frame) { + frame.eval("h = 3"); + }; +} + ")()"); +g = function h() { } +g(); diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index 9ac758e1ffad..d67e46c452df 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -1375,28 +1375,27 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler }; /* - * This function handles access to unaliased locals/formals. Since they are - * unaliased, the values of these variables are not stored in the slots of - * the normal Call/ClonedBlockObject scope objects and thus must be - * recovered from somewhere else: - * + if the invocation for which the scope was created is still executing, + * This function handles access to unaliased locals/formals. Since they + * are unaliased, the values of these variables are not stored in the + * slots of the normal CallObject and LexicalEnvironmentObject + * environments and thus must be recovered from somewhere else: + * + if the invocation for which the env was created is still executing, * there is a JS frame live on the stack holding the values; - * + if the invocation for which the scope was created finished executing: - * - and there was a DebugEnvironmentProxy associated with scope, then the - * DebugEnvironments::onPop(Call|Block) handler copied out the unaliased - * variables: - * . for block scopes, the unaliased values were copied directly - * into the block object, since there is a slot allocated for every - * block binding, regardless of whether it is aliased; - * . for function scopes, a dense array is created in onPopCall to hold - * the unaliased values and attached to the DebugEnvironmentProxy; + * + if the invocation for which the env was created finished executing: + * - and there was a DebugEnvironmentProxy associated with env, then + * the DebugEnvironments::onPop(Call|Lexical) handler copied out the + * unaliased variables. In both cases, a dense array is created in + * onPop(Call|Lexical) to hold the unaliased values and attached to + * the DebugEnvironmentProxy; * - and there was not a DebugEnvironmentProxy yet associated with the * scope, then the unaliased values are lost and not recoverable. * * Callers should check accessResult for non-failure results: * - ACCESS_UNALIASED if the access was unaliased and completed * - ACCESS_GENERIC if the access was aliased or the property not found - * - ACCESS_LOST if the value has been lost to the debugger + * - ACCESS_LOST if the value has been lost to the debugger and the + * action is GET; if the action is SET, we assign to the + * name of the variable on the environment object */ bool handleUnaliasedAccess(JSContext* cx, Handle debugEnv, Handle env, HandleId id, Action action, @@ -1532,7 +1531,8 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler // Named lambdas that are not closed over are lost. if (loc.kind() == BindingLocation::Kind::NamedLambdaCallee) { - *accessResult = ACCESS_LOST; + if (action == GET) + *accessResult = ACCESS_LOST; return true; } @@ -2031,11 +2031,10 @@ class DebugEnvironmentProxyHandler : public BaseProxyHandler switch (access) { case ACCESS_UNALIASED: return result.succeed(); - case ACCESS_GENERIC: - { - RootedValue envVal(cx, ObjectValue(*env)); - return SetProperty(cx, env, id, v, envVal, result); - } + case ACCESS_GENERIC: { + RootedValue envVal(cx, ObjectValue(*env)); + return SetProperty(cx, env, id, v, envVal, result); + } default: MOZ_CRASH("bad AccessResult"); } From 2f6e3421dbc260fd16a981bfd5e715a0b989c504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Sat, 3 Sep 2016 00:28:50 +0200 Subject: [PATCH 070/102] Bug 1224732 - Draw tab separators with a border rather than background-image. r=gijs --- browser/themes/shared/tabs.inc.css | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/browser/themes/shared/tabs.inc.css b/browser/themes/shared/tabs.inc.css index bead38787022..c8f5493978ff 100644 --- a/browser/themes/shared/tabs.inc.css +++ b/browser/themes/shared/tabs.inc.css @@ -440,12 +440,10 @@ .tabbrowser-tab::after, .tabbrowser-tab::before { - width: 1px; + margin-top: 5px; + margin-bottom: 4px; margin-inline-start: -1px; - background-image: linear-gradient(transparent 5px, - currentColor 5px, - currentColor calc(100% - 4px), - transparent calc(100% - 4px)); + border-left: 1px solid currentColor; opacity: 0.2; } From 6beb9f31ca44127672ffdccebce051255137297f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 2 Sep 2016 16:07:56 -0700 Subject: [PATCH 071/102] Give CompositableChild into its own header and source files. (bug 1299375 part 1, r=nical) --- gfx/layers/client/CompositableChild.cpp | 46 ++++++++++++++++++++++ gfx/layers/client/CompositableChild.h | 50 ++++++++++++++++++++++++ gfx/layers/client/CompositableClient.cpp | 41 +++---------------- gfx/layers/moz.build | 2 + 4 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 gfx/layers/client/CompositableChild.cpp create mode 100644 gfx/layers/client/CompositableChild.h diff --git a/gfx/layers/client/CompositableChild.cpp b/gfx/layers/client/CompositableChild.cpp new file mode 100644 index 000000000000..e1eb0b40bbdc --- /dev/null +++ b/gfx/layers/client/CompositableChild.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 20; 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 "CompositableChild.h" +#include "CompositableClient.h" + +namespace mozilla { +namespace layers { + +CompositableChild::CompositableChild() + : mCompositableClient(nullptr), + mAsyncID(0) +{ + MOZ_COUNT_CTOR(CompositableChild); +} + +CompositableChild::~CompositableChild() +{ + MOZ_COUNT_DTOR(CompositableChild); +} + +void +CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID) +{ + mCompositableClient = aCompositable; + mAsyncID = aAsyncID; +} + +void +CompositableChild::RevokeCompositableClient() +{ + mCompositableClient = nullptr; +} + +void +CompositableChild::ActorDestroy(ActorDestroyReason) +{ + if (mCompositableClient) { + mCompositableClient->mCompositableChild = nullptr; + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/client/CompositableChild.h b/gfx/layers/client/CompositableChild.h new file mode 100644 index 000000000000..81815f5085a8 --- /dev/null +++ b/gfx/layers/client/CompositableChild.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 20; 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_gfx_layers_client_CompositableChild_h +#define mozilla_gfx_layers_client_CompositableChild_h + +#include +#include "IPDLActor.h" +#include "mozilla/layers/PCompositableChild.h" + +namespace mozilla { +namespace layers { + +class CompositableClient; + +/** + * IPDL actor used by CompositableClient to match with its corresponding + * CompositableHost on the compositor side. + * + * CompositableChild is owned by a CompositableClient. + */ +class CompositableChild : public ChildActor +{ +public: + CompositableChild(); + virtual ~CompositableChild(); + + void Init(CompositableClient* aCompositable, uint64_t aAsyncID); + void RevokeCompositableClient(); + + void ActorDestroy(ActorDestroyReason) override; + + CompositableClient* GetCompositableClient() { + return mCompositableClient; + } + uint64_t GetAsyncID() const { + return mAsyncID; + } + +private: + CompositableClient* mCompositableClient; + uint64_t mAsyncID; +}; + +} // namespace layers +} // namespace mozilla + +#endif // mozilla_gfx_layers_client_CompositableChild_h diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp index 45b4b2c1577b..57dccf3f328a 100644 --- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -6,6 +6,7 @@ #include "mozilla/layers/CompositableClient.h" #include // for uint64_t, uint32_t #include "gfxPlatform.h" // for gfxPlatform +#include "mozilla/layers/CompositableChild.h" #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/TextureClient.h" // for TextureClient, etc @@ -26,37 +27,6 @@ namespace layers { using namespace mozilla::gfx; -/** - * IPDL actor used by CompositableClient to match with its corresponding - * CompositableHost on the compositor side. - * - * CompositableChild is owned by a CompositableClient. - */ -class CompositableChild : public ChildActor -{ -public: - CompositableChild() - : mCompositableClient(nullptr), mAsyncID(0) - { - MOZ_COUNT_CTOR(CompositableChild); - } - - virtual ~CompositableChild() - { - MOZ_COUNT_DTOR(CompositableChild); - } - - virtual void ActorDestroy(ActorDestroyReason) override { - if (mCompositableClient) { - mCompositableClient->mCompositableChild = nullptr; - } - } - - CompositableClient* mCompositableClient; - - uint64_t mAsyncID; -}; - void RemoveTextureFromCompositableTracker::ReleaseTextureClient() { @@ -92,15 +62,14 @@ CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID) MOZ_ASSERT(aActor); CompositableChild* child = static_cast(aActor); mCompositableChild = child; - child->mCompositableClient = this; - child->mAsyncID = aAsyncID; + child->Init(this, aAsyncID); } /* static */ CompositableClient* CompositableClient::FromIPDLActor(PCompositableChild* aActor) { MOZ_ASSERT(aActor); - return static_cast(aActor)->mCompositableClient; + return static_cast(aActor)->GetCompositableClient(); } CompositableClient::CompositableClient(CompositableForwarder* aForwarder, @@ -163,7 +132,7 @@ CompositableClient::Destroy() if (mTextureClientRecycler) { mTextureClientRecycler->Destroy(); } - mCompositableChild->mCompositableClient = nullptr; + mCompositableChild->RevokeCompositableClient(); mCompositableChild->Destroy(mForwarder); mCompositableChild = nullptr; } @@ -178,7 +147,7 @@ uint64_t CompositableClient::GetAsyncID() const { if (mCompositableChild) { - return mCompositableChild->mAsyncID; + return mCompositableChild->GetAsyncID(); } return 0; // zero is always an invalid async ID } diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index e08490640394..2d72af97c949 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -128,6 +128,7 @@ EXPORTS.mozilla.layers += [ 'BSPTree.h', 'BufferTexture.h', 'client/CanvasClient.h', + 'client/CompositableChild.h', 'client/CompositableClient.h', 'client/ContentClient.h', 'client/ImageClient.h', @@ -316,6 +317,7 @@ UNIFIED_SOURCES += [ 'client/ClientLayerManager.cpp', 'client/ClientPaintedLayer.cpp', 'client/ClientTiledPaintedLayer.cpp', + 'client/CompositableChild.cpp', 'client/CompositableClient.cpp', 'client/ContentClient.cpp', 'client/ImageClient.cpp', From 1c2785873ca44c28a7a52216a579613b503a4a45 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 2 Sep 2016 16:08:49 -0700 Subject: [PATCH 072/102] Refactor CompositableClient memory management. (bug 1299375 part 2, r=nical) --- dom/canvas/OffscreenCanvas.cpp | 7 +- dom/canvas/OffscreenCanvas.h | 2 +- gfx/layers/IPDLActor.h | 1 + gfx/layers/ImageContainer.cpp | 6 +- gfx/layers/ImageContainer.h | 2 +- gfx/layers/client/ClientLayerManager.cpp | 4 +- gfx/layers/client/CompositableChild.cpp | 75 +++++++++++++++++- gfx/layers/client/CompositableChild.h | 57 ++++++++++++-- gfx/layers/client/CompositableClient.cpp | 63 +++++++-------- gfx/layers/client/CompositableClient.h | 21 +---- gfx/layers/client/ImageClient.h | 5 -- gfx/layers/ipc/CompositableForwarder.cpp | 28 +++++++ gfx/layers/ipc/CompositableForwarder.h | 8 ++ gfx/layers/ipc/ImageBridgeChild.cpp | 94 +++++++++++------------ gfx/layers/ipc/ImageBridgeChild.h | 11 ++- gfx/layers/ipc/LayerTransactionChild.cpp | 7 +- gfx/layers/ipc/ShadowLayers.cpp | 3 +- gfx/layers/ipc/ShadowLayers.h | 4 + gfx/layers/ipc/SharedPlanarYCbCrImage.cpp | 2 - gfx/layers/ipc/SharedRGBImage.cpp | 2 - gfx/layers/moz.build | 1 + 21 files changed, 260 insertions(+), 143 deletions(-) create mode 100644 gfx/layers/ipc/CompositableForwarder.cpp diff --git a/dom/canvas/OffscreenCanvas.cpp b/dom/canvas/OffscreenCanvas.cpp index 2dd477ae3a9f..9b405d027e0f 100644 --- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -51,7 +51,6 @@ OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, , mWidth(aWidth) , mHeight(aHeight) , mCompositorBackendType(aCompositorBackend) - , mCanvasClient(nullptr) , mCanvasRenderer(aRenderer) {} @@ -85,8 +84,6 @@ OffscreenCanvas::ClearResources() { if (mCanvasClient) { mCanvasClient->Clear(); - ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient); - mCanvasClient = nullptr; if (mCanvasRenderer) { nsCOMPtr activeThread = mCanvasRenderer->GetActiveThread(); @@ -99,6 +96,8 @@ OffscreenCanvas::ClearResources() mCanvasRenderer->mGLContext = nullptr; mCanvasRenderer->ResetActiveThread(); } + + mCanvasClient = nullptr; } } @@ -151,7 +150,7 @@ OffscreenCanvas::GetContext(JSContext* aCx, if (ImageBridgeChild::IsCreated()) { TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT; mCanvasClient = ImageBridgeChild::GetSingleton()-> - CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take(); + CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags); mCanvasRenderer->SetCanvasClient(mCanvasClient); gl::GLScreenBuffer* screen = gl->Screen(); diff --git a/dom/canvas/OffscreenCanvas.h b/dom/canvas/OffscreenCanvas.h index 520149e55b09..7595439425c5 100644 --- a/dom/canvas/OffscreenCanvas.h +++ b/dom/canvas/OffscreenCanvas.h @@ -204,7 +204,7 @@ private: layers::LayersBackend mCompositorBackendType; - layers::CanvasClient* mCanvasClient; + RefPtr mCanvasClient; RefPtr mCanvasRenderer; }; diff --git a/gfx/layers/IPDLActor.h b/gfx/layers/IPDLActor.h index ad45a1567aba..5143b38adb3a 100644 --- a/gfx/layers/IPDLActor.h +++ b/gfx/layers/IPDLActor.h @@ -9,6 +9,7 @@ #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/Unused.h" +#include "gfxPlatform.h" namespace mozilla { namespace layers { diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index d31f513359cb..d16bf2039c83 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -188,7 +188,6 @@ ImageContainer::ImageContainer(Mode flag) mDroppedImageCount(0), mImageFactory(new ImageFactory()), mRecycleBin(new BufferRecycleBin()), - mImageClient(nullptr), mCurrentProducerID(-1), mIPDLChild(nullptr) { @@ -200,7 +199,7 @@ ImageContainer::ImageContainer(Mode flag) break; case ASYNCHRONOUS: mIPDLChild = new ImageContainerChild(this); - mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take(); + mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this); MOZ_ASSERT(mImageClient); break; default: @@ -219,7 +218,6 @@ ImageContainer::ImageContainer(uint64_t aAsyncContainerID) mDroppedImageCount(0), mImageFactory(nullptr), mRecycleBin(nullptr), - mImageClient(nullptr), mAsyncContainerID(aAsyncContainerID), mCurrentProducerID(-1), mIPDLChild(nullptr) @@ -231,7 +229,7 @@ ImageContainer::~ImageContainer() { if (mIPDLChild) { mIPDLChild->ForgetImageContainer(); - ImageBridgeChild::DispatchReleaseImageClient(mImageClient, mIPDLChild); + ImageBridgeChild::DispatchReleaseImageContainer(mIPDLChild); } } diff --git a/gfx/layers/ImageContainer.h b/gfx/layers/ImageContainer.h index 00550d4d89d6..fa1d0912b5a1 100644 --- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -636,7 +636,7 @@ private: // In this case the ImageContainer is perfectly usable, but it will forward // frames to the compositor through transactions in the main thread rather than // asynchronusly using the ImageBridge IPDL protocol. - ImageClient* mImageClient; + RefPtr mImageClient; uint64_t mAsyncContainerID; diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 0acc69260566..421b67e6a10c 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -649,10 +649,10 @@ ClientLayerManager::ForwardTransaction(bool aScheduleComposite) const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap(); - CompositableClient* compositable = + RefPtr compositable = CompositableClient::FromIPDLActor(obs.compositableChild()); ContentClientRemote* contentClient = - static_cast(compositable); + static_cast(compositable.get()); MOZ_ASSERT(contentClient); contentClient->SwapBuffers(obs.frontUpdatedRegion()); diff --git a/gfx/layers/client/CompositableChild.cpp b/gfx/layers/client/CompositableChild.cpp index e1eb0b40bbdc..34a0e0696805 100644 --- a/gfx/layers/client/CompositableChild.cpp +++ b/gfx/layers/client/CompositableChild.cpp @@ -9,9 +9,24 @@ namespace mozilla { namespace layers { +/* static */ PCompositableChild* +CompositableChild::CreateActor() +{ + CompositableChild* child = new CompositableChild(); + child->AddRef(); + return child; +} + +/* static */ void +CompositableChild::DestroyActor(PCompositableChild* aChild) +{ + static_cast(aChild)->Release(); +} + CompositableChild::CompositableChild() : mCompositableClient(nullptr), - mAsyncID(0) + mAsyncID(0), + mCanSend(true) { MOZ_COUNT_CTOR(CompositableChild); } @@ -21,6 +36,12 @@ CompositableChild::~CompositableChild() MOZ_COUNT_DTOR(CompositableChild); } +bool +CompositableChild::IsConnected() const +{ + return mCompositableClient && mCanSend; +} + void CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID) { @@ -34,13 +55,65 @@ CompositableChild::RevokeCompositableClient() mCompositableClient = nullptr; } +RefPtr +CompositableChild::GetCompositableClient() +{ + return mCompositableClient; +} + void CompositableChild::ActorDestroy(ActorDestroyReason) { + MOZ_ASSERT(NS_IsMainThread()); + + mCanSend = false; + if (mCompositableClient) { mCompositableClient->mCompositableChild = nullptr; + mCompositableClient = nullptr; } } +/* static */ PCompositableChild* +AsyncCompositableChild::CreateActor() +{ + AsyncCompositableChild* child = new AsyncCompositableChild(); + child->AddRef(); + return child; +} + +AsyncCompositableChild::AsyncCompositableChild() + : mLock("AsyncCompositableChild.mLock") +{ +} + +AsyncCompositableChild::~AsyncCompositableChild() +{ +} + +void +AsyncCompositableChild::ActorDestroy(ActorDestroyReason) +{ + mCanSend = false; + + // We do not revoke CompositableClient::mCompositableChild here, since that + // could race with the main thread. + RevokeCompositableClient(); +} + +void +AsyncCompositableChild::RevokeCompositableClient() +{ + MutexAutoLock lock(mLock); + mCompositableClient = nullptr; +} + +RefPtr +AsyncCompositableChild::GetCompositableClient() +{ + MutexAutoLock lock(mLock); + return CompositableChild::GetCompositableClient(); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/client/CompositableChild.h b/gfx/layers/client/CompositableChild.h index 81815f5085a8..381d8051b2f0 100644 --- a/gfx/layers/client/CompositableChild.h +++ b/gfx/layers/client/CompositableChild.h @@ -8,12 +8,14 @@ #include #include "IPDLActor.h" +#include "mozilla/Mutex.h" #include "mozilla/layers/PCompositableChild.h" namespace mozilla { namespace layers { class CompositableClient; +class AsyncCompositableChild; /** * IPDL actor used by CompositableClient to match with its corresponding @@ -21,27 +23,66 @@ class CompositableClient; * * CompositableChild is owned by a CompositableClient. */ -class CompositableChild : public ChildActor +class CompositableChild : public PCompositableChild { public: - CompositableChild(); - virtual ~CompositableChild(); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild) + + static PCompositableChild* CreateActor(); + static void DestroyActor(PCompositableChild* aChild); void Init(CompositableClient* aCompositable, uint64_t aAsyncID); - void RevokeCompositableClient(); + virtual void RevokeCompositableClient(); - void ActorDestroy(ActorDestroyReason) override; + virtual void ActorDestroy(ActorDestroyReason) override; - CompositableClient* GetCompositableClient() { - return mCompositableClient; + virtual RefPtr GetCompositableClient(); + + virtual AsyncCompositableChild* AsAsyncCompositableChild() { + return nullptr; } + uint64_t GetAsyncID() const { return mAsyncID; } -private: + // These should only be called on the IPDL thread. + bool IsConnected() const; + bool CanSend() const { + return mCanSend; + } + +protected: + CompositableChild(); + virtual ~CompositableChild(); + +protected: CompositableClient* mCompositableClient; uint64_t mAsyncID; + bool mCanSend; +}; + +// This CompositableChild can be used off the main thread. +class AsyncCompositableChild final : public CompositableChild +{ +public: + static PCompositableChild* CreateActor(); + + void RevokeCompositableClient() override; + RefPtr GetCompositableClient() override; + + void ActorDestroy(ActorDestroyReason) override; + + AsyncCompositableChild* AsAsyncCompositableChild() override { + return this; + } + +protected: + AsyncCompositableChild(); + ~AsyncCompositableChild() override; + +private: + Mutex mLock; }; } // namespace layers diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp index 57dccf3f328a..40ac65b20840 100644 --- a/gfx/layers/client/CompositableClient.cpp +++ b/gfx/layers/client/CompositableClient.cpp @@ -43,39 +43,34 @@ RemoveTextureFromCompositableTracker::ReleaseTextureClient() } } -/* static */ PCompositableChild* -CompositableClient::CreateIPDLActor() -{ - return new CompositableChild(); -} - -/* static */ bool -CompositableClient::DestroyIPDLActor(PCompositableChild* actor) -{ - delete actor; - return true; -} - void CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID) { MOZ_ASSERT(aActor); - CompositableChild* child = static_cast(aActor); - mCompositableChild = child; - child->Init(this, aAsyncID); + + mForwarder->AssertInForwarderThread(); + + mCompositableChild = static_cast(aActor); + mCompositableChild->Init(this, aAsyncID); } -/* static */ CompositableClient* +/* static */ RefPtr CompositableClient::FromIPDLActor(PCompositableChild* aActor) { MOZ_ASSERT(aActor); - return static_cast(aActor)->GetCompositableClient(); + + RefPtr client = static_cast(aActor)->GetCompositableClient(); + if (!client) { + return nullptr; + } + + client->mForwarder->AssertInForwarderThread(); + return client; } CompositableClient::CompositableClient(CompositableForwarder* aForwarder, TextureFlags aTextureFlags) -: mCompositableChild(nullptr) -, mForwarder(aForwarder) +: mForwarder(aForwarder) , mTextureFlags(aTextureFlags) { MOZ_COUNT_CTOR(CompositableClient); @@ -93,12 +88,6 @@ CompositableClient::GetCompositorBackendType() const return mForwarder->GetCompositorBackendType(); } -void -CompositableClient::SetIPDLActor(CompositableChild* aChild) -{ - mCompositableChild = aChild; -} - PCompositableChild* CompositableClient::GetIPDLActor() const { @@ -112,6 +101,8 @@ CompositableClient::Connect(ImageContainer* aImageContainer) if (!GetForwarder() || GetIPDLActor()) { return false; } + + GetForwarder()->AssertInForwarderThread(); GetForwarder()->Connect(this, aImageContainer); return true; } @@ -119,28 +110,28 @@ CompositableClient::Connect(ImageContainer* aImageContainer) bool CompositableClient::IsConnected() const { - return mCompositableChild && mCompositableChild->CanSend(); + // CanSend() is only reliable in the same thread as the IPDL channel. + mForwarder->AssertInForwarderThread(); + return mCompositableChild && mCompositableChild->IsConnected(); } void CompositableClient::Destroy() { - if (!IsConnected()) { + if (!mCompositableChild) { return; } if (mTextureClientRecycler) { mTextureClientRecycler->Destroy(); } - mCompositableChild->RevokeCompositableClient(); - mCompositableChild->Destroy(mForwarder); - mCompositableChild = nullptr; -} -bool -CompositableClient::DestroyFallback(PCompositableChild* aActor) -{ - return aActor->SendDestroySync(); + // Take away our IPDL's actor reference back to us. + mCompositableChild->RevokeCompositableClient(); + + // Schedule the IPDL actor to be destroyed on the forwarder's thread. + mForwarder->Destroy(mCompositableChild); + mCompositableChild = nullptr; } uint64_t diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 5bbd74f53a43..92ae2768ed79 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -161,15 +161,10 @@ public: void Destroy(); - static bool DestroyFallback(PCompositableChild* aActor); - bool IsConnected() const; PCompositableChild* GetIPDLActor() const; - // should only be called by a CompositableForwarder - virtual void SetIPDLActor(CompositableChild* aChild); - CompositableForwarder* GetForwarder() const { return mForwarder; @@ -216,19 +211,7 @@ public: */ virtual void RemoveTexture(TextureClient* aTexture); - static CompositableClient* FromIPDLActor(PCompositableChild* aActor); - - /** - * Allocate and deallocate a CompositableChild actor. - * - * CompositableChild is an implementation detail of CompositableClient that is not - * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor - * are for use with the managing IPDL protocols only (so that they can - * implement AllocCompositableChild and DeallocPCompositableChild). - */ - static PCompositableChild* CreateIPDLActor(); - - static bool DestroyIPDLActor(PCompositableChild* actor); + static RefPtr FromIPDLActor(PCompositableChild* aActor); void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0); @@ -242,7 +225,7 @@ public: TextureClient* aTexture, TextureDumpMode aCompress); protected: - CompositableChild* mCompositableChild; + RefPtr mCompositableChild; RefPtr mForwarder; // Some layers may want to enforce some flags to all their textures // (like disallowing tiling) diff --git a/gfx/layers/client/ImageClient.h b/gfx/layers/client/ImageClient.h index a2c35fd3d7d0..e51a6da2d12c 100644 --- a/gfx/layers/client/ImageClient.h +++ b/gfx/layers/client/ImageClient.h @@ -131,11 +131,6 @@ public: return TextureInfo(mType); } - virtual void SetIPDLActor(CompositableChild* aChild) override - { - MOZ_ASSERT(!aChild, "ImageClientBridge should not have IPDL actor"); - } - protected: uint64_t mAsyncContainerID; }; diff --git a/gfx/layers/ipc/CompositableForwarder.cpp b/gfx/layers/ipc/CompositableForwarder.cpp new file mode 100644 index 000000000000..20f2f61aeb86 --- /dev/null +++ b/gfx/layers/ipc/CompositableForwarder.cpp @@ -0,0 +1,28 @@ +/* -*- 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 "CompositableForwarder.h" +#include "mozilla/layers/CompositableChild.h" + +namespace mozilla { +namespace layers { + +void +CompositableForwarder::Destroy(CompositableChild* aCompositable) +{ + AssertInForwarderThread(); + + if (!aCompositable->CanSend()) { + return; + } + + if (!DestroyInTransaction(aCompositable, false)) { + aCompositable->SendDestroy(); + } +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/CompositableForwarder.h b/gfx/layers/ipc/CompositableForwarder.h index fcf65f816e3f..cab74c07be50 100644 --- a/gfx/layers/ipc/CompositableForwarder.h +++ b/gfx/layers/ipc/CompositableForwarder.h @@ -111,6 +111,8 @@ public: const gfx::IntRect& aPictureRect) = 0; #endif + virtual void Destroy(CompositableChild* aCompositable); + virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0; virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 0; @@ -174,6 +176,12 @@ public: return mTextureFactoryIdentifier.mMaxTextureSize; } + virtual bool InForwarderThread() = 0; + + void AssertInForwarderThread() { + MOZ_ASSERT(InForwarderThread()); + } + /** * Returns the type of backend that is used off the main thread. * We only don't allow changing the backend type at runtime so this value can diff --git a/gfx/layers/ipc/ImageBridgeChild.cpp b/gfx/layers/ipc/ImageBridgeChild.cpp index 1919e97e98dd..1faa46d7dba2 100644 --- a/gfx/layers/ipc/ImageBridgeChild.cpp +++ b/gfx/layers/ipc/ImageBridgeChild.cpp @@ -23,6 +23,7 @@ #include "mozilla/layers/AsyncCanvasRenderer.h" #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild +#include "mozilla/layers/CompositableChild.h" #include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator @@ -142,7 +143,7 @@ struct CompositableTransaction break; } case OpDestroy::TPCompositableChild: { - DebugOnly ok = CompositableClient::DestroyFallback(actor.get_PCompositableChild()); + DebugOnly ok = actor.get_PCompositableChild()->SendDestroySync(); MOZ_ASSERT(ok); break; } @@ -521,6 +522,8 @@ ImageBridgeChild::Connect(CompositableClient* aCompositable, { MOZ_ASSERT(aCompositable); MOZ_ASSERT(!mShuttingDown); + MOZ_ASSERT(InImageBridgeChildThread()); + uint64_t id = 0; PImageContainerChild* imageContainerChild = nullptr; @@ -539,13 +542,14 @@ ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, PImageContainerChild* aChild, uint64_t* aID) { MOZ_ASSERT(!mShuttingDown); - return CompositableClient::CreateIPDLActor(); + return AsyncCompositableChild::CreateActor(); } bool ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor) { - return CompositableClient::DestroyIPDLActor(aActor); + AsyncCompositableChild::DestroyActor(aActor); + return true; } @@ -564,13 +568,9 @@ bool ImageBridgeChild::IsCreated() return GetSingleton() != nullptr; } -static void ReleaseImageClientNow(ImageClient* aClient, - PImageContainerChild* aChild) +static void ReleaseImageContainerNow(PImageContainerChild* aChild) { MOZ_ASSERT(InImageBridgeChildThread()); - if (aClient) { - aClient->Release(); - } if (aChild) { ImageContainer::AsyncDestroyActor(aChild); @@ -578,57 +578,19 @@ static void ReleaseImageClientNow(ImageClient* aClient, } // static -void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient, - PImageContainerChild* aChild) +void ImageBridgeChild::DispatchReleaseImageContainer(PImageContainerChild* aChild) { - if (!aClient && !aChild) { + if (!aChild) { return; } if (!IsCreated()) { - if (aClient) { - // CompositableClient::Release should normally happen in the ImageBridgeChild - // thread because it usually generate some IPDL messages. - // However, if we take this branch it means that the ImageBridgeChild - // has already shut down, along with the CompositableChild, which means no - // message will be sent and it is safe to run this code from any thread. - MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); - aClient->Release(); - } delete aChild; return; } sImageBridgeChildSingleton->GetMessageLoop()->PostTask( - NewRunnableFunction(&ReleaseImageClientNow, aClient, aChild)); -} - -static void ReleaseCanvasClientNow(CanvasClient* aClient) -{ - MOZ_ASSERT(InImageBridgeChildThread()); - aClient->Release(); -} - -// static -void ImageBridgeChild::DispatchReleaseCanvasClient(CanvasClient* aClient) -{ - if (!aClient) { - return; - } - - if (!IsCreated()) { - // CompositableClient::Release should normally happen in the ImageBridgeChild - // thread because it usually generate some IPDL messages. - // However, if we take this branch it means that the ImageBridgeChild - // has already shut down, along with the CompositableChild, which means no - // message will be sent and it is safe to run this code from any thread. - MOZ_ASSERT(aClient->GetIPDLActor() == nullptr); - aClient->Release(); - return; - } - - sImageBridgeChildSingleton->GetMessageLoop()->PostTask( - NewRunnableFunction(&ReleaseCanvasClientNow, aClient)); + NewRunnableFunction(&ReleaseImageContainerNow, aChild)); } static void ReleaseTextureClientNow(TextureClient* aClient) @@ -659,7 +621,8 @@ void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient) NewRunnableFunction(&ReleaseTextureClientNow, aClient)); } -static void UpdateImageClientNow(ImageClient* aClient, RefPtr&& aContainer) +static void +UpdateImageClientNow(RefPtr aClient, RefPtr&& aContainer) { if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) { NS_WARNING("Something is holding on to graphics resources after the shutdown" @@ -668,6 +631,13 @@ static void UpdateImageClientNow(ImageClient* aClient, RefPtr&& } MOZ_ASSERT(aClient); MOZ_ASSERT(aContainer); + + // If the client has become disconnected before this event was dispatched, + // early return now. + if (!aClient->IsConnected()) { + return; + } + sImageBridgeChildSingleton->BeginTransaction(); aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE); sImageBridgeChildSingleton->EndTransaction(); @@ -690,8 +660,10 @@ void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient, UpdateImageClientNow(aClient, aContainer); return; } + sImageBridgeChildSingleton->GetMessageLoop()->PostTask( - NewRunnableFunction(&UpdateImageClientNow, aClient, RefPtr(aContainer))); + NewRunnableFunction(&UpdateImageClientNow, + RefPtr(aClient), RefPtr(aContainer))); } static void UpdateAsyncCanvasRendererSync(AsyncCanvasRenderer* aWrapper, @@ -1424,5 +1396,25 @@ bool ImageBridgeChild::IsSameProcess() const return OtherPid() == base::GetCurrentProcId(); } +static void +DestroyCompositableNow(RefPtr aImageBridge, + RefPtr aCompositable) +{ + aImageBridge->Destroy(aCompositable); +} + +void +ImageBridgeChild::Destroy(CompositableChild* aCompositable) +{ + if (!InImageBridgeChildThread()) { + RefPtr self = this; + RefPtr compositable = aCompositable; + GetMessageLoop()->PostTask( + NewRunnableFunction(&DestroyCompositableNow, self, compositable)); + return; + } + CompositableForwarder::Destroy(aCompositable); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/ImageBridgeChild.h b/gfx/layers/ipc/ImageBridgeChild.h index d5dfc8f01c37..e47bf5a119a9 100644 --- a/gfx/layers/ipc/ImageBridgeChild.h +++ b/gfx/layers/ipc/ImageBridgeChild.h @@ -227,9 +227,7 @@ public: already_AddRefed CreateCanvasClientNow(CanvasClient::CanvasClientType aType, TextureFlags aFlag); - static void DispatchReleaseImageClient(ImageClient* aClient, - PImageContainerChild* aChild = nullptr); - static void DispatchReleaseCanvasClient(CanvasClient* aClient); + static void DispatchReleaseImageContainer(PImageContainerChild* aChild = nullptr); static void DispatchReleaseTextureClient(TextureClient* aClient); static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer); @@ -262,6 +260,8 @@ public: const nsIntRect& aPictureRect) override; #endif + void Destroy(CompositableChild* aCompositable) override; + /** * Hold TextureClient ref until end of usage on host side if TextureFlags::RECYCLE is set. * Host side's usage is checked via CompositableRef. @@ -341,6 +341,11 @@ public: virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; } virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; } + bool InForwarderThread() override { + return InImageBridgeChildThread(); + } + + void MarkShutDown(); void FallbackDestroyActors(); diff --git a/gfx/layers/ipc/LayerTransactionChild.cpp b/gfx/layers/ipc/LayerTransactionChild.cpp index 0a42a75452d1..bba41e0e35ba 100644 --- a/gfx/layers/ipc/LayerTransactionChild.cpp +++ b/gfx/layers/ipc/LayerTransactionChild.cpp @@ -7,7 +7,7 @@ #include "LayerTransactionChild.h" #include "mozilla/gfx/Logging.h" -#include "mozilla/layers/CompositableClient.h" // for CompositableChild +#include "mozilla/layers/CompositableChild.h" #include "mozilla/layers/PCompositableChild.h" // for PCompositableChild #include "mozilla/layers/PLayerChild.h" // for PLayerChild #include "mozilla/layers/PImageContainerChild.h" @@ -60,13 +60,14 @@ PCompositableChild* LayerTransactionChild::AllocPCompositableChild(const TextureInfo& aInfo) { MOZ_ASSERT(!mDestroyed); - return CompositableClient::CreateIPDLActor(); + return CompositableChild::CreateActor(); } bool LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor) { - return CompositableClient::DestroyIPDLActor(actor); + CompositableChild::DestroyActor(actor); + return true; } void diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index e2e121ca067a..ed3ce1d1bfd2 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -30,6 +30,7 @@ #include "mozilla/layers/LayersTypes.h" // for MOZ_LAYERS_LOG #include "mozilla/layers/LayerTransactionChild.h" #include "mozilla/layers/SharedBufferManagerChild.h" +#include "mozilla/layers/PCompositableChild.h" #include "mozilla/layers/PTextureChild.h" #include "ShadowLayerUtils.h" #include "mozilla/layers/TextureClient.h" // for TextureClient @@ -169,7 +170,7 @@ public: break; } case OpDestroy::TPCompositableChild: { - DebugOnly ok = CompositableClient::DestroyFallback(actor.get_PCompositableChild()); + DebugOnly ok = actor.get_PCompositableChild()->SendDestroySync(); MOZ_ASSERT(ok); break; } diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h index ffa2ebfbf5a2..fe4e1c17bd62 100644 --- a/gfx/layers/ipc/ShadowLayers.h +++ b/gfx/layers/ipc/ShadowLayers.h @@ -392,6 +392,10 @@ public: virtual void UpdateFwdTransactionId() override; virtual uint64_t GetFwdTransactionId() override; + bool InForwarderThread() override { + return NS_IsMainThread(); + } + // Returns true if aSurface wraps a Shmem. static bool IsShmem(SurfaceDescriptor* aSurface); diff --git a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp index a1b61a9ec77c..d8f93c59c411 100644 --- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp +++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp @@ -43,8 +43,6 @@ SharedPlanarYCbCrImage::~SharedPlanarYCbCrImage() { ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient); mTextureClient = nullptr; } - - ImageBridgeChild::DispatchReleaseImageClient(mCompositable.forget().take()); } } diff --git a/gfx/layers/ipc/SharedRGBImage.cpp b/gfx/layers/ipc/SharedRGBImage.cpp index f38b06a6ddc1..d9a21ca73b71 100644 --- a/gfx/layers/ipc/SharedRGBImage.cpp +++ b/gfx/layers/ipc/SharedRGBImage.cpp @@ -68,8 +68,6 @@ SharedRGBImage::~SharedRGBImage() ADDREF_MANUALLY(mTextureClient); ImageBridgeChild::DispatchReleaseTextureClient(mTextureClient); mTextureClient = nullptr; - - ImageBridgeChild::DispatchReleaseImageClient(mCompositable.forget().take()); } } diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 2d72af97c949..2fed1c32dab1 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -354,6 +354,7 @@ UNIFIED_SOURCES += [ 'ipc/APZCTreeManagerChild.cpp', 'ipc/APZCTreeManagerParent.cpp', 'ipc/AsyncTransactionTracker.cpp', + 'ipc/CompositableForwarder.cpp', 'ipc/CompositableTransactionParent.cpp', 'ipc/CompositorBench.cpp', 'ipc/CompositorBridgeChild.cpp', From 382083077f49e0dda5e160cc87ce51c95f9766da Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sat, 3 Sep 2016 09:51:15 +0900 Subject: [PATCH 073/102] Bug 1284511 - Fix slow script check to use elapsed time from last check. r=mrbkap --- js/xpconnect/src/XPCJSRuntime.cpp | 59 +++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 10c0cf541301..d0b774271a81 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -975,7 +975,8 @@ class Watchdog , mHibernating(false) , mInitialized(false) , mShuttingDown(false) - , mSlowScriptSecondHalfCount(0) + , mSlowScriptSecondHalf(false) + , mSlowScriptHalfLastElapsedTime(0) , mMinScriptRunTimeSeconds(1) {} ~Watchdog() { MOZ_ASSERT(!Initialized()); } @@ -1085,9 +1086,27 @@ class Watchdog return mMinScriptRunTimeSeconds; } - uint32_t GetSlowScriptSecondHalfCount() { return mSlowScriptSecondHalfCount; } - void IncrementSlowScriptSecondHalfCount() { mSlowScriptSecondHalfCount++; } - void ResetSlowScriptSecondHalfCount() { mSlowScriptSecondHalfCount = 0; } + bool IsSlowScriptSecondHalf() + { + return mSlowScriptSecondHalf; + } + void FlipSlowScriptSecondHalf() + { + mSlowScriptSecondHalf = !mSlowScriptSecondHalf; + } + PRTime GetSlowScriptHalfLastElapsedTime() + { + return mSlowScriptHalfLastElapsedTime; + } + void SetSlowScriptHalfLastElapsedTime(PRTime t) + { + mSlowScriptHalfLastElapsedTime = t; + } + void ResetSlowScript() + { + mSlowScriptSecondHalf = false; + mSlowScriptHalfLastElapsedTime = 0; + } private: WatchdogManager* mManager; @@ -1100,7 +1119,8 @@ class Watchdog bool mShuttingDown; // See the comment in WatchdogMain. - uint32_t mSlowScriptSecondHalfCount; + bool mSlowScriptSecondHalf; + PRTime mSlowScriptHalfLastElapsedTime; mozilla::Atomic mMinScriptRunTimeSeconds; }; @@ -1166,7 +1186,7 @@ class WatchdogManager : public nsIObserver // Write state. mTimestamps[TimestampRuntimeStateChange] = PR_Now(); if (mWatchdog) - mWatchdog->ResetSlowScriptSecondHalfCount(); + mWatchdog->ResetSlowScript(); mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE; // The watchdog may be hibernating, waiting for the runtime to go @@ -1298,32 +1318,32 @@ WatchdogMain(void* arg) // T/2 periods, the script still has the other T/2 seconds to finish. // // + <-- TimestampRuntimeStateChange = PR_Now() - // | mSlowScriptSecondHalfCount = 0 // | - // | T/2 + // | t0 >= T/2 // | - // + <-- mSlowScriptSecondHalfCount = 1 + // + <-- mSlowScriptSecondHalf == false // | - // | T/2 + // | t1 >= T/2 // | - // + <-- mSlowScriptSecondHalfCount = 2 + // + <-- mSlowScriptSecondHalf == true // | Invoke interrupt callback // | - // | T/2 + // | t2 >= T/2 // | - // + <-- mSlowScriptSecondHalfCount = 3 + // + <-- mSlowScriptSecondHalf == false // | - // | T/2 + // | t3 >= T/2 // | - // + <-- mSlowScriptSecondHalfCount = 4 + // + <-- mSlowScriptSecondHalf == true // Invoke interrupt callback // PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC; if (manager->IsRuntimeActive()) { - uint32_t count = self->GetSlowScriptSecondHalfCount() + 1; - if (manager->TimeSinceLastRuntimeStateChange() >= usecs * count / 2) { - self->IncrementSlowScriptSecondHalfCount(); - if (count % 2 == 0) { + PRTime elapsedTime = manager->TimeSinceLastRuntimeStateChange(); + PRTime lastElapsedTime = self->GetSlowScriptHalfLastElapsedTime(); + if (elapsedTime >= lastElapsedTime + usecs / 2) { + self->SetSlowScriptHalfLastElapsedTime(elapsedTime); + if (self->IsSlowScriptSecondHalf()) { bool debuggerAttached = false; nsCOMPtr dbg = do_GetService("@mozilla.org/xpcom/debug;1"); if (dbg) @@ -1331,6 +1351,7 @@ WatchdogMain(void* arg) if (!debuggerAttached) JS_RequestInterruptCallback(manager->Runtime()->Context()); } + self->FlipSlowScriptSecondHalf(); } } } From 586c7b1a14afbfe83509ea4d048eb25cde8705c0 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sat, 13 Aug 2016 23:03:31 +0900 Subject: [PATCH 074/102] Bug 1289003 - Part 1: Add UTF8CharsToNewLatin1CharsZ, LossyUTF8CharsToNewLatin1CharsZ. r=jwalden --- js/public/CharacterEncoding.h | 33 ++++++++++++++ js/src/vm/CharacterEncoding.cpp | 81 ++++++++++++++++++++------------- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/js/public/CharacterEncoding.h b/js/public/CharacterEncoding.h index 99a6b4cdfce9..8c166013c788 100644 --- a/js/public/CharacterEncoding.h +++ b/js/public/CharacterEncoding.h @@ -31,6 +31,8 @@ class Latin1Chars : public mozilla::Range typedef mozilla::Range Base; public: + using CharT = Latin1Char; + Latin1Chars() : Base() {} Latin1Chars(char* aBytes, size_t aLength) : Base(reinterpret_cast(aBytes), aLength) {} Latin1Chars(const Latin1Char* aBytes, size_t aLength) @@ -49,6 +51,8 @@ class Latin1CharsZ : public mozilla::RangedPtr typedef mozilla::RangedPtr Base; public: + using CharT = Latin1Char; + Latin1CharsZ() : Base(nullptr, 0) {} Latin1CharsZ(char* aBytes, size_t aLength) @@ -73,6 +77,8 @@ class UTF8Chars : public mozilla::Range typedef mozilla::Range Base; public: + using CharT = unsigned char; + UTF8Chars() : Base() {} UTF8Chars(char* aBytes, size_t aLength) : Base(reinterpret_cast(aBytes), aLength) @@ -90,6 +96,8 @@ class UTF8CharsZ : public mozilla::RangedPtr typedef mozilla::RangedPtr Base; public: + using CharT = unsigned char; + UTF8CharsZ() : Base(nullptr, 0) {} UTF8CharsZ(char* aBytes, size_t aLength) @@ -120,6 +128,8 @@ class ConstUTF8CharsZ const char* data_; public: + using CharT = unsigned char; + ConstUTF8CharsZ() : data_(nullptr) {} @@ -157,6 +167,8 @@ class TwoByteChars : public mozilla::Range typedef mozilla::Range Base; public: + using CharT = char16_t; + TwoByteChars() : Base() {} TwoByteChars(char16_t* aChars, size_t aLength) : Base(aChars, aLength) {} TwoByteChars(const char16_t* aChars, size_t aLength) : Base(const_cast(aChars), aLength) {} @@ -170,6 +182,8 @@ class TwoByteCharsZ : public mozilla::RangedPtr typedef mozilla::RangedPtr Base; public: + using CharT = char16_t; + TwoByteCharsZ() : Base(nullptr, 0) {} TwoByteCharsZ(char16_t* chars, size_t length) @@ -191,6 +205,8 @@ class ConstTwoByteChars : public mozilla::Range typedef mozilla::Range Base; public: + using CharT = char16_t; + ConstTwoByteChars() : Base() {} ConstTwoByteChars(const char16_t* aChars, size_t aLength) : Base(aChars, aLength) {} }; @@ -272,6 +288,23 @@ JS_PUBLIC_API(void) DeflateStringToUTF8Buffer(JSFlatString* src, mozilla::RangedPtr dst, size_t* dstlenp = nullptr, size_t* numcharsp = nullptr); +/* + * Return a null-terminated Latin-1 string copied from the input string, + * storing its length (excluding null terminator) in |*outlen|. Fail and + * report an error if the string contains non-Latin-1 codepoints. Returns + * Latin1CharsZ() on failure. + */ +extern Latin1CharsZ +UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); + +/* + * Return a null-terminated Latin-1 string copied from the input string, + * storing its length (excluding null terminator) in |*outlen|. Non-Latin-1 + * codepoints are replaced by '?'. Returns Latin1CharsZ() on failure. + */ +extern Latin1CharsZ +LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen); + } // namespace JS inline void JS_free(JS::Latin1CharsZ& ptr) { js_free((void*)ptr.get()); } diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index fb4d6e12a71b..cd2cbb557b84 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -8,6 +8,8 @@ #include "mozilla/Range.h" +#include + #include "jscntxt.h" #include "jsprf.h" @@ -253,19 +255,20 @@ enum InflateUTF8Action { Copy }; -static const uint32_t REPLACE_UTF8 = 0xFFFD; +static const char16_t REPLACE_UTF8 = 0xFFFD; +static const Latin1Char REPLACE_UTF8_LATIN1 = '?'; // If making changes to this algorithm, make sure to also update // LossyConvertUTF8toUTF16() in dom/wifi/WifiUtils.cpp -template +template static bool -InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, size_t* dstlenp, +InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t* dstlenp, bool* isAsciip) { if (Action != AssertNoInvalids) *isAsciip = true; - // Count how many char16_t characters need to be in the inflated string. + // Count how many code units need to be in the inflated string. // |i| is the index into |src|, and |j| is the the index into |dst|. size_t srclen = src.length(); uint32_t j = 0; @@ -274,7 +277,7 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz if (!(v & 0x80)) { // ASCII code unit. Simple copy. if (Action == Copy) - dst[j] = char16_t(v); + dst[j] = CharT(v); } else { // Non-ASCII code unit. Determine its length in bytes (n). @@ -292,10 +295,14 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz } else if (Action == AssertNoInvalids) { \ MOZ_CRASH("invalid UTF-8 string: " # report); \ } else { \ - if (Action == Copy) \ - dst[j] = char16_t(REPLACE_UTF8); \ - else \ + if (Action == Copy) { \ + if (std::is_same::value) \ + dst[j] = CharT(REPLACE_UTF8_LATIN1); \ + else \ + dst[j] = CharT(REPLACE_UTF8); \ + } else { \ MOZ_ASSERT(Action == CountAndIgnoreInvalids); \ + } \ n = n2; \ goto invalidMultiByteCodeUnit; \ } \ @@ -324,25 +331,24 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz if ((src[i + m] & 0xC0) != 0x80) INVALID(ReportInvalidCharacter, i, m); - // Determine the code unit's length in char16_t and act accordingly. + // Determine the code unit's length in CharT and act accordingly. v = JS::Utf8ToOneUcs4Char((uint8_t*)&src[i], n); if (v < 0x10000) { - // The n-byte UTF8 code unit will fit in a single char16_t. + // The n-byte UTF8 code unit will fit in a single CharT. if (Action == Copy) - dst[j] = char16_t(v); - + dst[j] = CharT(v); } else { v -= 0x10000; if (v <= 0xFFFFF) { - // The n-byte UTF8 code unit will fit in two char16_t units. + // The n-byte UTF8 code unit will fit in two CharT units. if (Action == Copy) - dst[j] = char16_t((v >> 10) + 0xD800); + dst[j] = CharT((v >> 10) + 0xD800); j++; if (Action == Copy) - dst[j] = char16_t((v & 0x3FF) + 0xDC00); + dst[j] = CharT((v & 0x3FF) + 0xDC00); } else { - // The n-byte UTF8 code unit won't fit in two char16_t units. + // The n-byte UTF8 code unit won't fit in two CharT units. INVALID(ReportTooBigCharacter, v, 1); } } @@ -361,61 +367,73 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, char16_t* dst, siz return true; } -template -static TwoByteCharsZ +template +static CharsT InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, size_t* outlen) { + using CharT = typename CharsT::CharT; *outlen = 0; bool isAscii; - if (!InflateUTF8StringToBuffer(cx, src, /* dst = */ nullptr, outlen, &isAscii)) - return TwoByteCharsZ(); + if (!InflateUTF8StringToBuffer(cx, src, /* dst = */ nullptr, outlen, &isAscii)) + return CharsT(); - char16_t* dst = cx->pod_malloc(*outlen + 1); // +1 for NUL + CharT* dst = cx->pod_malloc(*outlen + 1); // +1 for NUL if (!dst) { ReportOutOfMemory(cx); - return TwoByteCharsZ(); + return CharsT(); } if (isAscii) { size_t srclen = src.length(); MOZ_ASSERT(*outlen == srclen); for (uint32_t i = 0; i < srclen; i++) - dst[i] = char16_t(src[i]); - + dst[i] = CharT(src[i]); } else { - JS_ALWAYS_TRUE(InflateUTF8StringToBuffer(cx, src, dst, outlen, &isAscii)); + JS_ALWAYS_TRUE((InflateUTF8StringToBuffer(cx, src, dst, outlen, &isAscii))); } dst[*outlen] = 0; // NUL char - return TwoByteCharsZ(dst, *outlen); + return CharsT(dst, *outlen); } TwoByteCharsZ JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen) { - return InflateUTF8StringHelper(cx, utf8, outlen); + return InflateUTF8StringHelper(cx, utf8, outlen); } TwoByteCharsZ JS::UTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen) { UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str())); - return InflateUTF8StringHelper(cx, chars, outlen); + return InflateUTF8StringHelper(cx, chars, outlen); } TwoByteCharsZ JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen) { - return InflateUTF8StringHelper(cx, utf8, outlen); + return InflateUTF8StringHelper(cx, utf8, outlen); } TwoByteCharsZ JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, size_t* outlen) { UTF8Chars chars(utf8.c_str(), strlen(utf8.c_str())); - return InflateUTF8StringHelper(cx, chars, outlen); + return InflateUTF8StringHelper(cx, chars, outlen); +} + +Latin1CharsZ +JS::UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen) +{ + return InflateUTF8StringHelper(cx, utf8, outlen); +} + +Latin1CharsZ +JS::LossyUTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen) +{ + return InflateUTF8StringHelper(cx, utf8, outlen); } #ifdef DEBUG @@ -424,6 +442,7 @@ JS::ConstUTF8CharsZ::validate(size_t aLength) { MOZ_ASSERT(data_); UTF8Chars chars(data_, aLength); - InflateUTF8StringToBuffer(nullptr, chars, nullptr, nullptr, nullptr); + InflateUTF8StringToBuffer(nullptr, chars, nullptr, nullptr, + nullptr); } #endif From 2057ca608bca29f81c28d75aeed4af0c6e79d37c Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Mon, 15 Aug 2016 15:50:15 +0900 Subject: [PATCH 075/102] Bug 1289003 - Part 2: Add FindSmallestEncoding. r=jwalden --- js/public/CharacterEncoding.h | 18 ++++++++++ js/src/vm/CharacterEncoding.cpp | 58 ++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/js/public/CharacterEncoding.h b/js/public/CharacterEncoding.h index 8c166013c788..af040ffbc4a3 100644 --- a/js/public/CharacterEncoding.h +++ b/js/public/CharacterEncoding.h @@ -288,6 +288,24 @@ JS_PUBLIC_API(void) DeflateStringToUTF8Buffer(JSFlatString* src, mozilla::RangedPtr dst, size_t* dstlenp = nullptr, size_t* numcharsp = nullptr); +/* + * The smallest character encoding capable of fully representing a particular + * string. + */ +enum class SmallestEncoding { + ASCII, + Latin1, + UTF16 +}; + +/* + * Returns the smallest encoding possible for the given string: if all + * codepoints are <128 then ASCII, otherwise if all codepoints are <256 + * Latin-1, else UTF16. + */ +JS_PUBLIC_API(SmallestEncoding) +FindSmallestEncoding(UTF8Chars utf8); + /* * Return a null-terminated Latin-1 string copied from the input string, * storing its length (excluding null terminator) in |*outlen|. Fail and diff --git a/js/src/vm/CharacterEncoding.cpp b/js/src/vm/CharacterEncoding.cpp index cd2cbb557b84..7339142142c0 100644 --- a/js/src/vm/CharacterEncoding.cpp +++ b/js/src/vm/CharacterEncoding.cpp @@ -8,6 +8,7 @@ #include "mozilla/Range.h" +#include #include #include "jscntxt.h" @@ -252,7 +253,8 @@ enum InflateUTF8Action { CountAndReportInvalids, CountAndIgnoreInvalids, AssertNoInvalids, - Copy + Copy, + FindEncoding }; static const char16_t REPLACE_UTF8 = 0xFFFD; @@ -263,10 +265,16 @@ static const Latin1Char REPLACE_UTF8_LATIN1 = '?'; template static bool InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t* dstlenp, - bool* isAsciip) + JS::SmallestEncoding *smallestEncoding) { if (Action != AssertNoInvalids) - *isAsciip = true; + *smallestEncoding = JS::SmallestEncoding::ASCII; + auto RequireLatin1 = [&smallestEncoding]{ + *smallestEncoding = std::max(JS::SmallestEncoding::Latin1, *smallestEncoding); + }; + auto RequireUTF16 = [&smallestEncoding]{ + *smallestEncoding = JS::SmallestEncoding::UTF16; + }; // Count how many code units need to be in the inflated string. // |i| is the index into |src|, and |j| is the the index into |dst|. @@ -281,8 +289,6 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t } else { // Non-ASCII code unit. Determine its length in bytes (n). - if (Action != AssertNoInvalids) - *isAsciip = false; uint32_t n = 1; while (v & (0x80 >> n)) n++; @@ -301,7 +307,8 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t else \ dst[j] = CharT(REPLACE_UTF8); \ } else { \ - MOZ_ASSERT(Action == CountAndIgnoreInvalids); \ + MOZ_ASSERT(Action == CountAndIgnoreInvalids || \ + Action == FindEncoding); \ } \ n = n2; \ goto invalidMultiByteCodeUnit; \ @@ -327,12 +334,24 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t } // Check the continuation bytes. - for (uint32_t m = 1; m < n; m++) + for (uint32_t m = 1; m < n; m++) { if ((src[i + m] & 0xC0) != 0x80) INVALID(ReportInvalidCharacter, i, m); + } // Determine the code unit's length in CharT and act accordingly. v = JS::Utf8ToOneUcs4Char((uint8_t*)&src[i], n); + if (Action != AssertNoInvalids) { + if (v > 0xff) { + RequireUTF16(); + if (Action == FindEncoding) { + MOZ_ASSERT(dst == nullptr); + return true; + } + } else { + RequireLatin1(); + } + } if (v < 0x10000) { // The n-byte UTF8 code unit will fit in a single CharT. if (Action == Copy) @@ -358,10 +377,12 @@ InflateUTF8StringToBuffer(JSContext* cx, const UTF8Chars src, CharT* dst, size_t // header will do the final i++ to move to the start of the next // code unit. i += n - 1; + if (Action != AssertNoInvalids) + RequireUTF16(); } } - if (Action != AssertNoInvalids) + if (Action != AssertNoInvalids || Action != FindEncoding) *dstlenp = j; return true; @@ -374,8 +395,8 @@ InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, size_t* outlen) using CharT = typename CharsT::CharT; *outlen = 0; - bool isAscii; - if (!InflateUTF8StringToBuffer(cx, src, /* dst = */ nullptr, outlen, &isAscii)) + JS::SmallestEncoding encoding; + if (!InflateUTF8StringToBuffer(cx, src, /* dst = */ nullptr, outlen, &encoding)) return CharsT(); CharT* dst = cx->pod_malloc(*outlen + 1); // +1 for NUL @@ -384,13 +405,13 @@ InflateUTF8StringHelper(JSContext* cx, const UTF8Chars src, size_t* outlen) return CharsT(); } - if (isAscii) { + if (encoding == JS::SmallestEncoding::ASCII) { size_t srclen = src.length(); MOZ_ASSERT(*outlen == srclen); for (uint32_t i = 0; i < srclen; i++) dst[i] = CharT(src[i]); } else { - JS_ALWAYS_TRUE((InflateUTF8StringToBuffer(cx, src, dst, outlen, &isAscii))); + MOZ_ALWAYS_TRUE((InflateUTF8StringToBuffer(cx, src, dst, outlen, &encoding))); } dst[*outlen] = 0; // NUL char @@ -424,6 +445,19 @@ JS::LossyUTF8CharsToNewTwoByteCharsZ(JSContext* cx, const ConstUTF8CharsZ& utf8, return InflateUTF8StringHelper(cx, chars, outlen); } +JS::SmallestEncoding +JS::FindSmallestEncoding(UTF8Chars utf8) +{ + JS::SmallestEncoding encoding; + MOZ_ALWAYS_TRUE((InflateUTF8StringToBuffer( + /* cx = */ nullptr, + utf8, + /* dst = */ nullptr, + /* dstlen = */ nullptr, + &encoding))); + return encoding; +} + Latin1CharsZ JS::UTF8CharsToNewLatin1CharsZ(JSContext* cx, const UTF8Chars utf8, size_t* outlen) { From 3a45d46820a4cfa040bc35d5324f4f0d1864adb0 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Sat, 13 Aug 2016 23:24:22 +0900 Subject: [PATCH 076/102] Bug 1289003 - Part 3: Add JS_NewStringCopyUTF8N and JS_NewStringCopyUTF8Z. r=jwalden --- js/src/jsapi.cpp | 17 +++++++++++++++++ js/src/jsapi.h | 7 +++++++ js/src/vm/String.cpp | 36 ++++++++++++++++++++++++++++++++++++ js/src/vm/String.h | 11 +++++++++++ 4 files changed, 71 insertions(+) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8b71d1c9df05..dbeb61126c5b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -81,6 +81,7 @@ #include "vm/SelfHosting.h" #include "vm/Shape.h" #include "vm/StopIterationObject.h" +#include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/Symbol.h" #include "vm/TypedArrayCommon.h" @@ -4952,6 +4953,22 @@ JS_NewStringCopyZ(JSContext* cx, const char* s) return NewStringCopyZ(cx, s); } +JS_PUBLIC_API(JSString*) +JS_NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ s) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + return NewStringCopyUTF8Z(cx, s); +} + +JS_PUBLIC_API(JSString*) +JS_NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars s) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + return NewStringCopyUTF8N(cx, s); +} + JS_PUBLIC_API(bool) JS_StringHasBeenPinned(JSContext* cx, JSString* str) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 7ba059eb3d82..3d6b2068f49f 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -27,6 +27,7 @@ #include "jspubtd.h" #include "js/CallArgs.h" +#include "js/CharacterEncoding.h" #include "js/Class.h" #include "js/GCVector.h" #include "js/HashTable.h" @@ -4639,6 +4640,12 @@ JS_NewStringCopyN(JSContext* cx, const char* s, size_t n); extern JS_PUBLIC_API(JSString*) JS_NewStringCopyZ(JSContext* cx, const char* s); +extern JS_PUBLIC_API(JSString*) +JS_NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ s); + +extern JS_PUBLIC_API(JSString*) +JS_NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars s); + extern JS_PUBLIC_API(JSString*) JS_AtomizeAndPinJSString(JSContext* cx, JS::HandleString str); diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 2a9f648232f9..532e3303ec51 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -1303,6 +1303,42 @@ NewStringCopyN(ExclusiveContext* cx, const Latin1Char* s, size_t n); template JSFlatString* NewStringCopyN(ExclusiveContext* cx, const Latin1Char* s, size_t n); + +template +JSFlatString* +NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8) +{ + JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8); + if (encoding == JS::SmallestEncoding::ASCII) + return NewStringCopyN(cx, utf8.start().get(), utf8.length()); + + size_t length; + if (encoding == JS::SmallestEncoding::Latin1) { + Latin1Char* latin1 = UTF8CharsToNewLatin1CharsZ(cx, utf8, &length).get(); + if (!latin1) + return nullptr; + + JSFlatString* result = NewString(cx, latin1, length); + if (!result) + js_free((void*)latin1); + return result; + } + + MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16); + + char16_t* utf16 = UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get(); + if (!utf16) + return nullptr; + + JSFlatString* result = NewString(cx, utf16, length); + if (!result) + js_free((void*)utf16); + return result; +} + +template JSFlatString* +NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8); + } /* namespace js */ #ifdef DEBUG diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 1cf9d5b97393..da82852d939b 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -1210,6 +1210,17 @@ NewStringCopyZ(js::ExclusiveContext* cx, const char* s) return NewStringCopyN(cx, s, strlen(s)); } +template +extern JSFlatString* +NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8); + +template +inline JSFlatString* +NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ utf8) +{ + return NewStringCopyUTF8N(cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str()))); +} + } /* namespace js */ // Addon IDs are interned atoms which are never destroyed. This detail is From 78ca020a316536596e4ec4a918510ca241587409 Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 13:56:11 +0800 Subject: [PATCH 077/102] Bug 1295094 - Part 10. nsDisplayMask creation flow. r=mstange MozReview-Commit-ID: Jk9B54WtThK --HG-- extra : rebase_source : 199b71fef0cce209b048dc362f89ecd700fda3e9 --- layout/base/nsDisplayList.cpp | 4 ++-- layout/generic/nsFrame.cpp | 10 +++++++--- layout/svg/nsSVGIntegrationUtils.cpp | 9 +++++++++ layout/svg/nsSVGIntegrationUtils.h | 6 ++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index fa8b8b1e1bba..70553ce16f32 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -6795,9 +6795,9 @@ nsDisplayMask::BuildLayer(nsDisplayListBuilder* aBuilder, return nullptr; } - if (mFrame->StyleEffects()->mOpacity == 0.0f && - !mOpacityItemCreated) + if (mFrame->StyleEffects()->mOpacity == 0.0f && !mOpacityItemCreated) { return nullptr; + } nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 5fb20fa91c4e..543ba608a6f8 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2446,9 +2446,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, } // Revert to the post-filter dirty rect. buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); - /* List now emptied, so add the new list to the top. */ - resultList.AppendNewToTop( - new (aBuilder) nsDisplayMask(aBuilder, this, &resultList, useOpacity)); + + if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) { + /* List now emptied, so add the new list to the top. */ + resultList.AppendNewToTop( + new (aBuilder) nsDisplayMask(aBuilder, this, &resultList, useOpacity)); + } + // Also add the hoisted scroll info items. We need those for APZ scrolling // because nsDisplayMask items can't build active layers. aBuilder->ExitSVGEffectsContents(); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 77071662a1ad..e092e6be9b62 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -160,6 +160,15 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) style->mMask.HasLayerWithImage(); } +bool +nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame) +{ + const nsStyleSVGReset *style = aFrame->StyleSVGReset(); + return style->HasClipPath() || + style->mMask.HasLayerWithImage() || + (aFrame->StyleEffects()->mOpacity != 1.0f); +} + // For non-SVG frames, this gives the offset to the frame's "user space". // For SVG frames, this returns a zero offset. static nsPoint diff --git a/layout/svg/nsSVGIntegrationUtils.h b/layout/svg/nsSVGIntegrationUtils.h index c4c592798a93..24c0828faccb 100644 --- a/layout/svg/nsSVGIntegrationUtils.h +++ b/layout/svg/nsSVGIntegrationUtils.h @@ -46,6 +46,12 @@ public: static bool UsingEffectsForFrame(const nsIFrame* aFrame); + /** + * Returns true if mask or clippath are currently applied to this frame. + */ + static bool + UsingMaskOrClipPathForFrame(const nsIFrame* aFrame); + /** * Returns the size of the union of the border-box rects of all of * aNonSVGFrame's continuations. From a16d0ca68b002e385e84fbf9c137c336d935530c Mon Sep 17 00:00:00 2001 From: cku Date: Tue, 16 Aug 2016 15:23:33 +0800 Subject: [PATCH 078/102] Bug 1295094 - Part 11. Implement nsDisplayFilter. r=mstange MozReview-Commit-ID: 1V6dxJsejsi --HG-- extra : rebase_source : 713cf1375c950644cda8dafa96b7227d9ebf2c89 --- layout/base/nsDisplayItemTypesList.h | 1 + layout/base/nsDisplayList.cpp | 163 ++++++++++++++++++++++++--- layout/base/nsDisplayList.h | 34 +++++- layout/base/nsLayoutDebugger.cpp | 6 + 4 files changed, 185 insertions(+), 19 deletions(-) diff --git a/layout/base/nsDisplayItemTypesList.h b/layout/base/nsDisplayItemTypesList.h index 7aaa9b1df8a2..7ca0bd052f6c 100644 --- a/layout/base/nsDisplayItemTypesList.h +++ b/layout/base/nsDisplayItemTypesList.h @@ -52,6 +52,7 @@ DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY) DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR) DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT) DECLARE_DISPLAY_ITEM_TYPE(MASK) +DECLARE_DISPLAY_ITEM_TYPE(FILTER) DECLARE_DISPLAY_ITEM_TYPE(SVG_GLYPHS) DECLARE_DISPLAY_ITEM_TYPE(SVG_OUTER_SVG) DECLARE_DISPLAY_ITEM_TYPE(SVG_PATH_GEOMETRY) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 70553ce16f32..990b88453819 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -6677,22 +6677,6 @@ nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect } } -bool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder, - nsRegion* aVisibleRegion) { - nsPoint offset = ToReferenceFrame(); - nsRect dirtyRect = - nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame, - mVisibleRect - offset) + - offset; - - // Our children may be made translucent or arbitrarily deformed so we should - // not allow them to subtract area from aVisibleRegion. - nsRegion childrenVisible(dirtyRect); - nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder)); - mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r); - return true; -} - gfxRect nsDisplaySVGEffects::BBoxInUserSpace() const { @@ -6826,6 +6810,16 @@ nsDisplayMask::GetLayerState(nsDisplayListBuilder* aBuilder, return LAYER_SVG_EFFECTS; } +bool nsDisplayMask::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) { + // Our children may be made translucent or arbitrarily deformed so we should + // not allow them to subtract area from aVisibleRegion. + nsRegion childrenVisible(mVisibleRect); + nsRect r = mVisibleRect.Intersect(mList.GetBounds(aBuilder)); + mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r); + return true; +} + void nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx, @@ -6885,3 +6879,140 @@ nsDisplayMask::PrintEffects(nsACString& aTo) } #endif +nsDisplayFilter::nsDisplayFilter(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, nsDisplayList* aList, + bool aOpacityItemCreated) + : nsDisplaySVGEffects(aBuilder, aFrame, aList, aOpacityItemCreated) +{ + MOZ_COUNT_CTOR(nsDisplayFilter); +} + +#ifdef NS_BUILD_REFCNT_LOGGING +nsDisplayFilter::~nsDisplayFilter() +{ + MOZ_COUNT_DTOR(nsDisplayFilter); +} +#endif + +already_AddRefed +nsDisplayFilter::BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) +{ + if (!ValidateSVGFrame()) { + return nullptr; + } + + if (mFrame->StyleEffects()->mOpacity == 0.0f && !mOpacityItemCreated) { + return nullptr; + } + + nsIFrame* firstFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + + ContainerLayerParameters newContainerParameters = aContainerParameters; + if (effectProperties.HasValidFilter()) { + newContainerParameters.mDisableSubpixelAntialiasingInDescendants = true; + } + + RefPtr container = aManager->GetLayerBuilder()-> + BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList, + newContainerParameters, nullptr); + + return container.forget(); +} + +bool nsDisplayFilter::TryMerge(nsDisplayItem* aItem) +{ + if (aItem->GetType() != TYPE_FILTER) { + return false; + } + + // items for the same content element should be merged into a single + // compositing group. + // aItem->Frame() returns non-null because it's nsDisplayFilter + if (aItem->Frame()->GetContent() != mFrame->GetContent()) { + return false; + } + if (aItem->GetClip() != GetClip()) { + return false; + } + if (aItem->ScrollClip() != ScrollClip()) { + return false; + } + + nsDisplayFilter* other = static_cast(aItem); + MergeFromTrackingMergedFrames(other); + mEffectsBounds.UnionRect(mEffectsBounds, + other->mEffectsBounds + other->mFrame->GetOffsetTo(mFrame)); + + return true; +} + +LayerState +nsDisplayFilter::GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) +{ + return LAYER_SVG_EFFECTS; +} + +bool nsDisplayFilter::ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) { + nsPoint offset = ToReferenceFrame(); + nsRect dirtyRect = + nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mFrame, + mVisibleRect - offset) + + offset; + + // Our children may be made translucent or arbitrarily deformed so we should + // not allow them to subtract area from aVisibleRegion. + nsRegion childrenVisible(dirtyRect); + nsRect r = dirtyRect.Intersect(mList.GetBounds(aBuilder)); + mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r); + return true; +} + +void +nsDisplayFilter::PaintAsLayer(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx, + LayerManager* aManager) +{ + nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize()); + nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(), + mFrame, mVisibleRect, + borderArea, aBuilder, + aManager, mOpacityItemCreated); + + image::DrawResult result = + nsSVGIntegrationUtils::PaintFilter(params); + + nsDisplaySVGEffectsGeometry::UpdateDrawResult(this, result); +} + +#ifdef MOZ_DUMP_PAINTING +void +nsDisplayFilter::PrintEffects(nsACString& aTo) +{ + nsIFrame* firstFrame = + nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + bool first = true; + aTo += " effects=("; + if (mFrame->StyleEffects()->mOpacity != 1.0f) { + first = false; + aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity); + } + if (effectProperties.HasValidFilter()) { + if (!first) { + aTo += ", "; + } + aTo += "filter"; + first = false; + } + aTo += ")"; +} +#endif diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index bb592d562f58..1b704800711d 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -3801,8 +3801,6 @@ public: return mEffectsBounds + ToReferenceFrame(); } - virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, - nsRegion* aVisibleRegion) override; virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override { return false; } @@ -3817,7 +3815,6 @@ public: virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry, nsRegion* aInvalidRegion) override; - protected: bool ValidateSVGFrame(); @@ -3850,6 +3847,37 @@ public: LayerManager* aManager, const ContainerLayerParameters& aParameters) override; + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) override; +#ifdef MOZ_DUMP_PAINTING + void PrintEffects(nsACString& aTo); +#endif + + void PaintAsLayer(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx, + LayerManager* aManager); +}; + +class nsDisplayFilter : public nsDisplaySVGEffects { +public: + nsDisplayFilter(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, + nsDisplayList* aList, bool aOpacityItemCreated); +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayFilter(); +#endif + + NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER) + + virtual bool TryMerge(nsDisplayItem* aItem) override; + virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aContainerParameters) override; + virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, + LayerManager* aManager, + const ContainerLayerParameters& aParameters) override; + + virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) override; #ifdef MOZ_DUMP_PAINTING void PrintEffects(nsACString& aTo); #endif diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 9becf23b5003..22c313c72b71 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -201,6 +201,12 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, (static_cast(aItem))->PrintEffects(str); aStream << str.get(); } + + if (aItem->GetType() == nsDisplayItem::TYPE_FILTER) { + nsCString str; + (static_cast(aItem))->PrintEffects(str); + aStream << str.get(); + } #endif aStream << "\n"; #ifdef MOZ_DUMP_PAINTING From 5760f377d34af7da55a9b218aafe3a8cabef3db1 Mon Sep 17 00:00:00 2001 From: cku Date: Thu, 1 Sep 2016 11:11:54 +0800 Subject: [PATCH 079/102] Bug 1295094 - Part 12. nsDisplayFilter creation flow. r=mstange MozReview-Commit-ID: 3Ts8GqYyNej --HG-- extra : rebase_source : 96a2f2c7ad69328a62a0033603fa572341b25099 --- layout/generic/nsFrame.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 543ba608a6f8..841dadd982a0 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2441,12 +2441,21 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, * output even if the element being filtered wouldn't otherwise do so. */ if (usingSVGEffects) { + MOZ_ASSERT(StyleEffects()->HasFilters() || + nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)); + if (clipCapturedBy == ContainerItemType::eSVGEffects) { clipState.ExitStackingContextContents(&containerItemScrollClip); } // Revert to the post-filter dirty rect. buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); + if (StyleEffects()->HasFilters()) { + /* List now emptied, so add the new list to the top. */ + resultList.AppendNewToTop( + new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList, useOpacity)); + } + if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) { /* List now emptied, so add the new list to the top. */ resultList.AppendNewToTop( From 2b475f7d03c9ba758da2f0471694b9ab40e87378 Mon Sep 17 00:00:00 2001 From: cku Date: Thu, 1 Sep 2016 11:14:00 +0800 Subject: [PATCH 080/102] Bug 1295094 - Part 13. nsDisplayFilter painting. r=mstange MozReview-Commit-ID: LyCUb4NacaO --HG-- extra : rebase_source : d3f86358e41e45e6b2d12bc87fdea5b927b1c92c --- layout/base/FrameLayerBuilder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index ae0e45451892..4a61c6c855ce 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -3594,6 +3594,11 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder, if (basic->InTransaction()) { basic->AbortTransaction(); } + } else if (aItem->GetType() == nsDisplayItem::TYPE_FILTER){ + static_cast(aItem)->PaintAsLayer(aBuilder, aCtx, basic); + if (basic->InTransaction()) { + basic->AbortTransaction(); + } } else { basic->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder); } From fecac37ed2b99c14066b4fd7e12acf9af3f9bf16 Mon Sep 17 00:00:00 2001 From: cku Date: Thu, 1 Sep 2016 14:54:11 +0800 Subject: [PATCH 081/102] Bug 1295094 - Part 14. Skip any filter effect while generating glyph mask for bg-clip:text. r=jfkthame MozReview-Commit-ID: CTzu7uhAaQM --HG-- extra : rebase_source : cf935bf3e8cc2b7081bd016fea94b8d5062ad014 --- layout/generic/nsFrame.cpp | 3 ++- layout/svg/nsSVGIntegrationUtils.cpp | 25 ++++++++----------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 841dadd982a0..6a6385e79570 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2450,7 +2450,8 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, // Revert to the post-filter dirty rect. buildingDisplayList.SetDirtyRect(dirtyRectOutsideSVGEffects); - if (StyleEffects()->HasFilters()) { + // Skip all filter effects while generating glyph mask. + if (StyleEffects()->HasFilters() && !aBuilder->IsForGenerateGlyphMask()) { /* List now emptied, so add the new list to the top. */ resultList.AppendNewToTop( new (aBuilder) nsDisplayFilter(aBuilder, this, &resultList, useOpacity)); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index e092e6be9b62..8bfb4cd6fc56 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -926,6 +926,8 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) DrawResult nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams) { + MOZ_ASSERT(!aParams.builder->IsForGenerateGlyphMask()); + nsIFrame* frame = aParams.frame; DrawResult result = DrawResult::SUCCESS; bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); @@ -968,23 +970,12 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams) } /* Paint the child and apply filters */ - if (!aParams.builder->IsForGenerateGlyphMask()) { - RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, - offsetToUserSpace); - - nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; - gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); - nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), - tm, &callback, &dirtyRegion); - } else { - target->SetMatrix(matrixAutoSaveRestore.Matrix()); - BasicLayerManager* basic = static_cast(aParams.layerManager); - RefPtr oldCtx = basic->GetTarget(); - basic->SetTarget(target); - aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, - aParams.builder); - basic->SetTarget(oldCtx); - } + RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, + offsetToUserSpace); + nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; + gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); + nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), + tm, &callback, &dirtyRegion); if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { MOZ_ASSERT(target != &aParams.ctx); From d576474289c2301ca2e511b825409a6cf7210023 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 1 Sep 2016 15:16:16 -0700 Subject: [PATCH 082/102] Bug 1298944 - Upgrade from Mercurial 3.8.4 to 3.9.1; r=dustin Mercurial 3.9 changes the default security settings to make Mercurial secure by default. It is important for Firefox's automation to be secure. MozReview-Commit-ID: IF7Z74111hI --HG-- extra : rebase_source : 3bc3cbe7688a8ecdd2593cbe202e456a649b49e1 --- testing/docker/recipes/install-mercurial.sh | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/testing/docker/recipes/install-mercurial.sh b/testing/docker/recipes/install-mercurial.sh index cb09f960934e..f771f832341c 100644 --- a/testing/docker/recipes/install-mercurial.sh +++ b/testing/docker/recipes/install-mercurial.sh @@ -13,22 +13,22 @@ if [ -f /etc/lsb-release ]; then if [ "${DISTRIB_ID}" = "Ubuntu" -a "${DISTRIB_RELEASE}" = "16.04" ]; then HG_DEB=1 - HG_DIGEST=7b1fc1217e0dcaeea852b0af2dc559b1aafb704fbee7e29cbec75af57bacb84910a7ec92b5c33f04ee98f23b3a57f1fa451173fe7c8a96f58faefe319dc7dde1 - HG_SIZE=44878 - HG_FILENAME=mercurial_3.8.4_amd64.deb + HG_DIGEST=09c7c80324158b755c23855d47caeb40b953218b1c89c7f5f21bbdea9de1d13a7ed5a7e647022ff626fb9674655baf05f6965361ccef3fa82b94d1fa8a684187 + HG_SIZE=44956 + HG_FILENAME=mercurial_3.9.1_amd64.deb - HG_COMMON_DIGEST=b476e2612e7495a1c7c5adfd84511aa7479e26cc9070289513ec705fbfc4c61806ce2dbcceca0e63f2e80669be416f3467a3cebb522dcb8a6aeb62cdd3df82f2 - HG_COMMON_SIZE=1818422 - HG_COMMON_FILENAME=mercurial-common_3.8.4_all.deb + HG_COMMON_DIGEST=ef281d1368a8cf2363bc08c050aff3825028ba4d47d491e50e10f4c78574d5e87231a0096c7d9cb3439dd4b5172057a050e02946b4cf8b2bdf239ffd50a85d06 + HG_COMMON_SIZE=1847796 + HG_COMMON_FILENAME=mercurial-common_3.9.1_all.deb elif [ "${DISTRIB_ID}" = "Ubuntu" -a "${DISTRIB_RELEASE}" = "12.04" ]; then HG_DEB=1 - HG_DIGEST=96366b6baac26017a2ef9cd0bbbba4f18756e02921b0ff3541f677484d53d2041f01202b743ea8cdb96db58e88317da18befefc9711a085b054f9abc3dad1679 - HG_SIZE=54992 - HG_FILENAME=mercurial_3.8.4_amd64.deb + HG_DIGEST=f816a6ca91129c0723527d98a2978c253a3f941f4358f9f8abd6f3ab8e8601ed3efc347828aac8f0d0762f819f9b777299e31037c39eb0c5af05aa76ac25c3bf + HG_SIZE=55144 + HG_FILENAME=mercurial_3.9.1_amd64.deb - HG_COMMON_SIZE=2946616 - HG_COMMON_DIGEST=96c6bc305ae85f16885d0b6ac6800361d680811346a01338a56a2174a3c0ae5d86bbd827d93fe8c59d71acd2e1cc7d6e79e39a179836d5695cbfa3b370982ee5 - HG_COMMON_FILENAME=mercurial-common_3.8.4_all.deb + HG_COMMON_DIGEST=ac2b2fab9f19438c77147dca8df5020d10b129052e6c5f77ebe9a4c21eb0cedb1acfe25b146577bf5e9b66f3d6cfca2474f7575adfba1b3849b66bf5bc321015 + HG_COMMON_SIZE=2993590 + HG_COMMON_FILENAME=mercurial-common_3.9.4_all.deb fi fi From c8bb104946b5a5e868980db3fbb40d37ab4cda86 Mon Sep 17 00:00:00 2001 From: "Kearwood (Kip) Gilbert" Date: Tue, 30 Aug 2016 15:34:11 -0700 Subject: [PATCH 083/102] Bug 1297105 - Prevent crash when DX11 resources fail to be allocated for Oculus VR HMD presentation r=mccr8 MozReview-Commit-ID: 3KLGu3DJVbP --HG-- extra : rebase_source : 8a860df4e9ec374343c73c647833d8305d0f31b6 --- gfx/vr/gfxVROculus.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gfx/vr/gfxVROculus.cpp b/gfx/vr/gfxVROculus.cpp index 72edad08eca3..53f89158be74 100644 --- a/gfx/vr/gfxVROculus.cpp +++ b/gfx/vr/gfxVROculus.cpp @@ -758,6 +758,17 @@ VRDisplayOculus::SubmitFrame(TextureSourceD3D11* aSource, if (!mIsPresenting) { return; } + if (mRenderTargets.IsEmpty()) { + /** + * XXX - We should resolve fail the promise returned by + * VRDisplay.requestPresent() when the DX11 resources fail allocation + * in VRDisplayOculus::StartPresentation(). + * Bailing out here prevents the crash but content should be aware + * that frames are not being presented. + * See Bug 1299309. + **/ + return; + } MOZ_ASSERT(mDevice); MOZ_ASSERT(mContext); From cf040c01caf8951c94e8dfc9a345d3056b545b83 Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Fri, 2 Sep 2016 14:40:21 -0300 Subject: [PATCH 084/102] Bug 1298945 - Only show Slow Startup notification if profile is 3 months old. r=MattN MozReview-Commit-ID: J0bsvtBoEsv --HG-- extra : rebase_source : f63ddd59ca99cecd0c6d6e99b1319a77e081fefb --- browser/components/nsBrowserGlue.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 041b2c80a1a7..ce3c2e045c2f 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -845,7 +845,7 @@ BrowserGlue.prototype = { if (samples >= Services.prefs.getIntPref("browser.slowStartup.maxSamples")) { if (averageTime > Services.prefs.getIntPref("browser.slowStartup.timeThreshold")) - this._showSlowStartupNotification(); + this._calculateProfileAgeInDays().then(this._showSlowStartupNotification, null); averageTime = 0; samples = 0; } @@ -854,7 +854,25 @@ BrowserGlue.prototype = { Services.prefs.setIntPref("browser.slowStartup.samples", samples); }, - _showSlowStartupNotification: function () { + _calculateProfileAgeInDays: Task.async(function* () { + let ProfileAge = Cu.import("resource://gre/modules/ProfileAge.jsm", {}).ProfileAge; + let profileAge = new ProfileAge(null, null); + + let creationDate = yield profileAge.created; + let resetDate = yield profileAge.reset; + + // if the profile was reset, consider the + // reset date for its age. + let profileDate = resetDate || creationDate; + + const ONE_DAY = 24 * 60 * 60 * 1000; + return (Date.now() - profileDate) / ONE_DAY; + }), + + _showSlowStartupNotification: function (profileAge) { + if (profileAge < 90) // 3 months + return; + let win = RecentWindow.getMostRecentBrowserWindow(); if (!win) return; From 647b0985cc44d0f69c6cdb153314a32ec4033c97 Mon Sep 17 00:00:00 2001 From: Wes Kocher Date: Fri, 2 Sep 2016 11:38:22 -0700 Subject: [PATCH 085/102] Backed out 2 changesets (bug 1299164) for win vm bc4 bustage a=backout Backed out changeset 53dc795121e1 (bug 1299164) Backed out changeset c39ec15e7e21 (bug 1299164) --- dom/base/ImageEncoder.cpp | 12 +++------ dom/canvas/CanvasRenderingContext2D.cpp | 13 +--------- dom/canvas/ImageBitmapRenderingContext.cpp | 3 +-- dom/plugins/ipc/PluginInstanceChild.cpp | 10 +++---- gfx/2d/DataSurfaceHelpers.cpp | 26 ++----------------- gfx/2d/DataSurfaceHelpers.h | 16 ------------ gfx/2d/Factory.cpp | 15 ++--------- gfx/2d/ImageScaling.cpp | 16 ++++++------ gfx/2d/SourceSurfaceRawData.h | 3 +-- gfx/layers/TextureDIB.cpp | 19 +++++--------- gfx/layers/basic/GrallocTextureHostBasic.cpp | 10 +++---- gfx/layers/composite/CanvasLayerComposite.cpp | 4 +-- gfx/layers/composite/ImageLayerComposite.cpp | 4 +-- gfx/thebes/gfxAndroidPlatform.cpp | 4 --- gfx/thebes/gfxPlatformGtk.cpp | 4 --- gfx/thebes/gfxPlatformMac.cpp | 4 --- gfx/thebes/gfxWindowsPlatform.cpp | 4 --- 17 files changed, 32 insertions(+), 135 deletions(-) diff --git a/dom/base/ImageEncoder.cpp b/dom/base/ImageEncoder.cpp index a41dee080989..e9d24844b31d 100644 --- a/dom/base/ImageEncoder.cpp +++ b/dom/base/ImageEncoder.cpp @@ -369,10 +369,6 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, // get image bytes nsresult rv; if (aImageBuffer) { - if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { - return NS_ERROR_INVALID_ARG; - } - rv = ImageEncoder::GetInputStream( aSize.width, aSize.height, @@ -421,10 +417,6 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, imgIEncoder::INPUT_FORMAT_HOSTARGB, aOptions); } else { - if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { - return NS_ERROR_INVALID_ARG; - } - RefPtr dataSurface; RefPtr image(aImage); dataSurface = GetBRGADataSourceSurfaceSync(image.forget()); @@ -447,10 +439,12 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType, imgStream = do_QueryInterface(aEncoder); } } else { - if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) { + CheckedInt32 requiredBytes = CheckedInt32(aSize.width) * CheckedInt32(aSize.height) * 4; + if (MOZ_UNLIKELY(!requiredBytes.isValid())) { return NS_ERROR_INVALID_ARG; } + // no context, so we have to encode an empty image // note that if we didn't have a current context, the spec says we're // supposed to just return transparent black pixels of the canvas diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 4a2392fa30f1..6633e2175c66 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -5243,13 +5243,8 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32)); } else { - IntSize dtSize = IntSize::Ceil(sw, sh); - if (!Factory::AllowedSurfaceSize(dtSize)) { - aError.Throw(NS_ERROR_FAILURE); - return; - } drawDT = - gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(dtSize, + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize::Ceil(sw, sh), SurfaceFormat::B8G8R8A8); if (!drawDT || !drawDT->IsValid()) { aError.Throw(NS_ERROR_FAILURE); @@ -5275,12 +5270,6 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& aWindow, double aX, return; } RefPtr data = snapshot->GetDataSurface(); - if (!data || !Factory::AllowedSurfaceSize(data->GetSize())) { - gfxCriticalError() << "Unexpected invalid data source surface " << - (data ? data->GetSize() : IntSize(0,0)); - aError.Throw(NS_ERROR_FAILURE); - return; - } DataSourceSurface::MappedSurface rawData; if (NS_WARN_IF(!data->Map(DataSourceSurface::READ, &rawData))) { diff --git a/dom/canvas/ImageBitmapRenderingContext.cpp b/dom/canvas/ImageBitmapRenderingContext.cpp index 33845264e536..0238928386f8 100644 --- a/dom/canvas/ImageBitmapRenderingContext.cpp +++ b/dom/canvas/ImageBitmapRenderingContext.cpp @@ -109,8 +109,7 @@ ImageBitmapRenderingContext::MatchWithIntrinsicSize() temp->GetSize(), map.GetStride(), temp->GetFormat()); - if (!dt || !dt->IsValid()) { - gfxWarning() << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed"; + if (!dt) { return nullptr; } diff --git a/dom/plugins/ipc/PluginInstanceChild.cpp b/dom/plugins/ipc/PluginInstanceChild.cpp index 44a93574fbf0..f51fbac8c7c7 100644 --- a/dom/plugins/ipc/PluginInstanceChild.cpp +++ b/dom/plugins/ipc/PluginInstanceChild.cpp @@ -3786,13 +3786,9 @@ PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect, // Copy helper surface content to target dt = CreateDrawTargetForSurface(aSurface); } - if (dt && dt->IsValid()) { - RefPtr surface = - gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface); - dt->CopySurface(surface, aRect, aRect.TopLeft()); - } else { - gfxWarning() << "PluginInstanceChild::PaintRectToSurface failure"; - } + RefPtr surface = + gfxPlatform::GetSourceSurfaceForSurface(dt, renderSurface); + dt->CopySurface(surface, aRect, aRect.TopLeft()); } } diff --git a/gfx/2d/DataSurfaceHelpers.cpp b/gfx/2d/DataSurfaceHelpers.cpp index 87ef00fcd205..7c42afc4e861 100644 --- a/gfx/2d/DataSurfaceHelpers.cpp +++ b/gfx/2d/DataSurfaceHelpers.cpp @@ -265,7 +265,7 @@ BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight, int32_t aExtraBytes) { - if (MOZ_UNLIKELY(aHeight <= 0) || MOZ_UNLIKELY(aStride <= 0)) { + if (MOZ_UNLIKELY(aHeight <= 0)) { return 0; } @@ -279,29 +279,7 @@ BufferSizeFromStrideAndHeight(int32_t aStride, CheckedInt32 requiredBytes = CheckedInt32(aStride) * CheckedInt32(aHeight) + CheckedInt32(aExtraBytes); if (MOZ_UNLIKELY(!requiredBytes.isValid())) { - gfxWarning() << "Buffer size too big; returning zero " << aStride << ", " << aHeight << ", " << aExtraBytes; - return 0; - } - return requiredBytes.value(); -} - -size_t -BufferSizeFromDimensions(int32_t aWidth, - int32_t aHeight, - int32_t aDepth, - int32_t aExtraBytes) -{ - if (MOZ_UNLIKELY(aHeight <= 0) || - MOZ_UNLIKELY(aWidth <= 0) || - MOZ_UNLIKELY(aDepth <= 0)) { - return 0; - } - - // Similar to BufferSizeFromStrideAndHeight, but with an extra parameter. - - CheckedInt32 requiredBytes = CheckedInt32(aWidth) * CheckedInt32(aHeight) * CheckedInt32(aDepth) + CheckedInt32(aExtraBytes); - if (MOZ_UNLIKELY(!requiredBytes.isValid())) { - gfxWarning() << "Buffer size too big; returning zero " << aWidth << ", " << aHeight << ", " << aDepth << ", " << aExtraBytes; + gfxWarning() << "Buffer size too big; returning zero"; return 0; } return requiredBytes.value(); diff --git a/gfx/2d/DataSurfaceHelpers.h b/gfx/2d/DataSurfaceHelpers.h index b0fdf2983cbf..a0172e3ad744 100644 --- a/gfx/2d/DataSurfaceHelpers.h +++ b/gfx/2d/DataSurfaceHelpers.h @@ -92,22 +92,6 @@ BufferSizeFromStrideAndHeight(int32_t aStride, int32_t aHeight, int32_t aExtraBytes = 0); -/** - * Multiplies aWidth, aHeight, aDepth and makes sure the result is limited to - * something sane. To keep things consistent, this should always be used - * wherever we allocate a buffer based on surface dimensions. - * - * @param aExtra Optional argument to specify an additional number of trailing - * bytes (useful for creating intermediate surfaces for filters, for - * example). - * - * @return The result of the multiplication if it is acceptable, or else zero. - */ -size_t -BufferSizeFromDimensions(int32_t aWidth, - int32_t aHeight, - int32_t aDepth, - int32_t aExtraBytes = 0); /** * Copy aSrcRect from aSrc to aDest starting at aDestPoint. * @returns false if the copy is not successful or the aSrc's size is empty. diff --git a/gfx/2d/Factory.cpp b/gfx/2d/Factory.cpp index 92be0f08cf10..e791c953d16b 100644 --- a/gfx/2d/Factory.cpp +++ b/gfx/2d/Factory.cpp @@ -735,11 +735,6 @@ Factory::PurgeAllCaches() already_AddRefed Factory::CreateDrawTargetForCairoSurface(cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat) { - if (!AllowedSurfaceSize(aSize)) { - gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (Cairo) " << aSize; - return nullptr; - } - RefPtr retVal; #ifdef USE_CAIRO @@ -775,11 +770,6 @@ Factory::CreateSourceSurfaceForCairoSurface(cairo_surface_t* aSurface, const Int already_AddRefed Factory::CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize) { - if (!AllowedSurfaceSize(aSize)) { - gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "Failed to allocate a surface due to invalid size (CG) " << aSize; - return nullptr; - } - RefPtr retVal; RefPtr newTarget = new DrawTargetCG(); @@ -809,7 +799,7 @@ Factory::CreateWrappingDataSourceSurface(uint8_t *aData, SourceSurfaceDeallocator aDeallocator /* = nullptr */, void* aClosure /* = nullptr */) { - if (!AllowedSurfaceSize(aSize) || aStride <= 0) { + if (aSize.width <= 0 || aSize.height <= 0) { return nullptr; } if (!aDeallocator && aClosure) { @@ -853,8 +843,7 @@ Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize, int32_t aStride, bool aZero) { - if (!AllowedSurfaceSize(aSize) || - aStride < aSize.width * BytesPerPixel(aFormat)) { + if (aStride < aSize.width * BytesPerPixel(aFormat)) { gfxCriticalError(LoggerOptionsBasedOnSize(aSize)) << "CreateDataSourceSurfaceWithStride failed with bad stride " << aStride << ", " << aSize << ", " << aFormat; return nullptr; } diff --git a/gfx/2d/ImageScaling.cpp b/gfx/2d/ImageScaling.cpp index 190b7a7b95a7..85e99522bd4f 100644 --- a/gfx/2d/ImageScaling.cpp +++ b/gfx/2d/ImageScaling.cpp @@ -65,20 +65,20 @@ ImageHalfScaler::ScaleForSize(const IntSize &aSize) return; } - delete [] mDataStorage; - IntSize internalSurfSize; + internalSurfSize.width = max(scaleSize.width, mOrigSize.width / 2); internalSurfSize.height = max(scaleSize.height, mOrigSize.height / 2); - size_t bufLen = 0; - mStride = GetAlignedStride<16>(internalSurfSize.width, 4); - if (mStride > 0) { - // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We - // should add tools for this, see bug 751696. - bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); + mStride = internalSurfSize.width * 4; + if (mStride % 16) { + mStride += 16 - (mStride % 16); } + delete [] mDataStorage; + // Allocate 15 bytes extra to make sure we can get 16 byte alignment. We + // should add tools for this, see bug 751696. + size_t bufLen = BufferSizeFromStrideAndHeight(mStride, internalSurfSize.height, 15); if (bufLen == 0) { mSize.SizeTo(0, 0); mDataStorage = nullptr; diff --git a/gfx/2d/SourceSurfaceRawData.h b/gfx/2d/SourceSurfaceRawData.h index f4c707198b34..27c8c24d6ec5 100644 --- a/gfx/2d/SourceSurfaceRawData.h +++ b/gfx/2d/SourceSurfaceRawData.h @@ -80,8 +80,7 @@ private: friend class Factory; // If we have a custom deallocator, the |aData| will be released using the - // custom deallocator and |aClosure| in dtor. The assumption is that the - // caller will check for valid size and stride before making this call. + // custom deallocator and |aClosure| in dtor. void InitWrappingData(unsigned char *aData, const IntSize &aSize, int32_t aStride, diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index 6f3739d85bb0..9031b52d782d 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -5,7 +5,6 @@ #include "TextureDIB.h" #include "gfx2DGlue.h" -#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions #include "mozilla/layers/ISurfaceAllocator.h" #include "mozilla/ipc/ProtocolUtils.h" @@ -434,7 +433,7 @@ DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion) RefPtr surf = Factory::CreateWrappingDataSourceSurface(imgSurf->Data(), imgSurf->Stride(), mSize, mFormat); - if (!surf || !mTextureSource->Update(surf, const_cast(aRegion))) { + if (!mTextureSource->Update(surf, const_cast(aRegion))) { mTextureSource = nullptr; } @@ -474,20 +473,14 @@ TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) mTextureSource = mCompositor->CreateDataTextureSource(mFlags); } - uint8_t* data = nullptr; - int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height, BytesPerPixel(mFormat)); - if (totalBytes > 0) { - data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, totalBytes); - } + uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0, mSize.width * mSize.height * BytesPerPixel(mFormat)); if (data) { RefPtr surf = Factory::CreateWrappingDataSourceSurface(data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat); - if (surf) { - surf->AddUserData(&kFileMappingKey, data, UnmapFileData); - if (!mTextureSource->Update(surf, const_cast(aRegion))) { - mTextureSource = nullptr; - } - } else { + + surf->AddUserData(&kFileMappingKey, data, UnmapFileData); + + if (!mTextureSource->Update(surf, const_cast(aRegion))) { mTextureSource = nullptr; } } else { diff --git a/gfx/layers/basic/GrallocTextureHostBasic.cpp b/gfx/layers/basic/GrallocTextureHostBasic.cpp index a157eecacec4..f7d061785fac 100644 --- a/gfx/layers/basic/GrallocTextureHostBasic.cpp +++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp @@ -140,13 +140,9 @@ GrallocTextureHostBasic::Lock() mCropSize, mFormat); } - if (surf) { - mTextureSource = mCompositor->CreateDataTextureSource(mFlags); - mTextureSource->Update(surf, nullptr); - return true; - } - mMappedBuffer = nullptr; - return false; + mTextureSource = mCompositor->CreateDataTextureSource(mFlags); + mTextureSource->Update(surf, nullptr); + return true; } bool diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index 3c8299e05f72..53bfbc672a93 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -89,9 +89,7 @@ CanvasLayerComposite::RenderLayer(const IntRect& aClipRect) #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { RefPtr surf = mCompositableHost->GetAsSurface(); - if (surf) { - WriteSnapshotToDumpFile(this, surf); - } + WriteSnapshotToDumpFile(this, surf); } #endif diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index bac9f37909d9..7f3c7756f89f 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -99,9 +99,7 @@ ImageLayerComposite::RenderLayer(const IntRect& aClipRect) #ifdef MOZ_DUMP_PAINTING if (gfxEnv::DumpCompositorTextures()) { RefPtr surf = mImageHost->GetAsSurface(); - if (surf) { - WriteSnapshotToDumpFile(this, surf); - } + WriteSnapshotToDumpFile(this, surf); } #endif diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index d39ba46661aa..85297e1e740d 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -123,10 +123,6 @@ already_AddRefed gfxAndroidPlatform::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { - if (!Factory::AllowedSurfaceSize(aSize)) { - return nullptr; - } - RefPtr newSurface; newSurface = new gfxImageSurface(aSize, aFormat); diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp index 9d7f512f27a7..05537d68650f 100644 --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -142,10 +142,6 @@ already_AddRefed gfxPlatformGtk::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { - if (!Factory::AllowedSurfaceSize(aSize)) { - return nullptr; - } - RefPtr newSurface; bool needsClear = true; #ifdef MOZ_X11 diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp index f578e5fe9f9d..29bdd8f6d19d 100644 --- a/gfx/thebes/gfxPlatformMac.cpp +++ b/gfx/thebes/gfxPlatformMac.cpp @@ -116,10 +116,6 @@ already_AddRefed gfxPlatformMac::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { - if (!Factory::AllowedSurfaceSize(aSize)) { - return nullptr; - } - RefPtr newSurface = new gfxQuartzSurface(aSize, aFormat); return newSurface.forget(); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 3133cc0886d2..8e2e37bb2f90 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -585,10 +585,6 @@ already_AddRefed gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize, gfxImageFormat aFormat) { - if (!Factory::AllowedSurfaceSize(aSize)) { - return nullptr; - } - RefPtr surf = nullptr; #ifdef CAIRO_HAS_WIN32_SURFACE From bbfb2f45a2ded43591621e359d37db387ee29bc4 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 1 Sep 2016 15:41:26 -0700 Subject: [PATCH 086/102] Bug 1298947 - Remove curl and jq from decision image; r=dustin curl and jq were previously used to fetch and parse the TC secret. We now use Python for that. So remove the unused packages. This reduces the Docker image size by ~10MB. MozReview-Commit-ID: Nl7fC1aG7w --HG-- extra : rebase_source : e1bfe76f0e2efd06c2e2e8a94f6c3fb3dc4c0d50 --- testing/docker/decision/system-setup.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/docker/decision/system-setup.sh b/testing/docker/decision/system-setup.sh index 1dab3c7a35f7..6a8eccfdf793 100644 --- a/testing/docker/decision/system-setup.sh +++ b/testing/docker/decision/system-setup.sh @@ -7,8 +7,6 @@ test `whoami` == 'root' apt-get update apt-get install -y --force-yes --no-install-recommends \ ca-certificates \ - curl \ - jq \ python \ sudo From b695861225a37df6ac6a4236eeec46f01691cf44 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 1 Sep 2016 15:38:30 -0700 Subject: [PATCH 087/102] Bug 1298947 - Pin hg.mozilla.org fingerprint; r=dustin We just upgraded our run-time environment to Mercurial 3.9. 3.9 features a new [hostsecurity] config section and allows certificate fingerprints to be defined using SHA-256 hashes (not just SHA-1). A TaskCluster secret with the Mercurial 3.9 fingerprint format has been added. This commit takes advantage of it. MozReview-Commit-ID: 5NwJl9zOse2 --HG-- extra : rebase_source : fc985712d299242aec8f8fbc328eba8baca95508 --- testing/docker/recipes/run-task | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/testing/docker/recipes/run-task b/testing/docker/recipes/run-task index 96c5aef0053b..8d2d6c9018ae 100755 --- a/testing/docker/recipes/run-task +++ b/testing/docker/recipes/run-task @@ -19,11 +19,16 @@ import argparse import datetime import errno import grp +import json import os import pwd import re import subprocess import sys +import urllib2 + + +FINGERPRINT_URL = 'http://taskcluster/secrets/v1/secret/project/taskcluster/gecko/hgfingerprint' def print_line(prefix, m): @@ -88,8 +93,28 @@ def vcs_checkout(args): print('revision is not specified for checkout') sys.exit(1) + # Obtain certificate fingerprints. + try: + print_line(b'vcs', 'fetching hg.mozilla.org fingerprint from %s\n' % + FINGERPRINT_URL) + res = urllib2.urlopen(FINGERPRINT_URL, timeout=10) + secret = res.read() + except urllib2.URLError as e: + print('error retrieving hg fingerprint: %s' % e) + sys.exit(1) + + try: + secret = json.loads(secret, encoding='utf-8') + except ValueError: + print('invalid JSON in hg fingerprint secret') + sys.exit(1) + + hgmo_fingerprint = secret['secret']['fingerprints'].encode('ascii') + res = run_and_prefix_output(b'vcs', [ - b'/usr/bin/hg', b'robustcheckout', + b'/usr/bin/hg', + b'--config', b'hostsecurity.hg.mozilla.org:fingerprints=%s' % hgmo_fingerprint, + b'robustcheckout', b'--sharebase', b'/home/worker/hg-shared', b'--purge', b'--upstream', base_repo, From ef92f5ddf548d2419386bf9375b55062d56cd16d Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Thu, 1 Sep 2016 15:21:02 -0700 Subject: [PATCH 088/102] Bug 1298947 - Tag and use decision image 0.1.6; r=dustin Containing the Mercurial 3.9.1 version bump and the change to pin the hg.mo fingerprint from a TC secret. MozReview-Commit-ID: LVU7P0LqIvD --HG-- extra : rebase_source : 8ad46e014ba9840c9972b51ea43e8ccf14492cf2 --- .taskcluster.yml | 2 +- taskcluster/taskgraph/action.yml | 2 +- testing/docker/decision/VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.taskcluster.yml b/.taskcluster.yml index 9809ab4d57d6..2cc3978da7de 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -85,7 +85,7 @@ tasks: # Note: This task is built server side without the context or tooling that # exist in tree so we must hard code the version - image: 'taskcluster/decision:0.1.5' + image: 'taskcluster/decision:0.1.6' maxRunTime: 1800 diff --git a/taskcluster/taskgraph/action.yml b/taskcluster/taskgraph/action.yml index 5de296ca90ad..eaeb4b11635a 100644 --- a/taskcluster/taskgraph/action.yml +++ b/taskcluster/taskgraph/action.yml @@ -41,7 +41,7 @@ payload: # Note: This task is built server side without the context or tooling that # exist in tree so we must hard code the version - image: 'taskcluster/decision:0.1.5' + image: 'taskcluster/decision:0.1.6' # Virtually no network or other potentially risky operations happen as part # of the task timeout aside from the initial clone. We intentionally have diff --git a/testing/docker/decision/VERSION b/testing/docker/decision/VERSION index 9faa1b7a7339..c946ee6160c2 100644 --- a/testing/docker/decision/VERSION +++ b/testing/docker/decision/VERSION @@ -1 +1 @@ -0.1.5 +0.1.6 From 9bad2fd425ddb91a2648f9ecc50a6601deb9a9ec Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Thu, 25 Aug 2016 11:04:16 -0400 Subject: [PATCH 089/102] Bug 1272083 - Download and unpacking should be performed in process. r=gps Instead of downloading a file first and then unpacking it, we would like to fetch it into memory and then unpacking directly from there. This saves writing the file first to disk, thus, saving on IO. MozReview-Commit-ID: JdNGnxIYEvy --HG-- extra : rebase_source : 73c280fd23f3066d04dd3af07ac443db77dfd6d0 --- testing/mozharness/mozharness/base/script.py | 171 ++++++++++++++++--- testing/mozharness/test/test_base_script.py | 44 +++++ 2 files changed, 192 insertions(+), 23 deletions(-) diff --git a/testing/mozharness/mozharness/base/script.py b/testing/mozharness/mozharness/base/script.py index ad67830e4be7..ffbb460a3e8d 100755 --- a/testing/mozharness/mozharness/base/script.py +++ b/testing/mozharness/mozharness/base/script.py @@ -50,6 +50,8 @@ try: except ImportError: import json +from cStringIO import StringIO + from mozprocess import ProcessHandler from mozharness.base.config import BaseConfig from mozharness.base.log import SimpleFileLogger, MultiFileLogger, \ @@ -458,28 +460,157 @@ class ScriptMixin(PlatformMixin): **retry_args ) - def download_unpack(self, url, extract_to, extract_dirs=None, - error_level=FATAL): - """Generic method to download and extract a compressed file. - The downloaded file will always be saved to the working directory and is not getting - deleted after extracting. + def _filter_entries(self, namelist, extract_dirs): + """Filter entries of the archive based on the specified list of to extract dirs.""" + filter_partial = functools.partial(fnmatch.filter, namelist) + entries = itertools.chain(*map(filter_partial, extract_dirs or ['*'])) + + for entry in entries: + yield entry + + + def unzip(self, file_object, extract_to='.', extract_dirs='*', verbose=False): + """This method allows to extract a zip file without writing to disk first. + + Args: + file_object (object): Any file like object that is seekable. + extract_to (str, optional): where to extract the compressed file. + extract_dirs (list, optional): directories inside the archive file to extract. + Defaults to '*'. + """ + compressed_file = StringIO(file_object.read()) + try: + with zipfile.ZipFile(compressed_file) as bundle: + entries = self._filter_entries(bundle.namelist(), extract_dirs) + + for entry in entries: + if verbose: + self.info(' {}'.format(entry)) + bundle.extract(entry, path=extract_to) + + # ZipFile doesn't preserve permissions during extraction: + # http://bugs.python.org/issue15795 + fname = os.path.realpath(os.path.join(extract_to, entry)) + mode = bundle.getinfo(entry).external_attr >> 16 & 0x1FF + # Only set permissions if attributes are available. Otherwise all + # permissions will be removed eg. on Windows. + if mode: + os.chmod(fname, mode) + + except zipfile.BadZipfile as e: + self.exception('{}'.format(e.message)) + + + def deflate(self, file_object, mode, extract_to='.', extract_dirs='*', verbose=False): + """This method allows to extract a tar, tar.bz2 and tar.gz file without writing to disk first. + + Args: + file_object (object): Any file like object that is seekable. + extract_to (str, optional): where to extract the compressed file. + extract_dirs (list, optional): directories inside the archive file to extract. + Defaults to `*`. + verbose (bool, optional): whether or not extracted content should be displayed. + Defaults to False. + """ + compressed_file = StringIO(file_object.read()) + t = tarfile.open(fileobj=compressed_file, mode=mode) + t.extractall(path=extract_to) + + + def download_unpack(self, url, extract_to='.', extract_dirs='*', verbose=False): + """Generic method to download and extract a compressed file without writing it to disk first. Args: url (str): URL where the file to be downloaded is located. - extract_to (str): directory where the downloaded file will - be extracted to. + extract_to (str, optional): directory where the downloaded file will + be extracted to. extract_dirs (list, optional): directories inside the archive to extract. - Defaults to `None`. - error_level (str, optional): log level to use in case an error occurs. - Defaults to `FATAL`. + Defaults to `*`. It currently only applies to zip files. + + Raises: + IOError: on `filename` file not found. """ - dirs = self.query_abs_dirs() - archive = self.download_file(url, parent_dir=dirs['abs_work_dir'], - error_level=error_level) - self.unpack(archive, extract_to, extract_dirs=extract_dirs, - error_level=error_level) + # Many scripts overwrite this method and set extract_dirs to None + extract_dirs = '*' if extract_dirs is None else extract_dirs + EXTENSION_TO_MIMETYPE = { + 'bz2': 'application/x-bzip2', + 'gz': 'application/x-gzip', + 'tar': 'application/x-tar', + 'zip': 'application/zip', + } + MIMETYPES = { + 'application/x-bzip2': { + 'function': self.deflate, + 'kwargs': {'mode': 'r:bz2'}, + }, + 'application/x-gzip': { + 'function': self.deflate, + 'kwargs': {'mode': 'r:gz'}, + }, + 'application/x-tar': { + 'function': self.deflate, + 'kwargs': {'mode': 'r'}, + }, + 'application/zip': { + 'function': self.unzip, + }, + } + + parsed_url = urlparse.urlparse(url) + + # In case we're referrencing a file without file:// + if parsed_url.scheme == '': + if not os.path.isfile(url): + raise IOError('Could not find file to extract: {}'.format(url)) + + url = 'file://%s' % os.path.abspath(url) + parsed_fd = urlparse.urlparse(url) + + request = urllib2.Request(url) + response = urllib2.urlopen(request) + + if parsed_url.scheme == 'file': + filename = url.split('/')[-1] + # XXX: bz2/gz instead of tar.{bz2/gz} + extension = filename[filename.rfind('.')+1:] + mimetype = EXTENSION_TO_MIMETYPE[extension] + else: + mimetype = response.headers.type + + self.debug('Url: {}'.format(url)) + self.debug('Mimetype: {}'.format(mimetype)) + self.debug('Content-Encoding {}'.format(response.headers.get('Content-Encoding'))) + + function = MIMETYPES[mimetype]['function'] + kwargs = { + 'file_object': response, + 'extract_to': extract_to, + 'extract_dirs': extract_dirs, + 'verbose': verbose, + } + kwargs.update(MIMETYPES[mimetype].get('kwargs', {})) + + self.info('Downloading and extracting to {} these dirs {} from {}'.format( + extract_to, + ', '.join(extract_dirs), + url, + )) + retry_args = dict( + failure_status=None, + retry_exceptions=(urllib2.HTTPError, urllib2.URLError, + httplib.BadStatusLine, + socket.timeout, socket.error), + error_message="Can't download from {}".format(url), + error_level=FATAL, + ) + self.retry( + function, + kwargs=kwargs, + **retry_args + ) + def load_json_url(self, url, error_level=None, *args, **kwargs): """ Returns a json object from a url (it retries). """ @@ -1409,12 +1540,6 @@ class ScriptMixin(PlatformMixin): IOError: on `filename` file not found. """ - def _filter_entries(namelist): - """Filter entries of the archive based on the specified list of to extract dirs.""" - filter_partial = functools.partial(fnmatch.filter, namelist) - for entry in itertools.chain(*map(filter_partial, extract_dirs or ['*'])): - yield entry - if not os.path.isfile(filename): raise IOError('Could not find file to extract: %s' % filename) @@ -1422,7 +1547,7 @@ class ScriptMixin(PlatformMixin): try: self.info('Using ZipFile to extract {} to {}'.format(filename, extract_to)) with zipfile.ZipFile(filename) as bundle: - for entry in _filter_entries(bundle.namelist()): + for entry in self._filter_entries(bundle.namelist(), extract_dirs): if verbose: self.info(' %s' % entry) bundle.extract(entry, path=extract_to) @@ -1444,7 +1569,7 @@ class ScriptMixin(PlatformMixin): try: self.info('Using TarFile to extract {} to {}'.format(filename, extract_to)) with tarfile.open(filename) as bundle: - for entry in _filter_entries(bundle.getnames()): + for entry in self._filter_entries(bundle.getnames(), extract_dirs): if verbose: self.info(' %s' % entry) bundle.extract(entry, path=extract_to) diff --git a/testing/mozharness/test/test_base_script.py b/testing/mozharness/test/test_base_script.py index 02bb37a7456f..c069a82f3850 100644 --- a/testing/mozharness/test/test_base_script.py +++ b/testing/mozharness/test/test_base_script.py @@ -259,6 +259,50 @@ class TestScript(unittest.TestCase): self.assertTrue(error_logsize > 0, msg="error list not working properly") + def test_download_unpack(self): + # NOTE: The action is called *download*, however, it can work for files in disk + self.s = get_debug_script_obj() + + archives_path = os.path.join(here, 'helper_files', 'archives') + + # Test basic decompression + for archive in ('archive.tar', 'archive.tar.bz2', 'archive.tar.gz', 'archive.zip'): + self.s.download_unpack( + url=os.path.join(archives_path, archive), + extract_to=self.tmpdir + ) + self.assertIn('script.sh', os.listdir(os.path.join(self.tmpdir, 'bin'))) + self.assertIn('lorem.txt', os.listdir(self.tmpdir)) + shutil.rmtree(self.tmpdir) + + # Test permissions for extracted entries from zip archive + self.s.download_unpack( + url=os.path.join(archives_path, 'archive.zip'), + extract_to=self.tmpdir, + ) + file_stats = os.stat(os.path.join(self.tmpdir, 'bin', 'script.sh')) + orig_fstats = os.stat(os.path.join(archives_path, 'reference', 'bin', 'script.sh')) + self.assertEqual(file_stats.st_mode, orig_fstats.st_mode) + shutil.rmtree(self.tmpdir) + + # Test unzip specific dirs only + self.s.download_unpack( + url=os.path.join(archives_path, 'archive.zip'), + extract_to=self.tmpdir, + extract_dirs=['bin/*'] + ) + self.assertIn('bin', os.listdir(self.tmpdir)) + self.assertNotIn('lorem.txt', os.listdir(self.tmpdir)) + shutil.rmtree(self.tmpdir) + + # Test for invalid filenames (Windows only) + if PYWIN32: + with self.assertRaises(IOError): + self.s.download_unpack( + url=os.path.join(archives_path, 'archive_invalid_filename.zip'), + extract_to=self.tmpdir + ) + def test_unpack(self): self.s = get_debug_script_obj() From 8d4519b8d65ec179814ab3a6d02cab16d8c98235 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Fri, 2 Sep 2016 12:05:00 -0700 Subject: [PATCH 090/102] Backed out changeset ba338ab6c809 for TC bustage (bug 1298947) --- testing/docker/recipes/run-task | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/testing/docker/recipes/run-task b/testing/docker/recipes/run-task index 8d2d6c9018ae..96c5aef0053b 100755 --- a/testing/docker/recipes/run-task +++ b/testing/docker/recipes/run-task @@ -19,16 +19,11 @@ import argparse import datetime import errno import grp -import json import os import pwd import re import subprocess import sys -import urllib2 - - -FINGERPRINT_URL = 'http://taskcluster/secrets/v1/secret/project/taskcluster/gecko/hgfingerprint' def print_line(prefix, m): @@ -93,28 +88,8 @@ def vcs_checkout(args): print('revision is not specified for checkout') sys.exit(1) - # Obtain certificate fingerprints. - try: - print_line(b'vcs', 'fetching hg.mozilla.org fingerprint from %s\n' % - FINGERPRINT_URL) - res = urllib2.urlopen(FINGERPRINT_URL, timeout=10) - secret = res.read() - except urllib2.URLError as e: - print('error retrieving hg fingerprint: %s' % e) - sys.exit(1) - - try: - secret = json.loads(secret, encoding='utf-8') - except ValueError: - print('invalid JSON in hg fingerprint secret') - sys.exit(1) - - hgmo_fingerprint = secret['secret']['fingerprints'].encode('ascii') - res = run_and_prefix_output(b'vcs', [ - b'/usr/bin/hg', - b'--config', b'hostsecurity.hg.mozilla.org:fingerprints=%s' % hgmo_fingerprint, - b'robustcheckout', + b'/usr/bin/hg', b'robustcheckout', b'--sharebase', b'/home/worker/hg-shared', b'--purge', b'--upstream', base_repo, From df92d6815f58714b7dda8a41a5d81d0fbf52a772 Mon Sep 17 00:00:00 2001 From: Kan-Ru Chen Date: Fri, 2 Sep 2016 18:58:59 +0800 Subject: [PATCH 091/102] Bug 1297664 - Null check docShell before use. r=mstange MozReview-Commit-ID: 1jA5K8is11X --HG-- extra : rebase_source : 3320dc3ef2be9c79cebe433fc5d81a9de38d0674 --- layout/base/nsDisplayList.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 990b88453819..267f637959a8 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1009,7 +1009,10 @@ nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame, } nsPresContext* pc = aReferenceFrame->PresContext(); - pc->GetDocShell()->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + nsCOMPtr docShell = pc->GetDocShell(); + if (docShell) { + docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + } mIsInChromePresContext = pc->IsChrome(); } @@ -1025,7 +1028,10 @@ nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame) if (!mPresShellStates.IsEmpty()) { nsPresContext* pc = CurrentPresContext(); - pc->GetDocShell()->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + nsCOMPtr docShell = pc->GetDocShell(); + if (docShell) { + docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + } mIsInChromePresContext = pc->IsChrome(); } } From 7e193ad5d42a59a9591e4d708855f7d58c9ff2fe Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Fri, 2 Sep 2016 16:45:12 -0300 Subject: [PATCH 092/102] Bug 1297753 - Configure list of add-ons for e10s rollout on release 49. r=mconley MozReview-Commit-ID: 6cVebHG2yLo --HG-- extra : rebase_source : fac7f2dd08c675f42f520ddaa6a986b25f38ee9e --- browser/extensions/e10srollout/bootstrap.js | 3 +- .../extensions/internal/E10SAddonsRollout.jsm | 46 ++++++++++++++++--- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/browser/extensions/e10srollout/bootstrap.js b/browser/extensions/e10srollout/bootstrap.js index 6fba551bd067..6720cd6098cb 100644 --- a/browser/extensions/e10srollout/bootstrap.js +++ b/browser/extensions/e10srollout/bootstrap.js @@ -17,7 +17,8 @@ const TEST_THRESHOLD = { }; const ADDON_ROLLOUT_POLICY = { - "beta" : "2a", // Set 2 + any WebExtension + "beta" : "49a", // 10 tested add-ons + any WebExtension + "release" : "49a", // 10 tested add-ons + any WebExtension }; const PREF_COHORT_SAMPLE = "e10s.rollout.cohortSample"; diff --git a/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm b/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm index 49d9bf7aca62..5e9f4f6f0c2d 100644 --- a/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm +++ b/toolkit/mozapps/extensions/internal/E10SAddonsRollout.jsm @@ -46,10 +46,14 @@ const ADDONS = { id: "jid0-GXjLLfbCoAx0LcltEdFrEkQdQPI@jetpack", minVersion: "3.0.10", }, - "PersonasPlus": { + "PersonasPlus": { // PersonasPlus id: "personas@christopher.beard", minVersion: "1.8.0", }, + "ACR": { // Add-on Compatibility Reporter + id: "compatibility@addons.mozilla.org", minVersion: "2.2.0", + }, + // Add-ons used for testing "test1": { id: "bootstrap1@tests.mozilla.org", minVersion: "1.0", @@ -62,10 +66,8 @@ const ADDONS = { // NOTE: Do not modify sets or policies after they have already been // published to users. They must remain unchanged to provide valid data. -const set1 = [ADDONS.Emoji, - ADDONS.ASP, - ADDONS.DYTV]; +// Set 2 used during 48 Beta cycle. Kept here for historical reasons. const set2 = [ADDONS.Greasemonkey, ADDONS.DYTV, ADDONS.VDH, @@ -76,16 +78,46 @@ const set2 = [ADDONS.Greasemonkey, ADDONS.ASP, ADDONS.PersonasPlus]; +const set49Release = [ + ADDONS.Greasemonkey, + ADDONS.DYTV, + ADDONS.VDH, + ADDONS.Lightbeam, + ADDONS.ABP, + ADDONS.uBlockOrigin, + ADDONS.Emoji, + ADDONS.ASP, + ADDONS.PersonasPlus, + ADDONS.ACR +]; + +// These are only the add-ons in the Add-Ons Manager Discovery +// pane. This set is here in case we need to reduce add-ons +// exposure live on Release. +const set49PaneOnly = [ + ADDONS.ABP, + ADDONS.VDH, + ADDONS.Emoji, + ADDONS.ASP, + ADDONS.ACR +] + // We use these named policies to correlate the telemetry // data with them, in order to understand how each set // is behaving in the wild. const RolloutPolicy = { - "1a": { addons: set1, webextensions: true }, + // Used during 48 Beta cycle "2a": { addons: set2, webextensions: true }, - - "1b": { addons: set1, webextensions: false }, "2b": { addons: set2, webextensions: false }, + // Set agreed for Release 49 + "49a": { addons: set49Release, webextensions: true }, + "49b": { addons: set49Release, webextensions: false }, + + // Smaller set that can be used for Release 49 + "49limiteda": { addons: set49PaneOnly, webextensions: true }, + "49limitedb": { addons: set49PaneOnly, webextensions: false }, + "xpcshell-test": { addons: [ADDONS.test1, ADDONS.test2], webextensions: false }, }; From d0d9263c8169ea25ac4104e849b7482bdf87a317 Mon Sep 17 00:00:00 2001 From: Steve Chung Date: Fri, 2 Sep 2016 11:53:37 +0800 Subject: [PATCH 093/102] Bug 1288557 - Part 0: Fix permissions dialog while receiving deleted event. r=MattN MozReview-Commit-ID: CPINRBFYW00 --HG-- extra : rebase_source : 87fa439ee7f2e6c77a4fe5f6e96a4fa28502f916 --- browser/components/preferences/permissions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/preferences/permissions.js b/browser/components/preferences/permissions.js index 9b3b17401917..145c8a913ea5 100644 --- a/browser/components/preferences/permissions.js +++ b/browser/components/preferences/permissions.js @@ -306,7 +306,7 @@ var gPermissionManager = { this._handleCapabilityChange(); } else if (aData == "deleted") { - this._removePermissionFromList(permission); + this._removePermissionFromList(permission.principal); } } }, From 1ea8e5606cf6d83d50b4d406c677f7dce1a28f87 Mon Sep 17 00:00:00 2001 From: Steve Chung Date: Tue, 30 Aug 2016 16:53:03 +0800 Subject: [PATCH 094/102] Bug 1288557 - Part 1: Replace custom exceptions dialog with usage of permissions. r=MattN MozReview-Commit-ID: 25fD9IJIVt0 --HG-- extra : rebase_source : 883976c1311db15d96bd6100e07f0ea09f576df5 --- .../preferences/in-content/security.js | 15 ++- .../components/preferences/permissions.xul | 8 +- .../preferences/preferences.properties | 4 +- .../content/passwordManagerCommon.js | 15 --- .../content/passwordManagerExceptions.js | 115 ------------------ .../content/passwordManagerExceptions.xul | 49 -------- toolkit/components/passwordmgr/jar.mn | 2 - .../passwordmgr/test/browser/browser.ini | 1 + .../test/browser/browser_exceptions_dialog.js | 56 +++++++++ .../browser/browser_passwordmgr_observers.js | 31 +---- .../chrome/passwordmgr/passwordManager.dtd | 2 - 11 files changed, 81 insertions(+), 217 deletions(-) delete mode 100644 toolkit/components/passwordmgr/content/passwordManagerExceptions.js delete mode 100644 toolkit/components/passwordmgr/content/passwordManagerExceptions.xul create mode 100644 toolkit/components/passwordmgr/test/browser/browser_exceptions_dialog.js diff --git a/browser/components/preferences/in-content/security.js b/browser/components/preferences/in-content/security.js index 63f51e43dc75..a8ad28c7ead4 100644 --- a/browser/components/preferences/in-content/security.js +++ b/browser/components/preferences/in-content/security.js @@ -127,7 +127,20 @@ var gSecurityPane = { */ showPasswordExceptions: function () { - gSubDialog.open("chrome://passwordmgr/content/passwordManagerExceptions.xul"); + var bundlePrefs = document.getElementById("bundlePreferences"); + var params = { + blockVisible: true, + sessionVisible: false, + allowVisible: false, + hideStatusColumn: true, + prefilledHost: "", + permissionType: "login-saving", + windowTitle: bundlePrefs.getString("savedLoginsExceptions_title"), + introText: bundlePrefs.getString("savedLoginsExceptions_desc") + }; + + gSubDialog.open("chrome://browser/content/preferences/permissions.xul", + null, params); }, /** diff --git a/browser/components/preferences/permissions.xul b/browser/components/preferences/permissions.xul index 63bed170bbd7..7a7040864a87 100644 --- a/browser/components/preferences/permissions.xul +++ b/browser/components/preferences/permissions.xul @@ -4,8 +4,8 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - + + @@ -34,7 +34,7 @@