Merge mozilla-central to b2g-inbound

This commit is contained in:
Carsten "Tomcat" Book 2015-07-14 14:33:48 +02:00
commit f3cb2827b1
66 changed files with 1100 additions and 847 deletions

View File

@ -42,7 +42,7 @@
<hbox id="clientBox">
<vbox id="leftBox" flex="1"/>
<vbox id="rightBox" flex="1">
#expand <label id="version">__MOZ_APP_VERSION_ABOUT__</label>
#expand <label id="version">__MOZ_APP_VERSION_DISPLAY__</label>
<label id="distribution" class="text-blurb"/>
<label id="distributionId" class="text-blurb"/>

View File

@ -25,7 +25,7 @@ BROWSER_CHROME_MANIFESTS += [
]
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_ABOUT'] = CONFIG['MOZ_APP_VERSION_ABOUT']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
DEFINES['APP_LICENSE_BLOCK'] = '%s/content/overrides/app-license.html' % SRCDIR

View File

@ -38,7 +38,7 @@ MOZ_SERVICES_METRICS=1
MOZ_SERVICES_SYNC=1
MOZ_SERVICES_CLOUDSYNC=1
MOZ_APP_VERSION=$FIREFOX_VERSION
MOZ_APP_VERSION_ABOUT=$FIREFOX_VERSION_ABOUT
MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY
MOZ_EXTENSIONS_DEFAULT=" gio"
# MOZ_APP_DISPLAYNAME will be set by branding/configure.sh
# MOZ_BRANDING_DIRECTORY is the default branding directory used when none is

View File

