diff options
Diffstat (limited to 'devtools/shared/jsinspector')
-rw-r--r-- | devtools/shared/jsinspector/moz.build | 17 | ||||
-rw-r--r-- | devtools/shared/jsinspector/nsIJSInspector.idl | 75 | ||||
-rw-r--r-- | devtools/shared/jsinspector/nsJSInspector.cpp | 147 | ||||
-rw-r--r-- | devtools/shared/jsinspector/nsJSInspector.h | 39 |
4 files changed, 278 insertions, 0 deletions
diff --git a/devtools/shared/jsinspector/moz.build b/devtools/shared/jsinspector/moz.build new file mode 100644 index 000000000..fc81f7e1e --- /dev/null +++ b/devtools/shared/jsinspector/moz.build @@ -0,0 +1,17 @@ +# -*- 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/. + +XPIDL_SOURCES += [ + 'nsIJSInspector.idl', +] + +XPIDL_MODULE = 'jsinspector' + +SOURCES += [ + 'nsJSInspector.cpp', +] + +FINAL_LIBRARY = 'xul' diff --git a/devtools/shared/jsinspector/nsIJSInspector.idl b/devtools/shared/jsinspector/nsIJSInspector.idl new file mode 100644 index 000000000..40ad49523 --- /dev/null +++ b/devtools/shared/jsinspector/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/shared/jsinspector/nsJSInspector.cpp b/devtools/shared/jsinspector/nsJSInspector.cpp new file mode 100644 index 000000000..6d717af5b --- /dev/null +++ b/devtools/shared/jsinspector/nsJSInspector.cpp @@ -0,0 +1,147 @@ +/* -*- 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 "nsIXPConnect.h" +#include "nsThreadUtils.h" +#include "jsfriendapi.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/ModuleUtils.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsServiceManagerUtils.h" +#include "nsMemory.h" +#include "nsArray.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_GENERIC_FACTORY_CONSTRUCTOR(nsJSInspector) + +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_SCRIPT_OBJECTS +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; + while (NS_SUCCEEDED(rv) && mNestedLoopLevel >= nestLevel) { + if (!NS_ProcessNextEvent()) + 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 + +NS_DEFINE_NAMED_CID(JSINSPECTOR_CID); + +static const mozilla::Module::CIDEntry kJSInspectorCIDs[] = { + { &kJSINSPECTOR_CID, false, nullptr, mozilla::jsinspector::nsJSInspectorConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kJSInspectorContracts[] = { + { JSINSPECTOR_CONTRACTID, &kJSINSPECTOR_CID }, + { nullptr } +}; + +static const mozilla::Module kJSInspectorModule = { + mozilla::Module::kVersion, + kJSInspectorCIDs, + kJSInspectorContracts +}; + +NSMODULE_DEFN(jsinspector) = &kJSInspectorModule; diff --git a/devtools/shared/jsinspector/nsJSInspector.h b/devtools/shared/jsinspector/nsJSInspector.h new file mode 100644 index 000000000..4e60b0428 --- /dev/null +++ b/devtools/shared/jsinspector/nsJSInspector.h @@ -0,0 +1,39 @@ +/* -*- 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 "mozilla/Attributes.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 |