mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -508,6 +508,7 @@ Promise::PerformMicroTaskCheckpoint()
|
||||
if (cx.isSome()) {
|
||||
JS_CheckForInterrupt(cx.ref());
|
||||
}
|
||||
runtime->AfterProcessMicrotask();
|
||||
} while (!microtaskQueue.empty());
|
||||
|
||||
return true;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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*.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user