Bug 950745 - Flag when we're processing urgent messages and disallow certain activities (r=bsmedberg,luke)
authorBill McCloskey <wmccloskey@mozilla.com>
Thu, 17 Jul 2014 21:09:45 -0700
changeset 194782 ed1736c6367a4228e4b62213248e4d020ada8874
parent 194781 274a3f27b49788fe6a2a3607cf8b118788e9a576
child 194783 2afafea0f09e098bef2e7fdc93e19024cbd80101
push id46435
push userwmccloskey@mozilla.com
push dateFri, 18 Jul 2014 04:09:55 +0000
treeherdermozilla-inbound@ed1736c6367a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, luke
bugs950745
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 950745 - Flag when we're processing urgent messages and disallow certain activities (r=bsmedberg,luke)
dom/events/EventDispatcher.cpp
ipc/glue/MessageChannel.cpp
ipc/glue/MessageChannel.h
js/ipc/JavaScriptChild.cpp
js/ipc/moz.build
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/vm/Interpreter.cpp
js/src/vm/Interpreter.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
widget/windows/nsWindow.cpp
xpcom/threads/nsThread.cpp
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/PageTransitionEvent.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/InternalMutationEvent.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/MiscEvents.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
 
@@ -395,16 +396,20 @@ EventDispatcher::Dispatch(nsISupports* a
   PROFILER_LABEL("EventDispatcher", "Dispatch",
     js::ProfileEntry::Category::EVENTS);
 
   NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
   NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
   NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
 
+#ifdef NIGHTLY_BUILD
+  MOZ_RELEASE_ASSERT(!mozilla::ipc::ProcessingUrgentMessages());
+#endif
+
   // If we're dispatching an already created DOMEvent object, make
   // sure it is initialized!
   // If aTargets is non-null, the event isn't going to be dispatched.
   NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
 
   nsCOMPtr<EventTarget> target = do_QueryInterface(aTarget);
 
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -31,16 +31,18 @@ struct RunnableMethodTraits<mozilla::ipc
 };
 
 #define IPC_ASSERT(_cond, ...)                                      \
     do {                                                            \
         if (!(_cond))                                               \
             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
     } while (0)
 
+static uintptr_t gDispatchingUrgentMessageCount;
+
 namespace mozilla {
 namespace ipc {
 
 const int32_t MessageChannel::kNoTimeout = INT32_MIN;
 
 // static
 bool MessageChannel::sIsPumpingMessages = false;
 
@@ -1094,19 +1096,23 @@ MessageChannel::DispatchSyncMessage(cons
 void
 MessageChannel::DispatchUrgentMessage(const Message& aMsg)
 {
     AssertWorkerThread();
     MOZ_ASSERT(aMsg.is_urgent());
 
     Message *reply = nullptr;
 
+    MOZ_ASSERT(NS_IsMainThread());
+
+    gDispatchingUrgentMessageCount++;
     mDispatchingUrgentMessageCount++;
     Result rv = mListener->OnCallReceived(aMsg, reply);
     mDispatchingUrgentMessageCount--;
+    gDispatchingUrgentMessageCount--;
 
     if (!MaybeHandleError(rv, "DispatchUrgentMessage")) {
         delete reply;
         reply = new Message();
         reply->set_urgent();
         reply->set_reply();
         reply->set_reply_error();
     }
@@ -1747,10 +1753,16 @@ MessageChannel::DumpInterruptStack(const
         const char* dir, *sems, *name;
         mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
 
         printf_stderr("%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
                       i, dir, sems, name, id);
     }
 }
 
+bool
+ProcessingUrgentMessages()
+{
+    return gDispatchingUrgentMessageCount > 0;
+}
+
 } // ipc
 } // mozilla
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -636,12 +636,15 @@ class MessageChannel : HasResultCodes
     HANDLE mEvent;
 #endif
 
     // Should the channel abort the process from the I/O thread when
     // a channel error occurs?
     bool mAbortOnError;
 };
 
+bool
+ProcessingUrgentMessages();
+
 } // namespace ipc
 } // namespace mozilla
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/js/ipc/JavaScriptChild.cpp
+++ b/js/ipc/JavaScriptChild.cpp
@@ -3,39 +3,55 @@
  *
  * 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 "JavaScriptChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "nsContentUtils.h"
 #include "xpcprivate.h"
 #include "jsfriendapi.h"
 #include "nsCxPusher.h"
+#include "AccessCheck.h"
 
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
 
 using mozilla::AutoSafeJSContext;
 
+#ifdef NIGHTLY_BUILD
+static void
+UrgentMessageCheck(JSContext *cx, HandleScript script)
+{
+    // We're only allowed to enter chrome JS code while processing urgent
+    // messages.
+    if (ipc::ProcessingUrgentMessages())
+        MOZ_RELEASE_ASSERT(xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)));
+}
+#endif
+
 static void
 FinalizeChild(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartment, void *data)
 {
     if (status == JSFINALIZE_GROUP_START) {
         static_cast<JavaScriptChild *>(data)->finalize(fop);
     }
 }
 
 JavaScriptChild::JavaScriptChild(JSRuntime *rt)
   : JavaScriptShared(rt),
     JavaScriptBase<PJavaScriptChild>(rt)
 {
+#ifdef NIGHTLY_BUILD
+    js::SetAssertOnScriptEntryHook(rt, UrgentMessageCheck);
+#endif
 }
 
 JavaScriptChild::~JavaScriptChild()
 {
     JS_RemoveFinalizeCallback(rt_, FinalizeChild);
 }
 
 bool
--- a/js/ipc/moz.build
+++ b/js/ipc/moz.build
@@ -25,10 +25,11 @@ FINAL_LIBRARY = 'xul'
 
 DEFINES['BIN_SUFFIX'] = '"%s"' % CONFIG['BIN_SUFFIX']
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/js/ipc',
     '/js/public',
     '/js/xpconnect/src',
+    '/js/xpconnect/wrappers',
 ]
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -58,16 +58,24 @@ js::SetSourceHook(JSRuntime *rt, UniqueP
 }
 
 JS_FRIEND_API(UniquePtr<SourceHook>)
 js::ForgetSourceHook(JSRuntime *rt)
 {
     return Move(rt->sourceHook);
 }
 
