Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2014-11-07 15:07:58 +01:00
commit 648e688ead
198 changed files with 2295 additions and 943 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ad276e6d4ec40ae2ac214e42c6c81cfc8cd86c3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ad276e6d4ec40ae2ac214e42c6c81cfc8cd86c3"/>

View File

@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
"revision": "ce1789cc91feafe53596cfd0360cd12f7cc69d3b",
"revision": "cfca676ee9f96a5bb98e5ec2704381653d4e8ed8",
"repo_path": "/integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -2,9 +2,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/.
no_tooltool=1
no_sccache=1
# This file is included at the top of all b2g mozconfigs
. "$topsrcdir/build/mozconfig.common"

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ad276e6d4ec40ae2ac214e42c6c81cfc8cd86c3"/>

View File

@ -6,6 +6,13 @@
"filename": "setup.sh"
},
{
"size": 80458572,
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
"algorithm": "sha512",
"filename": "gcc.tar.xz",
"unpack": "True"
},
{
"size": 168320,
"digest": "c0f4a2da0b07ca6fc69290fbc5ed68f56c6b1ba4d593b220fd49b14ac4885e6ec949e695fd9a7ac464e0e86b652e99f6bd4af849fec072264b29a8f9686d2fc4",
"algorithm": "sha512",

View File

@ -6,6 +6,13 @@
"filename": "setup.sh"
},
{
"size": 80458572,
"digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
"algorithm": "sha512",
"filename": "gcc.tar.xz",
"unpack": "True"
},
{
"size": 168320,
"digest": "c0f4a2da0b07ca6fc69290fbc5ed68f56c6b1ba4d593b220fd49b14ac4885e6ec949e695fd9a7ac464e0e86b652e99f6bd4af849fec072264b29a8f9686d2fc4",
"algorithm": "sha512",

View File

@ -17,7 +17,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="62c9bd93341fbfa8bf850c21e73465708f93b503"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="779f05fead3d009f6e7fe713ad0fea16b6f2fb31"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -237,12 +237,14 @@ const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs;
const CERTIFICATEDIALOGS_CONTRACTID = "@mozilla.org/nsCertificateDialogs;1"
// clipboard helper
try {
const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
}
catch(e) {
// do nothing, later code will handle the error
function getClipboardHelper() {
try {
return Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
} catch(e) {
// do nothing, later code will handle the error
}
}
const gClipboardHelper = getClipboardHelper();
// Interface for image loading content
const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;

View File

@ -21,8 +21,6 @@ __all__ = [
"addCommonOptions",
"dumpLeakLog",
"processLeakLog",
'KeyValueParseError',
'parseKeyValue',
'systemMemory',
'environment',
'dumpScreen',
@ -371,27 +369,6 @@ def processLeakLog(leakLogFile, options):
processSingleLeakFile(thisFile, processType, leakThreshold,
processType in ignoreMissingLeaks)
class KeyValueParseError(Exception):
"""error when parsing strings of serialized key-values"""
def __init__(self, msg, errors=()):
self.errors = errors
Exception.__init__(self, msg)
def parseKeyValue(strings, separator='=', context='key, value: '):
"""
parse string-serialized key-value pairs in the form of
`key = value`. Returns a list of 2-tuples.
Note that whitespace is not stripped.
"""
# syntax check
missing = [string for string in strings if separator not in string]
if missing:
raise KeyValueParseError("Error: syntax error in %s" % (context,
','.join(missing)),
errors=missing)
return [string.split(separator, 1) for string in strings]
def systemMemory():
"""
Returns total system memory in kilobytes.

View File

@ -76,6 +76,14 @@ def getUrlProperties(filename):
properties = {prop: 'UNKNOWN' for prop, condition in property_conditions}
return properties
def getPartialInfo(props):
return [{
"from_buildid": props.get("previous_buildid"),
"size": props.get("partialMarSize"),
"hash": props.get("partialMarHash"),
"url": props.get("partialMarUrl"),
}]
if __name__ == '__main__':
parser = ArgumentParser(description='Generate mach_build_properties.json for automation builds.')
parser.add_argument("--complete-mar-file", required=True,
@ -90,9 +98,18 @@ if __name__ == '__main__':
args = parser.parse_args()
json_data = getMarProperties(args.complete_mar_file)
json_data.update(getUrlProperties(args.upload_output))
if args.partial_mar_file:
json_data.update(getMarProperties(args.partial_mar_file, partial=True))
json_data.update(getUrlProperties(args.upload_output))
# Pull the previous buildid from the partial mar filename.
res = re.match(r'.*\.([0-9]+)-[0-9]+.mar', args.partial_mar_file)
if res:
json_data['previous_buildid'] = res.group(1)
# Set partialInfo to be a collection of the partial mar properties
# useful for balrog.
json_data['partialInfo'] = getPartialInfo(json_data)
with open('mach_build_properties.json', 'w') as outfile:
json.dump(json_data, outfile, indent=4)

View File

@ -7,6 +7,7 @@
import argparse
import errno
import itertools
import os
import re
import subprocess
@ -15,26 +16,24 @@ import pickle
import mozpack.path as mozpath
try:
from multiprocessing import Pool, cpu_count
except ImportError:
import itertools
class Pool(object):
def __init__(self, size):
pass
class Pool(object):
def __new__(cls, size):
try:
import multiprocessing
size = min(size, multiprocessing.cpu_count())
return multiprocessing.Pool(size)
except:
return super(Pool, cls).__new__(cls)
def imap_unordered(self, fn, iterable):
return itertools.imap(fn, iterable)
def imap_unordered(self, fn, iterable):
return itertools.imap(fn, iterable)
def close(self):
pass
def close(self):
pass
def join(self):
pass
def cpu_count():
return 1
def join(self):
pass
class File(object):
@ -389,7 +388,7 @@ def subconfigure(args):
# One would think using a ThreadPool would be faster, considering
# everything happens in subprocesses anyways, but no, it's actually
# slower on Windows. (20s difference overall!)
pool = Pool(min(len(subconfigures), cpu_count()))
pool = Pool(len(subconfigures))
for relobjdir, returncode, output in \
pool.imap_unordered(run, subconfigures):
print prefix_lines(output, relobjdir)

View File

@ -503,16 +503,6 @@ WIN32_EXE_LDFLAGS += -STACK:2097152
endif
endif
# If we're building a component on MSVC, we don't want to generate an
# import lib, because that import lib will collide with the name of a
# static version of the same library.
ifeq ($(GNU_LD)$(OS_ARCH),WINNT)
ifdef IS_COMPONENT
LDFLAGS += -IMPLIB:fake.lib
DELETE_AFTER_LINK = fake.lib fake.exp
endif
endif
#
# Include any personal overrides the user might think are needed.
#

View File

@ -841,7 +841,6 @@ ifdef MOZ_PROFILE_GENERATE
touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink
endif
endif # WINNT && !GCC
@$(RM) foodummyfilefoo $(DELETE_AFTER_LINK)
chmod +x $@
ifdef ENABLE_STRIP
$(STRIP) $(STRIP_FLAGS) $@
@ -1208,26 +1207,6 @@ misc:: $(call mkdir_deps,$(FINAL_TARGET))
$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest $(patsubst %,'manifest components/%',$(notdir $(EXTRA_MANIFESTS))))
endif
################################################################################
# Copy each element of EXTRA_JS_MODULES to
# $(FINAL_TARGET)/modules.
FINAL_JS_MODULES_PATH := $(FINAL_TARGET)/modules
ifdef EXTRA_JS_MODULES
ifndef NO_DIST_INSTALL
EXTRA_JS_MODULES_FILES := $(EXTRA_JS_MODULES)
EXTRA_JS_MODULES_DEST := $(FINAL_JS_MODULES_PATH)
INSTALL_TARGETS += EXTRA_JS_MODULES
endif
endif
ifdef EXTRA_PP_JS_MODULES
ifndef NO_DIST_INSTALL
EXTRA_PP_JS_MODULES_PATH := $(FINAL_JS_MODULES_PATH)
PP_TARGETS += EXTRA_PP_JS_MODULES
endif
endif
################################################################################
# SDK

View File

@ -12,10 +12,19 @@
#include "nsILoadContext.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "nsThreadUtils.h"
#include "prlog.h"
NS_IMPL_ISUPPORTS(ThirdPartyUtil, mozIThirdPartyUtil)
//
// NSPR_LOG_MODULES=thirdPartyUtil:5
//
static PRLogModuleInfo *gThirdPartyLog;
#undef LOG
#define LOG(args) PR_LOG(gThirdPartyLog, PR_LOG_DEBUG, args)
nsresult
ThirdPartyUtil::Init()
{
@ -23,6 +32,10 @@ ThirdPartyUtil::Init()
nsresult rv;
mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv);
if (!gThirdPartyLog)
gThirdPartyLog = PR_NewLogModule("thirdPartyUtil");
return rv;
}
@ -62,7 +75,11 @@ ThirdPartyUtil::GetURIFromWindow(nsIDOMWindow* aWin, nsIURI** result)
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIURI> uri;
if (prin->GetIsNullPrincipal()) {
LOG(("ThirdPartyUtil::GetURIFromWindow can't use null principal\n"));
return NS_ERROR_INVALID_ARG;
}
rv = prin->GetURI(result);
return rv;
}

View File

@ -253,7 +253,6 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
// XXXkhuey should this be GetOurCurrentDoc? Decoding if we're not in
// the document seems silly.
bool startedDecoding = false;
nsIDocument* doc = GetOurOwnerDoc();
nsIPresShell* shell = doc ? doc->GetShell() : nullptr;
if (shell && shell->IsVisible() &&
@ -274,9 +273,7 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
// visible.
if (!mFrameCreateCalled || (f->GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
mVisibleCount > 0 || shell->AssumeAllImagesVisible()) {
if (NS_SUCCEEDED(mCurrentRequest->StartDecoding())) {
startedDecoding = true;
}
mCurrentRequest->StartDecoding();
}
}
}
@ -288,8 +285,8 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
uint32_t reqStatus;
aRequest->GetImageStatus(&reqStatus);
if (NS_SUCCEEDED(aStatus) && !(reqStatus & imgIRequest::STATUS_ERROR) &&
(reqStatus & imgIRequest::STATUS_DECODE_STARTED ||
(startedDecoding && !(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)))) {
(reqStatus & imgIRequest::STATUS_DECODE_STARTED) &&
!(reqStatus & imgIRequest::STATUS_DECODE_COMPLETE)) {
mFireEventsOnDecode = true;
} else {
// Fire the appropriate DOM event.

View File

@ -45,6 +45,7 @@
#include "jsapi.h"
#include "jswrapper.h"
#include "js/SliceBudget.h"
#include "nsIArray.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
@ -1692,24 +1693,24 @@ nsJSContext::RunCycleCollectorSlice()
// Decide how long we want to budget for this slice. By default,
// use an unlimited budget.
int64_t sliceBudget = -1;
js::SliceBudget budget;
if (sIncrementalCC) {
if (gCCStats.mBeginTime.IsNull()) {
// If no CC is in progress, use the standard slice time.
sliceBudget = kICCSliceBudget;
budget = js::SliceBudget(js::TimeBudget(kICCSliceBudget));
} else {
TimeStamp now = TimeStamp::Now();
// Only run a limited slice if we're within the max running time.
if (TimeBetween(gCCStats.mBeginTime, now) < kMaxICCDuration) {
float sliceMultiplier = std::max(TimeBetween(gCCStats.mEndSliceTime, now) / (float)kICCIntersliceDelay, 1.0f);
sliceBudget = kICCSliceBudget * sliceMultiplier;
budget = js::SliceBudget(js::TimeBudget(kICCSliceBudget * sliceMultiplier));
}
}
}
nsCycleCollector_collectSlice(sliceBudget);
nsCycleCollector_collectSlice(budget);
gCCStats.FinishCycleCollectionSlice();
}
@ -1726,7 +1727,10 @@ nsJSContext::RunCycleCollectorWorkSlice(int64_t aWorkBudget)
js::ProfileEntry::Category::CC);
gCCStats.PrepareForCycleCollectionSlice();
nsCycleCollector_collectSliceWork(aWorkBudget);
js::SliceBudget budget = js::SliceBudget(js::WorkBudget(aWorkBudget));
nsCycleCollector_collectSlice(budget);
gCCStats.FinishCycleCollectionSlice();
}

View File

@ -379,6 +379,10 @@ BrowserElementChild.prototype = {
}
debug("Nested event loop - finish");
if (win.modalDepth == 0) {
delete this._windowIDDict[outerWindowID];
}
// If we exited the loop because the inner window changed, then bail on the
// modal prompt.
if (innerWindowID !== this._tryGetInnerWindowID(win)) {
@ -411,7 +415,6 @@ BrowserElementChild.prototype = {
}
let win = this._windowIDDict[outerID].get();
delete this._windowIDDict[outerID];
if (!win) {
debug("recvStopWaiting, but window is gone\n");

View File

@ -124,7 +124,7 @@ function test4() {
// test4 is a mozbrowsershowmodalprompt listener.
function test5(e) {
iframe.removeEventListener('mozbrowsershowmodalprompt', test4);
iframe.removeEventListener('mozbrowsershowmodalprompt', test5);
is(e.detail.message, 'test4');
e.preventDefault(); // cause the page to block.
@ -139,6 +139,140 @@ function test5a() {
function test5b() {
iframe.removeEventListener('mozbrowserloadend', test5b);
SimpleTest.executeSoon(test6);
}
// Test nested alerts
var promptBlockers = [];
function test6() {
iframe.addEventListener("mozbrowsershowmodalprompt", test6a);
var script = 'data:,\
this.testState = 0; \
content.alert(1); \
this.testState = 3; \
';
mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
}
function test6a(e) {
iframe.removeEventListener("mozbrowsershowmodalprompt", test6a);
is(e.detail.message, '1');
e.preventDefault(); // cause the alert to block.
promptBlockers.push(e);
SimpleTest.executeSoon(test6b);
}
function test6b() {
var script = 'data:,\
if (this.testState === 0) { \
sendAsyncMessage("test-success", "1: Correct testState"); \
} \
else { \
sendAsyncMessage("test-fail", "1: Wrong testState: " + this.testState); \
}';
mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
numPendingChildTests++;
waitForPendingTests(test6c);
}
function test6c() {
iframe.addEventListener("mozbrowsershowmodalprompt", test6d);
var script = 'data:,\
this.testState = 1; \
content.alert(2); \
this.testState = 2; \
';
mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
}
function test6d(e) {
iframe.removeEventListener("mozbrowsershowmodalprompt", test6d);
is(e.detail.message, '2');
e.preventDefault(); // cause the alert to block.
promptBlockers.push(e);
SimpleTest.executeSoon(test6e);
}
function test6e() {
var script = 'data:,\
if (this.testState === 1) { \
sendAsyncMessage("test-success", "2: Correct testState"); \
} \
else { \
sendAsyncMessage("test-fail", "2: Wrong testState: " + this.testState); \
}';
mm.loadFrameScript(script, /* allowDelayedLoad = */ false);
numPendingChildTests++;
waitForPendingTests(test6f);
}
function test6f() {
var e = promptBlockers.pop();
// Now unblock the iframe and check that the script completed.
e.detail.unblock();
var script2 = 'data:,\
if (this.testState === 2) { \
sendAsyncMessage("test-success", "3: Correct testState"); \
} \
else { \
sendAsyncMessage("test-try-again", "3: Wrong testState (for now): " + this.testState); \
}';
// Urgh. e.unblock() didn't necessarily unblock us immediately, so we have
// to spin and wait.
function onTryAgain() {
SimpleTest.executeSoon(function() {
//dump('onTryAgain\n');
mm.loadFrameScript(script2, /* allowDelayedLoad = */ false);
});
}
mm.addMessageListener('test-try-again', onTryAgain);
numPendingChildTests++;
onTryAgain();
waitForPendingTests(test6g);
}
function test6g() {
var e = promptBlockers.pop();
// Now unblock the iframe and check that the script completed.
e.detail.unblock();
var script2 = 'data:,\
if (this.testState === 3) { \
sendAsyncMessage("test-success", "4: Correct testState"); \
} \
else { \
sendAsyncMessage("test-try-again", "4: Wrong testState (for now): " + this.testState); \
}';
// Urgh. e.unblock() didn't necessarily unblock us immediately, so we have
// to spin and wait.
function onTryAgain() {
SimpleTest.executeSoon(function() {
//dump('onTryAgain\n');
mm.loadFrameScript(script2, /* allowDelayedLoad = */ false);
});
}
mm.addMessageListener('test-try-again', onTryAgain);
numPendingChildTests++;
onTryAgain();
waitForPendingTests(test6h);
}
function test6h() {
SimpleTest.finish();
}

View File

@ -2886,11 +2886,11 @@ void HTMLMediaElement::ProcessMediaFragmentURI()
}
void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
const MetadataTags* aTags)
nsAutoPtr<const MetadataTags> aTags)
{
mHasAudio = aInfo->HasAudio();
mHasVideo = aInfo->HasVideo();
mTags = aTags;
mTags = aTags.forget();
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
@ -2903,11 +2903,11 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
// If this element had a video track, but consists only of an audio track now,
// delete the VideoFrameContainer. This happens when the src is changed to an
// audio only file.
if (!aInfo->HasVideo() && mVideoFrameContainer) {
// call ForgetElement() such that callbacks from |mVideoFrameContainer|
// won't reach us anymore.
mVideoFrameContainer->ForgetElement();
mVideoFrameContainer = nullptr;
// Else update its dimensions.
if (!aInfo->HasVideo()) {
ResetState();
} else {
UpdateMediaSize(aInfo->mVideo.mDisplay);
}
}

View File

@ -160,7 +160,7 @@ public:
// when it has read the metadata containing video dimensions,
// etc.
virtual void MetadataLoaded(const MediaInfo* aInfo,
const MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
nsAutoPtr<const MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
// Called by the decoder object, on the main thread,
// when it has read the first frame of the video or audio.

View File

@ -45,7 +45,8 @@ function frameLoad() {
function load() {
window.loaded = true;
var imgsrc = "<img onload ='window.parent.imgLoad()' src='image.png'>\n";
var imgsrc = "<img onload ='window.parent.imgLoad()' src='image.png?noCache="
+ (new Date().getTime()) + "'>\n";
var doc = $('iframe').contentWindow.document;
doc.writeln(imgsrc);
doc.close();

View File

@ -1130,10 +1130,10 @@ ContentChild::RecvSetProcessSandbox()
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
// For B2G >= KitKat, sandboxing is mandatory; this has already
// been enforced by ContentParent::StartUp().
MOZ_ASSERT(CanSandboxContentProcess());
MOZ_ASSERT(ContentProcessSandboxStatus() != kSandboxingWouldFail);
#else
// Otherwise, sandboxing is best-effort.
if (!CanSandboxContentProcess()) {
if (ContentProcessSandboxStatus() == kSandboxingWouldFail) {
return true;
}
#endif

View File

@ -687,7 +687,7 @@ ContentParent::StartUp()
#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
// Require sandboxing on B2G >= KitKat. This condition must stay
// in sync with ContentChild::RecvSetProcessSandbox.
if (!CanSandboxContentProcess()) {
if (ContentProcessSandboxStatus() == kSandboxingWouldFail) {
// MOZ_CRASH strings are only for debug builds; make sure the
// message is clear on non-debug builds as well:
printf_stderr("Sandboxing support is required on this platform. "

View File

@ -88,8 +88,9 @@ public:
// Return true if the transport layer supports seeking.
virtual bool IsMediaSeekable() = 0;
virtual void MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags) = 0;
virtual void QueueMetadata(int64_t aTime, MediaInfo* aInfo, MetadataTags* aTags) = 0;
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) = 0;
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) = 0;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo) = 0;
virtual void RemoveMediaTracks() = 0;
@ -153,15 +154,29 @@ public:
#endif
};
class MetadataEventRunner : public nsRunnable
class MetadataContainer
{
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
public:
MetadataEventRunner(AbstractMediaDecoder* aDecoder, MediaInfo* aInfo, MetadataTags* aTags)
: mDecoder(aDecoder),
mInfo(aInfo),
mTags(aTags)
protected:
MetadataContainer(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
: mDecoder(aDecoder),
mInfo(aInfo),
mTags(aTags)
{}
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsAutoPtr<MediaInfo> mInfo;
nsAutoPtr<MetadataTags> mTags;
};
class MetadataEventRunner : public nsRunnable, private MetadataContainer
{
public:
MetadataEventRunner(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
: MetadataContainer(aDecoder, aInfo, aTags)
{}
NS_IMETHOD Run() MOZ_OVERRIDE
@ -169,12 +184,40 @@ class MetadataEventRunner : public nsRunnable
mDecoder->MetadataLoaded(mInfo, mTags);
return NS_OK;
}
};
// The ownership is transferred to MediaDecoder.
MediaInfo* mInfo;
class FirstFrameLoadedEventRunner : public nsRunnable, private MetadataContainer
{
public:
FirstFrameLoadedEventRunner(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo)
: MetadataContainer(aDecoder, aInfo, nsAutoPtr<MetadataTags>(nullptr))
{}
// The ownership is transferred to its owning element.
MetadataTags* mTags;
NS_IMETHOD Run() MOZ_OVERRIDE
{
mDecoder->FirstFrameLoaded(mInfo);
return NS_OK;
}
};
class MetadataUpdatedEventRunner : public nsRunnable, private MetadataContainer
{
public:
MetadataUpdatedEventRunner(AbstractMediaDecoder* aDecoder,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
: MetadataContainer(aDecoder, aInfo, aTags)
{}
NS_IMETHOD Run() MOZ_OVERRIDE
{
nsAutoPtr<MediaInfo> info(new MediaInfo());
*info = *mInfo;
mDecoder->MetadataLoaded(info, mTags);
mDecoder->FirstFrameLoaded(mInfo);
return NS_OK;
}
};
class RemoveMediaTracksEventRunner : public nsRunnable

View File

@ -437,7 +437,7 @@ MediaDecoder::MediaDecoder() :
mReentrantMonitor("media.decoder"),
mIsDormant(false),
mIsExitingDormant(false),
mPlayState(PLAY_STATE_PAUSED),
mPlayState(PLAY_STATE_LOADING),
mNextState(PLAY_STATE_PAUSED),
mIgnoreProgressData(false),
mInfiniteStream(false),
@ -562,8 +562,6 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
// set them now
SetStateMachineParameters();
ChangeState(PLAY_STATE_LOADING);
return ScheduleStateMachineThread();
}
@ -670,8 +668,8 @@ already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal()
}
void MediaDecoder::QueueMetadata(int64_t aPublishTime,
MediaInfo* aInfo,
MetadataTags* aTags)
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
GetReentrantMonitor().AssertCurrentThreadIn();
@ -686,9 +684,11 @@ MediaDecoder::IsDataCachedToEndOfResource()
mResource->IsDataCachedToEndOfResource(mDecoderPosition));
}
void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown) {
return;
}
@ -714,17 +714,37 @@ void MediaDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
SetInfinite(true);
}
mInfo = aInfo;
mInfo = aInfo.forget();
ConstructMediaTracks();
if (mOwner) {
// Make sure the element and the frame (if any) are told about
// our new size.
Invalidate();
mOwner->MetadataLoaded(aInfo, aTags);
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
}
}
void MediaDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo)
{
MOZ_ASSERT(NS_IsMainThread());
if (mShuttingDown) {
return;
}
DECODER_LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
aInfo->mAudio.mChannels, aInfo->mAudio.mRate,
aInfo->HasAudio(), aInfo->HasVideo());
if (mPlayState == PLAY_STATE_LOADING && mIsDormant && !mIsExitingDormant) {
return;
}
mInfo = aInfo.forget();
if (mOwner) {
Invalidate();
mOwner->FirstFrameLoaded();
}

View File

@ -67,7 +67,9 @@ The state machine has the following states:
DECODING_METADATA
The media headers are being loaded, and things like framerate, etc are
being determined, and the first frame of audio/video data is being decoded.
being determined.
DECODING_FIRSTFRAME
The first frame of audio/video data is being decoded.
DECODING
The decode has started. If the PlayState is PLAYING, the decode thread
should be alive and decoding video and audio frame, the audio thread
@ -104,32 +106,36 @@ Seek(double)
A state transition diagram:
DECODING_METADATA
| |
v | Shutdown()
| |
v -->-------------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Play() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Play() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
|---<-- DECODING_METADATA ----->--------|
| | |
Seek(t) v Shutdown()
| | |
-->--- DECODING_FIRSTFRAME |------->-----------------|
| | |
| Shutdown() |
| | |
v |-->----------------->--------------------------|
|---------------->----->------------------------| v
DECODING | | | | |
^ v Seek(t) | | | |
| Play() | v | | |
^-----------<----SEEKING | v Complete v v
| | | | | |
| | | COMPLETED SHUTDOWN-<-|
^ ^ | |Shutdown() |
| | | >-------->-----^
| Play() |Seek(t) |Buffer() |
-----------<--------<-------BUFFERING |
| ^
v Shutdown() |
| |
------------>-----|
The following represents the states that the MediaDecoder object
can be in, and the valid states the MediaDecoderStateMachine can be in at that
time:
player LOADING decoder DECODING_METADATA
player LOADING decoder DECODING_METADATA, DECODING_FIRSTFRAME
player PLAYING decoder DECODING, BUFFERING, SEEKING, COMPLETED
player PAUSED decoder DECODING, BUFFERING, SEEKING, COMPLETED
player SEEKING decoder SEEKING
@ -756,8 +762,8 @@ public:
// main thread to be presented when the |currentTime| of the media is greater
// or equal to aPublishTime.
void QueueMetadata(int64_t aPublishTime,
MediaInfo* aInfo,
MetadataTags* aTags);
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags);
int64_t GetSeekTime() { return mRequestedSeekTarget.mTime; }
void ResetSeekTime() { mRequestedSeekTarget.Reset(); }
@ -782,8 +788,12 @@ public:
// Called when the metadata from the media file has been loaded by the
// state machine. Call on the main thread only.
virtual void MetadataLoaded(MediaInfo* aInfo,
MetadataTags* aTags);
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags);
// Called when the first audio and/or video from the media file has been loaded
// by the state machine. Call on the main thread only.
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo);
// Called from MetadataLoaded(). Creates audio tracks and adds them to its
// owner's audio track list, and implies to video tracks respectively.
@ -794,10 +804,6 @@ public:
// the track list. Call on the main thread only.
virtual void RemoveMediaTracks() MOZ_OVERRIDE;
// Called when the first frame has been loaded.
// Call on the main thread only.
void FirstFrameLoaded();
// Returns true if the resource has been loaded. Acquires the monitor.
// Call from any thread.
virtual bool IsDataCachedToEndOfResource();

View File

@ -51,8 +51,9 @@ public:
// Called by the video decoder object, on the main thread,
// when it has read the metadata containing video dimensions,
// etc.
// Must take ownership of MetadataTags aTags argument.
virtual void MetadataLoaded(const MediaInfo* aInfo,
const MetadataTags* aTags) = 0;
nsAutoPtr<const MetadataTags> aTags) = 0;
// Called by the decoder object, on the main thread,
// when it has read the first frame of the video or audio.

View File

@ -101,6 +101,10 @@ public:
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) = 0;
// Fills aInfo with the latest cached data required to present the media,
// ReadUpdatedMetadata will always be called once ReadMetadata has succeeded.
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) { };
// Requests the Reader to seek and call OnSeekCompleted on the callback
// once completed.
// Moves the decode head to aTime microseconds. aStartTime and aEndTime

View File

@ -371,7 +371,8 @@ void MediaDecoderStateMachine::SendStreamData()
return;
}
if (mState == DECODER_STATE_DECODING_METADATA) {
if (mState == DECODER_STATE_DECODING_METADATA ||
mState == DECODER_STATE_DECODING_FIRSTFRAME) {
return;
}
@ -705,9 +706,9 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
(audio ? audio->mDiscontinuity : 0));
switch (mState) {
case DECODER_STATE_DECODING_METADATA: {
case DECODER_STATE_DECODING_FIRSTFRAME: {
Push(audio.forget());
MaybeFinishDecodeMetadata();
MaybeFinishDecodeFirstFrame();
return;
}
@ -770,7 +771,7 @@ MediaDecoderStateMachine::Push(AudioData* aSample)
// otherwise AdvanceFrame may pop the sample before we have a chance
// to reach playing.
AudioQueue().Push(aSample);
if (mState > DECODER_STATE_DECODING_METADATA) {
if (mState > DECODER_STATE_DECODING_FIRSTFRAME) {
SendStreamData();
// The ready state can change when we've decoded data, so update the
// ready state, so that DOM events can fire.
@ -788,7 +789,7 @@ MediaDecoderStateMachine::Push(VideoData* aSample)
// otherwise AdvanceFrame may pop the sample before we have a chance
// to reach playing.
VideoQueue().Push(aSample);
if (mState > DECODER_STATE_DECODING_METADATA) {
if (mState > DECODER_STATE_DECODING_FIRSTFRAME) {
SendStreamData();
// The ready state can change when we've decoded data, so update the
// ready state, so that DOM events can fire.
@ -839,8 +840,8 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
}
isAudio ? AudioQueue().Finish() : VideoQueue().Finish();
switch (mState) {
case DECODER_STATE_DECODING_METADATA: {
MaybeFinishDecodeMetadata();
case DECODER_STATE_DECODING_FIRSTFRAME: {
MaybeFinishDecodeFirstFrame();
return;
}
@ -883,14 +884,14 @@ MediaDecoderStateMachine::AcquireMonitorAndInvokeDecodeError()
}
void
MediaDecoderStateMachine::MaybeFinishDecodeMetadata()
MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
{
AssertCurrentThreadInMonitor();
if ((IsAudioDecoding() && AudioQueue().GetSize() == 0) ||
(IsVideoDecoding() && VideoQueue().GetSize() == 0)) {
return;
}
if (NS_FAILED(FinishDecodeMetadata())) {
if (NS_FAILED(FinishDecodeFirstFrame())) {
DecodeError();
}
}
@ -908,9 +909,9 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
(video ? video->mDiscontinuity : 0));
switch (mState) {
case DECODER_STATE_DECODING_METADATA: {
case DECODER_STATE_DECODING_FIRSTFRAME: {
Push(video.forget());
MaybeFinishDecodeMetadata();
MaybeFinishDecodeFirstFrame();
return;
}
@ -1221,6 +1222,7 @@ static const char* const gMachineStateStr[] = {
"NONE",
"DECODING_METADATA",
"WAIT_FOR_RESOURCES",
"DECODING_FIRSTFRAME",
"DORMANT",
"DECODING",
"SEEKING",
@ -1527,12 +1529,55 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
DECODER_WARN("Seek() function should not be called on a non-seekable state machine");
return;
}
// MediaDecoder::mPlayState should be SEEKING while we seek, and
// in that case MediaDecoder shouldn't be calling us.
NS_ASSERTION(mState != DECODER_STATE_SEEKING,
"We shouldn't already be seeking");
NS_ASSERTION(mState >= DECODER_STATE_DECODING,
"We should have loaded metadata");
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
"We should have got duration already");
if (mState <= DECODER_STATE_DECODING_FIRSTFRAME) {
DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
mQueuedSeekTarget = aTarget;
return;
}
mQueuedSeekTarget.Reset();
StartSeek(aTarget);
}
void
MediaDecoderStateMachine::EnqueueStartQueuedSeekTask()
{
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::StartQueuedSeek);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
void
MediaDecoderStateMachine::StartQueuedSeek()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (!mQueuedSeekTarget.IsValid()) {
return;
}
StartSeek(mQueuedSeekTarget);
mQueuedSeekTarget.Reset();
}
void
MediaDecoderStateMachine::StartSeek(const SeekTarget& aTarget)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
AssertCurrentThreadInMonitor();
MOZ_ASSERT(mState >= DECODER_STATE_DECODING);
if (mState == DECODER_STATE_SHUTDOWN) {
return;
}
// Bound the seek time to be inside the media range.
NS_ASSERTION(mStartTime != -1, "Should know start time by now");
@ -1554,8 +1599,8 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
void MediaDecoderStateMachine::StopAudioThread()
{
NS_ASSERTION(OnDecodeThread() ||
OnStateMachineThread(), "Should be on decode thread or state machine thread");
NS_ASSERTION(OnDecodeThread() || OnStateMachineThread(),
"Should be on decode thread or state machine thread");
AssertCurrentThreadInMonitor();
if (mStopAudioThread) {
@ -1598,6 +1643,19 @@ MediaDecoderStateMachine::EnqueueDecodeMetadataTask()
return NS_OK;
}
nsresult
MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
{
AssertCurrentThreadInMonitor();
MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
RefPtr<nsIRunnable> task(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
nsresult rv = mDecodeTaskQueue->Dispatch(task);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
MediaDecoderStateMachine::SetReaderIdle()
{
@ -1733,7 +1791,7 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
return NS_OK;
}
MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA);
MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
if (IsAudioDecoding() && !mAudioRequestPending && !mWaitingForDecoderSeek) {
RefPtr<nsIRunnable> task(
@ -1778,7 +1836,7 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
return NS_OK;
}
MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA);
MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
if (IsVideoDecoding() && !mVideoRequestPending && !mWaitingForDecoderSeek) {
RefPtr<nsIRunnable> task(
@ -1856,8 +1914,8 @@ bool MediaDecoderStateMachine::HasLowUndecodedData()
bool MediaDecoderStateMachine::HasLowUndecodedData(double aUsecs)
{
AssertCurrentThreadInMonitor();
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
"Must have loaded metadata for GetBuffered() to work");
NS_ASSERTION(mState > DECODER_STATE_DECODING_FIRSTFRAME,
"Must have loaded first frame for GetBuffered() to work");
bool reliable;
double bytesPerSecond = mDecoder->ComputePlaybackRate(&reliable);
@ -1967,6 +2025,55 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
mDecoder->StartProgressUpdates();
mGotDurationFromMetaData = (GetDuration() != -1);
if (mGotDurationFromMetaData) {
// We have all the information required: duration and size
// Inform the element that we've loaded the metadata.
EnqueueLoadedMetadataEvent();
}
if (mState == DECODER_STATE_DECODING_METADATA) {
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
res = EnqueueDecodeFirstFrameTask();
if (NS_FAILED(res)) {
return NS_ERROR_FAILURE;
}
}
ScheduleStateMachine();
return NS_OK;
}
void
MediaDecoderStateMachine::EnqueueLoadedMetadataEvent()
{
nsAutoPtr<MediaInfo> info(new MediaInfo());
*info = mInfo;
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
new MetadataEventRunner(mDecoder, info, mMetadataTags);
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
}
void
MediaDecoderStateMachine::CallDecodeFirstFrame()
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mState != DECODER_STATE_DECODING_FIRSTFRAME) {
return;
}
if (NS_FAILED(DecodeFirstFrame())) {
DECODER_WARN("Decode failed to start, shutting down decoder");
DecodeError();
}
}
nsresult
MediaDecoderStateMachine::DecodeFirstFrame()
{
AssertCurrentThreadInMonitor();
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
DECODER_LOG("DecodeFirstFrame started");
if (HasAudio()) {
RefPtr<nsIRunnable> decodeTask(
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded));
@ -1980,11 +2087,11 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
if (mScheduler->IsRealTime()) {
SetStartTime(0);
res = FinishDecodeMetadata();
nsresult res = FinishDecodeFirstFrame();
NS_ENSURE_SUCCESS(res, res);
} else if (mDecodingFrozenAtStateMetadata) {
SetStartTime(mStartTime);
res = FinishDecodeMetadata();
nsresult res = FinishDecodeFirstFrame();
NS_ENSURE_SUCCESS(res, res);
} else {
if (HasAudio()) {
@ -2001,11 +2108,11 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
}
nsresult
MediaDecoderStateMachine::FinishDecodeMetadata()
MediaDecoderStateMachine::FinishDecodeFirstFrame()
{
AssertCurrentThreadInMonitor();
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
DECODER_LOG("FinishDecodeMetadata");
DECODER_LOG("FinishDecodeFirstFrame");
if (mState == DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;
@ -2044,19 +2151,34 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
mLowAudioThresholdUsecs /= NO_VIDEO_AMPLE_AUDIO_DIVISOR;
}
// Inform the element that we've loaded the metadata and the first frame.
// Get potentially updated metadata
{
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
mReader->ReadUpdatedMetadata(&mInfo);
}
nsAutoPtr<MediaInfo> info(new MediaInfo());
*info = mInfo;
nsCOMPtr<nsIRunnable> metadataLoadedEvent =
new MetadataEventRunner(mDecoder, info.forget(), mMetadataTags.forget());
NS_DispatchToMainThread(metadataLoadedEvent, NS_DISPATCH_NORMAL);
nsCOMPtr<nsIRunnable> event;
if (!mGotDurationFromMetaData) {
// We now have a duration, we can fire the LoadedMetadata and
// FirstFrame event.
event =
new MetadataUpdatedEventRunner(mDecoder,
info,
mMetadataTags);
} else {
// Inform the element that we've loaded the first frame.
event =
new FirstFrameLoadedEventRunner(mDecoder, info);
}
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
if (mState == DECODER_STATE_DECODING_METADATA) {
DECODER_LOG("Changed state from DECODING_METADATA to DECODING");
if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
StartDecoding();
}
// For very short media the metadata decode can decode the entire media.
// For very short media the first frame decode can decode the entire media.
// So we need to check if this has occurred, else our decode pipeline won't
// run (since it doesn't need to) and we won't detect end of stream.
CheckIfDecodeComplete();
@ -2068,6 +2190,10 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
StartPlayback();
}
if (mQueuedSeekTarget.IsValid()) {
EnqueueStartQueuedSeekTask();
}
return NS_OK;
}
@ -2214,6 +2340,7 @@ MediaDecoderStateMachine::SeekCompleted()
mDecoder->StartProgressUpdates();
if (mState == DECODER_STATE_DECODING_METADATA ||
mState == DECODER_STATE_DECODING_FIRSTFRAME ||
mState == DECODER_STATE_DORMANT ||
mState == DECODER_STATE_SHUTDOWN) {
return;
@ -2399,6 +2526,11 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
return NS_OK;
}
case DECODER_STATE_DECODING_FIRSTFRAME: {
// DECODER_STATE_DECODING_FIRSTFRAME will be started by DecodeMetadata
return NS_OK;
}
case DECODER_STATE_DECODING: {
if (mDecoder->GetState() != MediaDecoder::PLAY_STATE_PLAYING &&
IsPlaying())
@ -3117,15 +3249,15 @@ bool MediaDecoderStateMachine::IsShutdown()
}
void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime,
MediaInfo* aInfo,
MetadataTags* aTags)
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
AssertCurrentThreadInMonitor();
TimedMetadata* metadata = new TimedMetadata;
metadata->mPublishTime = aPublishTime;
metadata->mInfo = aInfo;
metadata->mTags = aTags;
metadata->mInfo = aInfo.forget();
metadata->mTags = aTags.forget();
mMetadataManager.QueueMetadata(metadata);
}

View File

@ -138,6 +138,7 @@ public:
DECODER_STATE_DECODING_NONE,
DECODER_STATE_DECODING_METADATA,
DECODER_STATE_WAIT_FOR_RESOURCES,
DECODER_STATE_DECODING_FIRSTFRAME,
DECODER_STATE_DORMANT,
DECODER_STATE_DECODING,
DECODER_STATE_SEEKING,
@ -198,8 +199,22 @@ public:
void Play();
// Seeks to the decoder to aTarget asynchronously.
// Must be called from the main thread.
void Seek(const SeekTarget& aTarget);
// Dispatches a task to the main thread to seek to mQueuedSeekTarget.
// This is threadsafe and can be called on any thread.
void EnqueueStartQueuedSeekTask();
// Seeks to the decoder to mQueuedSeekTarget asynchronously.
// Must be called from the main thread.
void StartQueuedSeek();
// Seeks to the decoder to aTarget asynchronously.
// Must be called from the main thread.
// The decoder monitor must be held with exactly one lock count.
void StartSeek(const SeekTarget& aTarget);
// Returns the current playback position in seconds.
// Called from the main thread to get the current frame time. The decoder
// monitor must be obtained before calling this.
@ -321,7 +336,9 @@ public:
// shutting down. The decoder monitor must be held while calling this.
bool IsShutdown();
void QueueMetadata(int64_t aPublishTime, MediaInfo* aInfo, MetadataTags* aTags);
void QueueMetadata(int64_t aPublishTime,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags);
// Returns true if we're currently playing. The decoder monitor must
// be held.
@ -397,7 +414,7 @@ protected:
MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
nsresult FinishDecodeMetadata();
nsresult FinishDecodeFirstFrame();
RefPtr<MediaDataDecodedListener<MediaDecoderStateMachine>> mMediaDecodedListener;
@ -526,6 +543,16 @@ protected:
// The decoder monitor must be held.
nsresult EnqueueDecodeMetadataTask();
// Dispatches a LoadedMetadataEvent.
// This is threadsafe and can be called on any thread.
// The decoder monitor must be held.
void EnqueueLoadedMetadataEvent();
// Dispatches a task to the decode task queue to begin decoding content.
// This is threadsafe and can be called on any thread.
// The decoder monitor must be held.
nsresult EnqueueDecodeFirstFrameTask();
// Dispatches a task to the decode task queue to seek the decoder.
// The decoder monitor must be held.
nsresult EnqueueDecodeSeekTask();
@ -583,9 +610,16 @@ protected:
// Wraps the call to DecodeMetadata(), signals a DecodeError() on failure.
void CallDecodeMetadata();
// Checks whether we're finished decoding metadata, and switches to DECODING
// state if so.
void MaybeFinishDecodeMetadata();
// Initiate first content decoding. Called on the decode thread.
// The decoder monitor must be held with exactly one lock count.
nsresult DecodeFirstFrame();
// Wraps the call to DecodeFirstFrame(), signals a DecodeError() on failure.
void CallDecodeFirstFrame();
// Checks whether we're finished decoding first audio and/or video packets,
// and switches to DECODING state if so.
void MaybeFinishDecodeFirstFrame();
// Seeks to mSeekTarget. Called on the decode thread. The decoder monitor
// must be held with exactly one lock count.
@ -727,6 +761,11 @@ protected:
// this value. Accessed on main and decode thread.
SeekTarget mSeekTarget;
// Position to seek to in microseconds when DecodeFirstFrame completes.
// The decoder monitor lock must be obtained before reading or writing
// this value. Accessed on main and decode thread.
SeekTarget mQueuedSeekTarget;
// The position that we're currently seeking to. This differs from
// mSeekTarget, as mSeekTarget is the target we'll seek to next, whereas
// mCurrentSeekTarget is the position that the decode is in the process

View File

@ -52,9 +52,9 @@ namespace mozilla {
NS_DispatchToMainThread(removeTracksEvent);
nsCOMPtr<nsIRunnable> metadataUpdatedEvent =
new MetadataEventRunner(aDecoder,
metadata->mInfo.forget(),
metadata->mTags.forget());
new MetadataUpdatedEventRunner(aDecoder,
metadata->mInfo,
metadata->mTags);
NS_DispatchToMainThread(metadataUpdatedEvent);
delete mMetadataQueue.popFirst();
metadata = mMetadataQueue.getFirst();

View File

@ -395,10 +395,6 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
nsresult rv = mAudio.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, rv);
// Decode one audio frame to detect potentially incorrect channels count or
// sampling rate from demuxer.
Decode(kAudio);
}
if (HasVideo()) {
@ -431,6 +427,12 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
void
MP4Reader::ReadUpdatedMetadata(MediaInfo* aInfo)
{
*aInfo = mInfo;
}
bool
MP4Reader::IsMediaSeekable()
{

View File

@ -45,6 +45,8 @@ public:
virtual nsresult ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags) MOZ_OVERRIDE;
virtual void ReadUpdatedMetadata(MediaInfo* aInfo) MOZ_OVERRIDE;
virtual void Seek(int64_t aTime,
int64_t aStartTime,
int64_t aEndTime,

View File

@ -411,7 +411,7 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
// Enable sandboxing here -- we know the plugin file's path, but
// this process's execution hasn't been affected by its content yet.
if (mozilla::CanSandboxMediaPlugin()) {
if (mozilla::MediaPluginSandboxStatus() != mozilla::kSandboxingWouldFail) {
mozilla::SetMediaPluginSandbox(nativePath.get());
} else {
printf_stderr("GMPChild::LoadPluginLibrary: Loading media plugin %s unsandboxed.\n",

View File

@ -963,7 +963,7 @@ GMPParent::ReadGMPMetaData()
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (cap->mAPIName.EqualsLiteral("eme-decrypt") &&
!mozilla::CanSandboxMediaPlugin()) {
mozilla::MediaPluginSandboxStatus() == mozilla::kSandboxingWouldFail) {
printf_stderr("GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
" but this system can't sandbox it; not loading.\n",
mDisplayName.get());

View File

@ -479,7 +479,7 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
GMPDecryptorProxy** aDecryptor)
{
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (!mozilla::CanSandboxMediaPlugin()) {
if (mozilla::MediaPluginSandboxStatus() == kSandboxingWouldFail) {
NS_WARNING("GeckoMediaPluginService::GetGMPDecryptor: "
"EME decryption not available without sandboxing support.");
return NS_ERROR_NOT_AVAILABLE;

View File

@ -24,8 +24,8 @@ public:
}
virtual void FireTimeUpdate(bool aPeriodic) MOZ_OVERRIDE {}
virtual bool GetPaused() MOZ_OVERRIDE { return false; }
virtual void MetadataLoaded(const MediaInfo* aInfo, const MetadataTags* aTags)
MOZ_OVERRIDE
virtual void MetadataLoaded(const MediaInfo* aInfo,
nsAutoPtr<const MetadataTags> aTags) MOZ_OVERRIDE
{
}
virtual void NetworkError() MOZ_OVERRIDE {}

View File

@ -90,13 +90,22 @@ SourceBufferDecoder::IsMediaSeekable()
}
void
SourceBufferDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
SourceBufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
{
MSE_DEBUG("SourceBufferDecoder(%p)::MetadataLoaded UNIMPLEMENTED", this);
}
void
SourceBufferDecoder::QueueMetadata(int64_t aTime, MediaInfo* aInfo, MetadataTags* aTags)
SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo)
{
MSE_DEBUG("SourceBufferDecoder(%p)::FirstFrameLoaded UNIMPLEMENTED", this);
}
void
SourceBufferDecoder::QueueMetadata(int64_t aTime,
nsAutoPtr<MediaInfo> aInfo,
nsAutoPtr<MetadataTags> aTags)
{
MSE_DEBUG("SourceBufferDecoder(%p)::QueueMetadata UNIMPLEMENTED", this);
}

View File

@ -47,13 +47,14 @@ public:
virtual SourceBufferResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE;
virtual ReentrantMonitor& GetReentrantMonitor() MOZ_FINAL MOZ_OVERRIDE;
virtual VideoFrameContainer* GetVideoFrameContainer() MOZ_FINAL MOZ_OVERRIDE;
virtual void MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo) MOZ_FINAL MOZ_OVERRIDE;
virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) MOZ_FINAL MOZ_OVERRIDE;
virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded) MOZ_FINAL MOZ_OVERRIDE;
virtual void NotifyWaitingForResourcesStatusChanged() MOZ_FINAL MOZ_OVERRIDE;
virtual void OnReadMetadataCompleted() MOZ_FINAL MOZ_OVERRIDE;
virtual void QueueMetadata(int64_t aTime, MediaInfo* aInfo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE;
virtual void SetMediaDuration(int64_t aDuration) MOZ_FINAL MOZ_OVERRIDE;
virtual void SetMediaEndTime(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;

View File

@ -10,9 +10,11 @@ support-files =
[test_BufferedSeek.html]
[test_FrameSelection.html]
[test_HaveMetadataUnbufferedSeek.html]
[test_LoadedMetadataFired.html]
[test_SeekableAfterEndOfStream.html]
[test_SeekableAfterEndOfStreamSplit.html]
[test_SeekableBeforeEndOfStream.html]
[test_SeekableBeforeEndOfStreamSplit.html]
[test_SplitAppendDelay.html]
[test_SplitAppend.html]

View File

@ -0,0 +1,35 @@
<!DOCTYPE HTML>
<html>
<head>
<title>MSE: append initialization only</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function (ms, v) {
ms.addEventListener("sourceopen", function () {
var sb = ms.addSourceBuffer("video/webm");
v.addEventListener("loadedmetadata", function () {
ok(true, "Got loadedmetadata event");
SimpleTest.finish();
});
fetchWithXHR("seek.webm", function (arrayBuffer) {
sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
v.play();
});
});
});
</script>
</pre>
</body>
</html>

View File

@ -753,7 +753,7 @@ bool OggReader::ReadOggChain()
OpusState* newOpusState = nullptr;
#endif /* MOZ_OPUS */
VorbisState* newVorbisState = nullptr;
MetadataTags* tags = nullptr;
nsAutoPtr<MetadataTags> tags;
if (HasVideo() || HasSkeleton() || !HasAudio()) {
return false;
@ -846,7 +846,7 @@ bool OggReader::ReadOggChain()
*info = mInfo;
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->QueueMetadata((mDecodedAudioFrames * USECS_PER_S) / mInfo.mAudio.mRate,
info.forget(), tags);
info, tags);
}
return true;
}

View File

@ -57,11 +57,10 @@ MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
}
void
MediaOmxCommonDecoder::MetadataLoaded(MediaInfo* aInfo,
MetadataTags* aTags)
MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo)
{
MOZ_ASSERT(NS_IsMainThread());
MediaDecoder::MetadataLoaded(aInfo, aTags);
MediaDecoder::FirstFrameLoaded(aInfo);
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (!CheckDecoderCanOffloadAudio()) {

View File

@ -23,8 +23,7 @@ class MediaOmxCommonDecoder : public MediaDecoder
public:
MediaOmxCommonDecoder();
virtual void MetadataLoaded(MediaInfo* aInfo,
MetadataTags* aTags);
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo);
virtual void ChangeState(PlayState aState);
virtual void ApplyStateToStateMachine(PlayState aState);
virtual void SetVolume(double aVolume);

View File

@ -139,8 +139,8 @@ function PlayFragmented(test, elem, token)
sb.appendBuffer(new Uint8Array(req.response));
});
req.addEventListener("error", bail(token + " error fetching " + fragmentFile));
req.addEventListener("abort", bail(token + " aborted fetching " + fragmentFile));
req.addEventListener("error", function(){info(token + " error fetching " + fragmentFile);});
req.addEventListener("abort", function(){info(token + " aborted fetching " + fragmentFile);});
Log(token, "addNextFragment() fetching next fragment " + fragmentFile);
req.send(null);

View File

@ -26,7 +26,7 @@ function finish(v) {
manager.finished(v.token);
}
function onLoadedMetadata_Audio(e) {
function onLoadedData_Audio(e) {
var t = e.target;
is(t.videoHeight, 0, t.name + ": videoHeight should be zero when there is no video.");
is(t.videoWidth, 0, t.name + ": videoWidth should be zero when there is no video.");
@ -62,14 +62,14 @@ function onTimeUpdate_Video(e) {
todo("No audio file available.")
finish(t);
} else {
t.removeEventListener("loadedmetadata", onLoadedMetadata_Video);
t.addEventListener("loadedmetadata", onLoadedMetadata_Audio);
t.removeEventListener("loadeddata", onLoadedData_Video);
t.addEventListener("loadeddata", onLoadedData_Audio);
t.src = source.name;
}
}
}
function onLoadedMetadata_Video(e) {
function onLoadedData_Video(e) {
var t = e.target;
isnot(t.videoHeight, 0, t.name + ": We should have a videoHeight.");
isnot(t.videoWidth, 0, t.name + ": We should have a videoWidth.");
@ -80,12 +80,12 @@ function onLoadedMetadata_Video(e) {
function startTest(test, token) {
var v = document.createElement('video');
document.body.appendChild(v);
v.preload = "metadata";
v._firstTime = true;
v.addEventListener("loadedmetadata", onLoadedMetadata_Video);
v.addEventListener("loadeddata", onLoadedData_Video);
v.src = test.name;
v.token = token;
v.name = test.name;
v.play();
manager.started(token);
}

View File

@ -140,13 +140,19 @@ BufferDecoder::IsMediaSeekable()
}
void
BufferDecoder::MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags)
BufferDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags)
{
// ignore
}
void
BufferDecoder::QueueMetadata(int64_t aTime, MediaInfo* aInfo, MetadataTags* aTags)
BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo)
{
// ignore
}
void
BufferDecoder::QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags)
{
// ignore
}

View File

@ -58,8 +58,9 @@ public:
virtual bool IsMediaSeekable() MOZ_FINAL MOZ_OVERRIDE;
virtual void MetadataLoaded(MediaInfo* aInfo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void QueueMetadata(int64_t aTime, MediaInfo* aInfo, MetadataTags* aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void QueueMetadata(int64_t aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) MOZ_FINAL MOZ_OVERRIDE;
virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo) MOZ_FINAL MOZ_OVERRIDE;
virtual void RemoveMediaTracks() MOZ_FINAL MOZ_OVERRIDE;

View File

@ -19,8 +19,11 @@
SimpleTest.expectChildProcessCrash();
crashAndGetCrashServiceRecord("crash", function (cm, crash) {
ok(crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_CRASH),
"Record should be a plugin crash");
var isPluginCrash = crash.isOfType(cm.PROCESS_TYPE_PLUGIN, cm.CRASH_TYPE_CRASH);
ok(isPluginCrash, "Record should be a plugin crash");
if (!isPluginCrash) {
dump("Crash type: " + crash.type + "\n");
}
SimpleTest.finish();
});

View File

@ -15,12 +15,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1027221
SimpleTest.waitForExplicitFinish();
var x = "x";
// Trigger some incremental gc
SpecialPowers.Cu.getJSTestingFunctions().gcslice(0);
SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
// Kick off a worker that uses this same atom
var w = new Worker("data:text/plain,Promise.resolve('x').then(function() { postMessage(1); });");
// Maybe trigger some more incremental gc
SpecialPowers.Cu.getJSTestingFunctions().gcslice(0);
SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
w.onmessage = function() {
ok(true, "Got here");

View File

@ -37,6 +37,9 @@
#include <algorithm>
// 2^23
#define CAIRO_COORD_MAX (Float(0x7fffff))
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedCairoSurface, cairo_surface_t, cairo_surface_destroy);
@ -90,6 +93,64 @@ private:
cairo_t* mCtx;
};
/* Clamp r to (0,0) (2^23,2^23)
* these are to be device coordinates.
*
* Returns false if the rectangle is completely out of bounds,
* true otherwise.
*
* This function assumes that it will be called with a rectangle being
* drawn into a surface with an identity transformation matrix; that
* is, anything above or to the left of (0,0) will be offscreen.
*
* First it checks if the rectangle is entirely beyond
* CAIRO_COORD_MAX; if so, it can't ever appear on the screen --
* false is returned.
*
* Then it shifts any rectangles with x/y < 0 so that x and y are = 0,
* and adjusts the width and height appropriately. For example, a
* rectangle from (0,-5) with dimensions (5,10) will become a
* rectangle from (0,0) with dimensions (5,5).
*
* If after negative x/y adjustment to 0, either the width or height
* is negative, then the rectangle is completely offscreen, and
* nothing is drawn -- false is returned.
*
* Finally, if x+width or y+height are greater than CAIRO_COORD_MAX,
* the width and height are clamped such x+width or y+height are equal
* to CAIRO_COORD_MAX, and true is returned.
*/
static bool
ConditionRect(Rect& r) {
// if either x or y is way out of bounds;
// note that we don't handle negative w/h here
if (r.X() > CAIRO_COORD_MAX || r.Y() > CAIRO_COORD_MAX)
return false;
if (r.X() < 0.f) {
r.width += r.X();
if (r.width < 0.f)
return false;
r.x = 0.f;
}
if (r.XMost() > CAIRO_COORD_MAX) {
r.width = CAIRO_COORD_MAX - r.X();
}
if (r.Y() < 0.f) {
r.height += r.Y();
if (r.Height() < 0.f)
return false;
r.y = 0.f;
}
if (r.YMost() > CAIRO_COORD_MAX) {
r.height = CAIRO_COORD_MAX - r.Y();
}
return true;
}
} // end anonymous namespace
@ -856,16 +917,50 @@ DrawTargetCairo::FillRect(const Rect &aRect,
{
AutoPrepareForDrawing prep(this, mContext);
bool restoreTransform = false;
Matrix mat;
Rect r = aRect;
/* Clamp coordinates to work around a design bug in cairo */
if (r.width > CAIRO_COORD_MAX ||
r.height > CAIRO_COORD_MAX ||
r.x < -CAIRO_COORD_MAX ||
r.x > CAIRO_COORD_MAX ||
r.y < -CAIRO_COORD_MAX ||
r.y > CAIRO_COORD_MAX)
{
if (!mat.IsRectilinear()) {
gfxWarning() << "DrawTargetCairo::FillRect() misdrawing huge Rect "
"with non-rectilinear transform";
}
mat = GetTransform();
r = mat.TransformBounds(r);
if (!ConditionRect(r)) {
gfxWarning() << "Ignoring DrawTargetCairo::FillRect() call with "
"out-of-bounds Rect";
return;
}
restoreTransform = true;
SetTransform(Matrix());
}
cairo_new_path(mContext);
cairo_rectangle(mContext, aRect.x, aRect.y, aRect.Width(), aRect.Height());
cairo_rectangle(mContext, r.x, r.y, r.Width(), r.Height());
bool pathBoundsClip = false;
if (aRect.Contains(GetUserSpaceClip())) {
if (r.Contains(GetUserSpaceClip())) {
pathBoundsClip = true;
}
DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL, pathBoundsClip);
if (restoreTransform) {
SetTransform(mat);
}
}
void

View File

@ -31,7 +31,6 @@
#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
#include "nsIWidget.h" // for nsIWidget, NS_NATIVE_WINDOW
#include "nsRect.h" // for nsRect
#include "nsRenderingContext.h" // for nsRenderingContext
#include "nsServiceManagerUtils.h" // for do_GetService
#include "nsString.h" // for nsDependentString
#include "nsTArray.h" // for nsTArray, nsTArray_Impl

View File

@ -19,7 +19,7 @@ interface nsIPrincipal;
* @version 0.1
* @see imagelib2
*/
[scriptable, builtinclass, uuid(078f159c-28ee-4653-9cfe-633132152f47)]
[scriptable, builtinclass, uuid(710f22f0-558b-11e4-8ed6-0800200c9a66)]
interface imgIRequest : nsIRequest
{
/**
@ -42,14 +42,6 @@ interface imgIRequest : nsIRequest
* and height of the image, and have thus called SetSize()
* on the container.
*
* STATUS_LOAD_PARTIAL: Used internally by imgRequest to
* flag that a request is being cancelled as a result of
* a failure of a proxy holder and not an internal failure.
* At least I think that's what it does. Regardless, there's
* no reason for this flag to be public, and it should either
* go away or become a private state flag within imgRequest.
* Don't rely on it.
*
* STATUS_LOAD_COMPLETE: The data has been fully loaded
* to memory, but not necessarily fully decoded.
*
@ -66,12 +58,11 @@ interface imgIRequest : nsIRequest
//@{
const long STATUS_NONE = 0x0;
const long STATUS_SIZE_AVAILABLE = 0x1;
const long STATUS_LOAD_PARTIAL = 0x2;
const long STATUS_LOAD_COMPLETE = 0x4;
const long STATUS_ERROR = 0x8;
const long STATUS_DECODE_STARTED = 0x10;
const long STATUS_FRAME_COMPLETE = 0x20;
const long STATUS_DECODE_COMPLETE = 0x40;
const long STATUS_LOAD_COMPLETE = 0x2;
const long STATUS_ERROR = 0x4;
const long STATUS_DECODE_STARTED = 0x8;
const long STATUS_FRAME_COMPLETE = 0x10;
const long STATUS_DECODE_COMPLETE = 0x20;
//@}
/**

View File

@ -109,13 +109,6 @@ public:
*/
virtual void OnStopRequest(bool aIsLastPart, nsresult aStatus) = 0;
/**
* Called when the decoded image data is discarded. This means that the frames
* no longer exist in decoded form, and any attempt to access or draw the
* image will initiate a new series of progressive decode notifications.
*/
virtual void OnDiscard() = 0;
/**
* Called when we are asked to Draw an image that is not locked.
*/

View File

@ -107,14 +107,6 @@ public:
tracker->RecordStopRequest(aLastPart, aStatus);
}
virtual void OnDiscard() MOZ_OVERRIDE
{
LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnDiscard");
nsRefPtr<imgStatusTracker> tracker = mTracker.get();
if (!tracker) { return; }
tracker->RecordDiscard();
}
virtual void OnUnlockedDraw() MOZ_OVERRIDE
{
LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw");
@ -151,24 +143,16 @@ private:
// imgStatusTracker methods
imgStatusTracker::imgStatusTracker(Image* aImage)
: mImage(aImage),
mState(0),
mImageStatus(imgIRequest::STATUS_NONE),
mIsMultipart(false),
mHadLastPart(false),
mHasBeenDecoded(false)
: mImage(aImage)
, mState(0)
{
mTrackerObserver = new imgStatusTrackerObserver(this);
}
// Private, used only by CloneForRecording.
imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
: mImage(aOther.mImage),
mState(aOther.mState),
mImageStatus(aOther.mImageStatus),
mIsMultipart(aOther.mIsMultipart),
mHadLastPart(aOther.mHadLastPart),
mHasBeenDecoded(aOther.mHasBeenDecoded)
: mImage(aOther.mImage)
, mState(aOther.mState)
// Note: we explicitly don't copy several fields:
// - mRequestRunnable, because it won't be nulled out when the
// mRequestRunnable's Run function eventually gets called.
@ -218,19 +202,46 @@ imgStatusTracker::ResetImage()
mImage = nullptr;
}
void imgStatusTracker::SetIsMultipart()
{
mState |= FLAG_IS_MULTIPART;
}
bool
imgStatusTracker::IsLoading() const
{
// Checking for whether OnStopRequest has fired allows us to say we're
// loading before OnStartRequest gets called, letting the request properly
// get removed from the cache in certain cases.
return !(mState & stateRequestStopped);
return !(mState & FLAG_REQUEST_STOPPED);
}
uint32_t
imgStatusTracker::GetImageStatus() const
{
return mImageStatus;
uint32_t status = imgIRequest::STATUS_NONE;
// Translate our current state to a set of imgIRequest::STATE_* flags.
if (mState & FLAG_HAS_SIZE) {
status |= imgIRequest::STATUS_SIZE_AVAILABLE;
}
if (mState & FLAG_DECODE_STARTED) {
status |= imgIRequest::STATUS_DECODE_STARTED;
}
if (mState & FLAG_DECODE_STOPPED) {
status |= imgIRequest::STATUS_DECODE_COMPLETE;
}
if (mState & FLAG_FRAME_STOPPED) {
status |= imgIRequest::STATUS_FRAME_COMPLETE;
}
if (mState & FLAG_REQUEST_STOPPED) {
status |= imgIRequest::STATUS_LOAD_COMPLETE;
}
if (mState & FLAG_HAS_ERROR) {
status |= imgIRequest::STATUS_ERROR;
}
return status;
}
// A helper class to allow us to call SyncNotify asynchronously.
@ -371,23 +382,23 @@ imgStatusTracker::NotifyCurrentState(imgRequestProxy* proxy)
/* static */ void
imgStatusTracker::SyncNotifyState(ProxyArray& proxies,
bool hasImage, uint32_t state,
nsIntRect& dirtyRect, bool hadLastPart)
nsIntRect& dirtyRect)
{
MOZ_ASSERT(NS_IsMainThread());
// OnStartRequest
if (state & stateRequestStarted)
if (state & FLAG_REQUEST_STARTED)
NOTIFY_IMAGE_OBSERVERS(OnStartRequest());
// OnStartContainer
if (state & stateHasSize)
if (state & FLAG_HAS_SIZE)
NOTIFY_IMAGE_OBSERVERS(OnStartContainer());
// OnStartDecode
if (state & stateDecodeStarted)
if (state & FLAG_DECODE_STARTED)
NOTIFY_IMAGE_OBSERVERS(OnStartDecode());
// BlockOnload
if (state & stateBlockingOnload)
if (state & FLAG_ONLOAD_BLOCKED)
NOTIFY_IMAGE_OBSERVERS(BlockOnload());
if (hasImage) {
@ -398,21 +409,28 @@ imgStatusTracker::SyncNotifyState(ProxyArray& proxies,
if (!dirtyRect.IsEmpty())
NOTIFY_IMAGE_OBSERVERS(OnFrameUpdate(&dirtyRect));
if (state & stateFrameStopped)
if (state & FLAG_FRAME_STOPPED)
NOTIFY_IMAGE_OBSERVERS(OnStopFrame());
// OnImageIsAnimated
if (state & stateImageIsAnimated)
if (state & FLAG_IS_ANIMATED)
NOTIFY_IMAGE_OBSERVERS(OnImageIsAnimated());
}
if (state & stateDecodeStopped) {
// Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
// observers that can fire events when they receive those notifications to do
// so then, instead of being forced to wait for UnblockOnload.
if (state & FLAG_ONLOAD_UNBLOCKED) {
NOTIFY_IMAGE_OBSERVERS(UnblockOnload());
}
if (state & FLAG_DECODE_STOPPED) {
NS_ABORT_IF_FALSE(hasImage, "stopped decoding without ever having an image?");
NOTIFY_IMAGE_OBSERVERS(OnStopDecode());
}
if (state & stateRequestStopped) {
NOTIFY_IMAGE_OBSERVERS(OnStopRequest(hadLastPart));
if (state & FLAG_REQUEST_STOPPED) {
NOTIFY_IMAGE_OBSERVERS(OnStopRequest(state & FLAG_MULTIPART_STOPPED));
}
}
@ -421,27 +439,15 @@ imgStatusTracker::Difference(imgStatusTracker* aOther) const
{
MOZ_ASSERT(aOther, "aOther cannot be null");
ImageStatusDiff diff;
diff.diffState = ~mState & aOther->mState & ~stateRequestStarted;
diff.diffImageStatus = ~mImageStatus & aOther->mImageStatus;
diff.unblockedOnload = mState & stateBlockingOnload && !(aOther->mState & stateBlockingOnload);
diff.unsetDecodeStarted = mImageStatus & imgIRequest::STATUS_DECODE_STARTED
&& !(aOther->mImageStatus & imgIRequest::STATUS_DECODE_STARTED);
diff.foundError = (mImageStatus != imgIRequest::STATUS_ERROR)
&& (aOther->mImageStatus == imgIRequest::STATUS_ERROR);
MOZ_ASSERT(!mIsMultipart || aOther->mIsMultipart, "mIsMultipart should be monotonic");
diff.foundIsMultipart = !mIsMultipart && aOther->mIsMultipart;
diff.foundLastPart = !mHadLastPart && aOther->mHadLastPart;
diff.gotDecoded = !mHasBeenDecoded && aOther->mHasBeenDecoded;
diff.diffState = ~mState & aOther->mState;
// Only record partial invalidations if we haven't been decoded before.
// When images are re-decoded after discarding, we don't want to display
// partially decoded versions to the user.
const uint32_t combinedStatus = mImageStatus | aOther->mImageStatus;
const bool doInvalidations = !(mHasBeenDecoded || aOther->mHasBeenDecoded)
|| combinedStatus & imgIRequest::STATUS_ERROR
|| combinedStatus & imgIRequest::STATUS_DECODE_COMPLETE;
const uint32_t combinedState = mState | aOther->mState;
const bool doInvalidations = !(mState & FLAG_DECODE_STOPPED) ||
aOther->mState & FLAG_DECODE_STOPPED ||
combinedState & FLAG_HAS_ERROR;
// Record and reset the invalid rectangle.
// XXX(seth): We shouldn't be resetting anything here; see bug 910441.
@ -457,11 +463,8 @@ ImageStatusDiff
imgStatusTracker::DecodeStateAsDifference() const
{
ImageStatusDiff diff;
diff.diffState = mState & ~stateRequestStarted;
// All other ImageStatusDiff fields are intentionally left at their default
// values; we only want to notify decode state changes.
// XXX(seth): Is FLAG_REQUEST_STARTED really the only non-"decode state" flag?
diff.diffState = mState & ~FLAG_REQUEST_STARTED;
return diff;
}
@ -469,29 +472,7 @@ void
imgStatusTracker::ApplyDifference(const ImageStatusDiff& aDiff)
{
LOG_SCOPE(GetImgLog(), "imgStatusTracker::ApplyDifference");
// We must not modify or notify for the start-load state, which happens from Necko callbacks.
uint32_t loadState = mState & stateRequestStarted;
// Synchronize our state.
mState |= aDiff.diffState | loadState;
if (aDiff.unblockedOnload)
mState &= ~stateBlockingOnload;
mIsMultipart = mIsMultipart || aDiff.foundIsMultipart;
mHadLastPart = mHadLastPart || aDiff.foundLastPart;
mHasBeenDecoded = mHasBeenDecoded || aDiff.gotDecoded;
// Update the image status. There are some subtle points which are handled below.
mImageStatus |= aDiff.diffImageStatus;
// Unset bits which can get unset as part of the decoding process.
if (aDiff.unsetDecodeStarted)
mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
// The error state is sticky and overrides all other bits.
if (mImageStatus & imgIRequest::STATUS_ERROR)
mImageStatus = imgIRequest::STATUS_ERROR;
mState |= aDiff.diffState;
}
void
@ -502,24 +483,11 @@ imgStatusTracker::SyncNotifyDifference(const ImageStatusDiff& diff)
nsIntRect invalidRect = mInvalidRect.Union(diff.invalidRect);
SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect, mHadLastPart);
SyncNotifyState(mConsumers, !!mImage, diff.diffState, invalidRect);
mInvalidRect.SetEmpty();
if (diff.unblockedOnload) {
ProxyArray::ForwardIterator iter(mConsumers);
while (iter.HasMore()) {
// Hold on to a reference to this proxy, since notifying the state can
// cause it to disappear.
nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
if (proxy && !proxy->NotificationsDeferred()) {
SendUnblockOnload(proxy);
}
}
}
if (diff.foundError) {
if (diff.diffState & FLAG_HAS_ERROR) {
FireFailureNotification();
}
}
@ -554,7 +522,7 @@ imgStatusTracker::SyncNotify(imgRequestProxy* proxy)
ProxyArray array;
array.AppendElement(proxy);
SyncNotifyState(array, !!mImage, mState, r, mHadLastPart);
SyncNotifyState(array, !!mImage, mState, r);
}
void
@ -567,15 +535,15 @@ imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
// In certain cases the request might not have started yet.
// We still need to fulfill the contract.
if (!(mState & stateRequestStarted)) {
if (!(mState & FLAG_REQUEST_STARTED)) {
aProxy->OnStartRequest();
}
if (mState & stateBlockingOnload) {
if (mState & FLAG_ONLOAD_BLOCKED && !(mState & FLAG_ONLOAD_UNBLOCKED)) {
aProxy->UnblockOnload();
}
if (!(mState & stateRequestStopped)) {
if (!(mState & FLAG_REQUEST_STOPPED)) {
aProxy->OnStopRequest(true);
}
}
@ -629,34 +597,29 @@ imgStatusTracker::FirstConsumerIs(imgRequestProxy* aConsumer)
void
imgStatusTracker::RecordCancel()
{
if (!(mImageStatus & imgIRequest::STATUS_LOAD_PARTIAL))
mImageStatus = imgIRequest::STATUS_ERROR;
mState |= FLAG_HAS_ERROR;
}
void
imgStatusTracker::RecordLoaded()
{
NS_ABORT_IF_FALSE(mImage, "RecordLoaded called before we have an Image");
mState |= stateRequestStarted | stateHasSize | stateRequestStopped;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE;
mHadLastPart = true;
mState |= FLAG_REQUEST_STARTED | FLAG_HAS_SIZE |
FLAG_REQUEST_STOPPED | FLAG_MULTIPART_STOPPED;
}
void
imgStatusTracker::RecordDecoded()
{
NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image");
mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped;
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
mState |= FLAG_DECODE_STARTED | FLAG_DECODE_STOPPED | FLAG_FRAME_STOPPED;
}
void
imgStatusTracker::RecordStartDecode()
{
NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image");
mState |= stateDecodeStarted;
mImageStatus |= imgIRequest::STATUS_DECODE_STARTED;
mState |= FLAG_DECODE_STARTED;
}
void
@ -674,8 +637,7 @@ imgStatusTracker::RecordStartContainer(imgIContainer* aContainer)
"RecordStartContainer called before we have an Image");
NS_ABORT_IF_FALSE(mImage == aContainer,
"RecordStartContainer called with wrong Image");
mState |= stateHasSize;
mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
mState |= FLAG_HAS_SIZE;
}
void
@ -698,8 +660,7 @@ void
imgStatusTracker::RecordStopFrame()
{
NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image");
mState |= stateFrameStopped;
mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE;
mState |= FLAG_FRAME_STOPPED;
}
void
@ -713,17 +674,12 @@ imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy)
void
imgStatusTracker::RecordStopDecode(nsresult aStatus)
{
NS_ABORT_IF_FALSE(mImage,
"RecordStopDecode called before we have an Image");
mState |= stateDecodeStopped;
MOZ_ASSERT(mImage, "RecordStopDecode called before we have an Image");
if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR) {
mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
mHasBeenDecoded = true;
// If we weren't successful, clear all success status bits and set error.
} else {
mImageStatus = imgIRequest::STATUS_ERROR;
mState |= FLAG_DECODE_STOPPED;
if (NS_FAILED(aStatus)) {
mState |= FLAG_HAS_ERROR;
}
}
@ -736,22 +692,6 @@ imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy,
aProxy->OnStopDecode();
}
void
imgStatusTracker::RecordDiscard()
{
NS_ABORT_IF_FALSE(mImage,
"RecordDiscard called before we have an Image");
// Clear the state bits we no longer deserve.
uint32_t stateBitsToClear = stateDecodeStopped;
mState &= ~stateBitsToClear;
// Clear the status bits we no longer deserve.
uint32_t statusBitsToClear = imgIRequest::STATUS_DECODE_STARTED |
imgIRequest::STATUS_FRAME_COMPLETE |
imgIRequest::STATUS_DECODE_COMPLETE;
mImageStatus &= ~statusBitsToClear;
}
void
imgStatusTracker::SendDiscard(imgRequestProxy* aProxy)
{
@ -773,7 +713,7 @@ imgStatusTracker::RecordImageIsAnimated()
{
NS_ABORT_IF_FALSE(mImage,
"RecordImageIsAnimated called before we have an Image");
mState |= stateImageIsAnimated;
mState |= FLAG_IS_ANIMATED;
}
void
@ -828,20 +768,17 @@ void
imgStatusTracker::RecordStartRequest()
{
// We're starting a new load, so clear any status and state bits indicating
// load/decode
mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
mImageStatus &= ~imgIRequest::STATUS_DECODE_COMPLETE;
mState &= ~stateRequestStarted;
mState &= ~stateDecodeStarted;
mState &= ~stateDecodeStopped;
mState &= ~stateRequestStopped;
mState &= ~stateBlockingOnload;
mState &= ~stateImageIsAnimated;
// load/decode.
// XXX(seth): Are these really the only flags we want to clear?
mState &= ~FLAG_REQUEST_STARTED;
mState &= ~FLAG_DECODE_STARTED;
mState &= ~FLAG_DECODE_STOPPED;
mState &= ~FLAG_REQUEST_STOPPED;
mState &= ~FLAG_ONLOAD_BLOCKED;
mState &= ~FLAG_ONLOAD_UNBLOCKED;
mState &= ~FLAG_IS_ANIMATED;
mState |= stateRequestStarted;
mState |= FLAG_REQUEST_STARTED;
}
void
@ -870,14 +807,13 @@ void
imgStatusTracker::RecordStopRequest(bool aLastPart,
nsresult aStatus)
{
mHadLastPart = aLastPart;
mState |= stateRequestStopped;
// If we were successful in loading, note that the image is complete.
if (NS_SUCCEEDED(aStatus) && mImageStatus != imgIRequest::STATUS_ERROR)
mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
else
mImageStatus = imgIRequest::STATUS_ERROR;
mState |= FLAG_REQUEST_STOPPED;
if (aLastPart) {
mState |= FLAG_MULTIPART_STOPPED;
}
if (NS_FAILED(aStatus)) {
mState |= FLAG_HAS_ERROR;
}
}
void
@ -927,7 +863,7 @@ imgStatusTracker::OnStopRequest(bool aLastPart,
new OnStopRequestEvent(this, aLastPart, aStatus));
return;
}
bool preexistingError = mImageStatus == imgIRequest::STATUS_ERROR;
bool preexistingError = mState & FLAG_HAS_ERROR;
RecordStopRequest(aLastPart, aStatus);
/* notify the kids */
@ -948,7 +884,6 @@ void
imgStatusTracker::OnDiscard()
{
MOZ_ASSERT(NS_IsMainThread());
RecordDiscard();
/* notify the kids */
ProxyArray::ForwardIterator iter(mConsumers);
@ -1016,8 +951,7 @@ imgStatusTracker::OnDataAvailable()
void
imgStatusTracker::RecordBlockOnload()
{
MOZ_ASSERT(!(mState & stateBlockingOnload));
mState |= stateBlockingOnload;
mState |= FLAG_ONLOAD_BLOCKED;
}
void
@ -1032,7 +966,11 @@ imgStatusTracker::SendBlockOnload(imgRequestProxy* aProxy)
void
imgStatusTracker::RecordUnblockOnload()
{
mState &= ~stateBlockingOnload;
// We sometimes unblock speculatively, so only actually unblock if we've
// previously blocked.
if (mState & FLAG_ONLOAD_BLOCKED) {
mState |= FLAG_ONLOAD_UNBLOCKED;
}
}
void
@ -1052,7 +990,7 @@ imgStatusTracker::MaybeUnblockOnload()
NS_NewRunnableMethod(this, &imgStatusTracker::MaybeUnblockOnload));
return;
}
if (!(mState & stateBlockingOnload)) {
if (!(mState & FLAG_ONLOAD_BLOCKED) || (mState & FLAG_ONLOAD_UNBLOCKED)) {
return;
}
@ -1070,7 +1008,7 @@ imgStatusTracker::MaybeUnblockOnload()
void
imgStatusTracker::RecordError()
{
mImageStatus = imgIRequest::STATUS_ERROR;
mState |= FLAG_HAS_ERROR;
}
void

View File

@ -27,18 +27,27 @@ namespace image {
class Image;
// Image state bitflags.
enum {
FLAG_REQUEST_STARTED = 1u << 0,
FLAG_HAS_SIZE = 1u << 1, // STATUS_SIZE_AVAILABLE
FLAG_DECODE_STARTED = 1u << 2, // STATUS_DECODE_STARTED
FLAG_DECODE_STOPPED = 1u << 3, // STATUS_DECODE_COMPLETE
FLAG_FRAME_STOPPED = 1u << 4, // STATUS_FRAME_COMPLETE
FLAG_REQUEST_STOPPED = 1u << 5, // STATUS_LOAD_COMPLETE
FLAG_ONLOAD_BLOCKED = 1u << 6,
FLAG_ONLOAD_UNBLOCKED = 1u << 7,
FLAG_IS_ANIMATED = 1u << 8,
FLAG_IS_MULTIPART = 1u << 9,
FLAG_MULTIPART_STOPPED = 1u << 10,
FLAG_HAS_ERROR = 1u << 11 // STATUS_ERROR
};
struct ImageStatusDiff
{
ImageStatusDiff()
: invalidRect()
, diffState(0)
, diffImageStatus(0)
, unblockedOnload(false)
, unsetDecodeStarted(false)
, foundError(false)
, foundIsMultipart(false)
, foundLastPart(false)
, gotDecoded(false)
{ }
static ImageStatusDiff NoChange() { return ImageStatusDiff(); }
@ -47,48 +56,16 @@ struct ImageStatusDiff
bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); }
bool operator==(const ImageStatusDiff& aOther) const {
return aOther.invalidRect == invalidRect
&& aOther.diffState == diffState
&& aOther.diffImageStatus == diffImageStatus
&& aOther.unblockedOnload == unblockedOnload
&& aOther.unsetDecodeStarted == unsetDecodeStarted
&& aOther.foundError == foundError
&& aOther.foundIsMultipart == foundIsMultipart
&& aOther.foundLastPart == foundLastPart
&& aOther.gotDecoded == gotDecoded;
&& aOther.diffState == diffState;
}
void Combine(const ImageStatusDiff& aOther) {
invalidRect = invalidRect.Union(aOther.invalidRect);
diffState |= aOther.diffState;
diffImageStatus |= aOther.diffImageStatus;
unblockedOnload = unblockedOnload || aOther.unblockedOnload;
unsetDecodeStarted = unsetDecodeStarted || aOther.unsetDecodeStarted;
foundError = foundError || aOther.foundError;
foundIsMultipart = foundIsMultipart || aOther.foundIsMultipart;
foundLastPart = foundLastPart || aOther.foundLastPart;
gotDecoded = gotDecoded || aOther.gotDecoded;
}
nsIntRect invalidRect;
uint32_t diffState;
uint32_t diffImageStatus;
bool unblockedOnload : 1;
bool unsetDecodeStarted : 1;
bool foundError : 1;
bool foundIsMultipart : 1;
bool foundLastPart : 1;
bool gotDecoded : 1;
};
enum {
stateRequestStarted = 1u << 0,
stateHasSize = 1u << 1,
stateDecodeStarted = 1u << 2,
stateDecodeStopped = 1u << 3,
stateFrameStopped = 1u << 4,
stateRequestStopped = 1u << 5,
stateBlockingOnload = 1u << 6,
stateImageIsAnimated = 1u << 7
};
} // namespace image
@ -130,7 +107,7 @@ public:
void ResetImage();
// Inform this status tracker that it is associated with a multipart image.
void SetIsMultipart() { mIsMultipart = true; }
void SetIsMultipart();
// Schedule an asynchronous "replaying" of all the notifications that would
// have to happen to put us in the current state.
@ -220,7 +197,6 @@ public:
void SendStopFrame(imgRequestProxy* aProxy);
void RecordStopDecode(nsresult statusg);
void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
void RecordDiscard();
void SendDiscard(imgRequestProxy* aProxy);
void RecordUnlockedDraw();
void SendUnlockedDraw(imgRequestProxy* aProxy);
@ -263,7 +239,7 @@ public:
void RecordError();
bool IsMultipart() const { return mIsMultipart; }
bool IsMultipart() const { return mState & mozilla::image::FLAG_IS_MULTIPART; }
// Weak pointer getters - no AddRefs.
inline already_AddRefed<mozilla::image::Image> GetImage() const {
@ -308,7 +284,7 @@ private:
// thread, and mConsumers is not threadsafe.
static void SyncNotifyState(ProxyArray& proxies,
bool hasImage, uint32_t state,
nsIntRect& dirtyRect, bool hadLastPart);
nsIntRect& dirtyRect);
nsCOMPtr<nsIRunnable> mRequestRunnable;
@ -327,10 +303,6 @@ private:
mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;
uint32_t mState;
uint32_t mImageStatus;
bool mIsMultipart : 1;
bool mHadLastPart : 1;
bool mHasBeenDecoded : 1;
};
class imgStatusTrackerInit

View File

@ -10,11 +10,37 @@ let prefBranch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch('image.mem.');
function isImgDecoded() {
function ImageDiscardObserver(result) {
this.discard = function onDiscard(request)
{
result.wasDiscarded = true;
this.synchronous = false;
}
this.synchronous = true;
}
function currentRequest() {
let img = gBrowser.getBrowserForTab(newTab).contentWindow
.document.getElementById('testImg');
img.QueryInterface(Ci.nsIImageLoadingContent);
let request = img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
return img.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
}
function attachDiscardObserver(result) {
// Create the discard observer.
let observer = new ImageDiscardObserver(result);
let scriptedObserver = Cc["@mozilla.org/image/tools;1"]
.getService(Ci.imgITools)
.createScriptedObserver(observer);
// Clone the current imgIRequest with our new observer.
let request = currentRequest();
return request.clone(scriptedObserver);
}
function isImgDecoded() {
let request = currentRequest();
return request.imageStatus & Ci.imgIRequest.STATUS_FRAME_COMPLETE ? true : false;
}
@ -43,6 +69,10 @@ function test() {
}
function step2() {
// Attach a discard listener and create a place to hold the result.
var result = { wasDiscarded: false };
var clonedRequest = attachDiscardObserver(result);
// Check that the image is decoded.
forceDecodeImg();
ok(isImgDecoded(), 'Image should initially be decoded.');
@ -53,10 +83,11 @@ function step2() {
var os = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
os.notifyObservers(null, 'memory-pressure', 'heap-minimize');
ok(!isImgDecoded(), 'Image should be discarded.');
ok(result.wasDiscarded, 'Image should be discarded.');
// And we're done.
gBrowser.removeTab(newTab);
prefBranch.setBoolPref('discardable', oldDiscardingPref);
clonedRequest.cancelAndForgetObserver(0);
finish();
}

View File

@ -11,11 +11,25 @@
namespace js {
struct JS_PUBLIC_API(TimeBudget)
{
int64_t budget;
explicit TimeBudget(int64_t milliseconds) { budget = milliseconds; }
};
struct JS_PUBLIC_API(WorkBudget)
{
int64_t budget;
explicit WorkBudget(int64_t work) { budget = work; }
};
/*
* This class records how much work has been done in a given collection slice, so that
* we can return before pausing for too long. Some slices are allowed to run for
* unlimited time, and others are bounded. To reduce the number of gettimeofday
* calls, we only check the time every 1000 operations.
* This class records how much work has been done in a given collection slice,
* so that we can return before pausing for too long. Some slices are allowed
* to run for unlimited time, and others are bounded. To reduce the number of
* gettimeofday calls, we only check the time every 1000 operations.
*/
struct JS_PUBLIC_API(SliceBudget)
{
@ -24,15 +38,16 @@ struct JS_PUBLIC_API(SliceBudget)
static const intptr_t CounterReset = 1000;
static const int64_t Unlimited = 0;
static int64_t TimeBudget(int64_t millis);
static int64_t WorkBudget(int64_t work);
static const int64_t Unlimited = -1;
/* Equivalent to SliceBudget(UnlimitedBudget). */
/* Use to create an unlimited budget. */
SliceBudget();
/* Instantiate as SliceBudget(Time/WorkBudget(n)). */
explicit SliceBudget(int64_t budget);
/* Instantiate as SliceBudget(TimeBudget(n)). */
explicit SliceBudget(TimeBudget time);
/* Instantiate as SliceBudget(WorkBudget(n)). */
explicit SliceBudget(WorkBudget work);
void reset() {
deadline = unlimitedDeadline;
@ -43,10 +58,8 @@ struct JS_PUBLIC_API(SliceBudget)
counter -= amt;
}
bool checkOverBudget();
bool isOverBudget() {
if (counter >= 0)
if (counter > 0)
return false;
return checkOverBudget();
}
@ -55,10 +68,11 @@ struct JS_PUBLIC_API(SliceBudget)
return deadline == unlimitedDeadline;
}
private:
private:
bool checkOverBudget();
static const int64_t unlimitedDeadline = INT64_MAX;
static const intptr_t unlimitedStartCounter = INTPTR_MAX;
};
} // namespace js

View File

@ -628,16 +628,15 @@ GCSlice(JSContext *cx, unsigned argc, Value *vp)
return false;
}
bool limit = true;
uint32_t budget = 0;
SliceBudget budget;
if (args.length() == 1) {
if (!ToUint32(cx, args[0], &budget))
uint32_t work = 0;
if (!ToUint32(cx, args[0], &work))
return false;
} else {
limit = false;
budget = SliceBudget(WorkBudget(work));
}
cx->runtime()->gc.gcDebugSlice(limit, budget);
cx->runtime()->gc.gcDebugSlice(budget);
args.rval().setUndefined();
return true;
}

View File

@ -899,7 +899,8 @@ Parser<FullParseHandler>::checkFunctionArguments()
// ES6 9.2.13.17 says that a lexical binding of 'arguments' shadows the
// arguments object.
bool argumentsHasLocalBinding = maybeArgDef && (maybeArgDef->kind() != Definition::ARG &&
maybeArgDef->kind() != Definition::LET);
maybeArgDef->kind() != Definition::LET &&
maybeArgDef->kind() != Definition::CONST);
bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
if (hasRest && argumentsHasLocalBinding) {
report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);

View File

@ -281,7 +281,7 @@ class GCRuntime
void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
void gcDebugSlice(bool limit, int64_t objCount);
void gcDebugSlice(SliceBudget &budget);
void runDebugGC();
inline void poke();
@ -510,14 +510,14 @@ class GCRuntime
bool initZeal();
void requestMajorGC(JS::gcreason::Reason reason);
void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
void collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason);
bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
bool gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason);
gcstats::ZoneGCStats scanZonesBeforeGC();
void budgetIncrementalGC(int64_t *budget);
void budgetIncrementalGC(SliceBudget &budget);
void resetIncrementalGC(const char *reason);
void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason);
void incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason reason);
void pushZealSelectedObjects();
void purgeRuntime();
bool beginMarkPhase(JS::gcreason::Reason reason);

View File

@ -28,7 +28,7 @@ gcPreserveCode();
try {
mjitChunkLimit(1);
} catch(exc1) {}
gcslice(0);
gcslice(1);
m(1);
gc();
m(2);

View File

@ -3,4 +3,4 @@
//
gc();
evaluate("gcslice(0);");
evaluate("gcslice(1);");

View File

@ -20,7 +20,7 @@ function h(code) {
h("\
p=m();\
gcPreserveCode();\
gcslice(7);\
gcslice(8);\
")
h("\"\"")
h("")

View File

@ -3,7 +3,7 @@
//
gc()
schedulegc(this)
gcslice(2)
gcslice(3)
function f() {
this["x"] = this["x"] = {}
}

View File

@ -5,8 +5,8 @@
function printBugNumber (num) {
BUGNUMBER = num;
}
gcslice(0)
gcslice(1)
schedulegc(this);
gcslice(1);
gcslice(2);
var BUGNUMBER = ("one");
printBugNumber();

View File

@ -1,6 +1,6 @@
var g1 = newGlobal();
schedulegc(g1);
gcslice(0);
gcslice(1);
function testEq(b) {
var a = deserialize(serialize(b));
}

View File

@ -4,7 +4,7 @@ function gen()
yield 1;
local = null;
gc();
gcslice(0);
gcslice(0); // Start IGC, but don't mark anything.
yield 2;
}

View File

@ -12,5 +12,5 @@ function recur(n)
}
validategc(false);
gcslice(0);
gcslice(1);
recur(10);

View File

@ -11,8 +11,8 @@ expect = "generator function foo returns a value";
actual = (function (j) {}).message;
reportCompare(expect, actual, summary + ": 1");
reportCompare(expect, actual, summary + ": 2");
gcslice(0);
gcslice(1);
gcslice(2);
gc();
var strings = [ (0), ];
for (var i = 0; i < strings.length; i++)

View File

@ -4,7 +4,7 @@ evalcx("\
gcslice = function() { };\
array = new Uint8Array;\
t0 = array.subarray();\
gcslice(11); \
gcslice(12); \
array.subarray();\
gc();\
gc();\

View File

@ -3,7 +3,7 @@
if (typeof evalInWorker == "undefined")
quit();
gcslice(10);
gcslice(11);
evalInWorker("print('helo world');");
for (i = 0; i < 100000; i++) {}

View File

@ -0,0 +1,3 @@
// |jit-test| error: TypeError
(function () { const [arguments] = 0; })();

View File

@ -1,6 +1,6 @@
if (this.hasOwnProperty('Intl')) {
gc();
gcslice(0);
gcslice(1);
var thisValues = [ "x" ];
thisValues.forEach(function (value) {
var format = Intl.DateTimeFormat.call(value);

View File

@ -1,5 +1,5 @@
gc();
gcslice(0);
gcslice(1);
function isClone(a, b) {
var rmemory = new WeakMap();
rmemory.set(a,b);

View File

@ -9,5 +9,5 @@ var recursiveFunctions = [{
eval(a.text.replace(/@/g, ""))
}
})();
gcslice(2868);
gcslice(2869);
Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()

View File

@ -10,5 +10,5 @@ var recursiveFunctions = [{
eval(a.text.replace(/@/g, ""))
}
})();
gcslice(2868);
gcslice(2869);
Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()

View File

@ -10,5 +10,5 @@ var recursiveFunctions = [{
eval(a.text.replace(/@/g, ""))
}
})();
gcslice(2868);
gcslice(2869);
Function("v={c:[{x:[[]],N:{x:[{}[d]]}}]}=minorgc(true)")()

View File

@ -4,6 +4,6 @@ var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0);
for (var i = 0; i < function(x) myObj(Date.prototype.toString.apply(x)); void i) {
eval(a.text.replace(/@/g, ""))
}
gcslice(2600);
gcslice(2601);
function testcase() {}
new Uint16Array(testcase);

View File

@ -23,7 +23,7 @@ function init()
*/
eval("init()");
gcslice(0);
gcslice(0); // Start IGC, but don't mark anything.
selectforgc(objs.root2);
gcslice(1);
objs.root2.ptr = objs.root1.ptr;

View File

@ -22,7 +22,7 @@ function init()
*/
eval("init()");
gcslice(0);
gcslice(0); // Start IGC, but don't mark anything.
selectforgc(objs.root);
gcslice(1);
delete objs.root.b;

View File

@ -4,7 +4,7 @@ var g1 = newGlobal();
var g2 = newGlobal();
schedulegc(g1);
gcslice(0);
gcslice(0); // Start IGC, but don't mark anything.
schedulegc(g2);
gcslice(1);
gcslice();

View File

@ -5,7 +5,7 @@ var g2 = newGlobal();
schedulegc(g1);
schedulegc(g2);
gcslice(0);
gcslice(0); // Start IGC, but don't mark anything.
schedulegc(g1);
gcslice(1);
gcslice();

View File

@ -3545,7 +3545,7 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape,
static bool
IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *shape,
Shape* oldShape, bool *isScripted, bool *isTemporarilyUnoptimizable)
bool *isScripted, bool *isTemporarilyUnoptimizable)
{
MOZ_ASSERT(isScripted);
@ -3553,13 +3553,6 @@ IsCacheableSetPropCall(JSContext *cx, JSObject *obj, JSObject *holder, Shape *sh
if (obj == holder)
return false;
if (obj->lastProperty() != oldShape) {
// During the property set, the shape of the object changed. Not much
// point caching this based on the post-set shape, since the object
// wouldn't have matched such an IC stub anyway.
return false;
}
if (!shape || !IsCacheableProtoChain(obj, holder))
return false;
@ -5773,17 +5766,9 @@ UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
if (setPropStub->holder() == holder) {
// We want to update the holder shape to match the new one no
// matter what, even if the receiver shape is different.
//
// We would like to assert that either
// setPropStub->holderShape() != holder->lastProperty() or
// setPropStub->shape() != receiverShape, but that assertion can
// fail if there is something in a setter that changes something
// that we guard on in our stub but don't check for before/after
// differences across the set during stub generation. For
// example, a setter mutating the shape of the proto the setter
// lives on would cause us to create an IC stub that never
// matches as protos with the old shape flow into it, but always
// matches post-set, which is where we are now.
MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
setPropStub->shape() != receiverShape,
"Why didn't we end up using this stub?");
setPropStub->holderShape() = holder->lastProperty();
// Make sure to update the setter, since a shape change might
// have changed which setter we want to use.
@ -7730,15 +7715,14 @@ BaselineScript::noteAccessedGetter(uint32_t pcOffset)
// SetProp_Fallback
//
// Attach an optimized stub for a SETPROP/SETGNAME/SETNAME op.
// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on a
// value property.
static bool
TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
HandleObject obj, HandleShape oldShape, HandleTypeObject oldType, uint32_t oldSlots,
HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached,
bool *isTemporarilyUnoptimizable)
TryAttachSetValuePropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
HandleObject obj, HandleShape oldShape, HandleTypeObject oldType, uint32_t oldSlots,
HandlePropertyName name, HandleId id, HandleValue rhs, bool *attached)
{
MOZ_ASSERT(!*attached);
MOZ_ASSERT(!*isTemporarilyUnoptimizable);
if (!obj->isNative() || obj->watched())
return true;
@ -7816,8 +7800,30 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
return true;
}
return true;
}
// Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
// an accessor property.
static bool
TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
HandleObject obj, HandleShape receiverShape, HandlePropertyName name,
HandleId id, HandleValue rhs, bool *attached,
bool *isTemporarilyUnoptimizable)
{
MOZ_ASSERT(!*attached);
MOZ_ASSERT(!*isTemporarilyUnoptimizable);
if (!obj->isNative() || obj->watched())
return true;
RootedShape shape(cx);
RootedObject holder(cx);
if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
return false;
bool isScripted = false;
bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape, oldShape,
bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
&isScripted, isTemporarilyUnoptimizable);
// Try handling scripted setters.
@ -7827,7 +7833,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
MOZ_ASSERT(callee->hasScript());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
holder, oldShape, callee)) {
holder, receiverShape, callee)) {
*attached = true;
return true;
}
@ -7852,7 +7858,7 @@ TryAttachSetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetPr
MOZ_ASSERT(callee->isNative());
if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
holder, oldShape, callee)) {
holder, receiverShape, callee)) {
*attached = true;
return true;
}
@ -7908,6 +7914,19 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
return false;
uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
// We want to avoid calling noteUnoptimizableAccess() if the reason we
// failed to attach a stub is one of those temporary reasons, since we might
// end up attaching a stub for the exact same access later.
bool isTemporarilyUnoptimizable = false;
if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
!TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldShape, name, id,
rhs, &attached, &isTemporarilyUnoptimizable))
{
return false;
}
if (op == JSOP_INITPROP) {
MOZ_ASSERT(obj->is<JSObject>());
if (!DefineNativeProperty(cx, obj.as<NativeObject>(), id, rhs,
@ -7943,14 +7962,9 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
return true;
}
bool attached = false;
// There are some reasons we can fail to attach a stub that are temporary.
// We want to avoid calling noteUnoptimizableAccess() if the reason we
// failed to attach a stub is one of those temporary reasons, since we might
// end up attaching a stub for the exact same access later.
bool isTemporarilyUnoptimizable = false;
if (!TryAttachSetPropStub(cx, script, pc, stub, obj, oldShape, oldType, oldSlots,
name, id, rhs, &attached, &isTemporarilyUnoptimizable))
if (!attached &&
!TryAttachSetValuePropStub(cx, script, pc, stub, obj, oldShape,
oldType, oldSlots, name, id, rhs, &attached))
{
return false;
}

View File

@ -87,12 +87,14 @@ BEGIN_TEST(testGCFinalizeCallback)
FinalizeCalls = 0;
JS_SetGCZeal(cx, 9, 1000000);
JS::PrepareForFullGC(rt);
rt->gc.gcDebugSlice(true, 1);
js::SliceBudget budget(js::WorkBudget(1));
rt->gc.gcDebugSlice(budget);
CHECK(rt->gc.state() == js::gc::MARK);
CHECK(rt->gc.isFullGc());
JS::RootedObject global4(cx, createTestGlobal());
rt->gc.gcDebugSlice(true, 1);
budget = js::SliceBudget(js::WorkBudget(1));
rt->gc.gcDebugSlice(budget);
CHECK(rt->gc.state() == js::gc::NO_INCREMENTAL);
CHECK(!rt->gc.isFullGc());
CHECK(checkMultipleGroups());

View File

@ -89,7 +89,8 @@ BEGIN_TEST(testWeakMap_keyDelegates)
* zone to finish marking before the delegate zone.
*/
CHECK(newCCW(map, delegate));
rt->gc.gcDebugSlice(true, 1000000);
js::SliceBudget budget(js::WorkBudget(1000000));
rt->gc.gcDebugSlice(budget);
#ifdef DEBUG
CHECK(map->zone()->lastZoneGroupIndex() < delegate->zone()->lastZoneGroupIndex());
#endif
@ -102,7 +103,8 @@ BEGIN_TEST(testWeakMap_keyDelegates)
/* Check the delegate keeps the entry alive even if the key is not reachable. */
key = nullptr;
CHECK(newCCW(map, delegate));
rt->gc.gcDebugSlice(true, 100000);
budget = js::SliceBudget(js::WorkBudget(100000));
rt->gc.gcDebugSlice(budget);
CHECK(checkSize(map, 1));
/*

View File

@ -1457,7 +1457,7 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value)
setMaxMallocBytes(value);
break;
case JSGC_SLICE_TIME_BUDGET:
sliceBudget = SliceBudget::TimeBudget(value);
sliceBudget = value ? value : SliceBudget::Unlimited;
break;
case JSGC_MARK_STACK_LIMIT:
setMarkStackLimit(value);
@ -1554,7 +1554,7 @@ GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC &lock)
case JSGC_TOTAL_CHUNKS:
return uint32_t(chunkSet.count() + emptyChunks(lock).count());
case JSGC_SLICE_TIME_BUDGET:
return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0);
return uint32_t(sliceBudget > 0 ? sliceBudget : 0);
case JSGC_MARK_STACK_LIMIT:
return marker.maxCapacity();
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
@ -2930,41 +2930,36 @@ GCRuntime::refillFreeListInGC(Zone *zone, AllocKind thingKind)
return allocator.arenas.allocateFromArena(zone, thingKind);
}
/* static */ int64_t
SliceBudget::TimeBudget(int64_t millis)
{
return millis * PRMJ_USEC_PER_MSEC;
}
/* static */ int64_t
SliceBudget::WorkBudget(int64_t work)
{
/* For work = 0 not to mean Unlimited, we subtract 1. */
return -work - 1;
}
SliceBudget::SliceBudget()
{
reset();
}
SliceBudget::SliceBudget(int64_t budget)
SliceBudget::SliceBudget(TimeBudget time)
{
if (budget == Unlimited) {
if (time.budget < 0) {
reset();
} else if (budget > 0) {
deadline = PRMJ_Now() + budget;
} else {
// Note: TimeBudget(0) is equivalent to WorkBudget(CounterReset).
deadline = PRMJ_Now() + time.budget * PRMJ_USEC_PER_MSEC;
counter = CounterReset;
}
}
SliceBudget::SliceBudget(WorkBudget work)
{
if (work.budget < 0) {
reset();
} else {
deadline = 0;
counter = -budget - 1;
counter = work.budget;
}
}
bool
SliceBudget::checkOverBudget()
{
bool over = PRMJ_Now() > deadline;
bool over = PRMJ_Now() >= deadline;
if (!over)
counter = CounterReset;
return over;
@ -5536,7 +5531,7 @@ GCRuntime::resetIncrementalGC(const char *reason)
break;
}
case SWEEP:
case SWEEP: {
marker.reset();
for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
@ -5544,13 +5539,15 @@ GCRuntime::resetIncrementalGC(const char *reason)
/* Finish sweeping the current zone group, then abort. */
abortSweepAfterCurrentGroup = true;
incrementalCollectSlice(SliceBudget::Unlimited, JS::gcreason::RESET);
SliceBudget budget;
incrementalCollectSlice(budget, JS::gcreason::RESET);
{
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
rt->gc.waitBackgroundSweepOrAllocEnd();
}
break;
}
default:
MOZ_CRASH("Invalid incremental GC state");
@ -5638,8 +5635,7 @@ GCRuntime::pushZealSelectedObjects()
}
void
GCRuntime::incrementalCollectSlice(int64_t budget,
JS::gcreason::Reason reason)
GCRuntime::incrementalCollectSlice(SliceBudget &budget, JS::gcreason::Reason reason)
{
MOZ_ASSERT(rt->currentThreadHasExclusiveAccess());
@ -5652,7 +5648,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
int zeal = 0;
#ifdef JS_GC_ZEAL
if (reason == JS::gcreason::DEBUG_GC && budget != SliceBudget::Unlimited) {
if (reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited()) {
/*
* Do the incremental collection type specified by zeal mode if the
* collection was triggered by runDebugGC() and incremental GC has not
@ -5663,18 +5659,16 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
#endif
MOZ_ASSERT_IF(incrementalState != NO_INCREMENTAL, isIncremental);
isIncremental = budget != SliceBudget::Unlimited;
isIncremental = !budget.isUnlimited();
if (zeal == ZealIncrementalRootsThenFinish || zeal == ZealIncrementalMarkAllThenFinish) {
/*
* Yields between slices occurs at predetermined points in these modes;
* the budget is not used.
*/
budget = SliceBudget::Unlimited;
budget.reset();
}
SliceBudget sliceBudget(budget);
if (incrementalState == NO_INCREMENTAL) {
incrementalState = MARK_ROOTS;
lastMarkSlice = false;
@ -5704,11 +5698,11 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
case MARK: {
/* If we needed delayed marking for gray roots, then collect until done. */
if (!marker.hasBufferedGrayRoots()) {
sliceBudget.reset();
budget.reset();
isIncremental = false;
}
bool finished = drainMarkStack(sliceBudget, gcstats::PHASE_MARK);
bool finished = drainMarkStack(budget, gcstats::PHASE_MARK);
if (!finished)
break;
@ -5734,7 +5728,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
* now exhasted.
*/
beginSweepPhase(lastGC);
if (sliceBudget.isOverBudget())
if (budget.isOverBudget())
break;
/*
@ -5748,7 +5742,7 @@ GCRuntime::incrementalCollectSlice(int64_t budget,
}
case SWEEP: {
bool finished = sweepPhase(sliceBudget);
bool finished = sweepPhase(budget);
if (!finished)
break;
@ -5786,32 +5780,32 @@ gc::IsIncrementalGCSafe(JSRuntime *rt)
}
void
GCRuntime::budgetIncrementalGC(int64_t *budget)
GCRuntime::budgetIncrementalGC(SliceBudget &budget)
{
IncrementalSafety safe = IsIncrementalGCSafe(rt);
if (!safe) {
resetIncrementalGC(safe.reason());
*budget = SliceBudget::Unlimited;
budget.reset();
stats.nonincremental(safe.reason());
return;
}
if (mode != JSGC_MODE_INCREMENTAL) {
resetIncrementalGC("GC mode change");
*budget = SliceBudget::Unlimited;
budget.reset();
stats.nonincremental("GC mode");
return;
}
if (isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited;
budget.reset();
stats.nonincremental("malloc bytes trigger");
}
bool reset = false;
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
*budget = SliceBudget::Unlimited;
budget.reset();
stats.nonincremental("allocation trigger");
}
@ -5822,7 +5816,7 @@ GCRuntime::budgetIncrementalGC(int64_t *budget)
}
if (zone->isTooMuchMalloc()) {
*budget = SliceBudget::Unlimited;
budget.reset();
stats.nonincremental("malloc bytes trigger");
}
}
@ -5868,7 +5862,7 @@ struct AutoDisableStoreBuffer
* to run another cycle.
*/
MOZ_NEVER_INLINE bool
GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
GCRuntime::gcCycle(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason)
{
minorGC(reason);
@ -5922,9 +5916,9 @@ GCRuntime::gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
resetIncrementalGC("requested");
stats.nonincremental("requested");
budget = SliceBudget::Unlimited;
budget.reset();
} else {
budgetIncrementalGC(&budget);
budgetIncrementalGC(budget);
}
/* The GC was reset, so we need a do-over. */
@ -6017,7 +6011,7 @@ GCRuntime::scanZonesBeforeGC()
}
void
GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
GCRuntime::collect(bool incremental, SliceBudget &budget, JSGCInvocationKind gckind,
JS::gcreason::Reason reason)
{
/* GC shouldn't be running in parallel execution mode */
@ -6042,7 +6036,7 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
return;
#endif
MOZ_ASSERT_IF(!incremental || budget != SliceBudget::Unlimited, JSGC_INCREMENTAL);
MOZ_ASSERT_IF(!incremental || !budget.isUnlimited(), JSGC_INCREMENTAL);
AutoStopVerifyingBarriers av(rt, reason == JS::gcreason::SHUTDOWN_CC ||
reason == JS::gcreason::DESTROY_RUNTIME);
@ -6109,21 +6103,22 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
void
GCRuntime::gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
{
collect(false, SliceBudget::Unlimited, gckind, reason);
SliceBudget budget;
collect(false, budget, gckind, reason);
}
void
GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis)
{
int64_t budget;
SliceBudget budget;
if (millis)
budget = SliceBudget::TimeBudget(millis);
budget = SliceBudget(TimeBudget(millis));
else if (reason == JS::gcreason::ALLOC_TRIGGER)
budget = sliceBudget;
budget = SliceBudget(TimeBudget(sliceBudget));
else if (schedulingState.inHighFrequencyGCMode() && tunables.isDynamicMarkSliceEnabled())
budget = sliceBudget * IGC_MARK_SLICE_MULTIPLIER;
budget = SliceBudget(TimeBudget(sliceBudget * IGC_MARK_SLICE_MULTIPLIER));
else
budget = sliceBudget;
budget = SliceBudget(TimeBudget(sliceBudget));
collect(true, budget, gckind, reason);
}
@ -6131,7 +6126,8 @@ GCRuntime::gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64
void
GCRuntime::gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason)
{
collect(true, SliceBudget::Unlimited, gckind, reason);
SliceBudget budget;
collect(true, budget, gckind, reason);
}
void
@ -6174,9 +6170,8 @@ ZonesSelected(JSRuntime *rt)
}
void
GCRuntime::gcDebugSlice(bool limit, int64_t objCount)
GCRuntime::gcDebugSlice(SliceBudget &budget)
{
int64_t budget = limit ? SliceBudget::WorkBudget(objCount) : SliceBudget::Unlimited;
if (!ZonesSelected(rt)) {
if (JS::IsIncrementalGCInProgress(rt))
JS::PrepareForIncrementalGC(rt);
@ -6438,12 +6433,12 @@ GCRuntime::runDebugGC()
PrepareForDebugGC(rt);
SliceBudget budget;
if (type == ZealIncrementalRootsThenFinish ||
type == ZealIncrementalMarkAllThenFinish ||
type == ZealIncrementalMultipleSlices)
{
js::gc::State initialState = incrementalState;
int64_t budget;
if (type == ZealIncrementalMultipleSlices) {
/*
* Start with a small slice limit and double it every slice. This
@ -6454,10 +6449,10 @@ GCRuntime::runDebugGC()
incrementalLimit = zealFrequency / 2;
else
incrementalLimit *= 2;
budget = SliceBudget::WorkBudget(incrementalLimit);
budget = SliceBudget(WorkBudget(incrementalLimit));
} else {
// This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
budget = SliceBudget::WorkBudget(1);
budget = SliceBudget(WorkBudget(1));
}
collect(true, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
@ -6472,9 +6467,9 @@ GCRuntime::runDebugGC()
incrementalLimit = zealFrequency / 2;
}
} else if (type == ZealCompactValue) {
collect(false, SliceBudget::Unlimited, GC_SHRINK, JS::gcreason::DEBUG_GC);
collect(false, budget, GC_SHRINK, JS::gcreason::DEBUG_GC);
} else {
collect(false, SliceBudget::Unlimited, GC_NORMAL, JS::gcreason::DEBUG_GC);
collect(false, budget, GC_NORMAL, JS::gcreason::DEBUG_GC);
}
#endif

View File

@ -203,7 +203,7 @@ function test()
gc();
buffer = null;
views = null;
gcslice(2); gcslice(2); gcslice(2); gcslice(2); gcslice(2); gcslice(2); gc();
gcslice(3); gcslice(3); gcslice(3); gcslice(3); gcslice(3); gcslice(3); gc();
}
var buf, buf2;

View File

@ -22,17 +22,32 @@ def error(message):
sys.exit(1)
def get_xdr_version(dir):
version_pat = re.compile('XDR_BYTECODE_VERSION = uint32_t\(0xb973c0de - (\d+)\);')
subtrahend_pat = re.compile('XDR_BYTECODE_VERSION_SUBTRAHEND\s*=\s*(\d+);', re.S)
version_expr_pat = re.compile('XDR_BYTECODE_VERSION\s*=\s*uint32_t\(0xb973c0de\s*-\s*(.+?)\);', re.S)
# FIXME: Bug 1066322 - Enable ES6 symbols in all builds.
bug1066322_pat = re.compile('#ifdef\s+JS_HAS_SYMBOLS\s+\+\s*1*\s+#endif', re.S)
version = ''
with open('{dir}/js/src/vm/Xdr.h'.format(dir=dir), 'r') as f:
data = f.read()
m = version_pat.search(data)
if m:
version = int(m.group(1))
m = subtrahend_pat.search(data)
if not m:
error('XDR_BYTECODE_VERSION_SUBTRAHEND is not recognized.')
return version
subtrahend = int(m.group(1))
m = version_expr_pat.search(data)
if not m:
error('XDR_BYTECODE_VERSION is not recognized.')
version_expr = m.group(1)
bug1066322 = False
m = bug1066322_pat.search(version_expr)
if m:
bug1066322 = True
return (subtrahend, bug1066322)
quoted_pat = re.compile(r"([^A-Za-z0-9]|^)'([^']+)'")
js_pat = re.compile(r"([^A-Za-z0-9]|^)(JS[A-Z0-9_\*]+)")
@ -327,7 +342,7 @@ def print_opcode(opcode):
def make_element_id(name):
return name.replace(' ', '-')
def print_doc(version, index):
def print_doc(version, bug1066322, index):
print("""<h2 id="Bytecode_Listing">Bytecode Listing</h2>
<p>This document is automatically generated from
@ -341,6 +356,15 @@ def print_doc(version, index):
version=version,
actual_version=0xb973c0de - version))
if bug1066322:
symbol_version = version + 1
print("""
<p>Until {{{{bug(1066322)}}}} is fixed, JSOP_SYMBOL exists only in Nightly, and it uses different version.</p>
<p>Bytecode version with JSOP_SYMBOL: <code>{version}</code>
(<code>0x{actual_version:08x}</code>).</p>
""".format(version=symbol_version,
actual_version=0xb973c0de - symbol_version))
for (category_name, types) in index:
print('<h3 id="{id}">{name}</h3>'.format(name=category_name,
id=make_element_id(category_name)))
@ -360,6 +384,6 @@ if __name__ == '__main__':
file=sys.stderr)
sys.exit(1)
dir = sys.argv[1]
version = get_xdr_version(dir)
(version, bug1066322) = get_xdr_version(dir)
index = get_opcodes(dir)
print_doc(version, index)
print_doc(version, bug1066322, index)

