Bug 1342012 - Support import from timeout handlers by associating the initiating script with the compiled JSScript r=smaug
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 06 Dec 2018 16:52:18 -0500
changeset 509506 65742a0032968ba2a66c33d6786d3288e8644b88
parent 509505 0833244e1e018c2daf196eda87cf4ff97db148fc
child 509507 248ed24187a21a78acf2243ee8a1b4d247e46c36
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1342012
milestone66.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 1342012 - Support import from timeout handlers by associating the initiating script with the compiled JSScript r=smaug
dom/base/nsGlobalWindowInner.cpp
dom/base/nsIScriptTimeoutHandler.h
dom/base/nsJSTimeoutHandler.cpp
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
dom/script/moz.build
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -229,16 +229,17 @@
 #include "mozilla/dom/HashChangeEvent.h"
 #include "mozilla/dom/IntlUtils.h"
 #include "mozilla/dom/PopStateEvent.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/PopupBlockedEvent.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
+#include "mozilla/dom/LoadedScript.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/InstallTriggerBinding.h"
 #include "mozilla/dom/Report.h"
 #include "mozilla/dom/ReportingObserver.h"
@@ -5978,36 +5979,42 @@ bool nsGlobalWindowInner::RunTimeoutHand
   bool abortIntervalHandler = false;
   nsCOMPtr<nsIScriptTimeoutHandler> handler(
       do_QueryInterface(timeout->mScriptHandler));
   if (handler) {
     RefPtr<Function> callback = handler->GetCallback();
 
     if (!callback) {
       // Evaluate the timeout expression.
-      const nsAString& script = handler->GetHandlerText();
-
       const char* filename = nullptr;
       uint32_t lineNo = 0, dummyColumn = 0;
       handler->GetLocation(&filename, &lineNo, &dummyColumn);
 
       // New script entry point required, due to the "Create a script" sub-step
       // of
       // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
       nsAutoMicroTask mt;
       AutoEntryScript aes(this, reason, true);
       JS::CompileOptions options(aes.cx());
       options.setFileAndLine(filename, lineNo);
       options.setNoScriptRval(true);
       JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
       nsresult rv;
       {
         nsJSUtils::ExecutionContext exec(aes.cx(), global);
-        exec.Compile(options, script);
-        rv = exec.ExecScript();
+        rv = exec.Compile(options, handler->GetHandlerText());
+
+        if (rv == NS_OK) {
+          LoadedScript* initiatingScript = handler->GetInitiatingScript();
+          if (initiatingScript) {
+            initiatingScript->AssociateWithScript(exec.GetScript());
+          }
+
+          rv = exec.ExecScript();
+        }
       }
 
       if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE) {
         abortIntervalHandler = true;
       }
     } else {
       // Hold strong ref to ourselves while we call the callback.
       nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow*>(this));
--- a/dom/base/nsIScriptTimeoutHandler.h
+++ b/dom/base/nsIScriptTimeoutHandler.h
@@ -9,16 +9,17 @@
 #include "nsITimeoutHandler.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Maybe.h"
 
 namespace mozilla {
 namespace dom {
 class Function;
+class LoadedScript;
 }  // namespace dom
 }  // namespace mozilla
 
 #define NS_ISCRIPTTIMEOUTHANDLER_IID                 \
   {                                                  \
     0x53c8e80e, 0xcc78, 0x48bc, {                    \
       0xba, 0x63, 0x0c, 0xb9, 0xdb, 0xf7, 0x06, 0x34 \
     }                                                \
@@ -41,14 +42,17 @@ class nsIScriptTimeoutHandler : public n
   virtual const nsAString& GetHandlerText() = 0;
 
   // Get the location of the script.
   // Note: The memory pointed to by aFileName is owned by the
   // nsIScriptTimeoutHandler and should not be freed by the caller.
 
   // If we have a Function, get the arguments for passing to it.
   virtual const nsTArray<JS::Value>& GetArgs() = 0;
+
+  // If we have an expression, get the initiating script.
+  virtual mozilla::dom::LoadedScript* GetInitiatingScript() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptTimeoutHandler,
                               NS_ISCRIPTTIMEOUTHANDLER_IID)
 
 #endif  // nsIScriptTimeoutHandler_h___
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -6,16 +6,17 @@
 
 #include <algorithm>
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/dom/CSPEvalChecker.h"
 #include "mozilla/dom/FunctionBinding.h"
