Merge mozilla-central to inbound. a=merge CLOSED TREE
authorBrindusan Cristian <cbrindusan@mozilla.com>
Mon, 09 Jul 2018 00:45:10 +0300
changeset 815582 e02ce918e160e86ff76bd8e801dbabb354de9c88
parent 815581 f4076daf69adbe4c051a0dc7958fca4d0d538424 (current diff)
parent 815516 ffb7b5015fc331bdc4c5e6ab52b9de669faa8864 (diff)
child 815583 ae04f1ff217f85049f1d157517241274d15ab5bb
push id115563
push userbmo:ntim.bugs@gmail.com
push dateMon, 09 Jul 2018 12:45:57 +0000
reviewersmerge
milestone63.0a1
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -41,127 +41,134 @@ class TestFirefoxRefresh(MarionetteTestC
             "username",
             "password"
           );
           Services.logins.addLogin(myLogin)
         """, script_args=(self._username, self._password))
 
     def createBookmarkInMenu(self):
         error = self.runAsyncCode("""
-          let url = arguments[0];
-          let title = arguments[1];
+          // let url = arguments[0];
+          // let title = arguments[1];
+          // let resolve = arguments[arguments.length - 1];
+          let [url, title, resolve] = arguments;
           PlacesUtils.bookmarks.insert({
             parentGuid: PlacesUtils.bookmarks.menuGuid, url, title
-          }).then(() => marionetteScriptFinished(false), marionetteScriptFinished);
+          }).then(() => resolve(false), resolve);
         """, script_args=(self._bookmarkURL, self._bookmarkText))
         if error:
             print(error)
 
     def createBookmarksOnToolbar(self):
         error = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           let children = [];
           for (let i = 1; i <= 5; i++) {
             children.push({url: `about:rights?p=${i}`, title: `Bookmark ${i}`});
           }
           PlacesUtils.bookmarks.insertTree({
             guid: PlacesUtils.bookmarks.toolbarGuid,
             children
-          }).then(() => marionetteScriptFinished(false), marionetteScriptFinished);
+          }).then(() => resolve(false), resolve);
         """)
         if error:
             print(error)
 
     def createHistory(self):
         error = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           PlacesUtils.history.insert({
             url: arguments[0],
             title: arguments[1],
             visits: [{
               date: new Date(Date.now() - 5000),
               referrer: "about:mozilla"
             }]
-          }).then(() => marionetteScriptFinished(false),
-                  ex => marionetteScriptFinished("Unexpected error in adding visit: " + ex));
+          }).then(() => resolve(false),
+                  ex => resolve("Unexpected error in adding visit: " + ex));
         """, script_args=(self._historyURL, self._historyTitle))
         if error:
             print(error)
 
     def createFormHistory(self):
         error = self.runAsyncCode("""
           let updateDefinition = {
             op: "add",
             fieldname: arguments[0],
             value: arguments[1],
             firstUsed: (Date.now() - 5000) * 1000,
           };
           let finished = false;