View File

@ -527,8 +527,7 @@ nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect,
// If we have a border radius, do full rounded rectangles
// and fill, regardless of what sides we're asked to draw.
if (!AllCornersZeroSize(aBorderRadii)) {
RefPtr<PathBuilder> builder =
mDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD);
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
RectCornerRadii innerRadii;
ComputeInnerRadii(aBorderRadii, aBorderSizes, &innerRadii);
@ -1473,12 +1472,12 @@ nsCSSBorderRenderer::DrawBorders()
// doesn't need to compute an offset curve to stroke the path. We know that
// the rounded parts are elipses we can offset exactly and can just compute
// a new cubic approximation.
RefPtr<PathBuilder> builder =
mDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD);
RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
AppendRoundedRectToPath(builder, mOuterRect, mBorderRadii, true);
AppendRoundedRectToPath(builder, ToRect(borderInnerRect.rect), borderInnerRect.corners, false);
RefPtr<Path> path = builder->Finish();
mDrawTarget->Fill(path, color);
return;
}
bool hasCompositeColors;

View File

@ -6755,7 +6755,9 @@ ShouldInflateFontsForContainer(const nsIFrame *aFrame)
!(aFrame->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) &&
// We also want to disable font inflation for containers that have
// preformatted text.
styleText->WhiteSpaceCanWrap(aFrame);
// MathML cells need special treatment. See bug 1002526 comment 56.
(styleText->WhiteSpaceCanWrap(aFrame) ||
aFrame->IsFrameOfType(nsIFrame::eMathML));
}
nscoord
@ -7224,7 +7226,9 @@ AutoMaybeDisableFontInflation::AutoMaybeDisableFontInflation(nsIFrame *aFrame)
// root's NCA's (nearest common ancestor of its inflatable
// descendants) width, we could probably disable inflation in
// fewer cases than we currently do.
if (aFrame->IsContainerForFontSizeInflation()) {
// MathML cells need special treatment. See bug 1002526 comment 56.
if (aFrame->IsContainerForFontSizeInflation() &&
!aFrame->IsFrameOfType(nsIFrame::eMathML)) {
mPresContext = aFrame->PresContext();
mOldValue = mPresContext->mInflationDisabledForShrinkWrap;
mPresContext->mInflationDisabledForShrinkWrap = true;

View File

@ -97,6 +97,7 @@ nsListControlFrame::nsListControlFrame(
mMightNeedSecondPass(false),
mHasPendingInterruptAtStartOfReflow(false),
mDropdownCanGrow(false),
mForceSelection(false),
mLastDropdownComputedHeight(NS_UNCONSTRAINEDSIZE)
{
mComboboxFrame = nullptr;
@ -739,9 +740,9 @@ nsListControlFrame::PerformSelection(int32_t aClickedIndex,
{
bool wasChanged = false;
if (aClickedIndex == kNothingSelected) {
}
else if (GetMultiple()) {
if (aClickedIndex == kNothingSelected && !mForceSelection) {
// Ignore kNothingSelected unless the selection is forced
} else if (GetMultiple()) {
if (aIsShift) {
// Make sure shift+click actually does something expected when
// the user has never clicked on the select
@ -1237,10 +1238,12 @@ nsListControlFrame::SetOptionsSelectedFromFrame(int32_t aStartIndex,
dom::HTMLSelectElement::FromContent(mContent);
uint32_t mask = dom::HTMLSelectElement::NOTIFY;
if (mForceSelection) {
mask |= dom::HTMLSelectElement::SET_DISABLED;
}
if (aValue) {
mask |= dom::HTMLSelectElement::IS_SELECTED;
}
if (aClearAll) {
mask |= dom::HTMLSelectElement::CLEAR_ALL;
}
@ -1292,13 +1295,16 @@ nsListControlFrame::ComboboxFinish(int32_t aIndex)
gLastKeyTime = 0;
if (mComboboxFrame) {
int32_t displayIndex = mComboboxFrame->GetIndexOfDisplayArea();
// Make sure we can always reset to the displayed index
mForceSelection = displayIndex == aIndex;
nsWeakFrame weakFrame(this);
PerformSelection(aIndex, false, false); // might destroy us
if (!weakFrame.IsAlive() || !mComboboxFrame) {
return;
}
int32_t displayIndex = mComboboxFrame->GetIndexOfDisplayArea();
if (displayIndex != aIndex) {
mComboboxFrame->RedisplaySelectedText(); // might destroy us
}
@ -1415,6 +1421,7 @@ nsListControlFrame::AboutToDropDown()
#endif
}
mItemSelectionStarted = false;
mForceSelection = false;
}
// We are about to be rolledup from the outside (ComboboxFrame)

Some files were not shown because too many files have changed in this diff Show More