Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 var screenshot = (function() { | |
| 6 /** A map of id to pending callback. */ | |
| 7 var callbackMap = {}; | |
| 8 | |
| 9 /** An array of queued requests. They will all be executed when the | |
| 10 * background page injects screen code into this page */ | |
|
Sam Clegg
2013/09/05 00:05:31
Be consistent with javadoc style comments here. T
binji
2013/09/06 20:59:48
Done.
| |
| 11 var queuedRequests = []; | |
| 12 | |
| 13 /** The next id to assign. Used for mapping id to callback. */ | |
| 14 var nextId = 0; | |
| 15 | |
| 16 /** This is set to true when the background page injects screenshot code into | |
| 17 * this page */ | |
| 18 var extensionInjected = false; | |
| 19 | |
| 20 /** Generate a new id, which maps to the given callbacks. | |
| 21 * | |
| 22 * @param {function(string)} onSuccess | |
| 23 * @param {function(string)} onError | |
| 24 * @return {number} The id. | |
| 25 */ | |
| 26 function addCallback(onSuccess, onError) { | |
| 27 var id = nextId++; | |
| 28 callbackMap[id] = [onSuccess, onError]; | |
| 29 return id; | |
| 30 } | |
| 31 | |
| 32 /** Call the callback mapped to |id|. | |
| 33 * | |
| 34 * @param {number} id | |
| 35 * @param {boolean} success true to call the success callback, false for the | |
| 36 * error callback. | |
| 37 * @param {...} A variable number of arguments to pass to the callback. | |
| 38 */ | |
| 39 function callCallback(id, success) { | |
| 40 var callbacks = callbackMap[id]; | |
| 41 if (!callbacks) { | |
| 42 console.log('Unknown id: ' + id); | |
| 43 return; | |
| 44 } | |
| 45 | |
| 46 delete callbackMap[id]; | |
| 47 var callback = success ? callbacks[0] : callbacks[1]; | |
| 48 if (callback) | |
| 49 callback(Array.prototype.slice.call(arguments, 2)); | |
| 50 } | |
| 51 | |
| 52 /** Post a message to take a screenshot. | |
| 53 * | |
| 54 * This message will be enqueued if the extension has not yet injected the | |
| 55 * screenshot code. | |
| 56 * | |
| 57 * @param {number} id An id to associate with this request. When the | |
| 58 * screenshot is complete, the background page will return | |
| 59 * a result with this id. | |
| 60 */ | |
| 61 function postScreenshotMessage(id) { | |
| 62 if (!extensionInjected) { | |
| 63 queuedRequests.push(id); | |
| 64 return; | |
| 65 } | |
| 66 | |
| 67 window.postMessage({id: id, target: 'background'}, '*'); | |
| 68 } | |
| 69 | |
| 70 /** Post all queued screenshot requests. | |
| 71 * | |
| 72 * Should only be called after the screenshot code has been injected by the | |
| 73 * background page. | |
| 74 */ | |
| 75 function postQueuedRequests() { | |
| 76 for (var i = 0; i < queuedRequests.length; ++i) { | |
| 77 var id = queuedRequests[i]; | |
| 78 postScreenshotMessage(id); | |
| 79 } | |
| 80 queuedRequests = []; | |
| 81 } | |
| 82 | |
| 83 /** Predicate whether the extension has injected code yet. | |
| 84 * | |
| 85 * @return {boolean} | |
| 86 */ | |
| 87 function isInjected() { | |
| 88 // NOTE: This attribute name must match the one in injected.js. | |
| 89 return document.body && | |
| 90 document.body.getAttribute('screenshot_extension_injected'); | |
| 91 } | |
| 92 | |
| 93 /** Start an interval that polls for when the extension has injected code | |
| 94 * into this page. | |
| 95 * | |
| 96 * The extension first adds a postMessage handler to listen for requests, | |
| 97 * then adds an attribute to the body element. If we see this attribute, we | |
| 98 * know the listener is ready. | |
| 99 */ | |
| 100 function pollForInjection() { | |
| 101 var intervalId = window.setInterval(function() { | |
| 102 if (!isInjected()) | |
| 103 return; | |
| 104 | |
| 105 // Finally injected! | |
| 106 window.clearInterval(intervalId); | |
| 107 extensionInjected = true; | |
| 108 postQueuedRequests(); | |
| 109 }, 100); // Every 100ms. | |
| 110 } | |
| 111 | |
| 112 // Add a postMessage listener for when the injected code returns a result | |
| 113 // from the background page. | |
| 114 window.addEventListener('message', function(event) { | |
| 115 // If the message comes from another window, or is outbound (i.e. | |
| 116 // event.data.target === 'background'), ignore it. | |
| 117 if (event.source !== window || event.data.target !== 'page') | |
| 118 return; | |
| 119 | |
| 120 var success = event.data.error === undefined; | |
| 121 callCallback(event.data.id, success, event.data.data); | |
| 122 }, false); | |
| 123 | |
| 124 if (isInjected()) | |
| 125 extensionInjected = true; | |
| 126 else | |
| 127 pollForInjection(); | |
| 128 | |
| 129 // Public functions. | |
| 130 | |
| 131 /** Capture the current visible area of the tab as a PNG. | |
| 132 * | |
| 133 * If the request succeeds, |onSuccess| will be called with one parameter: | |
| 134 * the image encoded as a data URL. | |
| 135 * | |
| 136 * If the request fails, |onError| will be called with one parameter: an | |
| 137 * informational error message. | |
| 138 * | |
| 139 * @param {function(string)} onSuccess The success callback. | |
| 140 * @param {function(string)} onError The error callback. | |
| 141 */ | |
| 142 function captureTab(onSuccess, onError) { | |
| 143 var id = addCallback(onSuccess, onError); | |
| 144 postScreenshotMessage(id); | |
| 145 } | |
| 146 | |
| 147 /** Capture the current visible area of a given element as a PNG. | |
| 148 * | |
| 149 * If the request succeeds, |onSuccess| will be called with one parameter: | |
| 150 * the image encoded as a data URL. | |
| 151 * | |
| 152 * If the request fails, |onError| will be called with one parameter: an | |
| 153 * informational error message. | |
| 154 * | |
| 155 * @param {Element} element The element to capture. | |
| 156 * @param {function(string)} onSuccess The success callback. | |
| 157 * @param {function(string)} onError The error callback. | |
| 158 */ | |
| 159 function captureElement(element, onSuccess, onError) { | |
| 160 var elementRect = element.getBoundingClientRect(); | |
| 161 var elX = elementRect.left; | |
| 162 var elY = elementRect.top; | |
| 163 var elW = elementRect.width; | |
| 164 var elH = elementRect.height; | |
| 165 | |
| 166 function onScreenCaptured(dataURL) { | |
| 167 // Create a canvas of the correct size. | |
| 168 var canvasEl = document.createElement('canvas'); | |
| 169 canvasEl.setAttribute('width', elW); | |
| 170 canvasEl.setAttribute('height', elH); | |
| 171 var ctx = canvasEl.getContext('2d'); | |
| 172 | |
| 173 var imgEl = new Image(); | |
| 174 imgEl.onload = function() { | |
| 175 // Draw only the element region of the image. | |
| 176 ctx.drawImage(imgEl, elX, elY, elW, elH, 0, 0, elW, elH); | |
| 177 | |
| 178 // Extract the canvas to a new data URL, and return it via the callback. | |
| 179 onSuccess(canvasEl.toDataURL()); | |
| 180 }; | |
| 181 | |
| 182 // Load the full screenshot into imgEl. | |
| 183 imgEl.src = dataURL; | |
| 184 } | |
| 185 | |
| 186 var id = addCallback(onScreenCaptured, onError); | |
| 187 postScreenshotMessage(id); | |
| 188 } | |
| 189 | |
| 190 return { | |
| 191 captureTab: captureTab, | |
| 192 captureElement: captureElement | |
| 193 }; | |
| 194 })(); | |
| OLD | NEW |