Index: native_client_sdk/src/build_tools/screenshot_extension/screenshot.js |
diff --git a/native_client_sdk/src/build_tools/screenshot_extension/screenshot.js b/native_client_sdk/src/build_tools/screenshot_extension/screenshot.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0ad8a6ad1c0483f5d2004eb0992814bfdc9f6e9b |
--- /dev/null |
+++ b/native_client_sdk/src/build_tools/screenshot_extension/screenshot.js |
@@ -0,0 +1,196 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+var screenshot = (function() { |
+ /** A map of id to pending callback. */ |
+ var callbackMap = {}; |
+ |
+ /** An array of queued requests. They will all be executed when the |
+ * background page injects screen code into this page |
+ */ |
+ var queuedRequests = []; |
+ |
+ /** The next id to assign. Used for mapping id to callback. */ |
+ var nextId = 0; |
+ |
+ /** This is set to true when the background page injects screenshot code into |
+ * this page |
+ */ |
+ var extensionInjected = false; |
+ |
+ /** Generate a new id, which maps to the given callbacks. |
+ * |
+ * @param {function(string)} onSuccess |
+ * @param {function(string)} onError |
+ * @return {number} The id. |
+ */ |
+ function addCallback(onSuccess, onError) { |
+ var id = nextId++; |
+ callbackMap[id] = [onSuccess, onError]; |
+ return id; |
+ } |
+ |
+ /** Call the callback mapped to |id|. |
+ * |
+ * @param {number} id |
+ * @param {boolean} success true to call the success callback, false for the |
+ * error callback. |
+ * @param {...} A variable number of arguments to pass to the callback. |
+ */ |
+ function callCallback(id, success) { |
+ var callbacks = callbackMap[id]; |
+ if (!callbacks) { |
+ console.log('Unknown id: ' + id); |
+ return; |
+ } |
+ |
+ delete callbackMap[id]; |
+ var callback = success ? callbacks[0] : callbacks[1]; |
+ if (callback) |
+ callback(Array.prototype.slice.call(arguments, 2)); |
+ } |
+ |
+ /** Post a message to take a screenshot. |
+ * |
+ * This message will be enqueued if the extension has not yet injected the |
+ * screenshot code. |
+ * |
+ * @param {number} id An id to associate with this request. When the |
+ * screenshot is complete, the background page will return |
+ * a result with this id. |
+ */ |
+ function postScreenshotMessage(id) { |
+ if (!extensionInjected) { |
+ queuedRequests.push(id); |
+ return; |
+ } |
+ |
+ window.postMessage({id: id, target: 'background'}, '*'); |
+ } |
+ |
+ /** Post all queued screenshot requests. |
+ * |
+ * Should only be called after the screenshot code has been injected by the |
+ * background page. |
+ */ |
+ function postQueuedRequests() { |
+ for (var i = 0; i < queuedRequests.length; ++i) { |
+ var id = queuedRequests[i]; |
+ postScreenshotMessage(id); |
+ } |
+ queuedRequests = []; |
+ } |
+ |
+ /** Predicate whether the extension has injected code yet. |
+ * |
+ * @return {boolean} |
+ */ |
+ function isInjected() { |
+ // NOTE: This attribute name must match the one in injected.js. |
+ return document.body && |
+ document.body.getAttribute('screenshot_extension_injected'); |
+ } |
+ |
+ /** Start an interval that polls for when the extension has injected code |
+ * into this page. |
+ * |
+ * The extension first adds a postMessage handler to listen for requests, |
+ * then adds an attribute to the body element. If we see this attribute, we |
+ * know the listener is ready. |
+ */ |
+ function pollForInjection() { |
+ var intervalId = window.setInterval(function() { |
+ if (!isInjected()) |
+ return; |
+ |
+ // Finally injected! |
+ window.clearInterval(intervalId); |
+ extensionInjected = true; |
+ postQueuedRequests(); |
+ }, 100); // Every 100ms. |
+ } |
+ |
+ // Add a postMessage listener for when the injected code returns a result |
+ // from the background page. |
+ window.addEventListener('message', function(event) { |
+ // If the message comes from another window, or is outbound (i.e. |
+ // event.data.target === 'background'), ignore it. |
+ if (event.source !== window || event.data.target !== 'page') |
+ return; |
+ |
+ var success = event.data.error === undefined; |
+ callCallback(event.data.id, success, event.data.data); |
+ }, false); |
+ |
+ if (isInjected()) |
+ extensionInjected = true; |
+ else |
+ pollForInjection(); |
+ |
+ // Public functions. |
+ |
+ /** Capture the current visible area of the tab as a PNG. |
+ * |
+ * If the request succeeds, |onSuccess| will be called with one parameter: |
+ * the image encoded as a data URL. |
+ * |
+ * If the request fails, |onError| will be called with one parameter: an |
+ * informational error message. |
+ * |
+ * @param {function(string)} onSuccess The success callback. |
+ * @param {function(string)} onError The error callback. |
+ */ |
+ function captureTab(onSuccess, onError) { |
+ var id = addCallback(onSuccess, onError); |
+ postScreenshotMessage(id); |
+ } |
+ |
+ /** Capture the current visible area of a given element as a PNG. |
+ * |
+ * If the request succeeds, |onSuccess| will be called with one parameter: |
+ * the image encoded as a data URL. |
+ * |
+ * If the request fails, |onError| will be called with one parameter: an |
+ * informational error message. |
+ * |
+ * @param {Element} element The element to capture. |
+ * @param {function(string)} onSuccess The success callback. |
+ * @param {function(string)} onError The error callback. |
+ */ |
+ function captureElement(element, onSuccess, onError) { |
+ var elementRect = element.getBoundingClientRect(); |
+ var elX = elementRect.left; |
+ var elY = elementRect.top; |
+ var elW = elementRect.width; |
+ var elH = elementRect.height; |
+ |
+ function onScreenCaptured(dataURL) { |
+ // Create a canvas of the correct size. |
+ var canvasEl = document.createElement('canvas'); |
+ canvasEl.setAttribute('width', elW); |
+ canvasEl.setAttribute('height', elH); |
+ var ctx = canvasEl.getContext('2d'); |
+ |
+ var imgEl = new Image(); |
+ imgEl.onload = function() { |
+ // Draw only the element region of the image. |
+ ctx.drawImage(imgEl, elX, elY, elW, elH, 0, 0, elW, elH); |
+ |
+ // Extract the canvas to a new data URL, and return it via the callback. |
+ onSuccess(canvasEl.toDataURL()); |
+ }; |
+ |
+ // Load the full screenshot into imgEl. |
+ imgEl.src = dataURL; |
+ } |
+ |
+ var id = addCallback(onScreenCaptured, onError); |
+ postScreenshotMessage(id); |
+ } |
+ |
+ return { |
+ captureTab: captureTab, |
+ captureElement: captureElement |
+ }; |
+})(); |