@ -3,11 +3,6 @@
* 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/. */
%endif
%filter substitution
%define smallSeparatorDark linear-gradient(transparent 15%, #5a6169 15%, #5a6169 85%, transparent 85%)
%define smallSeparatorLight linear-gradient(transparent 15%, #aaa 15%, #aaa 85%, transparent 85%)
%define solidSeparatorDark linear-gradient(#2d5b7d, #2d5b7d)
%define solidSeparatorLight linear-gradient(#aaa, #aaa)
/* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
.theme-light {
@ -554,73 +549,57 @@
text-shadow: none;
}
.devtools-sidebar-tabs tabs > tab {
border-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%) 1 1;
}
.devtools-sidebar-tabs tabs > tab[selected],
.devtools-sidebar-tabs tabs > tab[selected] + tab {
border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
}
.devtools-sidebar-tabs tabs > tab:first-child {
-moz-border-start-width: 0;
}
.theme-dark .devtools-sidebar-tabs tabs > tab {
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab:hover {
background: hsla(206,37%,4%,.2);
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab:hover:active {
background: hsla(206,37%,4%,.4);
border-image: @smallSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab {
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab:hover {
background: hsla(206,37%,4%,.2);
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab[selected] + tab:hover:active {
background: hsla(206,37%,4%,.4);
border-image: @solidSeparatorDark@ 1 1;
}
.theme-dark .devtools-sidebar-tabs tabs > tab[selected],
.theme-dark .devtools-sidebar-tabs tabs > tab[selected]:hover:active {
color: var(--theme-selection-color);
background: #1d4f73;
border-image: @solidSeparatorDark@ 1 1;
}
.theme-light .devtools-sidebar-tabs tabs > tab {
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs tabs > tab:hover {
background: #ddd;
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs tabs > tab:hover:active {
background: #ddd;
border-image: @smallSeparatorLight@ 1 1;
}
.theme-light .devtools-sidebar-tabs tabs > tab[selected] + tab {
border-image: @solidSeparatorLight@;
}
.theme-light .devtools-sidebar-tabs tabs > tab[selected] + tab:hover {
background: #ddd;
border-image: @solidSeparatorLight@;
}
.theme-light .devtools-sidebar-tabs tabs > tab[selected],
.theme-light .devtools-sidebar-tabs tabs > tab[selected]:hover:active {
color: var(--theme-selection-color);
background: #4c9ed9;
border-image: @solidSeparatorLight@;
}
/* Toolbox - moved from toolbox.css.

View File

@ -18,3 +18,10 @@ DSO_LDOPTS := -shared
# correctly. Note that the binary produced here is a host tool and doesn't need
# to be distributed.
MACOSX_DEPLOYMENT_TARGET :=
# Temporarily relax the requirements for libstdc++ symbol versions on static
# analysis plugin in order to use a recent clang by accepting libstdc++ from
# gcc 4.4.0 (GLIBCXX_3.4.11).
ifdef CHECK_STDCXX
CHECK_STDCXX = $(call CHECK_SYMBOLS,$(1),GLIBCXX,libstdc++,v[1] > 3 || (v[1] == 3 && v[2] == 4 && v[3] > 11))
endif

View File

@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/python2.7
# 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/.
@ -97,6 +97,8 @@ def build_one_stage_aux(stage_dir, llvm_source_dir, gcc_toolchain_dir):
configure_opts = ["--enable-optimized",
"--enable-targets=" + ",".join(targets),
"--disable-assertions",
"--disable-libedit",
"--with-python=/usr/local/bin/python2.7",
"--prefix=%s" % inst_dir,
"--with-gcc-toolchain=%s" % gcc_toolchain_dir,
"--disable-compiler-version-checks"]
@ -115,6 +117,7 @@ if __name__ == "__main__":
llvm_source_dir = source_dir + "/llvm"
clang_source_dir = source_dir + "/clang"
compiler_rt_source_dir = source_dir + "/compiler-rt"
libcxx_source_dir = source_dir + "/libcxx"
gcc_dir = "/tools/gcc-4.7.3-0moz1"
@ -132,15 +135,19 @@ if __name__ == "__main__":
llvm_repo = config["llvm_repo"]
clang_repo = config["clang_repo"]
compiler_repo = config["compiler_repo"]
libcxx_repo = config["libcxx_repo"]
if not os.path.exists(source_dir):
os.makedirs(source_dir)
svn_co(llvm_repo, llvm_source_dir, llvm_revision)
svn_co(clang_repo, clang_source_dir, llvm_revision)
svn_co(compiler_repo, compiler_rt_source_dir, llvm_revision)
svn_co(libcxx_repo, libcxx_source_dir, llvm_revision)
os.symlink("../../clang", llvm_source_dir + "/tools/clang")
os.symlink("../../compiler-rt",
llvm_source_dir + "/projects/compiler-rt")
os.symlink("../../libcxx",
llvm_source_dir + "/projects/libcxx")
for p in config.get("patches", {}).get(get_platform(), []):
patch(p, source_dir)
@ -153,12 +160,16 @@ if __name__ == "__main__":
if is_darwin():
extra_cflags = ""
extra_cxxflags = ""
extra_cxxflags = "-stdlib=libc++"
extra_cflags2 = ""
extra_cxxflags2 = "-stdlib=libc++"
cc = "/usr/bin/clang"
cxx = "/usr/bin/clang++"
else:
extra_cflags = "-static-libgcc"
extra_cxxflags = "-static-libgcc -static-libstdc++"
extra_cflags = ""
extra_cxxflags = ""
extra_cflags2 = "-static-libgcc"
extra_cxxflags2 = "-static-libgcc -static-libstdc++"
cc = gcc_dir + "/bin/gcc"
cxx = gcc_dir + "/bin/g++"
@ -167,12 +178,15 @@ if __name__ == "__main__":
else:
os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
build_one_stage({"CC": cc, "CXX": cxx}, stage1_dir, llvm_source_dir, gcc_dir)
build_one_stage(
{"CC": cc + " %s" % extra_cflags,
"CXX": cxx + " %s" % extra_cxxflags},
stage1_dir, llvm_source_dir, gcc_dir)
stage2_dir = build_dir + '/stage2'
build_one_stage(
{"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags,
"CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags},
{"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags2,
"CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
stage2_dir, llvm_source_dir, gcc_dir)
build_tar_package("tar", "clang.tar.bz2", stage2_dir, "clang")

View File

@ -1,11 +1,12 @@
{
"llvm_revision": "200213",
"llvm_revision": "241406",
"llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
"clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
"compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
"libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
"patches": {
"macosx64": ["llvm-debug-frame.patch"],
"linux64": ["llvm-debug-frame.patch", "no-sse-on-linux-trunk.patch"],
"linux32": ["llvm-debug-frame.patch", "no-sse-on-linux-trunk.patch"]
"linux64": ["llvm-debug-frame.patch"],
"linux32": ["llvm-debug-frame.patch"]
}
}

View File

@ -1,13 +1,13 @@
diff --git a/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index d6d4510..c488d4a 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -172,6 +172,8 @@ bool AsmPrinter::doInitialization(Module &M) {
OutStreamer.EmitFileDirective(M.getModuleIdentifier());
Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp
===================================================================
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (revision 226419)
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (working copy)
@@ -210,6 +210,8 @@
OutStreamer->EmitFileDirective(M.getModuleIdentifier());
}
+ OutStreamer.EmitCFISections(true, true);
+ OutStreamer->EmitCFISections(true, true);
+
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
for (GCModuleInfo::iterator I = MI->begin(), E = MI->end(); I != E; ++I)
for (auto &I : *MI)

View File

@ -1,13 +0,0 @@
Index: lib/Driver/Tools.cpp
===================================================================
--- a/clang/lib/Driver/Tools.cpp (revision 199443)
+++ b/clang/lib/Driver/Tools.cpp (working copy)
@@ -1226,7 +1226,7 @@
// All x86 devices running Android have core2 as their common
// denominator. This makes a better choice than pentium4.
if (Triple.getEnvironment() == llvm::Triple::Android)
- return "core2";
+ return "686";
// Everything else goes to x86-64 in 64-bit mode.
if (Is64Bit)

View File

@ -311,7 +311,7 @@ CONFIG_STATUS_DEPS := \
$(TOPSRCDIR)/nsprpub/configure \
$(TOPSRCDIR)/config/milestone.txt \
$(TOPSRCDIR)/browser/config/version.txt \
$(TOPSRCDIR)/browser/config/version_about.txt \
$(TOPSRCDIR)/browser/config/version_display.txt \
$(TOPSRCDIR)/build/virtualenv_packages.txt \
$(TOPSRCDIR)/python/mozbuild/mozbuild/virtualenv.py \
$(TOPSRCDIR)/testing/mozbase/packages.txt \

View File

@ -1959,14 +1959,14 @@ MOZILLA_SYMBOLVERSION=`$PYTHON $srcdir/python/mozbuild/mozbuild/milestone.py --t
dnl Get version of various core apps from the version files.
FIREFOX_VERSION=`cat $_topsrcdir/browser/config/version.txt`
FIREFOX_VERSION_ABOUT=`cat $_topsrcdir/browser/config/version_about.txt`
FIREFOX_VERSION_DISPLAY=`cat $_topsrcdir/browser/config/version_display.txt`
if test -z "$FIREFOX_VERSION"; then
AC_MSG_ERROR([FIREFOX_VERSION is unexpectedly blank.])
fi
if test -z "$FIREFOX_VERSION_ABOUT"; then
AC_MSG_ERROR([FIREFOX_VERSION_ABOUT is unexpectedly blank.])
if test -z "$FIREFOX_VERSION_DISPLAY"; then
AC_MSG_ERROR([FIREFOX_VERSION_DISPLAY is unexpectedly blank.])
fi
AC_DEFINE_UNQUOTED(MOZILLA_VERSION,"$MOZILLA_VERSION")
@ -8760,7 +8760,7 @@ AC_SUBST(MOZ_CHILD_PROCESS_BUNDLE)
# Mac Bundle name, Updater, Installer), it is typically used for nightly
# builds (e.g. Aurora for Firefox).
# - MOZ_APP_VERSION: Defines the application version number.
# - MOZ_APP_VERSION_ABOUT: Defines the application version number. Used
# - MOZ_APP_VERSION_DISPLAY: Defines the application version number. Used
# in the "About" window. If not set, defaults to MOZ_APP_VERSION.
# - MOZ_APP_NAME: Used for e.g. the binary program file name. If not set,
# defaults to a lowercase form of MOZ_APP_BASENAME.
@ -8780,8 +8780,8 @@ if test -z "$MOZ_APP_REMOTINGNAME"; then
MOZ_APP_REMOTINGNAME=$MOZ_APP_NAME
fi
if test -z "$MOZ_APP_VERSION_ABOUT"; then
MOZ_APP_VERSION_ABOUT=$MOZ_APP_VERSION
if test -z "$MOZ_APP_VERSION_DISPLAY"; then
MOZ_APP_VERSION_DISPLAY=$MOZ_APP_VERSION
fi
# For extensions and langpacks, we require a max version that is compatible
@ -8820,7 +8820,7 @@ AC_DEFINE_UNQUOTED(MOZ_APP_UA_NAME, "$MOZ_APP_UA_NAME")
AC_SUBST(MOZ_APP_UA_NAME)
AC_DEFINE_UNQUOTED(MOZ_APP_UA_VERSION, "$MOZ_APP_VERSION")
AC_SUBST(MOZ_APP_VERSION)
AC_SUBST(MOZ_APP_VERSION_ABOUT)
AC_SUBST(MOZ_APP_VERSION_DISPLAY)
AC_SUBST(MOZ_APP_MAXVERSION)
AC_DEFINE_UNQUOTED(FIREFOX_VERSION,$FIREFOX_VERSION)
AC_SUBST(FIREFOX_VERSION)

View File

@ -3217,11 +3217,6 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
if (mIsEncrypted) {
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->NotifyObservers(static_cast<nsIContent*>(this), "media-eme-metadataloaded", nullptr);
}
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
if (IsVideo() && HasVideo()) {
DispatchAsyncEvent(NS_LITERAL_STRING("resize"));

View File

@ -10,7 +10,6 @@
#include "VideoSegment.h"
#include "MediaQueue.h"
#include "MediaData.h"
#include "MediaInfo.h"
#include "SharedBuffer.h"
#include "VideoUtils.h"
@ -185,13 +184,32 @@ OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStr
aStream->AddListener(mListener);
}
DecodedStream::DecodedStream()
DecodedStream::DecodedStream(MediaQueue<AudioData>& aAudioQueue,
MediaQueue<VideoData>& aVideoQueue)
: mMonitor("DecodedStream::mMonitor")
, mPlaying(false)
, mAudioQueue(aAudioQueue)
, mVideoQueue(aVideoQueue)
{
//
}
void
DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mStartTime.isNothing()) {
mStartTime.emplace(aStartTime);
mInfo = aInfo;
}
}
void DecodedStream::StopPlayback()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mStartTime.reset();
}
void
DecodedStream::DestroyData()
{
@ -343,38 +361,8 @@ DecodedStream::SetPlaying(bool aPlaying)
}
}
bool
DecodedStream::HaveEnoughAudio(const MediaInfo& aInfo) const
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mData->mStreamInitialized && !mData->mHaveSentFinishAudio) {
MOZ_ASSERT(aInfo.HasAudio());
TrackID audioTrackId = aInfo.mAudio.mTrackId;
if (!mData->mStream->HaveEnoughBuffered(audioTrackId)) {
return false;
}
}
return true;
}
bool
DecodedStream::HaveEnoughVideo(const MediaInfo& aInfo) const
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (mData->mStreamInitialized && !mData->mHaveSentFinishVideo) {
MOZ_ASSERT(aInfo.HasVideo());
TrackID videoTrackId = aInfo.mVideo.mTrackId;
if (!mData->mStream->HaveEnoughBuffered(videoTrackId)) {
return false;
}
}
return true;
}
void
DecodedStream::InitTracks(int64_t aStartTime, const MediaInfo& aInfo)
DecodedStream::InitTracks()
{
GetReentrantMonitor().AssertCurrentThreadIn();
@ -384,20 +372,20 @@ DecodedStream::InitTracks(int64_t aStartTime, const MediaInfo& aInfo)
SourceMediaStream* sourceStream = mData->mStream;
if (aInfo.HasAudio()) {
TrackID audioTrackId = aInfo.mAudio.mTrackId;
if (mInfo.HasAudio()) {
TrackID audioTrackId = mInfo.mAudio.mTrackId;
AudioSegment* audio = new AudioSegment();
sourceStream->AddAudioTrack(audioTrackId, aInfo.mAudio.mRate, 0, audio,
sourceStream->AddAudioTrack(audioTrackId, mInfo.mAudio.mRate, 0, audio,
SourceMediaStream::ADDTRACK_QUEUED);
mData->mNextAudioTime = aStartTime;
mData->mNextAudioTime = mStartTime.ref();
}
if (aInfo.HasVideo()) {
TrackID videoTrackId = aInfo.mVideo.mTrackId;
if (mInfo.HasVideo()) {
TrackID videoTrackId = mInfo.mVideo.mTrackId;
VideoSegment* video = new VideoSegment();
sourceStream->AddTrack(videoTrackId, 0, video,
SourceMediaStream::ADDTRACK_QUEUED);
mData->mNextVideoTime = aStartTime;
mData->mNextVideoTime = mStartTime.ref();
}
sourceStream->FinishAddTracks();
@ -452,28 +440,25 @@ SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
}
void
DecodedStream::SendAudio(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<AudioData>& aQueue,
double aVolume, bool aIsSameOrigin)
DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
{
GetReentrantMonitor().AssertCurrentThreadIn();
if (!aInfo.HasAudio()) {
if (!mInfo.HasAudio()) {
return;
}
AudioSegment output;
uint32_t rate = aInfo.mAudio.mRate;
uint32_t rate = mInfo.mAudio.mRate;
nsAutoTArray<nsRefPtr<AudioData>,10> audio;
TrackID audioTrackId = aInfo.mAudio.mTrackId;
TrackID audioTrackId = mInfo.mAudio.mTrackId;
SourceMediaStream* sourceStream = mData->mStream;
// It's OK to hold references to the AudioData because AudioData
// is ref-counted.
aQueue.GetElementsAfter(mData->mNextAudioTime, &audio);
mAudioQueue.GetElementsAfter(mData->mNextAudioTime, &audio);
for (uint32_t i = 0; i < audio.Length(); ++i) {
SendStreamAudio(mData.get(), aStartTime, audio[i], &output, rate, aVolume);
SendStreamAudio(mData.get(), mStartTime.ref(), audio[i], &output, rate, aVolume);
}
if (!aIsSameOrigin) {
@ -487,7 +472,7 @@ DecodedStream::SendAudio(int64_t aStartTime,
sourceStream->AppendToTrack(audioTrackId, &output);
}
if (aQueue.IsFinished() && !mData->mHaveSentFinishAudio) {
if (mAudioQueue.IsFinished() && !mData->mHaveSentFinishAudio) {
sourceStream->EndTrack(audioTrackId);
mData->mHaveSentFinishAudio = true;
}
@ -520,25 +505,22 @@ ZeroDurationAtLastChunk(VideoSegment& aInput)
}
void
DecodedStream::SendVideo(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<VideoData>& aQueue,
bool aIsSameOrigin)
DecodedStream::SendVideo(bool aIsSameOrigin)
{
GetReentrantMonitor().AssertCurrentThreadIn();
if (!aInfo.HasVideo()) {
if (!mInfo.HasVideo()) {
return;
}
VideoSegment output;
TrackID videoTrackId = aInfo.mVideo.mTrackId;
TrackID videoTrackId = mInfo.mVideo.mTrackId;
nsAutoTArray<nsRefPtr<VideoData>, 10> video;
SourceMediaStream* sourceStream = mData->mStream;
// It's OK to hold references to the VideoData because VideoData
// is ref-counted.
aQueue.GetElementsAfter(mData->mNextVideoTime, &video);
mVideoQueue.GetElementsAfter(mData->mNextVideoTime, &video);
for (uint32_t i = 0; i < video.Length(); ++i) {
VideoData* v = video[i];
@ -580,7 +562,7 @@ DecodedStream::SendVideo(int64_t aStartTime,
sourceStream->AppendToTrack(videoTrackId, &output);
}
if (aQueue.IsFinished() && !mData->mHaveSentFinishVideo) {
if (mVideoQueue.IsFinished() && !mData->mHaveSentFinishVideo) {
if (mData->mEOSVideoCompensation) {
VideoSegment endSegment;
// Calculate the deviation clock time from DecodedStream.
@ -601,21 +583,21 @@ DecodedStream::SendVideo(int64_t aStartTime,
}
void
DecodedStream::AdvanceTracks(int64_t aStartTime, const MediaInfo& aInfo)
DecodedStream::AdvanceTracks()
{
GetReentrantMonitor().AssertCurrentThreadIn();
StreamTime endPosition = 0;
if (aInfo.HasAudio()) {
if (mInfo.HasAudio()) {
StreamTime audioEnd = mData->mStream->TicksToTimeRoundDown(
aInfo.mAudio.mRate, mData->mAudioFramesWritten);
mInfo.mAudio.mRate, mData->mAudioFramesWritten);
endPosition = std::max(endPosition, audioEnd);
}
if (aInfo.HasVideo()) {
if (mInfo.HasVideo()) {
StreamTime videoEnd = mData->mStream->MicrosecondsToStreamTimeRoundDown(
mData->mNextVideoTime - aStartTime);
mData->mNextVideoTime - mStartTime.ref());
endPosition = std::max(endPosition, videoEnd);
}
@ -625,21 +607,18 @@ DecodedStream::AdvanceTracks(int64_t aStartTime, const MediaInfo& aInfo)
}
bool
DecodedStream::SendData(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<AudioData>& aAudioQueue,
MediaQueue<VideoData>& aVideoQueue,
double aVolume, bool aIsSameOrigin)
DecodedStream::SendData(double aVolume, bool aIsSameOrigin)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
InitTracks(aStartTime, aInfo);
SendAudio(aStartTime, aInfo, aAudioQueue, aVolume, aIsSameOrigin);
SendVideo(aStartTime, aInfo, aVideoQueue, aIsSameOrigin);
AdvanceTracks(aStartTime, aInfo);
InitTracks();
SendAudio(aVolume, aIsSameOrigin);
SendVideo(aIsSameOrigin);
AdvanceTracks();
bool finished = (!aInfo.HasAudio() || aAudioQueue.IsFinished()) &&
(!aInfo.HasVideo() || aVideoQueue.IsFinished());
bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) &&
(!mInfo.HasVideo() || mVideoQueue.IsFinished());
if (finished && !mData->mHaveSentFinish) {
mData->mHaveSentFinish = true;
@ -650,17 +629,22 @@ DecodedStream::SendData(int64_t aStartTime,
}
CheckedInt64
DecodedStream::AudioEndTime(int64_t aStartTime, uint32_t aRate) const
DecodedStream::AudioEndTime() const
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return aStartTime + FramesToUsecs(mData->mAudioFramesWritten, aRate);
MOZ_ASSERT(mStartTime.isSome(), "Must be called after StartPlayback()");
return mStartTime.ref() +
FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate);
}
int64_t
DecodedStream::GetPosition() const
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
return mData->GetPosition();
// This is only called after MDSM starts playback. So mStartTime is
// guaranteed to be something.
MOZ_ASSERT(mStartTime.isSome());
return mStartTime.ref() + mData->GetPosition();
}
bool

View File

@ -9,17 +9,18 @@
#include "nsRefPtr.h"
#include "nsTArray.h"
#include "MediaInfo.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Maybe.h"
namespace mozilla {
class AudioData;
class VideoData;
class MediaInfo;
class AudioSegment;
class MediaStream;
class MediaInputPort;
@ -96,24 +97,26 @@ public:
class DecodedStream {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
public:
DecodedStream();
DecodedStream(MediaQueue<AudioData>& aAudioQueue,
MediaQueue<VideoData>& aVideoQueue);
// Mimic MDSM::StartAudioThread.
// Must be called before any calls to SendData().
void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo);
// Mimic MDSM::StopAudioThread.
void StopPlayback();
void DestroyData();
void RecreateData();
void Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
void Remove(MediaStream* aStream);
void SetPlaying(bool aPlaying);
bool HaveEnoughAudio(const MediaInfo& aInfo) const;
bool HaveEnoughVideo(const MediaInfo& aInfo) const;
CheckedInt64 AudioEndTime(int64_t aStartTime, uint32_t aRate) const;
CheckedInt64 AudioEndTime() const;
int64_t GetPosition() const;
bool IsFinished() const;
// Return true if stream is finished.
bool SendData(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<AudioData>& aAudioQueue,
MediaQueue<VideoData>& aVideoQueue,
double aVolume, bool aIsSameOrigin);
bool SendData(double aVolume, bool aIsSameOrigin);
protected:
virtual ~DecodedStream() {}
@ -123,18 +126,10 @@ private:
void RecreateData(MediaStreamGraph* aGraph);
void Connect(OutputStreamData* aStream);
nsTArray<OutputStreamData>& OutputStreams();
void InitTracks(int64_t aStartTime, const MediaInfo& aInfo);
void AdvanceTracks(int64_t aStartTime, const MediaInfo& aInfo);
void SendAudio(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<AudioData>& aQueue,
double aVolume, bool aIsSameOrigin);
void SendVideo(int64_t aStartTime,
const MediaInfo& aInfo,
MediaQueue<VideoData>& aQueue,
bool aIsSameOrigin);
void InitTracks();
void AdvanceTracks();
void SendAudio(double aVolume, bool aIsSameOrigin);
void SendVideo(bool aIsSameOrigin);
UniquePtr<DecodedStreamData> mData;
// Data about MediaStreams that are being fed by the decoder.
@ -150,6 +145,11 @@ private:
mutable ReentrantMonitor mMonitor;
bool mPlaying;
Maybe<int64_t> mStartTime;
MediaInfo mInfo;
MediaQueue<AudioData>& mAudioQueue;
MediaQueue<VideoData>& mVideoQueue;
};
} // namespace mozilla

View File

@ -159,6 +159,19 @@ void MediaDecoder::NotifyOwnerActivityChanged()
StartDormantTimer();
}
bool
MediaDecoder::IsHeuristicDormantSupported() const
{
return
#if defined(MOZ_EME) && defined(RELEASE_BUILD)
// We disallow dormant for encrypted media on Beta and Release until
// bug 1181864 is fixed.
mInfo &&
!mInfo->IsEncrypted() &&
#endif
mIsHeuristicDormantSupported;
}
void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
{
MOZ_ASSERT(NS_IsMainThread());
@ -174,9 +187,11 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
}
DECODER_LOG("UpdateDormantState aTimeout=%d aActivity=%d mIsDormant=%d "
"ownerActive=%d ownerHidden=%d mIsHeuristicDormant=%d mPlayState=%s",
"ownerActive=%d ownerHidden=%d mIsHeuristicDormant=%d "
"mPlayState=%s encrypted=%s",
aDormantTimeout, aActivity, mIsDormant, mOwner->IsActive(),
mOwner->IsHidden(), mIsHeuristicDormant, PlayStateStr());
mOwner->IsHidden(), mIsHeuristicDormant, PlayStateStr(),
(!mInfo ? "Unknown" : (mInfo->IsEncrypted() ? "1" : "0")));
bool prevDormant = mIsDormant;
mIsDormant = false;
@ -188,10 +203,11 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
mIsDormant = true;
}
#endif
// Try to enable dormant by idle heuristic, when the owner is hidden.
bool prevHeuristicDormant = mIsHeuristicDormant;
mIsHeuristicDormant = false;
if (mIsHeuristicDormantSupported && mOwner->IsHidden()) {
if (IsHeuristicDormantSupported() && mOwner->IsHidden()) {
if (aDormantTimeout && !aActivity &&
(mPlayState == PLAY_STATE_PAUSED || IsEnded())) {
// Enable heuristic dormant
@ -252,7 +268,7 @@ void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
void MediaDecoder::StartDormantTimer()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mIsHeuristicDormantSupported) {
if (!IsHeuristicDormantSupported()) {
return;
}

View File

@ -1057,6 +1057,9 @@ protected:
virtual void CallSeek(const SeekTarget& aTarget);
// Returns true if heuristic dormant is supported.
bool IsHeuristicDormantSupported() const;
MediaPromiseRequestHolder<SeekPromise> mSeekRequest;
// True when seeking or otherwise moving the play position around in

View File

@ -212,7 +212,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mFragmentEndTime(-1),
mReader(aReader),
mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)"),
mStreamStartTime(0),
mAudioEndTime(-1),
mDecodedAudioEndTime(-1),
mVideoFrameEndTime(-1),
@ -244,7 +243,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mSentLoadedMetadataEvent(false),
mSentFirstFrameLoadedEvent(false),
mSentPlaybackEndedEvent(false),
mDecodedStream(new DecodedStream())
mDecodedStream(new DecodedStream(mAudioQueue, mVideoQueue))
{
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
@ -371,13 +370,10 @@ void MediaDecoderStateMachine::SendStreamData()
AssertCurrentThreadInMonitor();
MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
bool finished = mDecodedStream->SendData(
mStreamStartTime, mInfo, AudioQueue(), VideoQueue(),
mVolume, mDecoder->IsSameOriginMedia());
bool finished = mDecodedStream->SendData(mVolume, mDecoder->IsSameOriginMedia());
if (mInfo.HasAudio()) {
CheckedInt64 playedUsecs = mDecodedStream->AudioEndTime(
mStreamStartTime, mInfo.mAudio.mRate);
CheckedInt64 playedUsecs = mDecodedStream->AudioEndTime();
if (playedUsecs.isValid()) {
OnAudioEndTimeUpdate(playedUsecs.value());
}
@ -414,7 +410,10 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
return false;
}
return !mAudioCaptured || mDecodedStream->HaveEnoughAudio(mInfo);
// We don't have to check SourceMediaStream::HaveEnoughBuffered() in the
// case of stream-capture for MDSM will ensure buffering level is high enough
// for playback speed at 1x at which the DecodedStream is playing.
return true;
}
bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
@ -426,7 +425,7 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
return false;
}
return !mAudioCaptured || mDecodedStream->HaveEnoughVideo(mInfo);
return true;
}
bool
@ -1092,6 +1091,12 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
nsresult rv = StartAudioThread();
NS_ENSURE_SUCCESS_VOID(rv);
// Tell DecodedStream to start playback with specified start time and media
// info. This is consistent with how we create AudioSink in StartAudioThread().
if (mAudioCaptured) {
mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
}
mDecoder->GetReentrantMonitor().NotifyAll();
DispatchDecodeTasksIfNeeded();
}
@ -2144,7 +2149,6 @@ MediaDecoderStateMachine::SeekCompleted()
} else {
newCurrentTime = video ? video->mTime : seekTime;
}
mStreamStartTime = newCurrentTime;
mPlayDuration = newCurrentTime;
mDecoder->StartProgressUpdates();
@ -2419,6 +2423,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
}
StopAudioThread();
mDecodedStream->StopPlayback();
if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING &&
!mSentPlaybackEndedEvent)
@ -2460,10 +2465,10 @@ MediaDecoderStateMachine::Reset()
// outside of the decoder monitor while we are clearing the queue and causes
// crash for no samples to be popped.
StopAudioThread();
mDecodedStream->StopPlayback();
mVideoFrameEndTime = -1;
mDecodedVideoEndTime = -1;
mStreamStartTime = 0;
mAudioEndTime = -1;
mDecodedAudioEndTime = -1;
mAudioCompleted = false;
@ -2600,7 +2605,7 @@ int64_t MediaDecoderStateMachine::GetStreamClock() const
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
return mStreamStartTime + mDecodedStream->GetPosition();
return mDecodedStream->GetPosition();
}
int64_t MediaDecoderStateMachine::GetVideoStreamPosition(TimeStamp aTimeStamp) const
@ -3134,11 +3139,15 @@ void MediaDecoderStateMachine::DispatchAudioCaptured()
if (!self->mAudioCaptured) {
// Stop the audio sink if it's running.
self->StopAudioThread();
self->mStreamStartTime = self->GetMediaTime();
// Reset mAudioEndTime which will be updated as we send audio data to
// stream. Otherwise it will remain -1 if we don't have audio.
self->mAudioEndTime = -1;
self->mAudioCaptured = true;
// Start DecodedStream if we are already playing. Otherwise it will be
// handled in MaybeStartPlayback().
if (self->IsPlaying()) {
self->mDecodedStream->StartPlayback(self->GetMediaTime(), self->mInfo);
}
self->ScheduleStateMachine();
}
});

View File

@ -1066,10 +1066,6 @@ protected:
public:
AbstractCanonical<int64_t>* CanonicalCurrentPosition() { return &mCurrentPosition; }
protected:
// The presentation time of the first audio/video frame that is sent to the
// media stream.
int64_t mStreamStartTime;
// The end time of the last audio frame that's been pushed onto the audio
// hardware in microseconds. This will approximately be the end time of the
// audio stream, unless another frame is pushed to the hardware.

View File

@ -1545,4 +1545,10 @@ function setMediaTestsPrefs(callback, extraPrefs) {
SpecialPowers.pushPrefEnv({"set": prefs}, callback);
}
// B2G emulator and Android 2.3 are condidered slow platforms
function isSlowPlatform() {
return SpecialPowers.Services.appinfo.name == "B2G" ||
navigator.userAgent.indexOf("Mobile") != -1 && androidVersion == 10;
}
SimpleTest.requestFlakyTimeout("untriaged");

View File

@ -12,6 +12,11 @@
<pre id="test">
<script class="testbody" type="text/javascript">
//longer timeout for slow platforms
if (isSlowPlatform()) {
SimpleTest.requestLongerTimeout(1.5);
}
var manager = new MediaTestManager;
// Fragment parameters to try

View File

@ -10,11 +10,6 @@
<pre id="test">
<script class="testbody" type="text/javascript">
function isSlowPlatform() {
return SpecialPowers.Services.appinfo.name == "B2G" ||
navigator.userAgent.indexOf("Mobile") != -1 && androidVersion == 10;
}
// longer timeout for slow platforms
if (isSlowPlatform()) {
SimpleTest.requestLongerTimeout(3);

View File

@ -10,11 +10,6 @@
<pre id="test">
<script class="testbody" type="text/javascript">
function isSlowPlatform() {
return SpecialPowers.Services.appinfo.name == "B2G" ||
navigator.userAgent.indexOf("Mobile") != -1 && androidVersion == 10;
}
// longer timeout for slow platforms
if (isSlowPlatform()) {
SimpleTest.requestLongerTimeout(3);

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -3996,19 +3996,7 @@ FireControllerChangeOnMatchingDocument(nsISupports* aKey,
return PL_DHASH_NEXT;
}
static PLDHashOperator
ClaimMatchingClients(nsISupportsHashKey* aKey, void* aData)
{
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
ServiceWorkerRegistrationInfo* workerRegistration =
static_cast<ServiceWorkerRegistrationInfo*>(aData);
nsCOMPtr<nsIDocument> document = do_QueryInterface(aKey->GetKey());
swm->MaybeClaimClient(document, workerRegistration);
return PL_DHASH_NEXT;
}
} // namespace
} // anonymous namespace
void
ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
@ -4076,7 +4064,11 @@ ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal,
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
mAllDocuments.EnumerateEntries(ClaimMatchingClients, registration);
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
for (auto iter = mAllDocuments.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIDocument> document = do_QueryInterface(iter.Get()->GetKey());
swm->MaybeClaimClient(document, registration);
}
return NS_OK;
}

View File

@ -82,240 +82,6 @@ ServiceWorkerManagerService::UnregisterActor(ServiceWorkerManagerParent* aParent
mAgents.RemoveEntry(aParent);
}
namespace {
struct MOZ_STACK_CLASS RegistrationData final
{
RegistrationData(ServiceWorkerRegistrationData& aData,
uint64_t aParentID)
: mData(aData)
, mParentID(aParentID)
#ifdef DEBUG
, mParentFound(false)
#endif
{
MOZ_COUNT_CTOR(RegistrationData);
}
~RegistrationData()
{
MOZ_COUNT_DTOR(RegistrationData);
}
const ServiceWorkerRegistrationData& mData;
const uint64_t mParentID;
#ifdef DEBUG
bool mParentFound;
#endif
};
PLDHashOperator
RegistrationEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
{
AssertIsOnBackgroundThread();
auto* data = static_cast<RegistrationData*>(aPtr);
ServiceWorkerManagerParent* parent = aKey->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != data->mParentID) {
unused << parent->SendNotifyRegister(data->mData);
#ifdef DEBUG
} else {
data->mParentFound = true;
#endif
}
return PL_DHASH_NEXT;
}
struct MOZ_STACK_CLASS SoftUpdateData final
{
SoftUpdateData(const OriginAttributes& aOriginAttributes,
const nsAString& aScope,
uint64_t aParentID)
: mOriginAttributes(aOriginAttributes)
, mScope(aScope)
, mParentID(aParentID)
#ifdef DEBUG
, mParentFound(false)
#endif
{
MOZ_COUNT_CTOR(SoftUpdateData);
}
~SoftUpdateData()
{
MOZ_COUNT_DTOR(SoftUpdateData);
}
const OriginAttributes& mOriginAttributes;
const nsString mScope;
const uint64_t mParentID;
#ifdef DEBUG
bool mParentFound;
#endif
};
PLDHashOperator
SoftUpdateEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
{
AssertIsOnBackgroundThread();
auto* data = static_cast<SoftUpdateData*>(aPtr);
ServiceWorkerManagerParent* parent = aKey->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != data->mParentID) {
unused <<parent->SendNotifySoftUpdate(data->mOriginAttributes,
data->mScope);
#ifdef DEBUG
} else {
data->mParentFound = true;
#endif
}
return PL_DHASH_NEXT;
}
struct MOZ_STACK_CLASS UnregisterData final
{
UnregisterData(const PrincipalInfo& aPrincipalInfo,
const nsAString& aScope,
uint64_t aParentID)
: mPrincipalInfo(aPrincipalInfo)
, mScope(aScope)
, mParentID(aParentID)
#ifdef DEBUG
, mParentFound(false)
#endif
{
MOZ_COUNT_CTOR(UnregisterData);
}
~UnregisterData()
{
MOZ_COUNT_DTOR(UnregisterData);
}
const PrincipalInfo mPrincipalInfo;
const nsString mScope;
const uint64_t mParentID;
#ifdef DEBUG
bool mParentFound;
#endif
};
PLDHashOperator
UnregisterEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
{
AssertIsOnBackgroundThread();
auto* data = static_cast<UnregisterData*>(aPtr);
ServiceWorkerManagerParent* parent = aKey->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != data->mParentID) {
unused << parent->SendNotifyUnregister(data->mPrincipalInfo, data->mScope);
#ifdef DEBUG
} else {
data->mParentFound = true;
#endif
}
return PL_DHASH_NEXT;
}
struct MOZ_STACK_CLASS RemoveAllData final
{
explicit RemoveAllData(uint64_t aParentID)
: mParentID(aParentID)
#ifdef DEBUG
, mParentFound(false)
#endif
{
MOZ_COUNT_CTOR(RemoveAllData);
}
~RemoveAllData()
{
MOZ_COUNT_DTOR(RemoveAllData);
}
const uint64_t mParentID;
#ifdef DEBUG
bool mParentFound;
#endif
};
PLDHashOperator
RemoveAllEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
{
AssertIsOnBackgroundThread();
auto* data = static_cast<RemoveAllData*>(aPtr);
ServiceWorkerManagerParent* parent = aKey->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != data->mParentID) {
unused << parent->SendNotifyRemoveAll();
#ifdef DEBUG
} else {
data->mParentFound = true;
#endif
}
return PL_DHASH_NEXT;
}
struct MOZ_STACK_CLASS RemoveData final
{
RemoveData(const nsACString& aHost,
uint64_t aParentID)
: mHost(aHost)
, mParentID(aParentID)
#ifdef DEBUG
, mParentFound(false)
#endif
{
MOZ_COUNT_CTOR(RemoveData);
}
~RemoveData()
{
MOZ_COUNT_DTOR(RemoveData);
}
const nsCString mHost;
const uint64_t mParentID;
#ifdef DEBUG
bool mParentFound;
#endif
};
PLDHashOperator
RemoveEnumerator(nsPtrHashKey<ServiceWorkerManagerParent>* aKey, void* aPtr)
{
AssertIsOnBackgroundThread();
auto* data = static_cast<RemoveData*>(aPtr);
ServiceWorkerManagerParent* parent = aKey->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != data->mParentID) {
unused << parent->SendNotifyRemove(data->mHost);
#ifdef DEBUG
} else {
data->mParentFound = true;
#endif
}
return PL_DHASH_NEXT;
}
} // namespace
void
ServiceWorkerManagerService::PropagateRegistration(
uint64_t aParentID,
@ -323,11 +89,22 @@ ServiceWorkerManagerService::PropagateRegistration(
{
AssertIsOnBackgroundThread();
RegistrationData data(aData, aParentID);
mAgents.EnumerateEntries(RegistrationEnumerator, &data);
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerManagerParent* parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
unused << parent->SendNotifyRegister(aData);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(data.mParentFound);
MOZ_ASSERT(parentFound);
#endif
}
@ -339,11 +116,24 @@ ServiceWorkerManagerService::PropagateSoftUpdate(
{
AssertIsOnBackgroundThread();
SoftUpdateData data(aOriginAttributes, aScope, aParentID);
mAgents.EnumerateEntries(SoftUpdateEnumerator, &data);
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerManagerParent* parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
nsString scope(aScope);
unused << parent->SendNotifySoftUpdate(aOriginAttributes,
scope);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(data.mParentFound);
MOZ_ASSERT(parentFound);
#endif
}
@ -364,11 +154,23 @@ ServiceWorkerManagerService::PropagateUnregister(
service->UnregisterServiceWorker(aPrincipalInfo,
NS_ConvertUTF16toUTF8(aScope));
UnregisterData data(aPrincipalInfo, aScope, aParentID);
mAgents.EnumerateEntries(UnregisterEnumerator, &data);
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerManagerParent* parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
nsString scope(aScope);
unused << parent->SendNotifyUnregister(aPrincipalInfo, scope);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(data.mParentFound);
MOZ_ASSERT(parentFound);
#endif
}
@ -378,11 +180,23 @@ ServiceWorkerManagerService::PropagateRemove(uint64_t aParentID,
{
AssertIsOnBackgroundThread();
RemoveData data(aHost, aParentID);
mAgents.EnumerateEntries(RemoveEnumerator, &data);
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerManagerParent* parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
nsCString host(aHost);
unused << parent->SendNotifyRemove(host);
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(data.mParentFound);
MOZ_ASSERT(parentFound);
#endif
}
@ -397,11 +211,22 @@ ServiceWorkerManagerService::PropagateRemoveAll(uint64_t aParentID)
service->RemoveAll();
RemoveAllData data(aParentID);
mAgents.EnumerateEntries(RemoveAllEnumerator, &data);
DebugOnly<bool> parentFound = false;
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
ServiceWorkerManagerParent* parent = iter.Get()->GetKey();
MOZ_ASSERT(parent);
if (parent->ID() != aParentID) {
unused << parent->SendNotifyRemoveAll();
#ifdef DEBUG
} else {
parentFound = true;
#endif
}
}
#ifdef DEBUG
MOZ_ASSERT(data.mParentFound);
MOZ_ASSERT(parentFound);
#endif
}

View File

@ -231,6 +231,7 @@ if CONFIG['GNU_CC']:
'-Wno-incompatible-pointer-types',
'-Wno-tautological-compare',
'-Wno-tautological-constant-out-of-range-compare',
'-Wno-error=uninitialized',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':

View File

@ -154,8 +154,7 @@ static void LayerManagerUserDataDestroy(void *data)
* 1) Construction: layers are created, inserted, removed and have
* properties set on them in this phase.
* BeginTransaction and BeginTransactionWithTarget start a transaction in
* the Construction phase. When the client has finished constructing the layer
* tree, it should call EndConstruction() to enter the drawing phase.
* the Construction phase.
* 2) Drawing: PaintedLayers are rendered into in this phase, in tree
* order. When the client has finished drawing into the PaintedLayers, it should
* call EndTransaction to complete the transaction.

View File

@ -54,6 +54,43 @@ struct TabSizes
size_t other;
};
// These are the measurements used by Servo. It's important that this is a POD
// struct so that Servo can have a parallel |repr(C)| Rust equivalent.
struct ServoSizes
{
enum Kind {
GCHeapUsed,
GCHeapUnused,
GCHeapAdmin,
GCHeapDecommitted,
MallocHeap,
NonHeap,
Ignore
};
ServoSizes() { mozilla::PodZero(this); }
void add(Kind kind, size_t n) {
switch (kind) {
case GCHeapUsed: gcHeapUsed += n; break;
case GCHeapUnused: gcHeapUnused += n; break;
case GCHeapAdmin: gcHeapAdmin += n; break;
case GCHeapDecommitted: gcHeapDecommitted += n; break;
case MallocHeap: mallocHeap += n; break;
case NonHeap: nonHeap += n; break;
case Ignore: /* do nothing */ break;
default: MOZ_CRASH("bad ServoSizes kind");
}
}
size_t gcHeapUsed;
size_t gcHeapUnused;
size_t gcHeapAdmin;
size_t gcHeapDecommitted;
size_t mallocHeap;
size_t nonHeap;
};
} // namespace JS
namespace js {
@ -92,24 +129,24 @@ struct CStringHashPolicy
// then use the following macros to transform those lists into the required
// methods.
//
// - The |tabKind| value is used when measuring TabSizes.
//
// - The |servoKind| value is used when measuring ServoSizes and also for
// the various sizeOfLiveGCThings() methods.
//
// In some classes, one or more of the macro arguments aren't used. We use '_'
// for those.
//
#define DECL_SIZE(kind, gc, mSize) size_t mSize;
#define ZERO_SIZE(kind, gc, mSize) mSize(0),
#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize),
#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize;
#define SUB_OTHER_SIZE(kind, gc, mSize) MOZ_ASSERT(mSize >= other.mSize); \
mSize -= other.mSize;
#define ADD_SIZE_TO_N(kind, gc, mSize) n += mSize;
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc) ? mSize : 0;
#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize);
// Used to annotate which size_t fields measure live GC things and which don't.
enum {
NotLiveGCThing = false,
IsLiveGCThing = true
};
#define DECL_SIZE(tabKind, servoKind, mSize) size_t mSize;
#define ZERO_SIZE(tabKind, servoKind, mSize) mSize(0),
#define COPY_OTHER_SIZE(tabKind, servoKind, mSize) mSize(other.mSize),
#define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
#define SUB_OTHER_SIZE(tabKind, servoKind, mSize) MOZ_ASSERT(mSize >= other.mSize); \
mSize -= other.mSize;
#define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) n += (ServoSizes::servoKind == ServoSizes::GCHeapUsed) ? mSize : 0;
#define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) sizes->add(JS::TabSizes::tabKind, mSize);
#define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) sizes->add(JS::ServoSizes::servoKind, mSize);
} // namespace js
@ -118,21 +155,21 @@ namespace JS {
struct ClassInfo
{
#define FOR_EACH_SIZE(macro) \
macro(Objects, IsLiveGCThing, objectsGCHeap) \
macro(Objects, NotLiveGCThing, objectsMallocHeapSlots) \
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsNonAsmJS) \
macro(Objects, NotLiveGCThing, objectsMallocHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, objectsNonHeapElementsAsmJS) \
macro(Objects, NotLiveGCThing, objectsNonHeapElementsMapped) \
macro(Objects, NotLiveGCThing, objectsNonHeapCodeAsmJS) \
macro(Objects, NotLiveGCThing, objectsMallocHeapMisc) \
macro(Objects, GCHeapUsed, objectsGCHeap) \
macro(Objects, MallocHeap, objectsMallocHeapSlots) \
macro(Objects, MallocHeap, objectsMallocHeapElementsNonAsmJS) \
macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
macro(Objects, NonHeap, objectsNonHeapElementsAsmJS) \
macro(Objects, NonHeap, objectsNonHeapElementsMapped) \
macro(Objects, NonHeap, objectsNonHeapCodeAsmJS) \
macro(Objects, MallocHeap, objectsMallocHeapMisc) \
\
macro(Other, IsLiveGCThing, shapesGCHeapTree) \
macro(Other, IsLiveGCThing, shapesGCHeapDict) \
macro(Other, IsLiveGCThing, shapesGCHeapBase) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \
macro(Other, NotLiveGCThing, shapesMallocHeapTreeKids) \
macro(Other, GCHeapUsed, shapesGCHeapTree) \
macro(Other, GCHeapUsed, shapesGCHeapDict) \
macro(Other, GCHeapUsed, shapesGCHeapBase) \
macro(Other, MallocHeap, shapesMallocHeapTreeTables) \
macro(Other, MallocHeap, shapesMallocHeapDictTables) \
macro(Other, MallocHeap, shapesMallocHeapTreeKids)
ClassInfo()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -168,6 +205,10 @@ struct ClassInfo
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -201,17 +242,21 @@ struct NotableClassInfo : public ClassInfo
struct CodeSizes
{
#define FOR_EACH_SIZE(macro) \
macro(_, _, ion) \
macro(_, _, baseline) \
macro(_, _, regexp) \
macro(_, _, other) \
macro(_, _, unused)
macro(_, NonHeap, ion) \
macro(_, NonHeap, baseline) \
macro(_, NonHeap, regexp) \
macro(_, NonHeap, other) \
macro(_, NonHeap, unused)
CodeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -221,24 +266,30 @@ struct CodeSizes
// Data for tracking GC memory usage.
struct GCSizes
{
// |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
// because we don't consider the nursery to be part of the GC heap.
#define FOR_EACH_SIZE(macro) \
macro(_, _, marker) \
macro(_, _, nurseryCommitted) \
macro(_, _, nurseryDecommitted) \
macro(_, _, nurseryMallocedBuffers) \
macro(_, _, storeBufferVals) \
macro(_, _, storeBufferCells) \
macro(_, _, storeBufferSlots) \
macro(_, _, storeBufferWholeCells) \
macro(_, _, storeBufferRelocVals) \
macro(_, _, storeBufferRelocCells) \
macro(_, _, storeBufferGenerics)
macro(_, MallocHeap, marker) \
macro(_, NonHeap, nurseryCommitted) \
macro(_, NonHeap, nurseryDecommitted) \
macro(_, MallocHeap, nurseryMallocedBuffers) \
macro(_, MallocHeap, storeBufferVals) \
macro(_, MallocHeap, storeBufferCells) \
macro(_, MallocHeap, storeBufferSlots) \
macro(_, MallocHeap, storeBufferWholeCells) \
macro(_, MallocHeap, storeBufferRelocVals) \
macro(_, MallocHeap, storeBufferRelocCells) \
macro(_, MallocHeap, storeBufferGenerics)
GCSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -253,10 +304,10 @@ struct GCSizes
struct StringInfo
{
#define FOR_EACH_SIZE(macro) \
macro(Strings, IsLiveGCThing, gcHeapLatin1) \
macro(Strings, IsLiveGCThing, gcHeapTwoByte) \
macro(Strings, NotLiveGCThing, mallocHeapLatin1) \
macro(Strings, NotLiveGCThing, mallocHeapTwoByte)
macro(Strings, GCHeapUsed, gcHeapLatin1) \
macro(Strings, GCHeapUsed, gcHeapTwoByte) \
macro(Strings, MallocHeap, mallocHeapLatin1) \
macro(Strings, MallocHeap, mallocHeapTwoByte)
StringInfo()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -290,6 +341,10 @@ struct StringInfo
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
uint32_t numCopies; // How many copies of the string have we seen?
@ -326,9 +381,9 @@ struct NotableStringInfo : public StringInfo
struct ScriptSourceInfo
{
#define FOR_EACH_SIZE(macro) \
macro(_, _, compressed) \
macro(_, _, uncompressed) \
macro(_, _, misc)
macro(_, MallocHeap, compressed) \
macro(_, MallocHeap, uncompressed) \
macro(_, MallocHeap, misc)
ScriptSourceInfo()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -345,6 +400,10 @@ struct ScriptSourceInfo
numScripts--;
}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
bool isNotable() const {
static const size_t NotabilityThreshold = 16 * 1024;
size_t n = 0;
@ -387,16 +446,16 @@ struct NotableScriptSourceInfo : public ScriptSourceInfo
struct RuntimeSizes
{
#define FOR_EACH_SIZE(macro) \
macro(_, _, object) \
macro(_, _, atomsTable) \
macro(_, _, contexts) \
macro(_, _, dtoa) \
macro(_, _, temporary) \
macro(_, _, interpreterStack) \
macro(_, _, mathCache) \
macro(_, _, uncompressedSourceCache) \
macro(_, _, compressedSourceSet) \
macro(_, _, scriptData) \
macro(_, MallocHeap, object) \
macro(_, MallocHeap, atomsTable) \
macro(_, MallocHeap, contexts) \
macro(_, MallocHeap, dtoa) \
macro(_, MallocHeap, temporary) \
macro(_, MallocHeap, interpreterStack) \
macro(_, MallocHeap, mathCache) \
macro(_, MallocHeap, uncompressedSourceCache) \
macro(_, MallocHeap, compressedSourceSet) \
macro(_, MallocHeap, scriptData)
RuntimeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -417,14 +476,21 @@ struct RuntimeSizes
js_delete(allScriptSources);
}
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
scriptSourceInfo.addToServoSizes(sizes);
code.addToServoSizes(sizes);
gc.addToServoSizes(sizes);
}
// The script source measurements in |scriptSourceInfo| are initially for
// all script sources. At the end, if the measurement granularity is
// FineGrained, we subtract the measurements of the notable script sources
// and move them into |notableScriptSources|.
FOR_EACH_SIZE(DECL_SIZE)
ScriptSourceInfo scriptSourceInfo;
CodeSizes code;
GCSizes gc;
ScriptSourceInfo scriptSourceInfo;
CodeSizes code;
GCSizes gc;
typedef js::HashMap<const char*, ScriptSourceInfo,
js::CStringHashPolicy,
@ -440,25 +506,25 @@ struct RuntimeSizes
#undef FOR_EACH_SIZE
};
struct GCThingSizes
struct UnusedGCThingSizes
{
#define FOR_EACH_SIZE(macro) \
macro(_, _, object) \
macro(_, _, script) \
macro(_, _, lazyScript) \
macro(_, _, shape) \
macro(_, _, baseShape) \
macro(_, _, objectGroup) \
macro(_, _, string) \
macro(_, _, symbol) \
macro(_, _, jitcode) \
macro(Other, GCHeapUnused, object) \
macro(Other, GCHeapUnused, script) \
macro(Other, GCHeapUnused, lazyScript) \
macro(Other, GCHeapUnused, shape) \
macro(Other, GCHeapUnused, baseShape) \
macro(Other, GCHeapUnused, objectGroup) \
macro(Other, GCHeapUnused, string) \
macro(Other, GCHeapUnused, symbol) \
macro(Other, GCHeapUnused, jitcode) \
GCThingSizes()
UnusedGCThingSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
dummy()
{}
GCThingSizes(GCThingSizes&& other)
UnusedGCThingSizes(UnusedGCThingSizes&& other)
: FOR_EACH_SIZE(COPY_OTHER_SIZE)
dummy()
{}
@ -475,11 +541,11 @@ struct GCThingSizes
case JS::TraceKind::LazyScript: lazyScript += n; break;
case JS::TraceKind::ObjectGroup: objectGroup += n; break;
default:
MOZ_CRASH("Bad trace kind for GCThingSizes");
MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
}
}
void addSizes(const GCThingSizes& other) {
void addSizes(const UnusedGCThingSizes& other) {
FOR_EACH_SIZE(ADD_OTHER_SIZE)
}
@ -489,6 +555,14 @@ struct GCThingSizes
return n;
}
void addToTabSizes(JS::TabSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
}
void addToServoSizes(JS::ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
}
FOR_EACH_SIZE(DECL_SIZE)
int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
@ -498,15 +572,15 @@ struct GCThingSizes
struct ZoneStats
{
#define FOR_EACH_SIZE(macro) \
macro(Other, IsLiveGCThing, symbolsGCHeap) \
macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \
macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \
macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \
macro(Other, IsLiveGCThing, jitCodesGCHeap) \
macro(Other, IsLiveGCThing, objectGroupsGCHeap) \
macro(Other, NotLiveGCThing, objectGroupsMallocHeap) \
macro(Other, NotLiveGCThing, typePool) \
macro(Other, NotLiveGCThing, baselineStubsOptimized) \
macro(Other, GCHeapUsed, symbolsGCHeap) \
macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \
macro(Other, GCHeapUsed, lazyScriptsGCHeap) \
macro(Other, MallocHeap, lazyScriptsMallocHeap) \
macro(Other, GCHeapUsed, jitCodesGCHeap) \
macro(Other, GCHeapUsed, objectGroupsGCHeap) \
macro(Other, MallocHeap, objectGroupsMallocHeap) \
macro(Other, MallocHeap, typePool) \
macro(Other, MallocHeap, baselineStubsOptimized)
ZoneStats()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -558,16 +632,23 @@ struct ZoneStats
void addToTabSizes(JS::TabSizes* sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
sizes->add(JS::TabSizes::Other, unusedGCThings.totalSize());
unusedGCThings.addToTabSizes(sizes);
stringInfo.addToTabSizes(sizes);
}
void addToServoSizes(JS::ServoSizes *sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
unusedGCThings.addToServoSizes(sizes);
stringInfo.addToServoSizes(sizes);
}
// These string measurements are initially for all strings. At the end,
// if the measurement granularity is FineGrained, we subtract the
// measurements of the notable script sources and move them into
// |notableStrings|.
FOR_EACH_SIZE(DECL_SIZE)
GCThingSizes unusedGCThings;
UnusedGCThingSizes unusedGCThings;
StringInfo stringInfo;
void* extra; // This field can be used by embedders.
@ -588,25 +669,29 @@ struct ZoneStats
struct CompartmentStats
{
// We assume that |objectsPrivate| is on the malloc heap, but it's not
// actually guaranteed. But for Servo, at least, it's a moot point because
// it doesn't provide an ObjectPrivateVisitor so the value will always be
// zero.
#define FOR_EACH_SIZE(macro) \
macro(Private, NotLiveGCThing, objectsPrivate) \
macro(Other, IsLiveGCThing, scriptsGCHeap) \
macro(Other, NotLiveGCThing, scriptsMallocHeapData) \
macro(Other, NotLiveGCThing, baselineData) \
macro(Other, NotLiveGCThing, baselineStubsFallback) \
macro(Other, NotLiveGCThing, ionData) \
macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \
macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \
macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \
macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \
macro(Other, NotLiveGCThing, compartmentObject) \
macro(Other, NotLiveGCThing, compartmentTables) \
macro(Other, NotLiveGCThing, innerViewsTable) \
macro(Other, NotLiveGCThing, lazyArrayBuffersTable) \
macro(Other, NotLiveGCThing, objectMetadataTable) \
macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \
macro(Other, NotLiveGCThing, regexpCompartment) \
macro(Other, NotLiveGCThing, savedStacksSet)
macro(Private, MallocHeap, objectsPrivate) \
macro(Other, GCHeapUsed, scriptsGCHeap) \
macro(Other, MallocHeap, scriptsMallocHeapData) \
macro(Other, MallocHeap, baselineData) \
macro(Other, MallocHeap, baselineStubsFallback) \
macro(Other, MallocHeap, ionData) \
macro(Other, MallocHeap, typeInferenceTypeScripts) \
macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \
macro(Other, MallocHeap, typeInferenceArrayTypeTables) \
macro(Other, MallocHeap, typeInferenceObjectTypeTables) \
macro(Other, MallocHeap, compartmentObject) \
macro(Other, MallocHeap, compartmentTables) \
macro(Other, MallocHeap, innerViewsTable) \
macro(Other, MallocHeap, lazyArrayBuffersTable) \
macro(Other, MallocHeap, objectMetadataTable) \
macro(Other, MallocHeap, crossCompartmentWrappersTable) \
macro(Other, MallocHeap, regexpCompartment) \
macro(Other, MallocHeap, savedStacksSet)
CompartmentStats()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -658,12 +743,18 @@ struct CompartmentStats
classInfo.addToTabSizes(sizes);
}
void addToServoSizes(ServoSizes *sizes) const {
MOZ_ASSERT(isTotals);
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
classInfo.addToServoSizes(sizes);
}
// The class measurements in |classInfo| are initially for all classes. At
// the end, if the measurement granularity is FineGrained, we subtract the
// measurements of the notable classes and move them into |notableClasses|.
FOR_EACH_SIZE(DECL_SIZE)
ClassInfo classInfo;
void* extra; // This field can be used by embedders.
ClassInfo classInfo;
void* extra; // This field can be used by embedders.
typedef js::HashMap<const char*, ClassInfo,
js::CStringHashPolicy,
@ -682,13 +773,18 @@ typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
struct RuntimeStats
{
// |gcHeapChunkTotal| is ignored because it's the sum of all the other
// values. |gcHeapGCThings| is ignored because it's the sum of some of the
// values from the zones and compartments. Both of those values are not
// reported directly, but are just present for sanity-checking other
// values.
#define FOR_EACH_SIZE(macro) \
macro(_, _, gcHeapChunkTotal) \
macro(_, _, gcHeapDecommittedArenas) \
macro(_, _, gcHeapUnusedChunks) \
macro(_, _, gcHeapUnusedArenas) \
macro(_, _, gcHeapChunkAdmin) \
macro(_, _, gcHeapGCThings) \
macro(_, Ignore, gcHeapChunkTotal) \
macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
macro(_, GCHeapUnused, gcHeapUnusedChunks) \
macro(_, GCHeapUnused, gcHeapUnusedArenas) \
macro(_, GCHeapAdmin, gcHeapChunkAdmin) \
macro(_, Ignore, gcHeapGCThings)
explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
: FOR_EACH_SIZE(ZERO_SIZE)
@ -709,7 +805,7 @@ struct RuntimeStats
// - unused bytes
// - rtStats.gcHeapUnusedChunks (empty chunks)
// - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
// - rtStats.zTotals.unusedGCThings (empty GC thing slots within non-empty arenas)
// - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas)
// - used bytes
// - rtStats.gcHeapChunkAdmin
// - rtStats.zTotals.gcHeapArenaAdmin
@ -721,6 +817,11 @@ struct RuntimeStats
// it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
// multiple of the chunk size, which is good.
void addToServoSizes(ServoSizes *sizes) const {
FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
runtime.addToServoSizes(sizes);
}
FOR_EACH_SIZE(DECL_SIZE)
RuntimeSizes runtime;
@ -774,6 +875,10 @@ extern JS_PUBLIC_API(bool)
AddSizeOfTab(JSRuntime* rt, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
ObjectPrivateVisitor* opv, TabSizes* sizes);
extern JS_PUBLIC_API(bool)
AddServoSizeOf(JSRuntime *rt, mozilla::MallocSizeOf mallocSizeOf,
ObjectPrivateVisitor *opv, ServoSizes *sizes);
} // namespace JS
#undef DECL_SIZE

View File

@ -284,6 +284,7 @@ Function Properties of the `Debugger.Memory.prototype` Object
"timestamp": <i>timestamp</i>,
"frame": <i>allocationSite</i>,
"class": <i>className</i>,
"size": <i>byteSize</i>,
}
</pre>
@ -302,6 +303,9 @@ Function Properties of the `Debugger.Memory.prototype` Object
`[[Class]]` property, for example "Array", "Date", "RegExp", or (most
commonly) "Object".
* *byteSize* is the size of the newly tenured object (within the tenured
heap, not the nursery) in bytes.
When `trackingTenurePromotions` is `false`, `drainTenurePromotionsLog()`
throws an `Error`.

View File

@ -140,6 +140,7 @@ Zone::logPromotionsToTenured()
return;
auto now = JS_GetCurrentEmbedderTime();
JSRuntime* rt = runtimeFromAnyThread();
for (auto** dbgp = dbgs->begin(); dbgp != dbgs->end(); dbgp++) {
if (!(*dbgp)->isEnabled() || !(*dbgp)->isTrackingTenurePromotions())
@ -147,7 +148,7 @@ Zone::logPromotionsToTenured()
for (auto range = awaitingTenureLogging.all(); !range.empty(); range.popFront()) {
if ((*dbgp)->isDebuggee(range.front()->compartment()))
(*dbgp)->logTenurePromotion(*range.front(), now);
(*dbgp)->logTenurePromotion(rt, *range.front(), now);
}
}

View File

@ -0,0 +1,48 @@
// Test basic sizes of entries in the tenure promotions log. Does not attempt to
// test the actual object sizes in depth, as heap-analysis/byteSize-of-object.js
// does that pretty thoroughly, just that they are present where expected.
const root = newGlobal();
const dbg = root.dbg = new Debugger();
const wrappedRoot = dbg.addDebuggee(root)
root.eval(`
this.tests = [];
function Ctor() { }
function AsmModule(stdlib, foreign, heap) {
"use asm";
function test() {
return 5|0;
}
return { test: test };
}
const buf = new ArrayBuffer(1024*8);
(function immediate() {
this.dbg.memory.trackingTenurePromotions = true;
this.tests.push( new Object() );
this.tests.push( new Array() );
this.tests.push( new Uint8Array(256) );
this.tests.push( (function () { return arguments; }()) );
this.tests.push( AsmModule(this, {}, buf) );
this.tests.push( /2manyproblemz/g );
this.tests.push( [1,2,3][Symbol.iterator]() );
this.tests.push( Error() );
this.tests.push( new Ctor );
this.tests.push( {} );
this.tests.push( new Date );
this.tests.push( [1,2,3] );
}).call(this);
minorgc();
`);
const promotions = dbg.memory.drainTenurePromotionsLog();
print(uneval(promotions));
assertEq(promotions.every(p => p.size >= 1), true);

View File

@ -7,6 +7,7 @@
#include "vm/Debugger-inl.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ScopeExit.h"
#include "jscntxt.h"
#include "jscompartment.h"
@ -46,6 +47,7 @@ using JS::dbg::Builder;
using js::frontend::IsIdentifier;
using mozilla::ArrayLength;
using mozilla::DebugOnly;
using mozilla::MakeScopeExit;
using mozilla::Maybe;
using mozilla::UniquePtr;
@ -1699,10 +1701,18 @@ Debugger::isDebuggee(const JSCompartment* compartment) const
return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
}
Debugger::TenurePromotionsEntry::TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when)
: className(obj.getClass()->name),
when(when),
frame(getObjectAllocationSite(obj)),
size(JS::ubi::Node(&obj).size(rt->debuggerMallocSizeOf))
{ }
void
Debugger::logTenurePromotion(JSObject& obj, double when)
Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when)
{
auto* entry = js_new<TenurePromotionsEntry>(obj, when);
auto* entry = js_new<TenurePromotionsEntry>(rt, obj, when);
if (!entry)
CrashAtUnhandlableOOM("Debugger::logTenurePromotion");
@ -3298,75 +3308,86 @@ Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
/*
* For global to become this js::Debugger's debuggee:
*
* 1. global must be in this->debuggees,
* 2. this js::Debugger must be in global->getDebuggers(),
* 1. this js::Debugger must be in global->getDebuggers(),
* 2. global must be in this->debuggees,
* 3. it must be in zone->getDebuggers(),
* 4. JSCompartment::isDebuggee()'s bit must be set, and
* 4. the debuggee's zone must be in this->debuggeeZones,
* 5. if we are tracking allocations, the SavedStacksMetadataCallback must be
* installed for this compartment.
* installed for this compartment, and
* 6. JSCompartment::isDebuggee()'s bit must be set.
*
* All five indications must be kept consistent.
* All six indications must be kept consistent.
*/
bool inDebuggees = false;
bool inGlobalDebuggers = false;
bool inZoneDebuggers = false;
bool inDebuggeeZones = false;
AutoCompartment ac(cx, global);
Zone* zone = global->zone();
// (1)
auto* globalDebuggers = GlobalObject::getOrCreateDebuggers(cx, global);
if (!globalDebuggers)
goto error;
if (!globalDebuggers->append(this))
goto oom;
inGlobalDebuggers = true;
if (!debuggees.put(global))
goto oom;
inDebuggees = true;
Zone::DebuggerVector* zoneDebuggers;
if (!debuggeeZones.has(zone)) {
zoneDebuggers = zone->getOrCreateDebuggers(cx);
if (!zoneDebuggers)
goto error;
if (!zoneDebuggers->append(this))
goto oom;
inZoneDebuggers = true;
if (!debuggeeZones.put(zone))
goto oom;
inDebuggeeZones = true;
return false;
if (!globalDebuggers->append(this)) {
ReportOutOfMemory(cx);
return false;
}
auto globalDebuggersGuard = MakeScopeExit([&] {
globalDebuggers->popBack();
});
// (2)
if (!debuggees.put(global)) {
ReportOutOfMemory(cx);
return false;
}
auto debuggeesGuard = MakeScopeExit([&] {
debuggees.remove(global);
});
bool addingZoneRelation = !debuggeeZones.has(zone);
// (3)
auto* zoneDebuggers = zone->getOrCreateDebuggers(cx);
if (!zoneDebuggers)
return false;
if (addingZoneRelation && !zoneDebuggers->append(this)) {
ReportOutOfMemory(cx);
return false;
}
auto zoneDebuggersGuard = MakeScopeExit([&] {
if (addingZoneRelation)
zoneDebuggers->popBack();
});
// (4)
if (addingZoneRelation && !debuggeeZones.put(zone)) {
ReportOutOfMemory(cx);
return false;
}
auto debuggeeZonesGuard = MakeScopeExit([&] {
if (addingZoneRelation)
debuggeeZones.remove(zone);
});
// (5)
if (trackingAllocationSites && !Debugger::addAllocationsTracking(cx, *global))
goto error;
return false;
auto allocationsTrackingGuard = MakeScopeExit([&] {
if (trackingAllocationSites)
Debugger::removeAllocationsTracking(*global);
});
// (6)
debuggeeCompartment->setIsDebuggee();
debuggeeCompartment->updateDebuggerObservesAsmJS();
if (observesAllExecution() && !ensureExecutionObservabilityOfCompartment(cx, debuggeeCompartment))
goto error;
return false;
globalDebuggersGuard.release();
debuggeesGuard.release();
zoneDebuggersGuard.release();
debuggeeZonesGuard.release();
allocationsTrackingGuard.release();
return true;
oom:
ReportOutOfMemory(cx);
// Fall through...
error:
// Maintain consistency on error.
if (inGlobalDebuggers)
globalDebuggers->popBack();
if (inDebuggees)
debuggees.remove(global);
if (inZoneDebuggers)
zoneDebuggers->popBack();
if (inDebuggeeZones)
debuggeeZones.remove(zone);
return false;
}
bool

View File

@ -269,7 +269,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
return enabled;
}
void logTenurePromotion(JSObject& obj, double when);
void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
static JSObject* getObjectAllocationSite(JSObject& obj);
private:
@ -286,15 +286,12 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
struct TenurePromotionsEntry : public mozilla::LinkedListElement<TenurePromotionsEntry>
{
TenurePromotionsEntry(JSObject& obj, double when)
: className(obj.getClass()->name),
when(when),
frame(getObjectAllocationSite(obj))
{ }
TenurePromotionsEntry(JSRuntime* rt, JSObject& obj, double when);
const char* className;
double when;
RelocatablePtrObject frame;
size_t size;
};
using TenurePromotionsLog = mozilla::LinkedList<TenurePromotionsEntry>;