+#include "mozilla/dom/LoadedScript.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsGlobalWindow.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIScriptTimeoutHandler.h"
@@ -34,16 +35,17 @@ class nsJSScriptTimeoutHandler final : p
 
   nsJSScriptTimeoutHandler();
   // This will call SwapElements on aArguments with an empty array.
   nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow,
                            Function& aFunction,
                            nsTArray<JS::Heap<JS::Value>>&& aArguments,
                            ErrorResult& aError);
   nsJSScriptTimeoutHandler(JSContext* aCx, nsGlobalWindowInner* aWindow,
+                           LoadedScript* aInitiatingScript,
                            const nsAString& aExpression, bool* aAllowEval,
                            ErrorResult& aError);
   nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                            Function& aFunction,
                            nsTArray<JS::Heap<JS::Value>>&& aArguments);
   nsJSScriptTimeoutHandler(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                            const nsAString& aExpression, bool* aAllowEval,
                            ErrorResult& aRv);
@@ -58,16 +60,20 @@ class nsJSScriptTimeoutHandler final : p
 
   virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
                            uint32_t* aColumn) override {
     *aFileName = mFileName.get();
     *aLineNo = mLineNo;
     *aColumn = mColumn;
   }
 
+  virtual LoadedScript* GetInitiatingScript() override {
+    return mInitiatingScript;
+  }
+
   virtual void MarkForCC() override {
     if (mFunction) {
       mFunction->MarkForCC();
     }
   }
 
   void ReleaseJSObjects();
 
@@ -83,25 +89,31 @@ class nsJSScriptTimeoutHandler final : p
   uint32_t mLineNo;
   uint32_t mColumn;
   nsTArray<JS::Heap<JS::Value>> mArgs;
 
   // The expression to evaluate or function to call. If mFunction is non-null
   // it should be used, else use mExpr.
   nsString mExpr;
   RefPtr<Function> mFunction;
+
+  // Initiating script for use when evaluating mExpr on the main thread.
+  RefPtr<LoadedScript> mInitiatingScript;
 };
 
 // nsJSScriptTimeoutHandler
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFunction)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInitiatingScript)
   tmp->ReleaseJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
     nsAutoCString name("nsJSScriptTimeoutHandler");
     if (tmp->mFunction) {
       JSObject* obj = tmp->mFunction->CallablePreserveColor();
       JSFunction* fun =
           JS_GetObjectFunction(js::UncheckedUnwrapWithoutExpose(obj));
       if (fun && JS_GetFunctionId(fun)) {
@@ -129,16 +141,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   } else {
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsJSScriptTimeoutHandler,
                                       tmp->mRefCnt.get())
   }
 
   if (tmp->mFunction) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
   }
+  if (tmp->mInitiatingScript) {
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInitiatingScript)
+  }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
   for (uint32_t i = 0; i < tmp->mArgs.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i])
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
@@ -162,22 +177,24 @@ nsJSScriptTimeoutHandler::nsJSScriptTime
     // don't let a timer be scheduled on such a window.
     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     return;
   }
 
   Init(aCx, std::move(aArguments));
 }
 
-nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(JSContext* aCx,
-                                                   nsGlobalWindowInner* aWindow,
-                                                   const nsAString& aExpression,
-                                                   bool* aAllowEval,
-                                                   ErrorResult& aError)
-    : mLineNo(0), mColumn(0), mExpr(aExpression) {
+nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler(
+    JSContext* aCx, nsGlobalWindowInner* aWindow,
+    LoadedScript* aInitiatingScript, const nsAString& aExpression,
+    bool* aAllowEval, ErrorResult& aError)
+    : mLineNo(0),
+      mColumn(0),
+      mExpr(aExpression),
+      mInitiatingScript(aInitiatingScript) {
   if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
     // This window was already closed, or never properly initialized,
     // don't let a timer be scheduled on such a window.
     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     return;
   }
 
   aError =
@@ -255,19 +272,21 @@ already_AddRefed<nsIScriptTimeoutHandler
   RefPtr<nsJSScriptTimeoutHandler> handler = new nsJSScriptTimeoutHandler(
       aCx, aWindow, aFunction, std::move(args), aError);
   return aError.Failed() ? nullptr : handler.forget();
 }
 
 already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(
     JSContext* aCx, nsGlobalWindowInner* aWindow, const nsAString& aExpression,
     ErrorResult& aError) {
+  LoadedScript* script = ScriptLoader::GetActiveScript(aCx);
+
   bool allowEval = false;
   RefPtr<nsJSScriptTimeoutHandler> handler = new nsJSScriptTimeoutHandler(
-      aCx, aWindow, aExpression, &allowEval, aError);
+      aCx, aWindow, script, aExpression, &allowEval, aError);
   if (aError.Failed() || !allowEval) {
     return nullptr;
   }
 
   return handler.forget();
 }
 
 already_AddRefed<nsIScriptTimeoutHandler> NS_CreateJSTimeoutHandler(
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -2565,16 +2565,25 @@ nsresult ScriptLoader::EvaluateScript(Sc
     // call functions of a script for which we are recording the bytecode.
     LOG(("ScriptLoadRequest (%p): ScriptLoader = %p", aRequest, this));
     MaybeTriggerBytecodeEncoding();
   }
 
   return rv;
 }
 