+          let resolve = arguments[arguments.length - 1];
           global.FormHistory.update(updateDefinition, {
             handleError(error) {
               finished = true;
-              marionetteScriptFinished(error);
+              resolve(error);
             },
             handleCompletion() {
               if (!finished) {
-                marionetteScriptFinished(false);
+                resolve(false);
               }
             }
           });
         """, script_args=(self._formHistoryFieldName, self._formHistoryValue))
         if error:
             print(error)
 
     def createFormAutofill(self):
         if not self._formAutofillAvailable:
             return
         self._formAutofillAddressGuid = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           const TEST_ADDRESS_1 = {
             "given-name": "John",
             "additional-name": "R.",
             "family-name": "Smith",
             organization: "World Wide Web Consortium",
             "street-address": "32 Vassar Street\\\nMIT Room 32-G524",
             "address-level2": "Cambridge",
             "address-level1": "MA",
             "postal-code": "02139",
             country: "US",
             tel: "+15195555555",
             email: "user@example.com",
           };
           return global.formAutofillStorage.initialize().then(() => {
             return global.formAutofillStorage.addresses.add(TEST_ADDRESS_1);
-          }).then(marionetteScriptFinished);
+          }).then(resolve);
         """)
 
     def createCookie(self):
         self.runCode("""
           // Expire in 15 minutes:
           let expireTime = Math.floor(Date.now() / 1000) + 15 * 60;
           Services.cookies.add(arguments[0], arguments[1], arguments[2], arguments[3],
                                true, false, false, expireTime);
         """, script_args=(self._cookieHost, self._cookiePath, self._cookieName, self._cookieValue))
 
     def createSession(self):
         self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           const COMPLETE_STATE = Ci.nsIWebProgressListener.STATE_STOP +
                                  Ci.nsIWebProgressListener.STATE_IS_NETWORK;
           let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
           let expectedURLs = Array.from(arguments[0])
           gBrowser.addTabsProgressListener({
             onStateChange(browser, webprogress, request, flags, status) {
               try {
                 request && request.QueryInterface(Ci.nsIChannel);
               } catch (ex) {}
               let uriLoaded = request.originalURI && request.originalURI.spec;
               if ((flags & COMPLETE_STATE == COMPLETE_STATE) && uriLoaded &&
                   expectedURLs.includes(uriLoaded)) {
                 TabStateFlusher.flush(browser).then(function() {
                   expectedURLs.splice(expectedURLs.indexOf(uriLoaded), 1);
                   if (!expectedURLs.length) {
                     gBrowser.removeTabsProgressListener(this);
-                    marionetteScriptFinished();
+                    resolve();
                   }
                 });
               }
             }
           });
           let expectedTabs = new Set();
           for (let url of expectedURLs) {
             expectedTabs.add(gBrowser.addTab(url));
@@ -174,21 +181,22 @@ class TestFirefoxRefresh(MarionetteTestC
             }
           }
         """, script_args=(self._expectedURLs,))  # NOQA: E501
 
     def createSync(self):
         # This script will write an entry to the login manager and create
         # a signedInUser.json in the profile dir.
         self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           Cu.import("resource://gre/modules/FxAccountsStorage.jsm");
           let storage = new FxAccountsStorageManager();
           let data = {email: "test@test.com", uid: "uid", keyFetchToken: "top-secret"};
           storage.initialize(data);
-          storage.finalize().then(marionetteScriptFinished);
+          storage.finalize().then(resolve);
         """)
 
     def checkPassword(self):
         loginInfo = self.marionette.execute_script("""
           let ary = Services.logins.findLogins({},
             "test.marionette.mozilla.com",
             "http://test.marionette.mozilla.com/some/form/",
             null, {});
@@ -201,98 +209,102 @@ class TestFirefoxRefresh(MarionetteTestC
         loginCount = self.marionette.execute_script("""
           return Services.logins.getAllLogins().length;
         """)
         # Note that we expect 2 logins - one from us, one from sync.
         self.assertEqual(loginCount, 2, "No other logins are present")
 
     def checkBookmarkInMenu(self):
         titleInBookmarks = self.runAsyncCode("""
-          let url = arguments[0];
+          let [url, resolve] = arguments;
           PlacesUtils.bookmarks.fetch({url}).then(
-            bookmark => marionetteScriptFinished(bookmark ? bookmark.title : ""),
-            ex => marionetteScriptFinished(ex)
+            bookmark => resolve(bookmark ? bookmark.title : ""),
+            ex => resolve(ex)
           );
         """, script_args=(self._bookmarkURL,))
         self.assertEqual(titleInBookmarks, self._bookmarkText)
 
     def checkBookmarkToolbarVisibility(self):
         toolbarVisible = self.marionette.execute_script("""
           const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
           let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
           return xulStore.getValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed")
         """)
         self.assertEqual(toolbarVisible, "false")
 
     def checkHistory(self):
         historyResult = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           PlacesUtils.history.fetch(arguments[0]).then(pageInfo => {
             if (!pageInfo) {
-              marionetteScriptFinished("No visits found");
+              resolve("No visits found");
             } else {
-              marionetteScriptFinished(pageInfo);
+              resolve(pageInfo);
             }
           }).catch(e => {
-            marionetteScriptFinished("Unexpected error in fetching page: " + e);
+            resolve("Unexpected error in fetching page: " + e);
           });
         """, script_args=(self._historyURL,))
         if type(historyResult) == str:
             self.fail(historyResult)
             return
 
         self.assertEqual(historyResult['title'], self._historyTitle)
 
     def checkFormHistory(self):
         formFieldResults = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           let results = [];
           global.FormHistory.search(["value"], {fieldname: arguments[0]}, {
             handleError(error) {
               results = error;
             },
             handleResult(result) {
               results.push(result);
             },
             handleCompletion() {
-              marionetteScriptFinished(results);
+              resolve(results);
             },
           });
         """, script_args=(self._formHistoryFieldName,))
         if type(formFieldResults) == str:
             self.fail(formFieldResults)
             return
 
         formFieldResultCount = len(formFieldResults)
         self.assertEqual(formFieldResultCount, 1,
                          "Should have exactly 1 entry for this field, got %d" %
                          formFieldResultCount)
         if formFieldResultCount == 1:
             self.assertEqual(
                 formFieldResults[0]['value'], self._formHistoryValue)
 
         formHistoryCount = self.runAsyncCode("""
+          let [resolve] = arguments;
           let count;
           let callbacks = {
             handleResult: rv => count = rv,
             handleCompletion() {
-              marionetteScriptFinished(count);
+              resolve(count);
             },
           };
           global.FormHistory.count({}, callbacks);
         """)
         self.assertEqual(formHistoryCount, 1,
                          "There should be only 1 entry in the form history")
 
     def checkFormAutofill(self):
         if not self._formAutofillAvailable:
             return
 
         formAutofillResults = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1];
           return global.formAutofillStorage.initialize().then(() => {
             return global.formAutofillStorage.addresses.getAll()
-          }).then(marionetteScriptFinished);
+          }).then(resolve);
         """,)
         if type(formAutofillResults) == str:
             self.fail(formAutofillResults)
             return
 
         formAutofillAddressCount = len(formAutofillResults)
         self.assertEqual(formAutofillAddressCount, 1,
                          "Should have exactly 1 saved address, got %d" % formAutofillAddressCount)