View File

@ -387,6 +387,10 @@ DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp
if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
return false;
RootedValue sizeValue(cx, NumberValue(entry->size));
if (!DefineProperty(cx, obj, cx->names().size, sizeValue))
return false;
result->setDenseElement(i, ObjectValue(*obj));
// Pop the front queue entry, and delete it immediately, so that

View File

@ -693,9 +693,9 @@ FindNotableScriptSources(JS::RuntimeSizes& runtime)
return true;
}
JS_PUBLIC_API(bool)
JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisitor* opv,
bool anonymize)
static bool
CollectRuntimeStatsHelper(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisitor* opv,
bool anonymize, IterateCellCallback statsCellCallback)
{
if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
return false;
@ -720,7 +720,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
StatsZoneCallback,
StatsCompartmentCallback,
StatsArenaCallback,
StatsCellCallback<FineGrained>);
statsCellCallback);
// Take the "explicit/js/runtime/" measurements.
rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
@ -728,7 +728,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
if (!FindNotableScriptSources(rtStats->runtime))
return false;
ZoneStatsVector& zs = rtStats->zoneStatsVector;
JS::ZoneStatsVector& zs = rtStats->zoneStatsVector;
ZoneStats& zTotals = rtStats->zTotals;
// We don't look for notable strings for zTotals. So we first sum all the
@ -743,7 +743,7 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
MOZ_ASSERT(!zTotals.allStrings);
CompartmentStatsVector& cs = rtStats->compartmentStatsVector;
JS::CompartmentStatsVector& cs = rtStats->compartmentStatsVector;
CompartmentStats& cTotals = rtStats->cTotals;
// As with the zones, we sum all compartments first, and then get the
@ -790,6 +790,13 @@ JS::CollectRuntimeStats(JSRuntime* rt, RuntimeStats* rtStats, ObjectPrivateVisit
return true;
}
JS_PUBLIC_API(bool)
JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv,
bool anonymize)
{
return CollectRuntimeStatsHelper(rt, rtStats, opv, anonymize, StatsCellCallback<FineGrained>);
}
JS_PUBLIC_API(size_t)
JS::SystemCompartmentCount(JSRuntime* rt)
{
@ -820,26 +827,26 @@ JS::PeakSizeOfTemporary(const JSRuntime* rt)
namespace JS {
class SimpleJSRuntimeStats : public JS::RuntimeStats
{
public:
explicit SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
: JS::RuntimeStats(mallocSizeOf)
{}
virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats)
override
{}
virtual void initExtraCompartmentStats(
JSCompartment* c, JS::CompartmentStats* cStats) override
{}
};
JS_PUBLIC_API(bool)
AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv,
TabSizes* sizes)
{
class SimpleJSRuntimeStats : public JS::RuntimeStats
{
public:
explicit SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf)
: JS::RuntimeStats(mallocSizeOf)
{}
virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats)
override
{}
virtual void initExtraCompartmentStats(
JSCompartment* c, JS::CompartmentStats* cStats) override
{}
};
SimpleJSRuntimeStats rtStats(mallocSizeOf);
JS::Zone* zone = GetObjectZone(obj);
@ -855,8 +862,10 @@ AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
StatsClosure closure(&rtStats, opv, /* anonymize = */ false);
if (!closure.init())
return false;
IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
StatsCompartmentCallback, StatsArenaCallback,
IterateZoneCompartmentsArenasCells(rt, zone, &closure,
StatsZoneCallback,
StatsCompartmentCallback,
StatsArenaCallback,
StatsCellCallback<CoarseGrained>);
MOZ_ASSERT(rtStats.zoneStatsVector.length() == 1);
@ -874,5 +883,38 @@ AddSizeOfTab(JSRuntime* rt, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
return true;
}
JS_PUBLIC_API(bool)
AddServoSizeOf(JSRuntime *rt, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
ServoSizes *sizes)
{
SimpleJSRuntimeStats rtStats(mallocSizeOf);
// No need to anonymize because the results will be aggregated.
if (!CollectRuntimeStatsHelper(rt, &rtStats, opv, /* anonymize = */ false,
StatsCellCallback<CoarseGrained>))
return false;
#ifdef DEBUG
size_t gcHeapTotalOriginal = sizes->gcHeapUsed +
sizes->gcHeapUnused +
sizes->gcHeapAdmin +
sizes->gcHeapDecommitted;
#endif
rtStats.addToServoSizes(sizes);
rtStats.zTotals.addToServoSizes(sizes);
rtStats.cTotals.addToServoSizes(sizes);
#ifdef DEBUG
size_t gcHeapTotal = sizes->gcHeapUsed +
sizes->gcHeapUnused +
sizes->gcHeapAdmin +
sizes->gcHeapDecommitted;
MOZ_ASSERT(rtStats.gcHeapChunkTotal == gcHeapTotal - gcHeapTotalOriginal);
#endif
return true;
}
} // namespace JS

