mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Merge m-c to fx-team, a=merge
This commit is contained in:
commit
7d3cddeb71
@ -204,9 +204,6 @@ mobile/android/chrome/content/about.js
|
||||
mobile/android/installer/
|
||||
mobile/android/locales/
|
||||
|
||||
# Pretty sure we're disabling this one anyway
|
||||
mobile/android/modules/ContactService.jsm
|
||||
|
||||
# Non-standard `(catch ex if ...)`
|
||||
mobile/android/components/Snippets.js
|
||||
|
||||
|
@ -129,6 +129,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInvalidationList)
|
||||
for (auto it = tmp->mARIAOwnsHash.ConstIter(); !it.Done(); it.Next()) {
|
||||
nsTArray<RefPtr<Accessible> >* ar = it.UserData();
|
||||
for (uint32_t i = 0; i < ar->Length(); i++) {
|
||||
@ -147,6 +148,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
|
||||
tmp->mNodeToAccessibleMap.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInvalidationList)
|
||||
tmp->mARIAOwnsHash.Clear();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
|
@ -683,7 +683,7 @@ protected:
|
||||
*
|
||||
* @see ProcessInvalidationList
|
||||
*/
|
||||
nsTArray<nsIContent*> mInvalidationList;
|
||||
nsTArray<RefPtr<nsIContent>> mInvalidationList;
|
||||
|
||||
/**
|
||||
* Holds a list of aria-owns relocations.
|
||||
|
@ -1488,6 +1488,7 @@ pref("browser.migrate.automigrate.enabled", false);
|
||||
// 4 here means the suggestion notification will be automatically
|
||||
// hidden the 4th day, so it will actually be shown on 3 different days.
|
||||
pref("browser.migrate.automigrate.daysToOfferUndo", 4);
|
||||
pref("browser.migrate.automigrate.ui.enabled", true);
|
||||
|
||||
// Enable browser frames for use on desktop. Only exposed to chrome callers.
|
||||
pref("dom.mozBrowserFramesEnabled", true);
|
||||
|
@ -4,8 +4,8 @@ function test()
|
||||
|
||||
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
let ChromeUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
let EventUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
// ---- Test dragging the proxy icon ---
|
||||
var value = content.location.href;
|
||||
@ -41,5 +41,5 @@ function test()
|
||||
finish();
|
||||
}, true);
|
||||
|
||||
ChromeUtils.synthesizeDrop(tab, tab, [[{type: "text/uri-list", data: "http://mochi.test:8888/"}]], "copy", window);
|
||||
EventUtils.synthesizeDrop(tab, tab, [[{type: "text/uri-list", data: "http://mochi.test:8888/"}]], "copy", window);
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ function test() {
|
||||
|
||||
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
let ChromeUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
let EventUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
let homeButton = document.getElementById("home-button");
|
||||
ok(homeButton, "home button present");
|
||||
@ -53,14 +53,14 @@ function test() {
|
||||
// The drop handler throws an exception when dragging URIs that inherit
|
||||
// principal, e.g. javascript:
|
||||
expectUncaughtException();
|
||||
ChromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window);
|
||||
EventUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "javascript:8888"}]], "copy", window);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
Services.wm.addListener(dialogListener);
|
||||
|
||||
ChromeUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "http://mochi.test:8888/"}]], "copy", window);
|
||||
EventUtils.synthesizeDrop(homeButton, homeButton, [[{type: "text/plain", data: "http://mochi.test:8888/"}]], "copy", window);
|
||||
}
|
||||
|
||||
function WindowListener(aURL, aCallback) {
|
||||
|
@ -29,8 +29,8 @@ function* drop(text, valid = false) {
|
||||
info(`Starting test for text:${text}; valid:${valid}`);
|
||||
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
let ChromeUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
let EventUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
|
||||
let awaitTabOpen = valid && BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen");
|
||||
@ -46,7 +46,7 @@ function* drop(text, valid = false) {
|
||||
screenX: 0,
|
||||
screenY: 0,
|
||||
};
|
||||
ChromeUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, [[{type: "text/plain", data: text}]], "link", window, undefined, event);
|
||||
EventUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, [[{type: "text/plain", data: text}]], "link", window, undefined, event);
|
||||
let tabOpened = false;
|
||||
if (awaitTabOpen) {
|
||||
let tabOpenEvent = yield awaitTabOpen;
|
||||
|
@ -20,8 +20,8 @@ function test() {
|
||||
|
||||
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
let ChromeUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
let EventUtils = {};
|
||||
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
function dragAndDrop(tab1, tab2, copy) {
|
||||
let rect = tab2.getBoundingClientRect();
|
||||
@ -32,7 +32,7 @@ function test() {
|
||||
clientY: rect.top + rect.height / 2,
|
||||
};
|
||||
|
||||
ChromeUtils.synthesizeDrop(tab1, tab2, null, copy ? "copy" : "move", window, window, event);
|
||||
EventUtils.synthesizeDrop(tab1, tab2, null, copy ? "copy" : "move", window, window, event);
|
||||
}
|
||||
|
||||
dragAndDrop(newTab1, newTab2, false);
|
||||
|
@ -4,10 +4,10 @@
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const CHROMEUTILS_URL = "chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js";
|
||||
var ChromeUtils = {};
|
||||
const EVENTUTILS_URL = "chrome://mochikit/content/tests/SimpleTest/EventUtils.js";
|
||||
var EventUtils = {};
|
||||
|
||||
Services.scriptloader.loadSubScript(CHROMEUTILS_URL, ChromeUtils);
|
||||
Services.scriptloader.loadSubScript(EVENTUTILS_URL, EventUtils);
|
||||
|
||||
/**
|
||||
* Tests that tabs from Private Browsing windows cannot be dragged
|
||||
@ -23,12 +23,12 @@ add_task(function* test_dragging_private_windows() {
|
||||
let privateTab =
|
||||
yield BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser);
|
||||
|
||||
let effect = ChromeUtils.synthesizeDrop(normalTab, privateTab,
|
||||
let effect = EventUtils.synthesizeDrop(normalTab, privateTab,
|
||||
[[{type: TAB_DROP_TYPE, data: normalTab}]],
|
||||
null, normalWin, privateWin);
|
||||
is(effect, "none", "Should not be able to drag a normal tab to a private window");
|
||||
|
||||
effect = ChromeUtils.synthesizeDrop(privateTab, normalTab,
|
||||
effect = EventUtils.synthesizeDrop(privateTab, normalTab,
|
||||
[[{type: TAB_DROP_TYPE, data: privateTab}]],
|
||||
null, privateWin, normalWin);
|
||||
is(effect, "none", "Should not be able to drag a private tab to a normal window");
|
||||
@ -66,12 +66,12 @@ add_task(function* test_dragging_e10s_windows() {
|
||||
let nonRemoteTab =
|
||||
yield BrowserTestUtils.openNewForegroundTab(nonRemoteWin.gBrowser);
|
||||
|
||||
let effect = ChromeUtils.synthesizeDrop(remoteTab, nonRemoteTab,
|
||||
let effect = EventUtils.synthesizeDrop(remoteTab, nonRemoteTab,
|
||||
[[{type: TAB_DROP_TYPE, data: remoteTab}]],
|
||||
null, remoteWin, nonRemoteWin);
|
||||
is(effect, "none", "Should not be able to drag a remote tab to a non-e10s window");
|
||||
|
||||
effect = ChromeUtils.synthesizeDrop(nonRemoteTab, remoteTab,
|
||||
effect = EventUtils.synthesizeDrop(nonRemoteTab, remoteTab,
|
||||
[[{type: TAB_DROP_TYPE, data: nonRemoteTab}]],
|
||||
null, nonRemoteWin, remoteWin);
|
||||
is(effect, "none", "Should not be able to drag a non-remote tab to an e10s window");
|
||||
@ -122,7 +122,7 @@ add_task(function* test_dragging_blacklisted() {
|
||||
let otherTab =
|
||||
yield BrowserTestUtils.openNewForegroundTab(remoteWin2.gBrowser);
|
||||
|
||||
let effect = ChromeUtils.synthesizeDrop(blacklistedTab, otherTab,
|
||||
let effect = EventUtils.synthesizeDrop(blacklistedTab, otherTab,
|
||||
[[{type: TAB_DROP_TYPE, data: blacklistedTab}]],
|
||||
null, remoteWin1, remoteWin2);
|
||||
is(effect, "move", "Should be able to drag the blacklisted tab.");
|
||||
@ -163,7 +163,7 @@ add_task(function* test_dragging_adoption_events() {
|
||||
let awaitCloseEvent = BrowserTestUtils.waitForEvent(tab1, "TabClose");
|
||||
let awaitOpenEvent = BrowserTestUtils.waitForEvent(win2, "TabOpen");
|
||||
|
||||
let effect = ChromeUtils.synthesizeDrop(tab1, tab2,
|
||||
let effect = EventUtils.synthesizeDrop(tab1, tab2,
|
||||
[[{type: TAB_DROP_TYPE, data: tab1}]],
|
||||
null, win1, win2);
|
||||
is(effect, "move", "Tab should be moved from win1 to win2.");
|
||||
|
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
let ChromeUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
let EventUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
/**
|
||||
* Dragging an URL to a tab without userContextId set.
|
||||
@ -25,7 +25,7 @@ add_task(function* () {
|
||||
screenX: 0,
|
||||
screenY: 0,
|
||||
};
|
||||
ChromeUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
|
||||
EventUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
|
||||
|
||||
yield awaitDrop;
|
||||
|
||||
@ -72,7 +72,7 @@ add_task(function* () {
|
||||
screenX: 0,
|
||||
screenY: 0,
|
||||
};
|
||||
ChromeUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
|
||||
EventUtils.synthesizeDrop(tab, tab, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window, undefined, event);
|
||||
|
||||
yield awaitDrop;
|
||||
|
||||
@ -111,7 +111,7 @@ add_task(function* () {
|
||||
|
||||
let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
|
||||
|
||||
ChromeUtils.synthesizeDrop(tab, tab2, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window);
|
||||
EventUtils.synthesizeDrop(tab, tab2, [[{type: "text/plain", data: "http://test1.example.com/"}]], "link", window);
|
||||
|
||||
yield awaitDrop;
|
||||
Assert.equal(tab2.getAttribute("usercontextid"), 2);
|
||||
|
@ -9,8 +9,8 @@ function simulateItemDragAndEnd(aToDrag, aTarget) {
|
||||
|
||||
ds.startDragSession();
|
||||
try {
|
||||
var [result, dataTransfer] = ChromeUtils.synthesizeDragOver(aToDrag.parentNode, aTarget);
|
||||
ChromeUtils.synthesizeDropAfterDragOver(result, dataTransfer, aTarget);
|
||||
var [result, dataTransfer] = EventUtils.synthesizeDragOver(aToDrag.parentNode, aTarget);
|
||||
EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, aTarget);
|
||||
// Send dragend to move dragging item back to initial place.
|
||||
EventUtils.sendDragEvent({ type: "dragend", dataTransfer: dataTransfer },
|
||||
aToDrag.parentNode);
|
||||
|
@ -31,12 +31,12 @@ add_task(function*() {
|
||||
|
||||
ds.startDragSession();
|
||||
try {
|
||||
var [result, dataTransfer] = ChromeUtils.synthesizeDragOver(identityBox, overflowChevron);
|
||||
var [result, dataTransfer] = EventUtils.synthesizeDragOver(identityBox, overflowChevron);
|
||||
|
||||
// Wait for showing panel before ending drag session.
|
||||
yield panelShownPromise;
|
||||
|
||||
ChromeUtils.synthesizeDropAfterDragOver(result, dataTransfer, overflowChevron);
|
||||
EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, overflowChevron);
|
||||
} finally {
|
||||
ds.endDragSession(true);
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ Cu.import("resource:///modules/CustomizableUI.jsm", tmp);
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm", tmp);
|
||||
var {Promise, CustomizableUI, AppConstants} = tmp;
|
||||
|
||||
var ChromeUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
var EventUtils = {};
|
||||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
Services.prefs.setBoolPref("browser.uiCustomization.skipSourceNodeCheck", true);
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomization.skipSourceNodeCheck"));
|
||||
@ -22,7 +22,7 @@ registerCleanupFunction(() => Services.prefs.clearUserPref("browser.uiCustomizat
|
||||
CustomizableUI.destroyWidget("e10s-button");
|
||||
CustomizableUI.removeWidgetFromArea("e10s-button");
|
||||
|
||||
var {synthesizeDragStart, synthesizeDrop} = ChromeUtils;
|
||||
var {synthesizeDragStart, synthesizeDrop} = EventUtils;
|
||||
|
||||
const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const kTabEventFailureTimeoutInMs = 20000;
|
||||
|
@ -77,7 +77,7 @@ function convert(result) {
|
||||
return node;
|
||||
}
|
||||
|
||||
extensions.registerSchemaAPI("bookmarks", context => {
|
||||
extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
|
||||
return {
|
||||
bookmarks: {
|
||||
get: function(idOrIdList) {
|
||||
|
@ -365,7 +365,7 @@ extensions.on("shutdown", (type, extension) => {
|
||||
});
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
extensions.registerSchemaAPI("browserAction", context => {
|
||||
extensions.registerSchemaAPI("browserAction", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
browserAction: {
|
||||
|
@ -228,7 +228,7 @@ extensions.on("shutdown", (type, extension) => {
|
||||
});
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
extensions.registerSchemaAPI("commands", context => {
|
||||
extensions.registerSchemaAPI("commands", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
commands: {
|
||||
|
@ -485,7 +485,7 @@ extensions.on("shutdown", (type, extension) => {
|
||||
});
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
|
||||
extensions.registerSchemaAPI("contextMenus", context => {
|
||||
extensions.registerSchemaAPI("contextMenus", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
contextMenus: {
|
||||
|
@ -130,7 +130,7 @@ function getObserver() {
|
||||
return _observer;
|
||||
}
|
||||
|
||||
extensions.registerSchemaAPI("history", context => {
|
||||
extensions.registerSchemaAPI("history", "addon_parent", context => {
|
||||
return {
|
||||
history: {
|
||||
addUrl: function(details) {
|
||||
|
@ -217,7 +217,7 @@ PageAction.for = extension => {
|
||||
|
||||
global.pageActionFor = PageAction.for;
|
||||
|
||||
extensions.registerSchemaAPI("pageAction", context => {
|
||||
extensions.registerSchemaAPI("pageAction", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
pageAction: {
|
||||
|
@ -264,7 +264,7 @@ let tabListener = {
|
||||
},
|
||||
};
|
||||
|
||||
extensions.registerSchemaAPI("tabs", context => {
|
||||
extensions.registerSchemaAPI("tabs", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
let self = {
|
||||
tabs: {
|
||||
|
@ -15,7 +15,7 @@ var {
|
||||
EventManager,
|
||||
} = ExtensionUtils;
|
||||
|
||||
extensions.registerSchemaAPI("windows", context => {
|
||||
extensions.registerSchemaAPI("windows", "addon_parent", context => {
|
||||
let {extension} = context;
|
||||
return {
|
||||
windows: {
|
||||
|
@ -210,7 +210,7 @@ add_task(function* testBrowserActionClickCanceled() {
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
const {GlobalManager, browserActionFor} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
const {GlobalManager, Management: {global: {browserActionFor}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let ext = GlobalManager.extensionMap.get(extension.id);
|
||||
let browserAction = browserActionFor(ext);
|
||||
|
@ -90,7 +90,7 @@ add_task(function* () {
|
||||
|
||||
yield Promise.all([extension.startup(), extension.awaitMessage("background-ready")]);
|
||||
|
||||
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {WindowManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let winId1 = WindowManager.getId(win1);
|
||||
let winId2 = WindowManager.getId(win2);
|
||||
|
@ -104,7 +104,7 @@ add_task(function* () {
|
||||
|
||||
info("started");
|
||||
|
||||
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {WindowManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let winId1 = WindowManager.getId(win1);
|
||||
let winId2 = WindowManager.getId(win2);
|
||||
|
@ -176,7 +176,7 @@ add_task(function* () {
|
||||
});
|
||||
|
||||
extension.onMessage("change-tab", (tabId, attr, on) => {
|
||||
let {TabManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {TabManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let tab = TabManager.getTab(tabId);
|
||||
|
||||
|
@ -98,7 +98,7 @@ add_task(function* testDuplicateTabLazily() {
|
||||
});
|
||||
|
||||
extension.onMessage("duplicate-tab", tabId => {
|
||||
let {TabManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {TabManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let tab = TabManager.getTab(tabId);
|
||||
// This is a bit of a hack to load a tab in the background.
|
||||
|
@ -201,7 +201,7 @@ add_task(function* () {
|
||||
});
|
||||
|
||||
extension.onMessage("msg", (id, msg, ...args) => {
|
||||
let {TabManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {TabManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let resp;
|
||||
if (msg == "get-zoom") {
|
||||
|
@ -53,7 +53,7 @@ add_task(function* testWindowsEvents() {
|
||||
yield extension.startup();
|
||||
yield extension.awaitMessage("ready");
|
||||
|
||||
let {WindowManager} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
let {Management: {global: {WindowManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
|
||||
|
||||
let currentWindow = window;
|
||||
let currentWindowId = WindowManager.getId(currentWindow);
|
||||
|
@ -9,6 +9,7 @@ this.EXPORTED_SYMBOLS = ["AutoMigrate"];
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
const kAutoMigrateEnabledPref = "browser.migrate.automigrate.enabled";
|
||||
const kUndoUIEnabledPref = "browser.migrate.automigrate.ui.enabled";
|
||||
|
||||
const kAutoMigrateStartedPref = "browser.migrate.automigrate.started";
|
||||
const kAutoMigrateFinishedPref = "browser.migrate.automigrate.finished";
|
||||
@ -286,7 +287,8 @@ const AutoMigrate = {
|
||||
|
||||
maybeShowUndoNotification(target) {
|
||||
// The tab might have navigated since we requested the undo state:
|
||||
if (!this.canUndo() || target.currentURI.spec != "about:home") {
|
||||
if (!this.canUndo() || target.currentURI.spec != "about:home" ||
|
||||
!Preferences.get(kUndoUIEnabledPref, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2390,7 +2390,13 @@ BrowserGlue.prototype = {
|
||||
win.gBrowser.selectedTab = firstTab;
|
||||
}
|
||||
}
|
||||
AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
|
||||
|
||||
// Specify an icon because on Windows no icon is shown at the moment
|
||||
let imageURL;
|
||||
if (AppConstants.platform == "win") {
|
||||
imageURL = "chrome://branding/content/icon64.png";
|
||||
}
|
||||
AlertsService.showAlertNotification(imageURL, title, body, true, null, clickCallback);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error displaying tab(s) received by Sync: " + ex);
|
||||
}
|
||||
|
@ -884,8 +884,7 @@ this.PlacesUIUtils = {
|
||||
/**
|
||||
* Gives the user a chance to cancel loading lots of tabs at once
|
||||
*/
|
||||
_confirmOpenInTabs:
|
||||
function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
|
||||
confirmOpenInTabs(numTabsToOpen, aWindow) {
|
||||
const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
|
||||
var reallyOpen = true;
|
||||
|
||||
@ -988,7 +987,7 @@ this.PlacesUIUtils = {
|
||||
urlsToOpen.push({uri: node.uri, isBookmark: false});
|
||||
}
|
||||
|
||||
if (this._confirmOpenInTabs(urlsToOpen.length, window)) {
|
||||
if (this.confirmOpenInTabs(urlsToOpen.length, window)) {
|
||||
this._openTabset(urlsToOpen, aEvent, window);
|
||||
}
|
||||
}, Cu.reportError);
|
||||
@ -999,7 +998,7 @@ this.PlacesUIUtils = {
|
||||
let window = aView.ownerWindow;
|
||||
|
||||
let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
|
||||
if (this._confirmOpenInTabs(urlsToOpen.length, window)) {
|
||||
if (this.confirmOpenInTabs(urlsToOpen.length, window)) {
|
||||
this._openTabset(urlsToOpen, aEvent, window);
|
||||
}
|
||||
},
|
||||
|
@ -1,11 +1,11 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
// Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
|
||||
var ChromeUtils = {};
|
||||
// Instead of loading EventUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need EventUtils.js for a few files which is why we are using loadSubScript.
|
||||
var EventUtils = {};
|
||||
this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
add_task(function* test() {
|
||||
// Make sure the bookmarks bar is visible and restore its state on cleanup.
|
||||
@ -37,7 +37,7 @@ add_task(function* test() {
|
||||
let simulateDragDrop = function(aEffect, aMimeType) {
|
||||
const uriSpec = "http://www.mozilla.org/D1995729-A152-4e30-8329-469B01F30AA7";
|
||||
let uri = makeURI(uriSpec);
|
||||
ChromeUtils.synthesizeDrop(placesItems.childNodes[0],
|
||||
EventUtils.synthesizeDrop(placesItems.childNodes[0],
|
||||
placesItems,
|
||||
[[{type: aMimeType,
|
||||
data: uriSpec}]],
|
||||
|
@ -1,12 +1,12 @@
|
||||
// Tests that the suggestion popup appears at the right times in response to
|
||||
// focus and user events (mouse, keyboard, drop).
|
||||
|
||||
// Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
|
||||
var ChromeUtils = {};
|
||||
// Instead of loading EventUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need EventUtils.js for a few files which is why we are using loadSubScript.
|
||||
var EventUtils = {};
|
||||
this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
|
||||
|
||||
const searchbar = document.getElementById("searchbar");
|
||||
const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid", "searchbar-search-button");
|
||||
@ -447,7 +447,7 @@ add_task(function* dont_consume_clicks() {
|
||||
// Dropping text to the searchbar should open the popup
|
||||
add_task(function* drop_opens_popup() {
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
ChromeUtils.synthesizeDrop(searchIcon, textbox.inputField, [[ {type: "text/plain", data: "foo" } ]], "move", window);
|
||||
EventUtils.synthesizeDrop(searchIcon, textbox.inputField, [[ {type: "text/plain", data: "foo" } ]], "move", window);
|
||||
yield promise;
|
||||
|
||||
isnot(searchPopup.getAttribute("showonlysettings"), "true", "Should show the full popup");
|
||||
|
@ -13,6 +13,8 @@ let log = Cu.import("resource://gre/modules/Log.jsm", {})
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
||||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
|
||||
"resource:///modules/PlacesUIUtils.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"TabListComponent"
|
||||
@ -47,6 +49,7 @@ TabListComponent.prototype = {
|
||||
this._view = new this._View(this._window, {
|
||||
onSelectRow: (...args) => this.onSelectRow(...args),
|
||||
onOpenTab: (...args) => this.onOpenTab(...args),
|
||||
onOpenTabs: (...args) => this.onOpenTabs(...args),
|
||||
onMoveSelectionDown: (...args) => this.onMoveSelectionDown(...args),
|
||||
onMoveSelectionUp: (...args) => this.onMoveSelectionUp(...args),
|
||||
onToggleBranch: (...args) => this.onToggleBranch(...args),
|
||||
@ -113,6 +116,21 @@ TabListComponent.prototype = {
|
||||
BrowserUITelemetry.countSyncedTabEvent("open", "sidebar");
|
||||
},
|
||||
|
||||
onOpenTabs(urls, where, params) {
|
||||
if (!PlacesUIUtils.confirmOpenInTabs(urls.length, this._window)) {
|
||||
return;
|
||||
}
|
||||
if (where == "window") {
|
||||
this._window.openDialog(this._window.getBrowserURL(), "_blank",
|
||||
"chrome,dialog=no,all", urls.join("|"));
|
||||
} else {
|
||||
for (let url of urls) {
|
||||
this._window.openUILinkIn(url, where, params);
|
||||
}
|
||||
}
|
||||
BrowserUITelemetry.countSyncedTabEvent("openmultiple", "sidebar");
|
||||
},
|
||||
|
||||
onCopyTabLocation(url) {
|
||||
this._clipboardHelper.copyString(url);
|
||||
},
|
||||
|
@ -185,6 +185,7 @@ TabListView.prototype = {
|
||||
// These listeners have to be re-created every time since we re-create the list
|
||||
_attachListListeners() {
|
||||
this.list.addEventListener("click", this.onClick.bind(this));
|
||||
this.list.addEventListener("mouseup", this.onMouseUp.bind(this));
|
||||
this.list.addEventListener("keydown", this.onKeyDown.bind(this));
|
||||
},
|
||||
|
||||
@ -263,6 +264,12 @@ TabListView.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
onMouseUp(event) {
|
||||
if (event.which == 2) { // Middle click
|
||||
this.onClick(event);
|
||||
}
|
||||
},
|
||||
|
||||
onClick(event) {
|
||||
let itemNode = this._findParentItemNode(event.target);
|
||||
if (!itemNode) {
|
||||
@ -276,7 +283,18 @@ TabListView.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.target.classList.contains("item-twisty-container")) {
|
||||
// Middle click on a client
|
||||
if (itemNode.classList.contains("client")) {
|
||||
let where = getChromeWindow(this._window).whereToOpenLink(event);
|
||||
if (where != "current") {
|
||||
const tabs = itemNode.querySelector(".item-tabs-list").childNodes;
|
||||
const urls = [...tabs].map(tab => tab.dataset.url);
|
||||
this.props.onOpenTabs(urls, where, {});
|
||||
}
|
||||
}
|
||||
|
||||
if (event.target.classList.contains("item-twisty-container")
|
||||
&& event.which != 2) {
|
||||
this.props.onToggleBranch(itemNode.dataset.id);
|
||||
return;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ let { View } = Cu.import("resource:///modules/syncedtabs/TabListView.js", {});
|
||||
const ACTION_METHODS = [
|
||||
"onSelectRow",
|
||||
"onOpenTab",
|
||||
"onOpenTabs",
|
||||
"onMoveSelectionDown",
|
||||
"onMoveSelectionUp",
|
||||
"onToggleBranch",
|
||||
@ -77,6 +78,8 @@ add_task(function* testActions() {
|
||||
},
|
||||
PlacesUtils: { bookmarksMenuFolderId: "id" }
|
||||
},
|
||||
getBrowserURL() {},
|
||||
openDialog() {},
|
||||
openUILinkIn() {}
|
||||
};
|
||||
let component = new TabListComponent({
|
||||
@ -124,6 +127,13 @@ add_task(function* testActions() {
|
||||
component.onOpenTab("uri", "where", "params");
|
||||
Assert.ok(windowMock.openUILinkIn.calledWith("uri", "where", "params"));
|
||||
|
||||
component.onOpenTabs(["uri1", "uri2"], "where", "params");
|
||||
Assert.ok(windowMock.openUILinkIn.calledWith("uri1", "where", "params"));
|
||||
Assert.ok(windowMock.openUILinkIn.calledWith("uri2", "where", "params"));
|
||||
sinon.spy(windowMock, "openDialog");
|
||||
component.onOpenTabs(["uri1", "uri2"], "window", "params");
|
||||
Assert.deepEqual(windowMock.openDialog.args[0][3], ["uri1", "uri2"].join("|"));
|
||||
|
||||
sinon.spy(clipboardHelperMock, "copyString");
|
||||
component.onCopyTabLocation("uri");
|
||||
Assert.ok(clipboardHelperMock.copyString.calledWith("uri"));
|
||||
|
@ -53,15 +53,13 @@ function test() {
|
||||
yield waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
|
||||
let variables = gDebugger.DebuggerView.Variables;
|
||||
|
||||
is(variables._store.length, 4, "Correct number of scopes available");
|
||||
is(variables._store.length, 3, "Correct number of scopes available");
|
||||
is(variables.getScopeAtIndex(0).name, "Function scope [interval<]",
|
||||
"Paused with correct scope (0)");
|
||||
is(variables.getScopeAtIndex(1).name, "Block scope",
|
||||
"Paused with correct scope (1)");
|
||||
is(variables.getScopeAtIndex(2).name, "Block scope",
|
||||
is(variables.getScopeAtIndex(2).name, "Global scope [Window]",
|
||||
"Paused with correct scope (2)");
|
||||
is(variables.getScopeAtIndex(3).name, "Global scope [Window]",
|
||||
"Paused with correct scope (3)");
|
||||
|
||||
yield evalInTab(gTab, "clearInterval(interval)");
|
||||
let onceResumed = gTarget.once("thread-resumed");
|
||||
|
@ -358,8 +358,8 @@ ArrowScrollBox.prototype = {
|
||||
function HTMLBreadcrumbs(inspector) {
|
||||
this.inspector = inspector;
|
||||
this.selection = this.inspector.selection;
|
||||
this.chromeWin = this.inspector.panelWin;
|
||||
this.chromeDoc = this.inspector.panelDoc;
|
||||
this.win = this.inspector.panelWin;
|
||||
this.doc = this.inspector.panelDoc;
|
||||
this._init();
|
||||
}
|
||||
|
||||
@ -371,9 +371,9 @@ HTMLBreadcrumbs.prototype = {
|
||||
},
|
||||
|
||||
_init: function () {
|
||||
this.outer = this.chromeDoc.getElementById("inspector-breadcrumbs");
|
||||
this.outer = this.doc.getElementById("inspector-breadcrumbs");
|
||||
this.arrowScrollBox = new ArrowScrollBox(
|
||||
this.chromeWin,
|
||||
this.win,
|
||||
this.outer);
|
||||
|
||||
this.container = this.arrowScrollBox.inner;
|
||||
@ -382,7 +382,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
|
||||
// These separators are used for CSS purposes only, and are positioned
|
||||
// off screen, but displayed with -moz-element.
|
||||
this.separators = this.chromeDoc.createElementNS(NS_XHTML, "div");
|
||||
this.separators = this.doc.createElementNS(NS_XHTML, "div");
|
||||
this.separators.className = "breadcrumb-separator-container";
|
||||
this.separators.innerHTML =
|
||||
"<div id='breadcrumb-separator-before'></div>" +
|
||||
@ -395,7 +395,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
this.outer.addEventListener("mouseout", this, true);
|
||||
this.outer.addEventListener("focus", this, true);
|
||||
|
||||
this.shortcuts = new KeyShortcuts({ window: this.chromeWin, target: this.outer });
|
||||
this.shortcuts = new KeyShortcuts({ window: this.win, target: this.outer });
|
||||
this.handleShortcut = this.handleShortcut.bind(this);
|
||||
|
||||
this.shortcuts.on("Right", this.handleShortcut);
|
||||
@ -458,16 +458,16 @@ HTMLBreadcrumbs.prototype = {
|
||||
* @returns {DocumentFragment}
|
||||
*/
|
||||
prettyPrintNodeAsXHTML: function (node) {
|
||||
let tagLabel = this.chromeDoc.createElementNS(NS_XHTML, "span");
|
||||
let tagLabel = this.doc.createElementNS(NS_XHTML, "span");
|
||||
tagLabel.className = "breadcrumbs-widget-item-tag plain";
|
||||
|
||||
let idLabel = this.chromeDoc.createElementNS(NS_XHTML, "span");
|
||||
let idLabel = this.doc.createElementNS(NS_XHTML, "span");
|
||||
idLabel.className = "breadcrumbs-widget-item-id plain";
|
||||
|
||||
let classesLabel = this.chromeDoc.createElementNS(NS_XHTML, "span");
|
||||
let classesLabel = this.doc.createElementNS(NS_XHTML, "span");
|
||||
classesLabel.className = "breadcrumbs-widget-item-classes plain";
|
||||
|
||||
let pseudosLabel = this.chromeDoc.createElementNS(NS_XHTML, "span");
|
||||
let pseudosLabel = this.doc.createElementNS(NS_XHTML, "span");
|
||||
pseudosLabel.className = "breadcrumbs-widget-item-pseudo-classes plain";
|
||||
|
||||
let tagText = node.displayName;
|
||||
@ -506,7 +506,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
classesLabel.textContent = classesText;
|
||||
pseudosLabel.textContent = node.pseudoClassLocks.join("");
|
||||
|
||||
let fragment = this.chromeDoc.createDocumentFragment();
|
||||
let fragment = this.doc.createDocumentFragment();
|
||||
fragment.appendChild(tagLabel);
|
||||
fragment.appendChild(idLabel);
|
||||
fragment.appendChild(classesLabel);
|
||||
@ -661,9 +661,6 @@ HTMLBreadcrumbs.prototype = {
|
||||
}
|
||||
if (index > -1) {
|
||||
this.nodeHierarchy[index].button.setAttribute("checked", "true");
|
||||
if (this.hadFocus) {
|
||||
this.nodeHierarchy[index].button.focus();
|
||||
}
|
||||
} else {
|
||||
// Unset active active descendant when all buttons are unselected.
|
||||
this.outer.removeAttribute("aria-activedescendant");
|
||||
@ -703,7 +700,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
* @return {DOMNode} The <button> for this node.
|
||||
*/
|
||||
buildButton: function (node) {
|
||||
let button = this.chromeDoc.createElementNS(NS_XHTML, "button");
|
||||
let button = this.doc.createElementNS(NS_XHTML, "button");
|
||||
button.appendChild(this.prettyPrintNodeAsXHTML(node));
|
||||
button.className = "breadcrumbs-widget-item";
|
||||
button.id = "breadcrumbs-widget-item-" + this.breadcrumbsWidgetItemId++;
|
||||
@ -731,7 +728,7 @@ HTMLBreadcrumbs.prototype = {
|
||||
* @param {NodeFront} node The node to reach.
|
||||
*/
|
||||
expand: function (node) {
|
||||
let fragment = this.chromeDoc.createDocumentFragment();
|
||||
let fragment = this.doc.createDocumentFragment();
|
||||
let lastButtonInserted = null;
|
||||
let originalLength = this.nodeHierarchy.length;
|
||||
let stopNode = null;
|
||||
@ -856,10 +853,6 @@ HTMLBreadcrumbs.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
let cmdDispatcher = this.chromeDoc.commandDispatcher;
|
||||
this.hadFocus = (cmdDispatcher.focusedElement &&
|
||||
cmdDispatcher.focusedElement.parentNode == this.container);
|
||||
|
||||
if (!this.selection.isConnected()) {
|
||||
// remove all the crumbs
|
||||
this.cutAfter(-1);
|
||||
|
@ -20,7 +20,7 @@ loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", tr
|
||||
const { extend } = require("sdk/core/heritage");
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
|
||||
const STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
|
||||
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
|
||||
const { getSourceNames } = require("devtools/client/shared/source-utils");
|
||||
|
@ -22,7 +22,7 @@ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main",
|
||||
loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
|
||||
loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
|
||||
const STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
|
||||
const BROWSER_CONSOLE_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
|
||||
|
@ -27,7 +27,7 @@ loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/wi
|
||||
loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
|
||||
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
|
||||
const STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
|
||||
// Constants used for defining the direction of JSTerm input history navigation.
|
||||
|
@ -19,9 +19,8 @@ const NetRequest = require("./net-request");
|
||||
const { loadSheet } = require("sdk/stylesheet/utils");
|
||||
|
||||
// Localization
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
var networkStrings = Services.strings.createBundle(
|
||||
"chrome://devtools/locale/netmonitor.properties");
|
||||
const {LocalizationHelper} = require("devtools/client/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/locale/netmonitor.properties");
|
||||
|
||||
// Stylesheets
|
||||
var styleSheets = [
|
||||
@ -55,7 +54,7 @@ styleSheets.forEach(url => {
|
||||
this.Locale = {
|
||||
$STR: key => {
|
||||
try {
|
||||
return networkStrings.GetStringFromName(key);
|
||||
return L10N.getStr(key);
|
||||
} catch (err) {
|
||||
console.error(key + ": " + err);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
"use strict";
|
||||
|
||||
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
|
||||
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
|
||||
const STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
|
||||
const {
|
||||
|
@ -37,8 +37,7 @@ const SEVERITY_LOG = 3;
|
||||
// The indent of a console group in pixels.
|
||||
const GROUP_INDENT = 12;
|
||||
|
||||
const WEBCONSOLE_STRINGS_URI = "chrome://devtools/locale/" +
|
||||
"webconsole.properties";
|
||||
const WEBCONSOLE_STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
|
||||
|
||||
const DOCS_GA_PARAMS = "?utm_source=mozilla" +
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
const {Cc, Ci, Cu, components} = require("chrome");
|
||||
const Services = require("Services");
|
||||
const {LocalizationHelper} = require("devtools/client/shared/l10n");
|
||||
|
||||
// Match the function name from the result of toString() or toSource().
|
||||
//
|
||||
@ -335,19 +336,10 @@ exports.Utils = WebConsoleUtils;
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WebConsoleUtils.L10n = function (bundleURI) {
|
||||
this._bundleUri = bundleURI;
|
||||
this._helper = new LocalizationHelper(bundleURI);
|
||||
};
|
||||
|
||||
WebConsoleUtils.L10n.prototype = {
|
||||
_stringBundle: null,
|
||||
|
||||
get stringBundle() {
|
||||
if (!this._stringBundle) {
|
||||
this._stringBundle = Services.strings.createBundle(this._bundleUri);
|
||||
}
|
||||
return this._stringBundle;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates a formatted timestamp string for displaying in console messages.
|
||||
*
|
||||
@ -375,14 +367,12 @@ WebConsoleUtils.L10n.prototype = {
|
||||
* The localized string.
|
||||
*/
|
||||
getStr: function (name) {
|
||||
let result;
|
||||
try {
|
||||
result = this.stringBundle.GetStringFromName(name);
|
||||
return this._helper.getStr(name);
|
||||
} catch (ex) {
|
||||
console.error("Failed to get string: " + name);
|
||||
throw ex;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -397,14 +387,11 @@ WebConsoleUtils.L10n.prototype = {
|
||||
* The formatted local string.
|
||||
*/
|
||||
getFormatStr: function (name, array) {
|
||||
let result;
|
||||
try {
|
||||
result = this.stringBundle.formatStringFromName(name, array,
|
||||
array.length);
|
||||
return this._helper.getFormatStr(name, ...array);
|
||||
} catch (ex) {
|
||||
console.error("Failed to format string: " + name);
|
||||
throw ex;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
@ -39,7 +39,7 @@ loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm")
|
||||
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts", true);
|
||||
loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
|
||||
|
||||
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
|
||||
const STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
var l10n = new WebConsoleUtils.L10n(STRINGS_URI);
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
@ -36,8 +36,8 @@ function test_pause_frame()
|
||||
do_check_eq(vars.stopMe.value.class, "Function");
|
||||
do_check_true(!!vars.stopMe.value.actor);
|
||||
|
||||
// Skip both the eval lexical scope and the global lexical scope.
|
||||
parentEnv = parentEnv.parent.parent.parent;
|
||||
// Skip the global lexical scope.
|
||||
parentEnv = parentEnv.parent.parent;
|
||||
do_check_neq(parentEnv, undefined);
|
||||
let objClient = gThreadClient.pauseGrip(parentEnv.object);
|
||||
objClient.getPrototypeAndProperties(function (aResponse) {
|
||||
|
@ -36,8 +36,8 @@ function test_pause_frame()
|
||||
do_check_eq(aResponse.ownProperties.cos.value.class, "Function");
|
||||
do_check_true(!!aResponse.ownProperties.cos.value.actor);
|
||||
|
||||
// Skip both the eval lexical scope and the global lexical scope.
|
||||
let parentEnv = env.parent.parent.parent;
|
||||
// Skip the global lexical scope.
|
||||
let parentEnv = env.parent.parent;
|
||||
do_check_neq(parentEnv, undefined);
|
||||
|
||||
let parentClient = gThreadClient.pauseGrip(parentEnv.object);
|
||||
|
@ -87,19 +87,13 @@ namespace {
|
||||
// ---------------------------------------------------------------------------
|
||||
/* static */ already_AddRefed<Animation>
|
||||
Animation::Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationEffectReadOnly* aEffect,
|
||||
const Optional<AnimationTimeline*>& aTimeline,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<Animation> animation = new Animation(global);
|
||||
|
||||
if (!aEffect) {
|
||||
// Bug 1049975: We do not support null effect yet.
|
||||
aRv.Throw(NS_ERROR_DOM_ANIM_NO_EFFECT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AnimationTimeline* timeline;
|
||||
if (aTimeline.WasPassed()) {
|
||||
timeline = aTimeline.Value();
|
||||
@ -114,7 +108,7 @@ Animation::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
animation->SetTimelineNoUpdate(timeline);
|
||||
animation->SetEffect(aEffect);
|
||||
animation->SetEffectNoUpdate(aEffect);
|
||||
|
||||
return animation.forget();
|
||||
}
|
||||
@ -130,19 +124,90 @@ Animation::SetId(const nsAString& aId)
|
||||
}
|
||||
|
||||
void
|
||||
Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
|
||||
Animation::SetEffect(AnimationEffectReadOnly* aEffect)
|
||||
{
|
||||
SetEffectNoUpdate(aEffect);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#setting-the-target-effect
|
||||
void
|
||||
Animation::SetEffectNoUpdate(AnimationEffectReadOnly* aEffect)
|
||||
{
|
||||
RefPtr<Animation> kungFuDeathGrip(this);
|
||||
|
||||
if (mEffect == aEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoMutationBatchForAnimation mb(*this);
|
||||
bool wasRelevant = mIsRelevant;
|
||||
|
||||
if (mEffect) {
|
||||
mEffect->SetAnimation(nullptr);
|
||||
if (!aEffect) {
|
||||
// If the new effect is null, call ResetPendingTasks before clearing
|
||||
// mEffect since ResetPendingTasks needs it to get the appropriate
|
||||
// PendingAnimationTracker.
|
||||
ResetPendingTasks();
|
||||
}
|
||||
|
||||
// We need to notify observers now because once we set mEffect to null
|
||||
// we won't be able to find the target element to notify.
|
||||
if (mIsRelevant) {
|
||||
nsNodeUtils::AnimationRemoved(this);
|
||||
}
|
||||
|
||||
// Break links with the old effect and then drop it.
|
||||
RefPtr<AnimationEffectReadOnly> oldEffect = mEffect;
|
||||
mEffect = nullptr;
|
||||
oldEffect->SetAnimation(nullptr);
|
||||
|
||||
// The following will not do any notification because mEffect is null.
|
||||
UpdateRelevance();
|
||||
}
|
||||
mEffect = aEffect;
|
||||
if (mEffect) {
|
||||
|
||||
if (aEffect) {
|
||||
// Break links from the new effect to its previous animation, if any.
|
||||
RefPtr<AnimationEffectReadOnly> newEffect = aEffect;
|
||||
Animation* prevAnim = aEffect->GetAnimation();
|
||||
if (prevAnim) {
|
||||
prevAnim->SetEffect(nullptr);
|
||||
}
|
||||
|
||||
// Create links with the new effect.
|
||||
mEffect = newEffect;
|
||||
mEffect->SetAnimation(this);
|
||||
|
||||
// Update relevance and then notify possible add or change.
|
||||
// If the target is different, the change notification will be ignored by
|
||||
// AutoMutationBatchForAnimation.
|
||||
UpdateRelevance();
|
||||
if (wasRelevant && mIsRelevant) {
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
}
|
||||
|
||||
// Reschedule pending pause or pending play tasks.
|
||||
// If we have a pending animation, it will either be registered
|
||||
// in the pending animation tracker and have a null pending ready time,
|
||||
// or, after it has been painted, it will be removed from the tracker
|
||||
// and assigned a pending ready time.
|
||||
// After updating the effect we'll typically need to repaint so if we've
|
||||
// already been assigned a pending ready time, we should clear it and put
|
||||
// the animation back in the tracker.
|
||||
if (!mPendingReadyTime.IsNull()) {
|
||||
mPendingReadyTime.SetNull();
|
||||
|
||||
nsIDocument* doc = GetRenderedDocument();
|
||||
if (doc) {
|
||||
PendingAnimationTracker* tracker =
|
||||
doc->GetOrCreatePendingAnimationTracker();
|
||||
if (mPendingState == PendingState::PlayPending) {
|
||||
tracker->AddPlayPending(*this);
|
||||
} else {
|
||||
tracker->AddPausePending(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
@ -523,15 +588,20 @@ Animation::Tick()
|
||||
|
||||
if (IsPossiblyOrphanedPendingAnimation()) {
|
||||
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
|
||||
"Orphaned pending animtaions should have an active timeline");
|
||||
"Orphaned pending animations should have an active timeline");
|
||||
FinishPendingAt(mTimeline->GetCurrentTime().Value());
|
||||
}
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
|
||||
if (!mEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update layers if we are newly finished.
|
||||
if (mEffect &&
|
||||
!mEffect->Properties().IsEmpty() &&
|
||||
KeyframeEffectReadOnly* keyframeEffect = mEffect->AsKeyframeEffect();
|
||||
if (keyframeEffect &&
|
||||
!keyframeEffect->Properties().IsEmpty() &&
|
||||
!mFinishedAtLastComposeStyle &&
|
||||
PlayState() == AnimationPlayState::Finished) {
|
||||
PostUpdate();
|
||||
@ -671,12 +741,7 @@ Animation::SilentlySetPlaybackRate(double aPlaybackRate)
|
||||
void
|
||||
Animation::CancelNoUpdate()
|
||||
{
|
||||
if (mPendingState != PendingState::NotPending) {
|
||||
CancelPendingTasks();
|
||||
if (mReady) {
|
||||
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
ResetPendingTasks();
|
||||
|
||||
if (mFinished) {
|
||||
mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
@ -832,7 +897,10 @@ Animation::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
}
|
||||
}
|
||||
|
||||
mEffect->ComposeStyle(aStyleRule, aSetProperties);
|
||||
KeyframeEffectReadOnly* keyframeEffect = mEffect->AsKeyframeEffect();
|
||||
if (keyframeEffect) {
|
||||
keyframeEffect->ComposeStyle(aStyleRule, aSetProperties);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(playState == PlayState(),
|
||||
@ -1105,7 +1173,11 @@ Animation::UpdateEffect()
|
||||
{
|
||||
if (mEffect) {
|
||||
UpdateRelevance();
|
||||
mEffect->NotifyAnimationTimingUpdated();
|
||||
|
||||
KeyframeEffectReadOnly* keyframeEffect = mEffect->AsKeyframeEffect();
|
||||
if (keyframeEffect) {
|
||||
keyframeEffect->NotifyAnimationTimingUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,16 +1193,25 @@ Animation::FlushStyle() const
|
||||
void
|
||||
Animation::PostUpdate()
|
||||
{
|
||||
nsPresContext* presContext = GetPresContext();
|
||||
if (!presContext) {
|
||||
if (!mEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = mEffect->GetTarget();
|
||||
KeyframeEffectReadOnly* keyframeEffect = mEffect->AsKeyframeEffect();
|
||||
if (!keyframeEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> target = keyframeEffect->GetTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = keyframeEffect->GetPresContext();
|
||||
if (!presContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
presContext->EffectCompositor()
|
||||
->RequestRestyle(target->mElement,
|
||||
target->mPseudoType,
|
||||
@ -1161,6 +1242,20 @@ Animation::CancelPendingTasks()
|
||||
mPendingReadyTime.SetNull();
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#reset-an-animations-pending-tasks
|
||||
void
|
||||
Animation::ResetPendingTasks()
|
||||
{
|
||||
if (mPendingState == PendingState::NotPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
CancelPendingTasks();
|
||||
if (mReady) {
|
||||
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Animation::IsPossiblyOrphanedPendingAnimation() const
|
||||
{
|
||||
@ -1228,21 +1323,11 @@ Animation::EffectEnd() const
|
||||
nsIDocument*
|
||||
Animation::GetRenderedDocument() const
|
||||
{
|
||||
if (!mEffect) {
|
||||
if (!mEffect || !mEffect->AsKeyframeEffect()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mEffect->GetRenderedDocument();
|
||||
}
|
||||
|
||||
nsPresContext*
|
||||
Animation::GetPresContext() const
|
||||
{
|
||||
if (!mEffect) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mEffect->GetPresContext();
|
||||
return mEffect->AsKeyframeEffect()->GetRenderedDocument();
|
||||
}
|
||||
|
||||
void
|
||||
@ -1314,7 +1399,9 @@ Animation::DispatchPlaybackEvent(const nsAString& aName)
|
||||
bool
|
||||
Animation::IsRunningOnCompositor() const
|
||||
{
|
||||
return mEffect && mEffect->IsRunningOnCompositor();
|
||||
return mEffect &&
|
||||
mEffect->AsKeyframeEffect() &&
|
||||
mEffect->AsKeyframeEffect()->IsRunningOnCompositor();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -92,13 +92,13 @@ public:
|
||||
// Animation interface methods
|
||||
static already_AddRefed<Animation>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationEffectReadOnly* aEffect,
|
||||
const Optional<AnimationTimeline*>& aTimeline,
|
||||
ErrorResult& aRv);
|
||||
void GetId(nsAString& aResult) const { aResult = mId; }
|
||||
void SetId(const nsAString& aId);
|
||||
KeyframeEffectReadOnly* GetEffect() const { return mEffect; }
|
||||
void SetEffect(KeyframeEffectReadOnly* aEffect);
|
||||
AnimationEffectReadOnly* GetEffect() const { return mEffect; }
|
||||
void SetEffect(AnimationEffectReadOnly* aEffect);
|
||||
AnimationTimeline* GetTimeline() const { return mTimeline; }
|
||||
void SetTimeline(AnimationTimeline* aTimeline);
|
||||
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
|
||||
@ -146,6 +146,7 @@ public:
|
||||
|
||||
virtual void CancelFromStyle() { CancelNoUpdate(); }
|
||||
void SetTimelineNoUpdate(AnimationTimeline* aTimeline);
|
||||
void SetEffectNoUpdate(AnimationEffectReadOnly* aEffect);
|
||||
|
||||
virtual void Tick();
|
||||
bool NeedsTicks() const
|
||||
@ -370,14 +371,19 @@ protected:
|
||||
*/
|
||||
void CancelPendingTasks();
|
||||
|
||||
/**
|
||||
* Performs the same steps as CancelPendingTasks and also rejects and
|
||||
* recreates the ready promise if the animation was pending.
|
||||
*/
|
||||
void ResetPendingTasks();
|
||||
|
||||
bool IsPossiblyOrphanedPendingAnimation() const;
|
||||
StickyTimeDuration EffectEnd() const;
|
||||
|
||||
nsIDocument* GetRenderedDocument() const;
|
||||
nsPresContext* GetPresContext() const;
|
||||
|
||||
RefPtr<AnimationTimeline> mTimeline;
|
||||
RefPtr<KeyframeEffectReadOnly> mEffect;
|
||||
RefPtr<AnimationEffectReadOnly> mEffect;
|
||||
// The beginning of the delay period.
|
||||
Nullable<TimeDuration> mStartTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mHoldTime; // Animation timescale
|
||||
|
@ -6,11 +6,27 @@
|
||||
|
||||
#include "mozilla/dom/AnimationEffectReadOnly.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectReadOnly, mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEffectReadOnly)
|
||||
if (tmp->mTiming) {
|
||||
tmp->mTiming->Unlink();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument, mTiming, mAnimation)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument, mTiming, mAnimation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AnimationEffectReadOnly)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationEffectReadOnly)
|
||||
@ -20,5 +36,313 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationEffectReadOnly)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
AnimationEffectReadOnly::AnimationEffectReadOnly(
|
||||
nsIDocument* aDocument, AnimationEffectTimingReadOnly* aTiming)
|
||||
: mDocument(aDocument)
|
||||
, mTiming(aTiming)
|
||||
{
|
||||
MOZ_ASSERT(aTiming);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#in-play
|
||||
bool
|
||||
AnimationEffectReadOnly::IsInPlay() const
|
||||
{
|
||||
if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#current
|
||||
bool
|
||||
AnimationEffectReadOnly::IsCurrent() const
|
||||
{
|
||||
if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
|
||||
computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#in-effect
|
||||
bool
|
||||
AnimationEffectReadOnly::IsInEffect() const
|
||||
{
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return !computedTiming.mProgress.IsNull();
|
||||
}
|
||||
|
||||
already_AddRefed<AnimationEffectTimingReadOnly>
|
||||
AnimationEffectReadOnly::Timing()
|
||||
{
|
||||
RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
|
||||
{
|
||||
if (mTiming->AsTimingParams() == aTiming) {
|
||||
return;
|
||||
}
|
||||
mTiming->SetTimingParams(aTiming);
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
}
|
||||
// For keyframe effects, NotifyEffectTimingUpdated above will eventually cause
|
||||
// KeyframeEffectReadOnly::NotifyAnimationTimingUpdated to be called so it can
|
||||
// update its registration with the target element as necessary.
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
AnimationEffectReadOnly::GetComputedTimingAt(
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
double aPlaybackRate)
|
||||
{
|
||||
const StickyTimeDuration zeroDuration;
|
||||
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
ComputedTiming result;
|
||||
|
||||
if (aTiming.mDuration) {
|
||||
MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
|
||||
"Iteration duration should be positive");
|
||||
result.mDuration = aTiming.mDuration.ref();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
|
||||
"mIterations should be nonnegative & finite, as ensured by "
|
||||
"ValidateIterations or CSSParser");
|
||||
result.mIterations = aTiming.mIterations;
|
||||
|
||||
MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
|
||||
"mIterationStart should be nonnegative, as ensured by "
|
||||
"ValidateIterationStart");
|
||||
result.mIterationStart = aTiming.mIterationStart;
|
||||
|
||||
result.mActiveDuration = aTiming.ActiveDuration();
|
||||
result.mEndTime = aTiming.EndTime();
|
||||
result.mFill = aTiming.mFill == dom::FillMode::Auto ?
|
||||
dom::FillMode::None :
|
||||
aTiming.mFill;
|
||||
|
||||
// The default constructor for ComputedTiming sets all other members to
|
||||
// values consistent with an animation that has not been sampled.
|
||||
if (aLocalTime.IsNull()) {
|
||||
return result;
|
||||
}
|
||||
const TimeDuration& localTime = aLocalTime.Value();
|
||||
|
||||
// Calculate the time within the active interval.
|
||||
// https://w3c.github.io/web-animations/#active-time
|
||||
StickyTimeDuration activeTime;
|
||||
|
||||
StickyTimeDuration beforeActiveBoundary =
|
||||
std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime);
|
||||
StickyTimeDuration activeAfterBoundary =
|
||||
std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
|
||||
result.mEndTime);
|
||||
|
||||
if (localTime > activeAfterBoundary ||
|
||||
(aPlaybackRate >= 0 && localTime == activeAfterBoundary)) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::After;
|
||||
if (!result.FillsForwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
return result;
|
||||
}
|
||||
activeTime = std::max(std::min(result.mActiveDuration,
|
||||
result.mActiveDuration + aTiming.mEndDelay),
|
||||
zeroDuration);
|
||||
} else if (localTime < beforeActiveBoundary ||
|
||||
(aPlaybackRate < 0 && localTime == beforeActiveBoundary)) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::Before;
|
||||
if (!result.FillsBackwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
return result;
|
||||
}
|
||||
// activeTime is zero
|
||||
} else {
|
||||
MOZ_ASSERT(result.mActiveDuration != zeroDuration,
|
||||
"How can we be in the middle of a zero-duration interval?");
|
||||
result.mPhase = ComputedTiming::AnimationPhase::Active;
|
||||
activeTime = localTime - aTiming.mDelay;
|
||||
}
|
||||
|
||||
// Convert active time to a multiple of iterations.
|
||||
// https://w3c.github.io/web-animations/#overall-progress
|
||||
double overallProgress;
|
||||
if (result.mDuration == zeroDuration) {
|
||||
overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
|
||||
? 0.0
|
||||
: result.mIterations;
|
||||
} else {
|
||||
overallProgress = activeTime / result.mDuration;
|
||||
}
|
||||
|
||||
// Factor in iteration start offset.
|
||||
if (IsFinite(overallProgress)) {
|
||||
overallProgress += result.mIterationStart;
|
||||
}
|
||||
|
||||
// Determine the 0-based index of the current iteration.
|
||||
// https://w3c.github.io/web-animations/#current-iteration
|
||||
result.mCurrentIteration =
|
||||
IsInfinite(result.mIterations) &&
|
||||
result.mPhase == ComputedTiming::AnimationPhase::After
|
||||
? UINT64_MAX // In GetComputedTimingDictionary(),
|
||||
// we will convert this into Infinity
|
||||
: static_cast<uint64_t>(overallProgress);
|
||||
|
||||
// Convert the overall progress to a fraction of a single iteration--the
|
||||
// simply iteration progress.
|
||||
// https://w3c.github.io/web-animations/#simple-iteration-progress
|
||||
double progress = IsFinite(overallProgress)
|
||||
? fmod(overallProgress, 1.0)
|
||||
: fmod(result.mIterationStart, 1.0);
|
||||
|
||||
// When we finish exactly at the end of an iteration we need to report
|
||||
// the end of the final iteration and not the start of the next iteration.
|
||||
// We *don't* want to do this when we have a zero-iteration animation or
|
||||
// when the animation has been effectively made into a zero-duration animation
|
||||
// using a negative end-delay, however.
|
||||
if (result.mPhase == ComputedTiming::AnimationPhase::After &&
|
||||
progress == 0.0 &&
|
||||
result.mIterations != 0.0 &&
|
||||
(activeTime != zeroDuration || result.mDuration == zeroDuration)) {
|
||||
// The only way we can be in the after phase with a progress of zero and
|
||||
// a current iteration of zero, is if we have a zero iteration count or
|
||||
// were clipped using a negative end delay--both of which we should have
|
||||
// detected above.
|
||||
MOZ_ASSERT(result.mCurrentIteration != 0,
|
||||
"Should not have zero current iteration");
|
||||
progress = 1.0;
|
||||
if (result.mCurrentIteration != UINT64_MAX) {
|
||||
result.mCurrentIteration--;
|
||||
}
|
||||
}
|
||||
|
||||
// Factor in the direction.
|
||||
bool thisIterationReverse = false;
|
||||
switch (aTiming.mDirection) {
|
||||
case PlaybackDirection::Normal:
|
||||
thisIterationReverse = false;
|
||||
break;
|
||||
case PlaybackDirection::Reverse:
|
||||
thisIterationReverse = true;
|
||||
break;
|
||||
case PlaybackDirection::Alternate:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 1;
|
||||
break;
|
||||
case PlaybackDirection::Alternate_reverse:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 0;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(true, "Unknown PlaybackDirection type");
|
||||
}
|
||||
if (thisIterationReverse) {
|
||||
progress = 1.0 - progress;
|
||||
}
|
||||
|
||||
// Calculate the 'before flag' which we use when applying step timing
|
||||
// functions.
|
||||
if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
|
||||
thisIterationReverse) ||
|
||||
(result.mPhase == ComputedTiming::AnimationPhase::Before &&
|
||||
!thisIterationReverse)) {
|
||||
result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
|
||||
}
|
||||
|
||||
// Apply the easing.
|
||||
if (aTiming.mFunction) {
|
||||
progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
|
||||
result.mProgress.SetValue(progress);
|
||||
return result;
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
AnimationEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
|
||||
{
|
||||
double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
|
||||
return GetComputedTimingAt(GetLocalTime(),
|
||||
aTiming ? *aTiming : SpecifiedTiming(),
|
||||
playbackRate);
|
||||
}
|
||||
|
||||
// Helper functions for generating a ComputedTimingProperties dictionary
|
||||
static void
|
||||
GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
ComputedTimingProperties& aRetVal)
|
||||
{
|
||||
// AnimationEffectTimingProperties
|
||||
aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
|
||||
aRetVal.mEndDelay = aTiming.mEndDelay.ToMilliseconds();
|
||||
aRetVal.mFill = aComputedTiming.mFill;
|
||||
aRetVal.mIterations = aComputedTiming.mIterations;
|
||||
aRetVal.mIterationStart = aComputedTiming.mIterationStart;
|
||||
aRetVal.mDuration.SetAsUnrestrictedDouble() =
|
||||
aComputedTiming.mDuration.ToMilliseconds();
|
||||
aRetVal.mDirection = aTiming.mDirection;
|
||||
|
||||
// ComputedTimingProperties
|
||||
aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
|
||||
aRetVal.mEndTime = aComputedTiming.mEndTime.ToMilliseconds();
|
||||
aRetVal.mLocalTime = AnimationUtils::TimeDurationToDouble(aLocalTime);
|
||||
aRetVal.mProgress = aComputedTiming.mProgress;
|
||||
|
||||
if (!aRetVal.mProgress.IsNull()) {
|
||||
// Convert the returned currentIteration into Infinity if we set
|
||||
// (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
|
||||
double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
|
||||
? PositiveInfinity<double>()
|
||||
: static_cast<double>(aComputedTiming.mCurrentIteration);
|
||||
aRetVal.mCurrentIteration.SetValue(iteration);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectReadOnly::GetComputedTimingAsDict(
|
||||
ComputedTimingProperties& aRetVal) const
|
||||
{
|
||||
double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
|
||||
const Nullable<TimeDuration> currentTime = GetLocalTime();
|
||||
GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
|
||||
SpecifiedTiming(),
|
||||
playbackRate),
|
||||
currentTime,
|
||||
SpecifiedTiming(),
|
||||
aRetVal);
|
||||
}
|
||||
|
||||
AnimationEffectReadOnly::~AnimationEffectReadOnly()
|
||||
{
|
||||
// mTiming is cycle collected, so we have to do null check first even though
|
||||
// mTiming shouldn't be null during the lifetime of KeyframeEffect.
|
||||
if (mTiming) {
|
||||
mTiming->Unlink();
|
||||
}
|
||||
}
|
||||
|
||||
Nullable<TimeDuration>
|
||||
AnimationEffectReadOnly::GetLocalTime() const
|
||||
{
|
||||
// Since the *animation* start time is currently always zero, the local
|
||||
// time is equal to the parent time.
|
||||
Nullable<TimeDuration> result;
|
||||
if (mAnimation) {
|
||||
result = mAnimation->GetCurrentTime();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -7,14 +7,26 @@
|
||||
#ifndef mozilla_dom_AnimationEffectReadOnly_h
|
||||
#define mozilla_dom_AnimationEffectReadOnly_h
|
||||
|
||||
#include "mozilla/ComputedTiming.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/StickyTimeDuration.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TimingParams.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct ElementPropertyTransition;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class Animation;
|
||||
class AnimationEffectTimingReadOnly;
|
||||
class KeyframeEffectReadOnly;
|
||||
struct ComputedTimingProperties;
|
||||
|
||||
class AnimationEffectReadOnly : public nsISupports,
|
||||
@ -24,22 +36,61 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadOnly)
|
||||
|
||||
explicit AnimationEffectReadOnly(nsIDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
AnimationEffectReadOnly(nsIDocument* aDocument,
|
||||
AnimationEffectTimingReadOnly* aTiming);
|
||||
|
||||
virtual KeyframeEffectReadOnly* AsKeyframeEffect() { return nullptr; }
|
||||
|
||||
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
|
||||
virtual const ElementPropertyTransition* AsTransition() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsISupports* GetParentObject() const { return mDocument; }
|
||||
|
||||
virtual already_AddRefed<AnimationEffectTimingReadOnly> Timing() const = 0;
|
||||
bool IsInPlay() const;
|
||||
bool IsCurrent() const;
|
||||
bool IsInEffect() const;
|
||||
|
||||
virtual void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const = 0;
|
||||
already_AddRefed<AnimationEffectTimingReadOnly> Timing();
|
||||
const TimingParams& SpecifiedTiming() const
|
||||
{
|
||||
return mTiming->AsTimingParams();
|
||||
}
|
||||
void SetSpecifiedTiming(const TimingParams& aTiming);
|
||||
|
||||
// This function takes as input the timing parameters of an animation and
|
||||
// returns the computed timing at the specified local time.
|
||||
//
|
||||
// The local time may be null in which case only static parameters such as the
|
||||
// active duration are calculated. All other members of the returned object
|
||||
// are given a null/initial value.
|
||||
//
|
||||
// This function returns a null mProgress member of the return value
|
||||
// if the animation should not be run
|
||||
// (because it is not currently active and is not filling at this time).
|
||||
static ComputedTiming
|
||||
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
double aPlaybackRate);
|
||||
// Shortcut that gets the computed timing using the current local time as
|
||||
// calculated from the timeline time.
|
||||
ComputedTiming GetComputedTiming(const TimingParams* aTiming = nullptr) const;
|
||||
void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const;
|
||||
|
||||
virtual void SetAnimation(Animation* aAnimation) = 0;
|
||||
Animation* GetAnimation() const { return mAnimation; };
|
||||
|
||||
protected:
|
||||
virtual ~AnimationEffectReadOnly() = default;
|
||||
virtual ~AnimationEffectReadOnly();
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const;
|
||||
|
||||
protected:
|
||||
RefPtr<nsIDocument> mDocument;
|
||||
RefPtr<AnimationEffectTimingReadOnly> mTiming;
|
||||
RefPtr<Animation> mAnimation;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -24,55 +24,11 @@
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Helper functions for generating a ComputedTimingProperties dictionary
|
||||
static void
|
||||
GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
dom::ComputedTimingProperties& aRetVal)
|
||||
{
|
||||
// AnimationEffectTimingProperties
|
||||
aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
|
||||
aRetVal.mEndDelay = aTiming.mEndDelay.ToMilliseconds();
|
||||
aRetVal.mFill = aComputedTiming.mFill;
|
||||
aRetVal.mIterations = aComputedTiming.mIterations;
|
||||
aRetVal.mIterationStart = aComputedTiming.mIterationStart;
|
||||
aRetVal.mDuration.SetAsUnrestrictedDouble() =
|
||||
aComputedTiming.mDuration.ToMilliseconds();
|
||||
aRetVal.mDirection = aTiming.mDirection;
|
||||
|
||||
// ComputedTimingProperties
|
||||
aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds();
|
||||
aRetVal.mEndTime = aComputedTiming.mEndTime.ToMilliseconds();
|
||||
aRetVal.mLocalTime = AnimationUtils::TimeDurationToDouble(aLocalTime);
|
||||
aRetVal.mProgress = aComputedTiming.mProgress;
|
||||
|
||||
if (!aRetVal.mProgress.IsNull()) {
|
||||
// Convert the returned currentIteration into Infinity if we set
|
||||
// (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX
|
||||
double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX
|
||||
? PositiveInfinity<double>()
|
||||
: static_cast<double>(aComputedTiming.mCurrentIteration);
|
||||
aRetVal.mCurrentIteration.SetValue(iteration);
|
||||
}
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(KeyframeEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
if (tmp->mTiming) {
|
||||
tmp->mTiming->Unlink();
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTarget, mAnimation, mTiming)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTarget, mAnimation, mTiming)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly,
|
||||
mTarget)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
|
||||
AnimationEffectReadOnly)
|
||||
@ -101,14 +57,12 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
const Maybe<OwningAnimationTarget>& aTarget,
|
||||
AnimationEffectTimingReadOnly* aTiming,
|
||||
const KeyframeEffectParams& aOptions)
|
||||
: AnimationEffectReadOnly(aDocument)
|
||||
: AnimationEffectReadOnly(aDocument, aTiming)
|
||||
, mTarget(aTarget)
|
||||
, mTiming(aTiming)
|
||||
, mEffectOptions(aOptions)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
, mCumulativeChangeHint(nsChangeHint(0))
|
||||
{
|
||||
MOZ_ASSERT(aTiming);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -130,28 +84,6 @@ KeyframeEffectReadOnly::Composite() const
|
||||
return CompositeOperation::Replace;
|
||||
}
|
||||
|
||||
already_AddRefed<AnimationEffectTimingReadOnly>
|
||||
KeyframeEffectReadOnly::Timing() const
|
||||
{
|
||||
RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
|
||||
{
|
||||
if (mTiming->AsTimingParams() == aTiming) {
|
||||
return;
|
||||
}
|
||||
mTiming->SetTimingParams(aTiming);
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
}
|
||||
// NotifyEffectTimingUpdated will eventually cause
|
||||
// NotifyAnimationTimingUpdated to be called on this object which will
|
||||
// update our registration with the target element.
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
{
|
||||
@ -205,249 +137,6 @@ KeyframeEffectReadOnly::NotifyAnimationTimingUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
Nullable<TimeDuration>
|
||||
KeyframeEffectReadOnly::GetLocalTime() const
|
||||
{
|
||||
// Since the *animation* start time is currently always zero, the local
|
||||
// time is equal to the parent time.
|
||||
Nullable<TimeDuration> result;
|
||||
if (mAnimation) {
|
||||
result = mAnimation->GetCurrentTime();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::GetComputedTimingAsDict(
|
||||
ComputedTimingProperties& aRetVal) const
|
||||
{
|
||||
double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
|
||||
const Nullable<TimeDuration> currentTime = GetLocalTime();
|
||||
GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
|
||||
SpecifiedTiming(),
|
||||
playbackRate),
|
||||
currentTime,
|
||||
SpecifiedTiming(),
|
||||
aRetVal);
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
double aPlaybackRate)
|
||||
{
|
||||
const StickyTimeDuration zeroDuration;
|
||||
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
ComputedTiming result;
|
||||
|
||||
if (aTiming.mDuration) {
|
||||
MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
|
||||
"Iteration duration should be positive");
|
||||
result.mDuration = aTiming.mDuration.ref();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
|
||||
"mIterations should be nonnegative & finite, as ensured by "
|
||||
"ValidateIterations or CSSParser");
|
||||
result.mIterations = aTiming.mIterations;
|
||||
|
||||
MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
|
||||
"mIterationStart should be nonnegative, as ensured by "
|
||||
"ValidateIterationStart");
|
||||
result.mIterationStart = aTiming.mIterationStart;
|
||||
|
||||
result.mActiveDuration = aTiming.ActiveDuration();
|
||||
result.mEndTime = aTiming.EndTime();
|
||||
result.mFill = aTiming.mFill == dom::FillMode::Auto ?
|
||||
dom::FillMode::None :
|
||||
aTiming.mFill;
|
||||
|
||||
// The default constructor for ComputedTiming sets all other members to
|
||||
// values consistent with an animation that has not been sampled.
|
||||
if (aLocalTime.IsNull()) {
|
||||
return result;
|
||||
}
|
||||
const TimeDuration& localTime = aLocalTime.Value();
|
||||
|
||||
// Calculate the time within the active interval.
|
||||
// https://w3c.github.io/web-animations/#active-time
|
||||
StickyTimeDuration activeTime;
|
||||
|
||||
StickyTimeDuration beforeActiveBoundary =
|
||||
std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime);
|
||||
StickyTimeDuration activeAfterBoundary =
|
||||
std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
|
||||
result.mEndTime);
|
||||
|
||||
if (localTime > activeAfterBoundary ||
|
||||
(aPlaybackRate >= 0 && localTime == activeAfterBoundary)) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::After;
|
||||
if (!result.FillsForwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
return result;
|
||||
}
|
||||
activeTime = std::max(std::min(result.mActiveDuration,
|
||||
result.mActiveDuration + aTiming.mEndDelay),
|
||||
zeroDuration);
|
||||
} else if (localTime < beforeActiveBoundary ||
|
||||
(aPlaybackRate < 0 && localTime == beforeActiveBoundary)) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::Before;
|
||||
if (!result.FillsBackwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
return result;
|
||||
}
|
||||
// activeTime is zero
|
||||
} else {
|
||||
MOZ_ASSERT(result.mActiveDuration != zeroDuration,
|
||||
"How can we be in the middle of a zero-duration interval?");
|
||||
result.mPhase = ComputedTiming::AnimationPhase::Active;
|
||||
activeTime = localTime - aTiming.mDelay;
|
||||
}
|
||||
|
||||
// Convert active time to a multiple of iterations.
|
||||
// https://w3c.github.io/web-animations/#overall-progress
|
||||
double overallProgress;
|
||||
if (result.mDuration == zeroDuration) {
|
||||
overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
|
||||
? 0.0
|
||||
: result.mIterations;
|
||||
} else {
|
||||
overallProgress = activeTime / result.mDuration;
|
||||
}
|
||||
|
||||
// Factor in iteration start offset.
|
||||
if (IsFinite(overallProgress)) {
|
||||
overallProgress += result.mIterationStart;
|
||||
}
|
||||
|
||||
// Determine the 0-based index of the current iteration.
|
||||
// https://w3c.github.io/web-animations/#current-iteration
|
||||
result.mCurrentIteration =
|
||||
IsInfinite(result.mIterations) &&
|
||||
result.mPhase == ComputedTiming::AnimationPhase::After
|
||||
? UINT64_MAX // In GetComputedTimingDictionary(),
|
||||
// we will convert this into Infinity
|
||||
: static_cast<uint64_t>(overallProgress);
|
||||
|
||||
// Convert the overall progress to a fraction of a single iteration--the
|
||||
// simply iteration progress.
|
||||
// https://w3c.github.io/web-animations/#simple-iteration-progress
|
||||
double progress = IsFinite(overallProgress)
|
||||
? fmod(overallProgress, 1.0)
|
||||
: fmod(result.mIterationStart, 1.0);
|
||||
|
||||
// When we finish exactly at the end of an iteration we need to report
|
||||
// the end of the final iteration and not the start of the next iteration.
|
||||
// We *don't* want to do this when we have a zero-iteration animation or
|
||||
// when the animation has been effectively made into a zero-duration animation
|
||||
// using a negative end-delay, however.
|
||||
if (result.mPhase == ComputedTiming::AnimationPhase::After &&
|
||||
progress == 0.0 &&
|
||||
result.mIterations != 0.0 &&
|
||||
(activeTime != zeroDuration || result.mDuration == zeroDuration)) {
|
||||
// The only way we can be in the after phase with a progress of zero and
|
||||
// a current iteration of zero, is if we have a zero iteration count or
|
||||
// were clipped using a negative end delay--both of which we should have
|
||||
// detected above.
|
||||
MOZ_ASSERT(result.mCurrentIteration != 0,
|
||||
"Should not have zero current iteration");
|
||||
progress = 1.0;
|
||||
if (result.mCurrentIteration != UINT64_MAX) {
|
||||
result.mCurrentIteration--;
|
||||
}
|
||||
}
|
||||
|
||||
// Factor in the direction.
|
||||
bool thisIterationReverse = false;
|
||||
switch (aTiming.mDirection) {
|
||||
case PlaybackDirection::Normal:
|
||||
thisIterationReverse = false;
|
||||
break;
|
||||
case PlaybackDirection::Reverse:
|
||||
thisIterationReverse = true;
|
||||
break;
|
||||
case PlaybackDirection::Alternate:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 1;
|
||||
break;
|
||||
case PlaybackDirection::Alternate_reverse:
|
||||
thisIterationReverse = (result.mCurrentIteration & 1) == 0;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(true, "Unknown PlaybackDirection type");
|
||||
}
|
||||
if (thisIterationReverse) {
|
||||
progress = 1.0 - progress;
|
||||
}
|
||||
|
||||
// Calculate the 'before flag' which we use when applying step timing
|
||||
// functions.
|
||||
if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
|
||||
thisIterationReverse) ||
|
||||
(result.mPhase == ComputedTiming::AnimationPhase::Before &&
|
||||
!thisIterationReverse)) {
|
||||
result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
|
||||
}
|
||||
|
||||
// Apply the easing.
|
||||
if (aTiming.mFunction) {
|
||||
progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
|
||||
result.mProgress.SetValue(progress);
|
||||
return result;
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
KeyframeEffectReadOnly::GetComputedTiming(const TimingParams* aTiming) const
|
||||
{
|
||||
double playbackRate = mAnimation ? mAnimation->PlaybackRate() : 1;
|
||||
return GetComputedTimingAt(GetLocalTime(),
|
||||
aTiming ? *aTiming : SpecifiedTiming(),
|
||||
playbackRate);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#in-play
|
||||
bool
|
||||
KeyframeEffectReadOnly::IsInPlay() const
|
||||
{
|
||||
if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#current
|
||||
bool
|
||||
KeyframeEffectReadOnly::IsCurrent() const
|
||||
{
|
||||
if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before ||
|
||||
computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#in-effect
|
||||
bool
|
||||
KeyframeEffectReadOnly::IsInEffect() const
|
||||
{
|
||||
ComputedTiming computedTiming = GetComputedTiming();
|
||||
return !computedTiming.mProgress.IsNull();
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
|
||||
{
|
||||
mAnimation = aAnimation;
|
||||
NotifyAnimationTimingUpdated();
|
||||
}
|
||||
|
||||
static bool
|
||||
KeyframesEqualIgnoringComputedOffsets(const nsTArray<Keyframe>& aLhs,
|
||||
const nsTArray<Keyframe>& aRhs)
|
||||
@ -772,10 +461,6 @@ KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
|
||||
}
|
||||
}
|
||||
|
||||
KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
|
||||
{
|
||||
}
|
||||
|
||||
static const KeyframeEffectOptions&
|
||||
KeyframeEffectOptionsFromUnion(
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
|
||||
@ -1535,6 +1220,32 @@ KeyframeEffectReadOnly::CalculateCumulativeChangeHint(
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation)
|
||||
{
|
||||
if (mAnimation == aAnimation) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restyle for the old animation.
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
mAnimation = aAnimation;
|
||||
|
||||
// Restyle for the new animation.
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
|
||||
if (mTarget) {
|
||||
EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
|
||||
mTarget->mPseudoType);
|
||||
if (effectSet) {
|
||||
effectSet->MarkCascadeNeedsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
NotifyAnimationTimingUpdated();
|
||||
}
|
||||
|
||||
bool
|
||||
KeyframeEffectReadOnly::CanIgnoreIfNotVisible() const
|
||||
{
|
||||
@ -1683,14 +1394,5 @@ KeyframeEffect::SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
|
||||
}
|
||||
}
|
||||
|
||||
KeyframeEffect::~KeyframeEffect()
|
||||
{
|
||||
// mTiming is cycle collected, so we have to do null check first even though
|
||||
// mTiming shouldn't be null during the lifetime of KeyframeEffect.
|
||||
if (mTiming) {
|
||||
mTiming->Unlink();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -16,20 +16,13 @@
|
||||
#include "mozilla/AnimationPerformanceWarning.h"
|
||||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ComputedTiming.h"
|
||||
#include "mozilla/ComputedTimingFunction.h"
|
||||
#include "mozilla/EffectCompositor.h"
|
||||
#include "mozilla/KeyframeEffectParams.h"
|
||||
#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/StickyTimeDuration.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TimingParams.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnly.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
|
||||
struct JSContext;
|
||||
class nsCSSPropertyIDSet;
|
||||
@ -205,11 +198,7 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
|
||||
virtual const ElementPropertyTransition* AsTransition() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
KeyframeEffectReadOnly* AsKeyframeEffect() override { return this; }
|
||||
|
||||
// KeyframeEffectReadOnly interface
|
||||
static already_AddRefed<KeyframeEffectReadOnly>
|
||||
@ -241,46 +230,9 @@ public:
|
||||
mEffectOptions.GetSpacingAsString(aRetVal);
|
||||
}
|
||||
|
||||
already_AddRefed<AnimationEffectTimingReadOnly> Timing() const override;
|
||||
|
||||
const TimingParams& SpecifiedTiming() const
|
||||
{
|
||||
return mTiming->AsTimingParams();
|
||||
}
|
||||
void SetSpecifiedTiming(const TimingParams& aTiming);
|
||||
void NotifyAnimationTimingUpdated();
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const;
|
||||
|
||||
// This function takes as input the timing parameters of an animation and
|
||||
// returns the computed timing at the specified local time.
|
||||
//
|
||||
// The local time may be null in which case only static parameters such as the
|
||||
// active duration are calculated. All other members of the returned object
|
||||
// are given a null/initial value.
|
||||
//
|
||||
// This function returns a null mProgress member of the return value
|
||||
// if the animation should not be run
|
||||
// (because it is not currently active and is not filling at this time).
|
||||
static ComputedTiming
|
||||
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
|
||||
const TimingParams& aTiming,
|
||||
double aPlaybackRate);
|
||||
|
||||
// Shortcut for that gets the computed timing using the current local time as
|
||||
// calculated from the timeline time.
|
||||
ComputedTiming
|
||||
GetComputedTiming(const TimingParams* aTiming = nullptr) const;
|
||||
|
||||
void
|
||||
GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override;
|
||||
|
||||
bool IsInPlay() const;
|
||||
bool IsCurrent() const;
|
||||
bool IsInEffect() const;
|
||||
|
||||
void SetAnimation(Animation* aAnimation);
|
||||
Animation* GetAnimation() const { return mAnimation; }
|
||||
void SetAnimation(Animation* aAnimation) override;
|
||||
|
||||
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
ErrorResult& aRv);
|
||||
@ -288,13 +240,16 @@ public:
|
||||
nsStyleContext* aStyleContext);
|
||||
const AnimationProperty*
|
||||
GetAnimationOfProperty(nsCSSPropertyID aProperty) const;
|
||||
bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const {
|
||||
bool HasAnimationOfProperty(nsCSSPropertyID aProperty) const
|
||||
{
|
||||
return GetAnimationOfProperty(aProperty) != nullptr;
|
||||
}
|
||||
const InfallibleTArray<AnimationProperty>& Properties() const {
|
||||
const InfallibleTArray<AnimationProperty>& Properties() const
|
||||
{
|
||||
return mProperties;
|
||||
}
|
||||
InfallibleTArray<AnimationProperty>& Properties() {
|
||||
InfallibleTArray<AnimationProperty>& Properties()
|
||||
{
|
||||
return mProperties;
|
||||
}
|
||||
|
||||
@ -354,7 +309,7 @@ protected:
|
||||
AnimationEffectTimingReadOnly* aTiming,
|
||||
const KeyframeEffectParams& aOptions);
|
||||
|
||||
virtual ~KeyframeEffectReadOnly();
|
||||
~KeyframeEffectReadOnly() override = default;
|
||||
|
||||
template<class KeyframeEffectType, class OptionsType>
|
||||
static already_AddRefed<KeyframeEffectType>
|
||||
@ -397,9 +352,7 @@ protected:
|
||||
GetTargetStyleContext();
|
||||
|
||||
Maybe<OwningAnimationTarget> mTarget;
|
||||
RefPtr<Animation> mAnimation;
|
||||
|
||||
RefPtr<AnimationEffectTimingReadOnly> mTiming;
|
||||
KeyframeEffectParams mEffectOptions;
|
||||
|
||||
// The specified keyframes.
|
||||
@ -472,9 +425,6 @@ public:
|
||||
// that to update the properties rather than calling
|
||||
// GetStyleContextForElement.
|
||||
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
|
||||
|
||||
protected:
|
||||
~KeyframeEffect() override;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -1794,6 +1794,108 @@ addAsyncAnimTest("set_redundant_animation_target",
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
addAsyncAnimTest("set_null_animation_effect",
|
||||
{ observe: div, subtree: true }, function*() {
|
||||
var anim = div.animate({ opacity: [ 0, 1 ] },
|
||||
{ duration: 100 * MS_PER_SEC });
|
||||
yield await_frame();
|
||||
assert_records([{ added: [anim], changed: [], removed: [] }],
|
||||
"records after animation is added");
|
||||
|
||||
anim.effect = null;
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [], removed: [anim] }],
|
||||
"records after animation is removed");
|
||||
|
||||
anim.cancel();
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
addAsyncAnimTest("set_effect_on_null_effect_animation",
|
||||
{ observe: div, subtree: true }, function*() {
|
||||
var anim = new Animation();
|
||||
anim.play();
|
||||
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
||||
100 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [anim], changed: [], removed: [] }],
|
||||
"records after animation is added");
|
||||
|
||||
anim.cancel();
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
addAsyncAnimTest("replace_effect_targeting_on_the_same_element",
|
||||
{ observe: div, subtree: true }, function*() {
|
||||
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
|
||||
100 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [anim], changed: [], removed: [] }],
|
||||
"records after animation is added");
|
||||
|
||||
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
||||
100 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [anim], removed: [] }],
|
||||
"records after replace effects");
|
||||
|
||||
anim.cancel();
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
addAsyncAnimTest("replace_effect_targeting_on_the_same_element_not_in_effect",
|
||||
{ observe: div, subtree: true }, function*() {
|
||||
var anim = div.animate({ marginLeft: [ "0px", "100px" ] },
|
||||
100 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [anim], changed: [], removed: [] }],
|
||||
"records after animation is added");
|
||||
|
||||
anim.currentTime = 60 * MS_PER_SEC;
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [anim], removed: [] }],
|
||||
"records after animation is changed");
|
||||
|
||||
anim.effect = new KeyframeEffect(div, { opacity: [ 0, 1 ] },
|
||||
50 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [], removed: [anim] }],
|
||||
"records after replacing effects");
|
||||
|
||||
anim.cancel();
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
addAsyncAnimTest("set_effect_with_previous_animation",
|
||||
{ observe: div, subtree: true }, function*() {
|
||||
var child = document.createElement("div");
|
||||
div.appendChild(child);
|
||||
|
||||
var anim1 = div.animate({ marginLeft: [ "0px", "50px" ] },
|
||||
100 * MS_PER_SEC);
|
||||
var anim2 = child.animate({ marginLeft: [ "0px", "100px" ] },
|
||||
50 * MS_PER_SEC);
|
||||
yield await_frame();
|
||||
assert_records([{ added: [anim1], changed: [], removed: [] },
|
||||
{ added: [anim2], changed: [], removed: [] }],
|
||||
"records after animation is added");
|
||||
|
||||
// After setting a new effect, we remove the current animation, anim1, because
|
||||
// it is no longer attached to |div|, and then remove the previous animation,
|
||||
// anim2. Finally, add back the anim1 which is in effect on |child| now.
|
||||
// In addition, we sort them by tree order and they are batched.
|
||||
anim1.effect = anim2.effect;
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [], removed: [anim1] }, // div
|
||||
{ added: [anim1], changed: [], removed: [anim2] }], // child
|
||||
"records after animation effects are changed");
|
||||
|
||||
anim1.cancel();
|
||||
anim2.cancel();
|
||||
child.remove();
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
// Run the tests.
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -515,6 +515,31 @@ promise_test(function(t) {
|
||||
}, '100% opacity animation set up by converting an existing animation with ' +
|
||||
'cannot be run on the compositor, is running on the compositor');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
|
||||
var effect = new KeyframeEffect(div,
|
||||
[{ opacity: 1, offset: 0 },
|
||||
{ opacity: 1, offset: 0.99 },
|
||||
{ opacity: 0, offset: 1 }],
|
||||
100 * MS_PER_SEC);
|
||||
|
||||
return animation.ready.then(function() {
|
||||
assert_equals(animation.isRunningOnCompositor, false,
|
||||
'Color animation reports that it is not running on the ' +
|
||||
'compositor');
|
||||
|
||||
animation.effect = effect;
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
assert_equals(animation.isRunningOnCompositor, omtaEnabled,
|
||||
'100% opacity animation set up by changing effects reports ' +
|
||||
'that it is running on the compositor');
|
||||
});
|
||||
}, '100% opacity animation set up by changing the effects on an existing ' +
|
||||
'animation which cannot be run on the compositor, is running on the ' +
|
||||
'compositor');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t, { style: "opacity: 1 ! important" });
|
||||
|
||||
|
91
dom/animation/test/css-transitions/file_setting-effect.html
Normal file
91
dom/animation/test/css-transitions/file_setting-effect.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src='../testcommon.js'></script>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.left = '0px';
|
||||
|
||||
div.style.transition = 'left 100s';
|
||||
flushComputedStyle(div);
|
||||
div.style.left = '100px';
|
||||
|
||||
var transition = div.getAnimations()[0];
|
||||
return transition.ready.then(function() {
|
||||
transition.currentTime = 50 * MS_PER_SEC;
|
||||
transition.effect = null;
|
||||
assert_equals(transition.transitionProperty, 'left');
|
||||
assert_equals(transition.playState, 'finished');
|
||||
assert_equals(window.getComputedStyle(div).left, '100px');
|
||||
});
|
||||
}, 'Test for removing a transition effect');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.left = '0px';
|
||||
|
||||
div.style.transition = 'left 100s';
|
||||
flushComputedStyle(div);
|
||||
div.style.left = '100px';
|
||||
|
||||
var transition = div.getAnimations()[0];
|
||||
return transition.ready.then(function() {
|
||||
transition.currentTime = 50 * MS_PER_SEC;
|
||||
transition.effect = new KeyframeEffect(div,
|
||||
{ marginLeft: [ '0px' , '100px'] },
|
||||
100 * MS_PER_SEC);
|
||||
assert_equals(transition.transitionProperty, 'left');
|
||||
assert_equals(transition.playState, 'running');
|
||||
assert_equals(window.getComputedStyle(div).left, '100px');
|
||||
assert_equals(window.getComputedStyle(div).marginLeft, '50px');
|
||||
});
|
||||
}, 'Test for replacing the transition effect by a new keyframe effect');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.left = '0px';
|
||||
div.style.width = '0px';
|
||||
|
||||
div.style.transition = 'left 100s';
|
||||
flushComputedStyle(div);
|
||||
div.style.left = '100px';
|
||||
|
||||
var transition = div.getAnimations()[0];
|
||||
return transition.ready.then(function() {
|
||||
transition.currentTime = 50 * MS_PER_SEC;
|
||||
transition.effect = new KeyframeEffect(div,
|
||||
{ marginLeft: [ '0px' , '100px'] },
|
||||
20 * MS_PER_SEC);
|
||||
assert_equals(transition.playState, 'finished');
|
||||
});
|
||||
}, 'Test for setting a new keyframe effect with a shorter duration');
|
||||
|
||||
promise_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.left = '0px';
|
||||
div.style.width = '0px';
|
||||
|
||||
div.style.transition = 'left 100s';
|
||||
flushComputedStyle(div);
|
||||
div.style.left = '100px';
|
||||
|
||||
var transition = div.getAnimations()[0];
|
||||
assert_equals(transition.playState, 'pending');
|
||||
|
||||
transition.effect = new KeyframeEffect(div,
|
||||
{ marginLeft: [ '0px' , '100px'] },
|
||||
100 * MS_PER_SEC);
|
||||
assert_equals(transition.transitionProperty, 'left');
|
||||
assert_equals(transition.playState, 'pending');
|
||||
|
||||
return transition.ready.then(function() {
|
||||
assert_equals(transition.playState, 'running');
|
||||
});
|
||||
}, 'Test for setting a new keyframe effect to a pending transition');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
14
dom/animation/test/css-transitions/test_setting-effect.html
Normal file
14
dom/animation/test/css-transitions/test_setting-effect.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src='/resources/testharness.js'></script>
|
||||
<script src='/resources/testharnessreport.js'></script>
|
||||
<div id='log'></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ 'set': [['dom.animations-api.core.enabled', true]]},
|
||||
function() {
|
||||
window.open('file_setting-effect.html');
|
||||
});
|
||||
</script>
|
@ -34,6 +34,7 @@ support-files =
|
||||
css-transitions/file_element-get-animations.html
|
||||
css-transitions/file_keyframeeffect-getkeyframes.html
|
||||
css-transitions/file_pseudoElement-get-animations.html
|
||||
css-transitions/file_setting-effect.html
|
||||
document-timeline/file_document-timeline.html
|
||||
mozilla/file_cubic_bezier_limits.html
|
||||
mozilla/file_deferred_start.html
|
||||
@ -46,6 +47,7 @@ support-files =
|
||||
mozilla/file_underlying-discrete-value.html
|
||||
style/file_animation-seeking-with-current-time.html
|
||||
style/file_animation-seeking-with-start-time.html
|
||||
style/file_animation-setting-effect.html
|
||||
testcommon.js
|
||||
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
@ -81,6 +83,7 @@ skip-if = buildapp == 'mulet'
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-transitions/test_keyframeeffect-getkeyframes.html]
|
||||
[css-transitions/test_pseudoElement-get-animations.html]
|
||||
[css-transitions/test_setting-effect.html]
|
||||
[document-timeline/test_document-timeline.html]
|
||||
[document-timeline/test_request_animation_frame.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
@ -97,3 +100,4 @@ skip-if = (toolkit == 'gonk' && debug)
|
||||
[mozilla/test_underlying-discrete-value.html]
|
||||
[style/test_animation-seeking-with-current-time.html]
|
||||
[style/test_animation-seeking-with-start-time.html]
|
||||
[style/test_animation-setting-effect.html]
|
||||
|
125
dom/animation/test/style/file_animation-setting-effect.html
Normal file
125
dom/animation/test/style/file_animation-setting-effect.html
Normal file
@ -0,0 +1,125 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>Tests for setting effects by using Animation.effect</title>
|
||||
<script src='../testcommon.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type='text/javascript'>
|
||||
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
var target = addDiv(t);
|
||||
var anim = new Animation();
|
||||
anim.effect = new KeyframeEffectReadOnly(target,
|
||||
{ marginLeft: [ '0px', '100px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
anim.currentTime = 50 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '50px');
|
||||
}, 'After setting target effect on an animation with null effect, the ' +
|
||||
'animation still works');
|
||||
|
||||
test(function(t) {
|
||||
var target = addDiv(t);
|
||||
target.style.marginLeft = '10px';
|
||||
var anim = target.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
anim.currentTime = 50 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '50px');
|
||||
|
||||
anim.effect = null;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '10px');
|
||||
}, 'After setting null target effect, the computed style of the target ' +
|
||||
'element becomes the initial value');
|
||||
|
||||
test(function(t) {
|
||||
var target = addDiv(t);
|
||||
var animA = target.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
var animB = new Animation();
|
||||
animA.currentTime = 50 * MS_PER_SEC;
|
||||
animB.currentTime = 20 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '50px',
|
||||
'original computed style of the target element');
|
||||
|
||||
animB.effect = animA.effect;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '20px',
|
||||
'new computed style of the target element');
|
||||
}, 'After setting the target effect from an existing animation, the computed ' +
|
||||
'style of the target effect should reflect the time of the updated ' +
|
||||
'animation.');
|
||||
|
||||
test(function(t) {
|
||||
var target = addDiv(t);
|
||||
target.style.marginTop = '-10px';
|
||||
var animA = target.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
var animB = target.animate({ marginTop: [ '0px', '100px' ] },
|
||||
50 * MS_PER_SEC);
|
||||
animA.currentTime = 50 * MS_PER_SEC;
|
||||
animB.currentTime = 10 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '50px',
|
||||
'original margin-left of the target element');
|
||||
assert_equals(getComputedStyle(target).marginTop, '20px',
|
||||
'original margin-top of the target element');
|
||||
|
||||
animB.effect = animA.effect;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '10px',
|
||||
'new margin-left of the target element');
|
||||
assert_equals(getComputedStyle(target).marginTop, '-10px',
|
||||
'new margin-top of the target element');
|
||||
}, 'After setting target effect with an animation to another animation which ' +
|
||||
'also has an target effect and both animation effects target to the same ' +
|
||||
'element, the computed style of this element should reflect the time and ' +
|
||||
'effect of the animation that was set');
|
||||
|
||||
test(function(t) {
|
||||
var targetA = addDiv(t);
|
||||
var targetB = addDiv(t);
|
||||
targetB.style.marginLeft = '-10px';
|
||||
var animA = targetA.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
var animB = targetB.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
50 * MS_PER_SEC);
|
||||
animA.currentTime = 50 * MS_PER_SEC;
|
||||
animB.currentTime = 10 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(targetA).marginLeft, '50px',
|
||||
'original margin-left of the first element');
|
||||
assert_equals(getComputedStyle(targetB).marginLeft, '20px',
|
||||
'original margin-left of the second element');
|
||||
|
||||
animB.effect = animA.effect;
|
||||
assert_equals(getComputedStyle(targetA).marginLeft, '10px',
|
||||
'new margin-left of the first element');
|
||||
assert_equals(getComputedStyle(targetB).marginLeft, '-10px',
|
||||
'new margin-left of the second element');
|
||||
}, 'After setting target effect with an animation to another animation which ' +
|
||||
'also has an target effect and these animation effects target to ' +
|
||||
'different elements, the computed styles of the two elements should ' +
|
||||
'reflect the time and effect of the animation that was set');
|
||||
|
||||
test(function(t) {
|
||||
var target = addDiv(t);
|
||||
var animA = target.animate({ marginLeft: [ '0px', '100px' ] },
|
||||
50 * MS_PER_SEC);
|
||||
var animB = target.animate({ marginTop: [ '0px', '50px' ] },
|
||||
100 * MS_PER_SEC);
|
||||
animA.currentTime = 20 * MS_PER_SEC;
|
||||
animB.currentTime = 30 * MS_PER_SEC;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '40px');
|
||||
assert_equals(getComputedStyle(target).marginTop, '15px');
|
||||
|
||||
var effectA = animA.effect;
|
||||
animA.effect = animB.effect;
|
||||
animB.effect = effectA;
|
||||
assert_equals(getComputedStyle(target).marginLeft, '60px');
|
||||
assert_equals(getComputedStyle(target).marginTop, '10px');
|
||||
}, 'After swapping effects of two playing animations, both animations are ' +
|
||||
'still running with the same current time');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
15
dom/animation/test/style/test_animation-setting-effect.html
Normal file
15
dom/animation/test/style/test_animation-setting-effect.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src='/resources/testharness.js'></script>
|
||||
<script src='/resources/testharnessreport.js'></script>
|
||||
<div id='log'></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ 'set': [['dom.animations-api.core.enabled', true]]},
|
||||
function() {
|
||||
window.open('file_animation-setting-effect.html');
|
||||
});
|
||||
</script>
|
||||
</html>
|
@ -1081,8 +1081,8 @@ class BlobImplMemoryDataOwnerMemoryReporter final
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
|
||||
nsISupports *aClosure, bool aAnonymize) override
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) override
|
||||
{
|
||||
typedef BlobImplMemory::DataOwner DataOwner;
|
||||
|
||||
@ -1113,7 +1113,7 @@ public:
|
||||
digestString.AppendPrintf("%02x", digest[i]);
|
||||
}
|
||||
|
||||
nsresult rv = aCallback->Callback(
|
||||
aHandleReport->Callback(
|
||||
/* process */ NS_LITERAL_CSTRING(""),
|
||||
nsPrintfCString(
|
||||
"explicit/dom/memory-file-data/large/file(length=%llu, sha1=%s)",
|
||||
@ -1126,13 +1126,12 @@ public:
|
||||
"that is, an N-byte memory file may take up more than N bytes of "
|
||||
"memory.",
|
||||
owner->mLength, digestString.get()),
|
||||
aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aData);
|
||||
}
|
||||
}
|
||||
|
||||
if (smallObjectsTotal > 0) {
|
||||
nsresult rv = aCallback->Callback(
|
||||
aHandleReport->Callback(
|
||||
/* process */ NS_LITERAL_CSTRING(""),
|
||||
NS_LITERAL_CSTRING("explicit/dom/memory-file-data/small"),
|
||||
KIND_HEAP, UNITS_BYTES, smallObjectsTotal,
|
||||
@ -1141,8 +1140,7 @@ public:
|
||||
"Note that the allocator may round up a memory file's length -- "
|
||||
"that is, an N-byte memory file may take up more than N bytes of "
|
||||
"memory."),
|
||||
aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -436,6 +436,12 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
||||
imgStream = do_QueryInterface(aEncoder);
|
||||
}
|
||||
} else {
|
||||
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
|
||||
|
@ -119,7 +119,6 @@ DOM4_MSG_DEF(BtAuthRejectedError, "Authentication rejected", NS_ERROR_DOM_BLUET
|
||||
/* Web Animations errors */
|
||||
|
||||
DOM4_MSG_DEF(NotSupportedError, "Animation to or from an underlying value is not yet supported.", NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR)
|
||||
DOM4_MSG_DEF(NotSupportedError, "Animation with no effect is not yet supported.", NS_ERROR_DOM_ANIM_NO_EFFECT_ERR)
|
||||
|
||||
/* common global codes (from nsError.h) */
|
||||
|
||||
|
@ -1554,6 +1554,34 @@ nsAttrValue::ParseIntWithBounds(const nsAString& aString,
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsAttrValue::ParseIntWithFallback(const nsAString& aString, int32_t aDefault,
|
||||
int32_t aMax)
|
||||
{
|
||||
ResetIfSet();
|
||||
|
||||
nsContentUtils::ParseHTMLIntegerResultFlags result;
|
||||
int32_t val = nsContentUtils::ParseHTMLInteger(aString, &result);
|
||||
bool nonStrict = false;
|
||||
if ((result & nsContentUtils::eParseHTMLInteger_Error) || val < 1) {
|
||||
val = aDefault;
|
||||
nonStrict = true;
|
||||
}
|
||||
|
||||
if (val > aMax) {
|
||||
val = aMax;
|
||||
nonStrict = true;
|
||||
}
|
||||
|
||||
if ((result & nsContentUtils::eParseHTMLInteger_IsPercent) ||
|
||||
(result & nsContentUtils::eParseHTMLInteger_NonStandard) ||
|
||||
(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)) {
|
||||
nonStrict = true;
|
||||
}
|
||||
|
||||
SetIntValueAndType(val, eInteger, nonStrict ? &aString : nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
nsAttrValue::ParseNonNegativeIntValue(const nsAString& aString)
|
||||
{
|
||||
|
@ -321,6 +321,18 @@ public:
|
||||
bool ParseIntWithBounds(const nsAString& aString, int32_t aMin,
|
||||
int32_t aMax = INT32_MAX);
|
||||
|
||||
/**
|
||||
* Parse a string value into an integer with a fallback for invalid values.
|
||||
* Also allows clamping to a maximum value to support col/colgroup.span (this
|
||||
* is not per spec right now).
|
||||
*
|
||||
* @param aString the string to parse
|
||||
* @param aDefault the default value
|
||||
* @param aMax the maximum value (if value is greater it will be clamped)
|
||||
*/
|
||||
void ParseIntWithFallback(const nsAString& aString, int32_t aDefault,
|
||||
int32_t aMax = INT32_MAX);
|
||||
|
||||
/**
|
||||
* Parse a string value into a non-negative integer.
|
||||
* This method follows the rules for parsing non-negative integer from:
|
||||
|
@ -396,10 +396,12 @@ public:
|
||||
MallocSizeOf)
|
||||
: 0;
|
||||
|
||||
return MOZ_COLLECT_REPORT(
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
|
||||
amount,
|
||||
"Memory used by the event listener manager's hash table.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -390,12 +390,18 @@ void
|
||||
nsAnimationReceiver::RecordAnimationMutation(Animation* aAnimation,
|
||||
AnimationMutation aMutationType)
|
||||
{
|
||||
mozilla::dom::KeyframeEffectReadOnly* effect = aAnimation->GetEffect();
|
||||
mozilla::dom::AnimationEffectReadOnly* effect = aAnimation->GetEffect();
|
||||
if (!effect) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> animationTarget = effect->GetTarget();
|
||||
mozilla::dom::KeyframeEffectReadOnly* keyframeEffect =
|
||||
effect->AsKeyframeEffect();
|
||||
if (!keyframeEffect) {
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<NonOwningAnimationTarget> animationTarget = keyframeEffect->GetTarget();
|
||||
if (!animationTarget) {
|
||||
return;
|
||||
}
|
||||
|
@ -3637,13 +3637,18 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement,
|
||||
FrameLayerBuilder::GetDedicatedLayer(frame,
|
||||
nsDisplayItem::TYPE_OPACITY);
|
||||
if (layer) {
|
||||
float value;
|
||||
ShadowLayerForwarder* forwarder = layer->Manager()->AsShadowForwarder();
|
||||
if (forwarder && forwarder->HasShadowManager()) {
|
||||
forwarder->GetShadowManager()->SendGetOpacity(
|
||||
layer->AsShadowableLayer()->GetShadow(), &value);
|
||||
cssValue = new nsROCSSPrimitiveValue;
|
||||
cssValue->SetNumber(value);
|
||||
float value;
|
||||
bool hadAnimatedOpacity;
|
||||
forwarder->GetShadowManager()->SendGetAnimationOpacity(
|
||||
layer->AsShadowableLayer()->GetShadow(),
|
||||
&value, &hadAnimatedOpacity);
|
||||
|
||||
if (hadAnimatedOpacity) {
|
||||
cssValue = new nsROCSSPrimitiveValue;
|
||||
cssValue->SetNumber(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (aProperty.EqualsLiteral("transform")) {
|
||||
|
@ -1578,20 +1578,18 @@ MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
|
||||
}
|
||||
}
|
||||
|
||||
static nsresult
|
||||
static void
|
||||
ReportReferentCount(const char* aManagerType,
|
||||
const MessageManagerReferentCount& aReferentCount,
|
||||
nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData)
|
||||
{
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = aCb->Callback(EmptyCString(), _path, \
|
||||
nsIMemoryReporter::KIND_OTHER, \
|
||||
nsIMemoryReporter::UNITS_COUNT, _amount, \
|
||||
_desc, aClosure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
aHandleReport->Callback(EmptyCString(), _path, \
|
||||
nsIMemoryReporter::KIND_OTHER, \
|
||||
nsIMemoryReporter::UNITS_COUNT, _amount, \
|
||||
_desc, aData); \
|
||||
} while (0)
|
||||
|
||||
REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
|
||||
@ -1622,16 +1620,12 @@ ReportReferentCount(const char* aManagerType,
|
||||
}
|
||||
|
||||
#undef REPORT
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure, bool aAnonymize)
|
||||
MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsCOMPtr<nsIMessageBroadcaster> globalmm =
|
||||
do_GetService("@mozilla.org/globalmessagemanager;1");
|
||||
@ -1640,23 +1634,20 @@ MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
static_cast<nsFrameMessageManager*>(globalmm.get());
|
||||
MessageManagerReferentCount count;
|
||||
CountReferents(mm, &count);
|
||||
rv = ReportReferentCount("global-manager", count, aCb, aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ReportReferentCount("global-manager", count, aHandleReport, aData);
|
||||
}
|
||||
}
|
||||
|
||||
if (nsFrameMessageManager::sParentProcessManager) {
|
||||
MessageManagerReferentCount count;
|
||||
CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
|
||||
rv = ReportReferentCount("parent-process-manager", count, aCb, aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ReportReferentCount("parent-process-manager", count, aHandleReport, aData);
|
||||
}
|
||||
|
||||
if (nsFrameMessageManager::sChildProcessManager) {
|
||||
MessageManagerReferentCount count;
|
||||
CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
|
||||
rv = ReportReferentCount("child-process-manager", count, aCb, aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
ReportReferentCount("child-process-manager", count, aHandleReport, aData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -169,11 +169,13 @@ class HostObjectURLsReporter final : public nsIMemoryReporter
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) override
|
||||
{
|
||||
return MOZ_COLLECT_REPORT(
|
||||
MOZ_COLLECT_REPORT(
|
||||
"host-object-urls", KIND_OTHER, UNITS_COUNT,
|
||||
gDataTable ? gDataTable->Count() : 0,
|
||||
"The number of host objects stored for access via URLs "
|
||||
"(e.g. blobs passed to URL.createObjectURL).");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2149,7 +2149,7 @@ DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress, const JS::GCDescrip
|
||||
sHasRunGC = true;
|
||||
nsJSContext::MaybePokeCC();
|
||||
|
||||
if (aDesc.isCompartment_) {
|
||||
if (aDesc.isZone_) {
|
||||
if (!sFullGCTimer && !sShuttingDown) {
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
|
||||
sFullGCTimer->InitWithNamedFuncCallback(FullGCTimerFired,
|
||||
@ -2289,13 +2289,13 @@ SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
|
||||
static void
|
||||
SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
|
||||
{
|
||||
bool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
|
||||
bool enableZoneGC = Preferences::GetBool("javascript.options.mem.gc_per_zone");
|
||||
bool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
|
||||
JSGCMode mode;
|
||||
if (enableIncrementalGC) {
|
||||
mode = JSGC_MODE_INCREMENTAL;
|
||||
} else if (enableCompartmentGC) {
|
||||
mode = JSGC_MODE_COMPARTMENT;
|
||||
} else if (enableZoneGC) {
|
||||
mode = JSGC_MODE_ZONE;
|
||||
} else {
|
||||
mode = JSGC_MODE_GLOBAL;
|
||||
}
|
||||
@ -2486,7 +2486,7 @@ nsJSContext::EnsureStatics()
|
||||
"javascript.options.mem.max");
|
||||
|
||||
Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
|
||||
"javascript.options.mem.gc_per_compartment");
|
||||
"javascript.options.mem.gc_per_zone");
|
||||
|
||||
Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
|
||||
"javascript.options.mem.gc_incremental");
|
||||
|
@ -231,8 +231,11 @@ nsNodeUtils::ContentRemoved(nsINode* aContainer,
|
||||
Maybe<NonOwningAnimationTarget>
|
||||
nsNodeUtils::GetTargetForAnimation(const Animation* aAnimation)
|
||||
{
|
||||
KeyframeEffectReadOnly* effect = aAnimation->GetEffect();
|
||||
return effect ? effect->GetTarget() : Nothing();
|
||||
AnimationEffectReadOnly* effect = aAnimation->GetEffect();
|
||||
if (!effect || !effect->AsKeyframeEffect()) {
|
||||
return Nothing();
|
||||
}
|
||||
return effect->AsKeyframeEffect()->GetTarget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -412,10 +412,12 @@ NS_IMETHODIMP
|
||||
nsScriptNameSpaceManager::CollectReports(
|
||||
nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
return MOZ_COLLECT_REPORT(
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES,
|
||||
SizeOfIncludingThis(ScriptNameSpaceManagerMallocSizeOf),
|
||||
"Memory used for the script namespace manager.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@ -192,55 +192,55 @@ MOZ_DEFINE_MALLOC_SIZE_OF(WindowsMallocSizeOf)
|
||||
// The key is the window ID.
|
||||
typedef nsDataHashtable<nsUint64HashKey, nsCString> WindowPaths;
|
||||
|
||||
static nsresult
|
||||
static void
|
||||
ReportAmount(const nsCString& aBasePath, const char* aPathTail,
|
||||
size_t aAmount, const nsCString& aDescription,
|
||||
uint32_t aKind, uint32_t aUnits,
|
||||
nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData)
|
||||
{
|
||||
if (aAmount == 0) {
|
||||
return NS_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString path(aBasePath);
|
||||
path += aPathTail;
|
||||
|
||||
return aCb->Callback(EmptyCString(), path, aKind, aUnits,
|
||||
aAmount, aDescription, aClosure);
|
||||
aHandleReport->Callback(
|
||||
EmptyCString(), path, aKind, aUnits, aAmount, aDescription, aData);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
static void
|
||||
ReportSize(const nsCString& aBasePath, const char* aPathTail,
|
||||
size_t aAmount, const nsCString& aDescription,
|
||||
nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData)
|
||||
{
|
||||
return ReportAmount(aBasePath, aPathTail, aAmount, aDescription,
|
||||
nsIMemoryReporter::KIND_HEAP,
|
||||
nsIMemoryReporter::UNITS_BYTES, aCb, aClosure);
|
||||
ReportAmount(aBasePath, aPathTail, aAmount, aDescription,
|
||||
nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
|
||||
aHandleReport, aData);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
static void
|
||||
ReportCount(const nsCString& aBasePath, const char* aPathTail,
|
||||
size_t aAmount, const nsCString& aDescription,
|
||||
nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData)
|
||||
{
|
||||
return ReportAmount(aBasePath, aPathTail, aAmount, aDescription,
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
nsIMemoryReporter::UNITS_COUNT, aCb, aClosure);
|
||||
ReportAmount(aBasePath, aPathTail, aAmount, aDescription,
|
||||
nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
|
||||
aHandleReport, aData);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
static void
|
||||
CollectWindowReports(nsGlobalWindow *aWindow,
|
||||
amIAddonManager *addonManager,
|
||||
nsWindowSizes *aWindowTotalSizes,
|
||||
nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
|
||||
WindowPaths *aWindowPaths,
|
||||
WindowPaths *aTopWindowPaths,
|
||||
nsIMemoryReporterCallback *aCb,
|
||||
nsISupports *aClosure,
|
||||
nsIHandleReportCallback *aHandleReport,
|
||||
nsISupports *aData,
|
||||
bool aAnonymize)
|
||||
{
|
||||
nsAutoCString windowPath("explicit/");
|
||||
@ -302,19 +302,13 @@ CollectWindowReports(nsGlobalWindow *aWindow,
|
||||
// Remember the path for later.
|
||||
aWindowPaths->Put(aWindow->WindowID(), windowPath);
|
||||
|
||||
#define REPORT_SIZE(_pathTail, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv = ReportSize(windowPath, _pathTail, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aCb, aClosure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
#define REPORT_SIZE(_pathTail, _amount, _desc) \
|
||||
ReportSize(windowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
|
||||
aHandleReport, aData);
|
||||
|
||||
#define REPORT_COUNT(_pathTail, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv = ReportCount(censusWindowPath, _pathTail, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aCb, aClosure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
#define REPORT_COUNT(_pathTail, _amount, _desc) \
|
||||
ReportCount(censusWindowPath, _pathTail, _amount, NS_LITERAL_CSTRING(_desc), \
|
||||
aHandleReport, aData);
|
||||
|
||||
nsWindowSizes windowSizes(WindowsMallocSizeOf);
|
||||
aWindow->AddSizeOfIncludingThis(&windowSizes);
|
||||
@ -439,15 +433,13 @@ CollectWindowReports(nsGlobalWindow *aWindow,
|
||||
|
||||
#undef REPORT_SIZE
|
||||
#undef REPORT_COUNT
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
typedef nsTArray< RefPtr<nsGlobalWindow> > WindowArray;
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure, bool aAnonymize)
|
||||
nsWindowMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
nsGlobalWindow::WindowByIdTable* windowsById =
|
||||
nsGlobalWindow::GetWindowsTable();
|
||||
@ -460,11 +452,10 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
windows.AppendElement(iter.Data());
|
||||
}
|
||||
|
||||
// Get the IDs of all the "ghost" windows, and call aCb->Callback() for each
|
||||
// one.
|
||||
// Get the IDs of all the "ghost" windows, and call aHandleReport->Callback()
|
||||
// for each one.
|
||||
nsTHashtable<nsUint64HashKey> ghostWindows;
|
||||
CheckForGhostWindows(&ghostWindows);
|
||||
nsresult rv = NS_OK;
|
||||
for (auto iter = ghostWindows.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
nsGlobalWindow::WindowByIdTable* windowsById =
|
||||
nsGlobalWindow::GetWindowsTable();
|
||||
@ -483,19 +474,15 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
path.AppendLiteral("ghost-windows/");
|
||||
AppendWindowURI(window, path, aAnonymize);
|
||||
|
||||
nsresult callbackRv = aCb->Callback(
|
||||
aHandleReport->Callback(
|
||||
/* process = */ EmptyCString(),
|
||||
path,
|
||||
nsIMemoryReporter::KIND_OTHER,
|
||||
nsIMemoryReporter::UNITS_COUNT,
|
||||
/* amount = */ 1,
|
||||
/* description = */ NS_LITERAL_CSTRING("A ghost window."),
|
||||
aClosure);
|
||||
if (NS_FAILED(callbackRv) && NS_SUCCEEDED(rv)) {
|
||||
rv = callbackRv;
|
||||
}
|
||||
aData);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
WindowPaths windowPaths;
|
||||
WindowPaths topWindowPaths;
|
||||
@ -508,27 +495,21 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
addonManager = do_GetService("@mozilla.org/addons/integration;1");
|
||||
}
|
||||
for (uint32_t i = 0; i < windows.Length(); i++) {
|
||||
rv = CollectWindowReports(windows[i], addonManager,
|
||||
&windowTotalSizes, &ghostWindows,
|
||||
&windowPaths, &topWindowPaths, aCb,
|
||||
aClosure, aAnonymize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CollectWindowReports(windows[i], addonManager,
|
||||
&windowTotalSizes, &ghostWindows,
|
||||
&windowPaths, &topWindowPaths, aHandleReport,
|
||||
aData, aAnonymize);
|
||||
}
|
||||
|
||||
// Report JS memory usage. We do this from here because the JS memory
|
||||
// reporter needs to be passed |windowPaths|.
|
||||
rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
|
||||
aCb, aClosure, aAnonymize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
|
||||
aHandleReport, aData, aAnonymize);
|
||||
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
KIND_OTHER, UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aClosure); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
KIND_OTHER, UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aData);
|
||||
|
||||
REPORT("window-objects/dom/element-nodes", windowTotalSizes.mDOMElementNodesSize,
|
||||
"This is the sum of all windows' 'dom/element-nodes' numbers.");
|
||||
@ -645,7 +626,7 @@ nsWindowMemoryReporter::ObserveDOMWindowDetached(nsGlobalWindow* aWindow)
|
||||
|
||||
// static
|
||||
void
|
||||
nsWindowMemoryReporter::CheckTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
nsWindowMemoryReporter::CheckTimerFired(nsITimer* aTimer, void* aData)
|
||||
{
|
||||
if (sWindowReporter) {
|
||||
MOZ_ASSERT(!sWindowReporter->mCycleCollectorIsRunning);
|
||||
|
@ -180,7 +180,7 @@ private:
|
||||
CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
|
||||
bool aAnonymize) override
|
||||
{
|
||||
return MOZ_COLLECT_REPORT(
|
||||
MOZ_COLLECT_REPORT(
|
||||
"ghost-windows", KIND_OTHER, UNITS_COUNT, DistinguishedAmount(),
|
||||
"The number of ghost windows present (the number of nodes underneath "
|
||||
"explicit/window-objects/top(none)/ghost, modulo race conditions). A ghost "
|
||||
@ -190,6 +190,8 @@ private:
|
||||
"about:memory's minimize memory usage button.\n\n"
|
||||
"Ghost windows can happen legitimately, but they are often indicative of "
|
||||
"leaks in the browser or add-ons.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=391728
|
||||
<head>
|
||||
<title>Test for Bug 391728</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -7,7 +7,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=682305
|
||||
<title>XMLHttpRequest send and channel implemented in JS</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -7,7 +7,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=765993
|
||||
<title>Test for Bug 765993</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -123,10 +123,11 @@ public:
|
||||
sWebIDLGlobalNames ?
|
||||
sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
|
||||
|
||||
return MOZ_COLLECT_REPORT("explicit/dom/webidl-globalnames", KIND_HEAP,
|
||||
UNITS_BYTES, amount,
|
||||
"Memory used by the hash table for WebIDL's "
|
||||
"global names.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/dom/webidl-globalnames", KIND_HEAP, UNITS_BYTES, amount,
|
||||
"Memory used by the hash table for WebIDL's global names.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -178,11 +178,12 @@ public:
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) override
|
||||
{
|
||||
return MOZ_COLLECT_REPORT(
|
||||
"canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
|
||||
gCanvasAzureMemoryUsed,
|
||||
MOZ_COLLECT_REPORT(
|
||||
"canvas-2d-pixels", KIND_OTHER, UNITS_BYTES, gCanvasAzureMemoryUsed,
|
||||
"Memory used by 2D canvases. Each canvas requires "
|
||||
"(width * height * 4) bytes.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -17,76 +17,64 @@
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
|
||||
nsISupports* data, bool)
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool)
|
||||
{
|
||||
#define REPORT(_path, _kind, _units, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = handleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
_kind, _units, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), data); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-texture-memory", KIND_OTHER, UNITS_BYTES, GetTextureMemoryUsed(),
|
||||
"Memory used by WebGL textures. The OpenGL implementation is free to "
|
||||
"store these textures in either video memory or main memory. This "
|
||||
"measurement is only a lower bound, actual memory usage may be higher "
|
||||
"for example if the storage is strided.");
|
||||
|
||||
REPORT("webgl-texture-memory",
|
||||
KIND_OTHER, UNITS_BYTES, GetTextureMemoryUsed(),
|
||||
"Memory used by WebGL textures.The OpenGL"
|
||||
" implementation is free to store these textures in either video"
|
||||
" memory or main memory. This measurement is only a lower bound,"
|
||||
" actual memory usage may be higher for example if the storage"
|
||||
" is strided.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-texture-count", KIND_OTHER, UNITS_COUNT, GetTextureCount(),
|
||||
"Number of WebGL textures.");
|
||||
|
||||
REPORT("webgl-texture-count",
|
||||
KIND_OTHER, UNITS_COUNT, GetTextureCount(),
|
||||
"Number of WebGL textures.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-buffer-memory", KIND_OTHER, UNITS_BYTES, GetBufferMemoryUsed(),
|
||||
"Memory used by WebGL buffers. The OpenGL implementation is free to "
|
||||
"store these buffers in either video memory or main memory. This "
|
||||
"measurement is only a lower bound, actual memory usage may be higher "
|
||||
"for example if the storage is strided.");
|
||||
|
||||
REPORT("webgl-buffer-memory",
|
||||
KIND_OTHER, UNITS_BYTES, GetBufferMemoryUsed(),
|
||||
"Memory used by WebGL buffers. The OpenGL"
|
||||
" implementation is free to store these buffers in either video"
|
||||
" memory or main memory. This measurement is only a lower bound,"
|
||||
" actual memory usage may be higher for example if the storage"
|
||||
" is strided.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/webgl/buffer-cache-memory", KIND_HEAP, UNITS_BYTES,
|
||||
GetBufferCacheMemoryUsed(),
|
||||
"Memory used by WebGL buffer caches. The WebGL implementation caches "
|
||||
"the contents of element array buffers only. This adds up with the "
|
||||
"'webgl-buffer-memory' value, but contrary to it, this one represents "
|
||||
"bytes on the heap, not managed by OpenGL.");
|
||||
|
||||
REPORT("explicit/webgl/buffer-cache-memory",
|
||||
KIND_HEAP, UNITS_BYTES, GetBufferCacheMemoryUsed(),
|
||||
"Memory used by WebGL buffer caches. The WebGL"
|
||||
" implementation caches the contents of element array buffers"
|
||||
" only.This adds up with the webgl-buffer-memory value, but"
|
||||
" contrary to it, this one represents bytes on the heap,"
|
||||
" not managed by OpenGL.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-buffer-count", KIND_OTHER, UNITS_COUNT, GetBufferCount(),
|
||||
"Number of WebGL buffers.");
|
||||
|
||||
REPORT("webgl-buffer-count",
|
||||
KIND_OTHER, UNITS_COUNT, GetBufferCount(),
|
||||
"Number of WebGL buffers.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-renderbuffer-memory", KIND_OTHER, UNITS_BYTES,
|
||||
GetRenderbufferMemoryUsed(),
|
||||
"Memory used by WebGL renderbuffers. The OpenGL implementation is free "
|
||||
"to store these renderbuffers in either video memory or main memory. "
|
||||
"This measurement is only a lower bound, actual memory usage may be "
|
||||
"higher, for example if the storage is strided.");
|
||||
|
||||
REPORT("webgl-renderbuffer-memory",
|
||||
KIND_OTHER, UNITS_BYTES, GetRenderbufferMemoryUsed(),
|
||||
"Memory used by WebGL renderbuffers. The OpenGL"
|
||||
" implementation is free to store these renderbuffers in either"
|
||||
" video memory or main memory. This measurement is only a lower"
|
||||
" bound, actual memory usage may be higher for example if the"
|
||||
" storage is strided.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-renderbuffer-count", KIND_OTHER, UNITS_COUNT,
|
||||
GetRenderbufferCount(),
|
||||
"Number of WebGL renderbuffers.");
|
||||
|
||||
REPORT("webgl-renderbuffer-count",
|
||||
KIND_OTHER, UNITS_COUNT, GetRenderbufferCount(),
|
||||
"Number of WebGL renderbuffers.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"explicit/webgl/shader", KIND_HEAP, UNITS_BYTES, GetShaderSize(),
|
||||
"Combined size of WebGL shader ASCII sources and translation logs "
|
||||
"cached on the heap.");
|
||||
|
||||
REPORT("explicit/webgl/shader",
|
||||
KIND_HEAP, UNITS_BYTES, GetShaderSize(),
|
||||
"Combined size of WebGL shader ASCII sources and translation"
|
||||
" logs cached on the heap.");
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-shader-count", KIND_OTHER, UNITS_COUNT, GetShaderCount(),
|
||||
"Number of WebGL shaders.");
|
||||
|
||||
REPORT("webgl-shader-count",
|
||||
KIND_OTHER, UNITS_COUNT, GetShaderCount(),
|
||||
"Number of WebGL shaders.");
|
||||
|
||||
REPORT("webgl-context-count",
|
||||
KIND_OTHER, UNITS_COUNT, GetContextCount(),
|
||||
"Number of WebGL contexts.");
|
||||
|
||||
#undef REPORT
|
||||
MOZ_COLLECT_REPORT(
|
||||
"webgl-context-count", KIND_OTHER, UNITS_COUNT, GetContextCount(),
|
||||
"Number of WebGL contexts.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -102,6 +102,14 @@ CaptureStreamTestHelper.prototype = {
|
||||
return px.some((ch, i) => Math.abs(ch - refColor.data[i]) > threshold);
|
||||
},
|
||||
|
||||
/*
|
||||
* Behaves like isPixelNot but ignores the alpha channel.
|
||||
*/
|
||||
isOpaquePixelNot: function(px, refColor, threshold) {
|
||||
px[3] = refColor.data[3];
|
||||
return h.isPixelNot(px, refColor, threshold);
|
||||
},
|
||||
|
||||
/*
|
||||
* Returns a promise that resolves when the provided function |test|
|
||||
* returns true.
|
||||
|
@ -16,9 +16,5 @@ EXTRA_COMPONENTS += [
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'fallback/ContactDB.jsm',
|
||||
'fallback/ContactService.jsm'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
|
||||
EXTRA_JS_MODULES += [
|
||||
'fallback/ContactService.jsm'
|
||||
]
|
||||
|
@ -76,6 +76,14 @@
|
||||
assert_equals(event.isPrimary, true, "isPrimary should be true");
|
||||
}, event.type + ".isPrimary attribute is correct.");
|
||||
|
||||
// Test width and height
|
||||
test(function () {
|
||||
assert_equals(event.width, 1, "width of mouse should be 1");
|
||||
}, event.type + ".width attribute is correct.");
|
||||
test(function () {
|
||||
assert_equals(event.height, 1, "height of mouse should be 1");
|
||||
}, event.type + ".height attribute is correct.");
|
||||
|
||||
check_PointerEvent(event);
|
||||
detected_eventTypes[event.type] = true;
|
||||
if (Object.keys(detected_eventTypes).length == eventList.length)
|
||||
|
@ -7,7 +7,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=930374
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 930374</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
|
@ -686,8 +686,10 @@ HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<MediaStreamTrack> track =
|
||||
stream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO,
|
||||
new BasicUnstoppableTrackSource(principal));
|
||||
stream->AddTrackInternal(track);
|
||||
|
||||
rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -798,8 +798,7 @@ HTMLMediaElement::SetVisible(bool aVisible)
|
||||
return;
|
||||
}
|
||||
|
||||
mDecoder->NotifyOwnerActivityChanged(aVisible);
|
||||
|
||||
mDecoder->SetForcedHidden(!aVisible);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
@ -939,7 +938,6 @@ void HTMLMediaElement::AbortExistingLoads()
|
||||
// We need to remove StreamSizeListener before VideoTracks get emptied.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
@ -1241,6 +1239,7 @@ void HTMLMediaElement::NotifyLoadError()
|
||||
|
||||
void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
|
||||
{
|
||||
MOZ_ASSERT(aTrack);
|
||||
if (!aTrack) {
|
||||
return;
|
||||
}
|
||||
@ -1248,19 +1247,137 @@ void HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
|
||||
nsString id;
|
||||
aTrack->GetId(id);
|
||||
|
||||
LOG(LogLevel::Debug, ("MediaElement %p MediaStreamTrack enabled with id %s",
|
||||
this, NS_ConvertUTF16toUTF8(id).get()));
|
||||
LOG(LogLevel::Debug, ("MediaElement %p %sTrack with id %s enabled",
|
||||
this, aTrack->AsAudioTrack() ? "Audio" : "Video",
|
||||
NS_ConvertUTF16toUTF8(id).get()));
|
||||
#endif
|
||||
|
||||
// TODO: We are dealing with single audio track and video track for now.
|
||||
if (AudioTrack* track = aTrack->AsAudioTrack()) {
|
||||
if (!track->Enabled()) {
|
||||
SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
|
||||
} else {
|
||||
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_TRACK);
|
||||
MOZ_ASSERT((aTrack->AsAudioTrack() && aTrack->AsAudioTrack()->Enabled()) ||
|
||||
(aTrack->AsVideoTrack() && aTrack->AsVideoTrack()->Selected()));
|
||||
|
||||
if (aTrack->AsAudioTrack()) {
|
||||
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_TRACK);
|
||||
} else if (aTrack->AsVideoTrack()) {
|
||||
if (!IsVideo()) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
} else if (VideoTrack* track = aTrack->AsVideoTrack()) {
|
||||
mDisableVideo = !track->Selected();
|
||||
mDisableVideo = false;
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Unknown track type");
|
||||
}
|
||||
|
||||
if (mSrcStream) {
|
||||
if (aTrack->AsVideoTrack()) {
|
||||
MOZ_ASSERT(!mSelectedVideoStreamTrack);
|
||||
MOZ_ASSERT(!mMediaStreamSizeListener);
|
||||
|
||||
mSelectedVideoStreamTrack = aTrack->AsVideoTrack()->GetVideoStreamTrack();
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (mSrcStreamIsPlaying && container) {
|
||||
mSelectedVideoStreamTrack->AddVideoOutput(container);
|
||||
}
|
||||
HTMLVideoElement* self = static_cast<HTMLVideoElement*>(this);
|
||||
if (self->VideoWidth() <= 1 && self->VideoHeight() <= 1) {
|
||||
// MediaInfo uses dummy values of 1 for width and height to
|
||||
// mark video as valid. We need a new stream size listener
|
||||
// if size is 0x0 or 1x1.
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
mSelectedVideoStreamTrack->AddDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (mReadyState == HAVE_NOTHING) {
|
||||
// No MediaStreamTracks are captured until we have metadata.
|
||||
return;
|
||||
}
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
if (aTrack->AsVideoTrack() && ms.mCapturingAudioOnly) {
|
||||
// If the output stream is for audio only we ignore video tracks.
|
||||
continue;
|
||||
}
|
||||
AddCaptureMediaTrackToOutputStream(aTrack, ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::NotifyMediaTrackDisabled(MediaTrack* aTrack)
|
||||
{
|
||||
MOZ_ASSERT(aTrack);
|
||||
if (!aTrack) {
|
||||
return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
nsString id;
|
||||
aTrack->GetId(id);
|
||||
|
||||
LOG(LogLevel::Debug, ("MediaElement %p %sTrack with id %s disabled",
|
||||
this, aTrack->AsAudioTrack() ? "Audio" : "Video",
|
||||
NS_ConvertUTF16toUTF8(id).get()));
|
||||
#endif
|
||||
|
||||
MOZ_ASSERT((!aTrack->AsAudioTrack() || !aTrack->AsAudioTrack()->Enabled()) &&
|
||||
(!aTrack->AsVideoTrack() || !aTrack->AsVideoTrack()->Selected()));
|
||||
|
||||
if (aTrack->AsAudioTrack()) {
|
||||
bool shouldMute = true;
|
||||
for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) {
|
||||
if ((*AudioTracks())[i]->Enabled()) {
|
||||
shouldMute = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shouldMute) {
|
||||
SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
|
||||
}
|
||||
} else if (aTrack->AsVideoTrack()) {
|
||||
if (mSrcStream) {
|
||||
MOZ_ASSERT(mSelectedVideoStreamTrack);
|
||||
if (mSelectedVideoStreamTrack && mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (mSrcStreamIsPlaying && container) {
|
||||
mSelectedVideoStreamTrack->RemoveVideoOutput(container);
|
||||
}
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
if (ms.mCapturingDecoder) {
|
||||
MOZ_ASSERT(!ms.mCapturingMediaStream);
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(ms.mCapturingMediaStream);
|
||||
for (int32_t i = ms.mTrackPorts.Length() - 1; i >= 0; --i) {
|
||||
if (ms.mTrackPorts[i].first() == aTrack->GetId()) {
|
||||
// The source of this track just ended. Force-notify that it ended.
|
||||
// If we bounce it to the MediaStreamGraph it might not be picked up,
|
||||
// for instance if the MediaInputPort was destroyed in the same
|
||||
// iteration as it was added.
|
||||
MediaStreamTrack* outputTrack = ms.mStream->FindOwnedDOMTrack(
|
||||
ms.mTrackPorts[i].second()->GetDestination(),
|
||||
ms.mTrackPorts[i].second()->GetDestinationTrackId());
|
||||
MOZ_ASSERT(outputTrack);
|
||||
if (outputTrack) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod(outputTrack, &MediaStreamTrack::NotifyEnded));
|
||||
}
|
||||
|
||||
ms.mTrackPorts[i].second()->Destroy();
|
||||
ms.mTrackPorts.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
for (auto pair : ms.mTrackPorts) {
|
||||
MOZ_ASSERT(pair.first() != aTrack->GetId(),
|
||||
"The same MediaTrack was forwarded to the output stream more than once. This shouldn't happen.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1272,6 +1389,8 @@ void HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
|
||||
|
||||
LOG(LogLevel::Debug, ("MediaElement %p MediaStream tracks available", this));
|
||||
|
||||
mSrcStreamTracksAvailable = true;
|
||||
|
||||
bool videoHasChanged = IsVideo() && HasVideo() != !VideoTracks()->IsEmpty();
|
||||
|
||||
if (videoHasChanged) {
|
||||
@ -2094,16 +2213,91 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted(bool aMuted)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class HTMLMediaElement::CaptureStreamTrackSource :
|
||||
class HTMLMediaElement::StreamCaptureTrackSource :
|
||||
public MediaStreamTrackSource,
|
||||
public MediaStreamTrackSource::Sink
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StreamCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
|
||||
explicit StreamCaptureTrackSource(MediaStreamTrackSource* aCapturedTrackSource)
|
||||
: MediaStreamTrackSource(aCapturedTrackSource->GetPrincipal(),
|
||||
true,
|
||||
nsString())
|
||||
, mCapturedTrackSource(aCapturedTrackSource)
|
||||
{
|
||||
mCapturedTrackSource->RegisterSink(this);
|
||||
}
|
||||
|
||||
void Destroy() override
|
||||
{
|
||||
MOZ_ASSERT(mCapturedTrackSource);
|
||||
if (mCapturedTrackSource) {
|
||||
mCapturedTrackSource->UnregisterSink(this);
|
||||
}
|
||||
}
|
||||
|
||||
MediaSourceEnum GetMediaSource() const override
|
||||
{
|
||||
return MediaSourceEnum::Other;
|
||||
}
|
||||
|
||||
CORSMode GetCORSMode() const override
|
||||
{
|
||||
return mCapturedTrackSource->GetCORSMode();
|
||||
}
|
||||
|
||||
already_AddRefed<PledgeVoid>
|
||||
ApplyConstraints(nsPIDOMWindowInner* aWindow,
|
||||
const dom::MediaTrackConstraints& aConstraints) override
|
||||
{
|
||||
RefPtr<PledgeVoid> p = new PledgeVoid();
|
||||
p->Reject(new dom::MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
NS_LITERAL_STRING("")));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
void Stop() override
|
||||
{
|
||||
NS_ERROR("We're reporting remote=true to not be stoppable. "
|
||||
"Stop() should not be called.");
|
||||
}
|
||||
|
||||
void PrincipalChanged() override
|
||||
{
|
||||
mPrincipal = mCapturedTrackSource->GetPrincipal();
|
||||
MediaStreamTrackSource::PrincipalChanged();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~StreamCaptureTrackSource() {}
|
||||
|
||||
RefPtr<MediaStreamTrackSource> mCapturedTrackSource;
|
||||
};
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::StreamCaptureTrackSource)
|
||||
NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
|
||||
MediaStreamTrackSource,
|
||||
mCapturedTrackSource)
|
||||
|
||||
class HTMLMediaElement::DecoderCaptureTrackSource :
|
||||
public MediaStreamTrackSource,
|
||||
public DecoderPrincipalChangeObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CaptureStreamTrackSource,
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DecoderCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
|
||||
explicit CaptureStreamTrackSource(HTMLMediaElement* aElement)
|
||||
explicit DecoderCaptureTrackSource(HTMLMediaElement* aElement)
|
||||
: MediaStreamTrackSource(nsCOMPtr<nsIPrincipal>(aElement->GetCurrentPrincipal()).get(),
|
||||
true,
|
||||
nsString())
|
||||
@ -2157,20 +2351,20 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~CaptureStreamTrackSource()
|
||||
virtual ~DecoderCaptureTrackSource()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<HTMLMediaElement> mElement;
|
||||
};
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::CaptureStreamTrackSource,
|
||||
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::CaptureStreamTrackSource,
|
||||
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
|
||||
MediaStreamTrackSource)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSource)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource)
|
||||
NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSource,
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
|
||||
MediaStreamTrackSource,
|
||||
mElement)
|
||||
|
||||
@ -2188,13 +2382,18 @@ public:
|
||||
already_AddRefed<dom::MediaStreamTrackSource>
|
||||
GetMediaStreamTrackSource(TrackID aInputTrackID) override
|
||||
{
|
||||
if (mElement && mElement->mSrcStream) {
|
||||
NS_ERROR("Captured media element playing a stream adds tracks explicitly on main thread.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We can return a new source each time here, even for different streams,
|
||||
// since the sources don't keep any internal state and all of them call
|
||||
// through to the same HTMLMediaElement.
|
||||
// If this changes (after implementing Stop()?) we'll have to ensure we
|
||||
// return the same source for all requests to the same TrackID, and only
|
||||
// have one getter.
|
||||
return do_AddRef(new CaptureStreamTrackSource(mElement));
|
||||
return do_AddRef(new DecoderCaptureTrackSource(mElement));
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -2213,8 +2412,118 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGet
|
||||
MediaStreamTrackSourceGetter,
|
||||
mElement)
|
||||
|
||||
void
|
||||
HTMLMediaElement::SetCapturedOutputStreamsEnabled(bool aEnabled) {
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
if (ms.mCapturingDecoder) {
|
||||
MOZ_ASSERT(!ms.mCapturingMediaStream);
|
||||
continue;
|
||||
}
|
||||
for (auto pair : ms.mTrackPorts) {
|
||||
MediaStream* outputSource = ms.mStream->GetInputStream();
|
||||
if (!outputSource) {
|
||||
NS_ERROR("No output source stream");
|
||||
return;
|
||||
}
|
||||
|
||||
TrackID id = pair.second()->GetDestinationTrackId();
|
||||
outputSource->SetTrackEnabled(id, aEnabled ? DisabledTrackMode::ENABLED
|
||||
: DisabledTrackMode::SILENCE_FREEZE);
|
||||
|
||||
LOG(LogLevel::Debug,
|
||||
("%s track %d for captured MediaStream %p",
|
||||
aEnabled ? "Enabled" : "Disabled", id, ms.mStream.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AddCaptureMediaTrackToOutputStream(MediaTrack* aTrack,
|
||||
OutputMediaStream& aOutputStream,
|
||||
bool aAsyncAddtrack)
|
||||
{
|
||||
if (aOutputStream.mCapturingDecoder) {
|
||||
MOZ_ASSERT(!aOutputStream.mCapturingMediaStream);
|
||||
return;
|
||||
}
|
||||
aOutputStream.mCapturingMediaStream = true;
|
||||
|
||||
MediaStream* outputSource = aOutputStream.mStream->GetInputStream();
|
||||
if (!outputSource) {
|
||||
NS_ERROR("No output source stream");
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessedMediaStream* processedOutputSource =
|
||||
outputSource->AsProcessedStream();
|
||||
if (!processedOutputSource) {
|
||||
NS_ERROR("Input stream not a ProcessedMediaStream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aTrack) {
|
||||
MOZ_ASSERT(false, "Bad MediaTrack");
|
||||
return;
|
||||
}
|
||||
|
||||
MediaStreamTrack* inputTrack = mSrcStream->GetTrackById(aTrack->GetId());
|
||||
MOZ_ASSERT(inputTrack);
|
||||
if (!inputTrack) {
|
||||
NS_ERROR("Input track not found in source stream");
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
for (auto pair : aOutputStream.mTrackPorts) {
|
||||
MOZ_ASSERT(pair.first() != aTrack->GetId(),
|
||||
"Captured track already captured to output stream");
|
||||
}
|
||||
#endif
|
||||
|
||||
TrackID destinationTrackID = aOutputStream.mNextAvailableTrackID++;
|
||||
RefPtr<MediaStreamTrackSource> source =
|
||||
new StreamCaptureTrackSource(&inputTrack->GetSource());
|
||||
|
||||
MediaSegment::Type type = inputTrack->AsAudioStreamTrack()
|
||||
? MediaSegment::AUDIO
|
||||
: MediaSegment::VIDEO;
|
||||
|
||||
RefPtr<MediaStreamTrack> track =
|
||||
aOutputStream.mStream->CreateDOMTrack(destinationTrackID, type, source);
|
||||
|
||||
if (aAsyncAddtrack) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<StorensRefPtrPassByPtr<MediaStreamTrack>>(
|
||||
aOutputStream.mStream, &DOMMediaStream::AddTrackInternal, track));
|
||||
} else {
|
||||
aOutputStream.mStream->AddTrackInternal(track);
|
||||
}
|
||||
|
||||
// Track is muted initially, so we don't leak data if it's added while paused
|
||||
// and an MSG iteration passes before the mute comes into effect.
|
||||
processedOutputSource->SetTrackEnabled(destinationTrackID,
|
||||
DisabledTrackMode::SILENCE_FREEZE);
|
||||
RefPtr<MediaInputPort> port =
|
||||
inputTrack->ForwardTrackContentsTo(processedOutputSource,
|
||||
destinationTrackID);
|
||||
|
||||
Pair<nsString, RefPtr<MediaInputPort>> p(aTrack->GetId(), port);
|
||||
aOutputStream.mTrackPorts.AppendElement(Move(p));
|
||||
|
||||
if (mSrcStreamIsPlaying) {
|
||||
processedOutputSource->SetTrackEnabled(destinationTrackID,
|
||||
DisabledTrackMode::ENABLED);
|
||||
}
|
||||
|
||||
LOG(LogLevel::Debug,
|
||||
("Created %s track %p with id %d from track %p through MediaInputPort %p",
|
||||
inputTrack->AsAudioStreamTrack() ? "audio" : "video",
|
||||
track.get(), destinationTrackID, inputTrack, port.get()));
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
|
||||
bool aCaptureAudio,
|
||||
MediaStreamGraph* aGraph)
|
||||
{
|
||||
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
|
||||
@ -2243,26 +2552,91 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
|
||||
MediaStreamTrackSourceGetter* getter = new CaptureStreamTrackSourceGetter(this);
|
||||
out->mStream = DOMMediaStream::CreateTrackUnionStreamAsInput(window, aGraph, getter);
|
||||
out->mFinishWhenEnded = aFinishWhenEnded;
|
||||
out->mCapturingAudioOnly = aCaptureAudio;
|
||||
|
||||
if (aCaptureAudio) {
|
||||
if (mSrcStream) {
|
||||
// We don't support applying volume and mute to the captured stream, when
|
||||
// capturing a MediaStream.
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
|
||||
NS_LITERAL_CSTRING("Media"),
|
||||
OwnerDoc(),
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
"MediaElementAudioCaptureOfMediaStreamError");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// mAudioCaptured tells the user that the audio played by this media element
|
||||
// is being routed to the captureStreams *instead* of being played to
|
||||
// speakers.
|
||||
mAudioCaptured = true;
|
||||
}
|
||||
|
||||
if (mReadyState == HAVE_NOTHING) {
|
||||
// Do not expose the tracks directly before we have metadata.
|
||||
RefPtr<DOMMediaStream> result = out->mStream;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
mAudioCaptured = true;
|
||||
if (mDecoder) {
|
||||
out->mCapturingDecoder = true;
|
||||
mDecoder->AddOutputStream(out->mStream->GetInputStream()->AsProcessedStream(),
|
||||
aFinishWhenEnded);
|
||||
if (mReadyState >= HAVE_METADATA) {
|
||||
// Expose the tracks to JS directly.
|
||||
if (HasAudio()) {
|
||||
TrackID audioTrackId = mMediaInfo.mAudio.mTrackId;
|
||||
RefPtr<MediaStreamTrackSource> trackSource =
|
||||
getter->GetMediaStreamTrackSource(audioTrackId);
|
||||
if (HasAudio()) {
|
||||
TrackID audioTrackId = mMediaInfo.mAudio.mTrackId;
|
||||
RefPtr<MediaStreamTrackSource> trackSource =
|
||||
getter->GetMediaStreamTrackSource(audioTrackId);
|
||||
RefPtr<MediaStreamTrack> track =
|
||||
out->mStream->CreateDOMTrack(audioTrackId, MediaSegment::AUDIO,
|
||||
trackSource);
|
||||
}
|
||||
if (HasVideo()) {
|
||||
TrackID videoTrackId = mMediaInfo.mVideo.mTrackId;
|
||||
RefPtr<MediaStreamTrackSource> trackSource =
|
||||
getter->GetMediaStreamTrackSource(videoTrackId);
|
||||
out->mStream->AddTrackInternal(track);
|
||||
LOG(LogLevel::Debug,
|
||||
("Created audio track %d for captured decoder", audioTrackId));
|
||||
}
|
||||
if (IsVideo() && HasVideo() && !out->mCapturingAudioOnly) {
|
||||
TrackID videoTrackId = mMediaInfo.mVideo.mTrackId;
|
||||
RefPtr<MediaStreamTrackSource> trackSource =
|
||||
getter->GetMediaStreamTrackSource(videoTrackId);
|
||||
RefPtr<MediaStreamTrack> track =
|
||||
out->mStream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO,
|
||||
trackSource);
|
||||
out->mStream->AddTrackInternal(track);
|
||||
LOG(LogLevel::Debug,
|
||||
("Created video track %d for captured decoder", videoTrackId));
|
||||
}
|
||||
}
|
||||
|
||||
if (mSrcStream) {
|
||||
out->mCapturingMediaStream = true;
|
||||
MediaStream* inputStream = out->mStream->GetInputStream();
|
||||
if (!inputStream) {
|
||||
NS_ERROR("No input stream");
|
||||
RefPtr<DOMMediaStream> result = out->mStream;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
ProcessedMediaStream* processedInputStream =
|
||||
inputStream->AsProcessedStream();
|
||||
if (!processedInputStream) {
|
||||
NS_ERROR("Input stream not a ProcessedMediaStream");
|
||||
RefPtr<DOMMediaStream> result = out->mStream;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < AudioTracks()->Length(); ++i) {
|
||||
AudioTrack* t = (*AudioTracks())[i];
|
||||
if (t->Enabled()) {
|
||||
AddCaptureMediaTrackToOutputStream(t, *out, false);
|
||||
}
|
||||
}
|
||||
if (IsVideo() && !out->mCapturingAudioOnly) {
|
||||
// Only add video tracks if we're a video element and the output stream
|
||||
// wants video.
|
||||
for (size_t i = 0; i < VideoTracks()->Length(); ++i) {
|
||||
VideoTrack* t = (*VideoTracks())[i];
|
||||
if (t->Selected()) {
|
||||
AddCaptureMediaTrackToOutputStream(t, *out, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2270,6 +2644,19 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::CaptureAudio(ErrorResult& aRv,
|
||||
MediaStreamGraph* aGraph)
|
||||
{
|
||||
RefPtr<DOMMediaStream> stream = CaptureStreamInternal(false, aGraph);
|
||||
if (!stream) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return stream.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream>
|
||||
HTMLMediaElement::MozCaptureStream(ErrorResult& aRv,
|
||||
MediaStreamGraph* aGraph)
|
||||
@ -2462,6 +2849,7 @@ NS_IMPL_ISUPPORTS(HTMLMediaElement::ShutdownObserver, nsIObserver)
|
||||
HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mWatchManager(this, AbstractThread::MainThread()),
|
||||
mSrcStreamTracksAvailable(false),
|
||||
mSrcStreamPausedCurrentTime(-1),
|
||||
mShutdownObserver(new ShutdownObserver),
|
||||
mCurrentLoadID(0),
|
||||
@ -2807,6 +3195,20 @@ HTMLMediaElement::WakeLockRelease()
|
||||
}
|
||||
}
|
||||
|
||||
HTMLMediaElement::OutputMediaStream::OutputMediaStream()
|
||||
: mFinishWhenEnded(false)
|
||||
, mCapturingAudioOnly(false)
|
||||
, mCapturingDecoder(false)
|
||||
, mCapturingMediaStream(false)
|
||||
, mNextAvailableTrackID(1) {}
|
||||
|
||||
HTMLMediaElement::OutputMediaStream::~OutputMediaStream()
|
||||
{
|
||||
for (auto pair : mTrackPorts) {
|
||||
pair.second()->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool HTMLMediaElement::ParseAttribute(int32_t aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
@ -3424,10 +3826,15 @@ nsresult HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder,
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
|
||||
OutputMediaStream* ms = &mOutputStreams[i];
|
||||
aDecoder->AddOutputStream(ms->mStream->GetInputStream()->AsProcessedStream(),
|
||||
ms->mFinishWhenEnded);
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
if (ms.mCapturingMediaStream) {
|
||||
MOZ_ASSERT(!ms.mCapturingDecoder);
|
||||
continue;
|
||||
}
|
||||
|
||||
ms.mCapturingDecoder = true;
|
||||
aDecoder->AddOutputStream(ms.mStream->GetInputStream()->AsProcessedStream(),
|
||||
ms.mFinishWhenEnded);
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
@ -3681,13 +4088,8 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
|
||||
if (mSelectedVideoStreamTrack && container) {
|
||||
mSelectedVideoStreamTrack->AddVideoOutput(container);
|
||||
}
|
||||
VideoTrack* videoTrack = VideoTracks()->GetSelectedTrack();
|
||||
if (videoTrack) {
|
||||
VideoStreamTrack* videoStreamTrack = videoTrack->GetVideoStreamTrack();
|
||||
if (videoStreamTrack && container) {
|
||||
videoStreamTrack->AddVideoOutput(container);
|
||||
}
|
||||
}
|
||||
|
||||
SetCapturedOutputStreamsEnabled(true); // Unmute
|
||||
} else {
|
||||
if (stream) {
|
||||
mSrcStreamPausedCurrentTime = CurrentTime();
|
||||
@ -3699,13 +4101,8 @@ void HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
|
||||
if (mSelectedVideoStreamTrack && container) {
|
||||
mSelectedVideoStreamTrack->RemoveVideoOutput(container);
|
||||
}
|
||||
VideoTrack* videoTrack = VideoTracks()->GetSelectedTrack();
|
||||
if (videoTrack) {
|
||||
VideoStreamTrack* videoStreamTrack = videoTrack->GetVideoStreamTrack();
|
||||
if (videoStreamTrack && container) {
|
||||
videoStreamTrack->RemoveVideoOutput(container);
|
||||
}
|
||||
}
|
||||
|
||||
SetCapturedOutputStreamsEnabled(false); // Mute
|
||||
}
|
||||
// If stream is null, then DOMMediaStream::Destroy must have been
|
||||
// called and that will remove all listeners/outputs.
|
||||
@ -3744,7 +4141,11 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
|
||||
// If we pause this media element, track changes in the underlying stream
|
||||
// will continue to fire events at this element and alter its track list.
|
||||
// That's simpler than delaying the events, but probably confusing...
|
||||
ConstructMediaTracks();
|
||||
nsTArray<RefPtr<MediaStreamTrack>> tracks;
|
||||
mSrcStream->GetTracks(tracks);
|
||||
for (const RefPtr<MediaStreamTrack>& track : tracks) {
|
||||
NotifyMediaStreamTrackAdded(track);
|
||||
}
|
||||
|
||||
mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this));
|
||||
mMediaStreamTrackListener = new MediaStreamTrackListener(this);
|
||||
@ -3767,18 +4168,29 @@ void HTMLMediaElement::EndSrcMediaStreamPlayback()
|
||||
UpdateSrcMediaStreamPlaying(REMOVING_SRC_STREAM);
|
||||
|
||||
if (mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
MOZ_ASSERT(mSelectedVideoStreamTrack);
|
||||
if (mSelectedVideoStreamTrack) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
|
||||
mSrcStream->UnregisterTrackListener(mMediaStreamTrackListener);
|
||||
mMediaStreamTrackListener = nullptr;
|
||||
mSrcStreamTracksAvailable = false;
|
||||
|
||||
mSrcStream->RemovePrincipalChangeObserver(this);
|
||||
mSrcStreamVideoPrincipal = nullptr;
|
||||
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
for (auto pair : ms.mTrackPorts) {
|
||||
pair.second()->Destroy();
|
||||
}
|
||||
ms.mTrackPorts.Clear();
|
||||
}
|
||||
|
||||
mSrcStream = nullptr;
|
||||
}
|
||||
|
||||
@ -3791,8 +4203,7 @@ CreateAudioTrack(AudioStreamTrack* aStreamTrack)
|
||||
aStreamTrack->GetLabel(label);
|
||||
|
||||
return MediaTrackList::CreateAudioTrack(id, NS_LITERAL_STRING("main"),
|
||||
label, EmptyString(),
|
||||
aStreamTrack->Enabled());
|
||||
label, EmptyString(), true);
|
||||
}
|
||||
|
||||
static already_AddRefed<VideoTrack>
|
||||
@ -3808,57 +4219,22 @@ CreateVideoTrack(VideoStreamTrack* aStreamTrack)
|
||||
aStreamTrack);
|
||||
}
|
||||
|
||||
void HTMLMediaElement::ConstructMediaTracks()
|
||||
{
|
||||
nsTArray<RefPtr<MediaStreamTrack>> tracks;
|
||||
mSrcStream->GetTracks(tracks);
|
||||
|
||||
int firstEnabledVideo = -1;
|
||||
for (const RefPtr<MediaStreamTrack>& track : tracks) {
|
||||
if (track->Ended()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AudioStreamTrack* t = track->AsAudioStreamTrack()) {
|
||||
RefPtr<AudioTrack> audioTrack = CreateAudioTrack(t);
|
||||
AudioTracks()->AddTrack(audioTrack);
|
||||
} else if (VideoStreamTrack* t = track->AsVideoStreamTrack()) {
|
||||
RefPtr<VideoTrack> videoTrack = CreateVideoTrack(t);
|
||||
VideoTracks()->AddTrack(videoTrack);
|
||||
firstEnabledVideo = (t->Enabled() && firstEnabledVideo < 0)
|
||||
? (VideoTracks()->Length() - 1)
|
||||
: firstEnabledVideo;
|
||||
}
|
||||
}
|
||||
|
||||
if (VideoTracks()->Length() > 0) {
|
||||
// If media resource does not indicate a particular set of video tracks to
|
||||
// enable, the one that is listed first in the element's videoTracks object
|
||||
// must be selected.
|
||||
int index = firstEnabledVideo >= 0 ? firstEnabledVideo : 0;
|
||||
(*VideoTracks())[index]->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
VideoTrack* track = (*VideoTracks())[index];
|
||||
VideoStreamTrack* streamTrack = track->GetVideoStreamTrack();
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
streamTrack->AddDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = streamTrack;
|
||||
if (GetVideoFrameContainer()) {
|
||||
mSelectedVideoStreamTrack->AddVideoOutput(GetVideoFrameContainer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aTrack)
|
||||
{
|
||||
MOZ_ASSERT(aTrack);
|
||||
|
||||
if (aTrack->Ended()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsString id;
|
||||
aTrack->GetId(id);
|
||||
|
||||
LOG(LogLevel::Debug, ("%p, Adding MediaTrack with id %s",
|
||||
this, NS_ConvertUTF16toUTF8(id).get()));
|
||||
LOG(LogLevel::Debug, ("%p, Adding %sTrack with id %s",
|
||||
this, aTrack->AsAudioStreamTrack() ? "Audio" : "Video",
|
||||
NS_ConvertUTF16toUTF8(id).get()));
|
||||
#endif
|
||||
|
||||
if (AudioStreamTrack* t = aTrack->AsAudioStreamTrack()) {
|
||||
@ -3866,24 +4242,20 @@ HTMLMediaElement::NotifyMediaStreamTrackAdded(const RefPtr<MediaStreamTrack>& aT
|
||||
AudioTracks()->AddTrack(audioTrack);
|
||||
} else if (VideoStreamTrack* t = aTrack->AsVideoStreamTrack()) {
|
||||
// TODO: Fix this per the spec on bug 1273443.
|
||||
int32_t selectedIndex = VideoTracks()->SelectedIndex();
|
||||
if (!IsVideo()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<VideoTrack> videoTrack = CreateVideoTrack(t);
|
||||
VideoTracks()->AddTrack(videoTrack);
|
||||
// New MediaStreamTrack added, set the new added video track as selected
|
||||
// video track when there is no selected track.
|
||||
if (selectedIndex == -1) {
|
||||
if (VideoTracks()->SelectedIndex() == -1) {
|
||||
MOZ_ASSERT(!mSelectedVideoStreamTrack);
|
||||
videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
mMediaStreamSizeListener = new StreamSizeListener(this);
|
||||
t->AddDirectListener(mMediaStreamSizeListener);
|
||||
mSelectedVideoStreamTrack = t;
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (mSrcStreamIsPlaying && container) {
|
||||
mSelectedVideoStreamTrack->AddVideoOutput(container);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mWatchManager.ManualNotify(&HTMLMediaElement::UpdateReadyStateInternal);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3894,62 +4266,14 @@ HTMLMediaElement::NotifyMediaStreamTrackRemoved(const RefPtr<MediaStreamTrack>&
|
||||
nsAutoString id;
|
||||
aTrack->GetId(id);
|
||||
|
||||
LOG(LogLevel::Debug, ("%p, Removing MediaTrack with id %s",
|
||||
this, NS_ConvertUTF16toUTF8(id).get()));
|
||||
LOG(LogLevel::Debug, ("%p, Removing %sTrack with id %s",
|
||||
this, aTrack->AsAudioStreamTrack() ? "Audio" : "Video",
|
||||
NS_ConvertUTF16toUTF8(id).get()));
|
||||
|
||||
if (MediaTrack* t = AudioTracks()->GetTrackById(id)) {
|
||||
AudioTracks()->RemoveTrack(t);
|
||||
} else if (MediaTrack* t = VideoTracks()->GetTrackById(id)) {
|
||||
VideoTracks()->RemoveTrack(t);
|
||||
// TODO: Fix this per the spec on bug 1273443.
|
||||
// If the removed media stream track is selected video track and there are
|
||||
// still video tracks, change the selected video track to the first
|
||||
// remaining track.
|
||||
if (aTrack == mSelectedVideoStreamTrack) {
|
||||
// The mMediaStreamSizeListener might already reset to nullptr.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
VideoFrameContainer* container = GetVideoFrameContainer();
|
||||
if (mSrcStreamIsPlaying && container) {
|
||||
mSelectedVideoStreamTrack->RemoveVideoOutput(container);
|
||||
}
|
||||
mSelectedVideoStreamTrack = nullptr;
|
||||
MOZ_ASSERT(mSrcStream);
|
||||
nsTArray<RefPtr<VideoStreamTrack>> tracks;
|
||||
mSrcStream->GetVideoTracks(tracks);
|
||||
|
||||
for (const RefPtr<VideoStreamTrack>& track : tracks) {
|
||||
if (track->Ended()) {
|
||||
continue;
|
||||
}
|
||||
if (!track->Enabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString trackId;
|
||||
track->GetId(trackId);
|
||||
MediaTrack* videoTrack = VideoTracks()->GetTrackById(trackId);
|
||||
MOZ_ASSERT(videoTrack);
|
||||
|
||||
videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
|
||||
if (mMediaStreamSizeListener) {
|
||||
track->AddDirectListener(mMediaStreamSizeListener);
|
||||
}
|
||||
mSelectedVideoStreamTrack = track;
|
||||
if (container) {
|
||||
mSelectedVideoStreamTrack->AddVideoOutput(container);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no enabled video track existing, clean the
|
||||
// mMediaStreamSizeListener.
|
||||
if (mMediaStreamSizeListener) {
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// XXX (bug 1208328) Uncomment this when DOMMediaStream doesn't call
|
||||
// NotifyTrackRemoved multiple times for the same track, i.e., when it
|
||||
@ -4033,6 +4357,28 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
||||
SetCurrentTime(mDefaultPlaybackStartPosition);
|
||||
mDefaultPlaybackStartPosition = 0.0;
|
||||
}
|
||||
|
||||
if (!mSrcStream) {
|
||||
return;
|
||||
}
|
||||
for (OutputMediaStream& ms : mOutputStreams) {
|
||||
for (size_t i = 0; i < AudioTracks()->Length(); ++i) {
|
||||
AudioTrack* t = (*AudioTracks())[i];
|
||||
if (t->Enabled()) {
|
||||
AddCaptureMediaTrackToOutputStream(t, ms);
|
||||
}
|
||||
}
|
||||
if (IsVideo() && !ms.mCapturingAudioOnly) {
|
||||
// Only add video tracks if we're a video element and the output stream
|
||||
// wants video.
|
||||
for (size_t i = 0; i < VideoTracks()->Length(); ++i) {
|
||||
VideoTrack* t = (*VideoTracks())[i];
|
||||
if (t->Selected()) {
|
||||
AddCaptureMediaTrackToOutputStream(t, ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLMediaElement::FirstFrameLoaded()
|
||||
@ -4137,6 +4483,8 @@ void HTMLMediaElement::PlaybackEnded()
|
||||
// Discard all output streams that have finished now.
|
||||
for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
|
||||
if (mOutputStreams[i].mFinishWhenEnded) {
|
||||
LOG(LogLevel::Debug, ("Playback ended. Removing output stream %p",
|
||||
mOutputStreams[i].mStream.get()));
|
||||
mOutputStreams.RemoveElementAt(i);
|
||||
}
|
||||
}
|
||||
@ -4342,9 +4690,14 @@ HTMLMediaElement::UpdateReadyStateInternal()
|
||||
}
|
||||
|
||||
if (mSrcStream && mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
||||
if (!mSrcStreamTracksAvailable) {
|
||||
LOG(LogLevel::Debug, ("MediaElement %p UpdateReadyStateInternal() "
|
||||
"MediaStreamTracks not available yet", this));
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasAudioTracks = !AudioTracks()->IsEmpty();
|
||||
bool hasVideoTracks = !VideoTracks()->IsEmpty();
|
||||
|
||||
if (!hasAudioTracks && !hasVideoTracks) {
|
||||
LOG(LogLevel::Debug, ("MediaElement %p UpdateReadyStateInternal() "
|
||||
"Stream with no tracks", this));
|
||||
@ -4875,6 +5228,12 @@ void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
|
||||
if (!mMediaStreamSizeListener) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSelectedVideoStreamTrack) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
|
||||
mMediaStreamSizeListener->Forget();
|
||||
mMediaStreamSizeListener = nullptr;
|
||||
|
@ -316,7 +316,8 @@ public:
|
||||
*/
|
||||
bool RemoveDecoderPrincipalChangeObserver(DecoderPrincipalChangeObserver* aObserver);
|
||||
|
||||
class CaptureStreamTrackSource;
|
||||
class StreamCaptureTrackSource;
|
||||
class DecoderCaptureTrackSource;
|
||||
class CaptureStreamTrackSourceGetter;
|
||||
|
||||
// Update the visual size of the media. Called from the decoder on the
|
||||
@ -343,8 +344,18 @@ public:
|
||||
*/
|
||||
void NotifyLoadError();
|
||||
|
||||
/**
|
||||
* Called by one of our associated MediaTrackLists (audio/video) when an
|
||||
* AudioTrack is enabled or a VideoTrack is selected.
|
||||
*/
|
||||
void NotifyMediaTrackEnabled(MediaTrack* aTrack);
|
||||
|
||||
/**
|
||||
* Called by one of our associated MediaTrackLists (audio/video) when an
|
||||
* AudioTrack is disabled or a VideoTrack is unselected.
|
||||
*/
|
||||
void NotifyMediaTrackDisabled(MediaTrack* aTrack);
|
||||
|
||||
/**
|
||||
* Called when tracks become available to the source media stream.
|
||||
*/
|
||||
@ -648,6 +659,9 @@ public:
|
||||
return mAutoplayEnabled;
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMediaStream> CaptureAudio(ErrorResult& aRv,
|
||||
MediaStreamGraph* aGraph = nullptr);
|
||||
|
||||
already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv,
|
||||
MediaStreamGraph* aGraph = nullptr);
|
||||
|
||||
@ -774,6 +788,23 @@ protected:
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
};
|
||||
|
||||
// Holds references to the DOM wrappers for the MediaStreams that we're
|
||||
// writing to.
|
||||
struct OutputMediaStream {
|
||||
OutputMediaStream();
|
||||
~OutputMediaStream();
|
||||
|
||||
RefPtr<DOMMediaStream> mStream;
|
||||
bool mFinishWhenEnded;
|
||||
bool mCapturingAudioOnly;
|
||||
bool mCapturingDecoder;
|
||||
bool mCapturingMediaStream;
|
||||
|
||||
// The following members are keeping state for a captured MediaStream.
|
||||
TrackID mNextAvailableTrackID;
|
||||
nsTArray<Pair<nsString, RefPtr<MediaInputPort>>> mTrackPorts;
|
||||
};
|
||||
|
||||
nsresult PlayInternal(bool aCallerIsChrome);
|
||||
|
||||
/** Use this method to change the mReadyState member, so required
|
||||
@ -826,13 +857,6 @@ protected:
|
||||
enum { REMOVING_SRC_STREAM = 0x1 };
|
||||
void UpdateSrcMediaStreamPlaying(uint32_t aFlags = 0);
|
||||
|
||||
/**
|
||||
* If loading and playing a MediaStream, for each MediaStreamTrack in the
|
||||
* MediaStream, create a corresponding AudioTrack or VideoTrack during the
|
||||
* phase of resource fetching.
|
||||
*/
|
||||
void ConstructMediaTracks();
|
||||
|
||||
/**
|
||||
* Called by our DOMMediaStream::TrackListener when a new MediaStreamTrack has
|
||||
* been added to the playback stream of |mSrcStream|.
|
||||
@ -846,13 +870,36 @@ protected:
|
||||
void NotifyMediaStreamTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);
|
||||
|
||||
/**
|
||||
* Returns an nsDOMMediaStream containing the played contents of this
|
||||
* Enables or disables all tracks forwarded from mSrcStream to all
|
||||
* OutputMediaStreams. We do this for muting the tracks when pausing,
|
||||
* and unmuting when playing the media element again.
|
||||
*
|
||||
* If mSrcStream is unset, this does nothing.
|
||||
*/
|
||||
void SetCapturedOutputStreamsEnabled(bool aEnabled);
|
||||
|
||||
/**
|
||||
* Create a new MediaStreamTrack for aTrack and add it to the DOMMediaStream
|
||||
* in aOutputStream. This automatically sets the output track to enabled or
|
||||
* disabled depending on our current playing state.
|
||||
*/
|
||||
void AddCaptureMediaTrackToOutputStream(MediaTrack* aTrack,
|
||||
OutputMediaStream& aOutputStream,
|
||||
bool aAsyncAddtrack = true);
|
||||
|
||||
/**
|
||||
* Returns an DOMMediaStream containing the played contents of this
|
||||
* element. When aFinishWhenEnded is true, when this element ends playback
|
||||
* we will finish the stream and not play any more into it.
|
||||
* When aFinishWhenEnded is false, ending playback does not finish the stream.
|
||||
* The stream will never finish.
|
||||
*
|
||||
* When aCaptureAudio is true, we stop playout of audio and instead route it
|
||||
* to the DOMMediaStream. Volume and mute state will be applied to the audio
|
||||
* reaching the stream. No video tracks will be captured in this case.
|
||||
*/
|
||||
already_AddRefed<DOMMediaStream> CaptureStreamInternal(bool aFinishWhenEnded,
|
||||
bool aCaptureAudio,
|
||||
MediaStreamGraph* aGraph = nullptr);
|
||||
|
||||
/**
|
||||
@ -1240,6 +1287,9 @@ protected:
|
||||
// At most one of mDecoder and mSrcStream can be non-null.
|
||||
RefPtr<DOMMediaStream> mSrcStream;
|
||||
|
||||
// True once mSrcStream's initial set of tracks are known.
|
||||
bool mSrcStreamTracksAvailable;
|
||||
|
||||
// If non-negative, the time we should return for currentTime while playing
|
||||
// mSrcStream.
|
||||
double mSrcStreamPausedCurrentTime;
|
||||
@ -1249,10 +1299,6 @@ protected:
|
||||
|
||||
// Holds references to the DOM wrappers for the MediaStreams that we're
|
||||
// writing to.
|
||||
struct OutputMediaStream {
|
||||
RefPtr<DOMMediaStream> mStream;
|
||||
bool mFinishWhenEnded;
|
||||
};
|
||||
nsTArray<OutputMediaStream> mOutputStreams;
|
||||
|
||||
// Holds a reference to the MediaStreamListener attached to mSrcStream's
|
||||
|
@ -44,7 +44,8 @@ HTMLTableColElement::ParseAttribute(int32_t aNamespaceID,
|
||||
}
|
||||
if (aAttribute == nsGkAtoms::span) {
|
||||
/* protection from unrealistic large colspan values */
|
||||
return aResult.ParseIntWithBounds(aValue, 1, MAX_COLSPAN);
|
||||
aResult.ParseIntWithFallback(aValue, 1, MAX_COLSPAN);
|
||||
return true;
|
||||
}
|
||||
if (aAttribute == nsGkAtoms::width) {
|
||||
return aResult.ParseSpecialIntValue(aValue);
|
||||
|
@ -27,7 +27,8 @@ public:
|
||||
}
|
||||
void SetSpan(uint32_t aSpan, ErrorResult& aError)
|
||||
{
|
||||
SetHTMLIntAttr(nsGkAtoms::span, aSpan, aError);
|
||||
uint32_t span = aSpan ? aSpan : 1;
|
||||
SetUnsignedIntAttr(nsGkAtoms::span, span, 1, aError);
|
||||
}
|
||||
|
||||
void GetAlign(DOMString& aAlign)
|
||||
|
@ -402,9 +402,12 @@ HTMLTextAreaElement::ParseAttribute(int32_t aNamespaceID,
|
||||
if (aAttribute == nsGkAtoms::maxlength ||
|
||||
aAttribute == nsGkAtoms::minlength) {
|
||||
return aResult.ParseNonNegativeIntValue(aValue);
|
||||
} else if (aAttribute == nsGkAtoms::cols ||
|
||||
aAttribute == nsGkAtoms::rows) {
|
||||
return aResult.ParsePositiveIntValue(aValue);
|
||||
} else if (aAttribute == nsGkAtoms::cols) {
|
||||
aResult.ParseIntWithFallback(aValue, DEFAULT_COLS);
|
||||
return true;
|
||||
} else if (aAttribute == nsGkAtoms::rows) {
|
||||
aResult.ParseIntWithFallback(aValue, DEFAULT_ROWS_TEXTAREA);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
||||
|
@ -180,11 +180,8 @@ public:
|
||||
}
|
||||
void SetCols(uint32_t aCols, ErrorResult& aError)
|
||||
{
|
||||
if (aCols == 0) {
|
||||
aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
} else {
|
||||
SetUnsignedIntAttr(nsGkAtoms::cols, aCols, DEFAULT_COLS, aError);
|
||||
}
|
||||
uint32_t cols = aCols ? aCols : DEFAULT_COLS;
|
||||
SetUnsignedIntAttr(nsGkAtoms::cols, cols, DEFAULT_COLS, aError);
|
||||
}
|
||||
bool Disabled()
|
||||
{
|
||||
@ -262,11 +259,8 @@ public:
|
||||
}
|
||||
void SetRows(uint32_t aRows, ErrorResult& aError)
|
||||
{
|
||||
if (aRows == 0) {
|
||||
aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
} else {
|
||||
SetUnsignedIntAttr(nsGkAtoms::rows, aRows, DEFAULT_ROWS_TEXTAREA, aError);
|
||||
}
|
||||
uint32_t rows = aRows ? aRows : DEFAULT_ROWS_TEXTAREA;
|
||||
SetUnsignedIntAttr(nsGkAtoms::rows, rows, DEFAULT_ROWS_TEXTAREA, aError);
|
||||
}
|
||||
// XPCOM GetWrap is fine
|
||||
void SetWrap(const nsAString& aWrap, ErrorResult& aError)
|
||||
|
@ -25,6 +25,7 @@ reflectUnsignedInt({
|
||||
attribute: "cols",
|
||||
nonZero: true,
|
||||
defaultValue: 20,
|
||||
fallback: true,
|
||||
});
|
||||
|
||||
todo("dirName" in document.createElement("textarea"),
|
||||
@ -77,6 +78,7 @@ reflectUnsignedInt({
|
||||
attribute: "rows",
|
||||
nonZero: true,
|
||||
defaultValue: 2,
|
||||
fallback: true,
|
||||
});
|
||||
|
||||
// .wrap
|
||||
|
@ -140,6 +140,7 @@ function reflectUnsignedInt(aParameters)
|
||||
var attr = aParameters.attribute;
|
||||
var nonZero = aParameters.nonZero;
|
||||
var defaultValue = aParameters.defaultValue;
|
||||
var fallback = aParameters.fallback;
|
||||
|
||||
if (defaultValue === undefined) {
|
||||
if (nonZero) {
|
||||
@ -149,6 +150,10 @@ function reflectUnsignedInt(aParameters)
|
||||
}
|
||||
}
|
||||
|
||||
if (fallback === undefined) {
|
||||
fallback = false;
|
||||
}
|
||||
|
||||
ok(attr in element, attr + " should be an IDL attribute of this element");
|
||||
is(typeof element[attr], "number", attr + " IDL attribute should be a number");
|
||||
|
||||
@ -213,7 +218,7 @@ function reflectUnsignedInt(aParameters)
|
||||
is(e.code, DOMException.INDEX_SIZE_ERR, "exception code should be INDEX_SIZE_ERR");
|
||||
}
|
||||
|
||||
if (nonZero) {
|
||||
if (nonZero && !fallback) {
|
||||
ok(caught, "an exception should have been caught");
|
||||
} else {
|
||||
ok(!caught, "no exception should have been caught");
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
||||
#include "mozilla/layers/APZChild.h"
|
||||
#include "mozilla/layers/CompositorBridgeChild.h"
|
||||
#include "mozilla/layers/ContentProcessController.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/SharedBufferManagerChild.h"
|
||||
#include "mozilla/layout/RenderFrameChild.h"
|
||||
@ -810,13 +811,11 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
PBrowserChild::Send__delete__(newChild);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
if (!*aWindowIsNew) {
|
||||
PRenderFrameChild::Send__delete__(renderFrame);
|
||||
PBrowserChild::Send__delete__(newChild);
|
||||
return NS_ERROR_ABORT;
|
||||
}
|
||||
|
||||
@ -980,12 +979,12 @@ ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
|
||||
return actor;
|
||||
}
|
||||
|
||||
class MemoryReportCallback final : public nsIMemoryReporterCallback
|
||||
class HandleReportCallback final : public nsIHandleReportCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit MemoryReportCallback(MemoryReportRequestChild* aActor,
|
||||
explicit HandleReportCallback(MemoryReportRequestChild* aActor,
|
||||
const nsACString& aProcess)
|
||||
: mActor(aActor)
|
||||
, mProcess(aProcess)
|
||||
@ -1002,23 +1001,23 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
~MemoryReportCallback() {}
|
||||
~HandleReportCallback() {}
|
||||
|
||||
RefPtr<MemoryReportRequestChild> mActor;
|
||||
const nsCString mProcess;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
MemoryReportCallback
|
||||
, nsIMemoryReporterCallback
|
||||
HandleReportCallback
|
||||
, nsIHandleReportCallback
|
||||
)
|
||||
|
||||
class MemoryReportFinishedCallback final : public nsIFinishReportingCallback
|
||||
class FinishReportingCallback final : public nsIFinishReportingCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit MemoryReportFinishedCallback(MemoryReportRequestChild* aActor)
|
||||
explicit FinishReportingCallback(MemoryReportRequestChild* aActor)
|
||||
: mActor(aActor)
|
||||
{
|
||||
}
|
||||
@ -1030,13 +1029,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
~MemoryReportFinishedCallback() {}
|
||||
~FinishReportingCallback() {}
|
||||
|
||||
RefPtr<MemoryReportRequestChild> mActor;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(
|
||||
MemoryReportFinishedCallback
|
||||
FinishReportingCallback
|
||||
, nsIFinishReportingCallback
|
||||
)
|
||||
|
||||
@ -1076,14 +1075,15 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
|
||||
|
||||
// Run the reporters. The callback will turn each measurement into a
|
||||
// MemoryReport.
|
||||
RefPtr<MemoryReportCallback> cb =
|
||||
new MemoryReportCallback(this, process);
|
||||
RefPtr<MemoryReportFinishedCallback> finished =
|
||||
new MemoryReportFinishedCallback(this);
|
||||
RefPtr<HandleReportCallback> handleReport =
|
||||
new HandleReportCallback(this, process);
|
||||
RefPtr<FinishReportingCallback> finishReporting =
|
||||
new FinishReportingCallback(this);
|
||||
|
||||
return mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
|
||||
return mgr->GetReportsForThisProcessExtended(handleReport, nullptr,
|
||||
mAnonymize,
|
||||
FileDescriptorToFILE(mDMDFile, "wb"),
|
||||
finished, nullptr);
|
||||
finishReporting, nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1160,19 +1160,6 @@ ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
|
||||
return GMPServiceChild::Create(aTransport, aOtherProcess);
|
||||
}
|
||||
|
||||
PAPZChild*
|
||||
ContentChild::AllocPAPZChild(const TabId& aTabId)
|
||||
{
|
||||
return APZChild::Create(aTabId);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPAPZChild(PAPZChild* aActor)
|
||||
{
|
||||
delete aActor;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint)
|
||||
{
|
||||
@ -1391,6 +1378,13 @@ ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId)
|
||||
{
|
||||
APZChild* apz = ContentProcessController::Create(aTabId);
|
||||
return CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::RecvSpeakerManagerNotify()
|
||||
{
|
||||
@ -2701,7 +2695,7 @@ ContentChild::RecvMinimizeMemoryUsage()
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
NS_ENSURE_TRUE(mgr, true);
|
||||
|
||||
mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
|
||||
Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -143,11 +143,6 @@ public:
|
||||
AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
|
||||
base::ProcessId otherProcess) override;
|
||||
|
||||
PAPZChild*
|
||||
AllocPAPZChild(const TabId& aTabId) override;
|
||||
bool
|
||||
DeallocPAPZChild(PAPZChild* aActor) override;
|
||||
|
||||
bool
|
||||
RecvInitCompositor(Endpoint<PCompositorBridgeChild>&& aEndpoint) override;
|
||||
bool
|
||||
@ -397,6 +392,8 @@ public:
|
||||
|
||||
virtual bool RecvSetConnectivity(const bool& connectivity) override;
|
||||
|
||||
virtual bool RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId) override;
|
||||
|
||||
virtual bool RecvSpeakerManagerNotify() override;
|
||||
|
||||
virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL,
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include "chrome/common/process_watcher.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "mozilla/a11y/PDocAccessible.h"
|
||||
#include "AppProcessChecker.h"
|
||||
#include "AudioChannelService.h"
|
||||
@ -90,6 +88,7 @@
|
||||
#include "mozilla/layers/PAPZParent.h"
|
||||
#include "mozilla/layers/CompositorThread.h"
|
||||
#include "mozilla/layers/ImageBridgeParent.h"
|
||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||
#include "mozilla/layers/SharedBufferManagerParent.h"
|
||||
#include "mozilla/layout/RenderFrameParent.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
@ -103,6 +102,7 @@
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
#include "mozilla/ProfileGatherer.h"
|
||||
#endif
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -482,9 +482,10 @@ public:
|
||||
NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentParentsMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
|
||||
nsISupports* aClosure,
|
||||
bool aAnonymize)
|
||||
ContentParentsMemoryReporter::CollectReports(
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
bool aAnonymize)
|
||||
{
|
||||
AutoTArray<ContentParent*, 16> cps;
|
||||
ContentParent::GetAllEvenIfDead(cps);
|
||||
@ -522,15 +523,9 @@ ContentParentsMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
|
||||
"messages. Similarly, a ContentParent object for a process that's no "
|
||||
"longer running could indicate that we're leaking ContentParents.");
|
||||
|
||||
nsresult rv = cb->Callback(/* process */ EmptyCString(),
|
||||
path,
|
||||
KIND_OTHER,
|
||||
UNITS_COUNT,
|
||||
numQueuedMessages,
|
||||
desc,
|
||||
aClosure);
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aHandleReport->Callback(/* process */ EmptyCString(), path,
|
||||
KIND_OTHER, UNITS_COUNT,
|
||||
numQueuedMessages, desc, aData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1690,18 +1685,6 @@ ContentParent::ProcessingError(Result aCode, const char* aReason)
|
||||
KillHard(aReason);
|
||||
}
|
||||
|
||||
typedef std::pair<ContentParent*, std::set<uint64_t> > IDPair;
|
||||
|
||||
namespace {
|
||||
std::map<ContentParent*, std::set<uint64_t> >&
|
||||
NestedBrowserLayerIds()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static std::map<ContentParent*, std::set<uint64_t> > sNestedBrowserIds;
|
||||
return sNestedBrowserIds;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/* static */
|
||||
bool
|
||||
ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId)
|
||||
@ -1717,7 +1700,9 @@ ContentParent::AllocateLayerTreeId(ContentParent* aContent,
|
||||
uint64_t* aId)
|
||||
{
|
||||
GPUProcessManager* gpu = GPUProcessManager::Get();
|
||||
|
||||
*aId = gpu->AllocateLayerTreeId();
|
||||
gpu->MapLayerTreeId(*aId, aContent->OtherPid());
|
||||
|
||||
if (!gfxPlatform::AsyncPanZoomEnabled()) {
|
||||
return true;
|
||||
@ -1727,7 +1712,7 @@ ContentParent::AllocateLayerTreeId(ContentParent* aContent,
|
||||
return false;
|
||||
}
|
||||
|
||||
return gpu->UpdateRemoteContentController(*aId, aContent, aTabId, aTopLevel);
|
||||
return aContent->SendNotifyLayerAllocated(aTabId, *aId);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1753,33 +1738,22 @@ ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
|
||||
cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
|
||||
MOZ_ASSERT(contentParent && browserParent);
|
||||
|
||||
if (!AllocateLayerTreeId(contentParent, browserParent, aTabId, aId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = NestedBrowserLayerIds().find(this);
|
||||
if (iter == NestedBrowserLayerIds().end()) {
|
||||
std::set<uint64_t> ids;
|
||||
ids.insert(*aId);
|
||||
NestedBrowserLayerIds().insert(IDPair(this, ids));
|
||||
} else {
|
||||
iter->second.insert(*aId);
|
||||
}
|
||||
return true;
|
||||
return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId)
|
||||
{
|
||||
auto iter = NestedBrowserLayerIds().find(this);
|
||||
if (iter != NestedBrowserLayerIds().end() &&
|
||||
iter->second.find(aId) != iter->second.end())
|
||||
GPUProcessManager* gpu = GPUProcessManager::Get();
|
||||
|
||||
if (!gpu->IsLayerTreeIdMapped(aId, this->OtherPid()))
|
||||
{
|
||||
GPUProcessManager::Get()->DeallocateLayerTreeId(aId);
|
||||
} else {
|
||||
// You can't deallocate layer tree ids that you didn't allocate
|
||||
KillHard("DeallocateLayerTreeId");
|
||||
}
|
||||
|
||||
gpu->DeallocateLayerTreeId(aId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2919,21 +2893,6 @@ ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
|
||||
return GMPServiceParent::Create(aTransport, aOtherProcess);
|
||||
}
|
||||
|
||||
PAPZParent*
|
||||
ContentParent::AllocPAPZParent(const TabId& aTabId)
|
||||
{
|
||||
// The PAPZParent should just be created in the main process and then an IPDL
|
||||
// constructor message sent to hook it up.
|
||||
MOZ_CRASH("This shouldn't be called");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPAPZParent(PAPZParent* aActor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
PBackgroundParent*
|
||||
ContentParent::AllocPBackgroundParent(Transport* aTransport,
|
||||
ProcessId aOtherProcess)
|
||||
@ -4444,6 +4403,9 @@ ContentParent::RecvKeywordToURI(const nsCString& aKeyword,
|
||||
OptionalInputStreamParams* aPostData,
|
||||
OptionalURIParams* aURI)
|
||||
{
|
||||
*aPostData = void_t();
|
||||
*aURI = void_t();
|
||||
|
||||
nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
|
||||
if (!fixup) {
|
||||
return true;
|
||||
@ -5004,6 +4966,14 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
|
||||
TabParent* newTab = TabParent::GetFrom(aNewTab);
|
||||
MOZ_ASSERT(newTab);
|
||||
|
||||
auto destroyNewTabOnError = MakeScopeExit([&] {
|
||||
if (!*aWindowIsNew || NS_FAILED(*aResult)) {
|
||||
if (newTab) {
|
||||
newTab->Destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Content has requested that we open this new content window, so
|
||||
// we must have an opener.
|
||||
newTab->SetHasContentOpener(true);
|
||||
|
@ -703,11 +703,6 @@ private:
|
||||
AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess) override;
|
||||
|
||||
PAPZParent*
|
||||
AllocPAPZParent(const TabId& aTabId) override;
|
||||
bool
|
||||
DeallocPAPZParent(PAPZParent* aActor) override;
|
||||
|
||||
PSharedBufferManagerParent*
|
||||
AllocPSharedBufferManagerParent(mozilla::ipc::Transport* aTranport,
|
||||
base::ProcessId aOtherProcess) override;
|
||||
|
@ -670,11 +670,6 @@ child:
|
||||
int32_t aModifiers,
|
||||
bool aPreventDefault);
|
||||
|
||||
/**
|
||||
* APZ notification for mouse scroll testing events.
|
||||
*/
|
||||
async MouseScrollTestEvent(uint64_t aLayersId, ViewID aScrollId, nsString aEvent);
|
||||
|
||||
async CompositionEvent(WidgetCompositionEvent event);
|
||||
|
||||
async SelectionEvent(WidgetSelectionEvent event);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user