@@ -338,23 +350,24 @@ class TestFirefoxRefresh(MarionetteTestC
         # default browser dialog if it shows up.
         try:
             alert = self.marionette.switch_to_alert()
             alert.dismiss()
         except NoAlertPresentException:
             pass
 
         tabURIs = self.runAsyncCode("""
+          let resolve = arguments[arguments.length - 1]
           let mm = gBrowser.selectedBrowser.messageManager;
 
           let {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
           window.addEventListener("SSWindowStateReady", function testSSPostReset() {
             window.removeEventListener("SSWindowStateReady", testSSPostReset, false);
             Promise.all(gBrowser.browsers.map(b => TabStateFlusher.flush(b))).then(function() {
-              marionetteScriptFinished([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
+              resolve([... gBrowser.browsers].map(b => b.currentURI && b.currentURI.spec));
             });
           }, false);
 
           let fs = function() {
             if (content.document.readyState === "complete") {
               content.document.getElementById("errorTryAgain").click();
             } else {
               content.window.addEventListener("load", function(event) {
@@ -365,28 +378,29 @@ class TestFirefoxRefresh(MarionetteTestC
 
           mm.loadFrameScript("data:application/javascript,(" + fs.toString() + ")()", true);
         """)  # NOQA: E501
         self.assertSequenceEqual(tabURIs, self._expectedURLs)
 
     def checkSync(self, hasMigrated):
         result = self.runAsyncCode("""
           Cu.import("resource://gre/modules/FxAccountsStorage.jsm");
+          let resolve = arguments[arguments.length - 1];
           let prefs = new global.Preferences("services.sync.");
           let storage = new FxAccountsStorageManager();
           let result = {};
           storage.initialize();
           storage.getAccountData().then(data => {
             result.accountData = data;
             return storage.finalize();
           }).then(() => {
             result.prefUsername = prefs.get("username");
-            marionetteScriptFinished(result);
+            resolve(result);
           }).catch(err => {
-            marionetteScriptFinished(err.toString());
+            resolve(err.toString());
           });
         """)
         if type(result) != dict:
             self.fail(result)
             return
         self.assertEqual(result["accountData"]["email"], "test@test.com")
         self.assertEqual(result["accountData"]["uid"], "uid")
         self.assertEqual(result["accountData"]["keyFetchToken"], "top-secret")
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -151,16 +151,17 @@ nsImageFrame::CreateContinuingFrame(nsIP
 
 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
 
 nsImageFrame::nsImageFrame(ComputedStyle* aStyle, ClassID aID, Kind aKind)
   : nsAtomicContainerFrame(aStyle, aID)
   , mComputedSize(0, 0)
   , mIntrinsicRatio(0, 0)
   , mKind(aKind)
+  , mContentURLRequestRegistered(false)
   , mDisplayingIcon(false)
   , mFirstFrameComplete(false)
   , mReflowCallbackPosted(false)
   , mForceSyncDecoding(false)
 {
   EnableVisibilityTracking();
 
   // We assume our size is not constrained and we haven't gotten an
@@ -219,25 +220,28 @@ nsImageFrame::DestroyFrom(nsIFrame* aDes
   // This causes the nsImageMap to unregister itself as
   // a DOM listener.
   DisconnectMap();
 
   MOZ_ASSERT(mListener);
 
   if (mKind == Kind::ImageElement) {
     MOZ_ASSERT(!mContentURLRequest);
+    MOZ_ASSERT(!mContentURLRequestRegistered);
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
     MOZ_ASSERT(imageLoader);
 
     // Notify our image loading content that we are going away so it can
     // deregister with our refresh driver.
     imageLoader->FrameDestroyed(this);
     imageLoader->RemoveNativeObserver(mListener);
   } else {
     if (mContentURLRequest) {
+      nsLayoutUtils::DeregisterImageRequest(
+        PresContext(), mContentURLRequest, &mContentURLRequestRegistered);
       mContentURLRequest->Cancel(NS_BINDING_ABORTED);
     }
   }
 
   // set the frame to null so we don't send messages to a dead object.
   mListener->SetFrame(nullptr);
   mListener = nullptr;
 
@@ -311,38 +315,63 @@ nsImageFrame::Init(nsIContent* aContent,
     // We have a PresContext now, so we need to notify the image content node
     // that it can register images.
     imageLoader->FrameCreated(this);
   } else {
     if (auto* proxy = StyleContent()->ContentAt(0).GetImage()) {
       proxy->Clone(mListener,
                    mContent->OwnerDoc(),
                    getter_AddRefs(mContentURLRequest));
-      // Make sure we get the intrinsic size and such ASAP if available.
-      if (SizeIsAvailable(mContentURLRequest)) {
-        nsCOMPtr<imgIContainer> image;
-        mContentURLRequest->GetImage(getter_AddRefs(image));
-        OnSizeAvailable(mContentURLRequest, image);
-      }
+      SetupForContentURLRequest();
     }
   }
 
   // Give image loads associated with an image frame a small priority boost.
   if (nsCOMPtr<imgIRequest> currentRequest = GetCurrentRequest()) {
     uint32_t categoryToBoostPriority = imgIRequest::CATEGORY_FRAME_INIT;
 
     // Increase load priority further if intrinsic size might be important for layout.
     if (!HaveSpecifiedSize(StylePosition())) {
       categoryToBoostPriority |= imgIRequest::CATEGORY_SIZE_QUERY;
     }
 
     currentRequest->BoostPriority(categoryToBoostPriority);
   }
 }
 
+void
+nsImageFrame::SetupForContentURLRequest()
+{
+  MOZ_ASSERT(mKind != Kind::ImageElement);
+  if (!mContentURLRequest) {
+    return;
+  }
+
+  uint32_t status = 0;
+  nsresult rv = mContentURLRequest->GetImageStatus(&status);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  if (status & imgIRequest::STATUS_SIZE_AVAILABLE) {
+    nsCOMPtr<imgIContainer> image;
+    mContentURLRequest->GetImage(getter_AddRefs(image));
+    OnSizeAvailable(mContentURLRequest, image);
+  }
+
+  if (status & imgIRequest::STATUS_FRAME_COMPLETE) {
+    mFirstFrameComplete = true;
+  }
+
+  if (status & imgIRequest::STATUS_IS_ANIMATED) {
+    nsLayoutUtils::RegisterImageRequest(
+        PresContext(), mContentURLRequest, &mContentURLRequestRegistered);
+  }
+}
+
 static void
 ScaleIntrinsicSizeForDensity(nsIContent& aContent, nsSize& aSize)
 {
   auto* image = HTMLImageElement::FromNode(aContent);
   if (!image) {
     return;
   }
 
@@ -589,16 +618,22 @@ nsImageFrame::Notify(imgIRequest* aReque
   if (aType == imgINotificationObserver::FRAME_UPDATE) {
     return OnFrameUpdate(aRequest, aRect);
   }
 
   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
     mFirstFrameComplete = true;
   }
 
+  if (aType == imgINotificationObserver::IS_ANIMATED &&
+      mKind != Kind::ImageElement) {
+    nsLayoutUtils::RegisterImageRequest(
+        PresContext(), mContentURLRequest, &mContentURLRequestRegistered);
+  }
+
   if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t imgStatus;
     aRequest->GetImageStatus(&imgStatus);
     nsresult status =
         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnLoadComplete(aRequest, status);
   }
 
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -101,16 +101,17 @@ public:
   virtual nsresult AttributeChanged(int32_t aNameSpaceID,
                                     nsAtom* aAttribute,
                                     int32_t aModType) override;
 
   void OnVisibilityChange(Visibility aNewVisibility,
                           const Maybe<OnNonvisible>& aNonvisibleAction = Nothing()) override;
 
   void ResponsiveContentDensityChanged();
+  void SetupForContentURLRequest();
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsAtomicContainerFrame::IsFrameOfType(aFlags &
@@ -372,16 +373,17 @@ private:
 
   nsCOMPtr<imgIContainer> mImage;
   nsCOMPtr<imgIContainer> mPrevImage;
   nsSize mComputedSize;
   mozilla::IntrinsicSize mIntrinsicSize;
   nsSize mIntrinsicRatio;
 
   const Kind mKind;
+  bool mContentURLRequestRegistered;
   bool mDisplayingIcon;
   bool mFirstFrameComplete;
   bool mReflowCallbackPosted;
   bool mForceSyncDecoding;
 
   static nsIIOService* sIOService;
 
   /* loading / broken image icon support */
--- a/testing/awsy/awsy/awsy_test_case.py
+++ b/testing/awsy/awsy/awsy_test_case.py
@@ -139,21 +139,22 @@ class AwsyTestCase(MarionetteTestCase):
     def do_full_gc(self):
         """Performs a full garbage collection cycle and returns when it is finished.
 
         Returns True on success and False on failure.
         """
         # NB: we could do this w/ a signal or the fifo queue too
         self.logger.info("starting gc...")
         gc_script = """
+            let [resolve] = arguments;
             Cu.import("resource://gre/modules/Services.jsm");
             Services.obs.notifyObservers(null, "child-mmu-request", null);
 
             let memMgrSvc = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
-            memMgrSvc.minimizeMemoryUsage(() => marionetteScriptFinished("gc done!"));
+            memMgrSvc.minimizeMemoryUsage(() => {resolve("gc done!");});
             """
         result = None
         try:
             result = self.marionette.execute_async_script(
                 gc_script, script_timeout=180000)
         except JavascriptException, e:
             self.logger.error("GC JavaScript error: %s" % e)
         except ScriptTimeoutException:
@@ -182,20 +183,21 @@ class AwsyTestCase(MarionetteTestCase):
         # separator \ and escape it to prevent it from being
         # interpreted as an escape character.
         if sys.platform.startswith('win'):
             checkpoint_path = (checkpoint_path.
                                replace('\\', '\\\\').
                                replace('/', '\\\\'))
 
         checkpoint_script = r"""
+            let [resolve] = arguments;
             let dumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
             dumper.dumpMemoryReportsToNamedFile(
                 "%s",
-                () => marionetteScriptFinished("memory report done!"),
+                () => resolve("memory report done!"),
                 null,
                 /* anonymize */ false);
             """ % checkpoint_path
 
         checkpoint = None
         try:
             finished = self.marionette.execute_async_script(
                 checkpoint_script, script_timeout=60000)
--- a/testing/marionette/client/docs/basics.rst
+++ b/testing/marionette/client/docs/basics.rst
@@ -163,23 +163,24 @@ functions. They accomplish what their na
 synchronous JavaScript, while the latter provides a callback mechanism for
 running asynchronous JavaScript:
 
 .. parsed-literal::
    result = client.execute_script("return arguments[0] + arguments[1];",
                                   script_args=[2, 3])
    assert result == 5
 
-The async method works the same way, except it won't return until a special
-`marionetteScriptFinished()` function is called:
+The async method works the same way, except it won't return until the
+`resolve()` function is called:
 
 .. parsed-literal::
    result = client.execute_async_script("""
+       let [resolve] = arguments;
        setTimeout(function() {
-         marionetteScriptFinished("all done");
+         resolve("all done");
        }, arguments[0]);
    """, script_args=[1000])
    assert result == "all done"
 
 Beware that running asynchronous scripts can potentially hang the program
 indefinitely if they are not written properly. It is generally a good idea to
 set a script timeout using :func:`~Marionette.timeout.script` and handling
 `ScriptTimeoutException`.
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1732,18 +1732,19 @@ class Marionette(object):
 
         Usage example:
 
         ::
 
             marionette.timeout.script = 10
             result = self.marionette.execute_async_script('''
               // this script waits 5 seconds, and then returns the number 1
+              let [resolve] = arguments;
               setTimeout(function() {
-                marionetteScriptFinished(1);
+                resolve(1);
               }, 5000);
             ''')
             assert result == 1
         """
         args = self._to_json(script_args)
         stack = traceback.extract_stack()
         frame = stack[-2:-1][0]  # grab the second-to-last frame
         filename = frame[0] if sys.platform == "win32" else os.path.relpath(frame[0])
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -23,40 +23,39 @@ XPCOMUtils.defineLazyGetter(this, "log",
 
 this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"];
 
 const ARGUMENTS = "__webDriverArguments";
 const CALLBACK = "__webDriverCallback";
 const COMPLETE = "__webDriverComplete";
 const DEFAULT_TIMEOUT = 10000; // ms
 const FINISH = "finish";
-const MARIONETTE_SCRIPT_FINISHED = "marionetteScriptFinished";
 
 /** @namespace */
 this.evaluate = {};
 
 /**
  * Evaluate a script in given sandbox.
  *
  * The the provided `script` will be wrapped in an anonymous function
  * with the `args` argument applied.
  *
  * The arguments provided by the `args<` argument are exposed
  * through the `arguments` object available in the script context,
  * and if the script is executed asynchronously with the `async`
  * option, an additional last argument that is synonymous to the
- * `marionetteScriptFinished` global is appended, and can be accessed
+ * name `resolve` is appended, and can be accessed
  * through `arguments[arguments.length - 1]`.
  *
  * The `timeout` option specifies the duration for how long the
  * script should be allowed to run before it is interrupted and aborted.
  * An interrupted script will cause a {@link ScriptTimeoutError} to occur.
  *
  * The `async` option indicates that the script will not return
- * until the `marionetteScriptFinished` global callback is invoked,
+ * until the `resolve` callback is invoked,
  * which is analogous to the last argument of the `arguments` object.
  *
  * The `file` option is used in error messages to provide information
  * on the origin script file in the local end.
  *
  * The `line` option is used in error messages, along with `filename`,
  * to provide the line number in the origin script file on the local end.
  *
@@ -68,19 +67,16 @@ this.evaluate = {};
  *     A sequence of arguments to call the script with.
  * @param {boolean=} [async=false] async
  *     Indicates if the script should return immediately or wait for
  *     the callback to be invoked before returning.
  * @param {string=} [file="dummy file"] file
  *     File location of the program in the client.
  * @param {number=} [line=0] line
  *     Line number of th eprogram in the client.
- * @param {string=} sandboxName
- *     Name of the sandbox.  Elevated system privileges, equivalent to
- *     chrome space, will be given if it is <tt>system</tt>.
  * @param {number=} [timeout=DEFAULT_TIMEOUT] timeout
  *     Duration in milliseconds before interrupting the script.
  *
  * @return {Promise}
  *     A promise that when resolved will give you the return value from
  *     the script.  Note that the return value requires serialisation before
  *     it can be sent to the client.
  *
@@ -89,17 +85,16 @@ this.evaluate = {};
  * @throws {ScriptTimeoutError}
  *   If the script was interrupted due to script timeout.
  */
 evaluate.sandbox = function(sb, script, args = [],
     {
       async = false,
       file = "dummy file",
       line = 0,
-      sandboxName = null,
       timeout = DEFAULT_TIMEOUT,
     } = {}) {
   let scriptTimeoutID, timeoutHandler, unloadHandler;
 
   let promise = new Promise((resolve, reject) => {
     let src = "";
     sb[COMPLETE] = resolve;
     timeoutHandler = () => reject(new ScriptTimeoutError(`Timed out after ${timeout} ms`));
@@ -117,22 +112,16 @@ evaluate.sandbox = function(sb, script, 
     // on the arguments object
     if (async) {
       sb[CALLBACK] = sb[COMPLETE];
       src += `${ARGUMENTS}.push(rv => ${CALLBACK}(rv));`;
     }
 
     src += `(function() { ${script} }).apply(null, ${ARGUMENTS})`;
 
-    // marionetteScriptFinished is not WebDriver conformant,
-    // hence it is only exposed to immutable sandboxes
-    if (sandboxName) {
-      sb[MARIONETTE_SCRIPT_FINISHED] = sb[CALLBACK];
-    }
-
     // timeout and unload handlers
     scriptTimeoutID = setTimeout(timeoutHandler, timeout);
     sb.window.onunload = unloadHandler;
 
     let res;
     try {
       res = Cu.evalInSandbox(src, sb, "1.8", file, line);
     } catch (e) {
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py
@@ -25,34 +25,38 @@ class TestAddons(MarionetteTestCase):
         self.reset_addons()
 
         super(TestAddons, self).tearDown()
 
     @property
     def all_addon_ids(self):
         with self.marionette.using_context("chrome"):
             addons = self.marionette.execute_async_script("""
+              let [resolve] = arguments;
               Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
               AddonManager.getAllAddons().then(function(addons) {
                 let ids = addons.map(x => x.id);
-                marionetteScriptFinished(ids);
+                resolve(ids);
               });
             """)
 
         return set(addons)
 
     def reset_addons(self):
         with self.marionette.using_context("chrome"):
             for addon in (self.all_addon_ids - self.preinstalled_addons):
                 addon_id = self.marionette.execute_async_script("""
+                  let [resolve] = arguments;
                   Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
                   return new Promise(await resolve => {
                     let addon = await AddonManager.getAddonByID(arguments[0]);
                     addon.uninstall();
-                    marionetteScriptFinished(addon.id);
+                    resolve(addon.id);
                   });
                 """, script_args=(addon,))
                 self.assertEqual(addon_id, addon,
                                  msg="Failed to uninstall {}".format(addon))
 
     def test_temporary_install_and_remove_unsigned_addon(self):
         addon_path = os.path.join(here, "webextension-unsigned.xpi")
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py
@@ -14,53 +14,53 @@ class TestExecuteAsyncContent(Marionette
     def setUp(self):
         super(TestExecuteAsyncContent, self).setUp()
         self.marionette.timeout.script = 1
 
     def test_execute_async_simple(self):
         self.assertEqual(1, self.marionette.execute_async_script("arguments[arguments.length-1](1);"))
 
     def test_execute_async_ours(self):
-        self.assertEqual(1, self.marionette.execute_async_script("marionetteScriptFinished(1);"))
+        self.assertEqual(1, self.marionette.execute_async_script("arguments[0](1);"))
 
     def test_execute_async_timeout(self):
         self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "var x = 1;")
 
     def test_execute_async_unique_timeout(self):
-        self.assertEqual(2, self.marionette.execute_async_script("setTimeout(function() {marionetteScriptFinished(2);}, 2000);", script_timeout=5000))
-        self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "setTimeout(function() {marionetteScriptFinished(3);}, 2000);")
+        self.assertEqual(2, self.marionette.execute_async_script("setTimeout(() => arguments[0](2), 2000);", script_timeout=5000))
+        self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "setTimeout(() => arguments[0](3), 2000);")
 
     def test_no_timeout(self):
         self.marionette.timeout.script = 10
         self.assertTrue(self.marionette.execute_async_script("""
             var callback = arguments[arguments.length - 1];
             setTimeout(function() { callback(true); }, 500);
             """))
 
     def test_execute_async_unload(self):
         self.marionette.timeout.script = 5
         unload = """
                 window.location.href = "about:blank";
                  """
         self.assertRaises(JavascriptException, self.marionette.execute_async_script, unload)
 
     def test_check_window(self):
-        self.assertTrue(self.marionette.execute_async_script("marionetteScriptFinished(window !=null && window != undefined);"))
+        self.assertTrue(self.marionette.execute_async_script("arguments[0](window != null && window != undefined);"))
 
     def test_same_context(self):
         var1 = 'testing'
         self.assertEqual(self.marionette.execute_script("""
             this.testvar = '{}';
             return this.testvar;
             """.format(var1)), var1)
         self.assertEqual(self.marionette.execute_async_script(
-            "marionetteScriptFinished(this.testvar);", new_sandbox=False), var1)
+            "arguments[0](this.testvar);", new_sandbox=False), var1)
 
     def test_execute_no_return(self):
-        self.assertEqual(self.marionette.execute_async_script("marionetteScriptFinished()"), None)
+        self.assertEqual(self.marionette.execute_async_script("arguments[0]()"), None)
 
     def test_execute_js_exception(self):
         try:
             self.marionette.execute_async_script("""
                 let a = 1;
                 foo(bar);
                 """)
             self.fail()
@@ -76,39 +76,41 @@ class TestExecuteAsyncContent(Marionette
             """)
             self.fail()
         except JavascriptException as e:
             self.assertIsNotNone(e.stacktrace)
             self.assertIn(os.path.relpath(__file__.replace(".pyc", ".py")), e.stacktrace)
 
     def test_script_finished(self):
         self.assertTrue(self.marionette.execute_async_script("""
-            marionetteScriptFinished(true);
+            arguments[0](true);
             """))
 
     def test_execute_permission(self):
         self.assertRaises(JavascriptException, self.marionette.execute_async_script, """
 let prefs = Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);
-marionetteScriptFinished(4);
+arguments[0](4);
 """)
 
     def test_sandbox_reuse(self):
         # Sandboxes between `execute_script()` invocations are shared.
         self.marionette.execute_async_script("this.foobar = [23, 42];"
-                                             "marionetteScriptFinished();")
+                                             "arguments[0]();")
         self.assertEqual(self.marionette.execute_async_script(
-            "marionetteScriptFinished(this.foobar);", new_sandbox=False), [23, 42])
+            "arguments[0](this.foobar);", new_sandbox=False), [23, 42])
 
     def test_sandbox_refresh_arguments(self):
         self.marionette.execute_async_script("this.foobar = [arguments[0], arguments[1]];"
-                                             "marionetteScriptFinished();",
+                                             "let resolve = "
+                                                 "arguments[arguments.length - 1];"
+                                             "resolve();",
                                              script_args=[23, 42])
         self.assertEqual(self.marionette.execute_async_script(
-            "marionetteScriptFinished(this.foobar);", new_sandbox=False),
+            "arguments[0](this.foobar);", new_sandbox=False),
                          [23, 42])
 
     # Functions defined in higher privilege scopes, such as the privileged
     # content frame script listener.js runs in, cannot be accessed from
     # content.  This tests that it is possible to introspect the objects on
     # `arguments` without getting permission defined errors.  This is made
     # possible because the last argument is always the callback/complete
     # function.