View File

@ -226,7 +226,7 @@ public:
* sets the container layer children to layers which together render
* the contents of the display list. It reuses existing layers from
* the retained layer manager if possible.
* aContainer may be null, in which case we construct a root layer.
* aContainerItem may be null, in which case we construct a root layer.
* This gets called by display list code. It calls BuildLayer on the
* items in the display list, making items with their own layers
* children of the new container, and assigning all other items to
@ -479,7 +479,7 @@ public:
* longer than the transaction.
*
* Updates the geometry, frame list and clip.
* For items within a PaintedLayer, a geometry object must be specifed to retain
* For items within a PaintedLayer, a geometry object must be specified to retain
* until the next transaction.
*
*/

View File

@ -1018,7 +1018,7 @@ protected:
* class represents an entity that can be drawn on the screen, e.g., a
* frame's CSS background, or a frame's text string.
*
* nsDisplayListItems can be containers --- i.e., they can perform hit testing
* nsDisplayItems can be containers --- i.e., they can perform hit testing
* and painting by recursively traversing a list of child items.
*
* These are arena-allocated during display list construction. A typical
@ -1615,8 +1615,8 @@ public:
}
/**
* Append a new item to the top of the list. If the item is null we return
* NS_ERROR_OUT_OF_MEMORY. The intended usage is AppendNewToTop(new ...);
* Append a new item to the top of the list. The intended usage is
* AppendNewToTop(new ...);
*/
void AppendNewToTop(nsDisplayItem* aItem) {
if (aItem) {
@ -1625,8 +1625,8 @@ public:
}
/**
* Append a new item to the bottom of the list. If the item is null we return
* NS_ERROR_OUT_OF_MEMORY. The intended usage is AppendNewToBottom(new ...);
* Append a new item to the bottom of the list. The intended usage is
* AppendNewToBottom(new ...);
*/
void AppendNewToBottom(nsDisplayItem* aItem) {
if (aItem) {

View File

@ -1751,40 +1751,6 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent,
return NS_OK;
}
int32_t numOptions = GetNumberOfOptions();
if (numOptions < 1)
return NS_ERROR_FAILURE;
nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(aMouseEvent, this);
// If the event coordinate is above the first option frame, then target the
// first option frame
nsRefPtr<dom::HTMLOptionElement> firstOption = GetOption(0);
NS_ASSERTION(firstOption, "Can't find first option that's supposed to be there");
nsIFrame* optionFrame = firstOption->GetPrimaryFrame();
if (optionFrame) {
nsPoint ptInOptionFrame = pt - optionFrame->GetOffsetTo(this);
if (ptInOptionFrame.y < 0 && ptInOptionFrame.x >= 0 &&
ptInOptionFrame.x < optionFrame->GetSize().width) {
aCurIndex = 0;
return NS_OK;
}
}
nsRefPtr<dom::HTMLOptionElement> lastOption = GetOption(numOptions - 1);
// If the event coordinate is below the last option frame, then target the
// last option frame
NS_ASSERTION(lastOption, "Can't find last option that's supposed to be there");
optionFrame = lastOption->GetPrimaryFrame();
if (optionFrame) {
nsPoint ptInOptionFrame = pt - optionFrame->GetOffsetTo(this);
if (ptInOptionFrame.y >= optionFrame->GetSize().height && ptInOptionFrame.x >= 0 &&
ptInOptionFrame.x < optionFrame->GetSize().width) {
aCurIndex = numOptions - 1;
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}

View File

@ -125,19 +125,14 @@ FontFaceSet::FontFaceSet(nsPIDOMWindow* aWindow, nsIDocument* aDocument)
mUserFontSet = new UserFontSet(this);
}
static PLDHashOperator DestroyIterator(nsPtrHashKey<nsFontFaceLoader>* aKey,
void* aUserArg)
{
aKey->GetKey()->Cancel();
return PL_DHASH_REMOVE;
}
FontFaceSet::~FontFaceSet()
{
MOZ_COUNT_DTOR(FontFaceSet);
Disconnect();
mLoaders.EnumerateEntries(DestroyIterator, nullptr);
for (auto it = mLoaders.Iter(); !it.Done(); it.Next()) {
it.Get()->GetKey()->Cancel();
}
}
JSObject*

View File

@ -45,22 +45,6 @@ ImageLoader::SetAnimationModeEnumerator(nsISupports* aKey, FrameSet* aValue,
return PL_DHASH_NEXT;
}
static PLDHashOperator
ClearImageHashSet(nsPtrHashKey<ImageLoader::Image>* aKey, void* aClosure)
{
nsIDocument* doc = static_cast<nsIDocument*>(aClosure);
ImageLoader::Image* image = aKey->GetKey();
imgIRequest* request = image->mRequests.GetWeak(doc);
if (request) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
image->mRequests.Remove(doc);
return PL_DHASH_REMOVE;
}
void
ImageLoader::DropDocumentReference()
{
@ -68,7 +52,17 @@ ImageLoader::DropDocumentReference()
// on the document being null) as that means the presshell has already
// been destroyed, and it also calls ClearFrames when it is destroyed.
ClearFrames(GetPresContext());
mImages.EnumerateEntries(&ClearImageHashSet, mDocument);
for (auto it = mImages.Iter(); !it.Done(); it.Next()) {
ImageLoader::Image* image = it.Get()->GetKey();
imgIRequest* request = image->mRequests.GetWeak(mDocument);
if (request) {
request->CancelAndForgetObserver(NS_BINDING_ABORTED);
}
image->mRequests.Remove(mDocument);
}
mImages.Clear();
mDocument = nullptr;
}

View File

@ -659,30 +659,6 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
(aFrame->Properties().Get(FilterProperty()));
}
static PLDHashOperator
GatherEnumerator(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg)
{
nsTArray<nsSVGRenderingObserver*>* array =
static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
array->AppendElement(aEntry->GetKey());
return PL_DHASH_REMOVE;
}
static PLDHashOperator
GatherEnumeratorForReflow(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg)
{
if (!aEntry->GetKey()->ObservesReflow()) {
return PL_DHASH_NEXT;
}
nsTArray<nsSVGRenderingObserver*>* array =
static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
array->AppendElement(aEntry->GetKey());
return PL_DHASH_REMOVE;
}
void
nsSVGRenderingObserverList::InvalidateAll()
{
@ -691,8 +667,10 @@ nsSVGRenderingObserverList::InvalidateAll()
nsAutoTArray<nsSVGRenderingObserver*,10> observers;
// The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
mObservers.EnumerateEntries(GatherEnumerator, &observers);
for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
observers.AppendElement(it.Get()->GetKey());
}
mObservers.Clear();
for (uint32_t i = 0; i < observers.Length(); ++i) {
observers[i]->InvalidateViaReferencedElement();
@ -707,8 +685,13 @@ nsSVGRenderingObserverList::InvalidateAllForReflow()
nsAutoTArray<nsSVGRenderingObserver*,10> observers;
// The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
mObservers.EnumerateEntries(GatherEnumeratorForReflow, &observers);
for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
nsSVGRenderingObserver* obs = it.Get()->GetKey();
if (obs->ObservesReflow()) {
observers.AppendElement(obs);
it.Remove();
}
}
for (uint32_t i = 0; i < observers.Length(); ++i) {
observers[i]->InvalidateViaReferencedElement();
@ -720,8 +703,10 @@ nsSVGRenderingObserverList::RemoveAll()
{
nsAutoTArray<nsSVGRenderingObserver*,10> observers;
// The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
mObservers.EnumerateEntries(GatherEnumerator, &observers);
for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
observers.AppendElement(it.Get()->GetKey());
}
mObservers.Clear();
// Our list is now cleared. We need to notify the observers we've removed,
// so they can update their state & remove themselves as mutation-observers.

View File

@ -623,19 +623,14 @@ nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
#endif
}
static PLDHashOperator CheckForeignObjectInvalidatedArea(nsPtrHashKey<nsSVGForeignObjectFrame>* aEntry, void* aData)
{
nsRegion* region = static_cast<nsRegion*>(aData);
region->Or(*region, aEntry->GetKey()->GetInvalidRegion());
return PL_DHASH_NEXT;
}
nsRegion
nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
{
nsRegion result;
if (mForeignObjectHash && mForeignObjectHash->Count()) {
mForeignObjectHash->EnumerateEntries(CheckForeignObjectInvalidatedArea, &result);
for (auto it = mForeignObjectHash->Iter(); !it.Done(); it.Next()) {
result.Or(result, it.Get()->GetKey()->GetInvalidRegion());
}
}
return result;
}

View File

@ -96,7 +96,7 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
}
public void hide() {
if (!mInflated) {
if (!mInflated || getVisibility() == View.GONE) {
// There's nothing to hide yet.
return;
}
@ -104,8 +104,13 @@ public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnC
// Always clear the Find string, primarily for privacy.
mFindText.setText("");
// Only close the IMM if its EditText is the one with focus.
if (mFindText.isFocused()) {
getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
}
// Close the FIPB / FindHelper state.
setVisibility(GONE);
getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Closed", null));
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -267,7 +267,7 @@ public class CommandProcessor {
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
// Create a Notification.
final int icon = R.drawable.icon;
final int icon = R.drawable.flat_icon;
String notificationTitle = context.getString(R.string.sync_new_tab);
if (title != null) {
notificationTitle = notificationTitle.concat(": " + title);

View File

@ -9,7 +9,7 @@ Cu.import("resource://gre/modules/Services.jsm");
function init() {
// Include the build date and a warning about Telemetry
// if this is an "a#" (nightly or aurora) build
#expand const version = "__MOZ_APP_VERSION_ABOUT__";
#expand const version = "__MOZ_APP_VERSION_DISPLAY__";
if (/a\d+$/.test(version)) {
let buildID = Services.appinfo.appBuildID;
let buildDate = buildID.slice(0, 4) + "-" + buildID.slice(4, 6) + "-" + buildID.slice(6, 8);

View File

@ -24,7 +24,7 @@
<body dir="&locale.dir;">
<div id="header">
<div id="wordmark"></div>
#expand <p id="version">__MOZ_APP_VERSION_ABOUT__</p>
#expand <p id="version">__MOZ_APP_VERSION_DISPLAY__</p>
</div>
<div id="banner">

View File

@ -7,7 +7,7 @@
DEFINES['AB_CD'] = CONFIG['MOZ_UI_LOCALE']
DEFINES['PACKAGE'] = 'browser'
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_VERSION_ABOUT'] = CONFIG['MOZ_APP_VERSION_ABOUT']
DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
JAR_MANIFESTS += ['jar.mn']

View File

@ -92,7 +92,6 @@ class CommandAction(argparse.Action):
elif values:
command = values[0].lower()
args = values[1:]
if command == 'help':
if args and args[0] not in ['-h', '--help']:
# Make sure args[0] is indeed a command.
@ -102,8 +101,17 @@ class CommandAction(argparse.Action):
sys.exit(0)
elif '-h' in args or '--help' in args:
# -h or --help is in the command arguments.
self._handle_command_help(parser, command)
sys.exit(0)
if '--' in args:
# -- is in command arguments
if '-h' in args[:args.index('--')] or '--help' in args[:args.index('--')]:
# Honor -h or --help only if it appears before --
self._handle_main_help(parser, command)
sys.exit(0)
else:
self._handle_main_help(parser, command)
sys.exit(0)
else:
raise NoCommandError()

View File

@ -12,7 +12,6 @@ config = {
"--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
"--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
"--quiet", "--log-raw=%(raw_log_file)s", "--screenshot-on-fail",
"--total-chunks=16",
],
},
"mochitest-gl": {

View File

@ -13,7 +13,6 @@ config = {
"--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
"--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
"--quiet", "--log-raw=%(raw_log_file)s", "--screenshot-on-fail",
"--total-chunks=16",
],
},
"mochitest-gl": {

View File

@ -10,6 +10,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Troubleshoot.jsm");
Cu.import("resource://gre/modules/ResetProfile.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
@ -37,7 +38,7 @@ let snapshotFormatters = {
$("application-box").textContent = data.name;
$("useragent-box").textContent = data.userAgent;
$("supportLink").href = data.supportURL;
let version = data.version;
let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
if (data.vendor)
version += " (" + data.vendor + ")";
$("version-box").textContent = version;

View File

@ -189,7 +189,7 @@ function runTests()
synthesizeMouse(document.getElementById("option3"), 2, 2, { type: "mousedown" });
synthesizeMouse(document.getElementById("option3"), 2, 1000, { type: "mousemove" });
var select = document.getElementById("select");
is(select.selectedIndex, 9, "scroll select");
is(select.selectedIndex, 2, "scroll select");
synthesizeMouse(document.getElementById("select"), 2, 2, { type: "mouseup" });
synthesizeMouse(custom, 2, 2, { type: "mousedown" });

View File

@ -355,25 +355,45 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
function SourceMapConsumer_fromSourceMap(aSourceMap) {
var smc = Object.create(BasicSourceMapConsumer.prototype);
smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
smc.sourceRoot = aSourceMap._sourceRoot;
smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
smc.sourceRoot);
smc.file = aSourceMap._file;
smc.__generatedMappings = aSourceMap._mappings.toArray().slice();
smc.__originalMappings = aSourceMap._mappings.toArray().slice().sort();
// Because we are modifying the entries (by converting string sources and
// names to indices into the sources and names ArraySets), we have to make
// a copy of the entry or else bad things happen. Shared mutable state
// strikes again! See github issue #191.
smc.__generatedMappings.forEach(function (m) {
if (m.source !== null) {
m.source = smc._sources.indexOf(m.source);
var generatedMappings = aSourceMap._mappings.toArray().slice();
var destGeneratedMappings = smc.__generatedMappings = [];
var destOriginalMappings = smc.__originalMappings = [];
if (m.name !== null) {
m.name = smc._names.indexOf(m.name);
for (var i = 0, length = generatedMappings.length; i < length; i++) {
var srcMapping = generatedMappings[i];
var destMapping = new Mapping;
destMapping.generatedLine = srcMapping.generatedLine;
destMapping.generatedColumn = srcMapping.generatedColumn;
if (srcMapping.source) {
destMapping.source = sources.indexOf(srcMapping.source);
destMapping.originalLine = srcMapping.originalLine;
destMapping.originalColumn = srcMapping.originalColumn;
if (srcMapping.name) {
destMapping.name = names.indexOf(srcMapping.name);
}
destOriginalMappings.push(destMapping);
}
});
destGeneratedMappings.push(destMapping);
}
quickSort(smc.__originalMappings, util.compareByOriginalPositions);
return smc;
};
@ -507,7 +527,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
}
}
quickSort(generatedMappings, util.compareByGeneratedPositions);
quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated);
this.__generatedMappings = generatedMappings;
quickSort(originalMappings, util.compareByOriginalPositions);
@ -597,7 +617,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
this._generatedMappings,
"generatedLine",
"generatedColumn",
util.compareByGeneratedPositions,
util.compareByGeneratedPositionsDeflated,
util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
);
@ -1066,7 +1086,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou
};
};
quickSort(this.__generatedMappings, util.compareByGeneratedPositions);
quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
quickSort(this.__originalMappings, util.compareByOriginalPositions);
};
@ -1355,15 +1375,15 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
exports.compareByOriginalPositions = compareByOriginalPositions;
/**
* Comparator between two mappings where the generated positions are
* compared.
* Comparator between two mappings with deflated source and name indices where
* the generated positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same generated line and column, but different
* source/name/original line and column the same. Useful when searching for a
* mapping with a stubbed out mapping.
*/
function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
@ -1391,7 +1411,53 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require,
return mappingA.name - mappingB.name;
};
exports.compareByGeneratedPositions = compareByGeneratedPositions;
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
function strcmp(aStr1, aStr2) {
if (aStr1 === aStr2) {
return 0;
}
if (aStr1 > aStr2) {
return 1;
}
return -1;
}
/**
* Comparator between two mappings with inflated source and name strings where
* the generated positions are compared.
*/
function compareByGeneratedPositionsInflated(mappingA, mappingB) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0) {
return cmp;
}
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0) {
return cmp;
}
return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
});
/* -*- Mode: js; js-indent-level: 2; -*- */
@ -2238,7 +2304,6 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
var mapping;
var mappings = this._mappings.toArray();
for (var i = 0, len = mappings.length; i < len; i++) {
mapping = mappings[i];
@ -2251,7 +2316,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
}
else {
if (i > 0) {
if (!util.compareByGeneratedPositions(mapping, mappings[i - 1])) {
if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) {
continue;
}
result += ',';
@ -2360,7 +2425,7 @@ define('source-map/mapping-list', ['require', 'exports', 'module' , 'source-map
var columnA = mappingA.generatedColumn;
var columnB = mappingB.generatedColumn;
return lineB > lineA || lineB == lineA && columnB >= columnA ||
util.compareByGeneratedPositions(mappingA, mappingB) <= 0;
util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0;
}
/**
@ -2413,7 +2478,7 @@ define('source-map/mapping-list', ['require', 'exports', 'module' , 'source-map
*/
MappingList.prototype.toArray = function MappingList_toArray() {
if (!this._sorted) {
this._array.sort(util.compareByGeneratedPositions);
this._array.sort(util.compareByGeneratedPositionsInflated);
this._sorted = true;
}
return this._array;

View File

@ -652,15 +652,15 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
exports.compareByOriginalPositions = compareByOriginalPositions;
/**
* Comparator between two mappings where the generated positions are
* compared.
* Comparator between two mappings with deflated source and name indices where
* the generated positions are compared.
*
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
* mappings with the same generated line and column, but different
* source/name/original line and column the same. Useful when searching for a
* mapping with a stubbed out mapping.
*/
function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
@ -688,7 +688,53 @@ define('lib/source-map/util', ['require', 'exports', 'module' , ], function(requ
return mappingA.name - mappingB.name;
};
exports.compareByGeneratedPositions = compareByGeneratedPositions;
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
function strcmp(aStr1, aStr2) {
if (aStr1 === aStr2) {
return 0;
}
if (aStr1 > aStr2) {
return 1;
}
return -1;
}
/**
* Comparator between two mappings with inflated source and name strings where
* the generated positions are compared.
*/
function compareByGeneratedPositionsInflated(mappingA, mappingB) {
var cmp = mappingA.generatedLine - mappingB.generatedLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
if (cmp !== 0) {
return cmp;
}
cmp = strcmp(mappingA.source, mappingB.source);
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalLine - mappingB.originalLine;
if (cmp !== 0) {
return cmp;
}
cmp = mappingA.originalColumn - mappingB.originalColumn;
if (cmp !== 0) {
return cmp;
}
return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
});
/* -*- Mode: js; js-indent-level: 2; -*- */

View File

@ -1062,6 +1062,30 @@ define("test/source-map/test-source-map-consumer", ["require", "exports", "modul
assert.equal(pos.column, 4);
};
exports['test issue #191'] = function (assert, util) {
var generator = new SourceMapGenerator({ file: 'a.css' });
generator.addMapping({
source: 'b.css',
original: {
line: 1,
column: 0
},
generated: {
line: 1,
column: 0
}
});
// Create a SourceMapConsumer from the SourceMapGenerator, ...
var consumer = SourceMapConsumer.fromSourceMap(generator);
// ... and then try and use the SourceMapGenerator again. This should not
// throw.
generator.toJSON();
assert.ok(true, "Using a SourceMapGenerator again after creating a " +
"SourceMapConsumer from it should not throw");
};
});
function run_test() {
runSourceMapTests('test/source-map/test-source-map-consumer', do_throw);

View File

@ -723,6 +723,30 @@ define("test/source-map/test-source-map-generator", ["require", "exports", "modu
util.assertEqualMaps(assert, map1.toJSON(), expectedMap.toJSON());
};
exports['test issue #192'] = function (assert, util) {
var generator = new SourceMapGenerator();
generator.addMapping({
source: 'a.js',
generated: { line: 1, column: 10 },
original: { line: 1, column: 10 },
});
generator.addMapping({
source: 'b.js',
generated: { line: 1, column: 10 },
original: { line: 2, column: 20 },
});
var consumer = new SourceMapConsumer(generator.toJSON());
var n = 0;
consumer.eachMapping(function () { n++ });
assert.equal(n, 2,
"Should not de-duplicate mappings that have the same " +
"generated positions, but different original positions.");
};
});
function run_test() {
runSourceMapTests('test/source-map/test-source-map-generator', do_throw);

View File

@ -172,6 +172,7 @@ this.AppConstants = Object.freeze({
MOZ_APP_NAME: "@MOZ_APP_NAME@",
MOZ_APP_VERSION: "@MOZ_APP_VERSION@",
MOZ_APP_VERSION_DISPLAY: "@MOZ_APP_VERSION_DISPLAY@",
MOZ_BUILD_APP: "@MOZ_BUILD_APP@",
MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@",
MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@",

View File

@ -172,7 +172,7 @@ let dataProviders = {
application: function application(done) {
let data = {
name: Services.appinfo.name,
version: Services.appinfo.version,
version: AppConstants.MOZ_APP_VERSION_DISPLAY,
buildID: Services.appinfo.appBuildID,
userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].
getService(Ci.nsIHttpProtocolHandler).

View File

@ -101,6 +101,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
for var in ('ANDROID_PACKAGE_NAME',
'MOZ_APP_NAME',
'MOZ_APP_VERSION',
'MOZ_APP_VERSION_DISPLAY',
'MOZ_WIDGET_TOOLKIT',
'DLL_PREFIX',
'DLL_SUFFIX'):

View File

@ -0,0 +1,60 @@
browser/components/translation/cld2/
build/stlport/
db/sqlite3/src/
dom/media/platforms/ffmpeg/libav
extensions/spellcheck/hunspell/src/*.cxx
extensions/spellcheck/hunspell/src/*.hxx
gfx/2d/convolver
gfx/2d/image_operations
gfx/angle/
gfx/cairo/
gfx/graphite2/
gfx/harfbuzz/
gfx/ots/
gfx/qcms/
gfx/skia/
gfx/ycbcr/
intl/hyphenation/
intl/icu/
ipc/chromium/
js/src/jit/arm64/vixl/
media/kiss_ftt/
media/libav/
media/libcubeb/
media/libjpeg/
media/libmkv/
media/libnestegg/
media/libogg/
media/libopus/
media/libpng/
media/libsoundtouch/
media/libspeex_resampler/
media/libstagefright/
media/libtheora/
media/libtremor/
media/libvorbis/
media/libvpx/
media/libyuv/
media/mtransport/
media/openmax_dl/
media/pocketsphinx/
media/sphinxbase/
media/webrtc/trunk/
memory/jemalloc/src/
mfbt/decimal/
mfbt/double-conversion/
mfbt/lz4
modules/brotli/
modules/freetype2/
modules/libbz2/
modules/libmar/
modules/zlib/
netwerk/sctp/src/
netwerk/srtp/src/
nsprpub/
other-licenses/
security/sandbox/chromium/
testing/gtest/gmock/
testing/gtest/gtest/
toolkit/components/protobuf/
toolkit/crashreporter/google-breakpad/

View File

@ -792,30 +792,49 @@ PL_DHashTableSizeOfIncludingThis(
PLDHashTable::Iterator::Iterator(Iterator&& aOther)
: mTable(aOther.mTable)
, mCurrent(aOther.mCurrent)
, mStart(aOther.mStart)
, mLimit(aOther.mLimit)
, mCurrent(aOther.mCurrent)
, mNexts(aOther.mNexts)
, mNextsLimit(aOther.mNextsLimit)
, mHaveRemoved(aOther.mHaveRemoved)
{
// No need to change |mChecker| here.
aOther.mTable = nullptr;
aOther.mCurrent = nullptr;
aOther.mStart = nullptr;
aOther.mLimit = nullptr;
aOther.mCurrent = nullptr;
aOther.mNexts = 0;
aOther.mNextsLimit = 0;
aOther.mHaveRemoved = false;
}
PLDHashTable::Iterator::Iterator(PLDHashTable* aTable)
: mTable(aTable)
, mCurrent(mTable->mEntryStore)
, mStart(mTable->mEntryStore)
, mLimit(mTable->mEntryStore + mTable->Capacity() * mTable->mEntrySize)
, mCurrent(mTable->mEntryStore)
, mNexts(0)
, mNextsLimit(mTable->EntryCount())
, mHaveRemoved(false)
{
#ifdef DEBUG
mTable->mChecker.StartReadOp();
#endif
// Advance to the first live entry, or to the end if there are none.
while (IsOnNonLiveEntry()) {
mCurrent += mTable->mEntrySize;
if (ChaosMode::isActive(ChaosMode::HashTableIteration) &&
mTable->Capacity() > 0) {
// Start iterating at a random entry. It would be even more chaotic to
// iterate in fully random order, but that's harder.
mCurrent += ChaosMode::randomUint32LessThan(mTable->Capacity()) *
mTable->mEntrySize;
}
// Advance to the first live entry, if there is one.
if (!Done()) {
while (IsOnNonLiveEntry()) {
MoveToNextEntry();
}
}
}
@ -834,13 +853,23 @@ PLDHashTable::Iterator::~Iterator()
bool
PLDHashTable::Iterator::Done() const
{
return mCurrent == mLimit;
return mNexts == mNextsLimit;
}
MOZ_ALWAYS_INLINE bool
PLDHashTable::Iterator::IsOnNonLiveEntry() const
{
return !Done() && !ENTRY_IS_LIVE(reinterpret_cast<PLDHashEntryHdr*>(mCurrent));
MOZ_ASSERT(!Done());
return !ENTRY_IS_LIVE(reinterpret_cast<PLDHashEntryHdr*>(mCurrent));
}
MOZ_ALWAYS_INLINE void
PLDHashTable::Iterator::MoveToNextEntry()
{
mCurrent += mTable->mEntrySize;
if (mCurrent == mLimit) {
mCurrent = mStart; // Wrap-around. Possible due to Chaos Mode.
}
}
PLDHashEntryHdr*
@ -858,9 +887,14 @@ PLDHashTable::Iterator::Next()
{
MOZ_ASSERT(!Done());
do {
mCurrent += mTable->mEntrySize;
} while (IsOnNonLiveEntry());
mNexts++;
// Advance to the next live entry, if there is one.
if (!Done()) {
do {
MoveToNextEntry();
} while (IsOnNonLiveEntry());
}
}
void

View File

@ -393,12 +393,16 @@ public:
PLDHashTable* mTable; // Main table pointer.
private:
char* mCurrent; // Pointer to the current entry.
char* mStart; // The first entry.
char* mLimit; // One past the last entry.
char* mCurrent; // Pointer to the current entry.
uint32_t mNexts; // Number of Next() calls.
uint32_t mNextsLimit; // Next() call limit.
bool mHaveRemoved; // Have any elements been removed?
bool IsOnNonLiveEntry() const;
void MoveToNextEntry();
Iterator() = delete;
Iterator(const Iterator&) = delete;