Bug 1570147 - Add SpecialPowers API for calling drawSnapshot in the parent and returning the results. r=kmag
☠☠ backed out by 1f3fcdc28dfd ☠ ☠
authorMatt Woodrow <mwoodrow@mozilla.com>
Thu, 15 Aug 2019 06:09:51 +0000
changeset 488188 4b4feec0a4e28887f61a53ec5e5d423db2cfb251
parent 488187 52aec1da83945cfef108316fc233e0bfd5f622ee
child 488189 4d528be8bae4048f06fb4c2333a6e852146f25d0
push id36437
push userncsoregi@mozilla.com
push dateThu, 15 Aug 2019 19:33:18 +0000
treeherdermozilla-central@44aac6fc3352 [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
@@ -1358,16 +1358,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
@@ -1396,33 +1407,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;
       }