+/* static */ LoadedScript* ScriptLoader::GetActiveScript(JSContext* aCx) {
+  JS::Value value = JS::GetScriptedCallerPrivate(aCx);
+  if (value.isUndefined()) {
+    return nullptr;
+  }
+
+  return static_cast<LoadedScript*>(value.toPrivate());
+}
+
 void ScriptLoader::RegisterForBytecodeEncoding(ScriptLoadRequest* aRequest) {
   MOZ_ASSERT(aRequest->mCacheInfo);
   MOZ_ASSERT(aRequest->mScript);
   mBytecodeEncodingQueue.AppendElement(aRequest);
 }
 
 void ScriptLoader::LoadEventFired() {
   mLoadEventFired = true;
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -37,16 +37,17 @@ template <typename UnitT>
 class SourceText;
 
 }  // namespace JS
 
 namespace mozilla {
 namespace dom {
 
 class AutoJSAPI;
+class LoadedScript;
 class ModuleLoadRequest;
 class ModuleScript;
 class ScriptLoadHandler;
 class ScriptRequestProcessor;
 
 //////////////////////////////////////////////////////////////
 // Script loader implementation
 //////////////////////////////////////////////////////////////
@@ -310,16 +311,22 @@ class ScriptLoader final : public nsISup
    */
   void Destroy() { GiveUpBytecodeEncoding(); }
 
   void StartDynamicImport(ModuleLoadRequest* aRequest);
   void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
   void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
                            nsresult aResult);
 
+  /*
+   * Get the currently active script. This is used as the initiating script when
+   * executing timeout handler scripts.
+   */
+  static LoadedScript* GetActiveScript(JSContext* aCx);
+
  private:
   virtual ~ScriptLoader();
 
   void EnsureModuleHooksInitialized();
 
   ScriptLoadRequest* CreateLoadRequest(
       ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
       nsIPrincipal* aTriggeringPrincipal, mozilla::CORSMode aCORSMode,
--- a/dom/script/moz.build
+++ b/dom/script/moz.build
@@ -13,16 +13,17 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'dom'
 
 EXPORTS += [
     'nsIScriptElement.h',
 ]
 
 EXPORTS.mozilla.dom += [
+    'LoadedScript.h',
     'ScriptElement.h',
     'ScriptLoader.h',
     'ScriptLoadRequest.h',
     'ScriptSettings.h',
 ]
 
 UNIFIED_SOURCES += [
     'LoadedScript.cpp',
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-base-url-external-classic.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected-inline-event-handlers should successfully import]
     expected: FAIL
 
   [inline-event-handlers-UA-code should successfully import]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-base-url-external-module.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected-inline-event-handlers should successfully import]
     expected: FAIL
 
   [inline-event-handlers-UA-code should successfully import]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-base-url-inline-classic.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected inline event handlers should successfully import]
     expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-base-url-inline-module.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected inline event handlers should successfully import]
     expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-classic.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected inline event handlers should successfully import]
     expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
     expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
@@ -1,10 +1,7 @@
 [string-compilation-module.html]
-  [setTimeout should successfully import]
-    expected: FAIL
-
   [reflected inline event handlers should successfully import]
     expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
     expected: FAIL