зеркало из https://github.com/mozilla/gecko-dev.git
Bug 547399 - 'Workers: Don't let worker messages run if the worker is suspended'. r+sr=jst.
This commit is contained in:
Родитель
fe2a33cdae
Коммит
9554ef498f
|
@ -933,6 +933,17 @@ public:
|
||||||
return NS_ERROR_ABORT;
|
return NS_ERROR_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the worker is suspended and we're running on the main thread then we
|
||||||
|
// can't actually dispatch the event yet. Instead we queue it for whenever
|
||||||
|
// we resume.
|
||||||
|
if (mWorker->IsSuspended() && NS_IsMainThread()) {
|
||||||
|
if (!mWorker->QueueSuspendedRunnable(this)) {
|
||||||
|
NS_ERROR("Out of memory?!");
|
||||||
|
return NS_ERROR_ABORT;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMEventTarget> target = mToInner ?
|
nsCOMPtr<nsIDOMEventTarget> target = mToInner ?
|
||||||
static_cast<nsDOMWorkerMessageHandler*>(mWorker->GetInnerScope()) :
|
static_cast<nsDOMWorkerMessageHandler*>(mWorker->GetInnerScope()) :
|
||||||
static_cast<nsDOMWorkerMessageHandler*>(mWorker);
|
static_cast<nsDOMWorkerMessageHandler*>(mWorker);
|
||||||
|
@ -1044,6 +1055,7 @@ nsDOMWorker::~nsDOMWorker()
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ASSERTION(!mFeatures.Length(), "Live features!");
|
NS_ASSERTION(!mFeatures.Length(), "Live features!");
|
||||||
|
NS_ASSERTION(!mQueuedRunnables.Length(), "Events that never ran!");
|
||||||
|
|
||||||
nsCOMPtr<nsIThread> mainThread;
|
nsCOMPtr<nsIThread> mainThread;
|
||||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
NS_GetMainThread(getter_AddRefs(mainThread));
|
||||||
|
@ -1349,6 +1361,9 @@ nsDOMWorker::Kill()
|
||||||
features[index]->Cancel();
|
features[index]->Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure we kill any queued runnables that we never had a chance to run.
|
||||||
|
mQueuedRunnables.Clear();
|
||||||
|
|
||||||
// We no longer need to keep our inner scope.
|
// We no longer need to keep our inner scope.
|
||||||
mInnerScope = nsnull;
|
mInnerScope = nsnull;
|
||||||
mScopeWN = nsnull;
|
mScopeWN = nsnull;
|
||||||
|
@ -1400,6 +1415,13 @@ nsDOMWorker::Resume()
|
||||||
if (shouldResumeFeatures) {
|
if (shouldResumeFeatures) {
|
||||||
ResumeFeatures();
|
ResumeFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repost any events that were queued for the main thread while suspended.
|
||||||
|
PRUint32 count = mQueuedRunnables.Length();
|
||||||
|
for (PRUint32 index = 0; index < count; index++) {
|
||||||
|
NS_DispatchToCurrentThread(mQueuedRunnables[index]);
|
||||||
|
}
|
||||||
|
mQueuedRunnables.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
PRBool
|
PRBool
|
||||||
|
@ -1934,6 +1956,13 @@ nsDOMWorker::GetExpirationTime()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
PRBool
|
||||||
|
nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
return mQueuedRunnables.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDOMWorker::AddEventListener(const nsAString& aType,
|
nsDOMWorker::AddEventListener(const nsAString& aType,
|
||||||
nsIDOMEventListener* aListener,
|
nsIDOMEventListener* aListener,
|
||||||
|
|
|
@ -64,6 +64,7 @@ class nsDOMWorkerTimeout;
|
||||||
class nsICancelable;
|
class nsICancelable;
|
||||||
class nsIDOMEventListener;
|
class nsIDOMEventListener;
|
||||||
class nsIEventTarget;
|
class nsIEventTarget;
|
||||||
|
class nsIRunnable;
|
||||||
class nsIScriptGlobalObject;
|
class nsIScriptGlobalObject;
|
||||||
class nsIXPConnectWrappedNative;
|
class nsIXPConnectWrappedNative;
|
||||||
|
|
||||||
|
@ -118,6 +119,7 @@ class nsDOMWorker : public nsDOMWorkerMessageHandler,
|
||||||
friend class nsDOMWorkerXHR;
|
friend class nsDOMWorkerXHR;
|
||||||
friend class nsDOMWorkerXHRProxy;
|
friend class nsDOMWorkerXHRProxy;
|
||||||
friend class nsReportErrorRunnable;
|
friend class nsReportErrorRunnable;
|
||||||
|
friend class nsDOMFireEventRunnable;
|
||||||
|
|
||||||
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
|
friend JSBool DOMWorkerOperationCallback(JSContext* aCx);
|
||||||
friend void DOMWorkerErrorReporter(JSContext* aCx,
|
friend void DOMWorkerErrorReporter(JSContext* aCx,
|
||||||
|
@ -288,6 +290,8 @@ private:
|
||||||
return mLocation;
|
return mLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool QueueSuspendedRunnable(nsIRunnable* aRunnable);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// mParent will live as long as mParentWN but only mParentWN will keep the JS
|
// mParent will live as long as mParentWN but only mParentWN will keep the JS
|
||||||
|
@ -327,6 +331,8 @@ private:
|
||||||
|
|
||||||
nsCOMPtr<nsIWorkerLocation> mLocation;
|
nsCOMPtr<nsIWorkerLocation> mLocation;
|
||||||
|
|
||||||
|
nsTArray<nsCOMPtr<nsIRunnable> > mQueuedRunnables;
|
||||||
|
|
||||||
PRPackedBool mSuspended;
|
PRPackedBool mSuspended;
|
||||||
PRPackedBool mCompileAttempted;
|
PRPackedBool mCompileAttempted;
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,6 +82,9 @@ _TEST_FILES = \
|
||||||
relativeLoad_import.js \
|
relativeLoad_import.js \
|
||||||
test_scopeOnerror.html \
|
test_scopeOnerror.html \
|
||||||
scopeOnerror_worker.js \
|
scopeOnerror_worker.js \
|
||||||
|
test_suspend.html \
|
||||||
|
suspend_iframe.html \
|
||||||
|
suspend_worker.js \
|
||||||
test_simpleThread.html \
|
test_simpleThread.html \
|
||||||
simpleThread_worker.js \
|
simpleThread_worker.js \
|
||||||
test_terminate.html \
|
test_terminate.html \
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test for DOM Worker Threads Suspending</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="test">
|
||||||
|
<div id="output"></div>
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
var output = document.getElementById("output");
|
||||||
|
|
||||||
|
var worker;
|
||||||
|
|
||||||
|
function terminateWorker() {
|
||||||
|
if (worker) {
|
||||||
|
worker.terminate();
|
||||||
|
worker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startWorker(messageCallback, errorCallback) {
|
||||||
|
worker = new Worker("suspend_worker.js");
|
||||||
|
|
||||||
|
worker.onmessage = function(event) {
|
||||||
|
output.textContent = output.textContent + event.data + "\n";
|
||||||
|
messageCallback(event.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = function(event) {
|
||||||
|
this.terminate();
|
||||||
|
errorCallback(event.message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,5 @@
|
||||||
|
var counter = 0;
|
||||||
|
|
||||||
|
setInterval(function() {
|
||||||
|
postMessage(++counter);
|
||||||
|
}, 100);
|
|
@ -0,0 +1,113 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test for DOM Worker Threads</title>
|
||||||
|
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none"></div>
|
||||||
|
<pre id="test">
|
||||||
|
<iframe id="workerFrame" src="suspend_iframe.html" onload="subframeLoaded();">
|
||||||
|
</iframe>
|
||||||
|
<script class="testbody" type="text/javascript">
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
var iframe;
|
||||||
|
var lastCount;
|
||||||
|
|
||||||
|
var suspended;
|
||||||
|
var resumed;
|
||||||
|
|
||||||
|
var interval;
|
||||||
|
var oldMessageCount;
|
||||||
|
var waitCount = 0;
|
||||||
|
|
||||||
|
function setCachePref(enabled) {
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||||
|
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPrefBranch);
|
||||||
|
if (enabled) {
|
||||||
|
prefBranch.setBoolPref("browser.sessionhistory.cache_subframes", true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
prefBranch.clearUserPref("browser.sessionhistory.cache_subframes");
|
||||||
|
} catch (e) { /* Pref didn't exist, meh */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishTest() {
|
||||||
|
setCachePref(false);
|
||||||
|
iframe.terminateWorker();
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitInterval() {
|
||||||
|
is(iframe.location, "about:blank", "Wrong url!");
|
||||||
|
is(suspended, true, "Not suspended?");
|
||||||
|
is(lastCount, oldMessageCount, "Received a message while suspended!");
|
||||||
|
if (++waitCount == 5) {
|
||||||
|
clearInterval(interval);
|
||||||
|
iframe.history.back();
|
||||||
|
resumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function badOnloadCallback() {
|
||||||
|
ok(false, "iframe didn't go into fastback cache!");
|
||||||
|
finishTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
function suspendCallback() {
|
||||||
|
is(iframe.location, "about:blank", "Wrong url!");
|
||||||
|
is(suspended, true, "Not suspended?");
|
||||||
|
iframe.onload = badOnloadCallback;
|
||||||
|
oldMessageCount = lastCount;
|
||||||
|
interval = setInterval(waitInterval, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function messageCallback(data) {
|
||||||
|
if (!suspended) {
|
||||||
|
ok(lastCount === undefined || lastCount == data - 1,
|
||||||
|
"Data is inconsistent");
|
||||||
|
lastCount = data;
|
||||||
|
if (lastCount == 50) {
|
||||||
|
setCachePref(true);
|
||||||
|
iframe.location = "about:blank";
|
||||||
|
suspended = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var newLocation =
|
||||||
|
window.location.toString().replace("test_suspend.html",
|
||||||
|
"suspend_iframe.html");
|
||||||
|
is(iframe.location, newLocation, "Wrong url!");
|
||||||
|
ok(suspended && resumed, "Got message before resumed!");
|
||||||
|
is(lastCount, data - 1, "Missed a message, suspend failed!");
|
||||||
|
finishTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorCallback(data) {
|
||||||
|
ok(false, "Iframe had an error: '" + data + "'");
|
||||||
|
finishTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
function subframeLoaded() {
|
||||||
|
var iframeElement = document.getElementById("workerFrame");
|
||||||
|
iframeElement.onload = suspendCallback;
|
||||||
|
|
||||||
|
iframe = iframeElement.contentWindow;
|
||||||
|
ok(iframe, "No iframe?!");
|
||||||
|
|
||||||
|
iframe.startWorker(messageCallback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
Загрузка…
Ссылка в новой задаче