import change from rmottola/Arctic-Fox:

- Bug 1146557 P1 Fix CacheStorage and Cache to QI to nsISupports correctly. r=ehsan (b56ba80cd)
- Bug 1146557 P2 Flip dom.caches.enabled to true on non-release builds. (b1ac38779)
- Bug 1126014 - DomainPolicy support for e10s. r=mrbkap (484fa7f8d)
- Bug 1092102 - Implement worker debugger runnables; (cfb3cb53d)
- Bug 1092102 - Implement WorkerDebugger.initialize; (24f467b14)
- Bug 1092102 - Implement WorkerDebugger.postMessage; (ad69a161d)
- Bug 1092102 - Implement WorkerDebuggerGlobalScope.reportError; (f937913f2)
- Bug 1092102 - Implement WorkerDebuggerGlobalScope.enterEventLoop; (d1d0ad392)
- Bug 1092102 - Implement WorkerDebuggerGlobalScope.setImmediate (a22de0f0d)
- Bug 1092102 - Implement WorkerDebuggerGlobalScope.createSandbox (d281de130)
- Bug 1092102 followup: Add missing 'override' annotation on DebuggerImmediateRunnable::IsDebuggerRunnable(). (b4d053cfe)
- Bug 1092102, followup 2: Add missing 'override' annotation to WorkerDebuggerSandboxPrivate::GetGlobalJSObject() (and 'virtual', for consistency). (72c9f3c84)
- Bug 1092102 - Rename Suspend/Resume to Freeze/Thaw; (a7f2b5c11)
- Bug 1092102 - Implement WorkerDebugger.isFrozen; (805ba0e9e)
This commit is contained in:
2019-06-29 10:04:40 +08:00
parent 7f40159c31
commit 253db3c3f9
72 changed files with 2540 additions and 417 deletions
+112 -6
View File
@@ -5,17 +5,50 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DomainPolicy.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/unused.h"
#include "nsIMessageManager.h"
#include "nsScriptSecurityManager.h"
namespace mozilla {
using namespace ipc;
using namespace dom;
NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy)
DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet())
, mSuperBlacklist(new DomainSet())
, mWhitelist(new DomainSet())
, mSuperWhitelist(new DomainSet())
{}
static nsresult
BroadcastDomainSetChange(DomainSetType aSetType, DomainSetChangeType aChangeType,
nsIURI* aDomain = nullptr)
{
MOZ_ASSERT(XRE_GetProcessType() == GoannaProcessType_Default,
"DomainPolicy should only be exposed to the chrome process.");
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
if (!parents.Length()) {
return NS_OK;
}
OptionalURIParams uri;
SerializeURI(aDomain, uri);
for (uint32_t i = 0; i < parents.Length(); i++) {
unused << parents[i]->SendDomainSetChanged(aSetType, aChangeType, uri);
}
return NS_OK;
}
DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet(BLACKLIST))
, mSuperBlacklist(new DomainSet(SUPER_BLACKLIST))
, mWhitelist(new DomainSet(WHITELIST))
, mSuperWhitelist(new DomainSet(SUPER_WHITELIST))
{
if (XRE_GetProcessType() == GoannaProcessType_Default) {
BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY);
}
}
DomainPolicy::~DomainPolicy()
{
@@ -75,10 +108,47 @@ DomainPolicy::Deactivate()
mSuperWhitelist = nullptr;
// Inform the SSM.
nsScriptSecurityManager::GetScriptSecurityManager()->DeactivateDomainPolicy();
nsScriptSecurityManager* ssm = nsScriptSecurityManager::GetScriptSecurityManager();
if (ssm) {
ssm->DeactivateDomainPolicy();
}
if (XRE_GetProcessType() == GoannaProcessType_Default) {
BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY);
}
return NS_OK;
}
void
DomainPolicy::CloneDomainPolicy(DomainPolicyClone* aClone)
{
aClone->active() = true;
static_cast<DomainSet*>(mBlacklist.get())->CloneSet(&aClone->blacklist());
static_cast<DomainSet*>(mSuperBlacklist.get())->CloneSet(&aClone->superBlacklist());
static_cast<DomainSet*>(mWhitelist.get())->CloneSet(&aClone->whitelist());
static_cast<DomainSet*>(mSuperWhitelist.get())->CloneSet(&aClone->superWhitelist());
}
static
void
CopyURIs(const InfallibleTArray<URIParams>& aDomains, nsIDomainSet* aSet)
{
for (uint32_t i = 0; i < aDomains.Length(); i++) {
nsCOMPtr<nsIURI> uri = DeserializeURI(aDomains[i]);
aSet->Add(uri);
}
}
void
DomainPolicy::ApplyClone(DomainPolicyClone* aClone)
{
nsCOMPtr<nsIDomainSet> list;
CopyURIs(aClone->blacklist(), mBlacklist);
CopyURIs(aClone->whitelist(), mWhitelist);
CopyURIs(aClone->superBlacklist(), mSuperBlacklist);
CopyURIs(aClone->superWhitelist(), mSuperWhitelist);
}
static already_AddRefed<nsIURI>
GetCanonicalClone(nsIURI* aURI)
{
@@ -100,6 +170,9 @@ DomainSet::Add(nsIURI* aDomain)
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
mHashTable.PutEntry(clone);
if (XRE_GetProcessType() == GoannaProcessType_Default)
return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain);
return NS_OK;
}
@@ -109,6 +182,9 @@ DomainSet::Remove(nsIURI* aDomain)
nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
mHashTable.RemoveEntry(clone);
if (XRE_GetProcessType() == GoannaProcessType_Default)
return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain);
return NS_OK;
}
@@ -116,6 +192,9 @@ NS_IMETHODIMP
DomainSet::Clear()
{
mHashTable.Clear();
if (XRE_GetProcessType() == GoannaProcessType_Default)
return BroadcastDomainSetChange(mType, CLEAR_DOMAINS);
return NS_OK;
}
@@ -160,4 +239,31 @@ DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains)
}
NS_IMETHODIMP
DomainSet::GetType(uint32_t* aType)
{
*aType = mType;
return NS_OK;
}
static
PLDHashOperator
DomainEnumerator(nsURIHashKey* aEntry, void* aUserArg)
{
InfallibleTArray<URIParams>* uris = static_cast<InfallibleTArray<URIParams>*>(aUserArg);
nsIURI* key = aEntry->GetKey();
URIParams uri;
SerializeURI(key, uri);
uris->AppendElement(uri);
return PL_DHASH_NEXT;
}
void
DomainSet::CloneSet(InfallibleTArray<URIParams>* aDomains)
{
mHashTable.EnumerateEntries(DomainEnumerator, aDomains);
}
} /* namespace mozilla */
+30 -1
View File
@@ -13,6 +13,30 @@
namespace mozilla {
namespace dom {
class nsIContentParent;
};
namespace ipc {
class URIParams;
};
enum DomainSetChangeType{
ACTIVATE_POLICY,
DEACTIVATE_POLICY,
ADD_DOMAIN,
REMOVE_DOMAIN,
CLEAR_DOMAINS
};
enum DomainSetType{
NO_TYPE,
BLACKLIST,
SUPER_BLACKLIST,
WHITELIST,
SUPER_WHITELIST
};
class DomainPolicy : public nsIDomainPolicy
{
public:
@@ -35,11 +59,16 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMAINSET
DomainSet() {}
explicit DomainSet(DomainSetType aType)
: mType(aType)
{}
void CloneSet(InfallibleTArray<mozilla::ipc::URIParams>* aDomains);
protected:
virtual ~DomainSet() {}
nsTHashtable<nsURIHashKey> mHashTable;
DomainSetType mType;
};
} /* namespace mozilla */
+20 -2
View File
@@ -8,6 +8,16 @@
interface nsIURI;
interface nsIDomainSet;
%{ C++
namespace mozilla {
namespace dom {
class DomainPolicyClone;
}
}
%}
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
/*
* When a domain policy is instantiated by invoking activateDomainPolicy() on
* nsIScriptSecurityManager, these domain sets are consulted when each new
@@ -20,7 +30,7 @@ interface nsIDomainSet;
* When deactivate() is invoked, the domain sets are emptied, and the
* nsIDomainPolicy ceases to have any effect on the system.
*/
[scriptable, builtinclass, uuid(27b10f54-f34b-42b7-8594-4348d3ad7953)]
[scriptable, builtinclass, uuid(82b24a20-6701-4d40-a0f9-f5dc7321b555)]
interface nsIDomainPolicy : nsISupports
{
readonly attribute nsIDomainSet blacklist;
@@ -29,11 +39,19 @@ interface nsIDomainPolicy : nsISupports
readonly attribute nsIDomainSet superWhitelist;
void deactivate();
[noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone);
[noscript, notxpcom] void applyClone(in DomainPolicyClonePtr aClone);
};
[scriptable, builtinclass, uuid(946a01ff-6525-4007-a2c2-447ebe1875d3)]
[scriptable, builtinclass, uuid(665c981b-0a0f-4229-ac06-a826e02d4f69)]
interface nsIDomainSet : nsISupports
{
/*
* The type of the set. See: DomainSetType
*/
[noscript] readonly attribute uint32_t type;
/*
* Add a domain to the set. No-op if it already exists.
*/
+24 -1
View File
@@ -14,12 +14,19 @@ interface nsILoadContext;
%{ C++
#include "jspubtd.h"
namespace mozilla {
namespace dom {
class DomainPolicyClone;
}
}
%}
[ptr] native JSContextPtr(JSContext);
[ptr] native JSObjectPtr(JSObject);
[ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone);
[scriptable, uuid(f649959d-dae3-4027-83fd-5b7f8c8a8815)]
[scriptable, uuid(ba602ca6-dc7a-457e-a57a-ee5b343fd863)]
interface nsIScriptSecurityManager : nsISupports
{
/**
@@ -240,6 +247,22 @@ interface nsIScriptSecurityManager : nsISupports
nsIDomainPolicy activateDomainPolicy();
readonly attribute boolean domainPolicyActive;
/**
* Only the parent process can directly access domain policies, child
* processes only have a read-only mirror to the one in the parent.
* For child processes the mirror is updated via messages
* and ContentChild will hold the DomainPolicy by calling
* ActivateDomainPolicyInternal directly. New consumer to this
* function should not be addded.
*/
[noscript] nsIDomainPolicy activateDomainPolicyInternal();
/**
* This function is for internal use only. Every time a child process is spawned, we
* must clone any active domain policies in the parent to the new child.
*/
[noscript, notxpcom] void cloneDomainPolicy(in DomainPolicyClonePtr aClone);
/**
* Query mechanism for the above policy.
*
+28 -2
View File
@@ -1320,9 +1320,14 @@ static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
nsScriptSecurityManager::~nsScriptSecurityManager(void)
{
Preferences::RemoveObservers(this, kObservedPrefs);
if (mDomainPolicy)
if (mDomainPolicy) {
mDomainPolicy->Deactivate();
MOZ_ASSERT(!mDomainPolicy);
}
// ContentChild might hold a reference to the domain policy,
// and it might release it only after the security manager is
// gone. But we can still assert this for the main process.
MOZ_ASSERT_IF(XRE_GetProcessType() == GoannaProcessType_Default,
!mDomainPolicy);
}
void
@@ -1537,6 +1542,16 @@ nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
NS_IMETHODIMP
nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
{
if (XRE_GetProcessType() != GoannaProcessType_Default) {
return NS_ERROR_SERVICE_NOT_AVAILABLE;
}
return ActivateDomainPolicyInternal(aRv);
}
NS_IMETHODIMP
nsScriptSecurityManager::ActivateDomainPolicyInternal(nsIDomainPolicy** aRv)
{
// We only allow one domain policy at a time. The holder of the previous
// policy must explicitly deactivate it first.
@@ -1558,6 +1573,17 @@ nsScriptSecurityManager::DeactivateDomainPolicy()
mDomainPolicy = nullptr;
}
void
nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone)
{
MOZ_ASSERT(aClone);
if (mDomainPolicy) {
mDomainPolicy->CloneDomainPolicy(aClone);
} else {
aClone->active() = false;
}
}
NS_IMETHODIMP
nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
{
+4 -4
View File
@@ -13154,8 +13154,8 @@ nsGlobalWindow::SuspendTimeouts(uint32_t aIncrease,
}
DisableGamepadUpdates();
// Suspend all of the workers for this window.
mozilla::dom::workers::SuspendWorkersForWindow(this);
// Freeze all of the workers for this window.
mozilla::dom::workers::FreezeWorkersForWindow(this);
TimeStamp now = TimeStamp::Now();
for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
@@ -13243,8 +13243,8 @@ nsGlobalWindow::ResumeTimeouts(bool aThawChildren)
mAudioContexts[i]->Resume();
}
// Resume all of the workers for this window.
mozilla::dom::workers::ResumeWorkersForWindow(this);
// Thaw all of the workers for this window.
mozilla::dom::workers::ThawWorkersForWindow(this);
// Restore all of the timeouts, using the stored time remaining
// (stored in timeout->mTimeRemaining).
+1 -1
View File
@@ -1513,7 +1513,7 @@ DOMInterfaces = {
'headerFile': 'mozilla/dom/WorkerScope.h',
'nativeType': 'mozilla::dom::workers::WorkerDebuggerGlobalScope',
'implicitJSContext': [
'dump', 'global',
'dump', 'global', 'setImmediate', 'reportError',
],
},
+1
View File
@@ -93,6 +93,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::Cache)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
+1
View File
@@ -55,6 +55,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::CacheStorage)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_END
+91 -2
View File
@@ -27,6 +27,7 @@
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#include "mozilla/dom/ContentBridgeChild.h"
#include "mozilla/dom/ContentBridgeParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/ProcessGlobal.h"
@@ -108,8 +109,9 @@
#include "mozilla/dom/PMemoryReportRequestChild.h"
#include "mozilla/dom/PCycleCollectWithLogsChild.h"
#ifdef MOZ_PERMISSIONS
#include "nsIScriptSecurityManager.h"
#ifdef MOZ_PERMISSIONS
#include "nsPermission.h"
#include "nsPermissionManager.h"
#endif
@@ -166,6 +168,7 @@
#include "nsIPrincipal.h"
#include "nsDeviceStorage.h"
#include "AudioChannelService.h"
#include "DomainPolicy.h"
#include "mozilla/dom/DataStoreService.h"
#include "mozilla/dom/telephony/PTelephonyChild.h"
#include "mozilla/dom/time/DateCacheCleaner.h"
@@ -739,9 +742,21 @@ ContentChild::InitXPCOM()
bool isOffline;
ClipboardCapabilities clipboardCaps;
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps);
DomainPolicyClone domainPolicy;
SendGetXPCOMProcessAttributes(&isOffline, &mAvailableDictionaries, &clipboardCaps, &domainPolicy);
RecvSetOffline(isOffline);
if (domainPolicy.active()) {
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
if (!mPolicy) {
MOZ_CRASH("Failed to activate domain policy.");
}
mPolicy->ApplyClone(&domainPolicy);
}
nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
clipboardProxy->SetCapabilities(clipboardCaps);
@@ -2528,9 +2543,83 @@ ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
return true;
}
bool
ContentChild::RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType,
const OptionalURIParams& aDomain)
{
if (aChangeType == ACTIVATE_POLICY) {
if (mPolicy) {
return true;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
MOZ_ASSERT(ssm);
ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
return !!mPolicy;
} else if (!mPolicy) {
MOZ_ASSERT_UNREACHABLE("If the domain policy is not active yet,"
" the first message should be ACTIVATE_POLICY");
return false;
}
NS_ENSURE_TRUE(mPolicy, false);
if (aChangeType == DEACTIVATE_POLICY) {
mPolicy->Deactivate();
mPolicy = nullptr;
return true;
}
nsCOMPtr<nsIDomainSet> set;
switch(aSetType) {
case BLACKLIST:
mPolicy->GetBlacklist(getter_AddRefs(set));
break;
case SUPER_BLACKLIST:
mPolicy->GetSuperBlacklist(getter_AddRefs(set));
break;
case WHITELIST:
mPolicy->GetWhitelist(getter_AddRefs(set));
break;
case SUPER_WHITELIST:
mPolicy->GetSuperWhitelist(getter_AddRefs(set));
break;
default:
NS_NOTREACHED("Unexpected setType");
return false;
}
MOZ_ASSERT(set);
nsCOMPtr<nsIURI> uri = DeserializeURI(aDomain);
switch(aChangeType) {
case ADD_DOMAIN:
NS_ENSURE_TRUE(uri, false);
set->Add(uri);
break;
case REMOVE_DOMAIN:
NS_ENSURE_TRUE(uri, false);
set->Remove(uri);
break;
case CLEAR_DOMAINS:
set->Clear();
break;
default:
NS_NOTREACHED("Unexpected changeType");
return false;
}
return true;
}
bool
ContentChild::RecvShutdown()
{
if (mPolicy) {
mPolicy->Deactivate();
mPolicy = nullptr;
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(this, "content-child-shutdown", nullptr);
+5
View File
@@ -24,6 +24,7 @@ class nsIDOMBlob;
class nsIObserver;
struct ResourceMapping;
struct OverrideMapping;
class nsIDomainPolicy;
namespace mozilla {
class RemoteSpellcheckEngineChild;
@@ -378,6 +379,8 @@ public:
nsTArray<nsCString>&& aThreadNameFilters) override;
virtual bool RecvStopProfiler() override;
virtual bool RecvGetProfile(nsCString* aProfile) override;
virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType,
const OptionalURIParams& aDomain) override;
virtual bool RecvShutdown() override;
#ifdef ANDROID
@@ -475,6 +478,8 @@ private:
static ContentChild* sSingleton;
nsCOMPtr<nsIDomainPolicy> mPolicy;
DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
};
+9 -2
View File
@@ -2710,8 +2710,9 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
bool isOffline;
InfallibleTArray<nsString> unusedDictionaries;
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
RecvGetXPCOMProcessAttributes(&isOffline, &unusedDictionaries,
&clipboardCaps);
&clipboardCaps, &domainPolicy);
mozilla::unused << content->SendSetOffline(isOffline);
MOZ_ASSERT(!clipboardCaps.supportsSelectionClipboard() &&
!clipboardCaps.supportsFindClipboard(),
@@ -3022,7 +3023,8 @@ ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId,
bool
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps)
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy)
{
nsCOMPtr<nsIIOService> io(do_GetIOService());
MOZ_ASSERT(io, "No IO service?");
@@ -3043,6 +3045,11 @@ ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Let's copy the domain policy from the parent to the child (if it's active).
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
NS_ENSURE_TRUE(ssm, false);
ssm->CloneDomainPolicy(domainPolicy);
return true;
}
+2 -1
View File
@@ -497,7 +497,8 @@ private:
bool* aIsForBrowser) override;
virtual bool RecvGetXPCOMProcessAttributes(bool* aIsOffline,
InfallibleTArray<nsString>* dictionaries,
ClipboardCapabilities* clipboardCaps)
ClipboardCapabilities* clipboardCaps,
DomainPolicyClone* domainPolicy)
override;
virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
+13 -1
View File
@@ -345,6 +345,15 @@ union OptionalContentId
void_t;
};
struct DomainPolicyClone
{
bool active;
URIParams[] blacklist;
URIParams[] whitelist;
URIParams[] superBlacklist;
URIParams[] superWhitelist;
};
prio(normal upto urgent) sync protocol PContent
{
parent spawns PPluginModule;
@@ -550,6 +559,8 @@ child:
NuwaFreeze();
async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams aDomain);
/**
* Notify the child to shutdown. The child will in turn call FinishShutdown
* and let the parent close the channel.
@@ -584,7 +595,8 @@ parent:
returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
sync GetXPCOMProcessAttributes()
returns (bool isOffline, nsString[] dictionaries,
ClipboardCapabilities clipboardCaps);
ClipboardCapabilities clipboardCaps,
DomainPolicyClone domainPolicy);
sync CreateChildProcess(IPCTabContext context,
ProcessPriority priority,
+1
View File
@@ -147,6 +147,7 @@ for var in ('MOZ_PERMISSIONS', 'MOZ_CHILD_PERMISSIONS'):
JAR_MANIFESTS += ['jar.mn']
BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
@@ -6,6 +6,24 @@
[Global=(WorkerDebugger), Exposed=WorkerDebugger]
interface WorkerDebuggerGlobalScope : EventTarget {
readonly attribute object global;
object createSandbox(DOMString name, object prototype);
[Throws]
void loadSubScript(DOMString url, optional object sandbox);
void enterEventLoop();
void leaveEventLoop();
void postMessage(DOMString message);
attribute EventHandler onmessage;
[Throws]
void setImmediate(Function handler);
void reportError(DOMString message);
};
// So you can debug while you debug
+1 -1
View File
@@ -280,7 +280,7 @@ MessagePort::PreHandleEvent(EventChainPreVisitor& aVisitor)
if (IsClosed()) {
preventDispatch = true;
} else if (NS_IsMainThread() && mSharedWorker->IsSuspended()) {
} else if (NS_IsMainThread() && mSharedWorker->IsFrozen()) {
mSharedWorker->QueueEvent(event);
preventDispatch = true;
} else if (!mStarted) {
+17 -10
View File
@@ -60,6 +60,7 @@
#include "Principal.h"
#include "SharedWorker.h"
#include "WorkerDebuggerManager.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerThread.h"
@@ -870,14 +871,14 @@ JSObject*
Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj)
{
JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx);
if (!IsDebuggerGlobal(targetGlobal)) {
if (!IsDebuggerGlobal(targetGlobal) && !IsDebuggerSandbox(targetGlobal)) {
MOZ_CRASH("There should be no edges from the debuggee to the debugger.");
}
JSObject* originGlobal = js::GetGlobalForObjectCrossCompartment(obj);
const js::Wrapper* wrapper = nullptr;
if (IsDebuggerGlobal(originGlobal)) {
if (IsDebuggerGlobal(originGlobal) || IsDebuggerSandbox(originGlobal)) {
wrapper = &js::CrossCompartmentWrapper::singleton;
} else {
if (obj != originGlobal) {
@@ -1243,22 +1244,22 @@ CancelWorkersForWindow(nsPIDOMWindow* aWindow)
}
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
FreezeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->SuspendWorkersForWindow(aWindow);
runtime->FreezeWorkersForWindow(aWindow);
}
}
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
ThawWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
RuntimeService* runtime = RuntimeService::GetService();
if (runtime) {
runtime->ResumeWorkersForWindow(aWindow);
runtime->ThawWorkersForWindow(aWindow);
}
}
@@ -1918,6 +1919,12 @@ RuntimeService::Shutdown()
// That's it, no more workers.
mShuttingDown = true;
// Remove all listeners from the worker debugger manager to ensure that it
// gets properly destroyed.
if (NS_FAILED(ClearWorkerDebuggerManagerListeners())) {
NS_WARNING("Failed to clear worker debugger manager listeners!");
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_WARN_IF_FALSE(obs, "Failed to get observer service?!");
@@ -2211,7 +2218,7 @@ RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow)
}
void
RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
RuntimeService::FreezeWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
@@ -2227,7 +2234,7 @@ RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
JSContext* cx = jsapi.cx();
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->Suspend(cx, aWindow)) {
if (!workers[index]->Freeze(cx, aWindow)) {
JS_ReportPendingException(cx);
}
}
@@ -2235,7 +2242,7 @@ RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
}
void
RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
RuntimeService::ThawWorkersForWindow(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWindow);
@@ -2251,7 +2258,7 @@ RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
JSContext* cx = jsapi.cx();
for (uint32_t index = 0; index < workers.Length(); index++) {
if (!workers[index]->SynchronizeAndResume(cx, aWindow)) {
if (!workers[index]->Thaw(cx, aWindow)) {
JS_ReportPendingException(cx);
}
}
+2 -2
View File
@@ -129,10 +129,10 @@ public:
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
FreezeWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
ThawWorkersForWindow(nsPIDOMWindow* aWindow);
nsresult
CreateSharedWorker(const GlobalObject& aGlobal,
+65 -25
View File
@@ -53,7 +53,8 @@ ChannelFromScriptURL(nsIPrincipal* principal,
nsIIOService* ios,
nsIScriptSecurityManager* secMan,
const nsAString& aScriptURL,
bool aIsWorkerScript,
bool aIsMainScript,
WorkerScriptType aWorkerScriptType,
nsIChannel** aChannel)
{
AssertIsOnMainThread();
@@ -84,10 +85,22 @@ ChannelFromScriptURL(nsIPrincipal* principal,
}
}
// If this script loader is being used to make a new worker then we need
// to do a same-origin check. Otherwise we need to clear the load with the
// security manager.
if (aIsWorkerScript) {
if (aWorkerScriptType == DebuggerScript) {
bool isChrome = false;
NS_ENSURE_SUCCESS(uri->SchemeIs("chrome", &isChrome),
NS_ERROR_DOM_SECURITY_ERR);
bool isResource = false;
NS_ENSURE_SUCCESS(uri->SchemeIs("resource", &isResource),
NS_ERROR_DOM_SECURITY_ERR);
if (!isChrome && !isResource) {
return NS_ERROR_DOM_SECURITY_ERR;
}
} else if (aIsMainScript) {
// If this script loader is being used to make a new worker then we need
// to do a same-origin check. Otherwise we need to clear the load with the
// security manager.
nsCString scheme;
rv = uri->GetScheme(scheme);
NS_ENSURE_SUCCESS(rv, rv);
@@ -190,6 +203,9 @@ private:
~ScriptExecutorRunnable()
{ }
virtual bool
IsDebuggerRunnable() const override;
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
@@ -214,7 +230,8 @@ class ScriptLoaderRunnable final : public WorkerFeature,
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
nsTArray<ScriptLoadInfo> mLoadInfos;
bool mIsWorkerScript;
bool mIsMainScript;
WorkerScriptType mWorkerScriptType;
bool mCanceled;
bool mCanceledMainThread;
@@ -224,14 +241,15 @@ public:
ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
nsIEventTarget* aSyncLoopTarget,
nsTArray<ScriptLoadInfo>& aLoadInfos,
bool aIsWorkerScript)
bool aIsMainScript,
WorkerScriptType aWorkerScriptType)
: mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
mIsWorkerScript(aIsWorkerScript), mCanceled(false),
mCanceledMainThread(false)
mIsMainScript(aIsMainScript), mWorkerScriptType(aWorkerScriptType),
mCanceled(false), mCanceledMainThread(false)
{
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aSyncLoopTarget);
MOZ_ASSERT_IF(aIsWorkerScript, aLoadInfos.Length() == 1);
MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1);
mLoadInfos.SwapElements(aLoadInfos);
}
@@ -300,6 +318,12 @@ private:
return true;
}
bool
IsMainWorkerScript() const
{
return mIsMainScript && mWorkerScriptType == WorkerScript;
}
void
CancelMainThread()
{
@@ -338,7 +362,7 @@ private:
nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
if (!principal) {
NS_ASSERTION(parentWorker, "Must have a principal!");
NS_ASSERTION(mIsWorkerScript, "Must have a principal for importScripts!");
NS_ASSERTION(mIsMainScript, "Must have a principal for importScripts!");
principal = parentWorker->GetPrincipal();
loadGroup = parentWorker->GetLoadGroup();
@@ -348,7 +372,7 @@ private:
// Figure out our base URI.
nsCOMPtr<nsIURI> baseURI;
if (mIsWorkerScript) {
if (mIsMainScript) {
if (parentWorker) {
baseURI = parentWorker->GetBaseURI();
NS_ASSERTION(baseURI, "Should have been set already!");
@@ -367,7 +391,7 @@ private:
nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
nsCOMPtr<nsIChannel> channel;
if (mIsWorkerScript) {
if (IsMainWorkerScript()) {
// May be null.
channel = mWorkerPrivate->ForgetWorkerChannel();
}
@@ -383,8 +407,8 @@ private:
if (!channel) {
rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios,
secMan, loadInfo.mURL, mIsWorkerScript,
getter_AddRefs(channel));
secMan, loadInfo.mURL, mIsMainScript,
mWorkerScriptType, getter_AddRefs(channel));
if (NS_FAILED(rv)) {
return rv;
}
@@ -489,7 +513,7 @@ private:
// Update the principal of the worker and its base URI if we just loaded the
// worker's primary script.
if (mIsWorkerScript) {
if (IsMainWorkerScript()) {
// Take care of the base URI first.
mWorkerPrivate->SetBaseURI(finalURI);
@@ -572,7 +596,7 @@ private:
{
AssertIsOnMainThread();
if (mIsWorkerScript) {
if (IsMainWorkerScript()) {
mWorkerPrivate->WorkerScriptLoaded();
}
@@ -699,6 +723,15 @@ ScriptExecutorRunnable::ScriptExecutorRunnable(
MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
}
bool
ScriptExecutorRunnable::IsDebuggerRunnable() const
{
// ScriptExecutorRunnable is used to execute both worker and debugger scripts.
// In the latter case, the runnable needs to be dispatched to the debugger
// queue.
return mScriptLoader.mWorkerScriptType == DebuggerScript;
}
bool
ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
@@ -757,6 +790,10 @@ ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
options.setFileAndLine(filename.get(), 1)
.setNoScriptRval(true);
if (mScriptLoader.mWorkerScriptType == DebuggerScript) {
options.setVersion(JSVERSION_LATEST);
}
JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
loadInfo.mScriptTextLength,
JS::SourceBufferHolder::GiveOwnership);
@@ -814,7 +851,8 @@ ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
bool
LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsWorkerScript)
nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsMainScript,
WorkerScriptType aWorkerScriptType)
{
aWorkerPrivate->AssertIsOnWorkerThread();
NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
@@ -823,7 +861,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
nsRefPtr<ScriptLoaderRunnable> loader =
new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.EventTarget(),
aLoadInfos, aIsWorkerScript);
aLoadInfos, aIsMainScript, aWorkerScriptType);
NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
@@ -863,7 +901,7 @@ ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
NS_ASSERTION(secMan, "This should never be null!");
return ChannelFromScriptURL(aPrincipal, aBaseURI, aParentDoc, aLoadGroup,
ios, secMan, aScriptURL, true, aChannel);
ios, secMan, aScriptURL, true, WorkerScript, aChannel);
}
nsresult
@@ -922,7 +960,8 @@ void ReportLoadError(JSContext* aCx, const nsAString& aURL,
}
bool
LoadWorkerScript(JSContext* aCx)
LoadMainScript(JSContext* aCx, const nsAString& aScriptURL,
WorkerScriptType aWorkerScriptType)
{
WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
NS_ASSERTION(worker, "This should never be null!");
@@ -930,14 +969,15 @@ LoadWorkerScript(JSContext* aCx)
nsTArray<ScriptLoadInfo> loadInfos;
ScriptLoadInfo* info = loadInfos.AppendElement();
info->mURL = worker->ScriptURL();
info->mURL = aScriptURL;
return LoadAllScripts(aCx, worker, loadInfos, true);
return LoadAllScripts(aCx, worker, loadInfos, true, aWorkerScriptType);
}
void
Load(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
const Sequence<nsString>& aScriptURLs, ErrorResult& aRv)
const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
ErrorResult& aRv)
{
const uint32_t urlCount = aScriptURLs.Length();
@@ -957,7 +997,7 @@ Load(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
loadInfos[index].mURL = aScriptURLs[index];
}
if (!LoadAllScripts(aCx, aWorkerPrivate, loadInfos, false)) {
if (!LoadAllScripts(aCx, aWorkerPrivate, loadInfos, false, aWorkerScriptType)) {
// LoadAllScripts can fail if we're shutting down.
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
}
+9 -2
View File
@@ -29,6 +29,11 @@ class Sequence;
BEGIN_WORKERS_NAMESPACE
enum WorkerScriptType {
WorkerScript,
DebuggerScript
};
namespace scriptloader {
nsresult
@@ -48,11 +53,13 @@ ChannelFromScriptURLWorkerThread(JSContext* aCx,
void ReportLoadError(JSContext* aCx, const nsAString& aURL,
nsresult aLoadResult, bool aIsMainThread);
bool LoadWorkerScript(JSContext* aCx);
bool LoadMainScript(JSContext* aCx, const nsAString& aScriptURL,
WorkerScriptType aWorkerScriptType);
void Load(JSContext* aCx,
WorkerPrivate* aWorkerPrivate,
const mozilla::dom::Sequence<nsString>& aScriptURLs,
const nsTArray<nsString>& aScriptURLs,
WorkerScriptType aWorkerScriptType,
mozilla::ErrorResult& aRv);
} // namespace scriptloader
+14 -14
View File
@@ -27,7 +27,7 @@ USING_WORKERS_NAMESPACE
SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
WorkerPrivate* aWorkerPrivate)
: DOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
mSuspended(false)
mFrozen(false)
{
AssertIsOnMainThread();
MOZ_ASSERT(aWorkerPrivate);
@@ -85,25 +85,25 @@ SharedWorker::Port()
}
void
SharedWorker::Suspend()
SharedWorker::Freeze()
{
AssertIsOnMainThread();
MOZ_ASSERT(!IsSuspended());
MOZ_ASSERT(!IsFrozen());
mSuspended = true;
mFrozen = true;
}
void
SharedWorker::Resume()
SharedWorker::Thaw()
{
AssertIsOnMainThread();
MOZ_ASSERT(IsSuspended());
MOZ_ASSERT(IsFrozen());
mSuspended = false;
mFrozen = false;
if (!mSuspendedEvents.IsEmpty()) {
if (!mFrozenEvents.IsEmpty()) {
nsTArray<nsCOMPtr<nsIDOMEvent>> events;
mSuspendedEvents.SwapElements(events);
mFrozenEvents.SwapElements(events);
for (uint32_t index = 0; index < events.Length(); index++) {
nsCOMPtr<nsIDOMEvent>& event = events[index];
@@ -127,9 +127,9 @@ SharedWorker::QueueEvent(nsIDOMEvent* aEvent)
{
AssertIsOnMainThread();
MOZ_ASSERT(aEvent);
MOZ_ASSERT(IsSuspended());
MOZ_ASSERT(IsFrozen());
mSuspendedEvents.AppendElement(aEvent);
mFrozenEvents.AppendElement(aEvent);
}
void
@@ -181,14 +181,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
DOMEventTargetHelper)
tmp->Close();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedEvents)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
JSObject*
@@ -206,7 +206,7 @@ SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor)
nsIDOMEvent*& event = aVisitor.mDOMEvent;
if (IsSuspended() && event) {
if (IsFrozen() && event) {
QueueEvent(event);
aVisitor.mCanHandle = false;
+6 -6
View File
@@ -35,9 +35,9 @@ class SharedWorker final : public DOMEventTargetHelper
nsRefPtr<WorkerPrivate> mWorkerPrivate;
nsRefPtr<MessagePort> mMessagePort;
nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
nsTArray<nsCOMPtr<nsIDOMEvent>> mFrozenEvents;
uint64_t mSerial;
bool mSuspended;
bool mFrozen;
public:
static already_AddRefed<SharedWorker>
@@ -55,16 +55,16 @@ public:
}
bool
IsSuspended() const
IsFrozen() const
{
return mSuspended;
return mFrozen;
}
void
Suspend();
Freeze();
void
Resume();
Thaw();
void
QueueEvent(nsIDOMEvent* aEvent);
+10
View File
@@ -141,6 +141,16 @@ WorkerDebuggerManager::RemoveListener(
return NS_OK;
}
void
WorkerDebuggerManager::ClearListeners()
{
AssertIsOnMainThread();
MutexAutoLock lock(mMutex);
mListeners.Clear();
}
void
WorkerDebuggerManager::RegisterDebugger(WorkerDebugger* aDebugger)
{
+17 -2
View File
@@ -41,9 +41,9 @@ public:
static WorkerDebuggerManager*
GetOrCreateService()
{
nsCOMPtr<nsIWorkerDebuggerManager> wdm =
nsCOMPtr<nsIWorkerDebuggerManager> manager =
do_GetService(WORKERDEBUGGERMANAGER_CONTRACTID);
return static_cast<WorkerDebuggerManager*>(wdm.get());
return static_cast<WorkerDebuggerManager*>(manager.get());
}
WorkerDebuggerManager();
@@ -51,6 +51,8 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWORKERDEBUGGERMANAGER
void ClearListeners();
void RegisterDebugger(WorkerDebugger* aDebugger);
void UnregisterDebugger(WorkerDebugger* aDebugger);
@@ -64,6 +66,19 @@ private:
void UnregisterDebuggerOnMainThread(WorkerDebugger* aDebugger);
};
inline nsresult
ClearWorkerDebuggerManagerListeners()
{
nsRefPtr<WorkerDebuggerManager> manager =
WorkerDebuggerManager::GetOrCreateService();
if (!manager) {
return NS_ERROR_FAILURE;
}
manager->ClearListeners();
return NS_OK;
}
inline nsresult
RegisterWorkerDebugger(WorkerDebugger* aDebugger)
{
File diff suppressed because it is too large Load Diff
+76 -23
View File
@@ -56,6 +56,8 @@ class PrincipalInfo;
struct PRThread;
class ReportDebuggerErrorRunnable;
BEGIN_WORKERS_NAMESPACE
class AutoSyncLoopHolder;
@@ -122,8 +124,6 @@ public:
template <class Derived>
class WorkerPrivateParent : public DOMEventTargetHelper
{
class SynchronizeAndResumeRunnable;
protected:
class EventTarget;
friend class EventTarget;
@@ -166,7 +166,6 @@ private:
// Only used for top level workers.
nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
nsRevocableEventPtr<SynchronizeAndResumeRunnable> mSynchronizeRunnable;
// Only for ChromeWorkers without window and only touched on the main thread.
nsTArray<nsCString> mHostObjectURIs;
@@ -181,7 +180,7 @@ private:
uint64_t mBusyCount;
uint64_t mMessagePortSerial;
Status mParentStatus;
bool mParentSuspended;
bool mParentFrozen;
bool mIsChromeWorker;
bool mMainThreadObjectsForgotten;
WorkerType mWorkerType;
@@ -261,6 +260,9 @@ public:
nsresult
DispatchControlRunnable(WorkerControlRunnable* aWorkerControlRunnable);
nsresult
DispatchDebuggerRunnable(WorkerRunnable* aDebuggerRunnable);
already_AddRefed<WorkerRunnable>
MaybeWrapAsWorkerRunnable(nsIRunnable* aRunnable);
@@ -290,17 +292,13 @@ public:
return Notify(aCx, Killing);
}
// We can assume that an nsPIDOMWindow will be available for Suspend, Resume
// and SynchronizeAndResume as these are only used for globals going in and
// out of the bfcache.
// We can assume that an nsPIDOMWindow will be available for Freeze, Thaw
// as these are only used for globals going in and out of the bfcache.
bool
Suspend(JSContext* aCx, nsPIDOMWindow* aWindow);
Freeze(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
Resume(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow);
Thaw(JSContext* aCx, nsPIDOMWindow* aWindow);
bool
Terminate(JSContext* aCx)
@@ -409,10 +407,10 @@ public:
}
bool
IsSuspended() const
IsFrozen() const
{
AssertIsOnParentThread();
return mParentSuspended;
return mParentFrozen;
}
bool
@@ -718,6 +716,8 @@ public:
};
class WorkerDebugger : public nsIWorkerDebugger {
friend class ::ReportDebuggerErrorRunnable;
mozilla::Mutex mMutex;
mozilla::CondVar mCondVar;
@@ -726,6 +726,8 @@ class WorkerDebugger : public nsIWorkerDebugger {
bool mIsEnabled;
// Only touched on the main thread.
bool mIsInitialized;
bool mIsFrozen;
nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
public:
@@ -734,18 +736,51 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIWORKERDEBUGGER
void AssertIsOnParentThread();
void
AssertIsOnParentThread();
void WaitIsEnabled(bool aIsEnabled);
void
WaitIsEnabled(bool aIsEnabled);
void Enable();
void
Enable();
void Disable();
void
Disable();
void
Freeze();
void
Thaw();
void
PostMessageToDebugger(const nsAString& aMessage);
void
ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
const nsAString& aMessage);
private:
virtual ~WorkerDebugger();
virtual
~WorkerDebugger();
void NotifyIsEnabled(bool aIsEnabled);
void
NotifyIsEnabled(bool aIsEnabled);
void
FreezeOnMainThread();
void
ThawOnMainThread();
void
PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
void
ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
uint32_t aLineno,
const nsAString& aMessage);
};
class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
@@ -771,6 +806,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsRefPtr<WorkerDebugger> mDebugger;
Queue<WorkerControlRunnable*, 4> mControlQueue;
Queue<WorkerRunnable*, 4> mDebuggerQueue;
// Touched on multiple threads, protected with mMutex.
JSContext* mJSContext;
@@ -785,6 +821,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
nsTArray<ParentType*> mChildWorkers;
nsTArray<WorkerFeature*> mFeatures;
nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
uint32_t mDebuggerEventLoopLevel;
struct SyncLoopInfo
{
@@ -820,7 +857,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
uint32_t mErrorHandlerRecursionCount;
uint32_t mNextTimeoutId;
Status mStatus;
bool mSuspended;
bool mFrozen;
bool mTimerRunning;
bool mRunningExpiredTimeouts;
bool mCloseHandlerStarted;
@@ -895,10 +932,10 @@ public:
}
bool
SuspendInternal(JSContext* aCx);
FreezeInternal(JSContext* aCx);
bool
ResumeInternal(JSContext* aCx);
ThawInternal(JSContext* aCx);
void
TraceTimeouts(const TraceCallbacks& aCallbacks, void* aClosure) const;
@@ -945,6 +982,22 @@ public:
const Optional<Sequence<JS::Value>>& aTransferable,
ErrorResult& aRv);
void
EnterDebuggerEventLoop();
void
LeaveDebuggerEventLoop();
void
PostMessageToDebugger(const nsAString& aMessage);
void
SetDebuggerImmediate(JSContext* aCx, Function& aHandler, ErrorResult& aRv);
void
ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
const nsAString& aMessage);
bool
NotifyInternal(JSContext* aCx, Status aStatus);
+38 -6
View File
@@ -50,6 +50,22 @@ WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
}
#endif
bool
WorkerRunnable::IsDebuggerRunnable() const
{
return false;
}
nsIGlobalObject*
WorkerRunnable::DefaultGlobalObject() const
{
if (IsDebuggerRunnable()) {
return mWorkerPrivate->DebuggerGlobalScope();
} else {
return mWorkerPrivate->GlobalScope();
}
}
bool
WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
@@ -121,7 +137,11 @@ WorkerRunnable::DispatchInternal()
{
if (mBehavior == WorkerThreadModifyBusyCount ||
mBehavior == WorkerThreadUnchangedBusyCount) {
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
if (IsDebuggerRunnable()) {
return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(this));
} else {
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this));
}
}
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
@@ -285,9 +305,13 @@ WorkerRunnable::Run()
MOZ_ASSERT(isMainThread == NS_IsMainThread());
nsRefPtr<WorkerPrivate> kungFuDeathGrip;
if (targetIsWorkerThread) {
globalObject = mWorkerPrivate->GlobalScope();
}
else {
JSObject* global = JS::CurrentGlobalOrNull(GetCurrentThreadJSContext());
if (global) {
globalObject = GetGlobalObjectForGlobal(global);
} else {
globalObject = DefaultGlobalObject();
}
} else {
kungFuDeathGrip = mWorkerPrivate;
if (isMainThread) {
globalObject = static_cast<nsGlobalWindow*>(mWorkerPrivate->GetWindow());
@@ -327,8 +351,8 @@ WorkerRunnable::Run()
// In the case of CompileScriptRunnnable, WorkerRun above can cause us to
// lazily create a global, so we construct aes here before calling PostRun.
if (targetIsWorkerThread && !aes && mWorkerPrivate->GlobalScope()) {
aes.emplace(mWorkerPrivate->GlobalScope(), false, GetCurrentThreadJSContext());
if (targetIsWorkerThread && !aes && DefaultGlobalObject()) {
aes.emplace(DefaultGlobalObject(), false, GetCurrentThreadJSContext());
cx = aes->cx();
}
@@ -349,6 +373,14 @@ WorkerRunnable::Cancel()
return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
}
void
WorkerDebuggerRunnable::PostDispatch(JSContext* aCx,
WorkerPrivate* aWorkerPrivate,
bool aDispatchResult)
{
MaybeReportMainThreadException(aCx, aDispatchResult);
}
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
nsIEventTarget* aSyncLoopTarget)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
+40
View File
@@ -99,6 +99,14 @@ protected:
virtual ~WorkerRunnable()
{ }
// Returns true if this runnable should be dispatched to the debugger queue,
// and false otherwise.
virtual bool
IsDebuggerRunnable() const;
nsIGlobalObject*
DefaultGlobalObject() const;
// By default asserts that Dispatch() is being called on the right thread
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
// Also increments the busy count of |mWorkerPrivate| if targeting the
@@ -133,6 +141,38 @@ protected:
NS_DECL_NSIRUNNABLE
};
// This runnable is used to send a message to a worker debugger.
class WorkerDebuggerRunnable : public WorkerRunnable
{
protected:
explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
{
}
virtual ~WorkerDebuggerRunnable()
{ }
private:
virtual bool
IsDebuggerRunnable() const override
{
return true;
}
virtual bool
PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
{
AssertIsOnMainThread();
return true;
}
virtual void
PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
bool aDispatchResult) override;
};
// This runnable is used to send a message directly to a worker's sync loop.
class WorkerSyncRunnable : public WorkerRunnable
{
+230 -2
View File
@@ -208,7 +208,7 @@ WorkerGlobalScope::ImportScripts(JSContext* aCx,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
scriptloader::Load(aCx, mWorkerPrivate, aScriptURLs, aRv);
scriptloader::Load(aCx, mWorkerPrivate, aScriptURLs, WorkerScript, aRv);
}
int32_t
@@ -513,6 +513,220 @@ WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper());
}
class WorkerDebuggerSandboxPrivate : public nsIGlobalObject,
public nsWrapperCache
{
public:
explicit WorkerDebuggerSandboxPrivate(JSObject *global)
{
SetWrapper(global);
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WorkerDebuggerSandboxPrivate,
nsIGlobalObject)
virtual JSObject *GetGlobalJSObject() override
{
return GetWrapper();
}
virtual JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) override
{
MOZ_CRASH("WorkerDebuggerSandboxPrivate doesn't use DOM bindings!");
}
private:
virtual ~WorkerDebuggerSandboxPrivate()
{
ClearWrapper();
}
};
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerDebuggerSandboxPrivate)
NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerDebuggerSandboxPrivate)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerDebuggerSandboxPrivate)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerSandboxPrivate)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
NS_INTERFACE_MAP_END
static bool
workerdebuggersandbox_enumerate(JSContext *cx, JS::Handle<JSObject *> obj)
{
return JS_EnumerateStandardClasses(cx, obj);
}
static bool
workerdebuggersandbox_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
JS::Handle<jsid> id, bool *resolvedp)
{
return JS_ResolveStandardClass(cx, obj, id, resolvedp);
}
static bool
workerdebuggersandbox_convert(JSContext *cx, JS::Handle<JSObject *> obj,
JSType type, JS::MutableHandle<JS::Value> vp)
{
if (type == JSTYPE_OBJECT) {
vp.set(OBJECT_TO_JSVAL(obj));
return true;
}
return JS::OrdinaryToPrimitive(cx, obj, type, vp);
}
static void
workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj)
{
nsIGlobalObject *globalObject =
static_cast<nsIGlobalObject *>(JS_GetPrivate(obj));
NS_RELEASE(globalObject);
}
static void
workerdebuggersandbox_moved(JSObject *obj, const JSObject *old)
{
}
const js::Class workerdebuggersandbox_class = {
"workerdebuggersandbox",
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
nullptr,
nullptr,
nullptr,
nullptr,
workerdebuggersandbox_enumerate,
workerdebuggersandbox_resolve,
workerdebuggersandbox_convert,
workerdebuggersandbox_finalize,
nullptr,
nullptr,
nullptr,
JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC, {
nullptr,
nullptr,
false,
nullptr,
workerdebuggersandbox_moved
}, JS_NULL_OBJECT_OPS
};
void
WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
JS::Handle<JSObject*> aPrototype,
JS::MutableHandle<JSObject*> aResult)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JS::CompartmentOptions options;
options.setInvisibleToDebugger(true);
JS::Rooted<JSObject*> sandbox(aCx,
JS_NewGlobalObject(aCx, js::Jsvalify(&workerdebuggersandbox_class), nullptr,
JS::DontFireOnNewGlobalHook, options));
if (!sandbox) {
JS_ReportError(aCx, "Can't create sandbox!");
aResult.set(nullptr);
return;
}
{
JSAutoCompartment ac(aCx, sandbox);
JS::Rooted<JSObject*> prototype(aCx, aPrototype);
if (!JS_WrapObject(aCx, &prototype)) {
JS_ReportError(aCx, "Can't wrap sandbox prototype!");
aResult.set(nullptr);
return;
}
if (!JS_SetPrototype(aCx, sandbox, prototype)) {
JS_ReportError(aCx, "Can't set sandbox prototype!");
aResult.set(nullptr);
return;
}
nsCOMPtr<nsIGlobalObject> globalObject =
new WorkerDebuggerSandboxPrivate(sandbox);
// Pass on ownership of globalObject to |sandbox|.
JS_SetPrivate(sandbox, globalObject.forget().take());
}
JS_FireOnNewGlobalObject(aCx, sandbox);
if (!JS_WrapObject(aCx, &sandbox)) {
JS_ReportError(aCx, "Can't wrap sandbox!");
aResult.set(nullptr);
return;
}
aResult.set(sandbox);
}
void
WorkerDebuggerGlobalScope::LoadSubScript(JSContext* aCx,
const nsAString& aURL,
const Optional<JS::Handle<JSObject*>>& aSandbox,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
Maybe<JSAutoCompartment> ac;
if (aSandbox.WasPassed()) {
JS::Rooted<JSObject*> sandbox(aCx, js::CheckedUnwrap(aSandbox.Value()));
if (!IsDebuggerSandbox(sandbox)) {
aRv.Throw(NS_ERROR_INVALID_ARG);
return;
}
ac.emplace(aCx, sandbox);
}
nsTArray<nsString> urls;
urls.AppendElement(aURL);
scriptloader::Load(aCx, mWorkerPrivate, urls, DebuggerScript, aRv);
}
void
WorkerDebuggerGlobalScope::EnterEventLoop()
{
mWorkerPrivate->EnterDebuggerEventLoop();
}
void
WorkerDebuggerGlobalScope::LeaveEventLoop()
{
mWorkerPrivate->LeaveDebuggerEventLoop();
}
void
WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage)
{
mWorkerPrivate->PostMessageToDebugger(aMessage);
}
void
WorkerDebuggerGlobalScope::SetImmediate(JSContext* aCx, Function& aHandler,
ErrorResult& aRv)
{
mWorkerPrivate->SetDebuggerImmediate(aCx, aHandler, aRv);
}
void
WorkerDebuggerGlobalScope::ReportError(JSContext* aCx,
const nsAString& aMessage)
{
JS::AutoFilename afn;
uint32_t lineno = 0;
JS::DescribeScriptedCaller(aCx, &afn, &lineno);
nsString filename(NS_ConvertUTF8toUTF16(afn.get()));
mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage);
}
void
WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
const Optional<nsAString>& aString) const
@@ -525,10 +739,18 @@ GetGlobalObjectForGlobal(JSObject* global)
{
nsIGlobalObject* globalObject = nullptr;
UNWRAP_WORKER_OBJECT(WorkerGlobalScope, global, globalObject);
if (!globalObject) {
UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalObject);
MOZ_ASSERT(globalObject);
if (!globalObject) {
MOZ_ASSERT(IsDebuggerSandbox(global));
globalObject = static_cast<nsIGlobalObject *>(JS_GetPrivate(global));
MOZ_ASSERT(globalObject);
}
}
return globalObject;
}
@@ -548,6 +770,12 @@ IsDebuggerGlobal(JSObject* object)
globalObject)) && !!globalObject;
}
bool
IsDebuggerSandbox(JSObject* object)
{
return js::GetObjectClass(object) == &workerdebuggersandbox_class;
}
bool
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{
+27
View File
@@ -264,6 +264,33 @@ public:
void
GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal);
void
CreateSandbox(JSContext* aCx, const nsAString& aName,
JS::Handle<JSObject*> aPrototype,
JS::MutableHandle<JSObject*> aResult);
void
LoadSubScript(JSContext* aCx, const nsAString& aURL,
const Optional<JS::Handle<JSObject*>>& aSandbox,
ErrorResult& aRv);
void
EnterEventLoop();
void
LeaveEventLoop();
void
PostMessage(const nsAString& aMessage);
IMPL_EVENT_HANDLER(message)
void
SetImmediate(JSContext* aCx, Function& aHandler, ErrorResult& aRv);
void
ReportError(JSContext* aCx, const nsAString& aMessage);
void
Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
+5 -2
View File
@@ -262,10 +262,10 @@ void
CancelWorkersForWindow(nsPIDOMWindow* aWindow);
void
SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
FreezeWorkersForWindow(nsPIDOMWindow* aWindow);
void
ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
ThawWorkersForWindow(nsPIDOMWindow* aWindow);
class WorkerTask
{
@@ -338,6 +338,9 @@ IsWorkerGlobal(JSObject* global);
bool
IsDebuggerGlobal(JSObject* global);
bool
IsDebuggerSandbox(JSObject* object);
// Throws the JSMSG_GETTER_ONLY exception. This shouldn't be used going
// forward -- getter-only properties should just use JS_PSG for the setter
// (implying no setter at all), which will not throw when set in non-strict
+19 -2
View File
@@ -2,13 +2,22 @@
interface nsIDOMWindow;
[scriptable, uuid(54fd2dd3-c01b-4f71-888f-462f37a54f57)]
[scriptable, uuid(530db841-1b2c-485a-beeb-f2b1acb9714e)]
interface nsIWorkerDebuggerListener : nsISupports
{
void onClose();
void onError(in DOMString filename, in unsigned long lineno,
in DOMString message);
void onFreeze();
void onMessage(in DOMString message);
void onThaw();
};
[scriptable, builtinclass, uuid(0833b363-bffe-4cdb-ad50-1c4563e0C8ff)]
[scriptable, builtinclass, uuid(d7c73e54-3c41-4393-9d13-fa2ed4Ba6764)]
interface nsIWorkerDebugger : nsISupports
{
const unsigned long TYPE_DEDICATED = 0;
@@ -19,6 +28,8 @@ interface nsIWorkerDebugger : nsISupports
readonly attribute bool isChrome;
readonly attribute bool isFrozen;
readonly attribute nsIWorkerDebugger parent;
readonly attribute unsigned long type;
@@ -27,6 +38,12 @@ interface nsIWorkerDebugger : nsISupports
readonly attribute nsIDOMWindow window;
[implicit_jscontext]
void initialize(in DOMString url);
[implicit_jscontext, binaryname(PostMessageMoz)]
void postMessage(in DOMString message);
void addListener(in nsIWorkerDebuggerListener listener);
void removeListener(in nsIWorkerDebuggerListener listener);
@@ -0,0 +1,6 @@
"use strict";
self.onmessage = function () {};
debugger;
postMessage("worker");
@@ -0,0 +1,6 @@
"use strict";
var dbg = new Debugger(global);
dbg.onDebuggerStatement = function (frame) {
frame.eval("postMessage('debugger');");
};
@@ -0,0 +1,9 @@
"use strict";
var worker = new Worker("WorkerDebugger.initialize_childWorker.js");
worker.onmessage = function (event) {
postMessage("child:" + event.data);
};
debugger;
postMessage("worker");
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker1.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 1.
</body>
<html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
var worker = new Worker("WorkerDebugger.isFrozen_worker2.js");
worker.onmessage = function () {
parent.postMessage("ready", "*");
};
</script>
</head>
<body>
This is page 2.
</body>
<html>
@@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");
@@ -0,0 +1,5 @@
"use strict";
onmessage = function () {};
postMessage("ready");
@@ -0,0 +1,3 @@
"use strict";
self.onmessage = function () {};
@@ -0,0 +1,9 @@
"use strict"
this.onmessage = function (event) {
switch (event.data) {
case "ping":
postMessage("pong");
break;
}
};
@@ -0,0 +1,3 @@
"use strict";
var worker = new Worker("WorkerDebugger.postMessage_childWorker.js");
@@ -0,0 +1,9 @@
"use strict";
const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js";
let prototype = {
self: this,
};
let sandbox = createSandbox(SANDBOX_URL, prototype);
loadSubScript(SANDBOX_URL, sandbox);
@@ -0,0 +1,9 @@
"use strict";
self.addEventListener("message", function(event) {
switch (event.data) {
case "ping":
self.postMessage("pong");
break;
}
});
@@ -0,0 +1,3 @@
"use strict";
self.onmessage = function () {};
@@ -0,0 +1,14 @@
"use strict";
function f() {
debugger;
}
self.onmessage = function (event) {
switch (event.data) {
case "ping":
debugger;
postMessage("pong");
break;
};
};
@@ -0,0 +1,29 @@
"use strict";
let frames = [];
var dbg = new Debugger(global);
dbg.onDebuggerStatement = function (frame) {
frames.push(frame);
postMessage("paused");
enterEventLoop();
frames.pop();
postMessage("resumed");
};
this.onmessage = function (event) {
switch (event.data) {
case "eval":
frames[frames.length - 1].eval("f()");
postMessage("evalled");
break;
case "ping":
postMessage("pong");
break;
case "resume":
leaveEventLoop();
break;
};
};
@@ -0,0 +1,25 @@
"use strict";
function f() {
debugger;
}
var worker = new Worker("WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js");
worker.onmessage = function (event) {
postMessage("child:" + event.data);
};
self.onmessage = function (event) {
var message = event.data;
if (message.indexOf(":") >= 0) {
worker.postMessage(message.split(":")[1]);
return;
}
switch (message) {
case "ping":
debugger;
postMessage("pong");
break;
};
};
@@ -0,0 +1,5 @@
"use strict";
self.onerror = function () {
postMessage("error");
}
@@ -0,0 +1,12 @@
"use strict";
this.onmessage = function (event) {
switch (event.data) {
case "report":
reportError("reported");
break;
case "throw":
throw new Error("thrown");
break;
}
};
@@ -0,0 +1,11 @@
"use strict";
var worker = new Worker("WorkerDebuggerGlobalScope.reportError_childWorker.js");
worker.onmessage = function (event) {
postMessage("child:" + event.data);
};
self.onerror = function () {
postMessage("error");
};
@@ -0,0 +1,12 @@
"use strict";
this.onmessage = function (event) {
switch (event.data) {
case "ping":
setImmediate(function () {
postMessage("pong1");
});
postMessage("pong2");
break;
}
};
@@ -0,0 +1,3 @@
"use strict"
self.onmessage = function () {};
@@ -1,3 +1,3 @@
"use strict";
onmessage = function () {};
self.onmessage = function () {};
@@ -1,3 +1,3 @@
"use strict";
onmessage = function () {};
self.onmessage = function () {};
@@ -1,3 +0,0 @@
"use strict";
var worker = new Worker("WorkerDebugger_childWorker.js");
@@ -1,6 +1,6 @@
"use strict";
onconnect = function (event) {
self.onconnect = function (event) {
event.ports[0].onmessage = function (event) {
switch (event.data) {
case "close":
@@ -0,0 +1,8 @@
"use strict";
var worker = new Worker("WorkerDebugger_childWorker.js");
self.onmessage = function (event) {
postMessage("child:" + event.data);
};
debugger;
postMessage("worker");
+30 -2
View File
@@ -1,9 +1,30 @@
[DEFAULT]
support-files =
WorkerDebugger.initialize_childWorker.js
WorkerDebugger.initialize_debugger.js
WorkerDebugger.initialize_worker.js
WorkerDebugger.isFrozen_iframe1.html
WorkerDebugger.isFrozen_iframe2.html
WorkerDebugger.isFrozen_worker1.js
WorkerDebugger.isFrozen_worker2.js
WorkerDebugger.postMessage_childWorker.js
WorkerDebugger.postMessage_debugger.js
WorkerDebugger.postMessage_worker.js
WorkerDebuggerGlobalScope.createSandbox_debugger.js
WorkerDebuggerGlobalScope.createSandbox_sandbox.js
WorkerDebuggerGlobalScope.createSandbox_worker.js
WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js
WorkerDebuggerGlobalScope.enterEventLoop_debugger.js
WorkerDebuggerGlobalScope.enterEventLoop_worker.js
WorkerDebuggerGlobalScope.reportError_childWorker.js
WorkerDebuggerGlobalScope.reportError_debugger.js
WorkerDebuggerGlobalScope.reportError_worker.js
WorkerDebuggerGlobalScope.setImmediate_debugger.js
WorkerDebuggerGlobalScope.setImmediate_worker.js
WorkerDebuggerManager_childWorker.js
WorkerDebuggerManager_parentWorker.js
WorkerDebuggerManager_worker.js
WorkerDebugger_childWorker.js
WorkerDebugger_parentWorker.js
WorkerDebugger_worker.js
WorkerDebugger_sharedWorker.js
WorkerTest.jsm
WorkerTest_subworker.js
@@ -28,6 +49,13 @@ support-files =
bug1062920_worker.js
[test_WorkerDebugger.xul]
[test_WorkerDebugger.initialize.xul]
[test_WorkerDebugger.isFrozen.xul]
[test_WorkerDebugger.postMessage.xul]
[test_WorkerDebuggerGlobalScope.createSandbox.xul]
[test_WorkerDebuggerGlobalScope.enterEventLoop.xul]
[test_WorkerDebuggerGlobalScope.reportError.xul]
[test_WorkerDebuggerGlobalScope.setImmediate.xul]
[test_WorkerDebuggerManager.xul]
[test_bug883784.jsm]
[test_bug883784.xul]
+99 -18
View File
@@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].
getService(Ci.nsIWorkerDebuggerManager);
const BASE_URL = "chrome://mochitests/content/chrome/dom/workers/test/";
var gRemainingTests = 0;
function waitForWorkerFinish() {
@@ -47,50 +49,54 @@ function* generateDebuggers() {
}
}
function findDebugger(predicate) {
function findDebugger(url) {
for (let dbg of generateDebuggers()) {
if (predicate(dbg)) {
if (dbg.url === url) {
return dbg;
}
}
return null;
}
function waitForRegister(predicate = () => true) {
function waitForRegister(url, dbgUrl) {
return new Promise(function (resolve) {
wdm.addListener({
onRegister: function (dbg) {
if (!predicate(dbg)) {
if (dbg.url !== url) {
return;
}
ok(true, "Debugger with url " + url + " should be registered.");
wdm.removeListener(this);
if (dbgUrl) {
info("Initializing worker debugger with url " + url + ".");
dbg.initialize(dbgUrl);
}
resolve(dbg);
}
});
});
}
function waitForUnregister(predicate = () => true) {
function waitForUnregister(url) {
return new Promise(function (resolve) {
wdm.addListener({
onUnregister: function (dbg) {
if (!predicate(dbg)) {
if (dbg.url !== url) {
return;
}
ok(true, "Debugger with url " + url + " should be unregistered.");
wdm.removeListener(this);
resolve(dbg);
resolve();
}
});
});
}
function waitForDebuggerClose(dbg, predicate = () => true) {
function waitForDebuggerClose(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onClose: function () {
if (!predicate()) {
return;
}
ok(true, "Debugger should be closed.");
dbg.removeListener(this);
resolve();
}
@@ -98,17 +104,92 @@ function waitForDebuggerClose(dbg, predicate = () => true) {
});
}
function waitForDebuggerError(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onError: function (filename, lineno, message) {
dbg.removeListener(this);
resolve(new Error(message, filename, lineno));
}
});
});
}
function waitForDebuggerMessage(dbg, message) {
return new Promise(function (resolve) {
dbg.addListener({
onMessage: function (message1) {
if (message !== message1) {
return;
}
ok(true, "Should receive " + message + " message from debugger.");
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForDebuggerFreeze(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onFreeze: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForDebuggerThaw(dbg) {
return new Promise(function (resolve) {
dbg.addListener({
onThaw: function () {
dbg.removeListener(this);
resolve();
}
});
});
}
function waitForWindowMessage(window, message) {
return new Promise(function (resolve) {
let onmessage = function (event) {
if (event.data !== event.data) {
return;
}
window.removeEventListener("message", onmessage, false);
resolve();
};
window.addEventListener("message", onmessage, false);
});
}
function waitForWorkerMessage(worker, message) {
return new Promise(function (resolve) {
worker.addEventListener("message", function onmessage(event) {
if (event.data !== message) {
return;
}
ok(true, "Should receive " + message + " message from worker.");
worker.removeEventListener("message", onmessage);
resolve();
});
});
}
function waitForMultiple(promises) {
return new Promise(function (resolve) {
let results = [];
let values = [];
for (let i = 0; i < promises.length; ++i) {
let promise = promises[i];
let index = i;
promise.then(function (result) {
is(results.length, index, "events should occur in the specified order");
results.push(result);
if (results.length === promises.length) {
resolve(results);
promises[i].then(function (value) {
is(index + 1, values.length + 1,
"Promise " + (values.length + 1) + " out of " + promises.length +
" should be resolved.");
values.push(value);
if (values.length === promises.length) {
resolve(values);
}
});
}
@@ -0,0 +1,58 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger.initialize"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebugger.initialize_worker.js";
const CHILD_WORKER_URL = "WorkerDebugger.initialize_childWorker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebugger.initialize_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker that creates a child worker, wait for their " +
"debuggers to be registered, and initialize them.");
let promise = waitForMultiple([
waitForRegister(WORKER_URL, DEBUGGER_URL),
waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
]);
let worker = new Worker(WORKER_URL);
yield promise;
info("Check that the debuggers are initialized before the workers " +
"start running.");
yield waitForMultiple([
waitForWorkerMessage(worker, "debugger"),
waitForWorkerMessage(worker, "worker"),
waitForWorkerMessage(worker, "child:debugger"),
waitForWorkerMessage(worker, "child:worker")
]);
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
@@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger.isFrozen"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes";
const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers";
const IFRAME1_URL = "WorkerDebugger.isFrozen_iframe1.html";
const IFRAME2_URL = "WorkerDebugger.isFrozen_iframe2.html";
const WORKER1_URL = "WorkerDebugger.isFrozen_worker1.js";
const WORKER2_URL = "WorkerDebugger.isFrozen_worker2.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
let iframe = $("iframe");
let promise = waitForMultiple([
waitForRegister(WORKER1_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME1_URL;
let [dbg1] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg1),
waitForRegister(WORKER2_URL),
waitForWindowMessage(window, "ready"),
]);
iframe.src = IFRAME2_URL;
let [_, dbg2] = yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed");
is(dbg1.isFrozen, true,
"debugger for worker on page 1 should be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, false,
"debugger for worker on page 2 should not be frozen");
promise = waitForMultiple([
waitForDebuggerFreeze(dbg2),
waitForDebuggerThaw(dbg1),
]);
iframe.contentWindow.history.back();
yield promise;
is(dbg1.isClosed, false,
"debugger for worker on page 1 should not be closed")
is(dbg1.isFrozen, false,
"debugger for worker on page 1 should not be frozen");
is(dbg2.isClosed, false,
"debugger for worker on page 2 should not be closed");
is(dbg2.isFrozen, true,
"debugger for worker on page 2 should be frozen");
SpecialPowers.clearUserPref(CACHE_SUBFRAMES);
SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers);
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
<iframe id="iframe"></iframe>
</body>
<label id="test-result"/>
</window>
@@ -0,0 +1,61 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebugger.postMessage"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebugger.postMessage_worker.js";
const CHILD_WORKER_URL = "WorkerDebugger.postMessage_childWorker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebugger.postMessage_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker that creates a child worker, wait for their " +
"debuggers to be registered, and initialize them.");
let promise = waitForMultiple([
waitForRegister(WORKER_URL, DEBUGGER_URL),
waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
]);
let worker = new Worker(WORKER_URL);
let [dbg, childDbg] = yield promise;
info("Send a request to the worker debugger. This should cause the " +
"the worker debugger to send a response.");
promise = waitForDebuggerMessage(dbg, "pong");
dbg.postMessage("ping");
yield promise;
info("Send a request to the child worker debugger. This should cause " +
"the child worker debugger to send a response.");
promise = waitForDebuggerMessage(childDbg, "pong");
childDbg.postMessage("ping");
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
+64 -40
View File
@@ -16,7 +16,7 @@
<script type="application/javascript">
<![CDATA[
const PARENT_WORKER_URL = "WorkerDebugger_parentWorker.js";
const WORKER_URL = "WorkerDebugger_worker.js";
const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js";
const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js";
@@ -24,65 +24,89 @@
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a top-level chrome worker that creates a non-top-level " +
"content worker and wait for their debuggers to be registered.");
let promise = waitForMultiple([
waitForRegister((dbg) => dbg.url === PARENT_WORKER_URL),
waitForRegister((dbg) => dbg.url === CHILD_WORKER_URL),
waitForRegister(WORKER_URL),
waitForRegister(CHILD_WORKER_URL)
]);
worker = new ChromeWorker(PARENT_WORKER_URL);
let dbgs = yield promise;
is(dbgs[0].isChrome, true, "debugger should be for chrome worker");
is(dbgs[0].parent, null,
"debugger for a top-level worker should not have parent");
is(dbgs[0].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
"debugger should be for dedicated worker");
is(dbgs[0].window, window,
"debugger for top-level dedicated worker should have window");
is(dbgs[1].isChrome, false, "debugger should be for content worker");
is(dbgs[1].parent, dbgs[0],
"debugger for child worker should have parent");
is(dbgs[1].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED);
is(dbgs[1].window, null,
"debugger for non-top-level worker should not have window");
worker = new ChromeWorker(WORKER_URL);
let [dbg, childDbg] = yield promise;
info("Check that the top-level chrome worker debugger has the " +
"correct properties.");
is(dbg.isChrome, true,
"Chrome worker debugger should be chrome.");
is(dbg.parent, null,
"Top-level debugger should not have parent.");
is(dbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
"Chrome worker debugger should be dedicated.");
is(dbg.window, window,
"Top-level dedicated worker debugger should have window.");
info("Check that the non-top-level content worker debugger has the " +
"correct properties.");
is(childDbg.isChrome, false,
"Content worker debugger should be content.");
is(childDbg.parent, dbg,
"Non-top-level worker debugger should have parent.");
is(childDbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
"Content worker debugger should be dedicated.");
is(childDbg.window, null,
"Non-top-level worker debugger should not have window.");
info("Terminate the top-level chrome worker and the non-top-level " +
"content worker, and wait for their debuggers to be " +
"unregistered and closed.");
promise = waitForMultiple([
waitForUnregister((dbg) => dbg.url === CHILD_WORKER_URL),
waitForDebuggerClose(dbgs[1]),
waitForUnregister((dbg) => dbg.url === PARENT_WORKER_URL),
waitForDebuggerClose(dbgs[0]),
waitForUnregister(CHILD_WORKER_URL),
waitForDebuggerClose(childDbg),
waitForUnregister(WORKER_URL),
waitForDebuggerClose(dbg),
]);
worker.terminate();
yield promise;
promise = waitForRegister();
info("Create a shared worker and wait for its debugger to be " +
"registered");
promise = waitForRegister(SHARED_WORKER_URL);
worker = new SharedWorker(SHARED_WORKER_URL);
let dbg = yield promise;
is(dbg.isChrome, false, "debugger should be for content worker");
is(dbg.parent, null,
"debugger for top-level worker should not have parent");
is(dbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED,
"debugger should be for shared worker");
is(dbg.window, null,
"debugger for non-dedicated worker should not have window");
let sharedDbg = yield promise;
info("Check that the shared worker debugger has the correct " +
"properties.");
is(sharedDbg.isChrome, false,
"Shared worker debugger should be content.");
is(sharedDbg.parent, null,
"Shared worker debugger should not have parent.");
is(sharedDbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED,
"Shared worker debugger should be shared.");
is(sharedDbg.window, null,
"Shared worker debugger should not have window.");
info("Create a shared worker with the same URL and check that its " +
"debugger is not registered again.");
let listener = {
onRegistered: function () {
ok(false,
"debugger for shared worker should not be registered twice");
"Shared worker debugger should not be registered again.");
},
};
wdm.addListener(listener);
worker = new SharedWorker(SHARED_WORKER_URL);
dbg.addListener({
onClose: function () {
is(dbg.isClosed, true, "debugger should be closed");
wdm.removeListener(listener);
dbg.removeListener(this);
SimpleTest.finish();
}
});
info("Send a message to the shared worker to tell it to close " +
"itself, and wait for its debugger to be closed.");
promise = waitForMultiple([
waitForUnregister(SHARED_WORKER_URL),
waitForDebuggerClose(sharedDbg)
]);
worker.port.start();
worker.port.postMessage("close");
yield promise;
wdm.removeListener(listener);
SimpleTest.finish();
});
}
@@ -0,0 +1,52 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebuggerGlobalScope.createSandbox"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebuggerGlobalScope.createSandbox_worker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.createSandbox_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker, wait for its debugger to be registered, and " +
"initialize it.");
let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
let worker = new Worker(WORKER_URL);
let dbg = yield promise;
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to send a response from within a sandbox.");
promise = waitForDebuggerMessage(dbg, "pong");
dbg.postMessage("ping");
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
@@ -0,0 +1,126 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebuggerGlobalScope.enterEventLoop"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_worker.js";
const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.enterEventLoop_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker that creates a child worker, wait for their " +
"debuggers to be registered, and initialize them.");
let promise = waitForMultiple([
waitForRegister(WORKER_URL, DEBUGGER_URL),
waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
]);
let worker = new Worker(WORKER_URL);
let [dbg, childDbg] = yield promise;
info("Send a request to the child worker. This should cause the " +
"child worker debugger to enter a nested event loop.");
promise = waitForDebuggerMessage(childDbg, "paused");
worker.postMessage("child:ping");
yield promise;
info("Send a request to the child worker debugger. This should cause " +
"the child worker debugger to enter a second nested event loop.");
promise = waitForDebuggerMessage(childDbg, "paused");
childDbg.postMessage("eval");
yield promise;
info("Send a request to the child worker debugger. This should cause " +
"the child worker debugger to leave its second nested event " +
"loop. The child worker debugger should not send a response " +
"for its previous request until after it has left the nested " +
"event loop.");
promise = waitForMultiple([
waitForDebuggerMessage(childDbg, "resumed"),
waitForDebuggerMessage(childDbg, "evalled")
]);
childDbg.postMessage("resume");
yield promise;
info("Send a request to the child worker debugger. This should cause " +
"the child worker debugger to leave its first nested event loop." +
"The child worker should not send a response for its earlier " +
"request until after the child worker debugger has left the " +
"nested event loop.");
promise = waitForMultiple([
waitForDebuggerMessage(childDbg, "resumed"),
waitForWorkerMessage(worker, "child:pong")
]);
childDbg.postMessage("resume");
yield promise;
info("Send a request to the worker. This should cause the worker " +
"debugger to enter a nested event loop.");
promise = waitForDebuggerMessage(dbg, "paused");
worker.postMessage("ping");
yield promise;
info("Terminate the worker. This should not cause the worker " +
"debugger to terminate as well.");
worker.terminate();
worker.onmessage = function () {
ok(false, "Worker should have been terminated.");
};
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to enter a second nested event loop.");
promise = waitForDebuggerMessage(dbg, "paused");
dbg.postMessage("eval");
yield promise;
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to leave its second nested event loop. The " +
"worker debugger should not send a response for the previous " +
"request until after leaving the nested event loop.");
promise = waitForMultiple([
waitForDebuggerMessage(dbg, "resumed"),
waitForDebuggerMessage(dbg, "evalled")
]);
dbg.postMessage("resume");
yield promise;
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to leave its first nested event loop. The " +
"worker should not send a response for its earlier request, " +
"since it has been terminated.");
promise = waitForMultiple([
waitForDebuggerMessage(dbg, "resumed"),
]);
dbg.postMessage("resume");
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
@@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebuggerGlobalScope.reportError"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebuggerGlobalScope.reportError_worker.js";
const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.reportError_childWorker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.reportError_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker that creates a child worker, wait for their " +
"debuggers to be registered, and initialize them.");
let promise = waitForMultiple([
waitForRegister(WORKER_URL, DEBUGGER_URL),
waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
]);
let worker = new Worker(WORKER_URL);
let [dbg, childDbg] = yield promise;
worker.onmessage = function () {
ok(false, "Debugger error events should not be fired at workers.");
};
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to report an error.");
promise = waitForDebuggerError(dbg);
dbg.postMessage("report");
let error = yield promise;
is(error.fileName, DEBUGGER_URL,
"fileName should be name of file from which error is reported.");
is(error.lineNumber, 6,
"lineNumber should be line number from which error is reported.");
is(error.message, "reported", "message should be reported.");
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to throw an error.");
promise = waitForDebuggerError(dbg);
dbg.postMessage("throw");
error = yield promise;
is(error.fileName, DEBUGGER_URL,
"fileName should be name of file from which error is thrown");
is(error.lineNumber, 9,
"lineNumber should be line number from which error is thrown");
is(error.message, "Error: thrown", "message should be Error: thrown");
info("Send a reqeust to the child worker debugger. This should cause " +
"the child worker debugger to report an error.");
promise = waitForDebuggerError(childDbg);
childDbg.postMessage("report");
error = yield promise;
is(error.fileName, DEBUGGER_URL,
"fileName should be name of file from which error is reported.");
is(error.lineNumber, 6,
"lineNumber should be line number from which error is reported.");
is(error.message, "reported", "message should be reported.");
info("Send a message to the child worker debugger. This should cause " +
"the child worker debugger to throw an error.");
promise = waitForDebuggerError(childDbg);
childDbg.postMessage("throw");
error = yield promise;
is(error.fileName, DEBUGGER_URL,
"fileName should be name of file from which error is thrown");
is(error.lineNumber, 9,
"lineNumber should be line number from which error is thrown");
is(error.message, "Error: thrown", "message should be Error: thrown");
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
@@ -0,0 +1,54 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebuggerGlobalScope.setImmediate"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebuggerGlobalScope.setImmediate_worker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.setImmediate_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
let worker = new Worker(WORKER_URL);
let dbg = yield promise;
info("Send a request to the worker debugger. This should cause a " +
"the worker debugger to send two responses. The worker debugger " +
"should send the second response before the first one, since " +
"the latter is delayed until the next tick of the event loop.");
promise = waitForMultiple([
waitForDebuggerMessage(dbg, "pong2"),
waitForDebuggerMessage(dbg, "pong1")
]);
dbg.postMessage("ping");
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>
+58 -44
View File
@@ -16,65 +16,79 @@
<script type="application/javascript">
<![CDATA[
const PARENT_WORKER_URL = "WorkerDebuggerManager_parentWorker.js";
const WORKER_URL = "WorkerDebuggerManager_worker.js";
const CHILD_WORKER_URL = "WorkerDebuggerManager_childWorker.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
ok(!findDebugger((dbg) => dbg.url === PARENT_WORKER_URL),
"debugger for parent worker should not be enumerated before it is " +
"registered");
ok(!findDebugger((dbg) => dbg.url === CHILD_WORKER_URL),
"debugger for child worker should not be enumerated before it is " +
"registered");
info("Check that worker debuggers are not enumerated before they are " +
"registered.");
ok(!findDebugger(WORKER_URL),
"Worker debugger should not be enumerated before it is registered.");
ok(!findDebugger(CHILD_WORKER_URL),
"Child worker debugger should not be enumerated before it is " +
"registered.");
info("Create a worker that creates a child worker, and wait for " +
"their debuggers to be registered.");
let promise = waitForMultiple([
waitForRegister((dbg) => dbg.url === PARENT_WORKER_URL),
waitForRegister((dbg) => dbg.url === CHILD_WORKER_URL),
waitForRegister(WORKER_URL),
waitForRegister(CHILD_WORKER_URL)
]);
let worker = new Worker(PARENT_WORKER_URL);
let dbgs = yield promise;
is(dbgs[0].isClosed, false,
"debugger for parent worker should not be closed after it is " +
"registered");
is(dbgs[1].isClosed, false,
"debugger for child worker should not be closed after it is " +
"registered");
let worker = new Worker(WORKER_URL);
let [dbg, childDbg] = yield promise;
ok(findDebugger((dbg) => dbg.url === PARENT_WORKER_URL),
"debugger for parent worker should be enumerated after it is " +
"registered");
ok(findDebugger((dbg) => dbg.url === CHILD_WORKER_URL),
"debugger for child worker should be enumerated after it is " +
"registered");
info("Check that worker debuggers are enumerated after they are " +
"registered.");
ok(findDebugger(WORKER_URL),
"Worker debugger should be enumerated after it is registered.");
ok(findDebugger(CHILD_WORKER_URL),
"Child worker debugger should be enumerated after it is " +
"registered.");
info("Check that worker debuggers are not closed before they are " +
"unregistered.");
is(dbg.isClosed, false,
"Worker debugger should not be closed before it is unregistered.");
is(childDbg.isClosed, false,
"Child worker debugger should not be closed before it is " +
"unregistered");
info("Terminate the worker and the child worker, and wait for their " +
"debuggers to be unregistered.");
promise = waitForMultiple([
waitForUnregister((dbg) => dbg.url === CHILD_WORKER_URL),
waitForUnregister((dbg) => dbg.url === PARENT_WORKER_URL),
waitForUnregister(CHILD_WORKER_URL),
waitForUnregister(WORKER_URL),
]);
worker.terminate();
dbgs = yield promise;
is(dbgs[0].isClosed, true,
"debugger for parent worker should be closed after it is " +
"unregistered");
is(dbgs[1].isClosed, true,
"debugger for child worker should be closed after it is " +
"unregistered");
assertThrows(() => dbgs[0].url,
"accessing debugger for parent worker should throw " +
"after it is closed");
assertThrows(() => dbgs[0].url,
"accessing debugger for child worker should throw after " +
"it is closed");
yield promise;
ok(!findDebugger((dbg) => dbg.url === PARENT_WORKER_URL),
"debugger for parent worker should not be enumerated after it is " +
"unregistered");
ok(!findDebugger((dbg) => dbg.url === CHILD_WORKER_URL),
"debugger for child worker should not be enumerated after it is " +
"unregistered");
info("Check that worker debuggers are not enumerated after they are " +
"unregistered.");
ok(!findDebugger(WORKER_URL),
"Worker debugger should not be enumerated after it is " +
"unregistered.");
ok(!findDebugger(CHILD_WORKER_URL),
"Child worker debugger should not be enumerated after it is " +
"unregistered.");
info("Check that worker debuggers are closed after they are " +
"unregistered.");
is(dbg.isClosed, true,
"Worker debugger should be closed after it is unregistered.");
is(childDbg.isClosed, true,
"Child worker debugger should be closed after it is unregistered.");
info("Check that property accesses on worker debuggers throws " +
"after they are closed.");
assertThrows(() => dbg.url,
"Property accesses on worker debugger should throw " +
"after it is closed.");
assertThrows(() => childDbg.url,
"Property accesses on child worker debugger should " +
"throw after it is closed.");
SimpleTest.finish();
});
+4
View File
@@ -4768,7 +4768,11 @@ pref("beacon.enabled", true);
pref("camera.control.face_detection.enabled", true);
// SW Cache API
#ifdef RELEASE_BUILD
pref("dom.caches.enabled", false);
#else
pref("dom.caches.enabled", true);
#endif // RELEASE_BUILD
#ifdef MOZ_WIDGET_GONK
// Empirically, this is the value returned by hal::GetTotalSystemMemory()