diff options
Diffstat (limited to 'devtools/platform')
-rw-r--r-- | devtools/platform/IJSDebugger.idl | 22 | ||||
-rw-r--r-- | devtools/platform/JSDebugger.cpp | 55 | ||||
-rw-r--r-- | devtools/platform/JSDebugger.h | 30 | ||||
-rw-r--r-- | devtools/platform/components.conf | 20 | ||||
-rw-r--r-- | devtools/platform/jsdebugger.sys.mjs | 94 | ||||
-rw-r--r-- | devtools/platform/moz.build | 33 | ||||
-rw-r--r-- | devtools/platform/nsIJSInspector.idl | 75 | ||||
-rw-r--r-- | devtools/platform/nsJSInspector.cpp | 116 | ||||
-rw-r--r-- | devtools/platform/nsJSInspector.h | 37 | ||||
-rw-r--r-- | devtools/platform/tests/xpcshell/.eslintrc.js | 19 | ||||
-rw-r--r-- | devtools/platform/tests/xpcshell/head_dbg.js | 14 | ||||
-rw-r--r-- | devtools/platform/tests/xpcshell/test_nativewrappers.js | 45 | ||||
-rw-r--r-- | devtools/platform/tests/xpcshell/test_nsjsinspector.js | 65 | ||||
-rw-r--r-- | devtools/platform/tests/xpcshell/xpcshell.toml | 7 |
14 files changed, 632 insertions, 0 deletions
diff --git a/devtools/platform/IJSDebugger.idl b/devtools/platform/IJSDebugger.idl new file mode 100644 index 0000000000..ec309a315f --- /dev/null +++ b/devtools/platform/IJSDebugger.idl @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * Do not use this interface. Instead, write: + * const { addDebuggerToGlobal } = ChromeUtils.importESModule( + * "resource://gre/modules/jsdebugger.sys.mjs" + * ); + * addDebuggerToGlobal(global); + */ +[scriptable, uuid(a36fa816-31da-4b23-bc97-6412771f0867)] +interface IJSDebugger : nsISupports +{ + /** + * Define the global Debugger constructor on a given global. + */ + [implicit_jscontext] + void addClass(in jsval global); +}; diff --git a/devtools/platform/JSDebugger.cpp b/devtools/platform/JSDebugger.cpp new file mode 100644 index 0000000000..54fde46a54 --- /dev/null +++ b/devtools/platform/JSDebugger.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "JSDebugger.h" +#include "nsThreadUtils.h" +#include "jsapi.h" +#include "jsfriendapi.h" +#include "js/Wrapper.h" +#include "nsServiceManagerUtils.h" + +#define JSDEBUGGER_CONTRACTID "@mozilla.org/jsdebugger;1" + +#define JSDEBUGGER_CID \ + { \ + 0x0365cbd5, 0xd46e, 0x4e94, { \ + 0xa3, 0x9f, 0x83, 0xb6, 0x3c, 0xd1, 0xa9, 0x63 \ + } \ + } + +namespace mozilla::jsdebugger { + +NS_IMPL_ISUPPORTS(JSDebugger, IJSDebugger) + +JSDebugger::JSDebugger() = default; + +JSDebugger::~JSDebugger() = default; + +NS_IMETHODIMP +JSDebugger::AddClass(JS::Handle<JS::Value> global, JSContext* cx) { + if (!global.isObject()) { + return NS_ERROR_INVALID_ARG; + } + + JS::Rooted<JSObject*> obj(cx, &global.toObject()); + obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); + if (!obj) { + return NS_ERROR_FAILURE; + } + + if (!JS_IsGlobalObject(obj)) { + return NS_ERROR_INVALID_ARG; + } + + JSAutoRealm ar(cx, obj); + if (!JS_DefineDebuggerObject(cx, obj)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace mozilla::jsdebugger diff --git a/devtools/platform/JSDebugger.h b/devtools/platform/JSDebugger.h new file mode 100644 index 0000000000..830daa3a43 --- /dev/null +++ b/devtools/platform/JSDebugger.h @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef JSDebugger_h +#define JSDebugger_h + +#include "IJSDebugger.h" +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace jsdebugger { + +class JSDebugger final : public IJSDebugger { + public: + NS_DECL_ISUPPORTS + NS_DECL_IJSDEBUGGER + + JSDebugger(); + + private: + ~JSDebugger(); +}; + +} // namespace jsdebugger +} // namespace mozilla + +#endif /* JSDebugger_h */ diff --git a/devtools/platform/components.conf b/devtools/platform/components.conf new file mode 100644 index 0000000000..8e37217dd3 --- /dev/null +++ b/devtools/platform/components.conf @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +Classes = [ + { + 'cid': '{ec5aa99c-7abb-4142-ac5f-aab2419e38e2}', + 'contract_ids': ['@mozilla.org/jsinspector;1'], + 'type': 'mozilla::jsinspector::nsJSInspector', + 'headers': ['/devtools/platform/nsJSInspector.h'], + }, + { + 'cid': '{0365cbd5-d46e-4e94-a39f-83b63cd1a963}', + 'contract_ids': ['@mozilla.org/jsdebugger;1'], + 'type': 'mozilla::jsdebugger::JSDebugger', + 'headers': ['/devtools/platform/JSDebugger.h'], + }, +] diff --git a/devtools/platform/jsdebugger.sys.mjs b/devtools/platform/jsdebugger.sys.mjs new file mode 100644 index 0000000000..a4af1fdbc3 --- /dev/null +++ b/devtools/platform/jsdebugger.sys.mjs @@ -0,0 +1,94 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This is the js module for Debugger. Import it like so: + * const { addDebuggerToGlobal } = ChromeUtils.importESModule( + * "resource://gre/modules/jsdebugger.sys.mjs" + * ); + * addDebuggerToGlobal(globalThis); + * + * This will create a 'Debugger' object, which provides an interface to debug + * JavaScript code running in other compartments in the same process, on the + * same thread. + * + * For documentation on the API, see: + * https://developer.mozilla.org/en-US/docs/Tools/Debugger-API + */ + +const init = Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger); + +export function addDebuggerToGlobal(global) { + init.addClass(global); + initPromiseDebugging(global); +} + +// Defines the Debugger in a sandbox global in a separate compartment. This +// ensures the debugger and debuggee are in different compartments. +export function addSandboxedDebuggerToGlobal(global) { + const sb = Cu.Sandbox(global, { freshCompartment: true }); + addDebuggerToGlobal(sb); + global.Debugger = sb.Debugger; +} + +function initPromiseDebugging(global) { + if (global.Debugger.Object.prototype.PromiseDebugging) { + return; + } + + // If the PromiseDebugging object doesn't have all legacy functions, we're + // using the new accessors on Debugger.Object already. + if (!PromiseDebugging.getDependentPromises) { + return; + } + + // Otherwise, polyfill them using PromiseDebugging. + global.Debugger.Object.prototype.PromiseDebugging = PromiseDebugging; + global.eval(polyfillSource); +} + +const polyfillSource = ` + Object.defineProperty(Debugger.Object.prototype, "promiseState", { + get() { + const state = this.PromiseDebugging.getState(this.unsafeDereference()); + return { + state: state.state, + value: this.makeDebuggeeValue(state.value), + reason: this.makeDebuggeeValue(state.reason) + }; + } + }); + Object.defineProperty(Debugger.Object.prototype, "promiseLifetime", { + get() { + return this.PromiseDebugging.getPromiseLifetime(this.unsafeDereference()); + } + }); + Object.defineProperty(Debugger.Object.prototype, "promiseTimeToResolution", { + get() { + return this.PromiseDebugging.getTimeToSettle(this.unsafeDereference()); + } + }); + Object.defineProperty(Debugger.Object.prototype, "promiseDependentPromises", { + get() { + let promises = this.PromiseDebugging.getDependentPromises(this.unsafeDereference()); + return promises.map(p => this.makeDebuggeeValue(p)); + } + }); + Object.defineProperty(Debugger.Object.prototype, "promiseAllocationSite", { + get() { + return this.PromiseDebugging.getAllocationStack(this.unsafeDereference()); + } + }); + Object.defineProperty(Debugger.Object.prototype, "promiseResolutionSite", { + get() { + let state = this.promiseState.state; + if (state === "fulfilled") { + return this.PromiseDebugging.getFullfillmentStack(this.unsafeDereference()); + } else { + return this.PromiseDebugging.getRejectionStack(this.unsafeDereference()); + } + } + }); +`; diff --git a/devtools/platform/moz.build b/devtools/platform/moz.build new file mode 100644 index 0000000000..de8bf749c6 --- /dev/null +++ b/devtools/platform/moz.build @@ -0,0 +1,33 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.toml"] + +XPIDL_SOURCES += [ + "IJSDebugger.idl", + "nsIJSInspector.idl", +] + +XPIDL_MODULE = "jsdevtools" + +SOURCES += [ + "JSDebugger.cpp", + "nsJSInspector.cpp", +] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +EXTRA_JS_MODULES += [ + "jsdebugger.sys.mjs", +] + +# The /devtools/moz.build opted in to treatment of devtools as a browser +# component, but for this platform integration code it is simpler not too. +DIST_SUBDIR = "" + +FINAL_LIBRARY = "xul" diff --git a/devtools/platform/nsIJSInspector.idl b/devtools/platform/nsIJSInspector.idl new file mode 100644 index 0000000000..40ad495234 --- /dev/null +++ b/devtools/platform/nsIJSInspector.idl @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * Utilities for running nested event loops, asking them to return, and + * keeping track of which ones are still running. + */ +[scriptable, uuid(6758d0d7-e96a-4c5c-bca8-3bcbe5a15943)] +interface nsIJSInspector : nsISupports +{ + /** + * Process the current thread's event queue, calling event handlers until + * a call to exitNestedEventLoop, below, asks us to return. + * + * The name 'enterNestedEventLoop' may be misleading if read too literally. + * This method loops calling event handlers until one asks it to stop, and + * then returns. So by that point, the nested event loop has been not only + * entered, but also run and exited. + * + * When enterNestedEventLoop calls an event handler, that handler may itself + * call enterNestedEventLoop, and so on, so that there may be arbitrarily + * many such calls on the stack at the same time. + * + * We say an enterNestedEventLoop call is "running" if it has not yet been + * asked to return, or "stopped" if it has been asked to return once it has + * finished processing the current event. + * + * @param requestor A token of the caller's choice to identify this event + * loop. + * + * @return depth The number of running enterNestedEventLoop calls + * remaining, now that this one has returned. + * + * (Note that not all calls still on the stack are + * necessary running; exitNestedEventLoop can ask any + * number of enterNestedEventLoop calls to return.) + */ + unsigned long enterNestedEventLoop(in jsval requestor); + + /** + * Stop the youngest running enterNestedEventLoop call, asking it to return + * once it has finished processing the current event. + * + * The name 'exitNestedEventLoop' may be misleading if read too literally. + * The affected event loop does not return immediately when this method is + * called. Rather, this method simply returns to its caller; the affected + * loop's current event handler is allowed to run to completion; and then + * that loop returns without processing any more events. + * + * This method ignores loops that have already been stopped, and operates on + * the youngest loop that is still running. Each call to this method stops + * another running loop. + * + * @return depth The number of running enterNestedEventLoop calls + * remaining, now that one has been stopped. + * + * @throws NS_ERROR_FAILURE if there are no running enterNestedEventLoop calls. + */ + unsigned long exitNestedEventLoop(); + + /** + * The number of running enterNestedEventLoop calls on the stack. + * This count does not include stopped enterNestedEventLoop calls. + */ + readonly attribute unsigned long eventLoopNestLevel; + + /** + * The |requestor| value that was passed to the youngest running + * enterNestedEventLoop call. + */ + readonly attribute jsval lastNestRequestor; +}; diff --git a/devtools/platform/nsJSInspector.cpp b/devtools/platform/nsJSInspector.cpp new file mode 100644 index 0000000000..944cd9d943 --- /dev/null +++ b/devtools/platform/nsJSInspector.cpp @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsJSInspector.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsTArray.h" + +#define JSINSPECTOR_CONTRACTID "@mozilla.org/jsinspector;1" + +#define JSINSPECTOR_CID \ + { \ + 0xec5aa99c, 0x7abb, 0x4142, { \ + 0xac, 0x5f, 0xaa, 0xb2, 0x41, 0x9e, 0x38, 0xe2 \ + } \ + } + +namespace mozilla { +namespace jsinspector { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSInspector) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIJSInspector) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSInspector) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSInspector) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSInspector) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSInspector) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSInspector) + tmp->mRequestors.Clear(); + tmp->mLastRequestor = JS::NullValue(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSInspector) + for (uint32_t i = 0; i < tmp->mRequestors.Length(); ++i) { + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRequestors[i]) + } + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLastRequestor) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +nsJSInspector::nsJSInspector() + : mNestedLoopLevel(0), mRequestors(1), mLastRequestor(JS::NullValue()) {} + +nsJSInspector::~nsJSInspector() { + MOZ_ASSERT(mRequestors.Length() == 0); + MOZ_ASSERT(mLastRequestor.isNull()); + mozilla::DropJSObjects(this); +} + +NS_IMETHODIMP +nsJSInspector::EnterNestedEventLoop(JS::Handle<JS::Value> requestor, + uint32_t* out) { + nsresult rv = NS_OK; + + mLastRequestor = requestor; + mRequestors.AppendElement(requestor); + mozilla::HoldJSObjects(this); + + mozilla::dom::AutoNoJSAPI nojsapi; + + uint32_t nestLevel = ++mNestedLoopLevel; + if (!SpinEventLoopUntil("nsJSInspector::EnterNestedEventLoop"_ns, + [&]() { return mNestedLoopLevel < nestLevel; })) { + rv = NS_ERROR_UNEXPECTED; + } + + NS_ASSERTION(mNestedLoopLevel <= nestLevel, + "nested event didn't unwind properly"); + + if (mNestedLoopLevel == nestLevel) { + mLastRequestor = mRequestors.ElementAt(--mNestedLoopLevel); + } + + *out = mNestedLoopLevel; + return rv; +} + +NS_IMETHODIMP +nsJSInspector::ExitNestedEventLoop(uint32_t* out) { + if (mNestedLoopLevel > 0) { + mRequestors.RemoveElementAt(--mNestedLoopLevel); + if (mNestedLoopLevel > 0) + mLastRequestor = mRequestors.ElementAt(mNestedLoopLevel - 1); + else + mLastRequestor = JS::NullValue(); + } else { + return NS_ERROR_FAILURE; + } + + *out = mNestedLoopLevel; + + return NS_OK; +} + +NS_IMETHODIMP +nsJSInspector::GetEventLoopNestLevel(uint32_t* out) { + *out = mNestedLoopLevel; + return NS_OK; +} + +NS_IMETHODIMP +nsJSInspector::GetLastNestRequestor(JS::MutableHandle<JS::Value> out) { + out.set(mLastRequestor); + return NS_OK; +} + +} // namespace jsinspector +} // namespace mozilla diff --git a/devtools/platform/nsJSInspector.h b/devtools/platform/nsJSInspector.h new file mode 100644 index 0000000000..c4292fb88f --- /dev/null +++ b/devtools/platform/nsJSInspector.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef COMPONENTS_JSINSPECTOR_H +#define COMPONENTS_JSINSPECTOR_H + +#include "nsIJSInspector.h" +#include "nsCycleCollectionParticipant.h" +#include "nsTArray.h" +#include "js/Value.h" +#include "js/RootingAPI.h" + +namespace mozilla { +namespace jsinspector { + +class nsJSInspector final : public nsIJSInspector { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSInspector) + NS_DECL_NSIJSINSPECTOR + + nsJSInspector(); + + private: + ~nsJSInspector(); + + uint32_t mNestedLoopLevel; + nsTArray<JS::Heap<JS::Value> > mRequestors; + JS::Heap<JS::Value> mLastRequestor; +}; + +} // namespace jsinspector +} // namespace mozilla + +#endif diff --git a/devtools/platform/tests/xpcshell/.eslintrc.js b/devtools/platform/tests/xpcshell/.eslintrc.js new file mode 100644 index 0000000000..dc7f2c85c4 --- /dev/null +++ b/devtools/platform/tests/xpcshell/.eslintrc.js @@ -0,0 +1,19 @@ +"use strict"; + +// Parent config file for all devtools xpcshell files. +module.exports = { + extends: ["plugin:mozilla/xpcshell-test"], + rules: { + // Allow non-camelcase so that run_test doesn't produce a warning. + camelcase: "off", + // Allow using undefined variables so that tests can refer to functions + // and variables defined in head.js files, without having to maintain a + // list of globals in each .eslintrc file. + // Note that bug 1168340 will eventually help auto-registering globals + // from head.js files. + "no-undef": "off", + "block-scoped-var": "off", + // Tests can always import anything. + "mozilla/reject-some-requires": "off", + }, +}; diff --git a/devtools/platform/tests/xpcshell/head_dbg.js b/devtools/platform/tests/xpcshell/head_dbg.js new file mode 100644 index 0000000000..48942bdb7a --- /dev/null +++ b/devtools/platform/tests/xpcshell/head_dbg.js @@ -0,0 +1,14 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function testGlobal(name) { + const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].createInstance( + Ci.nsIPrincipal + ); + + const sandbox = Cu.Sandbox(systemPrincipal); + Cu.evalInSandbox("this.__name = '" + name + "'", sandbox); + return sandbox; +} diff --git a/devtools/platform/tests/xpcshell/test_nativewrappers.js b/devtools/platform/tests/xpcshell/test_nativewrappers.js new file mode 100644 index 0000000000..a7ea81adaf --- /dev/null +++ b/devtools/platform/tests/xpcshell/test_nativewrappers.js @@ -0,0 +1,45 @@ +"use strict"; + +function run_test() { + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + + Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true); + registerCleanupFunction(() => { + Services.prefs.clearUserPref("security.allow_eval_with_system_principal"); + }); + + addDebuggerToGlobal(globalThis); + const g = testGlobal("test1"); + + const dbg = new Debugger(); + dbg.addDebuggee(g); + dbg.onDebuggerStatement = function (frame) { + const args = frame.arguments; + try { + args[0]; + Assert.ok(true); + } catch (ex) { + Assert.ok(false); + } + }; + + g.eval("function stopMe(arg) {debugger;}"); + + const g2 = testGlobal("test2"); + g2.g = g; + g2.eval( + "(" + + function createBadEvent() { + // eslint-disable-next-line mozilla/reject-importGlobalProperties + Cu.importGlobalProperties(["DOMParser"]); + const parser = new DOMParser(); + const doc = parser.parseFromString("<foo></foo>", "text/xml"); + g.stopMe(doc.createEvent("MouseEvent")); + } + + ")()" + ); + + dbg.removeAllDebuggees(); +} diff --git a/devtools/platform/tests/xpcshell/test_nsjsinspector.js b/devtools/platform/tests/xpcshell/test_nsjsinspector.js new file mode 100644 index 0000000000..74d18b7689 --- /dev/null +++ b/devtools/platform/tests/xpcshell/test_nsjsinspector.js @@ -0,0 +1,65 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the basic functionality of the nsIJSInspector component. +var gCount = 0; +const MAX = 10; + +var inspector = Cc["@mozilla.org/jsinspector;1"].getService(Ci.nsIJSInspector); + +// Emulate 10 simultaneously-debugged windows from 3 separate client connections. +var requestor = count => ({ + url: "http://foo/bar/" + count, + connection: "conn" + (count % 3), +}); + +function run_test() { + test_nesting(); +} + +function test_nesting() { + Assert.equal(inspector.eventLoopNestLevel, 0); + + Services.tm.dispatchToMainThread({ run: enterEventLoop }); + + Assert.equal(inspector.enterNestedEventLoop(requestor(gCount)), 0); + Assert.equal(inspector.eventLoopNestLevel, 0); + Assert.equal(inspector.lastNestRequestor, null); +} + +function enterEventLoop() { + if (gCount++ < MAX) { + Services.tm.dispatchToMainThread({ run: enterEventLoop }); + + Object.create(requestor(gCount)); + + Assert.equal(inspector.eventLoopNestLevel, gCount); + Assert.equal(inspector.lastNestRequestor.url, requestor(gCount - 1).url); + Assert.equal( + inspector.lastNestRequestor.connection, + requestor(gCount - 1).connection + ); + Assert.equal(inspector.enterNestedEventLoop(requestor(gCount)), gCount); + } else { + Assert.equal(gCount, MAX + 1); + Services.tm.dispatchToMainThread({ run: exitEventLoop }); + } +} + +function exitEventLoop() { + if (inspector.lastNestRequestor != null) { + Assert.equal(inspector.lastNestRequestor.url, requestor(gCount - 1).url); + Assert.equal( + inspector.lastNestRequestor.connection, + requestor(gCount - 1).connection + ); + if (gCount-- > 1) { + Services.tm.dispatchToMainThread({ run: exitEventLoop }); + } + + Assert.equal(inspector.exitNestedEventLoop(), gCount); + Assert.equal(inspector.eventLoopNestLevel, gCount); + } +} diff --git a/devtools/platform/tests/xpcshell/xpcshell.toml b/devtools/platform/tests/xpcshell/xpcshell.toml new file mode 100644 index 0000000000..ede5c03e2b --- /dev/null +++ b/devtools/platform/tests/xpcshell/xpcshell.toml @@ -0,0 +1,7 @@ +[DEFAULT] +tags = "devtools" +head = "head_dbg.js" + +["test_nativewrappers.js"] + +["test_nsjsinspector.js"] |