Bug 1570147 - Add SpecialPowers API for calling drawSnapshot in the parent and returning the results. r=kmag
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 16 Aug 2019 03:12:52 +0000
changeset 488422 19443ebbd552f2d4240cbc5fc1a8a6af815f5323
parent 488421 25abe5d28a6a8d7f70f05449e6a850379877d159
child 488423 7ef0d6f34119ec4bb8b6f216c9a205bc93d65475
push id36443
push userccoroiu@mozilla.com
push dateFri, 16 Aug 2019 09:48:15 +0000
treeherdermozilla-central@5d4cbfe103bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1570147
milestone70.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 1570147 - Add SpecialPowers API for calling drawSnapshot in the parent and returning the results. r=kmag Differential Revision: https://phabricator.services.mozilla.com/D41826
testing/specialpowers/content/SpecialPowersAPI.jsm
testing/specialpowers/content/SpecialPowersAPIParent.jsm
--- a/testing/specialpowers/content/SpecialPowersAPI.jsm
+++ b/testing/specialpowers/content/SpecialPowersAPI.jsm
@@ -1411,16 +1411,27 @@ class SpecialPowersAPI extends JSWindowA
         obj = obj[p];
       } else {
         return null;
       }
     }
     return obj;
   }
 
+  _browsingContextForTarget(target) {
+    if (BrowsingContext.isInstance(target)) {
+      return target;
+    }
+    if (Element.isInstance(target)) {
+      return target.browsingContext;
+    }
+
+    return BrowsingContext.getFromWindow(target);
+  }
+
   /**
    * Runs a task in the context of the given frame, and returns a
    * promise which resolves to the return value of that task.
    *
    * The given frame may be in-process or out-of-process. Either way,
    * the task will run asynchronously, in a sandbox with access to the
    * frame's content window via its `content` global. Any arguments
    * passed will be copied via structured clone, as will its return
@@ -1449,33 +1460,38 @@ class SpecialPowersAPI extends JSWindowA
    * @returns {Promise<any>}
    *        A promise which resolves to the return value of the task, or
    *        which rejects if the task raises an exception. As this is
    *        being written, the rejection value will always be undefined
    *        in the cases where the task throws an error, though that may
    *        change in the future.
    */
   spawn(target, args, task) {
-    let browsingContext;
-    if (BrowsingContext.isInstance(target)) {
-      browsingContext = target;
-    } else if (Element.isInstance(target)) {
-      browsingContext = target.browsingContext;
-    } else {
-      browsingContext = BrowsingContext.getFromWindow(target);
-    }
+    let browsingContext = this._browsingContextForTarget(target);
 
     return this.sendQuery("Spawn", {
       browsingContext,
       args,
       task: String(task),
       caller: SpecialPowersSandbox.getCallerInfo(Components.stack.caller),
     });
   }
 
+  snapshotContext(target, rect, background) {
+    let browsingContext = this._browsingContextForTarget(target);
+
+    return this.sendQuery("Snapshot", {
+      browsingContext,
+      rect,
+      background,
+    }).then(imageData => {
+      return this.contentWindow.createImageBitmap(imageData);
+    });
+  }
+
   _spawnTask(task, args, caller, taskId) {
     let sb = new SpecialPowersSandbox(null, data => {
       this.sendAsyncMessage("ProxiedAssert", { taskId, data });
     });
 
     sb.sandbox.SpecialPowers = this;
     Object.defineProperty(sb.sandbox, "content", {
       get: () => {
--- a/testing/specialpowers/content/SpecialPowersAPIParent.jsm
+++ b/testing/specialpowers/content/SpecialPowersAPIParent.jsm
@@ -12,16 +12,17 @@ var { XPCOMUtils } = ChromeUtils.import(
 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   ExtensionData: "resource://gre/modules/Extension.jsm",
   ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
   PerTestCoverageUtils: "resource://testing-common/PerTestCoverageUtils.jsm",
   ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm",
   SpecialPowersSandbox: "resource://specialpowers/SpecialPowersSandbox.jsm",
+  HiddenFrame: "resource://gre/modules/HiddenFrame.jsm",
 });
 
 class SpecialPowersError extends Error {
   get name() {
     return "SpecialPowersError";
   }
 }
 
@@ -854,16 +855,38 @@ class SpecialPowersAPIParent extends JSW
 
         return spParent
           .sendQuery("Spawn", { task, args, caller, taskId })
           .finally(() => {
             spParent._taskActors.delete(taskId);
           });
       }
 
+      case "Snapshot": {
+        let { browsingContext, rect, background } = aMessage.data;
+
+        return browsingContext.currentWindowGlobal
+          .drawSnapshot(rect, 1.0, background)
+          .then(async image => {
+            let hiddenFrame = new HiddenFrame();
+            let win = await hiddenFrame.get();
+
+            let canvas = win.document.createElement("canvas");
+            canvas.width = image.width;
+            canvas.height = image.height;
+
+            const ctx = canvas.getContext("2d");
+            ctx.drawImage(image, 0, 0);
+
+            let data = ctx.getImageData(0, 0, image.width, image.height);
+            hiddenFrame.destroy();
+            return data;
+          });
+      }
+
       case "ProxiedAssert": {
         let { taskId, data } = aMessage.data;
         let actor = this._taskActors.get(taskId);
 
         actor.sendAsyncMessage("Assert", data);
         return undefined;
       }