@@ -126,17 +128,17 @@ class TestExecuteAsyncChrome(TestExecute
         self.marionette.set_context("chrome")
 
     def test_execute_async_unload(self):
         pass
 
     def test_execute_permission(self):
         self.assertEqual(5, self.marionette.execute_async_script("""
 var c = Components.classes;
-marionetteScriptFinished(5);
+arguments[0](5);
 """))
 
     def test_execute_async_js_exception(self):
         # Javascript exceptions are not propagated in chrome code
         self.marionette.timeout.script = 0.2
         self.assertRaises(ScriptTimeoutException,
             self.marionette.execute_async_script, """
             var callback = arguments[arguments.length - 1];
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py
@@ -16,22 +16,23 @@ class TestExecuteIsolationContent(Marion
 
     def test_execute_async_isolate(self):
         # Results from one execute call that has timed out should not
         # contaminate a future call.
         multiplier = "*3" if self.content else "*1"
         self.marionette.timeout.script = 0.5
         self.assertRaises(ScriptTimeoutException,
                           self.marionette.execute_async_script,
-                          ("setTimeout(function() {{ marionetteScriptFinished(5{}); }}, 3000);"
+                          ("setTimeout(function() {{ arguments[0](5{}); }}, 3000);"
                               .format(multiplier)))
 
         self.marionette.timeout.script = 6
         result = self.marionette.execute_async_script("""
-        setTimeout(function() {{ marionetteScriptFinished(10{}); }}, 5000);
+        let [resolve] = arguments;
+        setTimeout(function() {{ resolve(10{}); }}, 5000);
         """.format(multiplier))
         self.assertEqual(result, 30 if self.content else 10)
 
 class TestExecuteIsolationChrome(TestExecuteIsolationContent):
     def setUp(self):
         super(TestExecuteIsolationChrome, self).setUp()
         self.marionette.set_context("chrome")
         self.content = False
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py
@@ -20,17 +20,17 @@ class TestExecuteSandboxes(MarionetteTes
             sandbox="system")
         self.assertEqual(result, 1)
 
     def test_execute_async_system_sandbox(self):
         # Test that "system" sandbox has elevated privileges in
         # execute_async_script.
         result = self.marionette.execute_async_script("""
             let result = Ci.nsIPermissionManager.ALLOW_ACTION;
-            marionetteScriptFinished(result);""",
+            arguments[0](result);""",
             sandbox="system")
         self.assertEqual(result, 1)
 
     def test_execute_switch_sandboxes(self):
         # Test that sandboxes are retained when switching between them
         # for execute_script.
         self.marionette.execute_script("foo = 1", sandbox="1")
         self.marionette.execute_script("foo = 2", sandbox="2")
@@ -54,26 +54,26 @@ class TestExecuteSandboxes(MarionetteTes
         foo = self.marionette.execute_script(
             "return foo", sandbox="2", new_sandbox=False)
         self.assertEqual(foo, 2)
 
     def test_execute_async_switch_sandboxes(self):
         # Test that sandboxes are retained when switching between them
         # for execute_async_script.
         self.marionette.execute_async_script(
-            "foo = 1; marionetteScriptFinished()", sandbox="1")
+            "foo = 1; arguments[0]();", sandbox="1")
         self.marionette.execute_async_script(
-            "foo = 2; marionetteScriptFinished()", sandbox='2')
+            "foo = 2; arguments[0]();", sandbox='2')
         foo = self.marionette.execute_async_script(
-            "marionetteScriptFinished(foo)",
+            "arguments[0](foo);",
             sandbox="1",
             new_sandbox=False)
         self.assertEqual(foo, 1)
         foo = self.marionette.execute_async_script(
-            "marionetteScriptFinished(foo)",
+            "arguments[0](foo);",
             sandbox="2",
             new_sandbox=False)
         self.assertEqual(foo, 2)
 
 
 class TestExecuteSandboxesChrome(TestExecuteSandboxes):
     def setUp(self):
         super(TestExecuteSandboxesChrome, self).setUp()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
@@ -118,17 +118,17 @@ class TestCloseWindow(WindowManagerMixin
         self.marionette.switch_to_window(self.start_tab)
 
         with self.marionette.using_context("chrome"):
             self.marionette.execute_async_script("""
               Components.utils.import("resource:///modules/BrowserWindowTracker.jsm");
 
               let win = BrowserWindowTracker.getTopWindow();
               win.addEventListener("TabBrowserDiscarded", ev => {
-                marionetteScriptFinished(true);
+                arguments[0](true);
               }, { once: true});
               win.gBrowser.discardBrowser(win.gBrowser.tabs[1].linkedBrowser);
             """)
 
         window_handles = self.marionette.window_handles
         window_handles.remove(self.start_tab)
         self.assertEqual(1, len(window_handles))
         self.marionette.switch_to_window(window_handles[0], focus=False)
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/places.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/places.py
@@ -27,39 +27,41 @@ class Places(BaseLib):
 
         :param url: The URL to Check
 
         :returns: True, if the URL is a bookmark
         """
         return self.marionette.execute_async_script("""
           Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
 
-          PlacesUtils.bookmarks.fetch({url: arguments[0]}).then(bm => {
-            marionetteScriptFinished(bm != null);
+          let [url, resolve] = arguments;
+          PlacesUtils.bookmarks.fetch({url}).then(bm => {
+            resolve(bm != null);
           });
         """, script_args=[url])
 
     def get_folder_ids_for_url(self, url):
         """Retrieve the folder ids where the given URL has been bookmarked in.
 
         :param url: URL of the bookmark
 
         :returns: List of folder ids
         """
         return self.marionette.execute_async_script("""
+          let [url, resolve] = arguments;
           Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
 
-          let folderGuids = []
+          let folderGuids = [];
 
           function onResult(bm) {
             folderGuids.push(bm.parentGuid);
           }
 
-          PlacesUtils.bookmarks.fetch({url: arguments[0]}, onResult).then(() => {
-            marionetteScriptFinished(folderGuids);
+          PlacesUtils.bookmarks.fetch({url}, onResult).then(() => {
+            resolve(folderGuids);
           });
         """, script_args=[url])
 
     def is_bookmark_star_button_ready(self):
         """Check if the status of the star-button is not updating.
 
         :returns: True, if the button is ready
         """
@@ -67,26 +69,27 @@ class Places(BaseLib):
           let button = window.BookmarkingUI;
 
           return button.status !== button.STATUS_UPDATING;
         """)
 
     def restore_default_bookmarks(self):
         """Restore the default bookmarks for the current profile."""
         retval = self.marionette.execute_async_script("""
+          let [resolve] = arguments;
           Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
           // Default bookmarks.html file is stored inside omni.jar,
           // so get it via a resource URI
           let defaultBookmarks = 'chrome://browser/locale/bookmarks.html';
 
           // Trigger the import of the default bookmarks
           BookmarkHTMLUtils.importFromURL(defaultBookmarks, { replace: true })
-                           .then(() => marionetteScriptFinished(true))
-                           .catch(() => marionetteScriptFinished(false));
+                           .then(() => resolve(true))
+                           .catch(() => resolve(false));
         """, script_timeout=10000)
 
         if not retval:
             raise MarionetteException("Restore Default Bookmarks failed")
 
     # Browser history related helpers #
 
     def get_all_urls_in_history(self):
@@ -106,21 +109,22 @@ class Places(BaseLib):
           root.containerOpen = false;
 
           return urls;
         """)
 
     def remove_all_history(self):
         """Remove all history items."""
         retval = self.marionette.execute_async_script("""
+            let [resolve] = arguments;
             Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
 
             PlacesUtils.history.clear()
-                       .then(() => marionetteScriptFinished(true))
-                       .catch(() => marionetteScriptFinished(false));
+                       .then(() => resolve(true))
+                       .catch(() => resolve(false));
         """, script_timeout=10000)
 
         if not retval:
             raise MarionetteException("Removing all history failed")
 
     def wait_for_visited(self, urls, callback):
         """Wait until all passed-in urls have been visited.
 
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
@@ -363,25 +363,27 @@ class SoftwareUpdate(BaseLib):
     def get_formatted_update_url(self, force=False):
         """Retrieve the formatted AUS update URL the update snippet is retrieved from.
 
         :param force: Boolean flag to force an update check
 
         :returns: The URL of the update snippet
         """
         url = self.marionette.execute_async_script("""
+          let resolve = arguments[arguments.length - 1];
           Components.utils.import("resource://gre/modules/UpdateUtils.jsm");
           let res = UpdateUtils.formatUpdateURL(arguments[0]);
+
           // Format the URL by replacing placeholders
           // In 56 we switched the method to be async.
           // For now, support both approaches.
           if (res.then) {
-            res.then(marionetteScriptFinished);
+            res.then(resolve);
           } else {
-            marionetteScriptFinished(res);
+            resolve(res);
           }
         """, script_args=[self.update_url])
 
         if force:
             if '?' in url:
                 url += '&'
             else:
                 url += '?'
--- a/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/utils.py
+++ b/testing/marionette/puppeteer/firefox/firefox_puppeteer/api/utils.py
@@ -48,33 +48,34 @@ class Utils(BaseLib):
 
         more: https://dxr.mozilla.org/mozilla-central/source/browser/modules/Sanitizer.jsm
 
         :param data_type: optional, Information specifying data to be sanitized
         """
 
         with self.marionette.using_context('chrome'):
             result = self.marionette.execute_async_script("""
+              let resolve = arguments[arguments.length - 1];
               var {Sanitizer} = Components.utils.import("resource:///modules/Sanitizer.jsm", {});
 
               var data_type = arguments[0];
 
               // Apply options for what to sanitize
               var itemsToClear = [];
               for (var pref of Object.keys(data_type)) {
                 if (data_type[pref]) {
                     itemsToClear.push(pref);
                 }
               };
 
               // Sanitize and wait for the promise to resolve
               Sanitizer.sanitize(itemsToClear).then(() => {
-                marionetteScriptFinished(true);
+                resolve(true);
               }, aError => {
-                marionetteScriptFinished(false);
+                resolve(false);
               });
             """, script_args=[data_type])
 
             if not result:
                 raise MarionetteException('Sanitizing of profile data failed.')
 
 
 class Permissions(BaseLib):