import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1159409 - (Part 1-) - Remove Init() from the Image interface. r=tn (0b663ee45)
- Bug 1159409 - (Part 2) - Remove ProgressTrackerInit and register Images with a ProgressTracker in ImageFactory. r=tn (a24d4e849)
- Bug 1179909: Refactor stable state handling. r=smaug This is motivated by three separate but related problems: (0ead73dbd)
- remove mPreemptingRunnableInfos of PM which I couldn't trace in FF (96474c90a)
- Bug 1179909: Build fix. r=me CLOSED TREE (5d35a65d5)
- Bug 1144418 - target events for text nodes in shadow dom to the nearest element in the flattened tree. r=wchen (26c0eb8b2)
- Bug 853889 - Check single-box orientaton in _cairo_bentley_ottmann_tessellate_rectangular_traps and _cairo_bentley_ottmann_tessellate_boxes. r=jmuizelaar (a13abee2f)
- Bug 1143303 - extend D2D circle workaround to work for small circles. r=bas (1ccb1c0c1)
This commit is contained in:
2021-07-22 10:47:48 +08:00
parent 9d8e58858d
commit b9cfdbdbdc
64 changed files with 626 additions and 763 deletions
+11 -9
View File
@@ -5195,16 +5195,18 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
/* static */
void
nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable,
DispatchFailureHandling aHandling)
nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
{
nsCOMPtr<nsIRunnable> runnable = aRunnable;
nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
if (!appShell) {
MOZ_ASSERT(aHandling == DispatchFailureHandling::IgnoreFailure);
return;
}
appShell->RunInStableState(runnable.forget());
MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!");
CycleCollectedJSRuntime::Get()->RunInStableState(Move(aRunnable));
}
/* static */
void
nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
{
MOZ_ASSERT(CycleCollectedJSRuntime::Get(), "Must be on a script thread!");
CycleCollectedJSRuntime::Get()->RunInMetastableState(Move(aRunnable));
}
void
+13 -9
View File
@@ -1649,12 +1649,6 @@ public:
*/
static void WarnScriptWasIgnored(nsIDocument* aDocument);
/**
* Whether to assert that RunInStableState() succeeds, or ignore failure,
* which may happen late in shutdown.
*/
enum class DispatchFailureHandling { AssertSuccess, IgnoreFailure };
/**
* Add a "synchronous section", in the form of an nsIRunnable run once the
* event loop has reached a "stable state". |aRunnable| must not cause any
@@ -1666,9 +1660,19 @@ public:
* finishes. If called multiple times per task/event, all the runnables will
* be executed, in the order in which RunInStableState() was called.
*/
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable,
DispatchFailureHandling aHandling =
DispatchFailureHandling::AssertSuccess);
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable);
/* Add a "synchronous section", in the form of an nsIRunnable run once the
* event loop has reached a "metastable state". |aRunnable| must not cause any
* queued events to be processed (i.e. must not spin the event loop).
* We've reached a metastable state when the currently executing task or
* microtask has finished. This is not specced at this time.
* In practice this runs aRunnable once the currently executing task or
* microtask finishes. If called multiple times per microtask, all the
* runnables will be executed, in the order in which RunInMetastableState()
* was called
*/
static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
/**
* Retrieve information about the viewport as a data structure.
+1 -28
View File
@@ -88,8 +88,7 @@
#include "nsViewportInfo.h"
#include "nsIFormControl.h"
#include "nsIScriptError.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
//#include "nsWidgetsCID.h"
#include "FrameLayerBuilder.h"
#include "nsDisplayList.h"
#include "nsROCSSPrimitiveValue.h"
@@ -118,8 +117,6 @@ using namespace mozilla::gfx;
class gfxContext;
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
NS_INTERFACE_MAP_BEGIN(nsDOMWindowUtils)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWindowUtils)
NS_INTERFACE_MAP_ENTRY(nsIDOMWindowUtils)
@@ -3489,30 +3486,6 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget,
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::RunInStableState(nsIRunnable *aRunnable)
{
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
nsCOMPtr<nsIRunnable> runnable = aRunnable;
nsContentUtils::RunInStableState(runnable.forget());
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::RunBeforeNextEvent(nsIRunnable *runnable)
{
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
if (!appShell) {
return NS_ERROR_NOT_AVAILABLE;
}
return appShell->RunBeforeNextEvent(runnable);
}
NS_IMETHODIMP
nsDOMWindowUtils::RequestCompositorProperty(const nsAString& property,
float* aResult)
+1 -1
View File
@@ -487,7 +487,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
NS_WARN_IF_FALSE(!aTargetFrame ||
!aTargetFrame->GetContent() ||
aTargetFrame->GetContent() == aTargetContent ||
aTargetFrame->GetContent()->GetParent() == aTargetContent,
aTargetFrame->GetContent()->GetFlattenedTreeParent() == aTargetContent,
"aTargetFrame should be related with aTargetContent");
#endif
+1 -3
View File
@@ -25449,15 +25449,13 @@ DEBUGThreadSlower::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
NS_IMETHODIMP
DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
bool /* aMayWait */,
uint32_t /* aRecursionDepth */)
bool /* aMayWait */)
{
return NS_OK;
}
NS_IMETHODIMP
DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
uint32_t /* aRecursionDepth */,
bool /* aEventWasProcessed */)
{
MOZ_ASSERT(kDEBUGThreadSleepMS);
+3 -17
View File
@@ -12,7 +12,6 @@
#include "mozilla/dom/IDBFileHandleBinding.h"
#include "mozilla/dom/MetadataHelper.h"
#include "mozilla/EventDispatcher.h"
#include "nsIAppShell.h"
#include "nsServiceManagerUtils.h"
#include "nsWidgetsCID.h"
@@ -20,12 +19,6 @@ namespace mozilla {
namespace dom {
namespace indexedDB {
namespace {
NS_DEFINE_CID(kAppShellCID2, NS_APPSHELL_CID);
} // namespace
IDBFileHandle::IDBFileHandle(FileMode aMode,
RequestMode aRequestMode,
IDBMutableFile* aMutableFile)
@@ -51,15 +44,8 @@ IDBFileHandle::Create(FileMode aMode,
fileHandle->BindToOwner(aMutableFile);
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID2);
if (NS_WARN_IF(!appShell)) {
return nullptr;
}
nsresult rv = appShell->RunBeforeNextEvent(fileHandle);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
nsContentUtils::RunInMetastableState(runnable.forget());
fileHandle->SetCreating();
@@ -68,7 +54,7 @@ IDBFileHandle::Create(FileMode aMode,
return nullptr;
}
rv = service->Enqueue(fileHandle, nullptr);
nsresult rv = service->Enqueue(fileHandle, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
+4 -45
View File
@@ -16,11 +16,9 @@
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "nsIAppShell.h"
#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
#include "nsTHashtable.h"
#include "nsWidgetsCID.h"
#include "ProfilerHelpers.h"
#include "ReportInternalError.h"
#include "WorkerFeature.h"
@@ -36,36 +34,6 @@ namespace indexedDB {
using namespace mozilla::dom::workers;
using namespace mozilla::ipc;
namespace {
NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
bool
RunBeforeNextEvent(IDBTransaction* aTransaction)
{
MOZ_ASSERT(aTransaction);
if (NS_IsMainThread()) {
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
MOZ_ASSERT(appShell);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(appShell->RunBeforeNextEvent(aTransaction)));
return true;
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
if (NS_WARN_IF(!workerPrivate->RunBeforeNextEvent(aTransaction))) {
return false;
}
return true;
}
} // namespace
class IDBTransaction::WorkerFeature final
: public mozilla::dom::workers::WorkerFeature
{
@@ -222,15 +190,8 @@ IDBTransaction::CreateVersionChange(
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) {
MOZ_ASSERT(!NS_IsMainThread());
#ifdef DEBUG
// Silence assertions.
transaction->mSentCommitOrAbort = true;
#endif
aActor->SendDeleteMeInternal(/* aFailedConstructor */ true);
return nullptr;
}
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
nsContentUtils::RunInMetastableState(runnable.forget());
transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
transaction->mNextObjectStoreId = aNextObjectStoreId;
@@ -262,10 +223,8 @@ IDBTransaction::Create(IDBDatabase* aDatabase,
transaction->SetScriptOwner(aDatabase->GetScriptOwner());
if (NS_WARN_IF(!RunBeforeNextEvent(transaction))) {
MOZ_ASSERT(!NS_IsMainThread());
return nullptr;
}
nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
nsContentUtils::RunInMetastableState(runnable.forget());
transaction->mCreating = true;
+1 -27
View File
@@ -49,7 +49,7 @@ interface nsIJSRAIIHelper;
interface nsIContentPermissionRequest;
interface nsIObserver;
[scriptable, uuid(7a37e173-ea6e-495e-8702-013f8063352a)]
[scriptable, uuid(6064615a-a782-4d08-86db-26ef3851208a)]
interface nsIDOMWindowUtils : nsISupports {
/**
@@ -1715,32 +1715,6 @@ interface nsIDOMWindowUtils : nsISupports {
*/
attribute boolean paintFlashing;
/**
* Add a "synchronous section", in the form of an nsIRunnable run once the
* event loop has reached a "stable state". |runnable| must not cause any
* queued events to be processed (i.e. must not spin the event loop).
* We've reached a stable state when the currently executing task/event has
* finished, see:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
* In practice this runs aRunnable once the currently executing event
* finishes. If called multiple times per task/event, all the runnables will
* be executed, in the order in which runInStableState() was called.
*
* XXX - This can wreak havoc if you're not using this for very simple
* purposes, eg testing or setting a flag.
*/
void runInStableState(in nsIRunnable runnable);
/**
* Run the given runnable before the next iteration of the event loop (this
* includes native events too). If a nested loop is spawned within the current
* event then the runnable will not be run until that loop has terminated.
*
* XXX - This can wreak havoc if you're not using this for very simple
* purposes, eg testing or setting a flag.
*/
void runBeforeNextEvent(in nsIRunnable runnable);
/*
* Returns the value of a given property animated on the compositor thread.
* If the property is NOT currently being animated on the compositor thread,
+1 -3
View File
@@ -600,9 +600,7 @@ AudioDestinationNode::ScheduleStableStateNotification()
NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
// Dispatch will fail if this is called on AudioNode destruction during
// shutdown, in which case failure can be ignored.
nsContentUtils::RunInStableState(event.forget(),
nsContentUtils::
DispatchFailureHandling::IgnoreFailure);
nsContentUtils::RunInStableState(event.forget());
}
double
+1
View File
@@ -508,6 +508,7 @@ Promise::PerformMicroTaskCheckpoint()
if (cx.isSome()) {
JS_CheckForInterrupt(cx.ref());
}
runtime->AfterProcessMicrotask();
} while (!microtaskQueue.empty());
return true;
+21
View File
@@ -169,6 +169,26 @@ function promiseAsync_ResolveThenTimeout() {
ok(!handlerExecuted, "Handlers are not called before 'then' returns.");
}
function promiseAsync_SyncXHR()
{
var handlerExecuted = false;
Promise.resolve().then(function() {
handlerExecuted = true;
// Allow other assertions to run so the test could fail before the next one.
setTimeout(runTest, 0);
});
ok(!handlerExecuted, "Handlers are not called until the next microtask.");
var xhr = new XMLHttpRequest();
xhr.open("GET", "testXHR.txt", false);
xhr.send(null);
todo(!handlerExecuted, "Sync XHR should not trigger microtask execution.");
}
function promiseDoubleThen() {
var steps = 0;
var promise = new Promise(function(r1, r2) {
@@ -756,6 +776,7 @@ var tests = [ promiseResolve, promiseReject,
promiseAsync_TimeoutResolveThen,
promiseAsync_ResolveTimeoutThen,
promiseAsync_ResolveThenTimeout,
promiseAsync_SyncXHR,
promiseDoubleThen, promiseThenException,
promiseThenCatchThen, promiseRejectThenCatchThen,
promiseRejectThenCatchThen2,
+1 -3
View File
@@ -373,15 +373,13 @@ DOMStorageDBThread::ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread)
NS_IMETHODIMP
DOMStorageDBThread::ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread,
bool mayWait,
uint32_t recursionDepth)
bool mayWait)
{
return NS_OK;
}
NS_IMETHODIMP
DOMStorageDBThread::ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread,
uint32_t recursionDepth,
bool eventWasProcessed)
{
return NS_OK;
+9
View File
@@ -975,6 +975,15 @@ public:
}
}
virtual void AfterProcessTask(uint32_t aRecursionDepth) override
{
// Only perform the Promise microtask checkpoint on the outermost event
// loop. Don't run it, for example, during sync XHR or importScripts.
if (aRecursionDepth == 2) {
CycleCollectedJSRuntime::AfterProcessTask(aRecursionDepth);
}
}
private:
WorkerPrivate* mWorkerPrivate;
};
+8 -87
View File
@@ -2750,22 +2750,6 @@ WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
{
}
struct WorkerPrivate::PreemptingRunnableInfo final
{
nsCOMPtr<nsIRunnable> mRunnable;
uint32_t mRecursionDepth;
PreemptingRunnableInfo()
{
MOZ_COUNT_CTOR(WorkerPrivate::PreemptingRunnableInfo);
}
~PreemptingRunnableInfo()
{
MOZ_COUNT_DTOR(WorkerPrivate::PreemptingRunnableInfo);
}
};
template <class Derived>
nsIDocument*
WorkerPrivateParent<Derived>::GetDocument() const
@@ -5303,8 +5287,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
}
}
mPreemptingRunnableInfos.Clear();
// Clear away our MessagePorts.
mWorkerPorts.Clear();
@@ -5349,10 +5331,6 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
// Process a single runnable from the main queue.
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(mThread, false));
// Only perform the Promise microtask checkpoint on the outermost event
// loop. Don't run it, for example, during sync XHR or importScripts.
(void)Promise::PerformMicroTaskCheckpoint();
normalRunnablesPending = NS_HasPendingEvents(mThread);
if (normalRunnablesPending && GlobalScope()) {
// Now *might* be a good time to GC. Let the JS engine make the decision.
@@ -5372,85 +5350,28 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
}
void
WorkerPrivate::OnProcessNextEvent(uint32_t aRecursionDepth)
WorkerPrivate::OnProcessNextEvent()
{
AssertIsOnWorkerThread();
MOZ_ASSERT(aRecursionDepth);
uint32_t recursionDepth = CycleCollectedJSRuntime::Get()->RecursionDepth();
MOZ_ASSERT(recursionDepth);
// Normally we process control runnables in DoRunLoop or RunCurrentSyncLoop.
// However, it's possible that non-worker C++ could spin its own nested event
// loop, and in that case we must ensure that we continue to process control
// runnables here.
if (aRecursionDepth > 1 &&
mSyncLoopStack.Length() < aRecursionDepth - 1) {
if (recursionDepth > 1 &&
mSyncLoopStack.Length() < recursionDepth - 1) {
ProcessAllControlRunnables();
}
// Run any preempting runnables that match this depth.
if (!mPreemptingRunnableInfos.IsEmpty()) {
nsTArray<PreemptingRunnableInfo> pendingRunnableInfos;
for (uint32_t index = 0;
index < mPreemptingRunnableInfos.Length();
index++) {
PreemptingRunnableInfo& preemptingRunnableInfo =
mPreemptingRunnableInfos[index];
if (preemptingRunnableInfo.mRecursionDepth == aRecursionDepth) {
preemptingRunnableInfo.mRunnable->Run();
preemptingRunnableInfo.mRunnable = nullptr;
} else {
PreemptingRunnableInfo* pending = pendingRunnableInfos.AppendElement();
pending->mRunnable.swap(preemptingRunnableInfo.mRunnable);
pending->mRecursionDepth = preemptingRunnableInfo.mRecursionDepth;
}
}
mPreemptingRunnableInfos.SwapElements(pendingRunnableInfos);
}
}
void
WorkerPrivate::AfterProcessNextEvent(uint32_t aRecursionDepth)
WorkerPrivate::AfterProcessNextEvent()
{
AssertIsOnWorkerThread();
MOZ_ASSERT(aRecursionDepth);
}
bool
WorkerPrivate::RunBeforeNextEvent(nsIRunnable* aRunnable)
{
AssertIsOnWorkerThread();
MOZ_ASSERT(aRunnable);
MOZ_ASSERT_IF(!mPreemptingRunnableInfos.IsEmpty(),
NS_HasPendingEvents(mThread));
const uint32_t recursionDepth =
mThread->RecursionDepth(WorkerThreadFriendKey());
PreemptingRunnableInfo* preemptingRunnableInfo =
mPreemptingRunnableInfos.AppendElement();
preemptingRunnableInfo->mRunnable = aRunnable;
// Due to the weird way that the thread recursion counter is implemented we
// subtract one from the recursion level if we have one.
preemptingRunnableInfo->mRecursionDepth =
recursionDepth ? recursionDepth - 1 : 0;
// Ensure that we have a pending event so that the runnable will be guaranteed
// to run.
if (mPreemptingRunnableInfos.Length() == 1 && !NS_HasPendingEvents(mThread)) {
nsRefPtr<DummyRunnable> dummyRunnable = new DummyRunnable(this);
if (NS_FAILED(Dispatch(dummyRunnable.forget()))) {
NS_WARNING("RunBeforeNextEvent called after the thread is shutting "
"down!");
mPreemptingRunnableInfos.Clear();
return false;
}
}
return true;
MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth());
}
void
+2 -10
View File
@@ -948,9 +948,6 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
// modifications are done with mMutex held *only* in DEBUG builds.
nsTArray<nsAutoPtr<SyncLoopInfo>> mSyncLoopStack;
struct PreemptingRunnableInfo;
nsTArray<PreemptingRunnableInfo> mPreemptingRunnableInfos;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsITimer> mGCTimer;
@@ -1340,10 +1337,10 @@ public:
ClearMainEventQueue(WorkerRanOrNot aRanOrNot);
void
OnProcessNextEvent(uint32_t aRecursionDepth);
OnProcessNextEvent();
void
AfterProcessNextEvent(uint32_t aRecursionDepth);
AfterProcessNextEvent();
void
AssertValidSyncLoop(nsIEventTarget* aSyncLoopTarget)
@@ -1370,11 +1367,6 @@ public:
return mWorkerScriptExecutedSuccessfully;
}
// Just like nsIAppShell::RunBeforeNextEvent. May only be called on the worker
// thread.
bool
RunBeforeNextEvent(nsIRunnable* aRunnable);
void
MaybeDispatchLoadFailedRunnable();
+4 -6
View File
@@ -310,8 +310,7 @@ WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
NS_IMETHODIMP
WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
bool aMayWait,
uint32_t aRecursionDepth)
bool aMayWait)
{
mWorkerPrivate->AssertIsOnWorkerThread();
@@ -321,23 +320,22 @@ WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
// PrimaryWorkerRunnable::Run() and don't want to process the event in
// mWorkerPrivate yet.
if (aMayWait) {
MOZ_ASSERT(aRecursionDepth == 2);
MOZ_ASSERT(CycleCollectedJSRuntime::Get()->RecursionDepth() == 2);
MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
return NS_OK;
}
mWorkerPrivate->OnProcessNextEvent(aRecursionDepth);
mWorkerPrivate->OnProcessNextEvent();
return NS_OK;
}
NS_IMETHODIMP
WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
uint32_t aRecursionDepth,
bool /* aEventWasProcessed */)
{
mWorkerPrivate->AssertIsOnWorkerThread();
mWorkerPrivate->AfterProcessNextEvent(aRecursionDepth);
mWorkerPrivate->AfterProcessNextEvent();
return NS_OK;
}
+7 -2
View File
@@ -3,6 +3,11 @@ function ok(a, msg) {
postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
}
function todo(a, msg) {
dump("TODO: " + !a + " => " + a + " " + msg + "\n");
postMessage({type: 'status', status: !a, msg: a + ": " + msg });
}
function is(a, b, msg) {
dump("IS: " + (a===b) + " => " + a + " | " + b + " " + msg + "\n");
postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
@@ -148,7 +153,7 @@ function promiseAsync_ResolveThenTimeout() {
ok(!handlerExecuted, "Handlers are not called before 'then' returns.");
}
function promiseAsync_SyncHXRAndImportScripts()
function promiseAsync_SyncXHRAndImportScripts()
{
var handlerExecuted = false;
@@ -790,7 +795,7 @@ var tests = [
promiseAsync_TimeoutResolveThen,
promiseAsync_ResolveTimeoutThen,
promiseAsync_ResolveThenTimeout,
promiseAsync_SyncHXRAndImportScripts,
promiseAsync_SyncXHRAndImportScripts,
promiseDoubleThen,
promiseThenException,
promiseThenCatchThen,
+57 -16
View File
@@ -224,18 +224,36 @@ PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
// beginning and an end point. This means the circle will be the wrong way
// around if the start angle is smaller than the end angle. It might seem
// tempting to invert aAntiClockwise but that would change the sweeping
// direction of the arc to instead we exchange start/begin.
// direction of the arc so instead we exchange start/begin.
Float oldStart = aStartAngle;
aStartAngle = aEndAngle;
aEndAngle = oldStart;
}
const Float kSmallRadius = 0.007f;
Float midAngle = 0;
bool smallFullCircle = false;
// XXX - Workaround for now, D2D does not appear to do the desired thing when
// the angle sweeps a complete circle.
if (aEndAngle - aStartAngle >= 2 * M_PI) {
aEndAngle = Float(aStartAngle + M_PI * 1.9999);
if (aRadius > kSmallRadius) {
aEndAngle = Float(aStartAngle + M_PI * 1.9999);
}
else {
smallFullCircle = true;
midAngle = Float(aStartAngle + M_PI);
aEndAngle = Float(aStartAngle + 2 * M_PI);
}
} else if (aStartAngle - aEndAngle >= 2 * M_PI) {
aStartAngle = Float(aEndAngle + M_PI * 1.9999);
if (aRadius > kSmallRadius) {
aStartAngle = Float(aEndAngle + M_PI * 1.9999);
}
else {
smallFullCircle = true;
midAngle = Float(aEndAngle + M_PI);
aStartAngle = Float(aEndAngle + 2 * M_PI);
}
}
Point startPoint;
@@ -253,23 +271,46 @@ PathBuilderD2D::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
endPoint.y = aOrigin.y + aRadius * sin(aEndAngle);
D2D1_ARC_SIZE arcSize = D2D1_ARC_SIZE_SMALL;
D2D1_SWEEP_DIRECTION direction =
aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
D2D1_SWEEP_DIRECTION_CLOCKWISE;
if (aAntiClockwise) {
if (aStartAngle - aEndAngle > M_PI) {
arcSize = D2D1_ARC_SIZE_LARGE;
}
} else {
if (aEndAngle - aStartAngle > M_PI) {
arcSize = D2D1_ARC_SIZE_LARGE;
if (!smallFullCircle) {
if (aAntiClockwise) {
if (aStartAngle - aEndAngle > M_PI) {
arcSize = D2D1_ARC_SIZE_LARGE;
}
} else {
if (aEndAngle - aStartAngle > M_PI) {
arcSize = D2D1_ARC_SIZE_LARGE;
}
}
mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
D2D1::SizeF(aRadius, aRadius),
0.0f,
direction,
arcSize));
}
else {
// draw small circles as two half-circles
Point midPoint;
midPoint.x = aOrigin.x + aRadius * cos(midAngle);
midPoint.y = aOrigin.y + aRadius * sin(midAngle);
mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
D2D1::SizeF(aRadius, aRadius),
0.0f,
aAntiClockwise ? D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE :
D2D1_SWEEP_DIRECTION_CLOCKWISE,
arcSize));
mSink->AddArc(D2D1::ArcSegment(D2DPoint(midPoint),
D2D1::SizeF(aRadius, aRadius),
0.0f,
direction,
arcSize));
mSink->AddArc(D2D1::ArcSegment(D2DPoint(endPoint),
D2D1::SizeF(aRadius, aRadius),
0.0f,
direction,
arcSize));
}
mCurrentPoint = endPoint;
}
@@ -674,11 +674,20 @@ _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps,
cairo_status_t status;
int i;
if (unlikely (traps->num_traps <= 1))
return CAIRO_STATUS_SUCCESS;
assert (traps->is_rectangular);
if (unlikely (traps->num_traps <= 1)) {
if (traps->num_traps == 1) {
cairo_trapezoid_t *trap = traps->traps;
if (trap->left.p1.x > trap->right.p1.x) {
cairo_line_t tmp = trap->left;
trap->left = trap->right;
trap->right = tmp;
}
}
return CAIRO_STATUS_SUCCESS;
}
dump_traps (traps, "bo-rects-traps-in.txt");
rectangles = stack_rectangles;
@@ -746,8 +755,35 @@ _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in,
cairo_status_t status;
int i, j;
if (unlikely (in->num_boxes <= 1))
if (unlikely (in->num_boxes == 0)) {
_cairo_boxes_clear (out);
return CAIRO_STATUS_SUCCESS;
}
if (in->num_boxes == 1) {
if (in == out) {
cairo_box_t *box = &in->chunks.base[0];
if (box->p1.x > box->p2.x) {
cairo_fixed_t tmp = box->p1.x;
box->p1.x = box->p2.x;
box->p2.x = tmp;
}
} else {
cairo_box_t box = in->chunks.base[0];
if (box.p1.x > box.p2.x) {
cairo_fixed_t tmp = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = tmp;
}
_cairo_boxes_clear (out);
status = _cairo_boxes_add (out, &box);
assert (status == CAIRO_STATUS_SUCCESS);
}
return CAIRO_STATUS_SUCCESS;
}
rectangles = stack_rectangles;
rectangles_ptrs = stack_rectangles_ptrs;
+27
View File
@@ -0,0 +1,27 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
width="100%" height="100%">
<title>Testcase for small circles</title>
<!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1143303 -->
<rect width="100%" height="100%" fill="lime"/>
<circle cx="200" cy="150" r="95" fill="red"/>
<g transform="translate(200, 150)" fill="lime">
<g transform="scale(1e8, 1e8)">
<circle cx="0" cy="0" r="1e-6"/>
</g>
</g>
<circle cx="342" cy="176.06" r="1" fill="red"/>
<g transform="translate(342,1098.55)" fill="lime">
<g transform="scale(418.2,-405.9)">
<circle cx="0" cy="2.2727" r=".006"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 770 B

+12
View File
@@ -0,0 +1,12 @@
<!doctype html>
<html>
<head><title>Testcase for bug 853889</title></head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="400px" height="400px">
<path d="M 0 0 L 0 50 L 400 50 L 400 0 Z"
fill="rgb(12,200,12)"></path>
</svg>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
<!doctype html>
<html>
<head><title>Testcase for bug 853889</title></head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="400px" height="400px">
<path d="M 0 400 L 0 450 L 600 450 L 600 400 Z"
fill="rgb(200,12,12)"></path>
<path d="M 0 0 L 0 50 L 600 50 L 600 0 Z"
fill="rgb(200,12,12)"></path>
<path d="M 0 0 L 0 50 L 600 50 L 600 0 Z
M 0 400 L 0 450 L 600 450 L 600 400 Z"
fill="rgb(12,200,12)"></path>
</svg>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Testcase reference file for generic pass condition</title>
<rect width="100%" height="100%" fill="lime"/>
</svg>

After

Width:  |  Height:  |  Size: 297 B

+2
View File
@@ -3,3 +3,5 @@
fuzzy-if(winWidget,175,443) == 611498-1.html 611498-ref.html
skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,1000) == 709477-1.html 709477-1-ref.html # bug 773482
skip-if(!asyncPanZoom) == 1086723.html 1086723-ref.html
== 853889-1.html 853889-1-ref.html
== 1143303-1.svg pass.svg
-6
View File
@@ -24,12 +24,6 @@ namespace image {
// Inherited methods from Image.
nsresult
DynamicImage::Init(const char* aMimeType, uint32_t aFlags)
{
return NS_OK;
}
already_AddRefed<ProgressTracker>
DynamicImage::GetProgressTracker()
{
-2
View File
@@ -31,8 +31,6 @@ public:
}
// Inherited methods from Image.
virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override;
virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
virtual size_t SizeOfSourceWithComputedFallback(
MallocSizeOf aMallocSizeOf) const override;
+6
View File
@@ -23,6 +23,12 @@ ImageResource::ImageResource(ImageURL* aURI) :
mError(false)
{ }
ImageResource::~ImageResource()
{
// Ask our ProgressTracker to drop its weak reference to us.
mProgressTracker->ResetImage();
}
// Translates a mimetype into a concrete decoder
Image::eDecoderType
Image::GetDecoderType(const char* aMimeType)
+1 -9
View File
@@ -74,15 +74,6 @@ public:
static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE = 0x10;
static const uint32_t INIT_FLAG_SYNC_LOAD = 0x20;
/**
* Creates a new image container.
*
* @param aMimeType The mimetype of the image.
* @param aFlags Initialization flags of the INIT_FLAG_* variety.
*/
virtual nsresult Init(const char* aMimeType,
uint32_t aFlags) = 0;
virtual already_AddRefed<ProgressTracker> GetProgressTracker() = 0;
virtual void SetProgressTracker(ProgressTracker* aProgressTracker) {}
@@ -197,6 +188,7 @@ public:
protected:
explicit ImageResource(ImageURL* aURI);
~ImageResource();
// Shared functionality for implementors of imgIContainer. Every
// implementation of attribute animationMode should forward here.
+31 -2
View File
@@ -14,6 +14,7 @@
#include "nsMimeTypes.h"
#include "nsIRequest.h"
#include "MultipartImage.h"
#include "RasterImage.h"
#include "VectorImage.h"
#include "Image.h"
@@ -154,12 +155,32 @@ ImageFactory::CreateAnonymousImage(const nsCString& aMimeType)
nsRefPtr<RasterImage> newImage = new RasterImage();
nsRefPtr<ProgressTracker> newTracker = new ProgressTracker();
newTracker->SetImage(newImage);
newImage->SetProgressTracker(newTracker);
rv = newImage->Init(aMimeType.get(), Image::INIT_FLAG_SYNC_LOAD);
NS_ENSURE_SUCCESS(rv, BadImage(newImage));
return newImage.forget();
}
/* static */ already_AddRefed<MultipartImage>
ImageFactory::CreateMultipartImage(Image* aFirstPart,
ProgressTracker* aProgressTracker)
{
MOZ_ASSERT(aFirstPart);
MOZ_ASSERT(aProgressTracker);
nsRefPtr<MultipartImage> newImage = new MultipartImage(aFirstPart);
aProgressTracker->SetImage(newImage);
newImage->SetProgressTracker(aProgressTracker);
newImage->Init();
return newImage.forget();
}
int32_t
SaturateToInt32(int64_t val)
{
@@ -211,9 +232,13 @@ ImageFactory::CreateRasterImage(nsIRequest* aRequest,
uint32_t aImageFlags,
uint32_t aInnerWindowId)
{
MOZ_ASSERT(aProgressTracker);
nsresult rv;
nsRefPtr<RasterImage> newImage = new RasterImage(aProgressTracker, aURI);
nsRefPtr<RasterImage> newImage = new RasterImage(aURI);
aProgressTracker->SetImage(newImage);
newImage->SetProgressTracker(aProgressTracker);
rv = newImage->Init(aMimeType.get(), aImageFlags);
NS_ENSURE_SUCCESS(rv, BadImage(newImage));
@@ -273,9 +298,13 @@ ImageFactory::CreateVectorImage(nsIRequest* aRequest,
uint32_t aImageFlags,
uint32_t aInnerWindowId)
{
MOZ_ASSERT(aProgressTracker);
nsresult rv;
nsRefPtr<VectorImage> newImage = new VectorImage(aProgressTracker, aURI);
nsRefPtr<VectorImage> newImage = new VectorImage(aURI);
aProgressTracker->SetImage(newImage);
newImage->SetProgressTracker(aProgressTracker);
rv = newImage->Init(aMimeType.get(), aImageFlags);
NS_ENSURE_SUCCESS(rv, BadImage(newImage));
+13
View File
@@ -18,6 +18,7 @@ namespace image {
class Image;
class ImageURL;
class MultipartImage;
class ProgressTracker;
class ImageFactory
@@ -54,6 +55,18 @@ public:
static already_AddRefed<Image>
CreateAnonymousImage(const nsCString& aMimeType);
/**
* Creates a new multipart/x-mixed-replace image wrapper, and initializes it
* with the first part. Subsequent parts should be passed to the existing
* MultipartImage via MultipartImage::BeginTransitionToPart().
*
* @param aFirstPart An image containing the first part of the multipart
* stream.
* @param aProgressTracker A progress tracker for the multipart image.
*/
static already_AddRefed<MultipartImage>
CreateMultipartImage(Image* aFirstPart, ProgressTracker* aProgressTracker);
private:
// Factory functions that create specific types of image containers.
static already_AddRefed<Image>
-6
View File
@@ -21,12 +21,6 @@ namespace image {
// Inherited methods from Image.
nsresult
ImageWrapper::Init(const char* aMimeType, uint32_t aFlags)
{
return mInnerImage->Init(aMimeType, aFlags);
}
already_AddRefed<ProgressTracker>
ImageWrapper::GetProgressTracker()
{
-2
View File
@@ -22,8 +22,6 @@ public:
NS_DECL_IMGICONTAINER
// Inherited methods from Image.
virtual nsresult Init(const char* aMimeType, uint32_t aFlags) override;
virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
virtual size_t
+14 -5
View File
@@ -103,13 +103,18 @@ private:
// Implementation
///////////////////////////////////////////////////////////////////////////////
MultipartImage::MultipartImage(Image* aImage, ProgressTracker* aTracker)
: ImageWrapper(aImage)
MultipartImage::MultipartImage(Image* aFirstPart)
: ImageWrapper(aFirstPart)
, mDeferNotifications(false)
{
MOZ_ASSERT(aTracker);
mProgressTrackerInit = new ProgressTrackerInit(this, aTracker);
mNextPartObserver = new NextPartObserver(this);
}
void
MultipartImage::Init()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mTracker, "Should've called SetProgressTracker() by now");
// Start observing the first part.
nsRefPtr<ProgressTracker> firstPartTracker =
@@ -119,7 +124,11 @@ MultipartImage::MultipartImage(Image* aImage, ProgressTracker* aTracker)
InnerImage()->IncrementAnimationConsumers();
}
MultipartImage::~MultipartImage() { }
MultipartImage::~MultipartImage()
{
// Ask our ProgressTracker to drop its weak reference to us.
mTracker->ResetImage();
}
NS_IMPL_QUERY_INTERFACE_INHERITED0(MultipartImage, ImageWrapper)
NS_IMPL_ADDREF(MultipartImage)
+4 -3
View File
@@ -27,8 +27,6 @@ public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(MultipartImage)
NS_DECL_ISUPPORTS
MultipartImage(Image* aImage, ProgressTracker* aTracker);
void BeginTransitionToPart(Image* aNextPart);
// Overridden ImageWrapper methods:
@@ -73,12 +71,15 @@ protected:
virtual ~MultipartImage();
private:
friend class ImageFactory;
friend class NextPartObserver;
explicit MultipartImage(Image* aFirstPart);
void Init();
void FinishTransition();
nsRefPtr<ProgressTracker> mTracker;
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
nsRefPtr<NextPartObserver> mNextPartObserver;
nsRefPtr<Image> mNextPart;
bool mDeferNotifications : 1;
-20
View File
@@ -22,26 +22,6 @@ using mozilla::WeakPtr;
namespace mozilla {
namespace image {
ProgressTrackerInit::ProgressTrackerInit(Image* aImage,
ProgressTracker* aTracker)
{
MOZ_ASSERT(aImage);
if (aTracker) {
mTracker = aTracker;
} else {
mTracker = new ProgressTracker();
}
mTracker->SetImage(aImage);
aImage->SetProgressTracker(mTracker);
MOZ_ASSERT(mTracker);
}
ProgressTrackerInit::~ProgressTrackerInit()
{
mTracker->ResetImage();
}
static void
CheckProgressConsistency(Progress aProgress)
{
+6 -16
View File
@@ -161,22 +161,21 @@ public:
// probably be improved, but it's too scary to mess with at the moment.
bool FirstObserverIs(IProgressObserver* aObserver);
// Resets our weak reference to our image. Image subclasses should call this
// in their destructor.
void ResetImage();
private:
typedef nsTObserverArray<mozilla::WeakPtr<IProgressObserver>> ObserverArray;
friend class AsyncNotifyRunnable;
friend class AsyncNotifyCurrentStateRunnable;
friend class ProgressTrackerInit;
friend class ImageFactory;
ProgressTracker(const ProgressTracker& aOther) = delete;
// This method should only be called once, and only on an ProgressTracker
// that was initialized without an image. ProgressTrackerInit automates this.
// Sets our weak reference to our image. Only ImageFactory should call this.
void SetImage(Image* aImage);
// Resets our weak reference to our image, for when mImage is about to go out
// of scope. ProgressTrackerInit automates this.
void ResetImage();
// Send some notifications that would be necessary to make |aObserver| believe
// the request is finished downloading and decoding. We only send
// FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
@@ -206,15 +205,6 @@ private:
Progress mProgress;
};
class ProgressTrackerInit
{
public:
ProgressTrackerInit(Image* aImage, ProgressTracker* aTracker);
~ProgressTrackerInit();
private:
ProgressTracker* mTracker;
};
} // namespace image
} // namespace mozilla
+1 -4
View File
@@ -254,8 +254,7 @@ NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
#endif
//******************************************************************************
RasterImage::RasterImage(ProgressTracker* aProgressTracker,
ImageURL* aURI /* = nullptr */) :
RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
ImageResource(aURI), // invoke superclass's constructor
mSize(0,0),
mLockCount(0),
@@ -279,8 +278,6 @@ RasterImage::RasterImage(ProgressTracker* aProgressTracker,
mAnimationFinished(false),
mWantFullDecode(false)
{
mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker);
Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
}
+3 -8
View File
@@ -166,9 +166,6 @@ public:
virtual nsresult StopAnimation() override;
// Methods inherited from Image
nsresult Init(const char* aMimeType,
uint32_t aFlags) override;
virtual void OnSurfaceDiscarded() override;
// Raster-specific methods
@@ -289,6 +286,8 @@ public:
}
private:
nsresult Init(const char* aMimeType, uint32_t aFlags);
DrawResult DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
gfxContext* aContext,
const nsIntSize& aSize,
@@ -430,9 +429,6 @@ private: // data
TimeStamp mDrawStartTime;
// Initializes ProgressTracker and resets it on RasterImage destruction.
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
//////////////////////////////////////////////////////////////////////////////
// Scaling.
@@ -480,8 +476,7 @@ private: // data
bool CanDiscard();
protected:
explicit RasterImage(ProgressTracker* aProgressTracker = nullptr,
ImageURL* aURI = nullptr);
explicit RasterImage(ImageURL* aURI = nullptr);
bool ShouldAnimate() override;
+2 -5
View File
@@ -328,8 +328,7 @@ NS_IMPL_ISUPPORTS(VectorImage,
//------------------------------------------------------------------------------
// Constructor / Destructor
VectorImage::VectorImage(ProgressTracker* aProgressTracker,
ImageURL* aURI /* = nullptr */) :
VectorImage::VectorImage(ImageURL* aURI /* = nullptr */) :
ImageResource(aURI), // invoke superclass's constructor
mLockCount(0),
mIsInitialized(false),
@@ -337,9 +336,7 @@ VectorImage::VectorImage(ProgressTracker* aProgressTracker,
mIsDrawing(false),
mHaveAnimations(false),
mHasPendingInvalidation(false)
{
mProgressTrackerInit = new ProgressTrackerInit(this, aProgressTracker);
}
{ }
VectorImage::~VectorImage()
{
+3 -8
View File
@@ -34,9 +34,6 @@ public:
// (no public constructor - use ImageFactory)
// Methods inherited from Image
nsresult Init(const char* aMimeType,
uint32_t aFlags) override;
virtual size_t SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf)
const override;
virtual size_t SizeOfDecoded(gfxMemoryLocation aLocation,
@@ -72,8 +69,7 @@ public:
void OnSVGDocumentError();
protected:
explicit VectorImage(ProgressTracker* aProgressTracker = nullptr,
ImageURL* aURI = nullptr);
explicit VectorImage(ImageURL* aURI = nullptr);
virtual ~VectorImage();
virtual nsresult StartAnimation() override;
@@ -84,6 +80,8 @@ protected:
void Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams);
private:
nsresult Init(const char* aMimeType, uint32_t aFlags);
/**
* In catastrophic circumstances like a GPU driver crash, we may lose our
* surfaces even if they're locked. RecoverFromLossOfSurfaces discards all
@@ -112,9 +110,6 @@ private:
bool mHasPendingInvalidation; // Invalidate observers next refresh
// driver tick.
// Initializes ProgressTracker and resets it on RasterImage destruction.
nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
friend class ImageFactory;
};
+2 -1
View File
@@ -977,7 +977,8 @@ PrepareForNewPart(nsIRequest* aRequest, nsIInputStream* aInStr, uint32_t aCount,
if (result.mIsFirstPart) {
// First part for a multipart channel. Create the MultipartImage wrapper.
MOZ_ASSERT(aProgressTracker, "Shouldn't have given away tracker yet");
result.mImage = new MultipartImage(partImage, aProgressTracker);
result.mImage =
ImageFactory::CreateMultipartImage(partImage, aProgressTracker);
} else {
// Transition to the new part.
auto multipartImage = static_cast<MultipartImage*>(aExistingImage);
@@ -55,8 +55,7 @@ function cleanUpAndFinish() {
function frameUpdate(aRequest) {
if (!gDispatched) {
var util = window.getInterface(Ci.nsIDOMWindowUtils);
util.runBeforeNextEvent(function() {
Promise.resolve().then(function() {
gRanEvent = true;
});
gDispatched = true;
+1 -3
View File
@@ -439,15 +439,13 @@ MessagePumpForNonMainUIThreads::OnDispatchedEvent(nsIThreadInternal *thread)
NS_IMETHODIMP
MessagePumpForNonMainUIThreads::OnProcessNextEvent(nsIThreadInternal *thread,
bool mayWait,
uint32_t recursionDepth)
bool mayWait)
{
return NS_OK;
}
NS_IMETHODIMP
MessagePumpForNonMainUIThreads::AfterProcessNextEvent(nsIThreadInternal *thread,
uint32_t recursionDepth,
bool eventWasProcessed)
{
return NS_OK;
+53
View File
@@ -3456,6 +3456,59 @@ XPCJSRuntime::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* o
return true;
}
void
XPCJSRuntime::BeforeProcessTask(bool aMightBlock)
{
MOZ_ASSERT(NS_IsMainThread());
// If ProcessNextEvent was called during a Promise "then" callback, we
// must process any pending microtasks before blocking in the event loop,
// otherwise we may deadlock until an event enters the queue later.
if (aMightBlock) {
if (Promise::PerformMicroTaskCheckpoint()) {
// If any microtask was processed, we post a dummy event in order to
// force the ProcessNextEvent call not to block. This is required
// to support nested event loops implemented using a pattern like
// "while (condition) thread.processNextEvent(true)", in case the
// condition is triggered here by a Promise "then" callback.
class DummyRunnable : public nsRunnable {
public:
NS_IMETHOD Run() { return NS_OK; }
};
NS_DispatchToMainThread(new DummyRunnable());
}
}
// Start the slow script timer.
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
js::ResetStopwatches(Get()->Runtime());
// Push a null JSContext so that we don't see any script during
// event processing.
PushNullJSContext();
CycleCollectedJSRuntime::BeforeProcessTask(aMightBlock);
}
void
XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth)
{
// Now that we're back to the event loop, reset the slow script checkpoint.
mSlowScriptCheckpoint = mozilla::TimeStamp();
mSlowScriptSecondHalf = false;
// Call cycle collector occasionally.
MOZ_ASSERT(NS_IsMainThread());
nsJSContext::MaybePokeCC();
CycleCollectedJSRuntime::AfterProcessTask(aNewRecursionDepth);
PopNullJSContext();
}
/***************************************************************************/
void
+2 -88
View File
@@ -26,7 +26,6 @@
#include "nsDOMMutationObserver.h"
#include "nsICycleCollectorListener.h"
#include "nsThread.h"
#include "mozilla/XPTInterfaceInfoManager.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
@@ -37,9 +36,7 @@ using namespace mozilla::dom;
using namespace xpc;
using namespace JS;
NS_IMPL_ISUPPORTS(nsXPConnect,
nsIXPConnect,
nsIThreadObserver)
NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect)
nsXPConnect* nsXPConnect::gSelf = nullptr;
bool nsXPConnect::gOnceAliveNowDead = false;
@@ -61,8 +58,7 @@ const char XPC_XPCONNECT_CONTRACTID[] = "@mozilla.org/js/xpc/XPConnect;1";
nsXPConnect::nsXPConnect()
: mRuntime(nullptr),
mShuttingDown(false),
mEventDepth(0)
mShuttingDown(false)
{
mRuntime = XPCJSRuntime::newXPCJSRuntime(this);
@@ -120,11 +116,6 @@ nsXPConnect::InitStatics()
// balanced by explicit call to ReleaseXPConnectSingleton()
NS_ADDREF(gSelf);
// Set XPConnect as the main thread observer.
if (NS_FAILED(nsThread::SetMainThreadObserver(gSelf))) {
MOZ_CRASH();
}
// Fire up the SSM.
nsScriptSecurityManager::InitStatics();
gScriptSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
@@ -152,8 +143,6 @@ nsXPConnect::ReleaseXPConnectSingleton()
{
nsXPConnect* xpc = gSelf;
if (xpc) {
nsThread::SetMainThreadObserver(nullptr);
nsrefcnt cnt;
NS_RELEASE2(xpc, cnt);
}
@@ -949,81 +938,6 @@ nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval
return NS_OK;
}
namespace {
class DummyRunnable : public nsRunnable {
public:
NS_IMETHOD Run() { return NS_OK; }
};
} // namespace
NS_IMETHODIMP
nsXPConnect::OnProcessNextEvent(nsIThreadInternal* aThread, bool aMayWait,
uint32_t aRecursionDepth)
{
MOZ_ASSERT(NS_IsMainThread());
// If ProcessNextEvent was called during a Promise "then" callback, we
// must process any pending microtasks before blocking in the event loop,
// otherwise we may deadlock until an event enters the queue later.
if (aMayWait) {
if (Promise::PerformMicroTaskCheckpoint()) {
// If any microtask was processed, we post a dummy event in order to
// force the ProcessNextEvent call not to block. This is required
// to support nested event loops implemented using a pattern like
// "while (condition) thread.processNextEvent(true)", in case the
// condition is triggered here by a Promise "then" callback.
NS_DispatchToMainThread(new DummyRunnable());
}
}
// Record this event.
mEventDepth++;
// Start the slow script timer.
mRuntime->OnProcessNextEvent();
// Push a null JSContext so that we don't see any script during
// event processing.
bool ok = PushNullJSContext();
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
return NS_OK;
}
NS_IMETHODIMP
nsXPConnect::AfterProcessNextEvent(nsIThreadInternal* aThread,
uint32_t aRecursionDepth,
bool aEventWasProcessed)
{
// Watch out for unpaired events during observer registration.
if (MOZ_UNLIKELY(mEventDepth == 0))
return NS_OK;
mEventDepth--;
// Now that we're back to the event loop, reset the slow script checkpoint.
mRuntime->OnAfterProcessNextEvent();
// Call cycle collector occasionally.
MOZ_ASSERT(NS_IsMainThread());
nsJSContext::MaybePokeCC();
nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
Promise::PerformMicroTaskCheckpoint();
PopNullJSContext();
return NS_OK;
}
NS_IMETHODIMP
nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread)
{
NS_NOTREACHED("Why tell us?");
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsXPConnect::SetReportAllJSExceptions(bool newval)
{
+4 -21
View File
@@ -153,7 +153,6 @@
#include "nsJSPrincipals.h"
#include "nsIScriptObjectPrincipal.h"
#include "xpcObjectHelper.h"
#include "nsIThreadInternal.h"
#include "SandboxPrivate.h"
#include "BackstagePass.h"
@@ -246,14 +245,12 @@ static inline bool IS_WN_REFLECTOR(JSObject* obj)
// returned as function call result values they are not addref'd. Exceptions
// to this rule are noted explicitly.
class nsXPConnect final : public nsIXPConnect,
public nsIThreadObserver
class nsXPConnect final : public nsIXPConnect
{
public:
// all the interface method declarations...
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCONNECT
NS_DECL_NSITHREADOBSERVER
// non-interface implementation
public:
@@ -330,13 +327,6 @@ private:
XPCJSRuntime* mRuntime;
bool mShuttingDown;
// nsIThreadInternal doesn't remember which observers it called
// OnProcessNextEvent on when it gets around to calling AfterProcessNextEvent.
// So if XPConnect gets initialized mid-event (which can happen), we'll get
// an 'after' notification without getting an 'on' notification. If we don't
// watch out for this, we'll do an unmatched |pop| on the context stack.
uint16_t mEventDepth;
static uint32_t gReportAllJSExceptions;
public:
@@ -495,6 +485,9 @@ public:
NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const override;
virtual void BeforeProcessTask(bool aMightBlock) override;
virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;
/**
* Infrastructure for classes that need to defer part of the finalization
* until after the GC has run, for example for objects that we don't want to
@@ -621,16 +614,6 @@ public:
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
void OnProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
js::ResetStopwatches(Get()->Runtime());
}
void OnAfterProcessNextEvent() {
mSlowScriptCheckpoint = mozilla::TimeStamp();
mSlowScriptSecondHalf = false;
}
private:
XPCJSRuntime(); // no implementation
explicit XPCJSRuntime(nsXPConnect* aXPConnect);
+1 -1
View File
@@ -7729,7 +7729,7 @@ PresShell::HandlePositionedEvent(nsIFrame* aTargetFrame,
// We use weak pointers because during this tight loop, the node
// will *not* go away. And this happens on every mousemove.
while (targetElement && !targetElement->IsElement()) {
targetElement = targetElement->GetParent();
targetElement = targetElement->GetFlattenedTreeParent();
}
// If we found an element, target it. Otherwise, target *nothing*.
+3 -3
View File
@@ -431,9 +431,9 @@ SheetLoadData::OnDispatchedEvent(nsIThreadInternal* aThread)
NS_IMETHODIMP
SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
bool aMayWait,
uint32_t aRecursionDepth)
bool aMayWait)
{
// XXXkhuey this is insane!
// We want to fire our load even before or after event processing,
// whichever comes first.
FireLoadEvent(aThread);
@@ -442,9 +442,9 @@ SheetLoadData::OnProcessNextEvent(nsIThreadInternal* aThread,
NS_IMETHODIMP
SheetLoadData::AfterProcessNextEvent(nsIThreadInternal* aThread,
uint32_t aRecursionDepth,
bool aEventWasProcessed)
{
// XXXkhuey this too!
// We want to fire our load even before or after event processing,
// whichever comes first.
FireLoadEvent(aThread);
+1 -2
View File
@@ -709,14 +709,13 @@ nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
NS_IMETHODIMP
nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
bool mayWait, uint32_t depth)
bool mayWait)
{
return NS_OK;
}
NS_IMETHODIMP
nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
uint32_t depth,
bool eventWasProcessed)
{
return NS_OK;
+2 -2
View File
@@ -315,12 +315,12 @@ NS_IMETHODIMP CacheIOThread::OnDispatchedEvent(nsIThreadInternal *thread)
return NS_OK;
}
NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait, uint32_t recursionDepth)
NS_IMETHODIMP CacheIOThread::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait)
{
return NS_OK;
}
NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread, uint32_t recursionDepth,
NS_IMETHODIMP CacheIOThread::AfterProcessNextEvent(nsIThreadInternal *thread,
bool eventWasProcessed)
{
return NS_OK;
+3 -10
View File
@@ -169,16 +169,9 @@ ScreenProxy::InvalidateCacheOnNextTick()
mCacheWillInvalidate = true;
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache);
appShell->RunInStableState(r.forget());
} else {
// It's pretty bad news if we can't get the appshell. In that case,
// let's just invalidate the cache right away.
InvalidateCache();
}
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache);
nsContentUtils::RunInStableState(r.forget());
}
void
+1 -3
View File
@@ -35,10 +35,8 @@ public:
NS_IMETHOD Run(void);
NS_IMETHOD Exit(void);
NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
uint32_t aRecursionDepth);
NS_IMETHOD OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait);
NS_IMETHOD AfterProcessNextEvent(nsIThreadInternal *aThread,
uint32_t aRecursionDepth,
bool aEventWasProcessed);
// public only to be visible to Objective-C code that must call it
+3 -6
View File
@@ -719,8 +719,7 @@ nsAppShell::Exit(void)
//
// public
NS_IMETHODIMP
nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
uint32_t aRecursionDepth)
nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
@@ -730,7 +729,7 @@ nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
::CFArrayAppendValue(mAutoreleasePools, pool);
return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait, aRecursionDepth);
return nsBaseAppShell::OnProcessNextEvent(aThread, aMayWait);
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
@@ -744,7 +743,6 @@ nsAppShell::OnProcessNextEvent(nsIThreadInternal *aThread, bool aMayWait,
// public
NS_IMETHODIMP
nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
uint32_t aRecursionDepth,
bool aEventWasProcessed)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
@@ -759,8 +757,7 @@ nsAppShell::AfterProcessNextEvent(nsIThreadInternal *aThread,
::CFArrayRemoveValueAtIndex(mAutoreleasePools, count - 1);
[pool release];
return nsBaseAppShell::AfterProcessNextEvent(aThread, aRecursionDepth,
aEventWasProcessed);
return nsBaseAppShell::AfterProcessNextEvent(aThread, aEventWasProcessed);
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
+5 -116
View File
@@ -28,7 +28,6 @@ nsBaseAppShell::nsBaseAppShell()
, mSwitchTime(0)
, mLastNativeEventTime(0)
, mEventloopNestingState(eEventloopNone)
, mRunningSyncSections(false)
, mRunning(false)
, mExiting(false)
, mBlockNativeEvent(false)
@@ -37,7 +36,6 @@ nsBaseAppShell::nsBaseAppShell()
nsBaseAppShell::~nsBaseAppShell()
{
NS_ASSERTION(mSyncSections.IsEmpty(), "Must have run all sync sections");
}
nsresult
@@ -117,7 +115,7 @@ nsBaseAppShell::DoProcessMoreGeckoEvents()
// Main thread via OnProcessNextEvent below
bool
nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth)
nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait)
{
// The next native event to be processed may trigger our NativeEventCallback,
// in which case we do not want it to process any thread events since we'll
@@ -134,14 +132,7 @@ nsBaseAppShell::DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth)
mEventloopNestingState = eEventloopXPCOM;
IncrementEventloopNestingLevel();
bool result = ProcessNextNativeEvent(mayWait);
// Make sure that any sync sections registered during this most recent event
// are run now. This is not considered a stable state because we're not back
// to the event loop yet.
RunSyncSections(false, recursionDepth);
DecrementEventloopNestingLevel();
mEventloopNestingState = prevVal;
@@ -236,8 +227,7 @@ nsBaseAppShell::OnDispatchedEvent(nsIThreadInternal *thr)
// Called from the main thread
NS_IMETHODIMP
nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait,
uint32_t recursionDepth)
nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait)
{
if (mBlockNativeEvent) {
if (!mayWait)
@@ -275,13 +265,13 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait,
bool keepGoing;
do {
mLastNativeEventTime = now;
keepGoing = DoProcessNextNativeEvent(false, recursionDepth);
keepGoing = DoProcessNextNativeEvent(false);
} while (keepGoing && ((now = PR_IntervalNow()) - start) < limit);
} else {
// Avoid starving native events completely when in performance mode
if (start - mLastNativeEventTime > limit) {
mLastNativeEventTime = start;
DoProcessNextNativeEvent(false, recursionDepth);
DoProcessNextNativeEvent(false);
}
}
@@ -293,7 +283,7 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait,
mayWait = false;
mLastNativeEventTime = PR_IntervalNow();
if (!DoProcessNextNativeEvent(mayWait, recursionDepth) || !mayWait)
if (!DoProcessNextNativeEvent(mayWait) || !mayWait)
break;
}
@@ -306,9 +296,6 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, bool mayWait,
DispatchDummyEvent(thr);
}
// We're about to run an event, so we're in a stable state.
RunSyncSections(true, recursionDepth);
return NS_OK;
}
@@ -335,95 +322,11 @@ nsBaseAppShell::DecrementEventloopNestingLevel()
--mEventloopNestingLevel;
}
void
nsBaseAppShell::RunSyncSectionsInternal(bool aStable,
uint32_t aThreadRecursionLevel)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!mSyncSections.IsEmpty(), "Nothing to do!");
// We don't support re-entering sync sections. This effectively means that
// sync sections may not spin the event loop.
MOZ_RELEASE_ASSERT(!mRunningSyncSections);
mRunningSyncSections = true;
// We've got synchronous sections. Run all of them that are are awaiting a
// stable state if aStable is true (i.e. we really are in a stable state).
// Also run the synchronous sections that are simply waiting for the right
// combination of event loop nesting level and thread recursion level.
// Note that a synchronous section could add another synchronous section, so
// we don't remove elements from mSyncSections until all sections have been
// run, or else we'll screw up our iteration. Any sync sections that are not
// ready to be run are saved for later.
nsTArray<SyncSection> pendingSyncSections;
for (uint32_t i = 0; i < mSyncSections.Length(); i++) {
SyncSection& section = mSyncSections[i];
if ((aStable && section.mStable) ||
(!section.mStable &&
section.mEventloopNestingLevel == mEventloopNestingLevel &&
section.mThreadRecursionLevel == aThreadRecursionLevel)) {
section.mRunnable->Run();
}
else {
// Add to pending list.
SyncSection* pending = pendingSyncSections.AppendElement();
section.Forget(pending);
}
}
mSyncSections.SwapElements(pendingSyncSections);
mRunningSyncSections = false;
}
void
nsBaseAppShell::ScheduleSyncSection(already_AddRefed<nsIRunnable> aRunnable,
bool aStable)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
nsIThread* thread = NS_GetCurrentThread();
// Add this runnable to our list of synchronous sections.
SyncSection* section = mSyncSections.AppendElement();
section->mStable = aStable;
section->mRunnable = aRunnable;
// If aStable is false then this synchronous section is supposed to run before
// the next event at the current nesting level. Record the event loop nesting
// level and the thread recursion level so that the synchronous section will
// run at the proper time.
if (!aStable) {
section->mEventloopNestingLevel = mEventloopNestingLevel;
nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(thread);
NS_ASSERTION(threadInternal, "This should never fail!");
uint32_t recursionLevel;
if (NS_FAILED(threadInternal->GetRecursionDepth(&recursionLevel))) {
NS_ERROR("This should never fail!");
}
// Due to the weird way that the thread recursion counter is implemented we
// subtract one from the recursion level if we have one.
section->mThreadRecursionLevel = recursionLevel ? recursionLevel - 1 : 0;
}
// Ensure we've got a pending event, else the callbacks will never run.
if (!NS_HasPendingEvents(thread) && !DispatchDummyEvent(thread)) {
RunSyncSections(true, 0);
}
}
// Called from the main thread
NS_IMETHODIMP
nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr,
uint32_t recursionDepth,
bool eventWasProcessed)
{
// We've just finished running an event, so we're in a stable state.
RunSyncSections(true, recursionDepth);
return NS_OK;
}
@@ -435,17 +338,3 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
Exit();
return NS_OK;
}
void
nsBaseAppShell::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
{
ScheduleSyncSection(mozilla::Move(aRunnable), true);
}
NS_IMETHODIMP
nsBaseAppShell::RunBeforeNextEvent(nsIRunnable* aRunnable)
{
nsCOMPtr<nsIRunnable> runnable = aRunnable;
ScheduleSyncSection(runnable.forget(), false);
return NS_OK;
}
+1 -36
View File
@@ -25,7 +25,6 @@ class nsBaseAppShell : public nsIAppShell, public nsIThreadObserver,
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIAPPSHELL
void RunInStableState(already_AddRefed<nsIRunnable> runnable) override;
NS_DECL_NSITHREADOBSERVER
NS_DECL_NSIOBSERVER
@@ -77,45 +76,13 @@ protected:
uint32_t mEventloopNestingLevel;
private:
bool DoProcessNextNativeEvent(bool mayWait, uint32_t recursionDepth);
bool DoProcessNextNativeEvent(bool mayWait);
bool DispatchDummyEvent(nsIThread* target);
void IncrementEventloopNestingLevel();
void DecrementEventloopNestingLevel();
/**
* Runs all synchronous sections which are queued up in mSyncSections.
*/
void RunSyncSectionsInternal(bool stable, uint32_t threadRecursionLevel);
void RunSyncSections(bool stable, uint32_t threadRecursionLevel)
{
if (!mSyncSections.IsEmpty()) {
RunSyncSectionsInternal(stable, threadRecursionLevel);
}
}
void ScheduleSyncSection(already_AddRefed<nsIRunnable> runnable, bool stable);
struct SyncSection {
SyncSection()
: mStable(false), mEventloopNestingLevel(0), mThreadRecursionLevel(0)
{ }
void Forget(SyncSection* other) {
other->mStable = mStable;
other->mEventloopNestingLevel = mEventloopNestingLevel;
other->mThreadRecursionLevel = mThreadRecursionLevel;
other->mRunnable = mRunnable.forget();
}
bool mStable;
uint32_t mEventloopNestingLevel;
uint32_t mThreadRecursionLevel;
nsCOMPtr<nsIRunnable> mRunnable;
};
nsCOMPtr<nsIRunnable> mDummyEvent;
/**
* mBlockedWait points back to a slot that controls the wait loop in
@@ -135,8 +102,6 @@ private:
eEventloopOther // innermost native event loop is a native library/plugin etc
};
EventloopNestingState mEventloopNestingState;
nsTArray<SyncSection> mSyncSections;
bool mRunningSyncSections;
bool mRunning;
bool mExiting;
/**
+1 -23
View File
@@ -15,7 +15,7 @@ template <class T> struct already_AddRefed;
* Interface for the native event system layer. This interface is designed
* to be used on the main application thread only.
*/
[uuid(3d09973e-3975-4fd4-b103-276300cc8437)]
[uuid(7cd5c71d-223b-4afe-931d-5eedb1f2b01f)]
interface nsIAppShell : nsISupports
{
/**
@@ -73,26 +73,4 @@ interface nsIAppShell : nsISupports
* The current event loop nesting level.
*/
readonly attribute unsigned long eventloopNestingLevel;
%{ C++
/**
* Add a "synchronous section", in the form of an nsIRunnable run once the
* event loop has reached a "stable state". |runnable| must not cause any
* queued events to be processed (i.e. must not spin the event loop). We've
* reached a stable state when the currently executing task/event has
* finished, see:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
* In practice this runs aRunnable once the currently executing event
* finishes. If called multiple times per task/event, all the runnables will
* be executed, in the order in which runInStableState() was called.
*/
virtual void RunInStableState(already_AddRefed<nsIRunnable> runnable) = 0;
%}
/**
* Run the given runnable before the next iteration of the event loop (this
* includes native events too). If a nested loop is spawned within the current
* event then the runnable will not be run until that loop has terminated.
*/
void runBeforeNextEvent(in nsIRunnable runnable);
};
+3 -10
View File
@@ -198,16 +198,9 @@ nsScreenManagerProxy::InvalidateCacheOnNextTick()
mCacheWillInvalidate = true;
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
if (appShell) {
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache);
appShell->RunInStableState(r.forget());
} else {
// It's pretty bad news if we can't get the appshell. In that case,
// let's just invalidate the cache right away.
InvalidateCache();
}
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache);
nsContentUtils::RunInStableState(r.forget());
}
void
-4
View File
@@ -8,10 +8,6 @@ XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
MOCHITEST_MANIFESTS += ['mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome.ini']
GeckoCppUnitTests([
'TestAppShellSteadyState',
])
FAIL_ON_WARNINGS = True
# if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+127
View File
@@ -62,6 +62,7 @@
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/DebuggerOnGCRunnable.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ScriptSettings.h"
#include "jsprf.h"
#include "js/Debug.h"
@@ -76,6 +77,7 @@
#endif
#include "nsIException.h"
#include "nsThread.h"
#include "nsThreadUtils.h"
#include "xpcpublic.h"
@@ -401,9 +403,18 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(JSRuntime* aParentRuntime,
, mJSRuntime(nullptr)
, mPrevGCSliceCallback(nullptr)
, mJSHolders(256)
, mDoingStableStates(false)
, mOutOfMemoryState(OOMState::OK)
, mLargeAllocationFailureState(OOMState::OK)
{
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
mOwningThread = thread.forget().downcast<nsThread>().take();
MOZ_RELEASE_ASSERT(mOwningThread);
mOwningThread->SetScriptObserver(this);
// The main thread has a base recursion depth of 0, workers of 1.
mBaseRecursionDepth = RecursionDepth();
mozilla::dom::InitScriptSettings();
mJSRuntime = JS_NewRuntime(aMaxBytes, aMaxNurseryBytes, aParentRuntime);
@@ -439,6 +450,13 @@ CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
MOZ_ASSERT(mJSRuntime);
MOZ_ASSERT(!mDeferredFinalizerTable.Count());
// Last chance to process any events.
ProcessMetastableStateQueue(mBaseRecursionDepth);
MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
ProcessStableStateQueue();
MOZ_ASSERT(mStableStateEvents.IsEmpty());
// Clear mPendingException first, since it might be cycle collected.
mPendingException = nullptr;
@@ -447,6 +465,9 @@ CycleCollectedJSRuntime::~CycleCollectedJSRuntime()
nsCycleCollector_forgetJSRuntime();
mozilla::dom::DestroyScriptSettings();
mOwningThread->SetScriptObserver(nullptr);
NS_RELEASE(mOwningThread);
}
size_t
@@ -1010,6 +1031,112 @@ CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
js::DumpHeap(Runtime(), aFile, js::CollectNurseryBeforeDump);
}
void
CycleCollectedJSRuntime::ProcessStableStateQueue()
{
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
for (uint32_t i = 0; i < mStableStateEvents.Length(); ++i) {
nsCOMPtr<nsIRunnable> event = mStableStateEvents[i].forget();
event->Run();
}
mStableStateEvents.Clear();
mDoingStableStates = false;
}
void
CycleCollectedJSRuntime::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
{
MOZ_RELEASE_ASSERT(!mDoingStableStates);
mDoingStableStates = true;
nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
for (uint32_t i = 0; i < localQueue.Length(); ++i)
{
RunInMetastableStateData& data = localQueue[i];
if (data.mRecursionDepth != aRecursionDepth) {
continue;
}
{
nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
runnable->Run();
}
localQueue.RemoveElementAt(i--);
}
// If the queue has events in it now, they were added from something we called,
// so they belong at the end of the queue.
localQueue.AppendElements(mMetastableStateEvents);
localQueue.SwapElements(mMetastableStateEvents);
mDoingStableStates = false;
}
void
CycleCollectedJSRuntime::AfterProcessTask(uint32_t aRecursionDepth)
{
// See HTML 6.1.4.2 Processing model
// Execute any events that were waiting for a microtask to complete.
// This is not (yet) in the spec.
ProcessMetastableStateQueue(aRecursionDepth);
// Step 4.1: Execute microtasks.
if (NS_IsMainThread()) {
nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
}
Promise::PerformMicroTaskCheckpoint();
// Step 4.2 Execute any events that were waiting for a stable state.
ProcessStableStateQueue();
}
void
CycleCollectedJSRuntime::AfterProcessMicrotask()
{
AfterProcessMicrotask(RecursionDepth());
}
void
CycleCollectedJSRuntime::AfterProcessMicrotask(uint32_t aRecursionDepth)
{
// Between microtasks, execute any events that were waiting for a microtask
// to complete.
ProcessMetastableStateQueue(aRecursionDepth);
}
uint32_t
CycleCollectedJSRuntime::RecursionDepth()
{
return mOwningThread->RecursionDepth();
}
void
CycleCollectedJSRuntime::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
{
MOZ_ASSERT(mJSRuntime);
mStableStateEvents.AppendElement(Move(aRunnable));
}
void
CycleCollectedJSRuntime::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
{
RunInMetastableStateData data;
data.mRunnable = aRunnable;
MOZ_ASSERT(mOwningThread);
data.mRecursionDepth = RecursionDepth();
// There must be an event running to get here.
MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
mMetastableStateEvents.AppendElement(Move(data));
}
IncrementalFinalizeRunnable::IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
DeferredFinalizerTable& aFinalizers)
+32 -1
View File
@@ -21,6 +21,7 @@
class nsCycleCollectionNoteRootCallback;
class nsIException;
class nsIRunnable;
class nsThread;
namespace js {
struct Class;
@@ -151,7 +152,6 @@ protected:
}
private:
void
DescribeGCThing(bool aIsMarked, JS::GCCellPtr aThing,
nsCycleCollectionTraversalCallback& aCb) const;
@@ -208,6 +208,10 @@ private:
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
void TraceNativeGrayRoots(JSTracer* aTracer);
void AfterProcessMicrotask(uint32_t aRecursionDepth);
void ProcessStableStateQueue();
void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
public:
enum DeferredFinalizeType {
FinalizeIncrementally,
@@ -294,6 +298,21 @@ public:
return mJSRuntime;
}
// nsThread entrypoints
virtual void BeforeProcessTask(bool aMightBlock) { };
virtual void AfterProcessTask(uint32_t aRecursionDepth);
// microtask processor entry point
void AfterProcessMicrotask();
uint32_t RecursionDepth();
// Run in stable state (call through nsContentUtils)
void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
// This isn't in the spec at all yet, but this gets the behavior we want for IDB.
// Runs after the current microtask completes.
void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
// Get the current thread's CycleCollectedJSRuntime. Returns null if there
// isn't one.
static CycleCollectedJSRuntime* Get();
@@ -325,9 +344,21 @@ private:
nsRefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
nsCOMPtr<nsIException> mPendingException;
nsThread* mOwningThread; // Manual refcounting to avoid include hell.
std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
struct RunInMetastableStateData
{
nsCOMPtr<nsIRunnable> mRunnable;
uint32_t mRecursionDepth;
};
nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
nsTArray<RunInMetastableStateData> mMetastableStateEvents;
uint32_t mBaseRecursionDepth;
bool mDoingStableStates;
OOMState mOutOfMemoryState;
OOMState mLargeAllocationFailureState;
};
+1 -3
View File
@@ -526,15 +526,13 @@ LazyIdleThread::OnDispatchedEvent(nsIThreadInternal* /*aThread */)
NS_IMETHODIMP
LazyIdleThread::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
bool /* aMayWait */,
uint32_t /* aRecursionDepth */)
bool /* aMayWait */)
{
return NS_OK;
}
NS_IMETHODIMP
LazyIdleThread::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
uint32_t /* aRecursionDepth */,
bool aEventWasProcessed)
{
bool shouldNotifyIdle;
+5 -20
View File
@@ -13,7 +13,7 @@ interface nsIThreadObserver;
* The XPCOM thread object implements this interface, which allows a consumer
* to observe dispatch activity on the thread.
*/
[scriptable, uuid(b24c5af3-43c2-4d17-be14-94d6648a305f)]
[scriptable, uuid(9cc51754-2eb3-4b46-ae99-38a61881c622)]
interface nsIThreadInternal : nsIThread
{
/**
@@ -25,13 +25,6 @@ interface nsIThreadInternal : nsIThread
*/
attribute nsIThreadObserver observer;
/**
* The current recursion depth, 0 when no events are running, 1 when a single
* event is running, and higher when nested events are running. Must only be
* called on the target thread.
*/
readonly attribute unsigned long recursionDepth;
/**
* Add an observer that will *only* receive onProcessNextEvent,
* beforeProcessNextEvent. and afterProcessNextEvent callbacks. Always called
@@ -81,7 +74,7 @@ interface nsIThreadInternal : nsIThread
* onDispatchedEvent(thread) {
* NativeQueue.signal();
* }
* onProcessNextEvent(thread, mayWait, recursionDepth) {
* onProcessNextEvent(thread, mayWait) {
* if (NativeQueue.hasNextEvent())
* NativeQueue.processNextEvent();
* while (mayWait && !thread.hasPendingEvent()) {
@@ -100,7 +93,7 @@ interface nsIThreadInternal : nsIThread
* afterProcessNextEvent, then another that inherits the first and adds
* onDispatchedEvent.
*/
[scriptable, uuid(09b424c3-26b0-4128-9039-d66f85b02c63)]
[uuid(cc8da053-1776-44c2-9199-b5a629d0a19d)]
interface nsIThreadObserver : nsISupports
{
/**
@@ -122,29 +115,21 @@ interface nsIThreadObserver : nsISupports
* @param mayWait
* Indicates whether or not the method is allowed to block the calling
* thread. For example, this parameter is false during thread shutdown.
* @param recursionDepth
* Indicates the number of calls to ProcessNextEvent on the call stack in
* addition to the current call.
*/
void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait,
in unsigned long recursionDepth);
void onProcessNextEvent(in nsIThreadInternal thread, in boolean mayWait);
/**
* This method is called (from nsIThread::ProcessNextEvent) after an event
* is processed. It does not guarantee that an event was actually processed
* (depends on the value of |eventWasProcessed|. This method is only called
* on the target thread.
* on the target thread. DO NOT EVER RUN SCRIPT FROM THIS CALLBACK!!!
*
* @param thread
* The thread that processed another event.
* @param recursionDepth
* Indicates the number of calls to ProcessNextEvent on the call stack in
* addition to the current call.
* @param eventWasProcessed
* Indicates whether an event was actually processed. May be false if the
* |mayWait| flag was false when calling nsIThread::ProcessNextEvent().
*/
void afterProcessNextEvent(in nsIThreadInternal thread,
in unsigned long recursionDepth,
in bool eventWasProcessed);
};
+26 -38
View File
@@ -22,6 +22,7 @@
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "pratom.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/Logging.h"
#include "nsIObserverService.h"
#if !defined(MOZILLA_XPCOMRT_API)
@@ -89,8 +90,6 @@ GetThreadLog()
NS_DECL_CI_INTERFACE_GETTER(nsThread)
nsIThreadObserver* nsThread::sMainThreadObserver = nullptr;
//-----------------------------------------------------------------------------
// Because we do not have our own nsIFactory, we have to implement nsIClassInfo
// somewhat manually.
@@ -407,6 +406,7 @@ int sCanaryOutputFD = -1;
nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
: mLock("nsThread.mLock")
, mScriptObserver(nullptr)
, mEvents(&mEventsRoot)
, mPriority(PRIORITY_NORMAL)
, mThread(nullptr)
@@ -791,22 +791,19 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
}
#endif
bool notifyMainThreadObserver =
(MAIN_THREAD == mIsMainThread) && sMainThreadObserver;
if (notifyMainThreadObserver) {
sMainThreadObserver->OnProcessNextEvent(this, reallyWait,
mNestedEventLoopDepth);
++mNestedEventLoopDepth;
bool callScriptObserver = !!mScriptObserver;
if (callScriptObserver) {
mScriptObserver->BeforeProcessTask(reallyWait);
}
nsCOMPtr<nsIThreadObserver> obs = mObserver;
if (obs) {
obs->OnProcessNextEvent(this, reallyWait, mNestedEventLoopDepth);
obs->OnProcessNextEvent(this, reallyWait);
}
NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent,
(this, reallyWait, mNestedEventLoopDepth));
++mNestedEventLoopDepth;
NOTIFY_EVENT_OBSERVERS(OnProcessNextEvent, (this, reallyWait));
#ifdef MOZ_CANARY
Canary canary;
@@ -839,20 +836,18 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
}
}
--mNestedEventLoopDepth;
NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent,
(this, mNestedEventLoopDepth, *aResult));
NOTIFY_EVENT_OBSERVERS(AfterProcessNextEvent, (this, *aResult));
if (obs) {
obs->AfterProcessNextEvent(this, mNestedEventLoopDepth, *aResult);
obs->AfterProcessNextEvent(this, *aResult);
}
if (notifyMainThreadObserver && sMainThreadObserver) {
sMainThreadObserver->AfterProcessNextEvent(this, mNestedEventLoopDepth,
*aResult);
if (callScriptObserver && mScriptObserver) {
mScriptObserver->AfterProcessTask(mNestedEventLoopDepth);
}
--mNestedEventLoopDepth;
return rv;
}
@@ -929,15 +924,11 @@ nsThread::SetObserver(nsIThreadObserver* aObs)
return NS_OK;
}
NS_IMETHODIMP
nsThread::GetRecursionDepth(uint32_t* aDepth)
uint32_t
nsThread::RecursionDepth() const
{
if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
return NS_ERROR_NOT_SAME_THREAD;
}
*aDepth = mNestedEventLoopDepth;
return NS_OK;
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
return mNestedEventLoopDepth;
}
NS_IMETHODIMP
@@ -1036,19 +1027,16 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
return NS_OK;
}
nsresult
nsThread::SetMainThreadObserver(nsIThreadObserver* aObserver)
void
nsThread::SetScriptObserver(mozilla::CycleCollectedJSRuntime* aScriptObserver)
{
if (aObserver && nsThread::sMainThreadObserver) {
return NS_ERROR_NOT_AVAILABLE;
if (!aScriptObserver) {
mScriptObserver = nullptr;
return;
}
if (!NS_IsMainThread()) {
return NS_ERROR_UNEXPECTED;
}
nsThread::sMainThreadObserver = aObserver;
return NS_OK;
MOZ_ASSERT(!mScriptObserver);
mScriptObserver = aScriptObserver;
}
//-----------------------------------------------------------------------------
+10 -4
View File
@@ -18,6 +18,10 @@
#include "nsAutoPtr.h"
#include "mozilla/AlreadyAddRefed.h"
namespace mozilla {
class CycleCollectedJSRuntime;
}
// A native thread
class nsThread
: public nsIThreadInternal
@@ -67,12 +71,13 @@ public:
mEventObservers.Clear();
}
static nsresult
SetMainThreadObserver(nsIThreadObserver* aObserver);
void
SetScriptObserver(mozilla::CycleCollectedJSRuntime* aScriptObserver);
uint32_t
RecursionDepth() const;
protected:
static nsIThreadObserver* sMainThreadObserver;
class nsChainedEventQueue;
class nsNestedEventTarget;
@@ -175,6 +180,7 @@ protected:
mozilla::Mutex mLock;
nsCOMPtr<nsIThreadObserver> mObserver;
mozilla::CycleCollectedJSRuntime* mScriptObserver;
// Only accessed on the target thread.
nsAutoTObserverArray<nsCOMPtr<nsIThreadObserver>, 2> mEventObservers;