Merge mozilla-central to autoland. a=merge on a CLOSED TREE

This commit is contained in:
Andreea Pavel 2018-03-13 19:26:13 +02:00
Родитель 62afea2749 8fa0b32c84
Коммит 11bec2e097
2146 изменённых файлов: 269675 добавлений и 146438 удалений

Просмотреть файл

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

2
dom/cache/ReadStream.cpp поставляемый
Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше