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