Merge m-c to fx-team, a=merge

This commit is contained in:
Wes Kocher 2016-08-25 17:15:43 -07:00
commit 7d3cddeb71
667 changed files with 28897 additions and 28521 deletions

View File

@ -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

View File

@ -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

View File

@ -683,7 +683,7 @@ protected:
*
* @see ProcessInvalidationList
*/
nsTArray<nsIContent*> mInvalidationList;
nsTArray<RefPtr<nsIContent>> mInvalidationList;
/**
* Holds a list of aria-owns relocations.

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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.");

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -77,7 +77,7 @@ function convert(result) {
return node;
}
extensions.registerSchemaAPI("bookmarks", context => {
extensions.registerSchemaAPI("bookmarks", "addon_parent", context => {
return {
bookmarks: {
get: function(idOrIdList) {

View File

@ -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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -130,7 +130,7 @@ function getObserver() {
return _observer;
}
extensions.registerSchemaAPI("history", context => {
extensions.registerSchemaAPI("history", "addon_parent", context => {
return {
history: {
addUrl: function(details) {

View File

@ -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: {

View File

@ -264,7 +264,7 @@ let tabListener = {
},
};
extensions.registerSchemaAPI("tabs", context => {
extensions.registerSchemaAPI("tabs", "addon_parent", context => {
let {extension} = context;
let self = {
tabs: {

View File

@ -15,7 +15,7 @@ var {
EventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("windows", context => {
extensions.registerSchemaAPI("windows", "addon_parent", context => {
let {extension} = context;
return {
windows: {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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") {

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
},

View File

@ -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}]],

View File

@ -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");

View File

@ -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);
},

View File

@ -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;
}

View File

@ -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"));

View File

@ -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");

View File

@ -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);

View File

@ -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");

View File

@ -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";

View File

@ -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.

View File

@ -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);
}

View File

@ -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 {

View File

@ -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" +

View File

@ -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;
},
};

View File

@ -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";

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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" });

View 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>

View 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>

View File

@ -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]

View 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>

View 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>

View File

@ -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;

View File

@ -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

View File

@ -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) */

View File

@ -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)
{

View File

@ -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:

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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")) {

View File

@ -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;

View File

@ -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;
}
};

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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;
}
};

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
};

View File

@ -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;
}
};

View File

@ -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;
}

View File

@ -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.

View File

@ -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'
]

View File

@ -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)

View File

@ -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>

View File

@ -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)) {

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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");

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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