+#ifdef NIGHTLY_BUILD
+JS_FRIEND_API(void)
+js::SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook)
+{
+    rt->assertOnScriptEntryHook_ = hook;
+}
+#endif
+
 JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
     rt->gc.setGrayRootsTracer(traceOp, data);
 }
 
 JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -412,16 +412,23 @@ class SourceHook {
  */
 extern JS_FRIEND_API(void)
 SetSourceHook(JSRuntime *rt, mozilla::UniquePtr<SourceHook> hook);
 
 /* Remove |rt|'s source hook, and return it. The caller now owns the hook. */
 extern JS_FRIEND_API(mozilla::UniquePtr<SourceHook>)
 ForgetSourceHook(JSRuntime *rt);
 
+#ifdef NIGHTLY_BUILD
+typedef void (*AssertOnScriptEntryHook)(JSContext *cx, JS::HandleScript script);
+
+extern JS_FRIEND_API(void)
+SetAssertOnScriptEntryHook(JSRuntime *rt, AssertOnScriptEntryHook hook);
+#endif
+
 extern JS_FRIEND_API(JS::Zone *)
 GetCompartmentZone(JSCompartment *comp);
 
 typedef bool
 (* PreserveWrapperCallback)(JSContext *cx, JSObject *obj);
 
 typedef enum  {
     CollectNurseryBeforeDump,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -369,16 +369,21 @@ ExecuteState::pushInterpreterFrame(JSCon
                                                               type_, evalInFrame_);
 }
 
 bool
 js::RunScript(JSContext *cx, RunState &state)
 {
     JS_CHECK_RECURSION(cx, return false);
 
+#ifdef NIGHTLY_BUILD
+    if (AssertOnScriptEntryHook hook = cx->runtime()->assertOnScriptEntryHook_)
+        (*hook)(cx, state.script());
+#endif
+
     SPSEntryMarker marker(cx->runtime(), state.script());
 
     state.script()->ensureNonLazyCanonicalFunction(cx);
 
 #ifdef JS_ION
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -198,17 +198,17 @@ class RunState
         JS_ASSERT(isInvoke());
         return (InvokeState *)this;
     }
     GeneratorState *asGenerator() const {
         JS_ASSERT(isGenerator());
         return (GeneratorState *)this;
     }
 
-    JSScript *script() const { return script_; }
+    JS::HandleScript script() const { return script_; }
 
     virtual InterpreterFrame *pushInterpreterFrame(JSContext *cx) = 0;
     virtual void setReturnValue(Value v) = 0;
 
   private:
     RunState(const RunState &other) MOZ_DELETE;
     RunState(const ExecuteState &other) MOZ_DELETE;
     RunState(const InvokeState &other) MOZ_DELETE;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -176,16 +176,17 @@ JSRuntime::JSRuntime(JSRuntime *parentRu
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulatorRuntime_(nullptr),
 #endif
     scriptAndCountsVector(nullptr),
     NaNValue(DoubleNaNValue()),
     negativeInfinityValue(DoubleValue(NegativeInfinity<double>())),
     positiveInfinityValue(DoubleValue(PositiveInfinity<double>())),
     emptyString(nullptr),
+    assertOnScriptEntryHook_(nullptr),
     debugMode(false),
     spsProfiler(thisFromCtor()),
     profilingScripts(false),
     hadOutOfMemory(false),
     haveCreatedContext(false),
     data(nullptr),
     signalHandlersInstalled_(false),
     defaultFreeOp_(thisFromCtor(), false),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1031,16 +1031,18 @@ struct JSRuntime : public JS::shadow::Ru
     mozilla::LinkedList<JSContext> contextList;
 
     bool hasContexts() const {
         return !contextList.isEmpty();
     }
 
     mozilla::UniquePtr<js::SourceHook> sourceHook;
 
+    js::AssertOnScriptEntryHook assertOnScriptEntryHook_;
+
     /* Per runtime debug hooks -- see js/OldDebugAPI.h. */
     JSDebugHooks        debugHooks;
 
     /* If true, new compartments are initially in debug mode. */
     bool                debugMode;
 
     /* SPS profiling metadata */
     js::SPSProfiler     spsProfiler;
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -4336,16 +4336,18 @@ inline static mozilla::HangMonitor::Acti
   return mozilla::HangMonitor::kActivityUIAVail;
 }
 
 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
 // exceptions and passes the real work to WindowProcInternal. See bug 587406
 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
+  MOZ_RELEASE_ASSERT(!ipc::ProcessingUrgentMessages());
+
   HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));
 
   return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg, wParam, lParam);
 }
 
 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
   if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -20,16 +20,17 @@
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "pratom.h"
 #include "prlog.h"
 #include "nsIObserverService.h"
 #include "mozilla/HangMonitor.h"
 #include "mozilla/IOInterposer.h"
+#include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/Services.h"
 #include "nsXPCOMPrivate.h"
 #include "mozilla/ChaosMode.h"
 
 #ifdef XP_LINUX
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sched.h>
@@ -681,16 +682,18 @@ void canary_alarm_handler(int signum)
     }                                                                          \
   PR_END_MACRO
 
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait, mRunningEvent));
 
+  MOZ_RELEASE_ASSERT(!ipc::ProcessingUrgentMessages());
+
   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   // The toplevel event loop normally blocks waiting for the next event, but
   // if we're trying to shut this thread down, we must exit the event loop when
   // the event queue is empty.
   // This only applys to the toplevel event loop! Nested event loops (e.g.