зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
This commit is contained in:
Коммит
11bec2e097
|
@ -448,6 +448,7 @@ DocAccessible::Shutdown()
|
||||||
parentDocument->RemoveChildDocument(this);
|
parentDocument->RemoveChildDocument(this);
|
||||||
|
|
||||||
mParent->RemoveChild(this);
|
mParent->RemoveChild(this);
|
||||||
|
MOZ_ASSERT(!mParent, "Parent has to be null!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk the array backwards because child documents remove themselves from the
|
// Walk the array backwards because child documents remove themselves from the
|
||||||
|
|
|
@ -104,7 +104,10 @@ OuterDocAccessible::Shutdown()
|
||||||
// to its parent document. Otherwise a document accessible may be lost if
|
// to its parent document. Otherwise a document accessible may be lost if
|
||||||
// its outerdoc has being recreated (see bug 862863 for details).
|
// its outerdoc has being recreated (see bug 862863 for details).
|
||||||
if (!mDoc->IsDefunct()) {
|
if (!mDoc->IsDefunct()) {
|
||||||
mDoc->BindChildDocument(child->AsDoc());
|
MOZ_ASSERT(!child->IsDefunct(), "Attempt to reattach shutdown document accessible");
|
||||||
|
if (!child->IsDefunct()) {
|
||||||
|
mDoc->BindChildDocument(child->AsDoc());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,8 +146,8 @@ bool
|
||||||
OuterDocAccessible::RemoveChild(Accessible* aAccessible)
|
OuterDocAccessible::RemoveChild(Accessible* aAccessible)
|
||||||
{
|
{
|
||||||
Accessible* child = mChildren.SafeElementAt(0, nullptr);
|
Accessible* child = mChildren.SafeElementAt(0, nullptr);
|
||||||
|
MOZ_ASSERT(child == aAccessible, "Wrong child to remove!");
|
||||||
if (child != aAccessible) {
|
if (child != aAccessible) {
|
||||||
NS_ERROR("Wrong child to remove!");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ window._gBrowser = {
|
||||||
"resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
|
"resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
|
||||||
"purgeSessionHistory", "stopScroll", "startScroll",
|
"purgeSessionHistory", "stopScroll", "startScroll",
|
||||||
"userTypedValue", "userTypedClear", "mediaBlocked",
|
"userTypedValue", "userTypedClear", "mediaBlocked",
|
||||||
"didStartLoadSinceLastUserTyping"
|
"didStartLoadSinceLastUserTyping", "audioMuted"
|
||||||
],
|
],
|
||||||
|
|
||||||
_removingTabs: [],
|
_removingTabs: [],
|
||||||
|
@ -1937,7 +1937,7 @@ window._gBrowser = {
|
||||||
let setter;
|
let setter;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "audioMuted":
|
case "audioMuted":
|
||||||
getter = () => false;
|
getter = () => aTab.hasAttribute("muted");
|
||||||
break;
|
break;
|
||||||
case "contentTitle":
|
case "contentTitle":
|
||||||
getter = () => SessionStore.getLazyTabValue(aTab, "title");
|
getter = () => SessionStore.getLazyTabValue(aTab, "title");
|
||||||
|
|
|
@ -1906,39 +1906,6 @@
|
||||||
<parameter name="aMuteReason"/>
|
<parameter name="aMuteReason"/>
|
||||||
<body>
|
<body>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
// Do not attempt to toggle mute state if browser is lazy.
|
|
||||||
if (!this.linkedPanel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let browser = this.linkedBrowser;
|
|
||||||
let modifiedAttrs = [];
|
|
||||||
let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
|
|
||||||
|
|
||||||
if (this.hasAttribute("activemedia-blocked")) {
|
|
||||||
this.removeAttribute("activemedia-blocked");
|
|
||||||
modifiedAttrs.push("activemedia-blocked");
|
|
||||||
|
|
||||||
browser.resumeMedia();
|
|
||||||
hist.add(3 /* unblockByClickingIcon */);
|
|
||||||
this.finishMediaBlockTimer();
|
|
||||||
} else {
|
|
||||||
if (browser.audioMuted) {
|
|
||||||
browser.unmute();
|
|
||||||
this.removeAttribute("muted");
|
|
||||||
BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
|
|
||||||
hist.add(1 /* unmute */);
|
|
||||||
} else {
|
|
||||||
browser.mute();
|
|
||||||
this.setAttribute("muted", "true");
|
|
||||||
BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
|
|
||||||
hist.add(0 /* mute */);
|
|
||||||
}
|
|
||||||
this.muteReason = aMuteReason || null;
|
|
||||||
modifiedAttrs.push("muted");
|
|
||||||
}
|
|
||||||
gBrowser._tabAttrModified(this, modifiedAttrs);
|
|
||||||
]]>
|
|
||||||
</body>
|
</body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ support-files =
|
||||||
head.js
|
head.js
|
||||||
|
|
||||||
[browser_all_files_referenced.js]
|
[browser_all_files_referenced.js]
|
||||||
|
skip-if = (os == 'win' && bits == 32)
|
||||||
[browser_misused_characters_in_strings.js]
|
[browser_misused_characters_in_strings.js]
|
||||||
support-files =
|
support-files =
|
||||||
bug1262648_string_with_newlines.dtd
|
bug1262648_string_with_newlines.dtd
|
||||||
|
|
|
@ -96,9 +96,6 @@ let propNameWhitelist = [
|
||||||
// Bug 1441837
|
// Bug 1441837
|
||||||
{propName: "--in-content-category-text-active",
|
{propName: "--in-content-category-text-active",
|
||||||
isFromDevTools: false},
|
isFromDevTools: false},
|
||||||
// Bug 1441844
|
|
||||||
{propName: "--chrome-nav-bar-separator-color",
|
|
||||||
isFromDevTools: false},
|
|
||||||
// Bug 1441855
|
// Bug 1441855
|
||||||
{propName: "--chrome-nav-buttons-background",
|
{propName: "--chrome-nav-buttons-background",
|
||||||
isFromDevTools: false},
|
isFromDevTools: false},
|
||||||
|
|
|
@ -35,3 +35,4 @@ skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug
|
||||||
[browser_visibleTabs_bookmarkAllTabs.js]
|
[browser_visibleTabs_bookmarkAllTabs.js]
|
||||||
[browser_visibleTabs_contextMenu.js]
|
[browser_visibleTabs_contextMenu.js]
|
||||||
[browser_open_newtab_start_observer_notification.js]
|
[browser_open_newtab_start_observer_notification.js]
|
||||||
|
[browser_bug_1387976_restore_lazy_tab_browser_muted_state.js]
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { TabState } = ChromeUtils.import("resource:///modules/sessionstore/TabState.jsm", {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate a restart of a tab by removing it, then add a lazy tab
|
||||||
|
* which is restored with the tabData of the removed tab.
|
||||||
|
*
|
||||||
|
* @param tab
|
||||||
|
* The tab to restart.
|
||||||
|
* @return {Object} the restored lazy tab
|
||||||
|
*/
|
||||||
|
const restartTab = async function(tab) {
|
||||||
|
let tabData = TabState.clone(tab);
|
||||||
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
|
||||||
|
let restoredLazyTab = BrowserTestUtils.addTab(gBrowser, "", {createLazyBrowser: true});
|
||||||
|
SessionStore.setTabState(restoredLazyTab, JSON.stringify(tabData));
|
||||||
|
return restoredLazyTab;
|
||||||
|
};
|
||||||
|
|
||||||
|
function get_tab_state(tab) {
|
||||||
|
const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||||
|
return JSON.parse(ss.getTabState(tab));
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
const tab = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/");
|
||||||
|
const browser = gBrowser.getBrowserForTab(tab);
|
||||||
|
await BrowserTestUtils.browserLoaded(browser);
|
||||||
|
|
||||||
|
// Let's make sure the tab is not in a muted state at the beginning
|
||||||
|
ok(!("muted" in get_tab_state(tab)), "Tab should not be in a muted state");
|
||||||
|
|
||||||
|
info("toggling Muted audio...");
|
||||||
|
tab.toggleMuteAudio();
|
||||||
|
|
||||||
|
ok("muted" in get_tab_state(tab), "Tab should be in a muted state");
|
||||||
|
|
||||||
|
info("Restarting tab...");
|
||||||
|
let restartedTab = await restartTab(tab);
|
||||||
|
|
||||||
|
ok("muted" in get_tab_state(restartedTab), "Restored tab should still be in a muted state after restart");
|
||||||
|
|
||||||
|
BrowserTestUtils.removeTab(restartedTab);
|
||||||
|
});
|
|
@ -89,7 +89,7 @@ var gTests = [
|
||||||
await BrowserTestUtils.removeTab(tab);
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
|
||||||
// Check that we still show the sharing indicators for the first tab's stream.
|
// Check that we still show the sharing indicators for the first tab's stream.
|
||||||
await promiseWaitForCondition(() => !webrtcUI.showCameraIndicator);
|
await TestUtils.waitForCondition(() => !webrtcUI.showCameraIndicator);
|
||||||
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
|
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
|
||||||
ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
|
ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
|
||||||
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
|
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
|
||||||
|
@ -192,7 +192,7 @@ var gTests = [
|
||||||
await BrowserTestUtils.removeTab(tab);
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
|
||||||
// Check that we still show the sharing indicators for the first tab's stream.
|
// Check that we still show the sharing indicators for the first tab's stream.
|
||||||
await promiseWaitForCondition(() => webrtcUI.showCameraIndicator);
|
await TestUtils.waitForCondition(() => webrtcUI.showCameraIndicator);
|
||||||
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
|
ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
|
||||||
ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
|
ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
|
||||||
ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
|
ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
|
||||||
|
|
|
@ -67,7 +67,7 @@ var gTests = [
|
||||||
menulist.getItemAtIndex(2).doCommand();
|
menulist.getItemAtIndex(2).doCommand();
|
||||||
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
||||||
"the 'all windows will be shared' warning should now be visible");
|
"the 'all windows will be shared' warning should now be visible");
|
||||||
await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100);
|
await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden, 100, 100);
|
||||||
ok(!document.getElementById("webRTC-preview").hidden,
|
ok(!document.getElementById("webRTC-preview").hidden,
|
||||||
"the preview area is visible");
|
"the preview area is visible");
|
||||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||||
|
@ -169,7 +169,7 @@ var gTests = [
|
||||||
menulist.getItemAtIndex(scaryIndex).doCommand();
|
menulist.getItemAtIndex(scaryIndex).doCommand();
|
||||||
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
||||||
"the 'all windows will be shared' warning should still be hidden");
|
"the 'all windows will be shared' warning should still be hidden");
|
||||||
await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
||||||
ok(!document.getElementById("webRTC-preview").hidden,
|
ok(!document.getElementById("webRTC-preview").hidden,
|
||||||
"the preview area is visible");
|
"the preview area is visible");
|
||||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||||
|
@ -186,7 +186,7 @@ var gTests = [
|
||||||
menulist.getItemAtIndex(nonScaryIndex).doCommand();
|
menulist.getItemAtIndex(nonScaryIndex).doCommand();
|
||||||
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
||||||
"the 'all windows will be shared' warning should still be hidden");
|
"the 'all windows will be shared' warning should still be hidden");
|
||||||
await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
||||||
ok(!document.getElementById("webRTC-preview").hidden,
|
ok(!document.getElementById("webRTC-preview").hidden,
|
||||||
"the preview area is visible");
|
"the preview area is visible");
|
||||||
ok(document.getElementById("webRTC-previewWarning").hidden,
|
ok(document.getElementById("webRTC-previewWarning").hidden,
|
||||||
|
@ -277,7 +277,7 @@ var gTests = [
|
||||||
menulist.getItemAtIndex(2).doCommand();
|
menulist.getItemAtIndex(2).doCommand();
|
||||||
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
ok(document.getElementById("webRTC-all-windows-shared").hidden,
|
||||||
"the 'all windows will be shared' warning should still be hidden");
|
"the 'all windows will be shared' warning should still be hidden");
|
||||||
await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
||||||
ok(!document.getElementById("webRTC-preview").hidden,
|
ok(!document.getElementById("webRTC-preview").hidden,
|
||||||
"the preview area is visible");
|
"the preview area is visible");
|
||||||
ok(document.getElementById("webRTC-previewWarning").hidden,
|
ok(document.getElementById("webRTC-previewWarning").hidden,
|
||||||
|
@ -328,7 +328,7 @@ var gTests = [
|
||||||
menulist.getItemAtIndex(2).doCommand();
|
menulist.getItemAtIndex(2).doCommand();
|
||||||
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
||||||
"the 'all windows will be shared' warning should now be visible");
|
"the 'all windows will be shared' warning should now be visible");
|
||||||
await promiseWaitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
await TestUtils.waitForCondition(() => !document.getElementById("webRTC-preview").hidden);
|
||||||
ok(!document.getElementById("webRTC-preview").hidden,
|
ok(!document.getElementById("webRTC-preview").hidden,
|
||||||
"the preview area is visible");
|
"the preview area is visible");
|
||||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||||
|
@ -498,7 +498,7 @@ var gTests = [
|
||||||
Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
|
Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
|
||||||
let elt = win.document.getElementById("screenShareButton");
|
let elt = win.document.getElementById("screenShareButton");
|
||||||
EventUtils.synthesizeMouseAtCenter(elt, {}, win);
|
EventUtils.synthesizeMouseAtCenter(elt, {}, win);
|
||||||
await promiseWaitForCondition(() => !gIdentityHandler._identityPopup.hidden);
|
await TestUtils.waitForCondition(() => !gIdentityHandler._identityPopup.hidden);
|
||||||
}
|
}
|
||||||
ok(!gIdentityHandler._identityPopup.hidden, "control center should be open");
|
ok(!gIdentityHandler._identityPopup.hidden, "control center should be open");
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
--chrome-color: rgb(249, 249, 250);
|
--chrome-color: rgb(249, 249, 250);
|
||||||
--chrome-secondary-background-color: hsl(240, 1%, 20%);
|
--chrome-secondary-background-color: hsl(240, 1%, 20%);
|
||||||
--toolbox-border-bottom-color: hsla(240, 5%, 5%, .1);
|
--toolbox-border-bottom-color: hsla(240, 5%, 5%, .1);
|
||||||
--chrome-nav-bar-separator-color: rgba(0,0,0,.2);
|
|
||||||
--chrome-nav-buttons-background: hsla(240, 5%, 5%, .1);
|
--chrome-nav-buttons-background: hsla(240, 5%, 5%, .1);
|
||||||
--chrome-nav-buttons-hover-background: hsla(240, 5%, 5%, .15);
|
--chrome-nav-buttons-hover-background: hsla(240, 5%, 5%, .15);
|
||||||
--chrome-nav-bar-controls-border-color: hsla(240, 5%, 5%, .3);
|
--chrome-nav-bar-controls-border-color: hsla(240, 5%, 5%, .3);
|
||||||
|
@ -41,7 +40,6 @@
|
||||||
--chrome-color: #18191a;
|
--chrome-color: #18191a;
|
||||||
--chrome-secondary-background-color: #f5f6f7;
|
--chrome-secondary-background-color: #f5f6f7;
|
||||||
--toolbox-border-bottom-color: #cccccc;
|
--toolbox-border-bottom-color: #cccccc;
|
||||||
--chrome-nav-bar-separator-color: #B6B6B8;
|
|
||||||
--chrome-nav-buttons-background: #ffffff; /* --theme-body-background */
|
--chrome-nav-buttons-background: #ffffff; /* --theme-body-background */
|
||||||
--chrome-nav-buttons-hover-background: #DADBDB;
|
--chrome-nav-buttons-hover-background: #DADBDB;
|
||||||
--chrome-nav-bar-controls-border-color: #ccc;
|
--chrome-nav-bar-controls-border-color: #ccc;
|
||||||
|
|
|
@ -4,11 +4,6 @@
|
||||||
|
|
||||||
%include ../shared/compacttheme.inc.css
|
%include ../shared/compacttheme.inc.css
|
||||||
|
|
||||||
:root {
|
|
||||||
/* Matches the #browser-border-start, #browser-border-end color */
|
|
||||||
--chrome-nav-bar-separator-color: rgba(10, 31, 51, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The window background is white due to no accentcolor in the lightweight
|
/* The window background is white due to no accentcolor in the lightweight
|
||||||
theme. It can't be changed to transparent when there is no compositor
|
theme. It can't be changed to transparent when there is no compositor
|
||||||
(Win 7 in classic / basic theme), or else dragging and focus become
|
(Win 7 in classic / basic theme), or else dragging and focus become
|
||||||
|
|
|
@ -1,264 +1,264 @@
|
||||||
# This file contains an extensive compile-time blacklist for silencing highly
|
# This file contains an extensive compile-time blacklist for silencing highly
|
||||||
# frequent signed integer overflows in our codebase, found by the use of
|
# frequent signed integer overflows in our codebase, found by the use of
|
||||||
# -fsanitize=signed-integer-overflow. C/C++ say signed integer overflow is
|
# -fsanitize=signed-integer-overflow. C/C++ say signed integer overflow is
|
||||||
# undefined behavior, so instances of this need to be fixed. But not all code
|
# undefined behavior, so instances of this need to be fixed. But not all code
|
||||||
# has been properly written to not overflow, and overflow-checking can have
|
# has been properly written to not overflow, and overflow-checking can have
|
||||||
# significant compile time and runtime costs, so we will sometimes disable
|
# significant compile time and runtime costs, so we will sometimes disable
|
||||||
# signed overflow checking.
|
# signed overflow checking.
|
||||||
#
|
#
|
||||||
# The rules in this file are applied at compile time; changes to this list
|
# The rules in this file are applied at compile time; changes to this list
|
||||||
# usually require a full rebuild to apply. If you can modify the source in
|
# usually require a full rebuild to apply. If you can modify the source in
|
||||||
# question to exempt individual functions using MOZ_NO_SANITIZE_SINT_OVERFLOW,
|
# question to exempt specific functions using MOZ_NO_SANITIZE_SIGNED_OVERFLOW,
|
||||||
# do that instead.
|
# do that instead.
|
||||||
#
|
#
|
||||||
# The extensive number of entries below is for two reasons.
|
# The extensive number of entries below is for two reasons.
|
||||||
#
|
#
|
||||||
# First, compiler instrumentation for signed integer overflows has a cost, at
|
# First, compiler instrumentation for signed integer overflows has a cost, at
|
||||||
# compile time and at runtime. In performance-critical code proven to have no
|
# compile time and at runtime. In performance-critical code proven to have no
|
||||||
# signed overflow, it makes sense to turn off overflow detection to avoid both
|
# signed overflow, it makes sense to turn off overflow detection to avoid both
|
||||||
# costs. (Indeed, -fsanitize=signed-integer-overflow is unusably slow without
|
# costs. (Indeed, -fsanitize=signed-integer-overflow is unusably slow without
|
||||||
# this.)
|
# this.)
|
||||||
#
|
#
|
||||||
# Second, many entries here are overly aggressive to get the build into a state
|
# Second, many entries here are overly aggressive to get the build into a state
|
||||||
# that allows any testing to happen at all. Some of the entries here are for
|
# that allows any testing to happen at all. Some of the entries here are for
|
||||||
# issues that are highly frequent in our test suites -- over 500 times per run.
|
# issues that are highly frequent in our test suites -- over 500 times per run.
|
||||||
# Aggressive entries now let us start using this mode, without having to first
|
# Aggressive entries now let us start using this mode, without having to first
|
||||||
# fix wide swaths of existing code.
|
# fix wide swaths of existing code.
|
||||||
#
|
#
|
||||||
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
||||||
# can be moved out of this centralized file, into source-level blacklist
|
# can be moved out of this centralized file, into source-level blacklist
|
||||||
# attributes on individual functions.
|
# attributes on individual functions.
|
||||||
|
|
||||||
# All entries in this file are to suppress signed-integer-overflow problems.
|
# All entries in this file are to suppress signed-integer-overflow problems.
|
||||||
# Blacklists for other reasons should go in separate blacklist files.
|
# Blacklists for other reasons should go in separate blacklist files.
|
||||||
[signed-integer-overflow]
|
[signed-integer-overflow]
|
||||||
|
|
||||||
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
||||||
# a language implementation can depend on compiler-specific behavior where C/C++
|
# a language implementation can depend on compiler-specific behavior where C/C++
|
||||||
# leave the behavior undefined.
|
# leave the behavior undefined.
|
||||||
src:*bits/basic_string.h
|
src:*bits/basic_string.h
|
||||||
|
|
||||||
# Assume everything running through CheckedInt.h is ok. Signed overflows here
|
# Assume everything running through CheckedInt.h is ok. Signed overflows here
|
||||||
# should generally have been guarded by safe overflow checks, so it's likely
|
# should generally have been guarded by safe overflow checks, so it's likely
|
||||||
# safe to exempt it from overflow checking. (This should eventually be verified
|
# safe to exempt it from overflow checking. (This should eventually be verified
|
||||||
# and functions individually tagged safe so this entry can be removed.)
|
# and functions individually tagged safe so this entry can be removed.)
|
||||||
src:*/CheckedInt.h
|
src:*/CheckedInt.h
|
||||||
|
|
||||||
# Exclude bignum
|
# Exclude bignum
|
||||||
src:*/mfbt/double-conversion/source/bignum.cc
|
src:*/mfbt/double-conversion/source/bignum.cc
|
||||||
|
|
||||||
# Exclude anything within gtests
|
# Exclude anything within gtests
|
||||||
src:*/gtest/*
|
src:*/gtest/*
|
||||||
|
|
||||||
# Atomics can overflow, but without a full stack we can't trace these back
|
# Atomics can overflow, but without a full stack we can't trace these back
|
||||||
# to what is actually causing the overflow. Ignoring these for now, as it will
|
# to what is actually causing the overflow. Ignoring these for now, as it will
|
||||||
# be too much effort to determine every single source here.
|
# be too much effort to determine every single source here.
|
||||||
src:*/mfbt/Atomics.h
|
src:*/mfbt/Atomics.h
|
||||||
|
|
||||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||||
# arithmetics and crypto.
|
# arithmetics and crypto.
|
||||||
src:*/security/nss/lib/freebl/mpi/*
|
src:*/security/nss/lib/freebl/mpi/*
|
||||||
src:*/security/nss/lib/freebl/ecl/*
|
src:*/security/nss/lib/freebl/ecl/*
|
||||||
|
|
||||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||||
fun:*nsTArray_base*ShiftData*
|
fun:*nsTArray_base*ShiftData*
|
||||||
|
|
||||||
### Frequent 0 - 1 overflows
|
### Frequent 0 - 1 overflows
|
||||||
#
|
#
|
||||||
# We have several code patterns in our codebase that cause these overflows,
|
# We have several code patterns in our codebase that cause these overflows,
|
||||||
# but they are typically all harmless and could be filtered easily at runtime.
|
# but they are typically all harmless and could be filtered easily at runtime.
|
||||||
# However, some of them are so frequent that suppressing them at compile-time
|
# However, some of them are so frequent that suppressing them at compile-time
|
||||||
# makes sense to increase runtime performance.
|
# makes sense to increase runtime performance.
|
||||||
#
|
#
|
||||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||||
src:*/dom/xul/XULDocument.cpp
|
src:*/dom/xul/XULDocument.cpp
|
||||||
src:*/nsCharTraits.h
|
src:*/nsCharTraits.h
|
||||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||||
# Code in layout/painting/nsDisplayList.cpp
|
# Code in layout/painting/nsDisplayList.cpp
|
||||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||||
# Code in modules/libpref/Preferences.cpp
|
# Code in modules/libpref/Preferences.cpp
|
||||||
fun:*pref_InitInitialObjects*
|
fun:*pref_InitInitialObjects*
|
||||||
# Code in netwerk/base/nsIOService.cpp
|
# Code in netwerk/base/nsIOService.cpp
|
||||||
fun:*nsIOService*GetCachedProtocolHandler*
|
fun:*nsIOService*GetCachedProtocolHandler*
|
||||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||||
fun:*0nsCSSRuleProcessor@@*
|
fun:*0nsCSSRuleProcessor@@*
|
||||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||||
fun:*TreeMatchContext*InitAncestors*
|
fun:*TreeMatchContext*InitAncestors*
|
||||||
fun:*TreeMatchContext*InitStyleScopes*
|
fun:*TreeMatchContext*InitStyleScopes*
|
||||||
# Code in layout/xul/nsXULPopupManager.cpp
|
# Code in layout/xul/nsXULPopupManager.cpp
|
||||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||||
# Code in dom/base/nsDocument.cpp
|
# Code in dom/base/nsDocument.cpp
|
||||||
fun:*1nsDocument@@*
|
fun:*1nsDocument@@*
|
||||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||||
fun:*CompositorBridgeChild*Destroy*
|
fun:*CompositorBridgeChild*Destroy*
|
||||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||||
fun:*ImageBridgeChild*ShutdownStep1*
|
fun:*ImageBridgeChild*ShutdownStep1*
|
||||||
# Code in dom/base/nsGlobalWindow.cpp
|
# Code in dom/base/nsGlobalWindow.cpp
|
||||||
fun:*nsGlobalWindow*ClearControllers*
|
fun:*nsGlobalWindow*ClearControllers*
|
||||||
# Code in layout/style/AnimationCollection.cpp
|
# Code in layout/style/AnimationCollection.cpp
|
||||||
fun:*AnimationCollection*PropertyDtor*
|
fun:*AnimationCollection*PropertyDtor*
|
||||||
# Code in layout/style/nsStyleSet.cpp
|
# Code in layout/style/nsStyleSet.cpp
|
||||||
fun:*nsStyleSet*AddImportantRules*
|
fun:*nsStyleSet*AddImportantRules*
|
||||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||||
|
|
||||||
|
|
||||||
### Misc overflows
|
### Misc overflows
|
||||||
|
|
||||||
# Hot function in protobuf producing overflows
|
# Hot function in protobuf producing overflows
|
||||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||||
|
|
||||||
|
|
||||||
# SQLite3 is full of overflows :/
|
# SQLite3 is full of overflows :/
|
||||||
src:*/db/sqlite3/src/sqlite3.c
|
src:*/db/sqlite3/src/sqlite3.c
|
||||||
|
|
||||||
# zlib has some overflows, we can't deal with them right now
|
# zlib has some overflows, we can't deal with them right now
|
||||||
src:*/modules/zlib/src/*
|
src:*/modules/zlib/src/*
|
||||||
|
|
||||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||||
# miss some unintended overflows in that implementation, but we can't
|
# miss some unintended overflows in that implementation, but we can't
|
||||||
# check for it right now.
|
# check for it right now.
|
||||||
src:*/mfbt/lz4.c
|
src:*/mfbt/lz4.c
|
||||||
|
|
||||||
# Apparently this overflows a lot, because it contains some allocators
|
# Apparently this overflows a lot, because it contains some allocators
|
||||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||||
# to work here for operator new.
|
# to work here for operator new.
|
||||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||||
|
|
||||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||||
# We probably don't care about the frequent overflows there.
|
# We probably don't care about the frequent overflows there.
|
||||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||||
|
|
||||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||||
fun:*WeightDistance*
|
fun:*WeightDistance*
|
||||||
|
|
||||||
# Another frequent overflower
|
# Another frequent overflower
|
||||||
fun:*nsTObserverArray_base*AdjustIterators*
|
fun:*nsTObserverArray_base*AdjustIterators*
|
||||||
|
|
||||||
# Overflows in Skia
|
# Overflows in Skia
|
||||||
fun:*SkPathRef*makeSpace*
|
fun:*SkPathRef*makeSpace*
|
||||||
fun:*SkPathRef*resetToSize*
|
fun:*SkPathRef*resetToSize*
|
||||||
|
|
||||||
# Expat Parser has some overflows
|
# Expat Parser has some overflows
|
||||||
fun:*nsExpatDriver*ConsumeToken*
|
fun:*nsExpatDriver*ConsumeToken*
|
||||||
|
|
||||||
# Frequent overflowers in harfbuzz
|
# Frequent overflowers in harfbuzz
|
||||||
fun:*hb_in_range*
|
fun:*hb_in_range*
|
||||||
fun:*OT*collect_glyphs*
|
fun:*OT*collect_glyphs*
|
||||||
|
|
||||||
# These look like harmless layouting-related overflows
|
# These look like harmless layouting-related overflows
|
||||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||||
|
|
||||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||||
# being cast to unsigned and then overflowed.
|
# being cast to unsigned and then overflowed.
|
||||||
fun:*FilePath*Append*
|
fun:*FilePath*Append*
|
||||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||||
|
|
||||||
# Code in dom/base/nsJSEnvironment.cpp
|
# Code in dom/base/nsJSEnvironment.cpp
|
||||||
fun:*FireForgetSkippable*
|
fun:*FireForgetSkippable*
|
||||||
|
|
||||||
# Code in gfx/thebes/gfxSkipChars.h
|
# Code in gfx/thebes/gfxSkipChars.h
|
||||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||||
|
|
||||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||||
fun:*gfxScriptItemizer*fixup*
|
fun:*gfxScriptItemizer*fixup*
|
||||||
fun:*gfxScriptItemizer*push*
|
fun:*gfxScriptItemizer*push*
|
||||||
|
|
||||||
# Code in dom/base/nsDocument.cpp
|
# Code in dom/base/nsDocument.cpp
|
||||||
fun:*nsDocument*BlockOnload*
|
fun:*nsDocument*BlockOnload*
|
||||||
|
|
||||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||||
|
|
||||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||||
fun:*PL_ArenaRelease*
|
fun:*PL_ArenaRelease*
|
||||||
|
|
||||||
# This file contains a bunch of arithmetic operations on timestamps that
|
# This file contains a bunch of arithmetic operations on timestamps that
|
||||||
# apparently are allowed to overflow.
|
# apparently are allowed to overflow.
|
||||||
src:*/src/widget/SystemTimeConverter.h
|
src:*/src/widget/SystemTimeConverter.h
|
||||||
|
|
||||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||||
fun:*Frame*FindNext*
|
fun:*Frame*FindNext*
|
||||||
|
|
||||||
# Code in netwerk/base/nsStandardURL.cpp,
|
# Code in netwerk/base/nsStandardURL.cpp,
|
||||||
# these methods return signed but the subtraction is first performed unsigned
|
# these methods return signed but the subtraction is first performed unsigned
|
||||||
fun:*nsStandardURL*ReplaceSegment*
|
fun:*nsStandardURL*ReplaceSegment*
|
||||||
|
|
||||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||||
# same as previous with the previous entry.
|
# same as previous with the previous entry.
|
||||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||||
|
|
||||||
# Code in layout/tables/nsCellMap.cpp
|
# Code in layout/tables/nsCellMap.cpp
|
||||||
# again subtraction then cast to signed.
|
# again subtraction then cast to signed.
|
||||||
fun:*nsTableCellMap*GetColInfoAt*
|
fun:*nsTableCellMap*GetColInfoAt*
|
||||||
|
|
||||||
# Code in layout/generic/nsTextFrame.cpp
|
# Code in layout/generic/nsTextFrame.cpp
|
||||||
# again subtraction then cast to signed.
|
# again subtraction then cast to signed.
|
||||||
fun:*nsTextFrame*CharacterDataChanged*
|
fun:*nsTextFrame*CharacterDataChanged*
|
||||||
|
|
||||||
# Not sure what is going on in this file, but it doesn't look
|
# Not sure what is going on in this file, but it doesn't look
|
||||||
# related to what we are looking for.
|
# related to what we are looking for.
|
||||||
src:*/xpcom/base/CountingAllocatorBase.h
|
src:*/xpcom/base/CountingAllocatorBase.h
|
||||||
|
|
||||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||||
# Timestamp related, probably expecting the overflow
|
# Timestamp related, probably expecting the overflow
|
||||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||||
|
|
||||||
# Several unsigned arithmetic operations with -1
|
# Several unsigned arithmetic operations with -1
|
||||||
src:*/hal/HalWakeLock.cpp
|
src:*/hal/HalWakeLock.cpp
|
||||||
|
|
||||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||||
# somewhat frequent signed integer overflows. Probably harmless
|
# somewhat frequent signed integer overflows. Probably harmless
|
||||||
# because it's layout code.
|
# because it's layout code.
|
||||||
fun:*ClampAndAlignWithPixels*
|
fun:*ClampAndAlignWithPixels*
|
||||||
|
|
||||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||||
fun:*ClockResolutionNs*
|
fun:*ClockResolutionNs*
|
||||||
|
|
||||||
# This header has all sorts of operators that do post-operation
|
# This header has all sorts of operators that do post-operation
|
||||||
# overflow and underflow checking, triggering frequent reports
|
# overflow and underflow checking, triggering frequent reports
|
||||||
src:*/mozglue/misc/TimeStamp.h
|
src:*/mozglue/misc/TimeStamp.h
|
||||||
|
|
||||||
#
|
#
|
||||||
# Various hashing functions, both regular and cryptographic ones
|
# Various hashing functions, both regular and cryptographic ones
|
||||||
#
|
#
|
||||||
src:*/dom/canvas/MurmurHash3.cpp
|
src:*/dom/canvas/MurmurHash3.cpp
|
||||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||||
src:*/intl/icu/source/common/unifiedcache.h
|
src:*/intl/icu/source/common/unifiedcache.h
|
||||||
src:*/mfbt/SHA1.cpp
|
src:*/mfbt/SHA1.cpp
|
||||||
src:*/modules/zlib/src/adler32.c
|
src:*/modules/zlib/src/adler32.c
|
||||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||||
src:*/nsprpub/lib/ds/plhash.c
|
src:*/nsprpub/lib/ds/plhash.c
|
||||||
src:*/security/manager/ssl/md4.c
|
src:*/security/manager/ssl/md4.c
|
||||||
src:*/security/nss/lib/dbm/src/h_func.c
|
src:*/security/nss/lib/dbm/src/h_func.c
|
||||||
src:*/security/nss/lib/freebl/sha512.c
|
src:*/security/nss/lib/freebl/sha512.c
|
||||||
src:*/security/nss/lib/freebl/md5.c
|
src:*/security/nss/lib/freebl/md5.c
|
||||||
src:*/xpcom/ds/PLDHashTable.cpp
|
src:*/xpcom/ds/PLDHashTable.cpp
|
||||||
|
|
||||||
# Hash/Cache function in Skia
|
# Hash/Cache function in Skia
|
||||||
fun:*GradientShaderCache*Build32bitCache*
|
fun:*GradientShaderCache*Build32bitCache*
|
||||||
|
|
||||||
# Hashing functions in Cairo
|
# Hashing functions in Cairo
|
||||||
fun:*_hash_matrix_fnv*
|
fun:*_hash_matrix_fnv*
|
||||||
fun:*_hash_mix_bits*
|
fun:*_hash_mix_bits*
|
||||||
fun:*_cairo_hash_string*
|
fun:*_cairo_hash_string*
|
||||||
fun:*_cairo_hash_bytes*
|
fun:*_cairo_hash_bytes*
|
||||||
|
|
||||||
# intl code hashing functions
|
# intl code hashing functions
|
||||||
fun:*ustr_hash*CharsN*
|
fun:*ustr_hash*CharsN*
|
||||||
fun:*hashEntry*
|
fun:*hashEntry*
|
||||||
|
|
||||||
# harfbuzz hash/digest functions
|
# harfbuzz hash/digest functions
|
||||||
fun:*hb_set_digest_lowest_bits_t*
|
fun:*hb_set_digest_lowest_bits_t*
|
||||||
|
|
||||||
# Hash function in gfx
|
# Hash function in gfx
|
||||||
fun:*gfxFontStyle*Hash*
|
fun:*gfxFontStyle*Hash*
|
||||||
|
|
||||||
# expat uses a CHAR_HASH macro in several places that causes
|
# expat uses a CHAR_HASH macro in several places that causes
|
||||||
# a high amount of overflows. We should try finding a better
|
# a high amount of overflows. We should try finding a better
|
||||||
# way to disable this rather than blacklisting the whole thing.
|
# way to disable this rather than blacklisting the whole thing.
|
||||||
src:*/parser/expat/*
|
src:*/parser/expat/*
|
||||||
|
|
|
@ -1,271 +1,271 @@
|
||||||
# This file contains an extensive compile-time blacklist for silencing highly
|
# This file contains an extensive compile-time blacklist for silencing highly
|
||||||
# frequent *un*signed integer overflows in our codebase, found by the use of
|
# frequent *un*signed integer overflows in our codebase, found by the use of
|
||||||
# -fsanitize=unsigned-integer-overflow. Such overflows are not necessarily
|
# -fsanitize=unsigned-integer-overflow. Such overflows are not necessarily
|
||||||
# bugs -- unsigned integer overflow has well-defined semantics in C/C++. But
|
# bugs -- unsigned integer overflow has well-defined semantics in C/C++. But
|
||||||
# overflow may still be *unexpected* and incorrectly handled, so we try to
|
# overflow may still be *unexpected* and incorrectly handled, so we try to
|
||||||
# annotate those places where unsigned overflow is correct and desired.
|
# annotate those places where unsigned overflow is correct and desired.
|
||||||
#
|
#
|
||||||
# The rules in this file are applied at compile time; changes to this list
|
# The rules in this file are applied at compile time; changes to this list
|
||||||
# usually require a full rebuild to apply. If you can modify the source in
|
# usually require a full rebuild to apply. If you can modify the source in
|
||||||
# question to exempt individual functions using MOZ_NO_SANITIZE_UINT_OVERFLOW,
|
# question to exempt specific functions using MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW,
|
||||||
# do that instead.
|
# do that instead.
|
||||||
#
|
#
|
||||||
# The extensive number of entries below is for two reasons.
|
# The extensive number of entries below is for two reasons.
|
||||||
#
|
#
|
||||||
# First, compiler instrumentation for unsigned integer overflows has a cost, at
|
# First, compiler instrumentation for unsigned integer overflows has a cost, at
|
||||||
# compile time and at runtime. In places where code expects and depends upon
|
# compile time and at runtime. In places where code expects and depends upon
|
||||||
# overflow behavior -- and especially in performance-critical code -- it makes
|
# overflow behavior -- and especially in performance-critical code -- it makes
|
||||||
# sense to turn off overflow detection to avoid both costs. (Indeed,
|
# sense to turn off overflow detection to avoid both costs. (Indeed,
|
||||||
# -fsanitize=signed-integer-overflow is unusably slow without this.)
|
# -fsanitize=signed-integer-overflow is unusably slow without this.)
|
||||||
#
|
#
|
||||||
# Second, many entries here are overly aggressive to get the build into a state
|
# Second, many entries here are overly aggressive to get the build into a state
|
||||||
# that allows any testing to happen at all. Some of the entries here are for
|
# that allows any testing to happen at all. Some of the entries here are for
|
||||||
# issues that are highly frequent in our test suites -- over 500 times per run.
|
# issues that are highly frequent in our test suites -- over 500 times per run.
|
||||||
# Aggressive entries now let us start using this mode, without having to first
|
# Aggressive entries now let us start using this mode, without having to first
|
||||||
# fix wide swaths of existing code.
|
# fix wide swaths of existing code.
|
||||||
#
|
#
|
||||||
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
||||||
# can be moved out of this centralized file, into source-level blacklist
|
# can be moved out of this centralized file, into source-level blacklist
|
||||||
# attributes on individual functions.
|
# attributes on individual functions.
|
||||||
|
|
||||||
# All entries in this file are to suppress unsigned-integer-overflow problems.
|
# All entries in this file are to suppress unsigned-integer-overflow problems.
|
||||||
# Blacklists for other reasons should go in separate blacklist files.
|
# Blacklists for other reasons should go in separate blacklist files.
|
||||||
[unsigned-integer-overflow]
|
[unsigned-integer-overflow]
|
||||||
|
|
||||||
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
||||||
# a language implementation can depend on compiler-specific behavior where C/C++
|
# a language implementation can depend on compiler-specific behavior where C/C++
|
||||||
# leave the behavior undefined.
|
# leave the behavior undefined.
|
||||||
src:*bits/basic_string.h
|
src:*bits/basic_string.h
|
||||||
|
|
||||||
# Assume everything running through CheckedInt.h is ok. The CheckedInt class
|
# Assume everything running through CheckedInt.h is ok. The CheckedInt class
|
||||||
# casts signed integers to unsigned first and then does a post-overflow
|
# casts signed integers to unsigned first and then does a post-overflow
|
||||||
# check causing lots of unsigned integer overflow messages.
|
# check causing lots of unsigned integer overflow messages.
|
||||||
src:*/CheckedInt.h
|
src:*/CheckedInt.h
|
||||||
|
|
||||||
# Exclude bignum
|
# Exclude bignum
|
||||||
src:*/mfbt/double-conversion/source/bignum.cc
|
src:*/mfbt/double-conversion/source/bignum.cc
|
||||||
|
|
||||||
# Exclude anything within gtests
|
# Exclude anything within gtests
|
||||||
src:*/gtest/*
|
src:*/gtest/*
|
||||||
|
|
||||||
# The JS engine has a lot of code doing all sorts of overflows. This code
|
# The JS engine has a lot of code doing all sorts of overflows. This code
|
||||||
# is pretty well tested though and excluding it here will allow us to go
|
# is pretty well tested though and excluding it here will allow us to go
|
||||||
# for other, less tested code. Ideally, we would include the JS engine here
|
# for other, less tested code. Ideally, we would include the JS engine here
|
||||||
# at some point.
|
# at some point.
|
||||||
src:*/js/src/*
|
src:*/js/src/*
|
||||||
src:*/js/public/*
|
src:*/js/public/*
|
||||||
src:*/js/*.h
|
src:*/js/*.h
|
||||||
src:*/jsfriendapi.h
|
src:*/jsfriendapi.h
|
||||||
|
|
||||||
# Atomics can overflow, but without a full stack we can't trace these back
|
# Atomics can overflow, but without a full stack we can't trace these back
|
||||||
# to what is actually causing the overflow. Ignoring these for now, as it will
|
# to what is actually causing the overflow. Ignoring these for now, as it will
|
||||||
# be too much effort to determine every single source here.
|
# be too much effort to determine every single source here.
|
||||||
src:*/mfbt/Atomics.h
|
src:*/mfbt/Atomics.h
|
||||||
|
|
||||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||||
# arithmetics and crypto.
|
# arithmetics and crypto.
|
||||||
src:*/security/nss/lib/freebl/mpi/*
|
src:*/security/nss/lib/freebl/mpi/*
|
||||||
src:*/security/nss/lib/freebl/ecl/*
|
src:*/security/nss/lib/freebl/ecl/*
|
||||||
|
|
||||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||||
fun:*nsTArray_base*ShiftData*
|
fun:*nsTArray_base*ShiftData*
|
||||||
|
|
||||||
### Frequent 0 - 1 overflows
|
### Frequent 0 - 1 overflows
|
||||||
#
|
#
|
||||||
# We have several code patterns in our codebase that cause these overflows,
|
# We have several code patterns in our codebase that cause these overflows,
|
||||||
# but they are typically all harmless and could be filtered easily at runtime.
|
# but they are typically all harmless and could be filtered easily at runtime.
|
||||||
# However, some of them are so frequent that suppressing them at compile-time
|
# However, some of them are so frequent that suppressing them at compile-time
|
||||||
# makes sense to increase runtime performance.
|
# makes sense to increase runtime performance.
|
||||||
#
|
#
|
||||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||||
src:*/dom/xul/XULDocument.cpp
|
src:*/dom/xul/XULDocument.cpp
|
||||||
src:*/nsCharTraits.h
|
src:*/nsCharTraits.h
|
||||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||||
# Code in layout/painting/nsDisplayList.cpp
|
# Code in layout/painting/nsDisplayList.cpp
|
||||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||||
# Code in modules/libpref/Preferences.cpp
|
# Code in modules/libpref/Preferences.cpp
|
||||||
fun:*pref_InitInitialObjects*
|
fun:*pref_InitInitialObjects*
|
||||||
# Code in netwerk/base/nsIOService.cpp
|
# Code in netwerk/base/nsIOService.cpp
|
||||||
fun:*nsIOService*GetCachedProtocolHandler*
|
fun:*nsIOService*GetCachedProtocolHandler*
|
||||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||||
fun:*0nsCSSRuleProcessor@@*
|
fun:*0nsCSSRuleProcessor@@*
|
||||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||||
fun:*TreeMatchContext*InitAncestors*
|
fun:*TreeMatchContext*InitAncestors*
|
||||||
fun:*TreeMatchContext*InitStyleScopes*
|
fun:*TreeMatchContext*InitStyleScopes*
|
||||||
# Code in layout/xul/nsXULPopupManager.cpp
|
# Code in layout/xul/nsXULPopupManager.cpp
|
||||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||||
# Code in dom/base/nsDocument.cpp
|
# Code in dom/base/nsDocument.cpp
|
||||||
fun:*1nsDocument@@*
|
fun:*1nsDocument@@*
|
||||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||||
fun:*CompositorBridgeChild*Destroy*
|
fun:*CompositorBridgeChild*Destroy*
|
||||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||||
fun:*ImageBridgeChild*ShutdownStep1*
|
fun:*ImageBridgeChild*ShutdownStep1*
|
||||||
# Code in dom/base/nsGlobalWindow.cpp
|
# Code in dom/base/nsGlobalWindow.cpp
|
||||||
fun:*nsGlobalWindow*ClearControllers*
|
fun:*nsGlobalWindow*ClearControllers*
|
||||||
# Code in layout/style/AnimationCollection.cpp
|
# Code in layout/style/AnimationCollection.cpp
|
||||||
fun:*AnimationCollection*PropertyDtor*
|
fun:*AnimationCollection*PropertyDtor*
|
||||||
# Code in layout/style/nsStyleSet.cpp
|
# Code in layout/style/nsStyleSet.cpp
|
||||||
fun:*nsStyleSet*AddImportantRules*
|
fun:*nsStyleSet*AddImportantRules*
|
||||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||||
|
|
||||||
|
|
||||||
### Misc overflows
|
### Misc overflows
|
||||||
|
|
||||||
# Hot function in protobuf producing overflows
|
# Hot function in protobuf producing overflows
|
||||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||||
|
|
||||||
|
|
||||||
# SQLite3 is full of overflows :/
|
# SQLite3 is full of overflows :/
|
||||||
src:*/db/sqlite3/src/sqlite3.c
|
src:*/db/sqlite3/src/sqlite3.c
|
||||||
|
|
||||||
# zlib has some overflows, we can't deal with them right now
|
# zlib has some overflows, we can't deal with them right now
|
||||||
src:*/modules/zlib/src/*
|
src:*/modules/zlib/src/*
|
||||||
|
|
||||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||||
# miss some unintended overflows in that implementation, but we can't
|
# miss some unintended overflows in that implementation, but we can't
|
||||||
# check for it right now.
|
# check for it right now.
|
||||||
src:*/mfbt/lz4.c
|
src:*/mfbt/lz4.c
|
||||||
|
|
||||||
# Apparently this overflows a lot, because it contains some allocators
|
# Apparently this overflows a lot, because it contains some allocators
|
||||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||||
# to work here for operator new.
|
# to work here for operator new.
|
||||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||||
|
|
||||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||||
# We probably don't care about the frequent overflows there.
|
# We probably don't care about the frequent overflows there.
|
||||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||||
|
|
||||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||||
fun:*WeightDistance*
|
fun:*WeightDistance*
|
||||||
|
|
||||||
# Another frequent overflower
|
# Another frequent overflower
|
||||||
fun:*nsTObserverArray_base*AdjustIterators*
|
fun:*nsTObserverArray_base*AdjustIterators*
|
||||||
|
|
||||||
# Overflows in Skia
|
# Overflows in Skia
|
||||||
fun:*SkPathRef*makeSpace*
|
fun:*SkPathRef*makeSpace*
|
||||||
fun:*SkPathRef*resetToSize*
|
fun:*SkPathRef*resetToSize*
|
||||||
|
|
||||||
# Expat Parser has some overflows
|
# Expat Parser has some overflows
|
||||||
fun:*nsExpatDriver*ConsumeToken*
|
fun:*nsExpatDriver*ConsumeToken*
|
||||||
|
|
||||||
# Frequent overflowers in harfbuzz
|
# Frequent overflowers in harfbuzz
|
||||||
fun:*hb_in_range*
|
fun:*hb_in_range*
|
||||||
fun:*OT*collect_glyphs*
|
fun:*OT*collect_glyphs*
|
||||||
|
|
||||||
# These look like harmless layouting-related overflows
|
# These look like harmless layouting-related overflows
|
||||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||||
|
|
||||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||||
# being cast to unsigned and then overflowed.
|
# being cast to unsigned and then overflowed.
|
||||||
fun:*FilePath*Append*
|
fun:*FilePath*Append*
|
||||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||||
|
|
||||||
# Code in dom/base/nsJSEnvironment.cpp
|
# Code in dom/base/nsJSEnvironment.cpp
|
||||||
fun:*FireForgetSkippable*
|
fun:*FireForgetSkippable*
|
||||||
|
|
||||||
# Code in gfx/thebes/gfxSkipChars.h
|
# Code in gfx/thebes/gfxSkipChars.h
|
||||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||||
|
|
||||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||||
fun:*gfxScriptItemizer*fixup*
|
fun:*gfxScriptItemizer*fixup*
|
||||||
fun:*gfxScriptItemizer*push*
|
fun:*gfxScriptItemizer*push*
|
||||||
|
|
||||||
# Code in dom/base/nsDocument.cpp
|
# Code in dom/base/nsDocument.cpp
|
||||||
fun:*nsDocument*BlockOnload*
|
fun:*nsDocument*BlockOnload*
|
||||||
|
|
||||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||||
|
|
||||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||||
fun:*PL_ArenaRelease*
|
fun:*PL_ArenaRelease*
|
||||||
|
|
||||||
# This file contains a bunch of arithmetic operations on timestamps that
|
# This file contains a bunch of arithmetic operations on timestamps that
|
||||||
# apparently are allowed to overflow.
|
# apparently are allowed to overflow.
|
||||||
src:*/src/widget/SystemTimeConverter.h
|
src:*/src/widget/SystemTimeConverter.h
|
||||||
|
|
||||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||||
fun:*Frame*FindNext*
|
fun:*Frame*FindNext*
|
||||||
|
|
||||||
# Code in netwerk/base/nsStandardURL.cpp,
|
# Code in netwerk/base/nsStandardURL.cpp,
|
||||||
# these methods return signed but the subtraction is first performed unsigned
|
# these methods return signed but the subtraction is first performed unsigned
|
||||||
fun:*nsStandardURL*ReplaceSegment*
|
fun:*nsStandardURL*ReplaceSegment*
|
||||||
|
|
||||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||||
# same as previous with the previous entry.
|
# same as previous with the previous entry.
|
||||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||||
|
|
||||||
# Code in layout/tables/nsCellMap.cpp
|
# Code in layout/tables/nsCellMap.cpp
|
||||||
# again subtraction then cast to signed.
|
# again subtraction then cast to signed.
|
||||||
fun:*nsTableCellMap*GetColInfoAt*
|
fun:*nsTableCellMap*GetColInfoAt*
|
||||||
|
|
||||||
# Code in layout/generic/nsTextFrame.cpp
|
# Code in layout/generic/nsTextFrame.cpp
|
||||||
# again subtraction then cast to signed.
|
# again subtraction then cast to signed.
|
||||||
fun:*nsTextFrame*CharacterDataChanged*
|
fun:*nsTextFrame*CharacterDataChanged*
|
||||||
|
|
||||||
# Not sure what is going on in this file, but it doesn't look
|
# Not sure what is going on in this file, but it doesn't look
|
||||||
# related to what we are looking for.
|
# related to what we are looking for.
|
||||||
src:*/xpcom/base/CountingAllocatorBase.h
|
src:*/xpcom/base/CountingAllocatorBase.h
|
||||||
|
|
||||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||||
# Timestamp related, probably expecting the overflow
|
# Timestamp related, probably expecting the overflow
|
||||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||||
|
|
||||||
# Several unsigned arithmetic operations with -1
|
# Several unsigned arithmetic operations with -1
|
||||||
src:*/hal/HalWakeLock.cpp
|
src:*/hal/HalWakeLock.cpp
|
||||||
|
|
||||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||||
# somewhat frequent signed integer overflows. Probably harmless
|
# somewhat frequent signed integer overflows. Probably harmless
|
||||||
# because it's layout code.
|
# because it's layout code.
|
||||||
fun:*ClampAndAlignWithPixels*
|
fun:*ClampAndAlignWithPixels*
|
||||||
|
|
||||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||||
fun:*ClockResolutionNs*
|
fun:*ClockResolutionNs*
|
||||||
|
|
||||||
# This header has all sorts of operators that do post-operation
|
# This header has all sorts of operators that do post-operation
|
||||||
# overflow and underflow checking, triggering frequent reports
|
# overflow and underflow checking, triggering frequent reports
|
||||||
src:*/mozglue/misc/TimeStamp.h
|
src:*/mozglue/misc/TimeStamp.h
|
||||||
|
|
||||||
#
|
#
|
||||||
# Various hashing functions, both regular and cryptographic ones
|
# Various hashing functions, both regular and cryptographic ones
|
||||||
#
|
#
|
||||||
src:*/dom/canvas/MurmurHash3.cpp
|
src:*/dom/canvas/MurmurHash3.cpp
|
||||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||||
src:*/intl/icu/source/common/unifiedcache.h
|
src:*/intl/icu/source/common/unifiedcache.h
|
||||||
src:*/mfbt/SHA1.cpp
|
src:*/mfbt/SHA1.cpp
|
||||||
src:*/modules/zlib/src/adler32.c
|
src:*/modules/zlib/src/adler32.c
|
||||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||||
src:*/nsprpub/lib/ds/plhash.c
|
src:*/nsprpub/lib/ds/plhash.c
|
||||||
src:*/security/manager/ssl/md4.c
|
src:*/security/manager/ssl/md4.c
|
||||||
src:*/security/nss/lib/dbm/src/h_func.c
|
src:*/security/nss/lib/dbm/src/h_func.c
|
||||||
src:*/security/nss/lib/freebl/sha512.c
|
src:*/security/nss/lib/freebl/sha512.c
|
||||||
src:*/security/nss/lib/freebl/md5.c
|
src:*/security/nss/lib/freebl/md5.c
|
||||||
src:*/xpcom/ds/PLDHashTable.cpp
|
src:*/xpcom/ds/PLDHashTable.cpp
|
||||||
|
|
||||||
# Hash/Cache function in Skia
|
# Hash/Cache function in Skia
|
||||||
fun:*GradientShaderCache*Build32bitCache*
|
fun:*GradientShaderCache*Build32bitCache*
|
||||||
|
|
||||||
# Hashing functions in Cairo
|
# Hashing functions in Cairo
|
||||||
fun:*_hash_matrix_fnv*
|
fun:*_hash_matrix_fnv*
|
||||||
fun:*_hash_mix_bits*
|
fun:*_hash_mix_bits*
|
||||||
fun:*_cairo_hash_string*
|
fun:*_cairo_hash_string*
|
||||||
fun:*_cairo_hash_bytes*
|
fun:*_cairo_hash_bytes*
|
||||||
|
|
||||||
# intl code hashing functions
|
# intl code hashing functions
|
||||||
fun:*ustr_hash*CharsN*
|
fun:*ustr_hash*CharsN*
|
||||||
fun:*hashEntry*
|
fun:*hashEntry*
|
||||||
|
|
||||||
# harfbuzz hash/digest functions
|
# harfbuzz hash/digest functions
|
||||||
fun:*hb_set_digest_lowest_bits_t*
|
fun:*hb_set_digest_lowest_bits_t*
|
||||||
|
|
||||||
# Hash function in gfx
|
# Hash function in gfx
|
||||||
fun:*gfxFontStyle*Hash*
|
fun:*gfxFontStyle*Hash*
|
||||||
|
|
||||||
# expat uses a CHAR_HASH macro in several places that causes
|
# expat uses a CHAR_HASH macro in several places that causes
|
||||||
# a high amount of overflows. We should try finding a better
|
# a high amount of overflows. We should try finding a better
|
||||||
# way to disable this rather than blacklisting the whole thing.
|
# way to disable this rather than blacklisting the whole thing.
|
||||||
src:*/parser/expat/*
|
src:*/parser/expat/*
|
||||||
|
|
|
@ -91,7 +91,8 @@ nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint
|
||||||
rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()),
|
rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()),
|
||||||
NS_ConvertUTF8toUTF16(spec),
|
NS_ConvertUTF8toUTF16(spec),
|
||||||
EmptyString(),
|
EmptyString(),
|
||||||
aLineNumber, 0, flags, "chrome registration");
|
aLineNumber, 0, flags, "chrome registration",
|
||||||
|
false /* from private window */);
|
||||||
|
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -120,8 +120,11 @@ CONFIG_TOOLS = $(MOZ_BUILD_ROOT)/config
|
||||||
AUTOCONF_TOOLS = $(MOZILLA_DIR)/build/autoconf
|
AUTOCONF_TOOLS = $(MOZILLA_DIR)/build/autoconf
|
||||||
|
|
||||||
ifdef _MSC_VER
|
ifdef _MSC_VER
|
||||||
|
# clang-cl is smart enough to generate dependencies directly.
|
||||||
|
ifndef CLANG_CL
|
||||||
CC_WRAPPER ?= $(call py_action,cl)
|
CC_WRAPPER ?= $(call py_action,cl)
|
||||||
CXX_WRAPPER ?= $(call py_action,cl)
|
CXX_WRAPPER ?= $(call py_action,cl)
|
||||||
|
endif # CLANG_CL
|
||||||
endif # _MSC_VER
|
endif # _MSC_VER
|
||||||
|
|
||||||
CC := $(CC_WRAPPER) $(CC)
|
CC := $(CC_WRAPPER) $(CC)
|
||||||
|
|
|
@ -11,6 +11,7 @@ const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const EventEmitter = require("devtools/shared/event-emitter");
|
const EventEmitter = require("devtools/shared/event-emitter");
|
||||||
|
|
||||||
const App = createFactory(require("./components/App"));
|
const App = createFactory(require("./components/App"));
|
||||||
|
const CurrentTimeTimer = require("./current-time-timer");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
updateAnimations,
|
updateAnimations,
|
||||||
|
@ -19,21 +20,38 @@ const {
|
||||||
updateSelectedAnimation,
|
updateSelectedAnimation,
|
||||||
updateSidebarSize
|
updateSidebarSize
|
||||||
} = require("./actions/animations");
|
} = require("./actions/animations");
|
||||||
const { isAllAnimationEqual } = require("./utils/utils");
|
const {
|
||||||
|
isAllAnimationEqual,
|
||||||
|
hasAnimationIterationCountInfinite,
|
||||||
|
hasRunningAnimation,
|
||||||
|
} = require("./utils/utils");
|
||||||
|
|
||||||
class AnimationInspector {
|
class AnimationInspector {
|
||||||
constructor(inspector, win) {
|
constructor(inspector, win) {
|
||||||
this.inspector = inspector;
|
this.inspector = inspector;
|
||||||
this.win = win;
|
this.win = win;
|
||||||
|
|
||||||
|
this.addAnimationsCurrentTimeListener =
|
||||||
|
this.addAnimationsCurrentTimeListener.bind(this);
|
||||||
this.getAnimatedPropertyMap = this.getAnimatedPropertyMap.bind(this);
|
this.getAnimatedPropertyMap = this.getAnimatedPropertyMap.bind(this);
|
||||||
|
this.getAnimationsCurrentTime = this.getAnimationsCurrentTime.bind(this);
|
||||||
this.getComputedStyle = this.getComputedStyle.bind(this);
|
this.getComputedStyle = this.getComputedStyle.bind(this);
|
||||||
this.getNodeFromActor = this.getNodeFromActor.bind(this);
|
this.getNodeFromActor = this.getNodeFromActor.bind(this);
|
||||||
|
this.removeAnimationsCurrentTimeListener =
|
||||||
|
this.removeAnimationsCurrentTimeListener.bind(this);
|
||||||
|
this.rewindAnimationsCurrentTime = this.rewindAnimationsCurrentTime.bind(this);
|
||||||
this.selectAnimation = this.selectAnimation.bind(this);
|
this.selectAnimation = this.selectAnimation.bind(this);
|
||||||
|
this.setAnimationsCurrentTime = this.setAnimationsCurrentTime.bind(this);
|
||||||
|
this.setAnimationsPlaybackRate = this.setAnimationsPlaybackRate.bind(this);
|
||||||
|
this.setAnimationsPlayState = this.setAnimationsPlayState.bind(this);
|
||||||
this.setDetailVisibility = this.setDetailVisibility.bind(this);
|
this.setDetailVisibility = this.setDetailVisibility.bind(this);
|
||||||
this.simulateAnimation = this.simulateAnimation.bind(this);
|
this.simulateAnimation = this.simulateAnimation.bind(this);
|
||||||
|
this.simulateAnimationForKeyframesProgressBar =
|
||||||
|
this.simulateAnimationForKeyframesProgressBar.bind(this);
|
||||||
this.toggleElementPicker = this.toggleElementPicker.bind(this);
|
this.toggleElementPicker = this.toggleElementPicker.bind(this);
|
||||||
this.update = this.update.bind(this);
|
this.update = this.update.bind(this);
|
||||||
|
this.onAnimationsCurrentTimeUpdated = this.onAnimationsCurrentTimeUpdated.bind(this);
|
||||||
|
this.onCurrentTimeTimerUpdated = this.onCurrentTimeTimerUpdated.bind(this);
|
||||||
this.onElementPickerStarted = this.onElementPickerStarted.bind(this);
|
this.onElementPickerStarted = this.onElementPickerStarted.bind(this);
|
||||||
this.onElementPickerStopped = this.onElementPickerStopped.bind(this);
|
this.onElementPickerStopped = this.onElementPickerStopped.bind(this);
|
||||||
this.onSidebarResized = this.onSidebarResized.bind(this);
|
this.onSidebarResized = this.onSidebarResized.bind(this);
|
||||||
|
@ -56,19 +74,31 @@ class AnimationInspector {
|
||||||
} = this.inspector.getPanel("boxmodel").getComponentProps();
|
} = this.inspector.getPanel("boxmodel").getComponentProps();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
emit: emitEventForTest,
|
emit: emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
getNodeFromActor,
|
getNodeFromActor,
|
||||||
|
isAnimationsRunning,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
rewindAnimationsCurrentTime,
|
||||||
selectAnimation,
|
selectAnimation,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
setAnimationsPlaybackRate,
|
||||||
|
setAnimationsPlayState,
|
||||||
setDetailVisibility,
|
setDetailVisibility,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
toggleElementPicker,
|
toggleElementPicker,
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
const target = this.inspector.target;
|
const target = this.inspector.target;
|
||||||
this.animationsFront = new AnimationsFront(target.client, target.form);
|
this.animationsFront = new AnimationsFront(target.client, target.form);
|
||||||
|
|
||||||
|
this.animationsCurrentTimeListeners = [];
|
||||||
|
this.isCurrentTimeSet = false;
|
||||||
|
|
||||||
const provider = createElement(Provider,
|
const provider = createElement(Provider,
|
||||||
{
|
{
|
||||||
id: "newanimationinspector",
|
id: "newanimationinspector",
|
||||||
|
@ -77,16 +107,25 @@ class AnimationInspector {
|
||||||
},
|
},
|
||||||
App(
|
App(
|
||||||
{
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
getNodeFromActor,
|
getNodeFromActor,
|
||||||
|
isAnimationsRunning,
|
||||||
onHideBoxModelHighlighter,
|
onHideBoxModelHighlighter,
|
||||||
onShowBoxModelHighlighterForNode,
|
onShowBoxModelHighlighterForNode,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
rewindAnimationsCurrentTime,
|
||||||
selectAnimation,
|
selectAnimation,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
setAnimationsPlaybackRate,
|
||||||
|
setAnimationsPlayState,
|
||||||
setDetailVisibility,
|
setDetailVisibility,
|
||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
toggleElementPicker,
|
toggleElementPicker,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -117,10 +156,25 @@ class AnimationInspector {
|
||||||
this.simulatedElement = null;
|
this.simulatedElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.simulatedAnimationForKeyframesProgressBar) {
|
||||||
|
this.simulatedAnimationForKeyframesProgressBar.cancel();
|
||||||
|
this.simulatedAnimationForKeyframesProgressBar = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopAnimationsCurrentTimeTimer();
|
||||||
|
|
||||||
this.inspector = null;
|
this.inspector = null;
|
||||||
this.win = null;
|
this.win = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get state() {
|
||||||
|
return this.inspector.store.getState().animations;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAnimationsCurrentTimeListener(listener) {
|
||||||
|
this.animationsCurrentTimeListeners.push(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a map of animated property from given animation actor.
|
* Return a map of animated property from given animation actor.
|
||||||
*
|
*
|
||||||
|
@ -160,6 +214,10 @@ class AnimationInspector {
|
||||||
return animatedPropertyMap;
|
return animatedPropertyMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAnimationsCurrentTime() {
|
||||||
|
return this.currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the computed style of the specified property after setting the given styles
|
* Return the computed style of the specified property after setting the given styles
|
||||||
* to the simulated element.
|
* to the simulated element.
|
||||||
|
@ -191,6 +249,110 @@ class AnimationInspector {
|
||||||
this.inspector.sidebar.getCurrentTabID() === "newanimationinspector";
|
this.inspector.sidebar.getCurrentTabID() === "newanimationinspector";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should call when the current time is changed.
|
||||||
|
* Then, dispatches the current time to listeners that are registered
|
||||||
|
* by addAnimationsCurrentTimeListener.
|
||||||
|
*
|
||||||
|
* @param {Number} currentTime
|
||||||
|
*/
|
||||||
|
onAnimationsCurrentTimeUpdated(currentTime) {
|
||||||
|
this.currentTime = currentTime;
|
||||||
|
|
||||||
|
for (const listener of this.animationsCurrentTimeListeners) {
|
||||||
|
listener(currentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the current time proceed by CurrentTimeTimer.
|
||||||
|
*
|
||||||
|
* @param {Number} currentTime
|
||||||
|
* @param {Bool} shouldStop
|
||||||
|
*/
|
||||||
|
onCurrentTimeTimerUpdated(currentTime, shouldStop) {
|
||||||
|
if (shouldStop) {
|
||||||
|
this.setAnimationsCurrentTime(currentTime, true);
|
||||||
|
} else {
|
||||||
|
this.onAnimationsCurrentTimeUpdated(currentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onElementPickerStarted() {
|
||||||
|
this.inspector.store.dispatch(updateElementPickerEnabled(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
onElementPickerStopped() {
|
||||||
|
this.inspector.store.dispatch(updateElementPickerEnabled(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
onSidebarSelect() {
|
||||||
|
this.update();
|
||||||
|
this.onSidebarResized(null, this.inspector.getSidebarSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
onSidebarResized(type, size) {
|
||||||
|
if (!this.isPanelVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inspector.store.dispatch(updateSidebarSize(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAnimationsCurrentTimeListener(listener) {
|
||||||
|
this.animationsCurrentTimeListeners =
|
||||||
|
this.animationsCurrentTimeListeners.filter(l => l !== listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
async rewindAnimationsCurrentTime() {
|
||||||
|
await this.setAnimationsCurrentTime(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectAnimation(animation) {
|
||||||
|
this.inspector.store.dispatch(updateSelectedAnimation(animation));
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAnimationsCurrentTime(currentTime, shouldRefresh) {
|
||||||
|
this.stopAnimationsCurrentTimeTimer();
|
||||||
|
this.onAnimationsCurrentTimeUpdated(currentTime);
|
||||||
|
|
||||||
|
if (!shouldRefresh && this.isCurrentTimeSet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const animations = this.state.animations;
|
||||||
|
this.isCurrentTimeSet = true;
|
||||||
|
await this.animationsFront.setCurrentTimes(animations, currentTime, true);
|
||||||
|
await this.updateAnimations(animations);
|
||||||
|
this.isCurrentTimeSet = false;
|
||||||
|
|
||||||
|
if (shouldRefresh) {
|
||||||
|
this.updateState([...animations]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAnimationsPlaybackRate(playbackRate) {
|
||||||
|
const animations = this.state.animations;
|
||||||
|
await this.animationsFront.setPlaybackRates(animations, playbackRate);
|
||||||
|
await this.updateAnimations(animations);
|
||||||
|
await this.updateState([...animations]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAnimationsPlayState(doPlay) {
|
||||||
|
if (doPlay) {
|
||||||
|
await this.animationsFront.playAll();
|
||||||
|
} else {
|
||||||
|
await this.animationsFront.pauseAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.updateAnimations(this.state.animations);
|
||||||
|
await this.updateState([...this.state.animations]);
|
||||||
|
}
|
||||||
|
|
||||||
|
setDetailVisibility(isVisible) {
|
||||||
|
this.inspector.store.dispatch(updateDetailVisibility(isVisible));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns simulatable animation by given parameters.
|
* Returns simulatable animation by given parameters.
|
||||||
* The returned animation is implementing Animation interface of Web Animation API.
|
* The returned animation is implementing Animation interface of Web Animation API.
|
||||||
|
@ -233,6 +395,46 @@ class AnimationInspector {
|
||||||
return this.simulatedAnimation;
|
return this.simulatedAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a simulatable efect timing animation for the keyframes progress bar.
|
||||||
|
* The returned animation is implementing Animation interface of Web Animation API.
|
||||||
|
* https://drafts.csswg.org/web-animations/#the-animation-interface
|
||||||
|
*
|
||||||
|
* @param {Object} effectTiming
|
||||||
|
* e.g. { duration: 1000, fill: "both" }
|
||||||
|
* @return {Animation}
|
||||||
|
* https://drafts.csswg.org/web-animations/#the-animation-interface
|
||||||
|
*/
|
||||||
|
simulateAnimationForKeyframesProgressBar(effectTiming) {
|
||||||
|
if (!this.simulatedAnimationForKeyframesProgressBar) {
|
||||||
|
this.simulatedAnimationForKeyframesProgressBar = new this.win.Animation();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.simulatedAnimationForKeyframesProgressBar.effect =
|
||||||
|
new this.win.KeyframeEffect(null, null, effectTiming);
|
||||||
|
|
||||||
|
return this.simulatedAnimationForKeyframesProgressBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAnimationsCurrentTimeTimer() {
|
||||||
|
if (this.currentTimeTimer) {
|
||||||
|
this.currentTimeTimer.destroy();
|
||||||
|
this.currentTimeTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startAnimationsCurrentTimeTimer() {
|
||||||
|
const timeScale = this.state.timeScale;
|
||||||
|
const shouldStopAfterEndTime =
|
||||||
|
!hasAnimationIterationCountInfinite(this.state.animations);
|
||||||
|
|
||||||
|
const currentTimeTimer =
|
||||||
|
new CurrentTimeTimer(timeScale, shouldStopAfterEndTime,
|
||||||
|
this.win, this.onCurrentTimeTimerUpdated);
|
||||||
|
currentTimeTimer.start();
|
||||||
|
this.currentTimeTimer = currentTimeTimer;
|
||||||
|
}
|
||||||
|
|
||||||
toggleElementPicker() {
|
toggleElementPicker() {
|
||||||
this.inspector.toolbox.highlighterUtils.togglePicker();
|
this.inspector.toolbox.highlighterUtils.togglePicker();
|
||||||
}
|
}
|
||||||
|
@ -246,48 +448,35 @@ class AnimationInspector {
|
||||||
const done = this.inspector.updating("newanimationinspector");
|
const done = this.inspector.updating("newanimationinspector");
|
||||||
|
|
||||||
const selection = this.inspector.selection;
|
const selection = this.inspector.selection;
|
||||||
const animations =
|
const nextAnimations =
|
||||||
selection.isConnected() && selection.isElementNode()
|
selection.isConnected() && selection.isElementNode()
|
||||||
? await this.animationsFront.getAnimationPlayersForNode(selection.nodeFront)
|
? await this.animationsFront.getAnimationPlayersForNode(selection.nodeFront)
|
||||||
: [];
|
: [];
|
||||||
|
const currentAnimations = this.state.animations;
|
||||||
|
|
||||||
if (!this.animations || !isAllAnimationEqual(animations, this.animations)) {
|
if (!currentAnimations || !isAllAnimationEqual(currentAnimations, nextAnimations)) {
|
||||||
this.inspector.store.dispatch(updateAnimations(animations));
|
this.updateState(nextAnimations);
|
||||||
this.animations = animations;
|
|
||||||
// If number of displayed animations is one, we select the animation automatically.
|
|
||||||
this.selectAnimation(animations.length === 1 ? animations[0] : null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAnimation(animation) {
|
async updateAnimations(animations) {
|
||||||
this.inspector.store.dispatch(updateSelectedAnimation(animation));
|
const promises = animations.map(animation => {
|
||||||
|
return animation.refreshState();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
setDetailVisibility(isVisible) {
|
updateState(animations) {
|
||||||
this.inspector.store.dispatch(updateDetailVisibility(isVisible));
|
this.stopAnimationsCurrentTimeTimer();
|
||||||
}
|
|
||||||
|
|
||||||
onElementPickerStarted() {
|
this.inspector.store.dispatch(updateAnimations(animations));
|
||||||
this.inspector.store.dispatch(updateElementPickerEnabled(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
onElementPickerStopped() {
|
if (hasRunningAnimation(animations)) {
|
||||||
this.inspector.store.dispatch(updateElementPickerEnabled(false));
|
this.startAnimationsCurrentTimeTimer();
|
||||||
}
|
|
||||||
|
|
||||||
onSidebarSelect() {
|
|
||||||
this.update();
|
|
||||||
this.onSidebarResized(null, this.inspector.getSidebarSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
onSidebarResized(type, size) {
|
|
||||||
if (!this.isPanelVisible()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inspector.store.dispatch(updateSidebarSize(size));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,28 +14,47 @@ const AnimatedPropertyListHeader = createFactory(require("./AnimatedPropertyList
|
||||||
class AnimatedPropertyListContainer extends PureComponent {
|
class AnimatedPropertyListContainer extends PureComponent {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
animation: PropTypes.object.isRequired,
|
animation: PropTypes.object.isRequired,
|
||||||
emitEventForTest: PropTypes.func.isRequired,
|
emitEventForTest: PropTypes.func.isRequired,
|
||||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||||
|
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
getComputedStyle: PropTypes.func.isRequired,
|
getComputedStyle: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
|
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animation,
|
animation,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
className: `animated-property-list-container ${ animation.state.type }`
|
className: `animated-property-list-container ${ animation.state.type }`
|
||||||
},
|
},
|
||||||
AnimatedPropertyListHeader(),
|
AnimatedPropertyListHeader(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
animation,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
|
}
|
||||||
|
),
|
||||||
AnimatedPropertyList(
|
AnimatedPropertyList(
|
||||||
{
|
{
|
||||||
animation,
|
animation,
|
||||||
|
|
|
@ -6,16 +6,48 @@
|
||||||
|
|
||||||
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
const KeyframesProgressBar = createFactory(require("./KeyframesProgressBar"));
|
||||||
const KeyframesProgressTickList = createFactory(require("./KeyframesProgressTickList"));
|
const KeyframesProgressTickList = createFactory(require("./KeyframesProgressTickList"));
|
||||||
|
|
||||||
class AnimatedPropertyListHeader extends PureComponent {
|
class AnimatedPropertyListHeader extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
animation: PropTypes.object.isRequired,
|
||||||
|
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
animation,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
className: "animated-property-list-header devtools-toolbar"
|
className: "animated-property-list-header devtools-toolbar"
|
||||||
},
|
},
|
||||||
KeyframesProgressTickList()
|
KeyframesProgressTickList(),
|
||||||
|
KeyframesProgressBar(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
animation,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,23 +16,33 @@ const AnimatedPropertyListContainer =
|
||||||
class AnimationDetailContainer extends PureComponent {
|
class AnimationDetailContainer extends PureComponent {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
animation: PropTypes.object.isRequired,
|
animation: PropTypes.object.isRequired,
|
||||||
emitEventForTest: PropTypes.func.isRequired,
|
emitEventForTest: PropTypes.func.isRequired,
|
||||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||||
|
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
getComputedStyle: PropTypes.func.isRequired,
|
getComputedStyle: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
setDetailVisibility: PropTypes.func.isRequired,
|
setDetailVisibility: PropTypes.func.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
|
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animation,
|
animation,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
setDetailVisibility,
|
setDetailVisibility,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
|
@ -51,11 +61,16 @@ class AnimationDetailContainer extends PureComponent {
|
||||||
animation ?
|
animation ?
|
||||||
AnimatedPropertyListContainer(
|
AnimatedPropertyListContainer(
|
||||||
{
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animation,
|
animation,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
:
|
:
|
||||||
|
|
|
@ -18,7 +18,8 @@ class AnimationDetailHeader extends PureComponent {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick(event) {
|
||||||
|
event.stopPropagation();
|
||||||
const { setDetailVisibility } = this.props;
|
const { setDetailVisibility } = this.props;
|
||||||
setDetailVisibility(false);
|
setDetailVisibility(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,36 +12,41 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
const AnimationList = createFactory(require("./AnimationList"));
|
const AnimationList = createFactory(require("./AnimationList"));
|
||||||
const AnimationListHeader = createFactory(require("./AnimationListHeader"));
|
const AnimationListHeader = createFactory(require("./AnimationListHeader"));
|
||||||
|
|
||||||
const TimeScale = require("../utils/timescale");
|
|
||||||
|
|
||||||
class AnimationListContainer extends PureComponent {
|
class AnimationListContainer extends PureComponent {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
emitEventForTest: PropTypes.func.isRequired,
|
emitEventForTest: PropTypes.func.isRequired,
|
||||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||||
getNodeFromActor: PropTypes.func.isRequired,
|
getNodeFromActor: PropTypes.func.isRequired,
|
||||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
selectAnimation: PropTypes.func.isRequired,
|
selectAnimation: PropTypes.func.isRequired,
|
||||||
|
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
setSelectedNode: PropTypes.func.isRequired,
|
setSelectedNode: PropTypes.func.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animations,
|
animations,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
getNodeFromActor,
|
getNodeFromActor,
|
||||||
onHideBoxModelHighlighter,
|
onHideBoxModelHighlighter,
|
||||||
onShowBoxModelHighlighterForNode,
|
onShowBoxModelHighlighterForNode,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
selectAnimation,
|
selectAnimation,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
timeScale,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const timeScale = new TimeScale(animations);
|
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
|
@ -49,6 +54,9 @@ class AnimationListContainer extends PureComponent {
|
||||||
},
|
},
|
||||||
AnimationListHeader(
|
AnimationListHeader(
|
||||||
{
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
timeScale,
|
timeScale,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
@ -10,16 +10,26 @@ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
|
||||||
const AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));
|
const AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));
|
||||||
|
const CurrentTimeScrubberController =
|
||||||
|
createFactory(require("./CurrentTimeScrubberController"));
|
||||||
|
|
||||||
class AnimationListHeader extends PureComponent {
|
class AnimationListHeader extends PureComponent {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
timeScale: PropTypes.object.isRequired,
|
timeScale: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { timeScale } = this.props;
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
timeScale,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
|
@ -29,6 +39,14 @@ class AnimationListHeader extends PureComponent {
|
||||||
{
|
{
|
||||||
timeScale
|
timeScale
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
CurrentTimeScrubberController(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
timeScale,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,11 +35,11 @@ class AnimationTimelineTickList extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateTickList();
|
this.updateTickList(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.updateTickList();
|
this.updateTickList(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
@ -51,7 +51,7 @@ class AnimationTimelineTickList extends PureComponent {
|
||||||
const currentTickItem = this.state.tickList[i];
|
const currentTickItem = this.state.tickList[i];
|
||||||
const nextTickItem = nextState.tickList[i];
|
const nextTickItem = nextState.tickList[i];
|
||||||
|
|
||||||
if (currentTickItem.text !== nextTickItem.text) {
|
if (currentTickItem.timeTickLabel !== nextTickItem.timeTickLabel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,8 @@ class AnimationTimelineTickList extends PureComponent {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTickList() {
|
updateTickList(props) {
|
||||||
const { timeScale } = this.props;
|
const { timeScale } = props;
|
||||||
const tickListEl = ReactDOM.findDOMNode(this);
|
const tickListEl = ReactDOM.findDOMNode(this);
|
||||||
const width = tickListEl.offsetWidth;
|
const width = tickListEl.offsetWidth;
|
||||||
const animationDuration = timeScale.getDuration();
|
const animationDuration = timeScale.getDuration();
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
const CurrentTimeLabel = createFactory(require("./CurrentTimeLabel"));
|
||||||
|
const PauseResumeButton = createFactory(require("./PauseResumeButton"));
|
||||||
|
const PlaybackRateSelector = createFactory(require("./PlaybackRateSelector"));
|
||||||
|
const RewindButton = createFactory(require("./RewindButton"));
|
||||||
|
|
||||||
|
class AnimationToolbar extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
rewindAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
setAnimationsPlaybackRate: PropTypes.func.isRequired,
|
||||||
|
setAnimationsPlayState: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
animations,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
rewindAnimationsCurrentTime,
|
||||||
|
setAnimationsPlaybackRate,
|
||||||
|
setAnimationsPlayState,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return dom.div(
|
||||||
|
{
|
||||||
|
className: "animation-toolbar devtools-toolbar",
|
||||||
|
},
|
||||||
|
RewindButton(
|
||||||
|
{
|
||||||
|
rewindAnimationsCurrentTime,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
PauseResumeButton(
|
||||||
|
{
|
||||||
|
animations,
|
||||||
|
setAnimationsPlayState,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
PlaybackRateSelector(
|
||||||
|
{
|
||||||
|
animations,
|
||||||
|
setAnimationsPlaybackRate,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
CurrentTimeLabel(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AnimationToolbar;
|
|
@ -11,24 +11,34 @@ const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
|
||||||
const AnimationDetailContainer = createFactory(require("./AnimationDetailContainer"));
|
const AnimationDetailContainer = createFactory(require("./AnimationDetailContainer"));
|
||||||
const AnimationListContainer = createFactory(require("./AnimationListContainer"));
|
const AnimationListContainer = createFactory(require("./AnimationListContainer"));
|
||||||
|
const AnimationToolbar = createFactory(require("./AnimationToolbar"));
|
||||||
const NoAnimationPanel = createFactory(require("./NoAnimationPanel"));
|
const NoAnimationPanel = createFactory(require("./NoAnimationPanel"));
|
||||||
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
|
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
|
||||||
|
|
||||||
class App extends PureComponent {
|
class App extends PureComponent {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
return {
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
detailVisibility: PropTypes.bool.isRequired,
|
detailVisibility: PropTypes.bool.isRequired,
|
||||||
emitEventForTest: PropTypes.func.isRequired,
|
emitEventForTest: PropTypes.func.isRequired,
|
||||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||||
|
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
getComputedStyle: PropTypes.func.isRequired,
|
getComputedStyle: PropTypes.func.isRequired,
|
||||||
getNodeFromActor: PropTypes.func.isRequired,
|
getNodeFromActor: PropTypes.func.isRequired,
|
||||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
rewindAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
selectAnimation: PropTypes.func.isRequired,
|
selectAnimation: PropTypes.func.isRequired,
|
||||||
|
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
setAnimationsPlaybackRate: PropTypes.func.isRequired,
|
||||||
|
setAnimationsPlayState: PropTypes.func.isRequired,
|
||||||
setDetailVisibility: PropTypes.func.isRequired,
|
setDetailVisibility: PropTypes.func.isRequired,
|
||||||
setSelectedNode: PropTypes.func.isRequired,
|
setSelectedNode: PropTypes.func.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
|
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
toggleElementPicker: PropTypes.func.isRequired,
|
toggleElementPicker: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -39,18 +49,27 @@ class App extends PureComponent {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animations,
|
animations,
|
||||||
detailVisibility,
|
detailVisibility,
|
||||||
emitEventForTest,
|
emitEventForTest,
|
||||||
getAnimatedPropertyMap,
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
getComputedStyle,
|
getComputedStyle,
|
||||||
getNodeFromActor,
|
getNodeFromActor,
|
||||||
onHideBoxModelHighlighter,
|
onHideBoxModelHighlighter,
|
||||||
onShowBoxModelHighlighterForNode,
|
onShowBoxModelHighlighterForNode,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
rewindAnimationsCurrentTime,
|
||||||
selectAnimation,
|
selectAnimation,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
setAnimationsPlaybackRate,
|
||||||
|
setAnimationsPlayState,
|
||||||
setDetailVisibility,
|
setDetailVisibility,
|
||||||
setSelectedNode,
|
setSelectedNode,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
toggleElementPicker,
|
toggleElementPicker,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -58,37 +77,59 @@ class App extends PureComponent {
|
||||||
{
|
{
|
||||||
id: "animation-container",
|
id: "animation-container",
|
||||||
className: detailVisibility ? "animation-detail-visible" : "",
|
className: detailVisibility ? "animation-detail-visible" : "",
|
||||||
|
tabIndex: -1,
|
||||||
},
|
},
|
||||||
animations.length ?
|
animations.length ?
|
||||||
SplitBox({
|
[
|
||||||
className: "animation-container-splitter",
|
AnimationToolbar(
|
||||||
endPanel: AnimationDetailContainer(
|
|
||||||
{
|
|
||||||
emitEventForTest,
|
|
||||||
getAnimatedPropertyMap,
|
|
||||||
getComputedStyle,
|
|
||||||
setDetailVisibility,
|
|
||||||
simulateAnimation,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
endPanelControl: true,
|
|
||||||
initialHeight: "50%",
|
|
||||||
splitterSize: 1,
|
|
||||||
startPanel: AnimationListContainer(
|
|
||||||
{
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
animations,
|
animations,
|
||||||
emitEventForTest,
|
removeAnimationsCurrentTimeListener,
|
||||||
getAnimatedPropertyMap,
|
rewindAnimationsCurrentTime,
|
||||||
getNodeFromActor,
|
setAnimationsPlaybackRate,
|
||||||
onHideBoxModelHighlighter,
|
setAnimationsPlayState,
|
||||||
onShowBoxModelHighlighterForNode,
|
|
||||||
selectAnimation,
|
|
||||||
setSelectedNode,
|
|
||||||
simulateAnimation,
|
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
vert: false,
|
SplitBox({
|
||||||
})
|
className: "animation-container-splitter",
|
||||||
|
endPanel: AnimationDetailContainer(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
emitEventForTest,
|
||||||
|
getAnimatedPropertyMap,
|
||||||
|
getAnimationsCurrentTime,
|
||||||
|
getComputedStyle,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
setDetailVisibility,
|
||||||
|
simulateAnimation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
timeScale,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
endPanelControl: true,
|
||||||
|
initialHeight: "50%",
|
||||||
|
splitterSize: 1,
|
||||||
|
startPanel: AnimationListContainer(
|
||||||
|
{
|
||||||
|
addAnimationsCurrentTimeListener,
|
||||||
|
animations,
|
||||||
|
emitEventForTest,
|
||||||
|
getAnimatedPropertyMap,
|
||||||
|
getNodeFromActor,
|
||||||
|
onHideBoxModelHighlighter,
|
||||||
|
onShowBoxModelHighlighterForNode,
|
||||||
|
removeAnimationsCurrentTimeListener,
|
||||||
|
selectAnimation,
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
setSelectedNode,
|
||||||
|
simulateAnimation,
|
||||||
|
timeScale,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vert: false,
|
||||||
|
})
|
||||||
|
]
|
||||||
:
|
:
|
||||||
NoAnimationPanel(
|
NoAnimationPanel(
|
||||||
{
|
{
|
||||||
|
@ -103,6 +144,7 @@ const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
animations: state.animations.animations,
|
animations: state.animations.animations,
|
||||||
detailVisibility: state.animations.detailVisibility,
|
detailVisibility: state.animations.detailVisibility,
|
||||||
|
timeScale: state.animations.timeScale,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
class CurrentTimeLabel extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { addAnimationsCurrentTimeListener } = props;
|
||||||
|
this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
currentTime: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { removeAnimationsCurrentTimeListener } = this.props;
|
||||||
|
removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentTimeUpdated(currentTime) {
|
||||||
|
this.setState({ currentTime });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { currentTime } = this.state;
|
||||||
|
|
||||||
|
return dom.label(
|
||||||
|
{
|
||||||
|
className: "current-time-label",
|
||||||
|
},
|
||||||
|
formatStopwatchTime(currentTime)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a timestamp (in ms) as a mm:ss.mmm string.
|
||||||
|
*
|
||||||
|
* @param {Number} time
|
||||||
|
* @return {String}
|
||||||
|
*/
|
||||||
|
function formatStopwatchTime(time) {
|
||||||
|
// Format falsy values as 0
|
||||||
|
if (!time) {
|
||||||
|
return "00:00.000";
|
||||||
|
}
|
||||||
|
|
||||||
|
let milliseconds = parseInt(time % 1000, 10);
|
||||||
|
let seconds = parseInt((time / 1000) % 60, 10);
|
||||||
|
let minutes = parseInt((time / (1000 * 60)), 10);
|
||||||
|
|
||||||
|
let pad = (nb, max) => {
|
||||||
|
if (nb < max) {
|
||||||
|
return new Array((max + "").length - (nb + "").length + 1).join("0") + nb;
|
||||||
|
}
|
||||||
|
return nb;
|
||||||
|
};
|
||||||
|
|
||||||
|
minutes = pad(minutes, 10);
|
||||||
|
seconds = pad(seconds, 10);
|
||||||
|
milliseconds = pad(milliseconds, 100);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds}.${milliseconds}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CurrentTimeLabel;
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
class CurrentTimeScrubber extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
offset: PropTypes.number.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { offset } = this.props;
|
||||||
|
|
||||||
|
return dom.div(
|
||||||
|
{
|
||||||
|
className: "current-time-scrubber",
|
||||||
|
style: {
|
||||||
|
transform: `translateX(${ offset }px)`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CurrentTimeScrubber;
|
|
@ -0,0 +1,141 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||||
|
|
||||||
|
const CurrentTimeScrubber = createFactory(require("./CurrentTimeScrubber"));
|
||||||
|
|
||||||
|
class CurrentTimeScrubberController extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
const { addAnimationsCurrentTimeListener } = props;
|
||||||
|
this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
|
||||||
|
this.onMouseDown = this.onMouseDown.bind(this);
|
||||||
|
this.onMouseMove = this.onMouseMove.bind(this);
|
||||||
|
this.onMouseOut = this.onMouseOut.bind(this);
|
||||||
|
this.onMouseUp = this.onMouseUp.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
// offset of the position for the scrubber
|
||||||
|
offset: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const parentEl = ReactDOM.findDOMNode(this).parentElement;
|
||||||
|
parentEl.addEventListener("mousedown", this.onMouseDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { removeAnimationsCurrentTimeListener } = this.props;
|
||||||
|
removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentTimeUpdated(currentTime) {
|
||||||
|
const { timeScale } = this.props;
|
||||||
|
|
||||||
|
const thisEl = ReactDOM.findDOMNode(this);
|
||||||
|
const offset =
|
||||||
|
thisEl ? currentTime / timeScale.getDuration() * thisEl.clientWidth : 0;
|
||||||
|
this.setState({ offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseDown(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const thisEl = ReactDOM.findDOMNode(this);
|
||||||
|
this.controllerArea = thisEl.getBoundingClientRect();
|
||||||
|
this.listenerTarget = thisEl.closest(".animation-list-container");
|
||||||
|
this.listenerTarget.addEventListener("mousemove", this.onMouseMove);
|
||||||
|
this.listenerTarget.addEventListener("mouseout", this.onMouseOut);
|
||||||
|
this.listenerTarget.addEventListener("mouseup", this.onMouseUp);
|
||||||
|
this.listenerTarget.classList.add("active-scrubber");
|
||||||
|
|
||||||
|
this.updateAnimationsCurrentTime(event.pageX, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseMove(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.isMouseMoved = true;
|
||||||
|
this.updateAnimationsCurrentTime(event.pageX);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOut(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (!this.listenerTarget.contains(event.relatedTarget)) {
|
||||||
|
const endX = this.controllerArea.x + this.controllerArea.width;
|
||||||
|
const pageX = endX < event.pageX ? endX : event.pageX;
|
||||||
|
this.updateAnimationsCurrentTime(pageX, true);
|
||||||
|
this.uninstallListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseUp(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (this.isMouseMoved) {
|
||||||
|
this.updateAnimationsCurrentTime(event.pageX, true);
|
||||||
|
this.isMouseMoved = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uninstallListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstallListeners() {
|
||||||
|
this.listenerTarget.removeEventListener("mousemove", this.onMouseMove);
|
||||||
|
this.listenerTarget.removeEventListener("mouseout", this.onMouseOut);
|
||||||
|
this.listenerTarget.removeEventListener("mouseup", this.onMouseUp);
|
||||||
|
this.listenerTarget.classList.remove("active-scrubber");
|
||||||
|
this.listenerTarget = null;
|
||||||
|
this.controllerArea = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnimationsCurrentTime(pageX, needRefresh) {
|
||||||
|
const {
|
||||||
|
setAnimationsCurrentTime,
|
||||||
|
timeScale,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const time = pageX - this.controllerArea.x < 0 ?
|
||||||
|
0 :
|
||||||
|
(pageX - this.controllerArea.x) /
|
||||||
|
this.controllerArea.width * timeScale.getDuration();
|
||||||
|
|
||||||
|
setAnimationsCurrentTime(time, needRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { offset } = this.state;
|
||||||
|
|
||||||
|
return dom.div(
|
||||||
|
{
|
||||||
|
className: "current-time-scrubber-controller devtools-toolbar",
|
||||||
|
},
|
||||||
|
CurrentTimeScrubber(
|
||||||
|
{
|
||||||
|
offset,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CurrentTimeScrubberController;
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||||
|
|
||||||
|
class KeyframesProgressBar extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
animation: PropTypes.object.isRequired,
|
||||||
|
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||||
|
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||||
|
timeScale: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onCurrentTimeUpdated = this.onCurrentTimeUpdated.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
// offset of the position for the progress bar
|
||||||
|
offset: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { addAnimationsCurrentTimeListener } = this.props;
|
||||||
|
|
||||||
|
this.element = ReactDOM.findDOMNode(this);
|
||||||
|
this.setupAnimation(this.props);
|
||||||
|
addAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { getAnimationsCurrentTime } = nextProps;
|
||||||
|
|
||||||
|
this.setupAnimation(nextProps);
|
||||||
|
this.onCurrentTimeUpdated(getAnimationsCurrentTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { removeAnimationsCurrentTimeListener } = this.props;
|
||||||
|
|
||||||
|
removeAnimationsCurrentTimeListener(this.onCurrentTimeUpdated);
|
||||||
|
this.element = null;
|
||||||
|
this.simulatedAnimation = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCurrentTimeUpdated(currentTime) {
|
||||||
|
const {
|
||||||
|
animation,
|
||||||
|
timeScale,
|
||||||
|
} = this.props;
|
||||||
|
const {
|
||||||
|
playbackRate,
|
||||||
|
previousStartTime = 0,
|
||||||
|
} = animation.state;
|
||||||
|
|
||||||
|
this.simulatedAnimation.currentTime =
|
||||||
|
(timeScale.minStartTime + currentTime - previousStartTime) * playbackRate;
|
||||||
|
const offset = this.element.offsetWidth *
|
||||||
|
this.simulatedAnimation.effect.getComputedTiming().progress;
|
||||||
|
|
||||||
|
this.setState({ offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
setupAnimation(props) {
|
||||||
|
const {
|
||||||
|
animation,
|
||||||
|
simulateAnimationForKeyframesProgressBar,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (this.simulatedAnimation) {
|
||||||
|
this.simulatedAnimation.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
const timing = Object.assign({}, animation.state, {
|
||||||
|
iterations: animation.state.iterationCount || Infinity
|
||||||
|
});
|
||||||
|
|
||||||
|
this.simulatedAnimation = simulateAnimationForKeyframesProgressBar(timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { offset } = this.state;
|
||||||
|
|
||||||
|
return dom.div(
|
||||||
|
{
|
||||||
|
className: "keyframes-progress-bar-area devtools-toolbar",
|
||||||
|
},
|
||||||
|
dom.div(
|
||||||
|
{
|
||||||
|
className: "keyframes-progress-bar",
|
||||||
|
style: {
|
||||||
|
transform: `translateX(${ offset }px)`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = KeyframesProgressBar;
|
|
@ -41,7 +41,10 @@ class NoAnimationPanel extends PureComponent {
|
||||||
className: "animation-element-picker devtools-button" +
|
className: "animation-element-picker devtools-button" +
|
||||||
(elementPickerEnabled ? " checked" : ""),
|
(elementPickerEnabled ? " checked" : ""),
|
||||||
"data-standalone": true,
|
"data-standalone": true,
|
||||||
onClick: toggleElementPicker
|
onClick: event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
toggleElementPicker();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||||
|
|
||||||
|
const { KeyCodes } = require("devtools/client/shared/keycodes");
|
||||||
|
|
||||||
|
const { getStr } = require("../utils/l10n");
|
||||||
|
const { hasRunningAnimation } = require("../utils/utils");
|
||||||
|
|
||||||
|
class PauseResumeButton extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
setAnimationsPlayState: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
isRunning: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.updateState(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const targetEl = this.getKeyEventTarget();
|
||||||
|
targetEl.addEventListener("keydown", this.onKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.updateState(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnount() {
|
||||||
|
const targetEl = this.getKeyEventTarget();
|
||||||
|
targetEl.removeEventListener("keydown", this.onKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
getKeyEventTarget() {
|
||||||
|
return ReactDOM.findDOMNode(this).closest("#animation-container");
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggleAnimationsPlayState(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const { setAnimationsPlayState } = this.props;
|
||||||
|
const { isRunning } = this.state;
|
||||||
|
|
||||||
|
setAnimationsPlayState(!isRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(event) {
|
||||||
|
if (event.keyCode === KeyCodes.DOM_VK_SPACE) {
|
||||||
|
this.onToggleAnimationsPlayState(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState() {
|
||||||
|
const { animations } = this.props;
|
||||||
|
const isRunning = hasRunningAnimation(animations);
|
||||||
|
this.setState({ isRunning });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { isRunning } = this.state;
|
||||||
|
|
||||||
|
return dom.button(
|
||||||
|
{
|
||||||
|
className: "pause-resume-button devtools-button" +
|
||||||
|
(isRunning ? "" : " paused"),
|
||||||
|
onClick: this.onToggleAnimationsPlayState.bind(this),
|
||||||
|
title: isRunning ?
|
||||||
|
getStr("timeline.resumedButtonTooltip") :
|
||||||
|
getStr("timeline.pausedButtonTooltip"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PauseResumeButton;
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
const { getFormatStr } = require("../utils/l10n");
|
||||||
|
|
||||||
|
const PLAYBACK_RATES = [.1, .25, .5, 1, 2, 5, 10];
|
||||||
|
|
||||||
|
class PlaybackRateSelector extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
setAnimationsPlaybackRate: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
options: [],
|
||||||
|
selected: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.updateState(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.updateState(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaybackRates(animations) {
|
||||||
|
return sortAndUnique(animations.map(a => a.state.playbackRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectablePlaybackRates(animationsRates) {
|
||||||
|
return sortAndUnique(PLAYBACK_RATES.concat(animationsRates));
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(e) {
|
||||||
|
const { setAnimationsPlaybackRate } = this.props;
|
||||||
|
|
||||||
|
if (!e.target.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAnimationsPlaybackRate(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateState(props) {
|
||||||
|
const { animations } = props;
|
||||||
|
|
||||||
|
let options;
|
||||||
|
let selected;
|
||||||
|
const rates = this.getPlaybackRates(animations);
|
||||||
|
|
||||||
|
if (rates.length === 1) {
|
||||||
|
options = this.getSelectablePlaybackRates(rates);
|
||||||
|
selected = rates[0];
|
||||||
|
} else {
|
||||||
|
// When the animations displayed have mixed playback rates, we can't
|
||||||
|
// select any of the predefined ones.
|
||||||
|
options = ["", ...PLAYBACK_RATES];
|
||||||
|
selected = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ options, selected });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { options, selected } = this.state;
|
||||||
|
|
||||||
|
return dom.select(
|
||||||
|
{
|
||||||
|
className: "playback-rate-selector devtools-button",
|
||||||
|
onChange: this.onChange.bind(this),
|
||||||
|
},
|
||||||
|
options.map(rate => {
|
||||||
|
return dom.option(
|
||||||
|
{
|
||||||
|
selected: rate === selected ? "true" : null,
|
||||||
|
value: rate,
|
||||||
|
},
|
||||||
|
rate ? getFormatStr("player.playbackRateLabel", rate) : "-"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortAndUnique(array) {
|
||||||
|
return [...new Set(array)].sort((a, b) => a > b);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PlaybackRateSelector;
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { PureComponent } = require("devtools/client/shared/vendor/react");
|
||||||
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||||
|
|
||||||
|
const { getStr } = require("../utils/l10n");
|
||||||
|
|
||||||
|
class RewindButton extends PureComponent {
|
||||||
|
static get propTypes() {
|
||||||
|
return {
|
||||||
|
rewindAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { rewindAnimationsCurrentTime } = this.props;
|
||||||
|
|
||||||
|
return dom.button(
|
||||||
|
{
|
||||||
|
className: "rewind-button devtools-button",
|
||||||
|
onClick: event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
rewindAnimationsCurrentTime();
|
||||||
|
},
|
||||||
|
title: getStr("timeline.rewindButtonTooltip"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = RewindButton;
|
|
@ -16,6 +16,7 @@ class ComputedTimingPath extends TimingPath {
|
||||||
animation: PropTypes.object.isRequired,
|
animation: PropTypes.object.isRequired,
|
||||||
durationPerPixel: PropTypes.number.isRequired,
|
durationPerPixel: PropTypes.number.isRequired,
|
||||||
keyframes: PropTypes.object.isRequired,
|
keyframes: PropTypes.object.isRequired,
|
||||||
|
offset: PropTypes.number.isRequired,
|
||||||
opacity: PropTypes.number.isRequired,
|
opacity: PropTypes.number.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
totalDuration: PropTypes.number.isRequired,
|
totalDuration: PropTypes.number.isRequired,
|
||||||
|
@ -27,6 +28,7 @@ class ComputedTimingPath extends TimingPath {
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
keyframes,
|
keyframes,
|
||||||
|
offset,
|
||||||
opacity,
|
opacity,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
|
@ -78,7 +80,6 @@ class ComputedTimingPath extends TimingPath {
|
||||||
const helper = new SummaryGraphHelper(state, keyframes,
|
const helper = new SummaryGraphHelper(state, keyframes,
|
||||||
totalDuration, durationPerPixel,
|
totalDuration, durationPerPixel,
|
||||||
getValueFunc, toPathStringFunc);
|
getValueFunc, toPathStringFunc);
|
||||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
|
||||||
|
|
||||||
return dom.g(
|
return dom.g(
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,19 +21,23 @@ class DelaySign extends PureComponent {
|
||||||
animation,
|
animation,
|
||||||
timeScale,
|
timeScale,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const {
|
||||||
|
fill,
|
||||||
|
playbackRate,
|
||||||
|
previousStartTime = 0,
|
||||||
|
} = animation.state;
|
||||||
|
|
||||||
const { state } = animation;
|
const delay = animation.state.delay / playbackRate;
|
||||||
const startTime = (state.previousStartTime || 0) - timeScale.minStartTime
|
const startTime =
|
||||||
+ (state.delay < 0 ? state.delay : 0);
|
previousStartTime - timeScale.minStartTime + (delay < 0 ? delay : 0);
|
||||||
const offset = startTime / timeScale.getDuration() * 100;
|
const offset = startTime / timeScale.getDuration() * 100;
|
||||||
const width = Math.abs(state.delay) / timeScale.getDuration() * 100;
|
const width = Math.abs(delay) / timeScale.getDuration() * 100;
|
||||||
|
|
||||||
const delayClass = state.delay < 0 ? "negative" : "";
|
|
||||||
const fillClass = state.fill === "both" || state.fill === "backwards" ? "fill" : "";
|
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
className: `animation-delay-sign ${ delayClass } ${ fillClass }`,
|
className: "animation-delay-sign" +
|
||||||
|
(delay < 0 ? " negative" : "") +
|
||||||
|
(fill === "both" || fill === "backwards" ? " fill" : ""),
|
||||||
style: {
|
style: {
|
||||||
width: `${ width }%`,
|
width: `${ width }%`,
|
||||||
left: `${ offset }%`,
|
left: `${ offset }%`,
|
||||||
|
|
|
@ -15,6 +15,7 @@ class EffectTimingPath extends TimingPath {
|
||||||
return {
|
return {
|
||||||
animation: PropTypes.object.isRequired,
|
animation: PropTypes.object.isRequired,
|
||||||
durationPerPixel: PropTypes.number.isRequired,
|
durationPerPixel: PropTypes.number.isRequired,
|
||||||
|
offset: PropTypes.number.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
totalDuration: PropTypes.number.isRequired,
|
totalDuration: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -24,6 +25,7 @@ class EffectTimingPath extends TimingPath {
|
||||||
const {
|
const {
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
|
offset,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -57,7 +59,6 @@ class EffectTimingPath extends TimingPath {
|
||||||
const helper = new SummaryGraphHelper(state, null,
|
const helper = new SummaryGraphHelper(state, null,
|
||||||
totalDuration, durationPerPixel,
|
totalDuration, durationPerPixel,
|
||||||
getValueFunc, toPathStringFunc);
|
getValueFunc, toPathStringFunc);
|
||||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
|
||||||
|
|
||||||
return dom.g(
|
return dom.g(
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,20 +21,27 @@ class EndDelaySign extends PureComponent {
|
||||||
animation,
|
animation,
|
||||||
timeScale,
|
timeScale,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
const {
|
||||||
|
delay,
|
||||||
|
duration,
|
||||||
|
fill,
|
||||||
|
iterationCount,
|
||||||
|
playbackRate,
|
||||||
|
previousStartTime = 0,
|
||||||
|
} = animation.state;
|
||||||
|
|
||||||
const { state } = animation;
|
const endDelay = animation.state.endDelay / playbackRate;
|
||||||
const startTime = (state.previousStartTime || 0) - timeScale.minStartTime;
|
const startTime = previousStartTime - timeScale.minStartTime;
|
||||||
const endTime = state.duration * state.iterationCount + state.delay;
|
const endTime =
|
||||||
const endDelay = state.endDelay < 0 ? state.endDelay : 0;
|
(duration * iterationCount + delay) / playbackRate + (endDelay < 0 ? endDelay : 0);
|
||||||
const offset = (startTime + endTime + endDelay) / timeScale.getDuration() * 100;
|
const offset = (startTime + endTime) / timeScale.getDuration() * 100;
|
||||||
const width = Math.abs(state.endDelay) / timeScale.getDuration() * 100;
|
const width = Math.abs(endDelay) / timeScale.getDuration() * 100;
|
||||||
|
|
||||||
const endDelayClass = state.endDelay < 0 ? "negative" : "";
|
|
||||||
const fillClass = state.fill === "both" || state.fill === "forwards" ? "fill" : "";
|
|
||||||
|
|
||||||
return dom.div(
|
return dom.div(
|
||||||
{
|
{
|
||||||
className: `animation-end-delay-sign ${ endDelayClass } ${ fillClass }`,
|
className: "animation-end-delay-sign" +
|
||||||
|
(endDelay < 0 ? " negative" : "") +
|
||||||
|
(fill === "both" || fill === "forwards" ? " fill" : ""),
|
||||||
style: {
|
style: {
|
||||||
width: `${ width }%`,
|
width: `${ width }%`,
|
||||||
left: `${ offset }%`,
|
left: `${ offset }%`,
|
||||||
|
|
|
@ -4,25 +4,13 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
|
||||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
|
||||||
const NegativePath = require("./NegativePath");
|
const NegativePath = require("./NegativePath");
|
||||||
|
|
||||||
class NegativeDelayPath extends NegativePath {
|
class NegativeDelayPath extends NegativePath {
|
||||||
static get propTypes() {
|
getClassName() {
|
||||||
return {
|
return "animation-negative-delay-path";
|
||||||
animation: PropTypes.object.isRequired,
|
|
||||||
durationPerPixel: PropTypes.number.isRequired,
|
|
||||||
keyframes: PropTypes.object.isRequired,
|
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
|
||||||
totalDuration: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
props.className = "animation-negative-delay-path";
|
|
||||||
super(props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGraph(state, helper) {
|
renderGraph(state, helper) {
|
||||||
|
|
|
@ -4,25 +4,13 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
|
||||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||||
|
|
||||||
const NegativePath = require("./NegativePath");
|
const NegativePath = require("./NegativePath");
|
||||||
|
|
||||||
class NegativeEndDelayPath extends NegativePath {
|
class NegativeEndDelayPath extends NegativePath {
|
||||||
static get propTypes() {
|
getClassName() {
|
||||||
return {
|
return "animation-negative-end-delay-path";
|
||||||
animation: PropTypes.object.isRequired,
|
|
||||||
durationPerPixel: PropTypes.number.isRequired,
|
|
||||||
keyframes: PropTypes.object.isRequired,
|
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
|
||||||
totalDuration: PropTypes.number.isRequired,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
props.className = "animation-negative-end-delay-path";
|
|
||||||
super(props);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGraph(state, helper) {
|
renderGraph(state, helper) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ class NegativePath extends PureComponent {
|
||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
durationPerPixel: PropTypes.number.isRequired,
|
durationPerPixel: PropTypes.number.isRequired,
|
||||||
keyframes: PropTypes.object.isRequired,
|
keyframes: PropTypes.object.isRequired,
|
||||||
|
offset: PropTypes.number.isRequired,
|
||||||
simulateAnimation: PropTypes.func.isRequired,
|
simulateAnimation: PropTypes.func.isRequired,
|
||||||
totalDuration: PropTypes.number.isRequired,
|
totalDuration: PropTypes.number.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -25,9 +26,9 @@ class NegativePath extends PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
animation,
|
animation,
|
||||||
className,
|
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
keyframes,
|
keyframes,
|
||||||
|
offset,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -75,11 +76,10 @@ class NegativePath extends PureComponent {
|
||||||
const helper = new SummaryGraphHelper(state, keyframes,
|
const helper = new SummaryGraphHelper(state, keyframes,
|
||||||
totalDuration, durationPerPixel,
|
totalDuration, durationPerPixel,
|
||||||
getValueFunc, toPathStringFunc);
|
getValueFunc, toPathStringFunc);
|
||||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
|
||||||
|
|
||||||
return dom.g(
|
return dom.g(
|
||||||
{
|
{
|
||||||
className,
|
className: this.getClassName(),
|
||||||
transform: `translate(${ offset })`
|
transform: `translate(${ offset })`
|
||||||
},
|
},
|
||||||
this.renderGraph(state, helper)
|
this.renderGraph(state, helper)
|
||||||
|
|
|
@ -33,7 +33,8 @@ class SummaryGraph extends PureComponent {
|
||||||
this.onClick = this.onClick.bind(this);
|
this.onClick = this.onClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick(event) {
|
||||||
|
event.stopPropagation();
|
||||||
this.props.selectAnimation(this.props.animation);
|
this.props.selectAnimation(this.props.animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -174,7 +174,9 @@ class SummaryGraphPath extends PureComponent {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const totalDuration = this.getTotalDuration(animation, timeScale);
|
const totalDuration = this.getTotalDuration(animation, timeScale);
|
||||||
const startTime = timeScale.minStartTime;
|
const { playbackRate, previousStartTime = 0 } = animation.state;
|
||||||
|
const startTime = timeScale.minStartTime * playbackRate;
|
||||||
|
const offset = previousStartTime * playbackRate;
|
||||||
const opacity = Math.max(1 / keyframesList.length, MIN_KEYFRAMES_EASING_OPACITY);
|
const opacity = Math.max(1 / keyframesList.length, MIN_KEYFRAMES_EASING_OPACITY);
|
||||||
|
|
||||||
return dom.svg(
|
return dom.svg(
|
||||||
|
@ -190,6 +192,7 @@ class SummaryGraphPath extends PureComponent {
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
keyframes,
|
keyframes,
|
||||||
|
offset,
|
||||||
opacity,
|
opacity,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
|
@ -201,6 +204,7 @@ class SummaryGraphPath extends PureComponent {
|
||||||
{
|
{
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
|
offset,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
}
|
}
|
||||||
|
@ -214,6 +218,7 @@ class SummaryGraphPath extends PureComponent {
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
keyframes,
|
keyframes,
|
||||||
|
offset,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
}
|
}
|
||||||
|
@ -228,6 +233,7 @@ class SummaryGraphPath extends PureComponent {
|
||||||
animation,
|
animation,
|
||||||
durationPerPixel,
|
durationPerPixel,
|
||||||
keyframes,
|
keyframes,
|
||||||
|
offset,
|
||||||
simulateAnimation,
|
simulateAnimation,
|
||||||
totalDuration,
|
totalDuration,
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,16 @@ DevToolsModules(
|
||||||
'AnimationTarget.js',
|
'AnimationTarget.js',
|
||||||
'AnimationTimelineTickItem.js',
|
'AnimationTimelineTickItem.js',
|
||||||
'AnimationTimelineTickList.js',
|
'AnimationTimelineTickList.js',
|
||||||
|
'AnimationToolbar.js',
|
||||||
'App.js',
|
'App.js',
|
||||||
|
'CurrentTimeLabel.js',
|
||||||
|
'CurrentTimeScrubber.js',
|
||||||
|
'CurrentTimeScrubberController.js',
|
||||||
|
'KeyframesProgressBar.js',
|
||||||
'KeyframesProgressTickItem.js',
|
'KeyframesProgressTickItem.js',
|
||||||
'KeyframesProgressTickList.js',
|
'KeyframesProgressTickList.js',
|
||||||
'NoAnimationPanel.js',
|
'NoAnimationPanel.js',
|
||||||
|
'PauseResumeButton.js',
|
||||||
|
'PlaybackRateSelector.js',
|
||||||
|
'RewindButton.js',
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In animation inspector, the scrubber and the progress bar moves along the current time
|
||||||
|
* of animation. However, the processing which sync with actual animations is heavy since
|
||||||
|
* we have to communication by the actor. The role of this class is to make the pseudo
|
||||||
|
* current time in animation inspector to proceed.
|
||||||
|
*/
|
||||||
|
class CurrentTimeTimer {
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param {Object} timeScale
|
||||||
|
* @param {Bool} shouldStopAfterEndTime
|
||||||
|
* If need to stop the timer after animation end time, set true.
|
||||||
|
* @param {window} win
|
||||||
|
* Be used for requestAnimationFrame and performance.
|
||||||
|
* @param {Function} onUpdated
|
||||||
|
* Listener function to get updating.
|
||||||
|
* This function is called with 2 parameters.
|
||||||
|
* 1st: current time
|
||||||
|
* 2nd: if shouldStopAfterEndTime is true and
|
||||||
|
* the current time is over the end time, true is given.
|
||||||
|
*/
|
||||||
|
constructor(timeScale, shouldStopAfterEndTime, win, onUpdated) {
|
||||||
|
this.baseCurrentTime = timeScale.documentCurrentTime - timeScale.minStartTime;
|
||||||
|
this.endTime = timeScale.maxEndTime - timeScale.minStartTime;
|
||||||
|
this.timerStartTime = win.performance.now();
|
||||||
|
|
||||||
|
this.shouldStopAfterEndTime = shouldStopAfterEndTime;
|
||||||
|
this.onUpdated = onUpdated;
|
||||||
|
this.win = win;
|
||||||
|
this.next = this.next.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.stop();
|
||||||
|
this.onUpdated = null;
|
||||||
|
this.win = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proceed the pseudo current time.
|
||||||
|
*/
|
||||||
|
next() {
|
||||||
|
if (this.doStop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTime =
|
||||||
|
this.baseCurrentTime + this.win.performance.now() - this.timerStartTime;
|
||||||
|
|
||||||
|
if (this.endTime < currentTime && this.shouldStopAfterEndTime) {
|
||||||
|
this.onUpdated(this.endTime, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onUpdated(currentTime);
|
||||||
|
this.win.requestAnimationFrame(this.next);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.doStop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CurrentTimeTimer;
|
|
@ -12,5 +12,6 @@ DIRS += [
|
||||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'animation.js'
|
'animation.js',
|
||||||
|
'current-time-timer.js'
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,6 +12,8 @@ const {
|
||||||
UPDATE_SIDEBAR_SIZE,
|
UPDATE_SIDEBAR_SIZE,
|
||||||
} = require("../actions/index");
|
} = require("../actions/index");
|
||||||
|
|
||||||
|
const TimeScale = require("../utils/timescale");
|
||||||
|
|
||||||
const INITIAL_STATE = {
|
const INITIAL_STATE = {
|
||||||
animations: [],
|
animations: [],
|
||||||
detailVisibility: false,
|
detailVisibility: false,
|
||||||
|
@ -21,12 +23,25 @@ const INITIAL_STATE = {
|
||||||
height: 0,
|
height: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
},
|
},
|
||||||
|
timeScale: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const reducers = {
|
const reducers = {
|
||||||
[UPDATE_ANIMATIONS](state, { animations }) {
|
[UPDATE_ANIMATIONS](state, { animations }) {
|
||||||
|
let detailVisibility = state.detailVisibility;
|
||||||
|
let selectedAnimation = state.selectedAnimation;
|
||||||
|
|
||||||
|
if (!state.selectedAnimation ||
|
||||||
|
!animations.find(animation => animation.actorID === selectedAnimation.actorID)) {
|
||||||
|
selectedAnimation = animations.length === 1 ? animations[0] : null;
|
||||||
|
detailVisibility = !!selectedAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
animations,
|
animations,
|
||||||
|
detailVisibility,
|
||||||
|
selectedAnimation,
|
||||||
|
timeScale: new TimeScale(animations),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
tags = devtools
|
tags = devtools
|
||||||
subsuite = devtools
|
subsuite = devtools
|
||||||
support-files =
|
support-files =
|
||||||
|
doc_custom_playback_rate.html
|
||||||
doc_multi_easings.html
|
doc_multi_easings.html
|
||||||
doc_multi_keyframes.html
|
doc_multi_keyframes.html
|
||||||
doc_multi_timings.html
|
doc_multi_timings.html
|
||||||
|
@ -22,11 +23,19 @@ support-files =
|
||||||
[browser_animation_animation-list.js]
|
[browser_animation_animation-list.js]
|
||||||
[browser_animation_animation-target.js]
|
[browser_animation_animation-target.js]
|
||||||
[browser_animation_animation-timeline-tick.js]
|
[browser_animation_animation-timeline-tick.js]
|
||||||
|
[browser_animation_current-time-label.js]
|
||||||
|
[browser_animation_current-time-scrubber.js]
|
||||||
[browser_animation_empty_on_invalid_nodes.js]
|
[browser_animation_empty_on_invalid_nodes.js]
|
||||||
[browser_animation_inspector_exists.js]
|
[browser_animation_inspector_exists.js]
|
||||||
[browser_animation_keyframes-graph_computed-value-path.js]
|
[browser_animation_keyframes-graph_computed-value-path.js]
|
||||||
[browser_animation_keyframes-graph_computed-value-path_easing-hint.js]
|
[browser_animation_keyframes-graph_computed-value-path_easing-hint.js]
|
||||||
[browser_animation_keyframes-graph_keyframe-marker.js]
|
[browser_animation_keyframes-graph_keyframe-marker.js]
|
||||||
|
[browser_animation_keyframes-progress-bar.js]
|
||||||
|
[browser_animation_logic_auto-stop.js]
|
||||||
|
[browser_animation_pause-resume-button.js]
|
||||||
|
[browser_animation_pause-resume-button_spacebar.js]
|
||||||
|
[browser_animation_playback-rate-selector.js]
|
||||||
|
[browser_animation_rewind-button.js]
|
||||||
[browser_animation_summary-graph_animation-name.js]
|
[browser_animation_summary-graph_animation-name.js]
|
||||||
[browser_animation_summary-graph_compositor.js]
|
[browser_animation_summary-graph_compositor.js]
|
||||||
[browser_animation_summary-graph_computed-timing-path.js]
|
[browser_animation_summary-graph_computed-timing-path.js]
|
||||||
|
|
|
@ -14,7 +14,7 @@ add_task(async function() {
|
||||||
ok(panel.querySelector(".animation-list"),
|
ok(panel.querySelector(".animation-list"),
|
||||||
"The animation-list is in the DOM");
|
"The animation-list is in the DOM");
|
||||||
is(panel.querySelectorAll(".animation-list .animation-item").length,
|
is(panel.querySelectorAll(".animation-list .animation-item").length,
|
||||||
animationInspector.animations.length,
|
animationInspector.state.animations.length,
|
||||||
"The number of animations displayed matches the number of animations");
|
"The number of animations displayed matches the number of animations");
|
||||||
|
|
||||||
info("Checking the background color for the animation list items");
|
info("Checking the background color for the animation list items");
|
||||||
|
|
|
@ -14,7 +14,7 @@ add_task(async function() {
|
||||||
|
|
||||||
info("Checking the animation target elements existance");
|
info("Checking the animation target elements existance");
|
||||||
const animationItemEls = panel.querySelectorAll(".animation-list .animation-item");
|
const animationItemEls = panel.querySelectorAll(".animation-list .animation-item");
|
||||||
is(animationItemEls.length, animationInspector.animations.length,
|
is(animationItemEls.length, animationInspector.state.animations.length,
|
||||||
"Number of animation target element should be same to number of animations "
|
"Number of animation target element should be same to number of animations "
|
||||||
+ "that displays");
|
+ "that displays");
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ const TIME_GRADUATION_MIN_SPACING = 40;
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
await addTab(URL_ROOT + "doc_simple_animation.html");
|
await addTab(URL_ROOT + "doc_simple_animation.html");
|
||||||
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
const timeScale = new TimeScale(animationInspector.animations);
|
const timeScale = new TimeScale(animationInspector.state.animations);
|
||||||
|
|
||||||
info("Checking animation list header element existence");
|
info("Checking animation list header element existence");
|
||||||
const listContainerEl = panel.querySelector(".animation-list-container");
|
const listContainerEl = panel.querySelector(".animation-list-container");
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following CurrentTimeLabel component:
|
||||||
|
// * element existence
|
||||||
|
// * label content at plural timing
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_multi_timings.html");
|
||||||
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking current time label existence");
|
||||||
|
const labelEl = panel.querySelector(".current-time-label");
|
||||||
|
ok(labelEl, "current time label should exist");
|
||||||
|
|
||||||
|
info("Checking current time label content");
|
||||||
|
await selectNodeAndWaitForAnimations(".keyframes-easing-step", inspector);
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0.5);
|
||||||
|
assertLabelContent(labelEl, animationInspector.state.animations[0].state.currentTime);
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0.2);
|
||||||
|
assertLabelContent(labelEl, animationInspector.state.animations[0].state.currentTime);
|
||||||
|
|
||||||
|
info("Checking current time label content during running");
|
||||||
|
// Resume
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
const previousContent = labelEl.textContent;
|
||||||
|
await wait(1000);
|
||||||
|
const currentContent = labelEl.textContent;
|
||||||
|
isnot(previousContent, currentContent, "Current time label should change");
|
||||||
|
});
|
||||||
|
|
||||||
|
function assertLabelContent(labelEl, time) {
|
||||||
|
const expected = formatStopwatchTime(time);
|
||||||
|
is(labelEl.textContent, expected, `Content of label should be ${ expected }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatStopwatchTime(time) {
|
||||||
|
// Format falsy values as 0
|
||||||
|
if (!time) {
|
||||||
|
return "00:00.000";
|
||||||
|
}
|
||||||
|
|
||||||
|
let milliseconds = parseInt(time % 1000, 10);
|
||||||
|
let seconds = parseInt((time / 1000) % 60, 10);
|
||||||
|
let minutes = parseInt((time / (1000 * 60)), 10);
|
||||||
|
|
||||||
|
let pad = (nb, max) => {
|
||||||
|
if (nb < max) {
|
||||||
|
return new Array((max + "").length - (nb + "").length + 1).join("0") + nb;
|
||||||
|
}
|
||||||
|
return nb;
|
||||||
|
};
|
||||||
|
|
||||||
|
minutes = pad(minutes, 10);
|
||||||
|
seconds = pad(seconds, 10);
|
||||||
|
milliseconds = pad(milliseconds, 100);
|
||||||
|
|
||||||
|
return `${minutes}:${seconds}.${milliseconds}`;
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following CurrentTimeScrubber and CurrentTimeScrubberController components:
|
||||||
|
// * element existence
|
||||||
|
// * scrubber position validity
|
||||||
|
// * make animations currentTime to change by click on the controller
|
||||||
|
// * mouse drag on the scrubber
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_multi_timings.html");
|
||||||
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking scrubber controller existence");
|
||||||
|
const controllerEl = panel.querySelector(".current-time-scrubber-controller");
|
||||||
|
ok(controllerEl, "scrubber controller should exist");
|
||||||
|
|
||||||
|
info("Checking scrubber existence");
|
||||||
|
const scrubberEl = controllerEl.querySelector(".current-time-scrubber");
|
||||||
|
ok(scrubberEl, "scrubber should exist");
|
||||||
|
|
||||||
|
info("Checking scrubber changes current time of animation and the position");
|
||||||
|
await selectNodeAndWaitForAnimations(".enddelay-with-iterations-infinity", inspector);
|
||||||
|
const duration = animationInspector.state.timeScale.getDuration();
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0);
|
||||||
|
assertAnimationsCurrentTime(animationInspector, 0);
|
||||||
|
assertPosition(scrubberEl, controllerEl, 0, animationInspector);
|
||||||
|
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 1);
|
||||||
|
assertAnimationsCurrentTime(animationInspector, duration);
|
||||||
|
assertPosition(scrubberEl, controllerEl, duration, animationInspector);
|
||||||
|
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0.5);
|
||||||
|
assertAnimationsCurrentTime(animationInspector, duration * 0.5);
|
||||||
|
assertPosition(scrubberEl, controllerEl, duration * 0.5, animationInspector);
|
||||||
|
|
||||||
|
info("Checking current time scrubber position during running");
|
||||||
|
// Running again
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
let previousX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
await wait(100);
|
||||||
|
let currentX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
isnot(previousX, currentX, "Scrubber should be moved");
|
||||||
|
|
||||||
|
info("Checking draggable on scrubber over animation list");
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
previousX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
await dragOnCurrentTimeScrubber(animationInspector, panel, 0.5, 2, 30);
|
||||||
|
currentX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
isnot(previousX, currentX, "Scrubber should be draggable");
|
||||||
|
|
||||||
|
info("Checking a behavior which mouse out from animation inspector area " +
|
||||||
|
"during dragging from controller");
|
||||||
|
await dragOnCurrentTimeScrubberController(animationInspector, panel, 0.5, 2);
|
||||||
|
ok(!panel.querySelector(".animation-list-container")
|
||||||
|
.classList.contains("active-scrubber"), "Click and DnD should be inactive");
|
||||||
|
});
|
||||||
|
|
||||||
|
function assertPosition(scrubberEl, controllerEl, time, animationInspector) {
|
||||||
|
const controllerBounds = controllerEl.getBoundingClientRect();
|
||||||
|
const scrubberBounds = scrubberEl.getBoundingClientRect();
|
||||||
|
const scrubberX = scrubberBounds.x + scrubberBounds.width / 2 - controllerBounds.x;
|
||||||
|
const timeScale = animationInspector.state.timeScale;
|
||||||
|
const expected = Math.round(time / timeScale.getDuration() * controllerBounds.width);
|
||||||
|
is(scrubberX, expected, `Position should be ${ expected } at ${ time }ms`);
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following KeyframesProgressBar:
|
||||||
|
// * element existence
|
||||||
|
// * progress bar position in multi effect timings
|
||||||
|
// * progress bar position after changing playback rate
|
||||||
|
// * progress bar position when select another animation
|
||||||
|
|
||||||
|
const POSITION_TESTCASES = [
|
||||||
|
{
|
||||||
|
targetClassName: "cssanimation-linear",
|
||||||
|
scrubberPositions: [0, 0.25, 0.5, 0.75, 1],
|
||||||
|
expectedPositions: [0, 0.25, 0.5, 0.75, 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "easing-step",
|
||||||
|
scrubberPositions: [0, 0.49, 0.5, 0.99],
|
||||||
|
expectedPositions: [0, 0, 0.5, 0.5],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "delay-positive",
|
||||||
|
scrubberPositions: [0, 0.33, 0.5],
|
||||||
|
expectedPositions: [0, 0, 0.25],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "delay-negative",
|
||||||
|
scrubberPositions: [0, 0.49, 0.5, 0.75],
|
||||||
|
expectedPositions: [0, 0, 0.5, 0.75],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "enddelay-positive",
|
||||||
|
scrubberPositions: [0, 0.66, 0.67, 0.99],
|
||||||
|
expectedPositions: [0, 0.99, 0, 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "enddelay-negative",
|
||||||
|
scrubberPositions: [0, 0.49, 0.5, 0.99],
|
||||||
|
expectedPositions: [0, 0.49, 0, 0],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "direction-reverse-with-iterations-infinity",
|
||||||
|
scrubberPositions: [0, 0.25, 0.5, 0.75, 1],
|
||||||
|
expectedPositions: [1, 0.75, 0.5, 0.25, 1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targetClassName: "fill-both-width-delay-iterationstart",
|
||||||
|
scrubberPositions: [0, 0.33, 0.66, 0.833, 1],
|
||||||
|
expectedPositions: [0.5, 0.5, 0.99, 0.25, 0.5],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_multi_timings.html");
|
||||||
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking progress bar position in multi effect timings");
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
|
||||||
|
for (const testcase of POSITION_TESTCASES) {
|
||||||
|
info(`Checking progress bar position for ${ testcase.targetClassName }`);
|
||||||
|
await selectNodeAndWaitForAnimations(`.${ testcase.targetClassName }`, inspector);
|
||||||
|
|
||||||
|
info("Checking progress bar existence");
|
||||||
|
const areaEl = panel.querySelector(".keyframes-progress-bar-area");
|
||||||
|
ok(areaEl, "progress bar area should exist");
|
||||||
|
const barEl = areaEl.querySelector(".keyframes-progress-bar");
|
||||||
|
ok(barEl, "progress bar should exist");
|
||||||
|
|
||||||
|
const scrubberPositions = testcase.scrubberPositions;
|
||||||
|
const expectedPositions = testcase.expectedPositions;
|
||||||
|
|
||||||
|
for (let i = 0; i < scrubberPositions.length; i++) {
|
||||||
|
info(`Scrubber position is ${ scrubberPositions[i] }`);
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector,
|
||||||
|
panel, scrubberPositions[i]);
|
||||||
|
assertPosition(barEl, areaEl, expectedPositions[i], animationInspector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function assertPosition(barEl, areaEl, expectedRate, animationInspector) {
|
||||||
|
const controllerBounds = areaEl.getBoundingClientRect();
|
||||||
|
const barBounds = barEl.getBoundingClientRect();
|
||||||
|
const barX = barBounds.x + barBounds.width / 2 - controllerBounds.x;
|
||||||
|
const expected = controllerBounds.width * expectedRate;
|
||||||
|
ok(expected - 1 < barX && barX < expected + 1,
|
||||||
|
`Position should apploximately be ${ expected } (x of bar is ${ barX })`);
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Animation inspector makes the current time to stop
|
||||||
|
// after end of animation duration except iterations infinity.
|
||||||
|
// Test followings:
|
||||||
|
// * state of animations and UI components after end of animation duration
|
||||||
|
// * state of animations and UI components after end of animation duration
|
||||||
|
// but iteration count is infinity
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_multi_timings.html");
|
||||||
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking state after end of animation duration");
|
||||||
|
await selectNodeAndWaitForAnimations(".easing-step", inspector);
|
||||||
|
const pixelsData = getDurationAndRate(animationInspector, panel, 5);
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector,
|
||||||
|
panel, 1 - pixelsData.rate);
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
// Must be able to catch rendering event after stopping the animation.
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
await assertStates(animationInspector, panel, false);
|
||||||
|
|
||||||
|
info("Checking state after end of animation duration and infinity iterations");
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
await selectNodeAndWaitForAnimations(".enddelay-with-iterations-infinity", inspector);
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 1);
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
await assertStates(animationInspector, panel, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function assertStates(animationInspector, panel, shouldRunning) {
|
||||||
|
const buttonEl = panel.querySelector(".pause-resume-button");
|
||||||
|
const labelEl = panel.querySelector(".current-time-label");
|
||||||
|
const scrubberEl = panel.querySelector(".current-time-scrubber");
|
||||||
|
|
||||||
|
const previousLabelContent = labelEl.textContent;
|
||||||
|
const previousScrubberX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
await wait(100);
|
||||||
|
const currentLabelContent = labelEl.textContent;
|
||||||
|
const currentScrubberX = scrubberEl.getBoundingClientRect().x;
|
||||||
|
|
||||||
|
if (shouldRunning) {
|
||||||
|
isnot(previousLabelContent, currentLabelContent,
|
||||||
|
"Current time label content should change");
|
||||||
|
isnot(previousScrubberX, currentScrubberX,
|
||||||
|
"Current time scrubber position should change");
|
||||||
|
ok(!buttonEl.classList.contains("paused"),
|
||||||
|
"State of button should be running");
|
||||||
|
assertAnimationsRunning(animationInspector, panel);
|
||||||
|
} else {
|
||||||
|
is(previousLabelContent, currentLabelContent,
|
||||||
|
"Current time label Content should not change");
|
||||||
|
is(previousScrubberX, currentScrubberX,
|
||||||
|
"Current time scrubber position should not change");
|
||||||
|
ok(buttonEl.classList.contains("paused"),
|
||||||
|
"State of button should be paused");
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following PauseResumeButton component:
|
||||||
|
// * element existence
|
||||||
|
// * state during running animations
|
||||||
|
// * state during pausing animations
|
||||||
|
// * make animations to pause by push button
|
||||||
|
// * make animations to resume by push button
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_custom_playback_rate.html");
|
||||||
|
const { animationInspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking pause/resume button existence");
|
||||||
|
const buttonEl = panel.querySelector(".pause-resume-button");
|
||||||
|
ok(buttonEl, "pause/resume button should exist");
|
||||||
|
|
||||||
|
info("Checking state during running animations");
|
||||||
|
ok(!buttonEl.classList.contains("paused"), "State of button should be running");
|
||||||
|
|
||||||
|
info("Checking button makes animations to pause");
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
ok(buttonEl.classList.contains("paused"), "State of button should be paused");
|
||||||
|
|
||||||
|
info("Checking button makes animations to resume");
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
assertAnimationsRunning(animationInspector, panel);
|
||||||
|
ok(!buttonEl.classList.contains("paused"), "State of button should be resumed");
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following PauseResumeButton component with spacebar:
|
||||||
|
// * make animations to pause/resume by spacebar
|
||||||
|
// * combination with other UI components
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_custom_playback_rate.html");
|
||||||
|
const { animationInspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking spacebar makes animations to pause");
|
||||||
|
await sendSpaceKeyEvent(animationInspector, panel);
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
await sendSpaceKeyEvent(animationInspector, panel);
|
||||||
|
assertAnimationsRunning(animationInspector, panel);
|
||||||
|
|
||||||
|
info("Checking spacebar works with other UI components");
|
||||||
|
// To pause
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
// To resume
|
||||||
|
await sendSpaceKeyEvent(animationInspector, panel);
|
||||||
|
assertAnimationsRunning(animationInspector, panel);
|
||||||
|
// To pause
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0.5);
|
||||||
|
// To resume
|
||||||
|
await clickOnPauseResumeButton(animationInspector, panel);
|
||||||
|
// To pause
|
||||||
|
await sendSpaceKeyEvent(animationInspector, panel);
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
});
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following PlaybackRateSelector component:
|
||||||
|
// * element existence
|
||||||
|
// * make playback rate of animations by the selector
|
||||||
|
// * in case of animations have mixed playback rate
|
||||||
|
// * in case of animations have playback rate which is not default selectable value
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_custom_playback_rate.html");
|
||||||
|
const { animationInspector, inspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking playback rate selector existence");
|
||||||
|
const selectEl = panel.querySelector(".playback-rate-selector");
|
||||||
|
ok(selectEl, "scrubber controller should exist");
|
||||||
|
|
||||||
|
info("Checking playback rate existence which includes custom rate of animations");
|
||||||
|
const selectableRates = [0.1, 0.25, 0.5, 1, 1.5, 2, 5, 10];
|
||||||
|
is(selectEl.options.length, selectableRates.length,
|
||||||
|
`Length of options should be ${ selectableRates.length }`);
|
||||||
|
for (let i = 0; i < selectEl.options.length; i++) {
|
||||||
|
const optionEl = selectEl.options[i];
|
||||||
|
const selectableRate = selectableRates[i];
|
||||||
|
is(Number(optionEl.value), selectableRate,
|
||||||
|
`Option of index[${ i }] should be ${ selectableRate }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Checking selected playback rate");
|
||||||
|
is(Number(selectEl.value), 1.5, "Selected option should be 1.5");
|
||||||
|
|
||||||
|
info("Checking playback rate of animations");
|
||||||
|
await clickOnPlaybackRateSelector(animationInspector, panel, 0.5);
|
||||||
|
assertPlaybackRate(animationInspector, 0.5);
|
||||||
|
|
||||||
|
info("Checking mixed playback rate");
|
||||||
|
await selectNodeAndWaitForAnimations("div", inspector);
|
||||||
|
await clickOnPlaybackRateSelector(animationInspector, panel, 2);
|
||||||
|
assertPlaybackRate(animationInspector, 2);
|
||||||
|
await selectNodeAndWaitForAnimations("body", inspector);
|
||||||
|
is(selectEl.value, "", "Selected option should be empty");
|
||||||
|
|
||||||
|
info("Checking playback rate after re-setting");
|
||||||
|
await clickOnPlaybackRateSelector(animationInspector, panel, 1);
|
||||||
|
assertPlaybackRate(animationInspector, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function assertPlaybackRate(animationInspector, rate) {
|
||||||
|
const isRateEqual =
|
||||||
|
animationInspector.state.animations.every(({state}) => state.playbackRate === rate);
|
||||||
|
ok(isRateEqual, `Playback rate of animations should be ${ rate }`);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test for following RewindButton component:
|
||||||
|
// * element existence
|
||||||
|
// * make animations to rewind to zero
|
||||||
|
// * the state should be always paused after rewinding
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(URL_ROOT + "doc_custom_playback_rate.html");
|
||||||
|
const { animationInspector, panel } = await openAnimationInspector();
|
||||||
|
|
||||||
|
info("Checking button existence");
|
||||||
|
ok(panel.querySelector(".rewind-button"), "Rewind button should exist");
|
||||||
|
|
||||||
|
info("Checking rewind button makes animations to rewind to zero");
|
||||||
|
await clickOnRewindButton(animationInspector, panel);
|
||||||
|
assertAnimationsCurrentTime(animationInspector, 0);
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
|
||||||
|
info("Checking rewind button makes animations after clicking scrubber");
|
||||||
|
await clickOnCurrentTimeScrubberController(animationInspector, panel, 0.5);
|
||||||
|
await clickOnRewindButton(animationInspector, panel);
|
||||||
|
assertAnimationsCurrentTime(animationInspector, 0);
|
||||||
|
assertAnimationsPausing(animationInspector, panel);
|
||||||
|
});
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
background-color: lime;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const duration = 100000;
|
||||||
|
|
||||||
|
function createAnimation() {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
document.body.appendChild(div);
|
||||||
|
const animation = div.animate([{ opacity: 0 }], duration);
|
||||||
|
animation.playbackRate = 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
createAnimation();
|
||||||
|
createAnimation();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -121,6 +121,178 @@ const clickOnDetailCloseButton = function(panel) {
|
||||||
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
|
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on pause/resume button.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* The panel instance.
|
||||||
|
*/
|
||||||
|
const clickOnPauseResumeButton = async function(animationInspector, panel) {
|
||||||
|
info("Click on pause/resume button");
|
||||||
|
const buttonEl = panel.querySelector(".pause-resume-button");
|
||||||
|
const bounds = buttonEl.getBoundingClientRect();
|
||||||
|
const x = bounds.width / 2;
|
||||||
|
const y = bounds.height / 2;
|
||||||
|
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on rewind button.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* The panel instance.
|
||||||
|
*/
|
||||||
|
const clickOnRewindButton = async function(animationInspector, panel) {
|
||||||
|
info("Click on rewind button");
|
||||||
|
const buttonEl = panel.querySelector(".rewind-button");
|
||||||
|
const bounds = buttonEl.getBoundingClientRect();
|
||||||
|
const x = bounds.width / 2;
|
||||||
|
const y = bounds.height / 2;
|
||||||
|
EventUtils.synthesizeMouse(buttonEl, x, y, {}, buttonEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on the scrubber controller pane to update the animation current time.
|
||||||
|
*
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} mouseDownPosition
|
||||||
|
* rate on scrubber controller pane.
|
||||||
|
* This method calculates
|
||||||
|
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
|
||||||
|
* as the clientX of MouseEvent.
|
||||||
|
*/
|
||||||
|
const clickOnCurrentTimeScrubberController = async function(animationInspector,
|
||||||
|
panel,
|
||||||
|
mouseDownPosition,
|
||||||
|
mouseMovePosition) {
|
||||||
|
const controllerEl = panel.querySelector(".current-time-scrubber-controller");
|
||||||
|
const bounds = controllerEl.getBoundingClientRect();
|
||||||
|
const mousedonwX = bounds.width * mouseDownPosition;
|
||||||
|
|
||||||
|
info(`Click ${ mousedonwX } on scrubber controller`);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0, {}, controllerEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click on playback rate selector to select given rate.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} rate
|
||||||
|
*/
|
||||||
|
const clickOnPlaybackRateSelector = async function(animationInspector, panel, rate) {
|
||||||
|
info(`Click on playback rate selector to select ${rate}`);
|
||||||
|
const selectEl = panel.querySelector(".playback-rate-selector");
|
||||||
|
const optionEl = [...selectEl.options].filter(o => Number(o.value) === rate)[0];
|
||||||
|
|
||||||
|
if (!optionEl) {
|
||||||
|
ok(false, `Could not find an option for rate ${ rate } in the rate selector. ` +
|
||||||
|
`Values are: ${ [...selectEl.options].map(o => o.value) }`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const win = selectEl.ownerGlobal;
|
||||||
|
EventUtils.synthesizeMouseAtCenter(selectEl, { type: "mousedown" }, win);
|
||||||
|
EventUtils.synthesizeMouseAtCenter(optionEl, { type: "mouseup" }, win);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drag on the scrubber to update the animation current time.
|
||||||
|
*
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} mouseDownPosition
|
||||||
|
* rate on scrubber controller pane.
|
||||||
|
* This method calculates
|
||||||
|
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
|
||||||
|
* as the clientX of MouseEvent.
|
||||||
|
* @param {Number} mouseMovePosition
|
||||||
|
* Dispatch mousemove event with mouseMovePosition after mousedown.
|
||||||
|
* Calculation for clinetX is same to above.
|
||||||
|
* @param {Number} mouseYPixel
|
||||||
|
* Y of mouse in pixel.
|
||||||
|
*/
|
||||||
|
const dragOnCurrentTimeScrubber = async function(animationInspector,
|
||||||
|
panel,
|
||||||
|
mouseDownPosition,
|
||||||
|
mouseMovePosition,
|
||||||
|
mouseYPixel) {
|
||||||
|
const controllerEl = panel.querySelector(".current-time-scrubber");
|
||||||
|
const bounds = controllerEl.getBoundingClientRect();
|
||||||
|
const mousedonwX = bounds.width * mouseDownPosition;
|
||||||
|
const mousemoveX = bounds.width * mouseMovePosition;
|
||||||
|
|
||||||
|
info(`Drag on scrubber from ${ mousedonwX } to ${ mousemoveX }`);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousedonwX, mouseYPixel,
|
||||||
|
{ type: "mousedown" }, controllerEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousemoveX, mouseYPixel,
|
||||||
|
{ type: "mousemove" }, controllerEl.ownerGlobal);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousemoveX, mouseYPixel,
|
||||||
|
{ type: "mouseup" }, controllerEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drag on the scrubber controller pane to update the animation current time.
|
||||||
|
*
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} mouseDownPosition
|
||||||
|
* rate on scrubber controller pane.
|
||||||
|
* This method calculates
|
||||||
|
* `mouseDownPosition * offsetWidth + offsetLeft of scrubber controller pane`
|
||||||
|
* as the clientX of MouseEvent.
|
||||||
|
* @param {Number} mouseMovePosition
|
||||||
|
* Dispatch mousemove event with mouseMovePosition after mousedown.
|
||||||
|
* Calculation for clinetX is same to above.
|
||||||
|
*/
|
||||||
|
const dragOnCurrentTimeScrubberController = async function(animationInspector,
|
||||||
|
panel,
|
||||||
|
mouseDownPosition,
|
||||||
|
mouseMovePosition) {
|
||||||
|
const controllerEl = panel.querySelector(".current-time-scrubber-controller");
|
||||||
|
const bounds = controllerEl.getBoundingClientRect();
|
||||||
|
const mousedonwX = bounds.width * mouseDownPosition;
|
||||||
|
const mousemoveX = bounds.width * mouseMovePosition;
|
||||||
|
|
||||||
|
info(`Drag on scrubber controller from ${ mousedonwX } to ${ mousemoveX }`);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousedonwX, 0,
|
||||||
|
{ type: "mousedown" }, controllerEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousemoveX, 0,
|
||||||
|
{ type: "mousemove" }, controllerEl.ownerGlobal);
|
||||||
|
EventUtils.synthesizeMouse(controllerEl, mousemoveX, 0,
|
||||||
|
{ type: "mouseup" }, controllerEl.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current animation duration and rate of
|
||||||
|
* clickOrDragOnCurrentTimeScrubberController in given pixels.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} pixels
|
||||||
|
* @return {Object}
|
||||||
|
* {
|
||||||
|
* duration,
|
||||||
|
* rate,
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
const getDurationAndRate = function(animationInspector, panel, pixels) {
|
||||||
|
const controllerEl = panel.querySelector(".current-time-scrubber-controller");
|
||||||
|
const bounds = controllerEl.getBoundingClientRect();
|
||||||
|
const duration =
|
||||||
|
animationInspector.state.timeScale.getDuration() / bounds.width * pixels;
|
||||||
|
const rate = 1 / bounds.width * pixels;
|
||||||
|
return { duration, rate };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the inspector's current selection to a node or to the first match of the
|
* Set the inspector's current selection to a node or to the first match of the
|
||||||
* given css selector and wait for the animations to be displayed
|
* given css selector and wait for the animations to be displayed
|
||||||
|
@ -144,6 +316,18 @@ const selectNodeAndWaitForAnimations = async function(data, inspector, reason =
|
||||||
await waitForRendering(inspector.animationinspector);
|
await waitForRendering(inspector.animationinspector);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send keyboard event of space to given panel.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
*/
|
||||||
|
const sendSpaceKeyEvent = async function(animationInspector, panel) {
|
||||||
|
panel.focus();
|
||||||
|
EventUtils.sendKey("SPACE", panel.ownerGlobal);
|
||||||
|
await waitForSummaryAndDetail(animationInspector);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the sidebar width by given parameter.
|
* Set the sidebar width by given parameter.
|
||||||
*
|
*
|
||||||
|
@ -177,8 +361,10 @@ const waitForRendering = async function(animationInspector) {
|
||||||
*
|
*
|
||||||
* @param {AnimationInspector} inspector
|
* @param {AnimationInspector} inspector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const waitForAnimationDetail = async function(animationInspector) {
|
const waitForAnimationDetail = async function(animationInspector) {
|
||||||
if (animationInspector.animations.length === 1) {
|
if (animationInspector.state.selectedAnimation &&
|
||||||
|
animationInspector.state.detailVisibility) {
|
||||||
await animationInspector.once("animation-keyframes-rendered");
|
await animationInspector.once("animation-keyframes-rendered");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -190,7 +376,7 @@ const waitForAnimationDetail = async function(animationInspector) {
|
||||||
* @param {AnimationInspector} animationInspector
|
* @param {AnimationInspector} animationInspector
|
||||||
*/
|
*/
|
||||||
const waitForAllAnimationTargets = async function(animationInspector) {
|
const waitForAllAnimationTargets = async function(animationInspector) {
|
||||||
for (let i = 0; i < animationInspector.animations.length; i++) {
|
for (let i = 0; i < animationInspector.state.animations.length; i++) {
|
||||||
await animationInspector.once("animation-target-rendered");
|
await animationInspector.once("animation-target-rendered");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -201,11 +387,74 @@ const waitForAllAnimationTargets = async function(animationInspector) {
|
||||||
* @param {AnimationInspector} inspector
|
* @param {AnimationInspector} inspector
|
||||||
*/
|
*/
|
||||||
const waitForAllSummaryGraph = async function(animationInspector) {
|
const waitForAllSummaryGraph = async function(animationInspector) {
|
||||||
for (let i = 0; i < animationInspector.animations.length; i++) {
|
for (let i = 0; i < animationInspector.state.animations.length; i++) {
|
||||||
await animationInspector.once("animation-summary-graph-rendered");
|
await animationInspector.once("animation-summary-graph-rendered");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for rendering of all summary graph and detail.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} inspector
|
||||||
|
*/
|
||||||
|
const waitForSummaryAndDetail = async function(animationInspector) {
|
||||||
|
await Promise.all([
|
||||||
|
waitForAllSummaryGraph(animationInspector),
|
||||||
|
waitForAnimationDetail(animationInspector),
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether current time of all animations and UI are given specified time.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {Number} time
|
||||||
|
*/
|
||||||
|
function assertAnimationsCurrentTime(animationInspector, time) {
|
||||||
|
const isTimeEqual =
|
||||||
|
animationInspector.state.animations.every(({state}) => state.currentTime === time);
|
||||||
|
ok(isTimeEqual, `Current time of animations should be ${ time }`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the animations are pausing.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
*/
|
||||||
|
function assertAnimationsPausing(animationInspector, panel) {
|
||||||
|
assertAnimationsPausingOrRunning(animationInspector, panel, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the animations are pausing/running.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
* @param {boolean} shouldPause
|
||||||
|
*/
|
||||||
|
function assertAnimationsPausingOrRunning(animationInspector, panel, shouldPause) {
|
||||||
|
const hasRunningAnimation =
|
||||||
|
animationInspector.state.animations.some(({state}) => state.playState === "running");
|
||||||
|
|
||||||
|
if (shouldPause) {
|
||||||
|
is(hasRunningAnimation, false, "All animations should be paused");
|
||||||
|
} else {
|
||||||
|
is(hasRunningAnimation, true, "Animations should be running at least one");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the animations are running.
|
||||||
|
*
|
||||||
|
* @param {AnimationInspector} animationInspector
|
||||||
|
* @param {AnimationsPanel} panel
|
||||||
|
*/
|
||||||
|
function assertAnimationsRunning(animationInspector, panel) {
|
||||||
|
assertAnimationsPausingOrRunning(animationInspector, panel, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the <stop> element in the given linearGradientEl for the correct offset
|
* Check the <stop> element in the given linearGradientEl for the correct offset
|
||||||
* and color attributes.
|
* and color attributes.
|
||||||
|
|
|
@ -24,6 +24,8 @@ class TimeScale {
|
||||||
constructor(animations) {
|
constructor(animations) {
|
||||||
this.minStartTime = Infinity;
|
this.minStartTime = Infinity;
|
||||||
this.maxEndTime = 0;
|
this.maxEndTime = 0;
|
||||||
|
this.documentCurrentTime = 0;
|
||||||
|
|
||||||
for (const animation of animations) {
|
for (const animation of animations) {
|
||||||
this.addAnimation(animation.state);
|
this.addAnimation(animation.state);
|
||||||
}
|
}
|
||||||
|
@ -38,11 +40,12 @@ class TimeScale {
|
||||||
addAnimation(state) {
|
addAnimation(state) {
|
||||||
let {
|
let {
|
||||||
delay,
|
delay,
|
||||||
|
documentCurrentTime,
|
||||||
duration,
|
duration,
|
||||||
endDelay = 0,
|
endDelay = 0,
|
||||||
iterationCount,
|
iterationCount,
|
||||||
playbackRate,
|
playbackRate,
|
||||||
previousStartTime,
|
previousStartTime = 0,
|
||||||
} = state;
|
} = state;
|
||||||
|
|
||||||
const toRate = v => v / playbackRate;
|
const toRate = v => v / playbackRate;
|
||||||
|
@ -53,8 +56,6 @@ class TimeScale {
|
||||||
// be displaying the delay outside the time window if we didn't take it into
|
// be displaying the delay outside the time window if we didn't take it into
|
||||||
// account here.
|
// account here.
|
||||||
const relevantDelay = delay < 0 ? toRate(delay) : 0;
|
const relevantDelay = delay < 0 ? toRate(delay) : 0;
|
||||||
previousStartTime = previousStartTime || 0;
|
|
||||||
|
|
||||||
const startTime = toRate(minZero(delay)) +
|
const startTime = toRate(minZero(delay)) +
|
||||||
rateRelativeDuration +
|
rateRelativeDuration +
|
||||||
endDelay;
|
endDelay;
|
||||||
|
@ -67,6 +68,8 @@ class TimeScale {
|
||||||
const length = toRate(delay) + rateRelativeDuration + toRate(minZero(endDelay));
|
const length = toRate(delay) + rateRelativeDuration + toRate(minZero(endDelay));
|
||||||
const endTime = previousStartTime + length;
|
const endTime = previousStartTime + length;
|
||||||
this.maxEndTime = Math.max(this.maxEndTime, endTime);
|
this.maxEndTime = Math.max(this.maxEndTime, endTime);
|
||||||
|
|
||||||
|
this.documentCurrentTime = Math.max(this.documentCurrentTime, documentCurrentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -69,6 +69,27 @@ function isAllAnimationEqual(animationsA, animationsB) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether or not the given list of animations has an iteration count of infinite.
|
||||||
|
*
|
||||||
|
* @param {Array} animations.
|
||||||
|
* @return {Boolean} true if there is an animation in the list of animations
|
||||||
|
* whose animation iteration count is infinite.
|
||||||
|
*/
|
||||||
|
function hasAnimationIterationCountInfinite(animations) {
|
||||||
|
return animations.some(({state}) => !state.iterationCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wether the animations are running at least one.
|
||||||
|
*
|
||||||
|
* @param {Array} animations.
|
||||||
|
* @return {Boolean} true: running
|
||||||
|
*/
|
||||||
|
function hasRunningAnimation(animations) {
|
||||||
|
return animations.some(({state}) => state.playState === "running");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the equality given states as effect timing.
|
* Check the equality given states as effect timing.
|
||||||
*
|
*
|
||||||
|
@ -88,5 +109,7 @@ function isTimingEffectEqual(stateA, stateB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.findOptimalTimeInterval = findOptimalTimeInterval;
|
exports.findOptimalTimeInterval = findOptimalTimeInterval;
|
||||||
|
exports.hasAnimationIterationCountInfinite = hasAnimationIterationCountInfinite;
|
||||||
|
exports.hasRunningAnimation = hasRunningAnimation;
|
||||||
exports.isAllAnimationEqual = isAllAnimationEqual;
|
exports.isAllAnimationEqual = isAllAnimationEqual;
|
||||||
exports.isTimingEffectEqual = isTimingEffectEqual;
|
exports.isTimingEffectEqual = isTimingEffectEqual;
|
||||||
|
|
|
@ -49,6 +49,7 @@ class Census extends Component {
|
||||||
|
|
||||||
return Tree({
|
return Tree({
|
||||||
autoExpandDepth: 0,
|
autoExpandDepth: 0,
|
||||||
|
preventNavigationOnArrowRight: false,
|
||||||
focused: census.focused,
|
focused: census.focused,
|
||||||
getParent: node => {
|
getParent: node => {
|
||||||
const parent = parentMap[node.id];
|
const parent = parentMap[node.id];
|
||||||
|
|
|
@ -134,6 +134,7 @@ class DominatorTree extends Component {
|
||||||
return Tree({
|
return Tree({
|
||||||
key: "dominator-tree-tree",
|
key: "dominator-tree-tree",
|
||||||
autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
|
autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
|
||||||
|
preventNavigationOnArrowRight: false,
|
||||||
focused: dominatorTree.focused,
|
focused: dominatorTree.focused,
|
||||||
getParent: node =>
|
getParent: node =>
|
||||||
node instanceof DominatorTreeLazyChildren
|
node instanceof DominatorTreeLazyChildren
|
||||||
|
|
|
@ -35,6 +35,7 @@ class Individuals extends Component {
|
||||||
return Tree({
|
return Tree({
|
||||||
key: "individuals-tree",
|
key: "individuals-tree",
|
||||||
autoExpandDepth: 0,
|
autoExpandDepth: 0,
|
||||||
|
preventNavigationOnArrowRight: false,
|
||||||
focused: individuals.focused,
|
focused: individuals.focused,
|
||||||
getParent: node => null,
|
getParent: node => null,
|
||||||
getChildren: node => [],
|
getChildren: node => [],
|
||||||
|
|
|
@ -189,6 +189,7 @@ class JITOptimizations extends Component {
|
||||||
|
|
||||||
return Tree({
|
return Tree({
|
||||||
autoExpandDepth,
|
autoExpandDepth,
|
||||||
|
preventNavigationOnArrowRight: false,
|
||||||
getParent: node => {
|
getParent: node => {
|
||||||
let site = getSite(node.id);
|
let site = getSite(node.id);
|
||||||
let parent;
|
let parent;
|
||||||
|
|
|
@ -163,6 +163,7 @@ class WaterfallTree extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return Tree({
|
return Tree({
|
||||||
|
preventNavigationOnArrowRight: false,
|
||||||
getRoots: this._getRoots,
|
getRoots: this._getRoots,
|
||||||
getParent: this._getParent,
|
getParent: this._getParent,
|
||||||
getChildren: this._getChildren,
|
getChildren: this._getChildren,
|
||||||
|
|
|
@ -198,6 +198,10 @@ class Tree extends Component {
|
||||||
// Handle when item is activated with a keyboard (using Space or Enter)
|
// Handle when item is activated with a keyboard (using Space or Enter)
|
||||||
onActivate: PropTypes.func,
|
onActivate: PropTypes.func,
|
||||||
|
|
||||||
|
// Indicates if pressing ArrowRight key should only expand expandable node
|
||||||
|
// or if the selection should also move to the next node.
|
||||||
|
preventNavigationOnArrowRight: PropTypes.bool,
|
||||||
|
|
||||||
// The depth to which we should automatically expand new items.
|
// The depth to which we should automatically expand new items.
|
||||||
autoExpandDepth: PropTypes.number,
|
autoExpandDepth: PropTypes.number,
|
||||||
|
|
||||||
|
@ -228,6 +232,7 @@ class Tree extends Component {
|
||||||
static get defaultProps() {
|
static get defaultProps() {
|
||||||
return {
|
return {
|
||||||
autoExpandDepth: AUTO_EXPAND_DEPTH,
|
autoExpandDepth: AUTO_EXPAND_DEPTH,
|
||||||
|
preventNavigationOnArrowRight: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,9 +507,10 @@ class Tree extends Component {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "ArrowRight":
|
case "ArrowRight":
|
||||||
if (!this.props.isExpanded(this.props.focused)) {
|
if (this.props.getChildren(this.props.focused).length &&
|
||||||
|
!this.props.isExpanded(this.props.focused)) {
|
||||||
this._onExpand(this.props.focused);
|
this._onExpand(this.props.focused);
|
||||||
} else {
|
} else if (!this.props.preventNavigationOnArrowRight) {
|
||||||
this._focusNextNode();
|
this._focusNextNode();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -16,12 +16,17 @@ Test keyboard navigation with the Tree component.
|
||||||
<pre id="test">
|
<pre id="test">
|
||||||
<script src="head.js" type="application/javascript"></script>
|
<script src="head.js" type="application/javascript"></script>
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
window.onload = async function () {
|
window.onload = async function () {
|
||||||
try {
|
try {
|
||||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||||
const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
|
const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
|
||||||
const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
|
const { Simulate } =
|
||||||
const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
|
browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
|
||||||
|
const Tree =
|
||||||
|
createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
|
||||||
|
|
||||||
function renderTree(props) {
|
function renderTree(props) {
|
||||||
const treeProps = Object.assign({},
|
const treeProps = Object.assign({},
|
||||||
|
@ -175,7 +180,8 @@ window.onload = async function () {
|
||||||
"M:false",
|
"M:false",
|
||||||
"-N:false",
|
"-N:false",
|
||||||
"--O:true",
|
"--O:true",
|
||||||
], "After the DOWN, O should still be focused and we shouldn't have overflowed past it.");
|
], "After the DOWN, O should still be focused " +
|
||||||
|
"and we shouldn't have overflowed past it.");
|
||||||
|
|
||||||
// LEFT --------------------------------------------------------------------
|
// LEFT --------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -246,7 +252,30 @@ window.onload = async function () {
|
||||||
"--O:false",
|
"--O:false",
|
||||||
], "After the RIGHT, E's children should be expanded again.");
|
], "After the RIGHT, E's children should be expanded again.");
|
||||||
|
|
||||||
info("Right to go to next item.");
|
info("Right on already expanded node.");
|
||||||
|
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
|
||||||
|
await forceRender(tree);
|
||||||
|
|
||||||
|
isRenderedTree(document.body.textContent, [
|
||||||
|
"A:false",
|
||||||
|
"-B:false",
|
||||||
|
"--E:true",
|
||||||
|
"---K:false",
|
||||||
|
"---L:false",
|
||||||
|
"--F:false",
|
||||||
|
"--G:false",
|
||||||
|
"-C:false",
|
||||||
|
"--H:false",
|
||||||
|
"--I:false",
|
||||||
|
"-D:false",
|
||||||
|
"--J:false",
|
||||||
|
"M:false",
|
||||||
|
"-N:false",
|
||||||
|
"--O:false",
|
||||||
|
], "After the RIGHT on already expanded node, E should remain focused.");
|
||||||
|
|
||||||
|
info("Right when preventNavigationOnArrowRight is unset to go to next item.");
|
||||||
|
renderTree({ focused: "E", preventNavigationOnArrowRight: false });
|
||||||
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
|
Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
|
||||||
await forceRender(tree);
|
await forceRender(tree);
|
||||||
|
|
||||||
|
@ -275,6 +304,8 @@ window.onload = async function () {
|
||||||
{ key: "ArrowDown", metaKey: true },
|
{ key: "ArrowDown", metaKey: true },
|
||||||
{ key: "ArrowDown", shiftKey: true },
|
{ key: "ArrowDown", shiftKey: true },
|
||||||
];
|
];
|
||||||
|
await forceRender(tree);
|
||||||
|
|
||||||
for (let key of keysWithModifier) {
|
for (let key of keysWithModifier) {
|
||||||
Simulate.keyDown(document.querySelector(".tree"), key);
|
Simulate.keyDown(document.querySelector(".tree"), key);
|
||||||
await forceRender(tree);
|
await forceRender(tree);
|
||||||
|
@ -297,7 +328,7 @@ window.onload = async function () {
|
||||||
"--O:false",
|
"--O:false",
|
||||||
], "After DOWN + (alt|ctrl|meta|shift), K should remain focused.");
|
], "After DOWN + (alt|ctrl|meta|shift), K should remain focused.");
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||||
} finally {
|
} finally {
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
|
|
|
@ -13,6 +13,11 @@
|
||||||
--fill-color-scriptanimation: var(--theme-graphs-green);
|
--fill-color-scriptanimation: var(--theme-graphs-green);
|
||||||
--graph-right-offset: 10px;
|
--graph-right-offset: 10px;
|
||||||
--keyframe-marker-shadow-color: #c4c4c4;
|
--keyframe-marker-shadow-color: #c4c4c4;
|
||||||
|
--pause-image: url(chrome://devtools/skin/images/pause.svg);
|
||||||
|
--progress-bar-color: #909090;
|
||||||
|
--resume-image: url(chrome://devtools/skin/images/play.svg);
|
||||||
|
--rewind-image: url(chrome://devtools/skin/images/rewind.svg);
|
||||||
|
--scrubber-color: #dd00a9;
|
||||||
--sidebar-width: 200px;
|
--sidebar-width: 200px;
|
||||||
--stroke-color-cssanimation: var(--theme-highlight-lightorange);
|
--stroke-color-cssanimation: var(--theme-highlight-lightorange);
|
||||||
--stroke-color-csstransition: var(--theme-highlight-bluegrey);
|
--stroke-color-csstransition: var(--theme-highlight-bluegrey);
|
||||||
|
@ -27,10 +32,15 @@
|
||||||
|
|
||||||
:root.theme-firebug {
|
:root.theme-firebug {
|
||||||
--command-pick-image: url(chrome://devtools/skin/images/firebug/command-pick.svg);
|
--command-pick-image: url(chrome://devtools/skin/images/firebug/command-pick.svg);
|
||||||
|
--pause-image: url(chrome://devtools/skin/images/firebug/pause.svg);
|
||||||
|
--resume-image: url(chrome://devtools/skin/images/firebug/play.svg);
|
||||||
|
--rewind-image: url(chrome://devtools/skin/images/firebug/rewind.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Root element of animation inspector */
|
/* Root element of animation inspector */
|
||||||
#animation-container {
|
#animation-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +52,44 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#animation-container .animation-container-splitter {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation Toolbar */
|
||||||
|
.animation-toolbar {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pause-resume-button::before {
|
||||||
|
background-image: var(--pause-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pause-resume-button.paused::before {
|
||||||
|
background-image: var(--resume-image);
|
||||||
|
}
|
||||||
|
|
||||||
|
select.playback-rate-selector.devtools-button {
|
||||||
|
background-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||||
|
background-position: calc(100% - 4px) center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
padding-right: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
select.playback-rate-selector.devtools-button:not(:empty):not(:disabled):not(.checked):hover {
|
||||||
|
background: none;
|
||||||
|
background-color: var(--toolbarbutton-background);
|
||||||
|
background-image: url("chrome://devtools/skin/images/dropmarker.svg");
|
||||||
|
background-position: calc(100% - 4px) center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border-color: var(--toolbarbutton-hover-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rewind-button::before {
|
||||||
|
background-image: var(--rewind-image);
|
||||||
|
}
|
||||||
|
|
||||||
/* Animation List Container */
|
/* Animation List Container */
|
||||||
.animation-list-container {
|
.animation-list-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -49,28 +97,69 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
-moz-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-list-container.active-scrubber {
|
||||||
|
cursor: col-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation List Header */
|
/* Animation List Header */
|
||||||
.animation-list-header {
|
.animation-list-header {
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: flex-end;
|
grid-template-columns: var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation Timeline Tick List */
|
/* Animation Timeline Tick List */
|
||||||
.animation-timeline-tick-list {
|
.animation-timeline-tick-list {
|
||||||
margin-right: var(--graph-right-offset);
|
grid-column: 2/3;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.animation-timeline-tick-item {
|
.animation-timeline-tick-item {
|
||||||
border-left: var(--tick-line-style);
|
border-left: var(--tick-line-style);
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Current Time Scrubber */
|
||||||
|
.current-time-scrubber-controller {
|
||||||
|
cursor: col-resize;
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time-scrubber {
|
||||||
|
cursor: col-resize;
|
||||||
|
height: 100vh;
|
||||||
|
margin-left: -6px;
|
||||||
|
position: absolute;
|
||||||
|
width: 12px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time-scrubber::before {
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-top: 5px solid var(--scrubber-color);
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-time-scrubber::after {
|
||||||
|
border-left: 1px solid var(--scrubber-color);
|
||||||
|
content: "";
|
||||||
|
height: 100%;
|
||||||
|
left: 5px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Animation List */
|
/* Animation List */
|
||||||
.animation-list {
|
.animation-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -241,11 +330,13 @@
|
||||||
|
|
||||||
/* Animation Detail */
|
/* Animation Detail */
|
||||||
.animation-detail-container {
|
.animation-detail-container {
|
||||||
|
background-color: var(--theme-body-background);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.animation-detail-header {
|
.animation-detail-header {
|
||||||
|
@ -266,21 +357,20 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animated Property List Header */
|
/* Animated Property List Header */
|
||||||
.animated-property-list-header {
|
.animated-property-list-header {
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: flex-end;
|
grid-template-columns: var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keyframes Progress Tick List */
|
/* Keyframes Progress Tick List */
|
||||||
.keyframes-progress-tick-list {
|
.keyframes-progress-tick-list {
|
||||||
margin-right: var(--graph-right-offset);
|
grid-column: 2 / 3;
|
||||||
position: absolute;
|
position: relative;
|
||||||
width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyframes-progress-tick-item {
|
.keyframes-progress-tick-item {
|
||||||
|
@ -296,6 +386,41 @@
|
||||||
border-right: var(--tick-line-style);
|
border-right: var(--tick-line-style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keyframes Progress Bar */
|
||||||
|
.keyframes-progress-bar-area {
|
||||||
|
background: none;
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
padding: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyframes-progress-bar {
|
||||||
|
height: 100vh;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyframes-progress-bar::before {
|
||||||
|
border-left: 5px solid transparent;
|
||||||
|
border-right: 5px solid transparent;
|
||||||
|
border-top: 5px solid var(--progress-bar-color);
|
||||||
|
content: "";
|
||||||
|
left: -5px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyframes-progress-bar::after {
|
||||||
|
border-left: 1px solid var(--progress-bar-color);
|
||||||
|
content: "";
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Animated Property List */
|
/* Animated Property List */
|
||||||
.animated-property-list {
|
.animated-property-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -1918,7 +1918,7 @@ DebuggerServer.ObjectActorPreviewers.Object = [
|
||||||
props.push("propertyName", "pseudoElement");
|
props.push("propertyName", "pseudoElement");
|
||||||
} else if (obj.class == "AnimationEvent") {
|
} else if (obj.class == "AnimationEvent") {
|
||||||
props.push("animationName", "pseudoElement");
|
props.push("animationName", "pseudoElement");
|
||||||
} else if (rawObj instanceof Ci.nsIDOMClipboardEvent) {
|
} else if (obj.class == "ClipboardEvent") {
|
||||||
props.push("clipboardData");
|
props.push("clipboardData");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,12 @@ Element::GetSVGAnimatedClass() const
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
||||||
{
|
{
|
||||||
|
if (aIID.Equals(NS_GET_IID(Element))) {
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
*aInstancePtr = this;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_ASSERTION(aInstancePtr,
|
NS_ASSERTION(aInstancePtr,
|
||||||
"QueryInterface requires a non-NULL destination!");
|
"QueryInterface requires a non-NULL destination!");
|
||||||
nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
|
nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
|
||||||
|
@ -1706,11 +1712,6 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||||
if (ShadowRoot* shadowRootParent = ShadowRoot::FromNode(parent)) {
|
if (ShadowRoot* shadowRootParent = ShadowRoot::FromNode(parent)) {
|
||||||
parent = shadowRootParent->GetHost();
|
parent = shadowRootParent->GetHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inStyleScope = parent->IsElementInStyleScope();
|
|
||||||
|
|
||||||
SetIsElementInStyleScope(inStyleScope);
|
|
||||||
SetIsElementInStyleScopeFlagOnShadowTree(inStyleScope);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This has to be here, rather than in nsGenericHTMLElement::BindToTree,
|
// This has to be here, rather than in nsGenericHTMLElement::BindToTree,
|
||||||
|
|
|
@ -2093,7 +2093,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
|
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
|
||||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||||
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
|
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
|
||||||
NS_INTERFACE_MAP_ENTRY(Element)
|
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||||
|
@ -2469,40 +2468,3 @@ FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
|
||||||
*aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
|
*aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)
|
|
||||||
{
|
|
||||||
if (aInStyleScope && IsElementInStyleScope()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsElement()) {
|
|
||||||
SetIsElementInStyleScope(aInStyleScope);
|
|
||||||
SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIContent* n = GetNextNode(this);
|
|
||||||
while (n) {
|
|
||||||
if (n->IsElementInStyleScope()) {
|
|
||||||
n = n->GetNextNonChildNode(this);
|
|
||||||
} else {
|
|
||||||
if (n->IsElement()) {
|
|
||||||
n->SetIsElementInStyleScope(aInStyleScope);
|
|
||||||
n->AsElement()->SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
|
|
||||||
}
|
|
||||||
n = n->GetNextNode(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
|
|
||||||
"on a non-Element is useless");
|
|
||||||
ShadowRoot* shadowRoot = GetShadowRoot();
|
|
||||||
if (shadowRoot) {
|
|
||||||
shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -164,14 +164,6 @@ public:
|
||||||
return Children()->Length();
|
return Children()->Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the IsElementInStyleScope flag on each element in the subtree rooted
|
|
||||||
* at this node, including any elements reachable through shadow trees.
|
|
||||||
*
|
|
||||||
* @param aInStyleScope The flag value to set.
|
|
||||||
*/
|
|
||||||
void SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* If there are listeners for DOMNodeInserted event, fires the event on all
|
* If there are listeners for DOMNodeInserted event, fires the event on all
|
||||||
|
@ -375,14 +367,6 @@ protected:
|
||||||
return static_cast<nsExtendedDOMSlots*>(GetExistingExtendedContentSlots());
|
return static_cast<nsExtendedDOMSlots*>(GetExistingExtendedContentSlots());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls SetIsElementInStyleScopeFlagOnSubtree for each shadow tree attached
|
|
||||||
* to this node, which is assumed to be an Element.
|
|
||||||
*
|
|
||||||
* @param aInStyleScope The IsElementInStyleScope flag value to set.
|
|
||||||
*/
|
|
||||||
void SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope);
|
|
||||||
|
|
||||||
friend class ::ContentUnbinder;
|
friend class ::ContentUnbinder;
|
||||||
/**
|
/**
|
||||||
* Array containing all attributes and children for this element
|
* Array containing all attributes and children for this element
|
||||||
|
|
|
@ -148,6 +148,7 @@ nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto
|
||||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsEmptyContentList, nsBaseContentList, mRoot)
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsEmptyContentList, nsBaseContentList, mRoot)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEmptyContentList)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEmptyContentList)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsIHTMLCollection)
|
||||||
NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
|
NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,7 +158,7 @@ NS_IMPL_RELEASE_INHERITED(nsEmptyContentList, nsBaseContentList)
|
||||||
JSObject*
|
JSObject*
|
||||||
nsEmptyContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
nsEmptyContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||||
{
|
{
|
||||||
return NodeListBinding::Wrap(cx, this, aGivenProto);
|
return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
|
|
@ -311,7 +311,6 @@ bool nsContentUtils::sAutoFocusEnabled = true;
|
||||||
#ifndef RELEASE_OR_BETA
|
#ifndef RELEASE_OR_BETA
|
||||||
bool nsContentUtils::sBypassCSSOMOriginCheck = false;
|
bool nsContentUtils::sBypassCSSOMOriginCheck = false;
|
||||||
#endif
|
#endif
|
||||||
bool nsContentUtils::sIsScopedStyleEnabled = false;
|
|
||||||
|
|
||||||
bool nsContentUtils::sIsBytecodeCacheEnabled = false;
|
bool nsContentUtils::sIsBytecodeCacheEnabled = false;
|
||||||
int32_t nsContentUtils::sBytecodeCacheStrategy = 0;
|
int32_t nsContentUtils::sBytecodeCacheStrategy = 0;
|
||||||
|
@ -716,9 +715,6 @@ nsContentUtils::Init()
|
||||||
sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
|
sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Preferences::AddBoolVarCache(&sIsScopedStyleEnabled,
|
|
||||||
"layout.css.scoped-style.enabled", false);
|
|
||||||
|
|
||||||
Preferences::AddBoolVarCache(&sLowerNetworkPriority,
|
Preferences::AddBoolVarCache(&sLowerNetworkPriority,
|
||||||
"privacy.trackingprotection.lower_network_priority", false);
|
"privacy.trackingprotection.lower_network_priority", false);
|
||||||
|
|
||||||
|
@ -4146,7 +4142,8 @@ nsresult nsContentUtils::FormatLocalizedString(
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
||||||
const char * classification)
|
const char * classification,
|
||||||
|
bool aFromPrivateWindow)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIScriptError> scriptError =
|
nsCOMPtr<nsIScriptError> scriptError =
|
||||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
||||||
|
@ -4156,7 +4153,8 @@ nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
||||||
if (console && NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(),
|
if (console && NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(),
|
||||||
EmptyString(), 0, 0,
|
EmptyString(), 0, 0,
|
||||||
nsIScriptError::errorFlag,
|
nsIScriptError::errorFlag,
|
||||||
classification))) {
|
classification,
|
||||||
|
aFromPrivateWindow))) {
|
||||||
console->LogMessage(scriptError);
|
console->LogMessage(scriptError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5834,7 +5832,8 @@ nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
|
||||||
}
|
}
|
||||||
msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
|
msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
|
||||||
|
|
||||||
LogSimpleConsoleError(msg, "DOM");
|
LogSimpleConsoleError(msg, "DOM",
|
||||||
|
!!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
|
|
@ -1008,7 +1008,8 @@ public:
|
||||||
* @param classification Name of the module reporting error
|
* @param classification Name of the module reporting error
|
||||||
*/
|
*/
|
||||||
static void LogSimpleConsoleError(const nsAString& aErrorText,
|
static void LogSimpleConsoleError(const nsAString& aErrorText,
|
||||||
const char * classification);
|
const char * classification,
|
||||||
|
bool aFromPrivateWindow);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report a non-localized error message to the error console.
|
* Report a non-localized error message to the error console.
|
||||||
|
@ -2445,14 +2446,6 @@ public:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the <style scoped> enabling pref is true.
|
|
||||||
*/
|
|
||||||
static bool IsScopedStylePrefEnabled()
|
|
||||||
{
|
|
||||||
return sIsScopedStyleEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fire mutation events for changes caused by parsing directly into a
|
* Fire mutation events for changes caused by parsing directly into a
|
||||||
* context node.
|
* context node.
|
||||||
|
@ -3468,7 +3461,6 @@ private:
|
||||||
#ifndef RELEASE_OR_BETA
|
#ifndef RELEASE_OR_BETA
|
||||||
static bool sBypassCSSOMOriginCheck;
|
static bool sBypassCSSOMOriginCheck;
|
||||||
#endif
|
#endif
|
||||||
static bool sIsScopedStyleEnabled;
|
|
||||||
static bool sIsBytecodeCacheEnabled;
|
static bool sIsBytecodeCacheEnabled;
|
||||||
static int32_t sBytecodeCacheStrategy;
|
static int32_t sBytecodeCacheStrategy;
|
||||||
static uint32_t sCookiesLifetimePolicy;
|
static uint32_t sCookiesLifetimePolicy;
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include "mozilla/dom/BindingDeclarations.h"
|
#include "mozilla/dom/BindingDeclarations.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
#include "mozilla/dom/FramingChecker.h"
|
#include "mozilla/dom/FramingChecker.h"
|
||||||
|
#include "mozilla/dom/HTMLSharedElement.h"
|
||||||
#include "nsGenericHTMLElement.h"
|
#include "nsGenericHTMLElement.h"
|
||||||
#include "mozilla/dom/CDATASection.h"
|
#include "mozilla/dom/CDATASection.h"
|
||||||
#include "mozilla/dom/ProcessingInstruction.h"
|
#include "mozilla/dom/ProcessingInstruction.h"
|
||||||
|
@ -1500,8 +1501,8 @@ nsIDocument::nsIDocument()
|
||||||
mDelayFrameLoaderInitialization(false),
|
mDelayFrameLoaderInitialization(false),
|
||||||
mSynchronousDOMContentLoaded(false),
|
mSynchronousDOMContentLoaded(false),
|
||||||
mMaybeServiceWorkerControlled(false),
|
mMaybeServiceWorkerControlled(false),
|
||||||
mIsScopedStyleEnabled(eScopedStyle_Unknown),
|
|
||||||
mPendingFullscreenRequests(0),
|
mPendingFullscreenRequests(0),
|
||||||
|
mXMLDeclarationBits(0),
|
||||||
mCompatMode(eCompatibility_FullStandards),
|
mCompatMode(eCompatibility_FullStandards),
|
||||||
mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
|
mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
|
||||||
mStyleBackendType(StyleBackendType::None),
|
mStyleBackendType(StyleBackendType::None),
|
||||||
|
@ -1557,7 +1558,6 @@ nsDocument::nsDocument(const char* aContentType)
|
||||||
: nsIDocument()
|
: nsIDocument()
|
||||||
, mParserAborted(false)
|
, mParserAborted(false)
|
||||||
, mReportedUseCounters(false)
|
, mReportedUseCounters(false)
|
||||||
, mXMLDeclarationBits(0)
|
|
||||||
, mOnloadBlockCount(0)
|
, mOnloadBlockCount(0)
|
||||||
, mAsyncOnloadBlockCount(0)
|
, mAsyncOnloadBlockCount(0)
|
||||||
, mValidWidth(false)
|
, mValidWidth(false)
|
||||||
|
@ -1973,6 +1973,13 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
|
||||||
|
|
||||||
// Traverse all our nsCOMArrays.
|
// Traverse all our nsCOMArrays.
|
||||||
|
@ -2063,6 +2070,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
|
||||||
|
@ -6316,6 +6330,12 @@ nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HTMLSharedElement*
|
||||||
|
nsIDocument::GetHead()
|
||||||
|
{
|
||||||
|
return static_cast<HTMLSharedElement*>(GetHeadElement());
|
||||||
|
}
|
||||||
|
|
||||||
Element*
|
Element*
|
||||||
nsIDocument::GetTitleElement()
|
nsIDocument::GetTitleElement()
|
||||||
{
|
{
|
||||||
|
@ -6800,6 +6820,87 @@ nsIDocument::SetDir(const nsAString& aDirection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Images()
|
||||||
|
{
|
||||||
|
if (!mImages) {
|
||||||
|
mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img, nsGkAtoms::img);
|
||||||
|
}
|
||||||
|
return mImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Embeds()
|
||||||
|
{
|
||||||
|
if (!mEmbeds) {
|
||||||
|
mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed, nsGkAtoms::embed);
|
||||||
|
}
|
||||||
|
return mEmbeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
MatchLinks(Element* aElement, int32_t aNamespaceID,
|
||||||
|
nsAtom* aAtom, void* aData)
|
||||||
|
{
|
||||||
|
return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
|
||||||
|
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Links()
|
||||||
|
{
|
||||||
|
if (!mLinks) {
|
||||||
|
mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
return mLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Forms()
|
||||||
|
{
|
||||||
|
if (!mForms) {
|
||||||
|
// Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
|
||||||
|
mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, nsGkAtoms::form);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mForms;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Scripts()
|
||||||
|
{
|
||||||
|
if (!mScripts) {
|
||||||
|
mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script, nsGkAtoms::script);
|
||||||
|
}
|
||||||
|
return mScripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Applets()
|
||||||
|
{
|
||||||
|
if (!mApplets) {
|
||||||
|
mApplets = new nsEmptyContentList(this);
|
||||||
|
}
|
||||||
|
return mApplets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
MatchAnchors(Element* aElement, int32_t aNamespaceID,
|
||||||
|
nsAtom* aAtom, void* aData)
|
||||||
|
{
|
||||||
|
return aElement->IsHTMLElement(nsGkAtoms::a) &&
|
||||||
|
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIHTMLCollection*
|
||||||
|
nsIDocument::Anchors()
|
||||||
|
{
|
||||||
|
if (!mAnchors) {
|
||||||
|
mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
return mAnchors;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool
|
bool
|
||||||
nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
|
nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
|
||||||
|
@ -7626,9 +7727,9 @@ nsIDocument::FlushExternalResources(FlushType aType)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocument::SetXMLDeclaration(const char16_t *aVersion,
|
nsIDocument::SetXMLDeclaration(const char16_t* aVersion,
|
||||||
const char16_t *aEncoding,
|
const char16_t* aEncoding,
|
||||||
const int32_t aStandalone)
|
const int32_t aStandalone)
|
||||||
{
|
{
|
||||||
if (!aVersion || *aVersion == '\0') {
|
if (!aVersion || *aVersion == '\0') {
|
||||||
mXMLDeclarationBits = 0;
|
mXMLDeclarationBits = 0;
|
||||||
|
@ -7651,8 +7752,9 @@ nsDocument::SetXMLDeclaration(const char16_t *aVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
|
nsIDocument::GetXMLDeclaration(nsAString& aVersion,
|
||||||
nsAString& aStandalone)
|
nsAString& aEncoding,
|
||||||
|
nsAString& aStandalone)
|
||||||
{
|
{
|
||||||
aVersion.Truncate();
|
aVersion.Truncate();
|
||||||
aEncoding.Truncate();
|
aEncoding.Truncate();
|
||||||
|
@ -13238,18 +13340,6 @@ nsIDocument::IsThirdParty()
|
||||||
return mIsThirdParty.value();
|
return mIsThirdParty.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
nsIDocument::IsScopedStyleEnabled()
|
|
||||||
{
|
|
||||||
if (mIsScopedStyleEnabled == eScopedStyle_Unknown) {
|
|
||||||
mIsScopedStyleEnabled = nsContentUtils::IsChromeDoc(this) ||
|
|
||||||
nsContentUtils::IsScopedStylePrefEnabled()
|
|
||||||
? eScopedStyle_Enabled
|
|
||||||
: eScopedStyle_Disabled;
|
|
||||||
}
|
|
||||||
return mIsScopedStyleEnabled == eScopedStyle_Enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
nsIDocument::ClearStaleServoData()
|
nsIDocument::ClearStaleServoData()
|
||||||
{
|
{
|
||||||
|
|
|
@ -199,13 +199,6 @@ public:
|
||||||
virtual void BeginLoad() override;
|
virtual void BeginLoad() override;
|
||||||
virtual void EndLoad() override;
|
virtual void EndLoad() override;
|
||||||
|
|
||||||
virtual void SetXMLDeclaration(const char16_t *aVersion,
|
|
||||||
const char16_t *aEncoding,
|
|
||||||
const int32_t aStandalone) override;
|
|
||||||
virtual void GetXMLDeclaration(nsAString& aVersion,
|
|
||||||
nsAString& aEncoding,
|
|
||||||
nsAString& Standalone) override;
|
|
||||||
|
|
||||||
// nsIRadioGroupContainer
|
// nsIRadioGroupContainer
|
||||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
||||||
nsIRadioVisitor* aVisitor,
|
nsIRadioVisitor* aVisitor,
|
||||||
|
@ -404,8 +397,6 @@ public:
|
||||||
// that we only report them once for the document.
|
// that we only report them once for the document.
|
||||||
bool mReportedUseCounters:1;
|
bool mReportedUseCounters:1;
|
||||||
|
|
||||||
uint8_t mXMLDeclarationBits;
|
|
||||||
|
|
||||||
// The application cache that this document is associated with, if
|
// The application cache that this document is associated with, if
|
||||||
// any. This can change during the lifetime of the document.
|
// any. This can change during the lifetime of the document.
|
||||||
nsCOMPtr<nsIApplicationCache> mApplicationCache;
|
nsCOMPtr<nsIApplicationCache> mApplicationCache;
|
||||||
|
|
|
@ -515,7 +515,8 @@ GetParamsForMessage(JSContext* aCx,
|
||||||
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
||||||
error->Init(NS_LITERAL_STRING("Sending message that cannot be cloned. Are you trying to send an XPCOM object?"),
|
error->Init(NS_LITERAL_STRING("Sending message that cannot be cloned. Are you trying to send an XPCOM object?"),
|
||||||
filename, EmptyString(), lineno, column,
|
filename, EmptyString(), lineno, column,
|
||||||
nsIScriptError::warningFlag, "chrome javascript");
|
nsIScriptError::warningFlag, "chrome javascript",
|
||||||
|
false /* from private window */);
|
||||||
console->LogMessage(error);
|
console->LogMessage(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,7 +1112,8 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
||||||
if (console) {
|
if (console) {
|
||||||
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
||||||
error->Init(msg, EmptyString(), EmptyString(),
|
error->Init(msg, EmptyString(), EmptyString(),
|
||||||
0, 0, nsIScriptError::warningFlag, "chrome javascript");
|
0, 0, nsIScriptError::warningFlag, "chrome javascript",
|
||||||
|
false /* from private window */);
|
||||||
console->LogMessage(error);
|
console->LogMessage(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5826,7 +5826,8 @@ nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> a
|
||||||
R"(origin "%s" from a system principal scope with mismatched )"
|
R"(origin "%s" from a system principal scope with mismatched )"
|
||||||
R"(origin "%s".)",
|
R"(origin "%s".)",
|
||||||
targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
|
targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
|
||||||
"DOM");
|
"DOM",
|
||||||
|
!!principal->PrivateBrowsingId());
|
||||||
|
|
||||||
attrs = principal->OriginAttributesRef();
|
attrs = principal->OriginAttributesRef();
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ class FrameRequestCallback;
|
||||||
struct FullscreenRequest;
|
struct FullscreenRequest;
|
||||||
class ImageTracker;
|
class ImageTracker;
|
||||||
class HTMLBodyElement;
|
class HTMLBodyElement;
|
||||||
|
class HTMLSharedElement;
|
||||||
class HTMLImageElement;
|
class HTMLImageElement;
|
||||||
struct LifecycleCallbackArgs;
|
struct LifecycleCallbackArgs;
|
||||||
class Link;
|
class Link;
|
||||||
|
@ -1598,6 +1599,8 @@ public:
|
||||||
nsGenericHTMLElement* GetBody();
|
nsGenericHTMLElement* GetBody();
|
||||||
// Set the "body" in the sense of document.body.
|
// Set the "body" in the sense of document.body.
|
||||||
void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
|
void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
|
||||||
|
// Get the "head" element in the sense of document.head.
|
||||||
|
mozilla::dom::HTMLSharedElement* GetHead();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessors to the collection of stylesheets owned by this document.
|
* Accessors to the collection of stylesheets owned by this document.
|
||||||
|
@ -2134,12 +2137,12 @@ public:
|
||||||
* was no standalone parameter in the declaration, that it was given as no,
|
* was no standalone parameter in the declaration, that it was given as no,
|
||||||
* or that it was given as yes.
|
* or that it was given as yes.
|
||||||
*/
|
*/
|
||||||
virtual void SetXMLDeclaration(const char16_t *aVersion,
|
void SetXMLDeclaration(const char16_t* aVersion,
|
||||||
const char16_t *aEncoding,
|
const char16_t* aEncoding,
|
||||||
const int32_t aStandalone) = 0;
|
const int32_t aStandalone);
|
||||||
virtual void GetXMLDeclaration(nsAString& aVersion,
|
void GetXMLDeclaration(nsAString& aVersion,
|
||||||
nsAString& aEncoding,
|
nsAString& aEncoding,
|
||||||
nsAString& Standalone) = 0;
|
nsAString& Standalone);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this is what HTML 5 calls an "HTML document" (for example
|
* Returns true if this is what HTML 5 calls an "HTML document" (for example
|
||||||
|
@ -3280,6 +3283,15 @@ public:
|
||||||
void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
|
void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
|
||||||
void GetDir(nsAString& aDirection) const;
|
void GetDir(nsAString& aDirection) const;
|
||||||
void SetDir(const nsAString& aDirection);
|
void SetDir(const nsAString& aDirection);
|
||||||
|
nsIHTMLCollection* Images();
|
||||||
|
nsIHTMLCollection* Embeds();
|
||||||
|
nsIHTMLCollection* Plugins()
|
||||||
|
{
|
||||||
|
return Embeds();
|
||||||
|
}
|
||||||
|
nsIHTMLCollection* Links();
|
||||||
|
nsIHTMLCollection* Forms();
|
||||||
|
nsIHTMLCollection* Scripts();
|
||||||
already_AddRefed<nsContentList> GetElementsByName(const nsAString& aName)
|
already_AddRefed<nsContentList> GetElementsByName(const nsAString& aName)
|
||||||
{
|
{
|
||||||
return GetFuncStringContentList<nsCachableElementsByNameNodeList>(this,
|
return GetFuncStringContentList<nsCachableElementsByNameNodeList>(this,
|
||||||
|
@ -3294,6 +3306,8 @@ public:
|
||||||
}
|
}
|
||||||
Element* GetActiveElement();
|
Element* GetActiveElement();
|
||||||
bool HasFocus(mozilla::ErrorResult& rv) const;
|
bool HasFocus(mozilla::ErrorResult& rv) const;
|
||||||
|
nsIHTMLCollection* Applets();
|
||||||
|
nsIHTMLCollection* Anchors();
|
||||||
mozilla::TimeStamp LastFocusTime() const;
|
mozilla::TimeStamp LastFocusTime() const;
|
||||||
void SetLastFocusTime(const mozilla::TimeStamp& aFocusTime);
|
void SetLastFocusTime(const mozilla::TimeStamp& aFocusTime);
|
||||||
// Event handlers are all on nsINode already
|
// Event handlers are all on nsINode already
|
||||||
|
@ -3871,6 +3885,15 @@ protected:
|
||||||
// Our cached .children collection
|
// Our cached .children collection
|
||||||
nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
|
nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
|
||||||
|
|
||||||
|
// Various DOM lists
|
||||||
|
RefPtr<nsContentList> mImages;
|
||||||
|
RefPtr<nsContentList> mEmbeds;
|
||||||
|
RefPtr<nsContentList> mLinks;
|
||||||
|
RefPtr<nsContentList> mForms;
|
||||||
|
RefPtr<nsContentList> mScripts;
|
||||||
|
nsCOMPtr<nsIHTMLCollection> mApplets;
|
||||||
|
RefPtr<nsContentList> mAnchors;
|
||||||
|
|
||||||
// container for per-context fonts (downloadable, SVG, etc.)
|
// container for per-context fonts (downloadable, SVG, etc.)
|
||||||
RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
|
RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
|
||||||
|
|
||||||
|
@ -4105,12 +4128,10 @@ protected:
|
||||||
// Used to prevent multiple requests to ServiceWorkerManager.
|
// Used to prevent multiple requests to ServiceWorkerManager.
|
||||||
bool mMaybeServiceWorkerControlled: 1;
|
bool mMaybeServiceWorkerControlled: 1;
|
||||||
|
|
||||||
// Whether <style scoped> support is enabled in this document.
|
|
||||||
enum { eScopedStyle_Unknown, eScopedStyle_Disabled, eScopedStyle_Enabled };
|
|
||||||
unsigned int mIsScopedStyleEnabled : 2;
|
|
||||||
|
|
||||||
uint8_t mPendingFullscreenRequests;
|
uint8_t mPendingFullscreenRequests;
|
||||||
|
|
||||||
|
uint8_t mXMLDeclarationBits;
|
||||||
|
|
||||||
// Compatibility mode
|
// Compatibility mode
|
||||||
nsCompatibility mCompatMode;
|
nsCompatibility mCompatMode;
|
||||||
|
|
||||||
|
|
|
@ -1662,11 +1662,6 @@ private:
|
||||||
NodeHasTextNodeDirectionalityMap,
|
NodeHasTextNodeDirectionalityMap,
|
||||||
// Set if a node in the node's parent chain has dir=auto.
|
// Set if a node in the node's parent chain has dir=auto.
|
||||||
NodeAncestorHasDirAuto,
|
NodeAncestorHasDirAuto,
|
||||||
// Set if the element is in the scope of a scoped style sheet; this flag is
|
|
||||||
// only accurate for elements bound to a document
|
|
||||||
ElementIsInStyleScope,
|
|
||||||
// Set if the element is a scoped style sheet root
|
|
||||||
ElementIsScopedStyleRoot,
|
|
||||||
// Set if the node is handling a click.
|
// Set if the node is handling a click.
|
||||||
NodeHandlingClick,
|
NodeHandlingClick,
|
||||||
// Set if the node has had :hover selectors matched against it
|
// Set if the node has had :hover selectors matched against it
|
||||||
|
@ -1795,23 +1790,6 @@ public:
|
||||||
// Implemented in nsIContentInlines.h.
|
// Implemented in nsIContentInlines.h.
|
||||||
inline bool NodeOrAncestorHasDirAuto() const;
|
inline bool NodeOrAncestorHasDirAuto() const;
|
||||||
|
|
||||||
void SetIsElementInStyleScope(bool aValue) {
|
|
||||||
MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node");
|
|
||||||
SetBoolFlag(ElementIsInStyleScope, aValue);
|
|
||||||
}
|
|
||||||
void SetIsElementInStyleScope() {
|
|
||||||
MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node");
|
|
||||||
SetBoolFlag(ElementIsInStyleScope);
|
|
||||||
}
|
|
||||||
void ClearIsElementInStyleScope() {
|
|
||||||
MOZ_ASSERT(IsElement(), "ClearIsInStyleScope on a non-Element node");
|
|
||||||
ClearBoolFlag(ElementIsInStyleScope);
|
|
||||||
}
|
|
||||||
bool IsElementInStyleScope() const { return GetBoolFlag(ElementIsInStyleScope); }
|
|
||||||
|
|
||||||
void SetIsScopedStyleRoot() { SetBoolFlag(ElementIsScopedStyleRoot); }
|
|
||||||
void ClearIsScopedStyleRoot() { ClearBoolFlag(ElementIsScopedStyleRoot); }
|
|
||||||
bool IsScopedStyleRoot() { return GetBoolFlag(ElementIsScopedStyleRoot); }
|
|
||||||
bool HasRelevantHoverRules() const { return GetBoolFlag(NodeHasRelevantHoverRules); }
|
bool HasRelevantHoverRules() const { return GetBoolFlag(NodeHasRelevantHoverRules); }
|
||||||
void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); }
|
void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); }
|
||||||
void SetParserHasNotified() { SetBoolFlag(ParserHasNotified); };
|
void SetParserHasNotified() { SetBoolFlag(ParserHasNotified); };
|
||||||
|
|
|
@ -330,56 +330,6 @@ nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
|
||||||
&alternate, aForceUpdate);
|
&alternate, aForceUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
IsScopedStyleElement(nsIContent* aContent)
|
|
||||||
{
|
|
||||||
// This is quicker than, say, QIing aContent to nsStyleLinkElement
|
|
||||||
// and then calling its virtual GetStyleSheetInfo method to find out
|
|
||||||
// if it is scoped.
|
|
||||||
return (aContent->IsHTMLElement(nsGkAtoms::style) ||
|
|
||||||
aContent->IsSVGElement(nsGkAtoms::style)) &&
|
|
||||||
aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scoped) &&
|
|
||||||
aContent->OwnerDoc()->IsScopedStyleEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
HasScopedStyleSheetChild(nsIContent* aContent)
|
|
||||||
{
|
|
||||||
for (nsIContent* n = aContent->GetFirstChild(); n; n = n->GetNextSibling()) {
|
|
||||||
if (IsScopedStyleElement(n)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when aElement has had a <style scoped> child removed.
|
|
||||||
static void
|
|
||||||
UpdateIsElementInStyleScopeFlagOnSubtree(Element* aElement)
|
|
||||||
{
|
|
||||||
NS_ASSERTION(aElement->IsElementInStyleScope(),
|
|
||||||
"only call UpdateIsElementInStyleScopeFlagOnSubtree on a "
|
|
||||||
"subtree that has IsElementInStyleScope boolean flag set");
|
|
||||||
|
|
||||||
if (HasScopedStyleSheetChild(aElement)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
aElement->ClearIsElementInStyleScope();
|
|
||||||
|
|
||||||
nsIContent* n = aElement->GetNextNode(aElement);
|
|
||||||
while (n) {
|
|
||||||
if (HasScopedStyleSheetChild(n)) {
|
|
||||||
n = n->GetNextNonChildNode(aElement);
|
|
||||||
} else {
|
|
||||||
if (n->IsElement()) {
|
|
||||||
n->ClearIsElementInStyleScope();
|
|
||||||
}
|
|
||||||
n = n->GetNextNode(aElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
ShadowRoot* aOldShadowRoot,
|
ShadowRoot* aOldShadowRoot,
|
||||||
|
@ -409,20 +359,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXXheycam ServoStyleSheets do not support <style scoped>.
|
|
||||||
Element* oldScopeElement = nullptr;
|
|
||||||
if (mStyleSheet) {
|
|
||||||
if (mStyleSheet->IsServo()) {
|
|
||||||
// XXXheycam ServoStyleSheets don't support <style scoped>.
|
|
||||||
} else {
|
|
||||||
#ifdef MOZ_OLD_STYLE
|
|
||||||
oldScopeElement = mStyleSheet->AsGecko()->GetScopeElement();
|
|
||||||
#else
|
|
||||||
MOZ_CRASH("old style system disabled");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
|
if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
|
||||||
MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
|
MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
|
||||||
"ShadowRoot content is never in document, thus "
|
"ShadowRoot content is never in document, thus "
|
||||||
|
@ -442,9 +378,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsStyleLinkElement::SetStyleSheet(nullptr);
|
nsStyleLinkElement::SetStyleSheet(nullptr);
|
||||||
if (oldScopeElement) {
|
|
||||||
UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When static documents are created, stylesheets are cloned manually.
|
// When static documents are created, stylesheets are cloned manually.
|
||||||
|
@ -492,21 +425,14 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsAutoString title, type, media;
|
nsAutoString title, type, media;
|
||||||
bool isScoped;
|
|
||||||
bool isAlternate;
|
bool isAlternate;
|
||||||
|
|
||||||
GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
|
GetStyleSheetInfo(title, type, media, &isAlternate);
|
||||||
|
|
||||||
if (!type.LowerCaseEqualsLiteral("text/css")) {
|
if (!type.LowerCaseEqualsLiteral("text/css")) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Element* scopeElement = isScoped ? thisContent->GetParentElement() : nullptr;
|
|
||||||
if (scopeElement) {
|
|
||||||
NS_ASSERTION(isInline, "non-inline style must not have scope element");
|
|
||||||
scopeElement->SetIsElementInStyleScopeFlagOnSubtree(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool doneLoading = false;
|
bool doneLoading = false;
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
|
@ -538,7 +464,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
// Parse the style sheet.
|
// Parse the style sheet.
|
||||||
rv = doc->CSSLoader()->
|
rv = doc->CSSLoader()->
|
||||||
LoadInlineStyle(thisContent, text, triggeringPrincipal, mLineNumber,
|
LoadInlineStyle(thisContent, text, triggeringPrincipal, mLineNumber,
|
||||||
title, media, referrerPolicy, scopeElement,
|
title, media, referrerPolicy,
|
||||||
aObserver, &doneLoading, &isAlternate);
|
aObserver, &doneLoading, &isAlternate);
|
||||||
} else {
|
} else {
|
||||||
nsAutoString integrity;
|
nsAutoString integrity;
|
||||||
|
@ -577,60 +503,3 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsStyleLinkElement::UpdateStyleSheetScopedness(bool aIsNowScoped)
|
|
||||||
{
|
|
||||||
if (!mStyleSheet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStyleSheet->IsServo()) {
|
|
||||||
// XXXheycam ServoStyleSheets don't support <style scoped>.
|
|
||||||
NS_ERROR("stylo: ServoStyleSheets don't support <style scoped>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MOZ_OLD_STYLE
|
|
||||||
CSSStyleSheet* sheet = mStyleSheet->AsGecko();
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
|
|
||||||
|
|
||||||
Element* oldScopeElement = sheet->GetScopeElement();
|
|
||||||
Element* newScopeElement = aIsNowScoped ?
|
|
||||||
thisContent->GetParentElement() :
|
|
||||||
nullptr;
|
|
||||||
|
|
||||||
if (oldScopeElement == newScopeElement) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsIDocument* document = thisContent->GetOwnerDocument();
|
|
||||||
|
|
||||||
if (thisContent->IsInShadowTree()) {
|
|
||||||
ShadowRoot* containingShadow = thisContent->GetContainingShadow();
|
|
||||||
containingShadow->RemoveSheet(mStyleSheet);
|
|
||||||
|
|
||||||
sheet->SetScopeElement(newScopeElement);
|
|
||||||
|
|
||||||
containingShadow->InsertSheet(mStyleSheet, thisContent);
|
|
||||||
} else {
|
|
||||||
document->BeginUpdate(UPDATE_STYLE);
|
|
||||||
document->RemoveStyleSheet(mStyleSheet);
|
|
||||||
|
|
||||||
sheet->SetScopeElement(newScopeElement);
|
|
||||||
|
|
||||||
document->AddStyleSheet(mStyleSheet);
|
|
||||||
document->EndUpdate(UPDATE_STYLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldScopeElement) {
|
|
||||||
UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
|
|
||||||
}
|
|
||||||
if (newScopeElement) {
|
|
||||||
newScopeElement->SetIsElementInStyleScopeFlagOnSubtree(true);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
MOZ_CRASH("old style system disabled");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
|
@ -91,13 +91,10 @@ protected:
|
||||||
mozilla::dom::ShadowRoot *aOldShadowRoot,
|
mozilla::dom::ShadowRoot *aOldShadowRoot,
|
||||||
bool aForceUpdate = false);
|
bool aForceUpdate = false);
|
||||||
|
|
||||||
void UpdateStyleSheetScopedness(bool aIsNowScoped);
|
|
||||||
|
|
||||||
virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) = 0;
|
virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) = 0;
|
||||||
virtual void GetStyleSheetInfo(nsAString& aTitle,
|
virtual void GetStyleSheetInfo(nsAString& aTitle,
|
||||||
nsAString& aType,
|
nsAString& aType,
|
||||||
nsAString& aMedia,
|
nsAString& aMedia,
|
||||||
bool* aIsScoped,
|
|
||||||
bool* aIsAlternate) = 0;
|
bool* aIsAlternate) = 0;
|
||||||
|
|
||||||
virtual mozilla::CORSMode GetCORSMode() const
|
virtual mozilla::CORSMode GetCORSMode() const
|
||||||
|
|
|
@ -17296,7 +17296,7 @@ class GlobalGenRoots():
|
||||||
|
|
||||||
def fieldSizeAssert(amount, jitInfoField, message):
|
def fieldSizeAssert(amount, jitInfoField, message):
|
||||||
maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
|
maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
|
||||||
return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n"
|
return CGGeneric(define="static_assert(%s < %s, \"%s\");\n\n"
|
||||||
% (amount, maxFieldValue, message))
|
% (amount, maxFieldValue, message))
|
||||||
|
|
||||||
idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
|
idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
|
||||||
|
@ -17308,7 +17308,8 @@ class GlobalGenRoots():
|
||||||
idEnum = CGWrapper(idEnum, post='\n')
|
idEnum = CGWrapper(idEnum, post='\n')
|
||||||
|
|
||||||
curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
|
curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
|
||||||
CGGeneric(declare='#include "jsfriendapi.h"\n\n'),
|
CGGeneric(define='#include "jsfriendapi.h"\n\n'),
|
||||||
|
CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'),
|
||||||
idEnum])
|
idEnum])
|
||||||
|
|
||||||
# Let things know the maximum length of the prototype chain.
|
# Let things know the maximum length of the prototype chain.
|
||||||
|
|
|
@ -95,7 +95,8 @@ interface nsIScriptError : nsIConsoleMessage
|
||||||
in uint32_t lineNumber,
|
in uint32_t lineNumber,
|
||||||
in uint32_t columnNumber,
|
in uint32_t columnNumber,
|
||||||
in uint32_t flags,
|
in uint32_t flags,
|
||||||
in string category);
|
in string category,
|
||||||
|
[optional] in bool fromPrivateWindow);
|
||||||
|
|
||||||
/* This should be called instead of nsIScriptError.init to
|
/* This should be called instead of nsIScriptError.init to
|
||||||
* initialize with a window id. The window id should be for the
|
* initialize with a window id. The window id should be for the
|
||||||
|
|
|
@ -181,22 +181,6 @@ nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsScriptErrorBase::Init(const nsAString& message,
|
|
||||||
const nsAString& sourceName,
|
|
||||||
const nsAString& sourceLine,
|
|
||||||
uint32_t lineNumber,
|
|
||||||
uint32_t columnNumber,
|
|
||||||
uint32_t flags,
|
|
||||||
const char* category)
|
|
||||||
{
|
|
||||||
return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
|
|
||||||
columnNumber, flags,
|
|
||||||
category ? nsDependentCString(category)
|
|
||||||
: EmptyCString(),
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
AssignSourceNameHelper(nsString& aSourceNameDest, const nsAString& aSourceNameSrc)
|
AssignSourceNameHelper(nsString& aSourceNameDest, const nsAString& aSourceNameSrc)
|
||||||
{
|
{
|
||||||
|
@ -227,6 +211,26 @@ AssignSourceNameHelper(nsIURI* aSourceURI, nsString& aSourceNameDest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsScriptErrorBase::Init(const nsAString& message,
|
||||||
|
const nsAString& sourceName,
|
||||||
|
const nsAString& sourceLine,
|
||||||
|
uint32_t lineNumber,
|
||||||
|
uint32_t columnNumber,
|
||||||
|
uint32_t flags,
|
||||||
|
const char* category,
|
||||||
|
bool fromPrivateWindow)
|
||||||
|
{
|
||||||
|
InitializationHelper(message, sourceLine, lineNumber, columnNumber, flags,
|
||||||
|
category ? nsDependentCString(category)
|
||||||
|
: EmptyCString(),
|
||||||
|
0 /* inner Window ID */);
|
||||||
|
AssignSourceNameHelper(mSourceName, sourceName);
|
||||||
|
|
||||||
|
mIsFromPrivateWindow = fromPrivateWindow;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsScriptErrorBase::InitializationHelper(const nsAString& message,
|
nsScriptErrorBase::InitializationHelper(const nsAString& message,
|
||||||
const nsAString& sourceLine,
|
const nsAString& sourceLine,
|
||||||
|
|
|
@ -96,14 +96,6 @@ public:
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
|
||||||
|
|
||||||
NS_IMETHOD Init(const nsAString& message,
|
|
||||||
const nsAString& sourceName,
|
|
||||||
const nsAString& sourceLine,
|
|
||||||
uint32_t lineNumber,
|
|
||||||
uint32_t columnNumber,
|
|
||||||
uint32_t flags,
|
|
||||||
const char* category) override;
|
|
||||||
|
|
||||||
NS_IMETHOD GetStack(JS::MutableHandleValue) override;
|
NS_IMETHOD GetStack(JS::MutableHandleValue) override;
|
||||||
NS_IMETHOD ToString(nsACString& aResult) override;
|
NS_IMETHOD ToString(nsACString& aResult) override;
|
||||||
|
|
||||||
|
|
|
@ -73,18 +73,6 @@ nsScriptErrorWithStack::~nsScriptErrorWithStack() {
|
||||||
mozilla::DropJSObjects(this);
|
mozilla::DropJSObjects(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsScriptErrorWithStack::Init(const nsAString& message,
|
|
||||||
const nsAString& sourceName,
|
|
||||||
const nsAString& sourceLine,
|
|
||||||
uint32_t lineNumber,
|
|
||||||
uint32_t columnNumber,
|
|
||||||
uint32_t flags,
|
|
||||||
const char* category)
|
|
||||||
{
|
|
||||||
MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID");
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
|
nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
|
||||||
aStack.setObjectOrNull(mStack);
|
aStack.setObjectOrNull(mStack);
|
||||||
|
|
|
@ -213,9 +213,9 @@ public:
|
||||||
int8_t CachedWritableByte();
|
int8_t CachedWritableByte();
|
||||||
void SetCachedWritableByte(int8_t);
|
void SetCachedWritableByte(int8_t);
|
||||||
int8_t SideEffectFreeByte();
|
int8_t SideEffectFreeByte();
|
||||||
int8_t SetSideEffectFreeByte(int8_t);
|
void SetSideEffectFreeByte(int8_t);
|
||||||
int8_t DomDependentByte();
|
int8_t DomDependentByte();
|
||||||
int8_t SetDomDependentByte(int8_t);
|
void SetDomDependentByte(int8_t);
|
||||||
int8_t ConstantByte();
|
int8_t ConstantByte();
|
||||||
int8_t DeviceStateDependentByte();
|
int8_t DeviceStateDependentByte();
|
||||||
int8_t ReturnByteSideEffectFree();
|
int8_t ReturnByteSideEffectFree();
|
||||||
|
@ -828,7 +828,7 @@ public:
|
||||||
|
|
||||||
// Deprecated methods and attributes
|
// Deprecated methods and attributes
|
||||||
int8_t DeprecatedAttribute();
|
int8_t DeprecatedAttribute();
|
||||||
int8_t SetDeprecatedAttribute(int8_t);
|
void SetDeprecatedAttribute(int8_t);
|
||||||
int8_t DeprecatedMethod();
|
int8_t DeprecatedMethod();
|
||||||
int8_t DeprecatedMethodWithContext(JSContext*, const JS::Value&);
|
int8_t DeprecatedMethodWithContext(JSContext*, const JS::Value&);
|
||||||
|
|
||||||
|
@ -841,9 +841,9 @@ public:
|
||||||
|
|
||||||
// Deprecated static methods and attributes
|
// Deprecated static methods and attributes
|
||||||
static int8_t StaticDeprecatedAttribute(const GlobalObject&);
|
static int8_t StaticDeprecatedAttribute(const GlobalObject&);
|
||||||
static int8_t SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
|
static void SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
|
||||||
static int8_t StaticDeprecatedMethod(const GlobalObject&);
|
static void StaticDeprecatedMethod(const GlobalObject&);
|
||||||
static int8_t StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
|
static void StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
|
||||||
|
|
||||||
// Overload resolution tests
|
// Overload resolution tests
|
||||||
bool Overload1(TestInterface&);
|
bool Overload1(TestInterface&);
|
||||||
|
|
|
@ -611,7 +611,7 @@ ReadStream::Create(const CacheReadStream& aReadStream)
|
||||||
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
|
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
|
||||||
|
|
||||||
// Currently we expect all cache read streams to be blocking file streams.
|
// Currently we expect all cache read streams to be blocking file streams.
|
||||||
#if !defined(RELEASE_OR_BETA)
|
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
||||||
if (stream) {
|
if (stream) {
|
||||||
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
|
MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
|
||||||
|
|
|
@ -5283,6 +5283,9 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
||||||
HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
|
HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
|
||||||
int32_t displayWidth = video->VideoWidth();
|
int32_t displayWidth = video->VideoWidth();
|
||||||
int32_t displayHeight = video->VideoHeight();
|
int32_t displayHeight = video->VideoHeight();
|
||||||
|
if (displayWidth == 0 || displayHeight == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
aSw *= (double)imgSize.width / (double)displayWidth;
|
aSw *= (double)imgSize.width / (double)displayWidth;
|
||||||
aSh *= (double)imgSize.height / (double)displayHeight;
|
aSh *= (double)imgSize.height / (double)displayHeight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ fuzzy-if(skiaContent,1,150) == clip-multiple-move-2.html clip-multiple-move-2-re
|
||||||
== stroketext-shadow.html stroketext-shadow-ref.html
|
== stroketext-shadow.html stroketext-shadow-ref.html
|
||||||
|
|
||||||
# focus rings
|
# focus rings
|
||||||
pref(canvas.focusring.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
|
pref(canvas.focusring.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) fuzzy-if(skiaContent&&!cocoaWidget&&!winWidget,1,2) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
|
||||||
pref(canvas.customfocusring.enabled,true) skip-if(Android||cocoaWidget||winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
|
pref(canvas.customfocusring.enabled,true) skip-if(Android||cocoaWidget||winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
|
||||||
|
|
||||||
# Check that captureStream() displays in a local video element
|
# Check that captureStream() displays in a local video element
|
||||||
|
|
|
@ -26,28 +26,6 @@ ClipboardEvent::ClipboardEvent(EventTarget* aOwner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN(ClipboardEvent)
|
|
||||||
NS_INTERFACE_MAP_ENTRY(nsIDOMClipboardEvent)
|
|
||||||
NS_INTERFACE_MAP_END_INHERITING(Event)
|
|
||||||
|
|
||||||
NS_IMPL_ADDREF_INHERITED(ClipboardEvent, Event)
|
|
||||||
NS_IMPL_RELEASE_INHERITED(ClipboardEvent, Event)
|
|
||||||
|
|
||||||
nsresult
|
|
||||||
ClipboardEvent::InitClipboardEvent(const nsAString& aType,
|
|
||||||
bool aCanBubble,
|
|
||||||
bool aCancelable,
|
|
||||||
nsIDOMDataTransfer* aClipboardData)
|
|
||||||
{
|
|
||||||
nsCOMPtr<DataTransfer> clipboardData = do_QueryInterface(aClipboardData);
|
|
||||||
// Null clipboardData is OK
|
|
||||||
|
|
||||||
ErrorResult rv;
|
|
||||||
InitClipboardEvent(aType, aCanBubble, aCancelable, clipboardData);
|
|
||||||
|
|
||||||
return rv.StealNSResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ClipboardEvent::InitClipboardEvent(const nsAString& aType, bool aCanBubble,
|
ClipboardEvent::InitClipboardEvent(const nsAString& aType, bool aCanBubble,
|
||||||
bool aCancelable,
|
bool aCancelable,
|
||||||
|
@ -90,13 +68,6 @@ ClipboardEvent::Constructor(const GlobalObject& aGlobal,
|
||||||
return e.forget();
|
return e.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData)
|
|
||||||
{
|
|
||||||
NS_IF_ADDREF(*aClipboardData = GetClipboardData());
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataTransfer*
|
DataTransfer*
|
||||||
ClipboardEvent::GetClipboardData()
|
ClipboardEvent::GetClipboardData()
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,26 +10,19 @@
|
||||||
#include "mozilla/EventForwards.h"
|
#include "mozilla/EventForwards.h"
|
||||||
#include "mozilla/dom/ClipboardEventBinding.h"
|
#include "mozilla/dom/ClipboardEventBinding.h"
|
||||||
#include "mozilla/dom/Event.h"
|
#include "mozilla/dom/Event.h"
|
||||||
#include "nsIDOMClipboardEvent.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
class DataTransfer;
|
class DataTransfer;
|
||||||
|
|
||||||
class ClipboardEvent : public Event,
|
class ClipboardEvent : public Event
|
||||||
public nsIDOMClipboardEvent
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClipboardEvent(EventTarget* aOwner,
|
ClipboardEvent(EventTarget* aOwner,
|
||||||
nsPresContext* aPresContext,
|
nsPresContext* aPresContext,
|
||||||
InternalClipboardEvent* aEvent);
|
InternalClipboardEvent* aEvent);
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_INLINE_DECL_REFCOUNTING_INHERITED(ClipboardEvent, Event)
|
||||||
|
|
||||||
NS_DECL_NSIDOMCLIPBOARDEVENT
|
|
||||||
|
|
||||||
// Forward to base class
|
|
||||||
NS_FORWARD_TO_EVENT
|
|
||||||
|
|
||||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||||
{
|
{
|
||||||
|
|
|
@ -1750,7 +1750,8 @@ HTMLFormElement::GetActionURL(nsIURI** aActionURL,
|
||||||
0, // aLineNumber
|
0, // aLineNumber
|
||||||
0, // aColumnNumber
|
0, // aColumnNumber
|
||||||
nsIScriptError::warningFlag, "CSP",
|
nsIScriptError::warningFlag, "CSP",
|
||||||
document->InnerWindowID());
|
document->InnerWindowID(),
|
||||||
|
!!document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -433,13 +433,11 @@ void
|
||||||
HTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle,
|
HTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle,
|
||||||
nsAString& aType,
|
nsAString& aType,
|
||||||
nsAString& aMedia,
|
nsAString& aMedia,
|
||||||
bool* aIsScoped,
|
|
||||||
bool* aIsAlternate)
|
bool* aIsAlternate)
|
||||||
{
|
{
|
||||||
aTitle.Truncate();
|
aTitle.Truncate();
|
||||||
aType.Truncate();
|
aType.Truncate();
|
||||||
aMedia.Truncate();
|
aMedia.Truncate();
|
||||||
*aIsScoped = false;
|
|
||||||
*aIsAlternate = false;
|
*aIsAlternate = false;
|
||||||
|
|
||||||
nsAutoString rel;
|
nsAutoString rel;
|
||||||
|
|
|
@ -207,12 +207,13 @@ protected:
|
||||||
virtual ~HTMLLinkElement();
|
virtual ~HTMLLinkElement();
|
||||||
|
|
||||||
// nsStyleLinkElement
|
// nsStyleLinkElement
|
||||||
virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) override;
|
already_AddRefed<nsIURI>
|
||||||
virtual void GetStyleSheetInfo(nsAString& aTitle,
|
GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) final;
|
||||||
nsAString& aType,
|
|
||||||
nsAString& aMedia,
|
void GetStyleSheetInfo(nsAString& aTitle,
|
||||||
bool* aIsScoped,
|
nsAString& aType,
|
||||||
bool* aIsAlternate) override;
|
nsAString& aMedia,
|
||||||
|
bool* aIsAlternate) final;
|
||||||
protected:
|
protected:
|
||||||
RefPtr<nsDOMTokenList> mRelList;
|
RefPtr<nsDOMTokenList> mRelList;
|
||||||
};
|
};
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче