зеркало из 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);
|
||||
|
||||
mParent->RemoveChild(this);
|
||||
MOZ_ASSERT(!mParent, "Parent has to be null!");
|
||||
}
|
||||
|
||||
// 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
|
||||
// its outerdoc has being recreated (see bug 862863 for details).
|
||||
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)
|
||||
{
|
||||
Accessible* child = mChildren.SafeElementAt(0, nullptr);
|
||||
MOZ_ASSERT(child == aAccessible, "Wrong child to remove!");
|
||||
if (child != aAccessible) {
|
||||
NS_ERROR("Wrong child to remove!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ window._gBrowser = {
|
|||
"resumeMedia", "mute", "unmute", "blockedPopups", "lastURI",
|
||||
"purgeSessionHistory", "stopScroll", "startScroll",
|
||||
"userTypedValue", "userTypedClear", "mediaBlocked",
|
||||
"didStartLoadSinceLastUserTyping"
|
||||
"didStartLoadSinceLastUserTyping", "audioMuted"
|
||||
],
|
||||
|
||||
_removingTabs: [],
|
||||
|
@ -1937,7 +1937,7 @@ window._gBrowser = {
|
|||
let setter;
|
||||
switch (name) {
|
||||
case "audioMuted":
|
||||
getter = () => false;
|
||||
getter = () => aTab.hasAttribute("muted");
|
||||
break;
|
||||
case "contentTitle":
|
||||
getter = () => SessionStore.getLazyTabValue(aTab, "title");
|
||||
|
|
|
@ -1906,39 +1906,6 @@
|
|||
<parameter name="aMuteReason"/>
|
||||
<body>
|
||||
<![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>
|
||||
</method>
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ support-files =
|
|||
head.js
|
||||
|
||||
[browser_all_files_referenced.js]
|
||||
skip-if = (os == 'win' && bits == 32)
|
||||
[browser_misused_characters_in_strings.js]
|
||||
support-files =
|
||||
bug1262648_string_with_newlines.dtd
|
||||
|
|
|
@ -96,9 +96,6 @@ let propNameWhitelist = [
|
|||
// Bug 1441837
|
||||
{propName: "--in-content-category-text-active",
|
||||
isFromDevTools: false},
|
||||
// Bug 1441844
|
||||
{propName: "--chrome-nav-bar-separator-color",
|
||||
isFromDevTools: false},
|
||||
// Bug 1441855
|
||||
{propName: "--chrome-nav-buttons-background",
|
||||
isFromDevTools: false},
|
||||
|
|
|
@ -35,3 +35,4 @@ skip-if = (debug && os == 'mac') || (debug && os == 'linux' && bits == 64) #Bug
|
|||
[browser_visibleTabs_bookmarkAllTabs.js]
|
||||
[browser_visibleTabs_contextMenu.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);
|
||||
|
||||
// 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.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
|
||||
ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
|
||||
|
@ -192,7 +192,7 @@ var gTests = [
|
|||
await BrowserTestUtils.removeTab(tab);
|
||||
|
||||
// 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.showCameraIndicator, "webrtcUI wants the camera indicator shown");
|
||||
ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
|
||||
|
|
|
@ -67,7 +67,7 @@ var gTests = [
|
|||
menulist.getItemAtIndex(2).doCommand();
|
||||
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
||||
"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,
|
||||
"the preview area is visible");
|
||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||
|
@ -169,7 +169,7 @@ var gTests = [
|
|||
menulist.getItemAtIndex(scaryIndex).doCommand();
|
||||
ok(document.getElementById("webRTC-all-windows-shared").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,
|
||||
"the preview area is visible");
|
||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||
|
@ -186,7 +186,7 @@ var gTests = [
|
|||
menulist.getItemAtIndex(nonScaryIndex).doCommand();
|
||||
ok(document.getElementById("webRTC-all-windows-shared").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,
|
||||
"the preview area is visible");
|
||||
ok(document.getElementById("webRTC-previewWarning").hidden,
|
||||
|
@ -277,7 +277,7 @@ var gTests = [
|
|||
menulist.getItemAtIndex(2).doCommand();
|
||||
ok(document.getElementById("webRTC-all-windows-shared").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,
|
||||
"the preview area is visible");
|
||||
ok(document.getElementById("webRTC-previewWarning").hidden,
|
||||
|
@ -328,7 +328,7 @@ var gTests = [
|
|||
menulist.getItemAtIndex(2).doCommand();
|
||||
ok(!document.getElementById("webRTC-all-windows-shared").hidden,
|
||||
"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,
|
||||
"the preview area is visible");
|
||||
ok(!document.getElementById("webRTC-previewWarning").hidden,
|
||||
|
@ -498,7 +498,7 @@ var gTests = [
|
|||
Services.wm.getMostRecentWindow("Browser:WebRTCGlobalIndicator");
|
||||
let elt = win.document.getElementById("screenShareButton");
|
||||
EventUtils.synthesizeMouseAtCenter(elt, {}, win);
|
||||
await promiseWaitForCondition(() => !gIdentityHandler._identityPopup.hidden);
|
||||
await TestUtils.waitForCondition(() => !gIdentityHandler._identityPopup.hidden);
|
||||
}
|
||||
ok(!gIdentityHandler._identityPopup.hidden, "control center should be open");
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
--chrome-color: rgb(249, 249, 250);
|
||||
--chrome-secondary-background-color: hsl(240, 1%, 20%);
|
||||
--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-hover-background: hsla(240, 5%, 5%, .15);
|
||||
--chrome-nav-bar-controls-border-color: hsla(240, 5%, 5%, .3);
|
||||
|
@ -41,7 +40,6 @@
|
|||
--chrome-color: #18191a;
|
||||
--chrome-secondary-background-color: #f5f6f7;
|
||||
--toolbox-border-bottom-color: #cccccc;
|
||||
--chrome-nav-bar-separator-color: #B6B6B8;
|
||||
--chrome-nav-buttons-background: #ffffff; /* --theme-body-background */
|
||||
--chrome-nav-buttons-hover-background: #DADBDB;
|
||||
--chrome-nav-bar-controls-border-color: #ccc;
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
|
||||
%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
|
||||
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
|
||||
|
|
|
@ -1,264 +1,264 @@
|
|||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent signed integer overflows in our codebase, found by the use of
|
||||
# -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
|
||||
# has been properly written to not overflow, and overflow-checking can have
|
||||
# significant compile time and runtime costs, so we will sometimes disable
|
||||
# signed overflow checking.
|
||||
#
|
||||
# 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
|
||||
# question to exempt individual functions using MOZ_NO_SANITIZE_SINT_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for signed integer overflows has a cost, at
|
||||
# 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
|
||||
# costs. (Indeed, -fsanitize=signed-integer-overflow is unusably slow without
|
||||
# this.)
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# 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
|
||||
# attributes on individual functions.
|
||||
|
||||
# All entries in this file are to suppress signed-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[signed-integer-overflow]
|
||||
|
||||
# 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++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. Signed overflows here
|
||||
# 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
|
||||
# and functions individually tagged safe so this entry can be removed.)
|
||||
src:*/CheckedInt.h
|
||||
|
||||
# Exclude bignum
|
||||
src:*/mfbt/double-conversion/source/bignum.cc
|
||||
|
||||
# Exclude anything within gtests
|
||||
src:*/gtest/*
|
||||
|
||||
# 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
|
||||
# be too much effort to determine every single source here.
|
||||
src:*/mfbt/Atomics.h
|
||||
|
||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||
# arithmetics and crypto.
|
||||
src:*/security/nss/lib/freebl/mpi/*
|
||||
src:*/security/nss/lib/freebl/ecl/*
|
||||
|
||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||
fun:*nsTArray_base*ShiftData*
|
||||
|
||||
### Frequent 0 - 1 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.
|
||||
# However, some of them are so frequent that suppressing them at compile-time
|
||||
# makes sense to increase runtime performance.
|
||||
#
|
||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||
src:*/dom/xul/XULDocument.cpp
|
||||
src:*/nsCharTraits.h
|
||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||
# Code in layout/painting/nsDisplayList.cpp
|
||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||
# Code in modules/libpref/Preferences.cpp
|
||||
fun:*pref_InitInitialObjects*
|
||||
# Code in netwerk/base/nsIOService.cpp
|
||||
fun:*nsIOService*GetCachedProtocolHandler*
|
||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||
fun:*0nsCSSRuleProcessor@@*
|
||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||
fun:*TreeMatchContext*InitAncestors*
|
||||
fun:*TreeMatchContext*InitStyleScopes*
|
||||
# Code in layout/xul/nsXULPopupManager.cpp
|
||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*1nsDocument@@*
|
||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||
fun:*CompositorBridgeChild*Destroy*
|
||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||
fun:*ImageBridgeChild*ShutdownStep1*
|
||||
# Code in dom/base/nsGlobalWindow.cpp
|
||||
fun:*nsGlobalWindow*ClearControllers*
|
||||
# Code in layout/style/AnimationCollection.cpp
|
||||
fun:*AnimationCollection*PropertyDtor*
|
||||
# Code in layout/style/nsStyleSet.cpp
|
||||
fun:*nsStyleSet*AddImportantRules*
|
||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||
|
||||
|
||||
### Misc overflows
|
||||
|
||||
# Hot function in protobuf producing overflows
|
||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||
|
||||
|
||||
# SQLite3 is full of overflows :/
|
||||
src:*/db/sqlite3/src/sqlite3.c
|
||||
|
||||
# zlib has some overflows, we can't deal with them right now
|
||||
src:*/modules/zlib/src/*
|
||||
|
||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||
# miss some unintended overflows in that implementation, but we can't
|
||||
# check for it right now.
|
||||
src:*/mfbt/lz4.c
|
||||
|
||||
# Apparently this overflows a lot, because it contains some allocators
|
||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||
# to work here for operator new.
|
||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||
|
||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||
# We probably don't care about the frequent overflows there.
|
||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||
|
||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||
fun:*WeightDistance*
|
||||
|
||||
# Another frequent overflower
|
||||
fun:*nsTObserverArray_base*AdjustIterators*
|
||||
|
||||
# Overflows in Skia
|
||||
fun:*SkPathRef*makeSpace*
|
||||
fun:*SkPathRef*resetToSize*
|
||||
|
||||
# Expat Parser has some overflows
|
||||
fun:*nsExpatDriver*ConsumeToken*
|
||||
|
||||
# Frequent overflowers in harfbuzz
|
||||
fun:*hb_in_range*
|
||||
fun:*OT*collect_glyphs*
|
||||
|
||||
# These look like harmless layouting-related overflows
|
||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||
|
||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||
# being cast to unsigned and then overflowed.
|
||||
fun:*FilePath*Append*
|
||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||
|
||||
# Code in dom/base/nsJSEnvironment.cpp
|
||||
fun:*FireForgetSkippable*
|
||||
|
||||
# Code in gfx/thebes/gfxSkipChars.h
|
||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||
|
||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||
fun:*gfxScriptItemizer*fixup*
|
||||
fun:*gfxScriptItemizer*push*
|
||||
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*nsDocument*BlockOnload*
|
||||
|
||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||
|
||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||
fun:*PL_ArenaRelease*
|
||||
|
||||
# This file contains a bunch of arithmetic operations on timestamps that
|
||||
# apparently are allowed to overflow.
|
||||
src:*/src/widget/SystemTimeConverter.h
|
||||
|
||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||
fun:*Frame*FindNext*
|
||||
|
||||
# Code in netwerk/base/nsStandardURL.cpp,
|
||||
# these methods return signed but the subtraction is first performed unsigned
|
||||
fun:*nsStandardURL*ReplaceSegment*
|
||||
|
||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||
# same as previous with the previous entry.
|
||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||
|
||||
# Code in layout/tables/nsCellMap.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTableCellMap*GetColInfoAt*
|
||||
|
||||
# Code in layout/generic/nsTextFrame.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTextFrame*CharacterDataChanged*
|
||||
|
||||
# Not sure what is going on in this file, but it doesn't look
|
||||
# related to what we are looking for.
|
||||
src:*/xpcom/base/CountingAllocatorBase.h
|
||||
|
||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||
# Timestamp related, probably expecting the overflow
|
||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||
|
||||
# Several unsigned arithmetic operations with -1
|
||||
src:*/hal/HalWakeLock.cpp
|
||||
|
||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||
# somewhat frequent signed integer overflows. Probably harmless
|
||||
# because it's layout code.
|
||||
fun:*ClampAndAlignWithPixels*
|
||||
|
||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||
fun:*ClockResolutionNs*
|
||||
|
||||
# This header has all sorts of operators that do post-operation
|
||||
# overflow and underflow checking, triggering frequent reports
|
||||
src:*/mozglue/misc/TimeStamp.h
|
||||
|
||||
#
|
||||
# Various hashing functions, both regular and cryptographic ones
|
||||
#
|
||||
src:*/dom/canvas/MurmurHash3.cpp
|
||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||
src:*/intl/icu/source/common/unifiedcache.h
|
||||
src:*/mfbt/SHA1.cpp
|
||||
src:*/modules/zlib/src/adler32.c
|
||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/nsprpub/lib/ds/plhash.c
|
||||
src:*/security/manager/ssl/md4.c
|
||||
src:*/security/nss/lib/dbm/src/h_func.c
|
||||
src:*/security/nss/lib/freebl/sha512.c
|
||||
src:*/security/nss/lib/freebl/md5.c
|
||||
src:*/xpcom/ds/PLDHashTable.cpp
|
||||
|
||||
# Hash/Cache function in Skia
|
||||
fun:*GradientShaderCache*Build32bitCache*
|
||||
|
||||
# Hashing functions in Cairo
|
||||
fun:*_hash_matrix_fnv*
|
||||
fun:*_hash_mix_bits*
|
||||
fun:*_cairo_hash_string*
|
||||
fun:*_cairo_hash_bytes*
|
||||
|
||||
# intl code hashing functions
|
||||
fun:*ustr_hash*CharsN*
|
||||
fun:*hashEntry*
|
||||
|
||||
# harfbuzz hash/digest functions
|
||||
fun:*hb_set_digest_lowest_bits_t*
|
||||
|
||||
# Hash function in gfx
|
||||
fun:*gfxFontStyle*Hash*
|
||||
|
||||
# expat uses a CHAR_HASH macro in several places that causes
|
||||
# a high amount of overflows. We should try finding a better
|
||||
# way to disable this rather than blacklisting the whole thing.
|
||||
src:*/parser/expat/*
|
||||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent signed integer overflows in our codebase, found by the use of
|
||||
# -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
|
||||
# has been properly written to not overflow, and overflow-checking can have
|
||||
# significant compile time and runtime costs, so we will sometimes disable
|
||||
# signed overflow checking.
|
||||
#
|
||||
# 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
|
||||
# question to exempt specific functions using MOZ_NO_SANITIZE_SIGNED_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for signed integer overflows has a cost, at
|
||||
# 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
|
||||
# costs. (Indeed, -fsanitize=signed-integer-overflow is unusably slow without
|
||||
# this.)
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# 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
|
||||
# attributes on individual functions.
|
||||
|
||||
# All entries in this file are to suppress signed-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[signed-integer-overflow]
|
||||
|
||||
# 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++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. Signed overflows here
|
||||
# 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
|
||||
# and functions individually tagged safe so this entry can be removed.)
|
||||
src:*/CheckedInt.h
|
||||
|
||||
# Exclude bignum
|
||||
src:*/mfbt/double-conversion/source/bignum.cc
|
||||
|
||||
# Exclude anything within gtests
|
||||
src:*/gtest/*
|
||||
|
||||
# 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
|
||||
# be too much effort to determine every single source here.
|
||||
src:*/mfbt/Atomics.h
|
||||
|
||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||
# arithmetics and crypto.
|
||||
src:*/security/nss/lib/freebl/mpi/*
|
||||
src:*/security/nss/lib/freebl/ecl/*
|
||||
|
||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||
fun:*nsTArray_base*ShiftData*
|
||||
|
||||
### Frequent 0 - 1 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.
|
||||
# However, some of them are so frequent that suppressing them at compile-time
|
||||
# makes sense to increase runtime performance.
|
||||
#
|
||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||
src:*/dom/xul/XULDocument.cpp
|
||||
src:*/nsCharTraits.h
|
||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||
# Code in layout/painting/nsDisplayList.cpp
|
||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||
# Code in modules/libpref/Preferences.cpp
|
||||
fun:*pref_InitInitialObjects*
|
||||
# Code in netwerk/base/nsIOService.cpp
|
||||
fun:*nsIOService*GetCachedProtocolHandler*
|
||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||
fun:*0nsCSSRuleProcessor@@*
|
||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||
fun:*TreeMatchContext*InitAncestors*
|
||||
fun:*TreeMatchContext*InitStyleScopes*
|
||||
# Code in layout/xul/nsXULPopupManager.cpp
|
||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*1nsDocument@@*
|
||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||
fun:*CompositorBridgeChild*Destroy*
|
||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||
fun:*ImageBridgeChild*ShutdownStep1*
|
||||
# Code in dom/base/nsGlobalWindow.cpp
|
||||
fun:*nsGlobalWindow*ClearControllers*
|
||||
# Code in layout/style/AnimationCollection.cpp
|
||||
fun:*AnimationCollection*PropertyDtor*
|
||||
# Code in layout/style/nsStyleSet.cpp
|
||||
fun:*nsStyleSet*AddImportantRules*
|
||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||
|
||||
|
||||
### Misc overflows
|
||||
|
||||
# Hot function in protobuf producing overflows
|
||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||
|
||||
|
||||
# SQLite3 is full of overflows :/
|
||||
src:*/db/sqlite3/src/sqlite3.c
|
||||
|
||||
# zlib has some overflows, we can't deal with them right now
|
||||
src:*/modules/zlib/src/*
|
||||
|
||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||
# miss some unintended overflows in that implementation, but we can't
|
||||
# check for it right now.
|
||||
src:*/mfbt/lz4.c
|
||||
|
||||
# Apparently this overflows a lot, because it contains some allocators
|
||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||
# to work here for operator new.
|
||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||
|
||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||
# We probably don't care about the frequent overflows there.
|
||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||
|
||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||
fun:*WeightDistance*
|
||||
|
||||
# Another frequent overflower
|
||||
fun:*nsTObserverArray_base*AdjustIterators*
|
||||
|
||||
# Overflows in Skia
|
||||
fun:*SkPathRef*makeSpace*
|
||||
fun:*SkPathRef*resetToSize*
|
||||
|
||||
# Expat Parser has some overflows
|
||||
fun:*nsExpatDriver*ConsumeToken*
|
||||
|
||||
# Frequent overflowers in harfbuzz
|
||||
fun:*hb_in_range*
|
||||
fun:*OT*collect_glyphs*
|
||||
|
||||
# These look like harmless layouting-related overflows
|
||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||
|
||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||
# being cast to unsigned and then overflowed.
|
||||
fun:*FilePath*Append*
|
||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||
|
||||
# Code in dom/base/nsJSEnvironment.cpp
|
||||
fun:*FireForgetSkippable*
|
||||
|
||||
# Code in gfx/thebes/gfxSkipChars.h
|
||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||
|
||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||
fun:*gfxScriptItemizer*fixup*
|
||||
fun:*gfxScriptItemizer*push*
|
||||
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*nsDocument*BlockOnload*
|
||||
|
||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||
|
||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||
fun:*PL_ArenaRelease*
|
||||
|
||||
# This file contains a bunch of arithmetic operations on timestamps that
|
||||
# apparently are allowed to overflow.
|
||||
src:*/src/widget/SystemTimeConverter.h
|
||||
|
||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||
fun:*Frame*FindNext*
|
||||
|
||||
# Code in netwerk/base/nsStandardURL.cpp,
|
||||
# these methods return signed but the subtraction is first performed unsigned
|
||||
fun:*nsStandardURL*ReplaceSegment*
|
||||
|
||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||
# same as previous with the previous entry.
|
||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||
|
||||
# Code in layout/tables/nsCellMap.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTableCellMap*GetColInfoAt*
|
||||
|
||||
# Code in layout/generic/nsTextFrame.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTextFrame*CharacterDataChanged*
|
||||
|
||||
# Not sure what is going on in this file, but it doesn't look
|
||||
# related to what we are looking for.
|
||||
src:*/xpcom/base/CountingAllocatorBase.h
|
||||
|
||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||
# Timestamp related, probably expecting the overflow
|
||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||
|
||||
# Several unsigned arithmetic operations with -1
|
||||
src:*/hal/HalWakeLock.cpp
|
||||
|
||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||
# somewhat frequent signed integer overflows. Probably harmless
|
||||
# because it's layout code.
|
||||
fun:*ClampAndAlignWithPixels*
|
||||
|
||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||
fun:*ClockResolutionNs*
|
||||
|
||||
# This header has all sorts of operators that do post-operation
|
||||
# overflow and underflow checking, triggering frequent reports
|
||||
src:*/mozglue/misc/TimeStamp.h
|
||||
|
||||
#
|
||||
# Various hashing functions, both regular and cryptographic ones
|
||||
#
|
||||
src:*/dom/canvas/MurmurHash3.cpp
|
||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||
src:*/intl/icu/source/common/unifiedcache.h
|
||||
src:*/mfbt/SHA1.cpp
|
||||
src:*/modules/zlib/src/adler32.c
|
||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/nsprpub/lib/ds/plhash.c
|
||||
src:*/security/manager/ssl/md4.c
|
||||
src:*/security/nss/lib/dbm/src/h_func.c
|
||||
src:*/security/nss/lib/freebl/sha512.c
|
||||
src:*/security/nss/lib/freebl/md5.c
|
||||
src:*/xpcom/ds/PLDHashTable.cpp
|
||||
|
||||
# Hash/Cache function in Skia
|
||||
fun:*GradientShaderCache*Build32bitCache*
|
||||
|
||||
# Hashing functions in Cairo
|
||||
fun:*_hash_matrix_fnv*
|
||||
fun:*_hash_mix_bits*
|
||||
fun:*_cairo_hash_string*
|
||||
fun:*_cairo_hash_bytes*
|
||||
|
||||
# intl code hashing functions
|
||||
fun:*ustr_hash*CharsN*
|
||||
fun:*hashEntry*
|
||||
|
||||
# harfbuzz hash/digest functions
|
||||
fun:*hb_set_digest_lowest_bits_t*
|
||||
|
||||
# Hash function in gfx
|
||||
fun:*gfxFontStyle*Hash*
|
||||
|
||||
# expat uses a CHAR_HASH macro in several places that causes
|
||||
# a high amount of overflows. We should try finding a better
|
||||
# way to disable this rather than blacklisting the whole thing.
|
||||
src:*/parser/expat/*
|
||||
|
|
|
@ -1,271 +1,271 @@
|
|||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent *un*signed integer overflows in our codebase, found by the use of
|
||||
# -fsanitize=unsigned-integer-overflow. Such overflows are not necessarily
|
||||
# bugs -- unsigned integer overflow has well-defined semantics in C/C++. But
|
||||
# overflow may still be *unexpected* and incorrectly handled, so we try to
|
||||
# annotate those places where unsigned overflow is correct and desired.
|
||||
#
|
||||
# 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
|
||||
# question to exempt individual functions using MOZ_NO_SANITIZE_UINT_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for unsigned integer overflows has a cost, at
|
||||
# compile time and at runtime. In places where code expects and depends upon
|
||||
# overflow behavior -- and especially in performance-critical code -- it makes
|
||||
# sense to turn off overflow detection to avoid both costs. (Indeed,
|
||||
# -fsanitize=signed-integer-overflow is unusably slow without this.)
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# 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
|
||||
# attributes on individual functions.
|
||||
|
||||
# All entries in this file are to suppress unsigned-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[unsigned-integer-overflow]
|
||||
|
||||
# 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++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. The CheckedInt class
|
||||
# casts signed integers to unsigned first and then does a post-overflow
|
||||
# check causing lots of unsigned integer overflow messages.
|
||||
src:*/CheckedInt.h
|
||||
|
||||
# Exclude bignum
|
||||
src:*/mfbt/double-conversion/source/bignum.cc
|
||||
|
||||
# Exclude anything within gtests
|
||||
src:*/gtest/*
|
||||
|
||||
# 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
|
||||
# for other, less tested code. Ideally, we would include the JS engine here
|
||||
# at some point.
|
||||
src:*/js/src/*
|
||||
src:*/js/public/*
|
||||
src:*/js/*.h
|
||||
src:*/jsfriendapi.h
|
||||
|
||||
# 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
|
||||
# be too much effort to determine every single source here.
|
||||
src:*/mfbt/Atomics.h
|
||||
|
||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||
# arithmetics and crypto.
|
||||
src:*/security/nss/lib/freebl/mpi/*
|
||||
src:*/security/nss/lib/freebl/ecl/*
|
||||
|
||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||
fun:*nsTArray_base*ShiftData*
|
||||
|
||||
### Frequent 0 - 1 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.
|
||||
# However, some of them are so frequent that suppressing them at compile-time
|
||||
# makes sense to increase runtime performance.
|
||||
#
|
||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||
src:*/dom/xul/XULDocument.cpp
|
||||
src:*/nsCharTraits.h
|
||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||
# Code in layout/painting/nsDisplayList.cpp
|
||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||
# Code in modules/libpref/Preferences.cpp
|
||||
fun:*pref_InitInitialObjects*
|
||||
# Code in netwerk/base/nsIOService.cpp
|
||||
fun:*nsIOService*GetCachedProtocolHandler*
|
||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||
fun:*0nsCSSRuleProcessor@@*
|
||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||
fun:*TreeMatchContext*InitAncestors*
|
||||
fun:*TreeMatchContext*InitStyleScopes*
|
||||
# Code in layout/xul/nsXULPopupManager.cpp
|
||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*1nsDocument@@*
|
||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||
fun:*CompositorBridgeChild*Destroy*
|
||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||
fun:*ImageBridgeChild*ShutdownStep1*
|
||||
# Code in dom/base/nsGlobalWindow.cpp
|
||||
fun:*nsGlobalWindow*ClearControllers*
|
||||
# Code in layout/style/AnimationCollection.cpp
|
||||
fun:*AnimationCollection*PropertyDtor*
|
||||
# Code in layout/style/nsStyleSet.cpp
|
||||
fun:*nsStyleSet*AddImportantRules*
|
||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||
|
||||
|
||||
### Misc overflows
|
||||
|
||||
# Hot function in protobuf producing overflows
|
||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||
|
||||
|
||||
# SQLite3 is full of overflows :/
|
||||
src:*/db/sqlite3/src/sqlite3.c
|
||||
|
||||
# zlib has some overflows, we can't deal with them right now
|
||||
src:*/modules/zlib/src/*
|
||||
|
||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||
# miss some unintended overflows in that implementation, but we can't
|
||||
# check for it right now.
|
||||
src:*/mfbt/lz4.c
|
||||
|
||||
# Apparently this overflows a lot, because it contains some allocators
|
||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||
# to work here for operator new.
|
||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||
|
||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||
# We probably don't care about the frequent overflows there.
|
||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||
|
||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||
fun:*WeightDistance*
|
||||
|
||||
# Another frequent overflower
|
||||
fun:*nsTObserverArray_base*AdjustIterators*
|
||||
|
||||
# Overflows in Skia
|
||||
fun:*SkPathRef*makeSpace*
|
||||
fun:*SkPathRef*resetToSize*
|
||||
|
||||
# Expat Parser has some overflows
|
||||
fun:*nsExpatDriver*ConsumeToken*
|
||||
|
||||
# Frequent overflowers in harfbuzz
|
||||
fun:*hb_in_range*
|
||||
fun:*OT*collect_glyphs*
|
||||
|
||||
# These look like harmless layouting-related overflows
|
||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||
|
||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||
# being cast to unsigned and then overflowed.
|
||||
fun:*FilePath*Append*
|
||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||
|
||||
# Code in dom/base/nsJSEnvironment.cpp
|
||||
fun:*FireForgetSkippable*
|
||||
|
||||
# Code in gfx/thebes/gfxSkipChars.h
|
||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||
|
||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||
fun:*gfxScriptItemizer*fixup*
|
||||
fun:*gfxScriptItemizer*push*
|
||||
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*nsDocument*BlockOnload*
|
||||
|
||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||
|
||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||
fun:*PL_ArenaRelease*
|
||||
|
||||
# This file contains a bunch of arithmetic operations on timestamps that
|
||||
# apparently are allowed to overflow.
|
||||
src:*/src/widget/SystemTimeConverter.h
|
||||
|
||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||
fun:*Frame*FindNext*
|
||||
|
||||
# Code in netwerk/base/nsStandardURL.cpp,
|
||||
# these methods return signed but the subtraction is first performed unsigned
|
||||
fun:*nsStandardURL*ReplaceSegment*
|
||||
|
||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||
# same as previous with the previous entry.
|
||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||
|
||||
# Code in layout/tables/nsCellMap.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTableCellMap*GetColInfoAt*
|
||||
|
||||
# Code in layout/generic/nsTextFrame.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTextFrame*CharacterDataChanged*
|
||||
|
||||
# Not sure what is going on in this file, but it doesn't look
|
||||
# related to what we are looking for.
|
||||
src:*/xpcom/base/CountingAllocatorBase.h
|
||||
|
||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||
# Timestamp related, probably expecting the overflow
|
||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||
|
||||
# Several unsigned arithmetic operations with -1
|
||||
src:*/hal/HalWakeLock.cpp
|
||||
|
||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||
# somewhat frequent signed integer overflows. Probably harmless
|
||||
# because it's layout code.
|
||||
fun:*ClampAndAlignWithPixels*
|
||||
|
||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||
fun:*ClockResolutionNs*
|
||||
|
||||
# This header has all sorts of operators that do post-operation
|
||||
# overflow and underflow checking, triggering frequent reports
|
||||
src:*/mozglue/misc/TimeStamp.h
|
||||
|
||||
#
|
||||
# Various hashing functions, both regular and cryptographic ones
|
||||
#
|
||||
src:*/dom/canvas/MurmurHash3.cpp
|
||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||
src:*/intl/icu/source/common/unifiedcache.h
|
||||
src:*/mfbt/SHA1.cpp
|
||||
src:*/modules/zlib/src/adler32.c
|
||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/nsprpub/lib/ds/plhash.c
|
||||
src:*/security/manager/ssl/md4.c
|
||||
src:*/security/nss/lib/dbm/src/h_func.c
|
||||
src:*/security/nss/lib/freebl/sha512.c
|
||||
src:*/security/nss/lib/freebl/md5.c
|
||||
src:*/xpcom/ds/PLDHashTable.cpp
|
||||
|
||||
# Hash/Cache function in Skia
|
||||
fun:*GradientShaderCache*Build32bitCache*
|
||||
|
||||
# Hashing functions in Cairo
|
||||
fun:*_hash_matrix_fnv*
|
||||
fun:*_hash_mix_bits*
|
||||
fun:*_cairo_hash_string*
|
||||
fun:*_cairo_hash_bytes*
|
||||
|
||||
# intl code hashing functions
|
||||
fun:*ustr_hash*CharsN*
|
||||
fun:*hashEntry*
|
||||
|
||||
# harfbuzz hash/digest functions
|
||||
fun:*hb_set_digest_lowest_bits_t*
|
||||
|
||||
# Hash function in gfx
|
||||
fun:*gfxFontStyle*Hash*
|
||||
|
||||
# expat uses a CHAR_HASH macro in several places that causes
|
||||
# a high amount of overflows. We should try finding a better
|
||||
# way to disable this rather than blacklisting the whole thing.
|
||||
src:*/parser/expat/*
|
||||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent *un*signed integer overflows in our codebase, found by the use of
|
||||
# -fsanitize=unsigned-integer-overflow. Such overflows are not necessarily
|
||||
# bugs -- unsigned integer overflow has well-defined semantics in C/C++. But
|
||||
# overflow may still be *unexpected* and incorrectly handled, so we try to
|
||||
# annotate those places where unsigned overflow is correct and desired.
|
||||
#
|
||||
# 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
|
||||
# question to exempt specific functions using MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for unsigned integer overflows has a cost, at
|
||||
# compile time and at runtime. In places where code expects and depends upon
|
||||
# overflow behavior -- and especially in performance-critical code -- it makes
|
||||
# sense to turn off overflow detection to avoid both costs. (Indeed,
|
||||
# -fsanitize=signed-integer-overflow is unusably slow without this.)
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# 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
|
||||
# attributes on individual functions.
|
||||
|
||||
# All entries in this file are to suppress unsigned-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[unsigned-integer-overflow]
|
||||
|
||||
# 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++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. The CheckedInt class
|
||||
# casts signed integers to unsigned first and then does a post-overflow
|
||||
# check causing lots of unsigned integer overflow messages.
|
||||
src:*/CheckedInt.h
|
||||
|
||||
# Exclude bignum
|
||||
src:*/mfbt/double-conversion/source/bignum.cc
|
||||
|
||||
# Exclude anything within gtests
|
||||
src:*/gtest/*
|
||||
|
||||
# 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
|
||||
# for other, less tested code. Ideally, we would include the JS engine here
|
||||
# at some point.
|
||||
src:*/js/src/*
|
||||
src:*/js/public/*
|
||||
src:*/js/*.h
|
||||
src:*/jsfriendapi.h
|
||||
|
||||
# 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
|
||||
# be too much effort to determine every single source here.
|
||||
src:*/mfbt/Atomics.h
|
||||
|
||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||
# arithmetics and crypto.
|
||||
src:*/security/nss/lib/freebl/mpi/*
|
||||
src:*/security/nss/lib/freebl/ecl/*
|
||||
|
||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||
fun:*nsTArray_base*ShiftData*
|
||||
|
||||
### Frequent 0 - 1 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.
|
||||
# However, some of them are so frequent that suppressing them at compile-time
|
||||
# makes sense to increase runtime performance.
|
||||
#
|
||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||
src:*/dom/xul/XULDocument.cpp
|
||||
src:*/nsCharTraits.h
|
||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||
# Code in layout/painting/nsDisplayList.cpp
|
||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||
# Code in modules/libpref/Preferences.cpp
|
||||
fun:*pref_InitInitialObjects*
|
||||
# Code in netwerk/base/nsIOService.cpp
|
||||
fun:*nsIOService*GetCachedProtocolHandler*
|
||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||
fun:*0nsCSSRuleProcessor@@*
|
||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||
fun:*TreeMatchContext*InitAncestors*
|
||||
fun:*TreeMatchContext*InitStyleScopes*
|
||||
# Code in layout/xul/nsXULPopupManager.cpp
|
||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*1nsDocument@@*
|
||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||
fun:*CompositorBridgeChild*Destroy*
|
||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||
fun:*ImageBridgeChild*ShutdownStep1*
|
||||
# Code in dom/base/nsGlobalWindow.cpp
|
||||
fun:*nsGlobalWindow*ClearControllers*
|
||||
# Code in layout/style/AnimationCollection.cpp
|
||||
fun:*AnimationCollection*PropertyDtor*
|
||||
# Code in layout/style/nsStyleSet.cpp
|
||||
fun:*nsStyleSet*AddImportantRules*
|
||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||
|
||||
|
||||
### Misc overflows
|
||||
|
||||
# Hot function in protobuf producing overflows
|
||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||
|
||||
|
||||
# SQLite3 is full of overflows :/
|
||||
src:*/db/sqlite3/src/sqlite3.c
|
||||
|
||||
# zlib has some overflows, we can't deal with them right now
|
||||
src:*/modules/zlib/src/*
|
||||
|
||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||
# miss some unintended overflows in that implementation, but we can't
|
||||
# check for it right now.
|
||||
src:*/mfbt/lz4.c
|
||||
|
||||
# Apparently this overflows a lot, because it contains some allocators
|
||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||
# to work here for operator new.
|
||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||
|
||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||
# We probably don't care about the frequent overflows there.
|
||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||
|
||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||
fun:*WeightDistance*
|
||||
|
||||
# Another frequent overflower
|
||||
fun:*nsTObserverArray_base*AdjustIterators*
|
||||
|
||||
# Overflows in Skia
|
||||
fun:*SkPathRef*makeSpace*
|
||||
fun:*SkPathRef*resetToSize*
|
||||
|
||||
# Expat Parser has some overflows
|
||||
fun:*nsExpatDriver*ConsumeToken*
|
||||
|
||||
# Frequent overflowers in harfbuzz
|
||||
fun:*hb_in_range*
|
||||
fun:*OT*collect_glyphs*
|
||||
|
||||
# These look like harmless layouting-related overflows
|
||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||
|
||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||
# being cast to unsigned and then overflowed.
|
||||
fun:*FilePath*Append*
|
||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||
|
||||
# Code in dom/base/nsJSEnvironment.cpp
|
||||
fun:*FireForgetSkippable*
|
||||
|
||||
# Code in gfx/thebes/gfxSkipChars.h
|
||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||
|
||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||
fun:*gfxScriptItemizer*fixup*
|
||||
fun:*gfxScriptItemizer*push*
|
||||
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*nsDocument*BlockOnload*
|
||||
|
||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||
|
||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||
fun:*PL_ArenaRelease*
|
||||
|
||||
# This file contains a bunch of arithmetic operations on timestamps that
|
||||
# apparently are allowed to overflow.
|
||||
src:*/src/widget/SystemTimeConverter.h
|
||||
|
||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||
fun:*Frame*FindNext*
|
||||
|
||||
# Code in netwerk/base/nsStandardURL.cpp,
|
||||
# these methods return signed but the subtraction is first performed unsigned
|
||||
fun:*nsStandardURL*ReplaceSegment*
|
||||
|
||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||
# same as previous with the previous entry.
|
||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||
|
||||
# Code in layout/tables/nsCellMap.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTableCellMap*GetColInfoAt*
|
||||
|
||||
# Code in layout/generic/nsTextFrame.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTextFrame*CharacterDataChanged*
|
||||
|
||||
# Not sure what is going on in this file, but it doesn't look
|
||||
# related to what we are looking for.
|
||||
src:*/xpcom/base/CountingAllocatorBase.h
|
||||
|
||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||
# Timestamp related, probably expecting the overflow
|
||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||
|
||||
# Several unsigned arithmetic operations with -1
|
||||
src:*/hal/HalWakeLock.cpp
|
||||
|
||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||
# somewhat frequent signed integer overflows. Probably harmless
|
||||
# because it's layout code.
|
||||
fun:*ClampAndAlignWithPixels*
|
||||
|
||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||
fun:*ClockResolutionNs*
|
||||
|
||||
# This header has all sorts of operators that do post-operation
|
||||
# overflow and underflow checking, triggering frequent reports
|
||||
src:*/mozglue/misc/TimeStamp.h
|
||||
|
||||
#
|
||||
# Various hashing functions, both regular and cryptographic ones
|
||||
#
|
||||
src:*/dom/canvas/MurmurHash3.cpp
|
||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||
src:*/intl/icu/source/common/unifiedcache.h
|
||||
src:*/mfbt/SHA1.cpp
|
||||
src:*/modules/zlib/src/adler32.c
|
||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/nsprpub/lib/ds/plhash.c
|
||||
src:*/security/manager/ssl/md4.c
|
||||
src:*/security/nss/lib/dbm/src/h_func.c
|
||||
src:*/security/nss/lib/freebl/sha512.c
|
||||
src:*/security/nss/lib/freebl/md5.c
|
||||
src:*/xpcom/ds/PLDHashTable.cpp
|
||||
|
||||
# Hash/Cache function in Skia
|
||||
fun:*GradientShaderCache*Build32bitCache*
|
||||
|
||||
# Hashing functions in Cairo
|
||||
fun:*_hash_matrix_fnv*
|
||||
fun:*_hash_mix_bits*
|
||||
fun:*_cairo_hash_string*
|
||||
fun:*_cairo_hash_bytes*
|
||||
|
||||
# intl code hashing functions
|
||||
fun:*ustr_hash*CharsN*
|
||||
fun:*hashEntry*
|
||||
|
||||
# harfbuzz hash/digest functions
|
||||
fun:*hb_set_digest_lowest_bits_t*
|
||||
|
||||
# Hash function in gfx
|
||||
fun:*gfxFontStyle*Hash*
|
||||
|
||||
# expat uses a CHAR_HASH macro in several places that causes
|
||||
# a high amount of overflows. We should try finding a better
|
||||
# way to disable this rather than blacklisting the whole thing.
|
||||
src:*/parser/expat/*
|
||||
|
|
|
@ -91,7 +91,8 @@ nsChromeRegistry::LogMessageWithContext(nsIURI* aURL, uint32_t aLineNumber, uint
|
|||
rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()),
|
||||
NS_ConvertUTF8toUTF16(spec),
|
||||
EmptyString(),
|
||||
aLineNumber, 0, flags, "chrome registration");
|
||||
aLineNumber, 0, flags, "chrome registration",
|
||||
false /* from private window */);
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
|
|
@ -120,8 +120,11 @@ CONFIG_TOOLS = $(MOZ_BUILD_ROOT)/config
|
|||
AUTOCONF_TOOLS = $(MOZILLA_DIR)/build/autoconf
|
||||
|
||||
ifdef _MSC_VER
|
||||
# clang-cl is smart enough to generate dependencies directly.
|
||||
ifndef CLANG_CL
|
||||
CC_WRAPPER ?= $(call py_action,cl)
|
||||
CXX_WRAPPER ?= $(call py_action,cl)
|
||||
endif # CLANG_CL
|
||||
endif # _MSC_VER
|
||||
|
||||
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 App = createFactory(require("./components/App"));
|
||||
const CurrentTimeTimer = require("./current-time-timer");
|
||||
|
||||
const {
|
||||
updateAnimations,
|
||||
|
@ -19,21 +20,38 @@ const {
|
|||
updateSelectedAnimation,
|
||||
updateSidebarSize
|
||||
} = require("./actions/animations");
|
||||
const { isAllAnimationEqual } = require("./utils/utils");
|
||||
const {
|
||||
isAllAnimationEqual,
|
||||
hasAnimationIterationCountInfinite,
|
||||
hasRunningAnimation,
|
||||
} = require("./utils/utils");
|
||||
|
||||
class AnimationInspector {
|
||||
constructor(inspector, win) {
|
||||
this.inspector = inspector;
|
||||
this.win = win;
|
||||
|
||||
this.addAnimationsCurrentTimeListener =
|
||||
this.addAnimationsCurrentTimeListener.bind(this);
|
||||
this.getAnimatedPropertyMap = this.getAnimatedPropertyMap.bind(this);
|
||||
this.getAnimationsCurrentTime = this.getAnimationsCurrentTime.bind(this);
|
||||
this.getComputedStyle = this.getComputedStyle.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.setAnimationsCurrentTime = this.setAnimationsCurrentTime.bind(this);
|
||||
this.setAnimationsPlaybackRate = this.setAnimationsPlaybackRate.bind(this);
|
||||
this.setAnimationsPlayState = this.setAnimationsPlayState.bind(this);
|
||||
this.setDetailVisibility = this.setDetailVisibility.bind(this);
|
||||
this.simulateAnimation = this.simulateAnimation.bind(this);
|
||||
this.simulateAnimationForKeyframesProgressBar =
|
||||
this.simulateAnimationForKeyframesProgressBar.bind(this);
|
||||
this.toggleElementPicker = this.toggleElementPicker.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.onElementPickerStopped = this.onElementPickerStopped.bind(this);
|
||||
this.onSidebarResized = this.onSidebarResized.bind(this);
|
||||
|
@ -56,19 +74,31 @@ class AnimationInspector {
|
|||
} = this.inspector.getPanel("boxmodel").getComponentProps();
|
||||
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
emit: emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
getNodeFromActor,
|
||||
isAnimationsRunning,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
rewindAnimationsCurrentTime,
|
||||
selectAnimation,
|
||||
setAnimationsCurrentTime,
|
||||
setAnimationsPlaybackRate,
|
||||
setAnimationsPlayState,
|
||||
setDetailVisibility,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
toggleElementPicker,
|
||||
} = this;
|
||||
|
||||
const target = this.inspector.target;
|
||||
this.animationsFront = new AnimationsFront(target.client, target.form);
|
||||
|
||||
this.animationsCurrentTimeListeners = [];
|
||||
this.isCurrentTimeSet = false;
|
||||
|
||||
const provider = createElement(Provider,
|
||||
{
|
||||
id: "newanimationinspector",
|
||||
|
@ -77,16 +107,25 @@ class AnimationInspector {
|
|||
},
|
||||
App(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
getNodeFromActor,
|
||||
isAnimationsRunning,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
rewindAnimationsCurrentTime,
|
||||
selectAnimation,
|
||||
setAnimationsCurrentTime,
|
||||
setAnimationsPlaybackRate,
|
||||
setAnimationsPlayState,
|
||||
setDetailVisibility,
|
||||
setSelectedNode,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
toggleElementPicker,
|
||||
}
|
||||
)
|
||||
|
@ -117,10 +156,25 @@ class AnimationInspector {
|
|||
this.simulatedElement = null;
|
||||
}
|
||||
|
||||
if (this.simulatedAnimationForKeyframesProgressBar) {
|
||||
this.simulatedAnimationForKeyframesProgressBar.cancel();
|
||||
this.simulatedAnimationForKeyframesProgressBar = null;
|
||||
}
|
||||
|
||||
this.stopAnimationsCurrentTimeTimer();
|
||||
|
||||
this.inspector = 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.
|
||||
*
|
||||
|
@ -160,6 +214,10 @@ class AnimationInspector {
|
|||
return animatedPropertyMap;
|
||||
}
|
||||
|
||||
getAnimationsCurrentTime() {
|
||||
return this.currentTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the computed style of the specified property after setting the given styles
|
||||
* to the simulated element.
|
||||
|
@ -191,6 +249,110 @@ class AnimationInspector {
|
|||
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.
|
||||
* The returned animation is implementing Animation interface of Web Animation API.
|
||||
|
@ -233,6 +395,46 @@ class AnimationInspector {
|
|||
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() {
|
||||
this.inspector.toolbox.highlighterUtils.togglePicker();
|
||||
}
|
||||
|
@ -246,48 +448,35 @@ class AnimationInspector {
|
|||
const done = this.inspector.updating("newanimationinspector");
|
||||
|
||||
const selection = this.inspector.selection;
|
||||
const animations =
|
||||
const nextAnimations =
|
||||
selection.isConnected() && selection.isElementNode()
|
||||
? await this.animationsFront.getAnimationPlayersForNode(selection.nodeFront)
|
||||
: [];
|
||||
const currentAnimations = this.state.animations;
|
||||
|
||||
if (!this.animations || !isAllAnimationEqual(animations, this.animations)) {
|
||||
this.inspector.store.dispatch(updateAnimations(animations));
|
||||
this.animations = animations;
|
||||
// If number of displayed animations is one, we select the animation automatically.
|
||||
this.selectAnimation(animations.length === 1 ? animations[0] : null);
|
||||
if (!currentAnimations || !isAllAnimationEqual(currentAnimations, nextAnimations)) {
|
||||
this.updateState(nextAnimations);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
selectAnimation(animation) {
|
||||
this.inspector.store.dispatch(updateSelectedAnimation(animation));
|
||||
async updateAnimations(animations) {
|
||||
const promises = animations.map(animation => {
|
||||
return animation.refreshState();
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
setDetailVisibility(isVisible) {
|
||||
this.inspector.store.dispatch(updateDetailVisibility(isVisible));
|
||||
}
|
||||
updateState(animations) {
|
||||
this.stopAnimationsCurrentTimeTimer();
|
||||
|
||||
onElementPickerStarted() {
|
||||
this.inspector.store.dispatch(updateElementPickerEnabled(true));
|
||||
}
|
||||
this.inspector.store.dispatch(updateAnimations(animations));
|
||||
|
||||
onElementPickerStopped() {
|
||||
this.inspector.store.dispatch(updateElementPickerEnabled(false));
|
||||
}
|
||||
|
||||
onSidebarSelect() {
|
||||
this.update();
|
||||
this.onSidebarResized(null, this.inspector.getSidebarSize());
|
||||
}
|
||||
|
||||
onSidebarResized(type, size) {
|
||||
if (!this.isPanelVisible()) {
|
||||
return;
|
||||
if (hasRunningAnimation(animations)) {
|
||||
this.startAnimationsCurrentTimeTimer();
|
||||
}
|
||||
|
||||
this.inspector.store.dispatch(updateSidebarSize(size));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,28 +14,47 @@ const AnimatedPropertyListHeader = createFactory(require("./AnimatedPropertyList
|
|||
class AnimatedPropertyListContainer extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
animation: PropTypes.object.isRequired,
|
||||
emitEventForTest: PropTypes.func.isRequired,
|
||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
getComputedStyle: PropTypes.func.isRequired,
|
||||
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||
timeScale: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
animation,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: `animated-property-list-container ${ animation.state.type }`
|
||||
},
|
||||
AnimatedPropertyListHeader(),
|
||||
AnimatedPropertyListHeader(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
animation,
|
||||
getAnimationsCurrentTime,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
}
|
||||
),
|
||||
AnimatedPropertyList(
|
||||
{
|
||||
animation,
|
||||
|
|
|
@ -6,16 +6,48 @@
|
|||
|
||||
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 KeyframesProgressBar = createFactory(require("./KeyframesProgressBar"));
|
||||
const KeyframesProgressTickList = createFactory(require("./KeyframesProgressTickList"));
|
||||
|
||||
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() {
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
animation,
|
||||
getAnimationsCurrentTime,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
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 {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
animation: PropTypes.object.isRequired,
|
||||
emitEventForTest: PropTypes.func.isRequired,
|
||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
getComputedStyle: PropTypes.func.isRequired,
|
||||
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
setDetailVisibility: PropTypes.func.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||
timeScale: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
animation,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
setDetailVisibility,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
|
@ -51,11 +61,16 @@ class AnimationDetailContainer extends PureComponent {
|
|||
animation ?
|
||||
AnimatedPropertyListContainer(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
animation,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
}
|
||||
)
|
||||
:
|
||||
|
|
|
@ -18,7 +18,8 @@ class AnimationDetailHeader extends PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
onClick() {
|
||||
onClick(event) {
|
||||
event.stopPropagation();
|
||||
const { setDetailVisibility } = this.props;
|
||||
setDetailVisibility(false);
|
||||
}
|
||||
|
|
|
@ -12,36 +12,41 @@ const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
|||
const AnimationList = createFactory(require("./AnimationList"));
|
||||
const AnimationListHeader = createFactory(require("./AnimationListHeader"));
|
||||
|
||||
const TimeScale = require("../utils/timescale");
|
||||
|
||||
class AnimationListContainer extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
emitEventForTest: PropTypes.func.isRequired,
|
||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||
getNodeFromActor: PropTypes.func.isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
selectAnimation: PropTypes.func.isRequired,
|
||||
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
setSelectedNode: PropTypes.func.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
timeScale: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
animations,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getNodeFromActor,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
selectAnimation,
|
||||
setAnimationsCurrentTime,
|
||||
setSelectedNode,
|
||||
simulateAnimation,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
const timeScale = new TimeScale(animations);
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
|
@ -49,6 +54,9 @@ class AnimationListContainer extends PureComponent {
|
|||
},
|
||||
AnimationListHeader(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
setAnimationsCurrentTime,
|
||||
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 AnimationTimelineTickList = createFactory(require("./AnimationTimelineTickList"));
|
||||
const CurrentTimeScrubberController =
|
||||
createFactory(require("./CurrentTimeScrubberController"));
|
||||
|
||||
class AnimationListHeader extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
timeScale: PropTypes.object.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { timeScale } = this.props;
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
setAnimationsCurrentTime,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
|
@ -29,6 +39,14 @@ class AnimationListHeader extends PureComponent {
|
|||
{
|
||||
timeScale
|
||||
}
|
||||
),
|
||||
CurrentTimeScrubberController(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
setAnimationsCurrentTime,
|
||||
timeScale,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -35,11 +35,11 @@ class AnimationTimelineTickList extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateTickList();
|
||||
this.updateTickList(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.updateTickList();
|
||||
this.updateTickList(nextProps);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
@ -51,7 +51,7 @@ class AnimationTimelineTickList extends PureComponent {
|
|||
const currentTickItem = this.state.tickList[i];
|
||||
const nextTickItem = nextState.tickList[i];
|
||||
|
||||
if (currentTickItem.text !== nextTickItem.text) {
|
||||
if (currentTickItem.timeTickLabel !== nextTickItem.timeTickLabel) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +59,8 @@ class AnimationTimelineTickList extends PureComponent {
|
|||
return false;
|
||||
}
|
||||
|
||||
updateTickList() {
|
||||
const { timeScale } = this.props;
|
||||
updateTickList(props) {
|
||||
const { timeScale } = props;
|
||||
const tickListEl = ReactDOM.findDOMNode(this);
|
||||
const width = tickListEl.offsetWidth;
|
||||
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 AnimationListContainer = createFactory(require("./AnimationListContainer"));
|
||||
const AnimationToolbar = createFactory(require("./AnimationToolbar"));
|
||||
const NoAnimationPanel = createFactory(require("./NoAnimationPanel"));
|
||||
const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
|
||||
|
||||
class App extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
addAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
animations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
detailVisibility: PropTypes.bool.isRequired,
|
||||
emitEventForTest: PropTypes.func.isRequired,
|
||||
getAnimatedPropertyMap: PropTypes.func.isRequired,
|
||||
getAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
getComputedStyle: PropTypes.func.isRequired,
|
||||
getNodeFromActor: PropTypes.func.isRequired,
|
||||
onHideBoxModelHighlighter: PropTypes.func.isRequired,
|
||||
onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
|
||||
removeAnimationsCurrentTimeListener: PropTypes.func.isRequired,
|
||||
rewindAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
selectAnimation: PropTypes.func.isRequired,
|
||||
setAnimationsCurrentTime: PropTypes.func.isRequired,
|
||||
setAnimationsPlaybackRate: PropTypes.func.isRequired,
|
||||
setAnimationsPlayState: PropTypes.func.isRequired,
|
||||
setDetailVisibility: PropTypes.func.isRequired,
|
||||
setSelectedNode: PropTypes.func.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
simulateAnimationForKeyframesProgressBar: PropTypes.func.isRequired,
|
||||
timeScale: PropTypes.object.isRequired,
|
||||
toggleElementPicker: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
@ -39,18 +49,27 @@ class App extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
addAnimationsCurrentTimeListener,
|
||||
animations,
|
||||
detailVisibility,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getAnimationsCurrentTime,
|
||||
getComputedStyle,
|
||||
getNodeFromActor,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
rewindAnimationsCurrentTime,
|
||||
selectAnimation,
|
||||
setAnimationsCurrentTime,
|
||||
setAnimationsPlaybackRate,
|
||||
setAnimationsPlayState,
|
||||
setDetailVisibility,
|
||||
setSelectedNode,
|
||||
simulateAnimation,
|
||||
simulateAnimationForKeyframesProgressBar,
|
||||
timeScale,
|
||||
toggleElementPicker,
|
||||
} = this.props;
|
||||
|
||||
|
@ -58,37 +77,59 @@ class App extends PureComponent {
|
|||
{
|
||||
id: "animation-container",
|
||||
className: detailVisibility ? "animation-detail-visible" : "",
|
||||
tabIndex: -1,
|
||||
},
|
||||
animations.length ?
|
||||
SplitBox({
|
||||
className: "animation-container-splitter",
|
||||
endPanel: AnimationDetailContainer(
|
||||
{
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getComputedStyle,
|
||||
setDetailVisibility,
|
||||
simulateAnimation,
|
||||
}
|
||||
),
|
||||
endPanelControl: true,
|
||||
initialHeight: "50%",
|
||||
splitterSize: 1,
|
||||
startPanel: AnimationListContainer(
|
||||
[
|
||||
AnimationToolbar(
|
||||
{
|
||||
addAnimationsCurrentTimeListener,
|
||||
animations,
|
||||
emitEventForTest,
|
||||
getAnimatedPropertyMap,
|
||||
getNodeFromActor,
|
||||
onHideBoxModelHighlighter,
|
||||
onShowBoxModelHighlighterForNode,
|
||||
selectAnimation,
|
||||
setSelectedNode,
|
||||
simulateAnimation,
|
||||
removeAnimationsCurrentTimeListener,
|
||||
rewindAnimationsCurrentTime,
|
||||
setAnimationsPlaybackRate,
|
||||
setAnimationsPlayState,
|
||||
}
|
||||
),
|
||||
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(
|
||||
{
|
||||
|
@ -103,6 +144,7 @@ const mapStateToProps = state => {
|
|||
return {
|
||||
animations: state.animations.animations,
|
||||
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" +
|
||||
(elementPickerEnabled ? " checked" : ""),
|
||||
"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,
|
||||
durationPerPixel: PropTypes.number.isRequired,
|
||||
keyframes: PropTypes.object.isRequired,
|
||||
offset: PropTypes.number.isRequired,
|
||||
opacity: PropTypes.number.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
totalDuration: PropTypes.number.isRequired,
|
||||
|
@ -27,6 +28,7 @@ class ComputedTimingPath extends TimingPath {
|
|||
animation,
|
||||
durationPerPixel,
|
||||
keyframes,
|
||||
offset,
|
||||
opacity,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
|
@ -78,7 +80,6 @@ class ComputedTimingPath extends TimingPath {
|
|||
const helper = new SummaryGraphHelper(state, keyframes,
|
||||
totalDuration, durationPerPixel,
|
||||
getValueFunc, toPathStringFunc);
|
||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
||||
|
||||
return dom.g(
|
||||
{
|
||||
|
|
|
@ -21,19 +21,23 @@ class DelaySign extends PureComponent {
|
|||
animation,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
const {
|
||||
fill,
|
||||
playbackRate,
|
||||
previousStartTime = 0,
|
||||
} = animation.state;
|
||||
|
||||
const { state } = animation;
|
||||
const startTime = (state.previousStartTime || 0) - timeScale.minStartTime
|
||||
+ (state.delay < 0 ? state.delay : 0);
|
||||
const delay = animation.state.delay / playbackRate;
|
||||
const startTime =
|
||||
previousStartTime - timeScale.minStartTime + (delay < 0 ? delay : 0);
|
||||
const offset = startTime / timeScale.getDuration() * 100;
|
||||
const width = Math.abs(state.delay) / timeScale.getDuration() * 100;
|
||||
|
||||
const delayClass = state.delay < 0 ? "negative" : "";
|
||||
const fillClass = state.fill === "both" || state.fill === "backwards" ? "fill" : "";
|
||||
const width = Math.abs(delay) / timeScale.getDuration() * 100;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: `animation-delay-sign ${ delayClass } ${ fillClass }`,
|
||||
className: "animation-delay-sign" +
|
||||
(delay < 0 ? " negative" : "") +
|
||||
(fill === "both" || fill === "backwards" ? " fill" : ""),
|
||||
style: {
|
||||
width: `${ width }%`,
|
||||
left: `${ offset }%`,
|
||||
|
|
|
@ -15,6 +15,7 @@ class EffectTimingPath extends TimingPath {
|
|||
return {
|
||||
animation: PropTypes.object.isRequired,
|
||||
durationPerPixel: PropTypes.number.isRequired,
|
||||
offset: PropTypes.number.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
totalDuration: PropTypes.number.isRequired,
|
||||
};
|
||||
|
@ -24,6 +25,7 @@ class EffectTimingPath extends TimingPath {
|
|||
const {
|
||||
animation,
|
||||
durationPerPixel,
|
||||
offset,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
} = this.props;
|
||||
|
@ -57,7 +59,6 @@ class EffectTimingPath extends TimingPath {
|
|||
const helper = new SummaryGraphHelper(state, null,
|
||||
totalDuration, durationPerPixel,
|
||||
getValueFunc, toPathStringFunc);
|
||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
||||
|
||||
return dom.g(
|
||||
{
|
||||
|
|
|
@ -21,20 +21,27 @@ class EndDelaySign extends PureComponent {
|
|||
animation,
|
||||
timeScale,
|
||||
} = this.props;
|
||||
const {
|
||||
delay,
|
||||
duration,
|
||||
fill,
|
||||
iterationCount,
|
||||
playbackRate,
|
||||
previousStartTime = 0,
|
||||
} = animation.state;
|
||||
|
||||
const { state } = animation;
|
||||
const startTime = (state.previousStartTime || 0) - timeScale.minStartTime;
|
||||
const endTime = state.duration * state.iterationCount + state.delay;
|
||||
const endDelay = state.endDelay < 0 ? state.endDelay : 0;
|
||||
const offset = (startTime + endTime + endDelay) / timeScale.getDuration() * 100;
|
||||
const width = Math.abs(state.endDelay) / timeScale.getDuration() * 100;
|
||||
|
||||
const endDelayClass = state.endDelay < 0 ? "negative" : "";
|
||||
const fillClass = state.fill === "both" || state.fill === "forwards" ? "fill" : "";
|
||||
const endDelay = animation.state.endDelay / playbackRate;
|
||||
const startTime = previousStartTime - timeScale.minStartTime;
|
||||
const endTime =
|
||||
(duration * iterationCount + delay) / playbackRate + (endDelay < 0 ? endDelay : 0);
|
||||
const offset = (startTime + endTime) / timeScale.getDuration() * 100;
|
||||
const width = Math.abs(endDelay) / timeScale.getDuration() * 100;
|
||||
|
||||
return dom.div(
|
||||
{
|
||||
className: `animation-end-delay-sign ${ endDelayClass } ${ fillClass }`,
|
||||
className: "animation-end-delay-sign" +
|
||||
(endDelay < 0 ? " negative" : "") +
|
||||
(fill === "both" || fill === "forwards" ? " fill" : ""),
|
||||
style: {
|
||||
width: `${ width }%`,
|
||||
left: `${ offset }%`,
|
||||
|
|
|
@ -4,25 +4,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
|
||||
const NegativePath = require("./NegativePath");
|
||||
|
||||
class NegativeDelayPath extends NegativePath {
|
||||
static get propTypes() {
|
||||
return {
|
||||
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);
|
||||
getClassName() {
|
||||
return "animation-negative-delay-path";
|
||||
}
|
||||
|
||||
renderGraph(state, helper) {
|
||||
|
|
|
@ -4,25 +4,13 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
|
||||
const NegativePath = require("./NegativePath");
|
||||
|
||||
class NegativeEndDelayPath extends NegativePath {
|
||||
static get propTypes() {
|
||||
return {
|
||||
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);
|
||||
getClassName() {
|
||||
return "animation-negative-end-delay-path";
|
||||
}
|
||||
|
||||
renderGraph(state, helper) {
|
||||
|
|
|
@ -17,6 +17,7 @@ class NegativePath extends PureComponent {
|
|||
className: PropTypes.string.isRequired,
|
||||
durationPerPixel: PropTypes.number.isRequired,
|
||||
keyframes: PropTypes.object.isRequired,
|
||||
offset: PropTypes.number.isRequired,
|
||||
simulateAnimation: PropTypes.func.isRequired,
|
||||
totalDuration: PropTypes.number.isRequired,
|
||||
};
|
||||
|
@ -25,9 +26,9 @@ class NegativePath extends PureComponent {
|
|||
render() {
|
||||
const {
|
||||
animation,
|
||||
className,
|
||||
durationPerPixel,
|
||||
keyframes,
|
||||
offset,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
} = this.props;
|
||||
|
@ -75,11 +76,10 @@ class NegativePath extends PureComponent {
|
|||
const helper = new SummaryGraphHelper(state, keyframes,
|
||||
totalDuration, durationPerPixel,
|
||||
getValueFunc, toPathStringFunc);
|
||||
const offset = state.previousStartTime ? state.previousStartTime : 0;
|
||||
|
||||
return dom.g(
|
||||
{
|
||||
className,
|
||||
className: this.getClassName(),
|
||||
transform: `translate(${ offset })`
|
||||
},
|
||||
this.renderGraph(state, helper)
|
||||
|
|
|
@ -33,7 +33,8 @@ class SummaryGraph extends PureComponent {
|
|||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
onClick(event) {
|
||||
event.stopPropagation();
|
||||
this.props.selectAnimation(this.props.animation);
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,9 @@ class SummaryGraphPath extends PureComponent {
|
|||
} = this.props;
|
||||
|
||||
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);
|
||||
|
||||
return dom.svg(
|
||||
|
@ -190,6 +192,7 @@ class SummaryGraphPath extends PureComponent {
|
|||
animation,
|
||||
durationPerPixel,
|
||||
keyframes,
|
||||
offset,
|
||||
opacity,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
|
@ -201,6 +204,7 @@ class SummaryGraphPath extends PureComponent {
|
|||
{
|
||||
animation,
|
||||
durationPerPixel,
|
||||
offset,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
}
|
||||
|
@ -214,6 +218,7 @@ class SummaryGraphPath extends PureComponent {
|
|||
animation,
|
||||
durationPerPixel,
|
||||
keyframes,
|
||||
offset,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
}
|
||||
|
@ -228,6 +233,7 @@ class SummaryGraphPath extends PureComponent {
|
|||
animation,
|
||||
durationPerPixel,
|
||||
keyframes,
|
||||
offset,
|
||||
simulateAnimation,
|
||||
totalDuration,
|
||||
}
|
||||
|
|
|
@ -22,8 +22,16 @@ DevToolsModules(
|
|||
'AnimationTarget.js',
|
||||
'AnimationTimelineTickItem.js',
|
||||
'AnimationTimelineTickList.js',
|
||||
'AnimationToolbar.js',
|
||||
'App.js',
|
||||
'CurrentTimeLabel.js',
|
||||
'CurrentTimeScrubber.js',
|
||||
'CurrentTimeScrubberController.js',
|
||||
'KeyframesProgressBar.js',
|
||||
'KeyframesProgressTickItem.js',
|
||||
'KeyframesProgressTickList.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']
|
||||
|
||||
DevToolsModules(
|
||||
'animation.js'
|
||||
'animation.js',
|
||||
'current-time-timer.js'
|
||||
)
|
||||
|
|
|
@ -12,6 +12,8 @@ const {
|
|||
UPDATE_SIDEBAR_SIZE,
|
||||
} = require("../actions/index");
|
||||
|
||||
const TimeScale = require("../utils/timescale");
|
||||
|
||||
const INITIAL_STATE = {
|
||||
animations: [],
|
||||
detailVisibility: false,
|
||||
|
@ -21,12 +23,25 @@ const INITIAL_STATE = {
|
|||
height: 0,
|
||||
width: 0,
|
||||
},
|
||||
timeScale: null,
|
||||
};
|
||||
|
||||
const reducers = {
|
||||
[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, {
|
||||
animations,
|
||||
detailVisibility,
|
||||
selectedAnimation,
|
||||
timeScale: new TimeScale(animations),
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_custom_playback_rate.html
|
||||
doc_multi_easings.html
|
||||
doc_multi_keyframes.html
|
||||
doc_multi_timings.html
|
||||
|
@ -22,11 +23,19 @@ support-files =
|
|||
[browser_animation_animation-list.js]
|
||||
[browser_animation_animation-target.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_inspector_exists.js]
|
||||
[browser_animation_keyframes-graph_computed-value-path.js]
|
||||
[browser_animation_keyframes-graph_computed-value-path_easing-hint.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_compositor.js]
|
||||
[browser_animation_summary-graph_computed-timing-path.js]
|
||||
|
|
|
@ -14,7 +14,7 @@ add_task(async function() {
|
|||
ok(panel.querySelector(".animation-list"),
|
||||
"The animation-list is in the DOM");
|
||||
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");
|
||||
|
||||
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");
|
||||
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 "
|
||||
+ "that displays");
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ const TIME_GRADUATION_MIN_SPACING = 40;
|
|||
add_task(async function() {
|
||||
await addTab(URL_ROOT + "doc_simple_animation.html");
|
||||
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");
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -177,8 +361,10 @@ const waitForRendering = async function(animationInspector) {
|
|||
*
|
||||
* @param {AnimationInspector} inspector
|
||||
*/
|
||||
|
||||
const waitForAnimationDetail = async function(animationInspector) {
|
||||
if (animationInspector.animations.length === 1) {
|
||||
if (animationInspector.state.selectedAnimation &&
|
||||
animationInspector.state.detailVisibility) {
|
||||
await animationInspector.once("animation-keyframes-rendered");
|
||||
}
|
||||
};
|
||||
|
@ -190,7 +376,7 @@ const waitForAnimationDetail = async function(animationInspector) {
|
|||
* @param {AnimationInspector} 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");
|
||||
}
|
||||
};
|
||||
|
@ -201,11 +387,74 @@ const waitForAllAnimationTargets = async function(animationInspector) {
|
|||
* @param {AnimationInspector} inspector
|
||||
*/
|
||||
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");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* and color attributes.
|
||||
|
|
|
@ -24,6 +24,8 @@ class TimeScale {
|
|||
constructor(animations) {
|
||||
this.minStartTime = Infinity;
|
||||
this.maxEndTime = 0;
|
||||
this.documentCurrentTime = 0;
|
||||
|
||||
for (const animation of animations) {
|
||||
this.addAnimation(animation.state);
|
||||
}
|
||||
|
@ -38,11 +40,12 @@ class TimeScale {
|
|||
addAnimation(state) {
|
||||
let {
|
||||
delay,
|
||||
documentCurrentTime,
|
||||
duration,
|
||||
endDelay = 0,
|
||||
iterationCount,
|
||||
playbackRate,
|
||||
previousStartTime,
|
||||
previousStartTime = 0,
|
||||
} = state;
|
||||
|
||||
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
|
||||
// account here.
|
||||
const relevantDelay = delay < 0 ? toRate(delay) : 0;
|
||||
previousStartTime = previousStartTime || 0;
|
||||
|
||||
const startTime = toRate(minZero(delay)) +
|
||||
rateRelativeDuration +
|
||||
endDelay;
|
||||
|
@ -67,6 +68,8 @@ class TimeScale {
|
|||
const length = toRate(delay) + rateRelativeDuration + toRate(minZero(endDelay));
|
||||
const endTime = previousStartTime + length;
|
||||
this.maxEndTime = Math.max(this.maxEndTime, endTime);
|
||||
|
||||
this.documentCurrentTime = Math.max(this.documentCurrentTime, documentCurrentTime);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -69,6 +69,27 @@ function isAllAnimationEqual(animationsA, animationsB) {
|
|||
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.
|
||||
*
|
||||
|
@ -88,5 +109,7 @@ function isTimingEffectEqual(stateA, stateB) {
|
|||
}
|
||||
|
||||
exports.findOptimalTimeInterval = findOptimalTimeInterval;
|
||||
exports.hasAnimationIterationCountInfinite = hasAnimationIterationCountInfinite;
|
||||
exports.hasRunningAnimation = hasRunningAnimation;
|
||||
exports.isAllAnimationEqual = isAllAnimationEqual;
|
||||
exports.isTimingEffectEqual = isTimingEffectEqual;
|
||||
|
|
|
@ -49,6 +49,7 @@ class Census extends Component {
|
|||
|
||||
return Tree({
|
||||
autoExpandDepth: 0,
|
||||
preventNavigationOnArrowRight: false,
|
||||
focused: census.focused,
|
||||
getParent: node => {
|
||||
const parent = parentMap[node.id];
|
||||
|
|
|
@ -134,6 +134,7 @@ class DominatorTree extends Component {
|
|||
return Tree({
|
||||
key: "dominator-tree-tree",
|
||||
autoExpandDepth: DOMINATOR_TREE_AUTO_EXPAND_DEPTH,
|
||||
preventNavigationOnArrowRight: false,
|
||||
focused: dominatorTree.focused,
|
||||
getParent: node =>
|
||||
node instanceof DominatorTreeLazyChildren
|
||||
|
|
|
@ -35,6 +35,7 @@ class Individuals extends Component {
|
|||
return Tree({
|
||||
key: "individuals-tree",
|
||||
autoExpandDepth: 0,
|
||||
preventNavigationOnArrowRight: false,
|
||||
focused: individuals.focused,
|
||||
getParent: node => null,
|
||||
getChildren: node => [],
|
||||
|
|
|
@ -189,6 +189,7 @@ class JITOptimizations extends Component {
|
|||
|
||||
return Tree({
|
||||
autoExpandDepth,
|
||||
preventNavigationOnArrowRight: false,
|
||||
getParent: node => {
|
||||
let site = getSite(node.id);
|
||||
let parent;
|
||||
|
|
|
@ -163,6 +163,7 @@ class WaterfallTree extends Component {
|
|||
|
||||
render() {
|
||||
return Tree({
|
||||
preventNavigationOnArrowRight: false,
|
||||
getRoots: this._getRoots,
|
||||
getParent: this._getParent,
|
||||
getChildren: this._getChildren,
|
||||
|
|
|
@ -198,6 +198,10 @@ class Tree extends Component {
|
|||
// Handle when item is activated with a keyboard (using Space or Enter)
|
||||
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.
|
||||
autoExpandDepth: PropTypes.number,
|
||||
|
||||
|
@ -228,6 +232,7 @@ class Tree extends Component {
|
|||
static get defaultProps() {
|
||||
return {
|
||||
autoExpandDepth: AUTO_EXPAND_DEPTH,
|
||||
preventNavigationOnArrowRight: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -502,9 +507,10 @@ class Tree extends Component {
|
|||
break;
|
||||
|
||||
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);
|
||||
} else {
|
||||
} else if (!this.props.preventNavigationOnArrowRight) {
|
||||
this._focusNextNode();
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -16,12 +16,17 @@ Test keyboard navigation with the Tree component.
|
|||
<pre id="test">
|
||||
<script src="head.js" type="application/javascript"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
"use strict";
|
||||
|
||||
window.onload = async function () {
|
||||
try {
|
||||
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
|
||||
const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
|
||||
const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
|
||||
const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
|
||||
const { Simulate } =
|
||||
browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
|
||||
const Tree =
|
||||
createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
|
||||
|
||||
function renderTree(props) {
|
||||
const treeProps = Object.assign({},
|
||||
|
@ -175,7 +180,8 @@ window.onload = async function () {
|
|||
"M:false",
|
||||
"-N:false",
|
||||
"--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 --------------------------------------------------------------------
|
||||
|
||||
|
@ -246,7 +252,30 @@ window.onload = async function () {
|
|||
"--O:false",
|
||||
], "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" });
|
||||
await forceRender(tree);
|
||||
|
||||
|
@ -275,6 +304,8 @@ window.onload = async function () {
|
|||
{ key: "ArrowDown", metaKey: true },
|
||||
{ key: "ArrowDown", shiftKey: true },
|
||||
];
|
||||
await forceRender(tree);
|
||||
|
||||
for (let key of keysWithModifier) {
|
||||
Simulate.keyDown(document.querySelector(".tree"), key);
|
||||
await forceRender(tree);
|
||||
|
@ -297,7 +328,7 @@ window.onload = async function () {
|
|||
"--O:false",
|
||||
], "After DOWN + (alt|ctrl|meta|shift), K should remain focused.");
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
|
||||
} finally {
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -13,6 +13,11 @@
|
|||
--fill-color-scriptanimation: var(--theme-graphs-green);
|
||||
--graph-right-offset: 10px;
|
||||
--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;
|
||||
--stroke-color-cssanimation: var(--theme-highlight-lightorange);
|
||||
--stroke-color-csstransition: var(--theme-highlight-bluegrey);
|
||||
|
@ -27,10 +32,15 @@
|
|||
|
||||
:root.theme-firebug {
|
||||
--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 */
|
||||
#animation-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -42,6 +52,44 @@
|
|||
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 {
|
||||
display: flex;
|
||||
|
@ -49,28 +97,69 @@
|
|||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.animation-list-container.active-scrubber {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
/* Animation List Header */
|
||||
.animation-list-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Animation Timeline Tick List */
|
||||
.animation-timeline-tick-list {
|
||||
margin-right: var(--graph-right-offset);
|
||||
grid-column: 2/3;
|
||||
position: relative;
|
||||
width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
|
||||
}
|
||||
|
||||
.animation-timeline-tick-item {
|
||||
border-left: var(--tick-line-style);
|
||||
height: 100vh;
|
||||
pointer-events: none;
|
||||
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 {
|
||||
flex: 1;
|
||||
|
@ -241,11 +330,13 @@
|
|||
|
||||
/* Animation Detail */
|
||||
.animation-detail-container {
|
||||
background-color: var(--theme-body-background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.animation-detail-header {
|
||||
|
@ -266,21 +357,20 @@
|
|||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Animated Property List Header */
|
||||
.animated-property-list-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
display: grid;
|
||||
grid-template-columns: var(--sidebar-width) calc(100% - var(--sidebar-width) - var(--graph-right-offset)) var(--graph-right-offset);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Keyframes Progress Tick List */
|
||||
.keyframes-progress-tick-list {
|
||||
margin-right: var(--graph-right-offset);
|
||||
position: absolute;
|
||||
width: calc(100% - var(--sidebar-width) - var(--graph-right-offset));
|
||||
grid-column: 2 / 3;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.keyframes-progress-tick-item {
|
||||
|
@ -296,6 +386,41 @@
|
|||
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 {
|
||||
flex: 1;
|
||||
|
|
|
@ -1918,7 +1918,7 @@ DebuggerServer.ObjectActorPreviewers.Object = [
|
|||
props.push("propertyName", "pseudoElement");
|
||||
} else if (obj.class == "AnimationEvent") {
|
||||
props.push("animationName", "pseudoElement");
|
||||
} else if (rawObj instanceof Ci.nsIDOMClipboardEvent) {
|
||||
} else if (obj.class == "ClipboardEvent") {
|
||||
props.push("clipboardData");
|
||||
}
|
||||
|
||||
|
|
|
@ -214,6 +214,12 @@ Element::GetSVGAnimatedClass() const
|
|||
NS_IMETHODIMP
|
||||
Element::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
||||
{
|
||||
if (aIID.Equals(NS_GET_IID(Element))) {
|
||||
NS_ADDREF_THIS();
|
||||
*aInstancePtr = this;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aInstancePtr,
|
||||
"QueryInterface requires a non-NULL destination!");
|
||||
nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
|
||||
|
@ -1706,11 +1712,6 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|||
if (ShadowRoot* shadowRootParent = ShadowRoot::FromNode(parent)) {
|
||||
parent = shadowRootParent->GetHost();
|
||||
}
|
||||
|
||||
bool inStyleScope = parent->IsElementInStyleScope();
|
||||
|
||||
SetIsElementInStyleScope(inStyleScope);
|
||||
SetIsElementInStyleScopeFlagOnShadowTree(inStyleScope);
|
||||
}
|
||||
|
||||
// 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_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
|
||||
NS_INTERFACE_MAP_ENTRY(Element)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIContent)
|
||||
NS_INTERFACE_MAP_ENTRY(nsINode)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
|
||||
|
@ -2469,40 +2468,3 @@ FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
|
|||
*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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
/**
|
||||
* If there are listeners for DOMNodeInserted event, fires the event on all
|
||||
|
@ -375,14 +367,6 @@ protected:
|
|||
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;
|
||||
/**
|
||||
* 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_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEmptyContentList)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHTMLCollection)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
|
||||
|
||||
|
||||
|
@ -157,7 +158,7 @@ NS_IMPL_RELEASE_INHERITED(nsEmptyContentList, nsBaseContentList)
|
|||
JSObject*
|
||||
nsEmptyContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return NodeListBinding::Wrap(cx, this, aGivenProto);
|
||||
return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -311,7 +311,6 @@ bool nsContentUtils::sAutoFocusEnabled = true;
|
|||
#ifndef RELEASE_OR_BETA
|
||||
bool nsContentUtils::sBypassCSSOMOriginCheck = false;
|
||||
#endif
|
||||
bool nsContentUtils::sIsScopedStyleEnabled = false;
|
||||
|
||||
bool nsContentUtils::sIsBytecodeCacheEnabled = false;
|
||||
int32_t nsContentUtils::sBytecodeCacheStrategy = 0;
|
||||
|
@ -716,9 +715,6 @@ nsContentUtils::Init()
|
|||
sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
|
||||
#endif
|
||||
|
||||
Preferences::AddBoolVarCache(&sIsScopedStyleEnabled,
|
||||
"layout.css.scoped-style.enabled", false);
|
||||
|
||||
Preferences::AddBoolVarCache(&sLowerNetworkPriority,
|
||||
"privacy.trackingprotection.lower_network_priority", false);
|
||||
|
||||
|
@ -4146,7 +4142,8 @@ nsresult nsContentUtils::FormatLocalizedString(
|
|||
|
||||
/* static */ void
|
||||
nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
||||
const char * classification)
|
||||
const char * classification,
|
||||
bool aFromPrivateWindow)
|
||||
{
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
||||
|
@ -4156,7 +4153,8 @@ nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
|||
if (console && NS_SUCCEEDED(scriptError->Init(aErrorText, EmptyString(),
|
||||
EmptyString(), 0, 0,
|
||||
nsIScriptError::errorFlag,
|
||||
classification))) {
|
||||
classification,
|
||||
aFromPrivateWindow))) {
|
||||
console->LogMessage(scriptError);
|
||||
}
|
||||
}
|
||||
|
@ -5834,7 +5832,8 @@ nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
|
|||
}
|
||||
msg.AppendLiteral("Unable to run script because scripts are blocked internally.");
|
||||
|
||||
LogSimpleConsoleError(msg, "DOM");
|
||||
LogSimpleConsoleError(msg, "DOM",
|
||||
!!aDocument->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -1008,7 +1008,8 @@ public:
|
|||
* @param classification Name of the module reporting error
|
||||
*/
|
||||
static void LogSimpleConsoleError(const nsAString& aErrorText,
|
||||
const char * classification);
|
||||
const char * classification,
|
||||
bool aFromPrivateWindow);
|
||||
|
||||
/**
|
||||
* Report a non-localized error message to the error console.
|
||||
|
@ -2445,14 +2446,6 @@ public:
|
|||
#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
|
||||
* context node.
|
||||
|
@ -3468,7 +3461,6 @@ private:
|
|||
#ifndef RELEASE_OR_BETA
|
||||
static bool sBypassCSSOMOriginCheck;
|
||||
#endif
|
||||
static bool sIsScopedStyleEnabled;
|
||||
static bool sIsBytecodeCacheEnabled;
|
||||
static int32_t sBytecodeCacheStrategy;
|
||||
static uint32_t sCookiesLifetimePolicy;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/FramingChecker.h"
|
||||
#include "mozilla/dom/HTMLSharedElement.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "mozilla/dom/CDATASection.h"
|
||||
#include "mozilla/dom/ProcessingInstruction.h"
|
||||
|
@ -1500,8 +1501,8 @@ nsIDocument::nsIDocument()
|
|||
mDelayFrameLoaderInitialization(false),
|
||||
mSynchronousDOMContentLoaded(false),
|
||||
mMaybeServiceWorkerControlled(false),
|
||||
mIsScopedStyleEnabled(eScopedStyle_Unknown),
|
||||
mPendingFullscreenRequests(0),
|
||||
mXMLDeclarationBits(0),
|
||||
mCompatMode(eCompatibility_FullStandards),
|
||||
mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
|
||||
mStyleBackendType(StyleBackendType::None),
|
||||
|
@ -1557,7 +1558,6 @@ nsDocument::nsDocument(const char* aContentType)
|
|||
: nsIDocument()
|
||||
, mParserAborted(false)
|
||||
, mReportedUseCounters(false)
|
||||
, mXMLDeclarationBits(0)
|
||||
, mOnloadBlockCount(0)
|
||||
, mAsyncOnloadBlockCount(0)
|
||||
, 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(mTemplateContentsOwner)
|
||||
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)
|
||||
|
||||
// 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(mTemplateContentsOwner)
|
||||
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(mFontFaceSet)
|
||||
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*
|
||||
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 */
|
||||
bool
|
||||
nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
|
||||
|
@ -7626,9 +7727,9 @@ nsIDocument::FlushExternalResources(FlushType aType)
|
|||
}
|
||||
|
||||
void
|
||||
nsDocument::SetXMLDeclaration(const char16_t *aVersion,
|
||||
const char16_t *aEncoding,
|
||||
const int32_t aStandalone)
|
||||
nsIDocument::SetXMLDeclaration(const char16_t* aVersion,
|
||||
const char16_t* aEncoding,
|
||||
const int32_t aStandalone)
|
||||
{
|
||||
if (!aVersion || *aVersion == '\0') {
|
||||
mXMLDeclarationBits = 0;
|
||||
|
@ -7651,8 +7752,9 @@ nsDocument::SetXMLDeclaration(const char16_t *aVersion,
|
|||
}
|
||||
|
||||
void
|
||||
nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
|
||||
nsAString& aStandalone)
|
||||
nsIDocument::GetXMLDeclaration(nsAString& aVersion,
|
||||
nsAString& aEncoding,
|
||||
nsAString& aStandalone)
|
||||
{
|
||||
aVersion.Truncate();
|
||||
aEncoding.Truncate();
|
||||
|
@ -13238,18 +13340,6 @@ nsIDocument::IsThirdParty()
|
|||
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
|
||||
nsIDocument::ClearStaleServoData()
|
||||
{
|
||||
|
|
|
@ -199,13 +199,6 @@ public:
|
|||
virtual void BeginLoad() 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
|
||||
NS_IMETHOD WalkRadioGroup(const nsAString& aName,
|
||||
nsIRadioVisitor* aVisitor,
|
||||
|
@ -404,8 +397,6 @@ public:
|
|||
// that we only report them once for the document.
|
||||
bool mReportedUseCounters:1;
|
||||
|
||||
uint8_t mXMLDeclarationBits;
|
||||
|
||||
// The application cache that this document is associated with, if
|
||||
// any. This can change during the lifetime of the document.
|
||||
nsCOMPtr<nsIApplicationCache> mApplicationCache;
|
||||
|
|
|
@ -515,7 +515,8 @@ GetParamsForMessage(JSContext* aCx,
|
|||
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?"),
|
||||
filename, EmptyString(), lineno, column,
|
||||
nsIScriptError::warningFlag, "chrome javascript");
|
||||
nsIScriptError::warningFlag, "chrome javascript",
|
||||
false /* from private window */);
|
||||
console->LogMessage(error);
|
||||
}
|
||||
|
||||
|
@ -1111,7 +1112,8 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
|||
if (console) {
|
||||
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
||||
error->Init(msg, EmptyString(), EmptyString(),
|
||||
0, 0, nsIScriptError::warningFlag, "chrome javascript");
|
||||
0, 0, nsIScriptError::warningFlag, "chrome javascript",
|
||||
false /* from private window */);
|
||||
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".)",
|
||||
targetURL.get(), targetOrigin.get(), sourceOrigin.get())),
|
||||
"DOM");
|
||||
"DOM",
|
||||
!!principal->PrivateBrowsingId());
|
||||
|
||||
attrs = principal->OriginAttributesRef();
|
||||
}
|
||||
|
|
|
@ -166,6 +166,7 @@ class FrameRequestCallback;
|
|||
struct FullscreenRequest;
|
||||
class ImageTracker;
|
||||
class HTMLBodyElement;
|
||||
class HTMLSharedElement;
|
||||
class HTMLImageElement;
|
||||
struct LifecycleCallbackArgs;
|
||||
class Link;
|
||||
|
@ -1598,6 +1599,8 @@ public:
|
|||
nsGenericHTMLElement* GetBody();
|
||||
// Set the "body" in the sense of document.body.
|
||||
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.
|
||||
|
@ -2134,12 +2137,12 @@ public:
|
|||
* was no standalone parameter in the declaration, that it was given as no,
|
||||
* or that it was given as yes.
|
||||
*/
|
||||
virtual void SetXMLDeclaration(const char16_t *aVersion,
|
||||
const char16_t *aEncoding,
|
||||
const int32_t aStandalone) = 0;
|
||||
virtual void GetXMLDeclaration(nsAString& aVersion,
|
||||
nsAString& aEncoding,
|
||||
nsAString& Standalone) = 0;
|
||||
void SetXMLDeclaration(const char16_t* aVersion,
|
||||
const char16_t* aEncoding,
|
||||
const int32_t aStandalone);
|
||||
void GetXMLDeclaration(nsAString& aVersion,
|
||||
nsAString& aEncoding,
|
||||
nsAString& Standalone);
|
||||
|
||||
/**
|
||||
* 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 GetDir(nsAString& aDirection) const;
|
||||
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)
|
||||
{
|
||||
return GetFuncStringContentList<nsCachableElementsByNameNodeList>(this,
|
||||
|
@ -3294,6 +3306,8 @@ public:
|
|||
}
|
||||
Element* GetActiveElement();
|
||||
bool HasFocus(mozilla::ErrorResult& rv) const;
|
||||
nsIHTMLCollection* Applets();
|
||||
nsIHTMLCollection* Anchors();
|
||||
mozilla::TimeStamp LastFocusTime() const;
|
||||
void SetLastFocusTime(const mozilla::TimeStamp& aFocusTime);
|
||||
// Event handlers are all on nsINode already
|
||||
|
@ -3871,6 +3885,15 @@ protected:
|
|||
// Our cached .children collection
|
||||
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.)
|
||||
RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
|
||||
|
||||
|
@ -4105,12 +4128,10 @@ protected:
|
|||
// Used to prevent multiple requests to ServiceWorkerManager.
|
||||
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 mXMLDeclarationBits;
|
||||
|
||||
// Compatibility mode
|
||||
nsCompatibility mCompatMode;
|
||||
|
||||
|
|
|
@ -1662,11 +1662,6 @@ private:
|
|||
NodeHasTextNodeDirectionalityMap,
|
||||
// Set if a node in the node's parent chain has dir=auto.
|
||||
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.
|
||||
NodeHandlingClick,
|
||||
// Set if the node has had :hover selectors matched against it
|
||||
|
@ -1795,23 +1790,6 @@ public:
|
|||
// Implemented in nsIContentInlines.h.
|
||||
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); }
|
||||
void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); }
|
||||
void SetParserHasNotified() { SetBoolFlag(ParserHasNotified); };
|
||||
|
|
|
@ -330,56 +330,6 @@ nsStyleLinkElement::UpdateStyleSheetInternal(nsIDocument *aOldDocument,
|
|||
&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
|
||||
nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
||||
ShadowRoot* aOldShadowRoot,
|
||||
|
@ -409,20 +359,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
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)) {
|
||||
MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
|
||||
"ShadowRoot content is never in document, thus "
|
||||
|
@ -442,9 +378,6 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
}
|
||||
|
||||
nsStyleLinkElement::SetStyleSheet(nullptr);
|
||||
if (oldScopeElement) {
|
||||
UpdateIsElementInStyleScopeFlagOnSubtree(oldScopeElement);
|
||||
}
|
||||
}
|
||||
|
||||
// When static documents are created, stylesheets are cloned manually.
|
||||
|
@ -492,21 +425,14 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
}
|
||||
|
||||
nsAutoString title, type, media;
|
||||
bool isScoped;
|
||||
bool isAlternate;
|
||||
|
||||
GetStyleSheetInfo(title, type, media, &isScoped, &isAlternate);
|
||||
GetStyleSheetInfo(title, type, media, &isAlternate);
|
||||
|
||||
if (!type.LowerCaseEqualsLiteral("text/css")) {
|
||||
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;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
@ -538,7 +464,7 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
// Parse the style sheet.
|
||||
rv = doc->CSSLoader()->
|
||||
LoadInlineStyle(thisContent, text, triggeringPrincipal, mLineNumber,
|
||||
title, media, referrerPolicy, scopeElement,
|
||||
title, media, referrerPolicy,
|
||||
aObserver, &doneLoading, &isAlternate);
|
||||
} else {
|
||||
nsAutoString integrity;
|
||||
|
@ -577,60 +503,3 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument* aOldDocument,
|
|||
|
||||
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,
|
||||
bool aForceUpdate = false);
|
||||
|
||||
void UpdateStyleSheetScopedness(bool aIsNowScoped);
|
||||
|
||||
virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) = 0;
|
||||
virtual void GetStyleSheetInfo(nsAString& aTitle,
|
||||
nsAString& aType,
|
||||
nsAString& aMedia,
|
||||
bool* aIsScoped,
|
||||
bool* aIsAlternate) = 0;
|
||||
|
||||
virtual mozilla::CORSMode GetCORSMode() const
|
||||
|
|
|
@ -17296,7 +17296,7 @@ class GlobalGenRoots():
|
|||
|
||||
def fieldSizeAssert(amount, jitInfoField, message):
|
||||
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))
|
||||
|
||||
idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
|
||||
|
@ -17308,7 +17308,8 @@ class GlobalGenRoots():
|
|||
idEnum = CGWrapper(idEnum, post='\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])
|
||||
|
||||
# Let things know the maximum length of the prototype chain.
|
||||
|
|
|
@ -95,7 +95,8 @@ interface nsIScriptError : nsIConsoleMessage
|
|||
in uint32_t lineNumber,
|
||||
in uint32_t columnNumber,
|
||||
in uint32_t flags,
|
||||
in string category);
|
||||
in string category,
|
||||
[optional] in bool fromPrivateWindow);
|
||||
|
||||
/* This should be called instead of nsIScriptError.init to
|
||||
* initialize with a window id. The window id should be for the
|
||||
|
|
|
@ -181,22 +181,6 @@ nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
|
|||
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
|
||||
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
|
||||
nsScriptErrorBase::InitializationHelper(const nsAString& message,
|
||||
const nsAString& sourceLine,
|
||||
|
|
|
@ -96,14 +96,6 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
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 ToString(nsACString& aResult) override;
|
||||
|
||||
|
|
|
@ -73,18 +73,6 @@ nsScriptErrorWithStack::~nsScriptErrorWithStack() {
|
|||
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
|
||||
nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
|
||||
aStack.setObjectOrNull(mStack);
|
||||
|
|
|
@ -213,9 +213,9 @@ public:
|
|||
int8_t CachedWritableByte();
|
||||
void SetCachedWritableByte(int8_t);
|
||||
int8_t SideEffectFreeByte();
|
||||
int8_t SetSideEffectFreeByte(int8_t);
|
||||
void SetSideEffectFreeByte(int8_t);
|
||||
int8_t DomDependentByte();
|
||||
int8_t SetDomDependentByte(int8_t);
|
||||
void SetDomDependentByte(int8_t);
|
||||
int8_t ConstantByte();
|
||||
int8_t DeviceStateDependentByte();
|
||||
int8_t ReturnByteSideEffectFree();
|
||||
|
@ -828,7 +828,7 @@ public:
|
|||
|
||||
// Deprecated methods and attributes
|
||||
int8_t DeprecatedAttribute();
|
||||
int8_t SetDeprecatedAttribute(int8_t);
|
||||
void SetDeprecatedAttribute(int8_t);
|
||||
int8_t DeprecatedMethod();
|
||||
int8_t DeprecatedMethodWithContext(JSContext*, const JS::Value&);
|
||||
|
||||
|
@ -841,9 +841,9 @@ public:
|
|||
|
||||
// Deprecated static methods and attributes
|
||||
static int8_t StaticDeprecatedAttribute(const GlobalObject&);
|
||||
static int8_t SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
|
||||
static int8_t StaticDeprecatedMethod(const GlobalObject&);
|
||||
static int8_t StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
|
||||
static void SetStaticDeprecatedAttribute(const GlobalObject&, int8_t);
|
||||
static void StaticDeprecatedMethod(const GlobalObject&);
|
||||
static void StaticDeprecatedMethodWithContext(const GlobalObject&, const JS::Value&);
|
||||
|
||||
// Overload resolution tests
|
||||
bool Overload1(TestInterface&);
|
||||
|
|
|
@ -611,7 +611,7 @@ ReadStream::Create(const CacheReadStream& aReadStream)
|
|||
nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
|
||||
|
||||
// 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) {
|
||||
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
|
||||
|
|
|
@ -5283,6 +5283,9 @@ CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
HTMLVideoElement* video = &aImage.GetAsHTMLVideoElement();
|
||||
int32_t displayWidth = video->VideoWidth();
|
||||
int32_t displayHeight = video->VideoHeight();
|
||||
if (displayWidth == 0 || displayHeight == 0) {
|
||||
return;
|
||||
}
|
||||
aSw *= (double)imgSize.width / (double)displayWidth;
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
ClipboardEvent::InitClipboardEvent(const nsAString& aType, bool aCanBubble,
|
||||
bool aCancelable,
|
||||
|
@ -90,13 +68,6 @@ ClipboardEvent::Constructor(const GlobalObject& aGlobal,
|
|||
return e.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData)
|
||||
{
|
||||
NS_IF_ADDREF(*aClipboardData = GetClipboardData());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DataTransfer*
|
||||
ClipboardEvent::GetClipboardData()
|
||||
{
|
||||
|
|
|
@ -10,26 +10,19 @@
|
|||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/dom/ClipboardEventBinding.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "nsIDOMClipboardEvent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class DataTransfer;
|
||||
|
||||
class ClipboardEvent : public Event,
|
||||
public nsIDOMClipboardEvent
|
||||
class ClipboardEvent : public Event
|
||||
{
|
||||
public:
|
||||
ClipboardEvent(EventTarget* aOwner,
|
||||
nsPresContext* aPresContext,
|
||||
InternalClipboardEvent* aEvent);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIDOMCLIPBOARDEVENT
|
||||
|
||||
// Forward to base class
|
||||
NS_FORWARD_TO_EVENT
|
||||
NS_INLINE_DECL_REFCOUNTING_INHERITED(ClipboardEvent, Event)
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
|
|
|
@ -1750,7 +1750,8 @@ HTMLFormElement::GetActionURL(nsIURI** aActionURL,
|
|||
0, // aLineNumber
|
||||
0, // aColumnNumber
|
||||
nsIScriptError::warningFlag, "CSP",
|
||||
document->InnerWindowID());
|
||||
document->InnerWindowID(),
|
||||
!!document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -433,13 +433,11 @@ void
|
|||
HTMLLinkElement::GetStyleSheetInfo(nsAString& aTitle,
|
||||
nsAString& aType,
|
||||
nsAString& aMedia,
|
||||
bool* aIsScoped,
|
||||
bool* aIsAlternate)
|
||||
{
|
||||
aTitle.Truncate();
|
||||
aType.Truncate();
|
||||
aMedia.Truncate();
|
||||
*aIsScoped = false;
|
||||
*aIsAlternate = false;
|
||||
|
||||
nsAutoString rel;
|
||||
|
|
|
@ -207,12 +207,13 @@ protected:
|
|||
virtual ~HTMLLinkElement();
|
||||
|
||||
// nsStyleLinkElement
|
||||
virtual already_AddRefed<nsIURI> GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) override;
|
||||
virtual void GetStyleSheetInfo(nsAString& aTitle,
|
||||
nsAString& aType,
|
||||
nsAString& aMedia,
|
||||
bool* aIsScoped,
|
||||
bool* aIsAlternate) override;
|
||||
already_AddRefed<nsIURI>
|
||||
GetStyleSheetURL(bool* aIsInline, nsIPrincipal** aTriggeringPrincipal) final;
|
||||
|
||||
void GetStyleSheetInfo(nsAString& aTitle,
|
||||
nsAString& aType,
|
||||
nsAString& aMedia,
|
||||
bool* aIsAlternate) final;
|
||||
protected:
|
||||
RefPtr<nsDOMTokenList> mRelList;
|
||||
};
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче