Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / renderer / resources / set_icon.js
blob83f5a808451edd8b4b7408863d2008cf1323bdf1
1 // Copyright 2014 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 SetIconCommon = requireNative('setIcon').SetIconCommon;
6 var sendRequest = require('sendRequest').sendRequest;
8 function loadImagePath(path, iconSize, callback) {
9   var img = new Image();
10   img.onerror = function() {
11     console.error('Could not load action icon \'' + path + '\'.');
12   };
13   img.onload = function() {
14     var canvas = document.createElement('canvas');
15     canvas.width = img.width > iconSize ? iconSize : img.width;
16     canvas.height = img.height > iconSize ? iconSize : img.height;
18     var canvas_context = canvas.getContext('2d');
19     canvas_context.clearRect(0, 0, canvas.width, canvas.height);
20     canvas_context.drawImage(img, 0, 0, canvas.width, canvas.height);
21     var imageData = canvas_context.getImageData(0, 0, canvas.width,
22                                                 canvas.height);
23     callback(imageData);
24   };
25   img.src = path;
28 function verifyImageData(imageData, iconSize) {
29   // Verify that this at least looks like an ImageData element.
30   // Unfortunately, we cannot use instanceof because the ImageData
31   // constructor is not public.
32   //
33   // We do this manually instead of using JSONSchema to avoid having these
34   // properties show up in the doc.
35   if (!('width' in imageData) ||
36       !('height' in imageData) ||
37       !('data' in imageData)) {
38     throw new Error(
39         'The imageData property must contain an ImageData object or' +
40         ' dictionary of ImageData objects.');
41   }
43   if (imageData.width > iconSize ||
44       imageData.height > iconSize) {
45     throw new Error(
46         'The imageData property must contain an ImageData object that ' +
47         'is no larger than ' + iconSize + ' pixels square.');
48   }
51 /**
52  * Normalizes |details| to a format suitable for sending to the browser,
53  * for example converting ImageData to a binary representation.
54  *
55  * @param {ImageDetails} details
56  *   The ImageDetails passed into an extension action-style API.
57  * @param {Function} callback
58  *   The callback function to pass processed imageData back to. Note that this
59  *   callback may be called reentrantly.
60  */
61 function setIcon(details, callback) {
62   var iconSizes = [19, 38];
63   // Note that iconIndex is actually deprecated, and only available to the
64   // pageAction API.
65   // TODO(kalman): Investigate whether this is for the pageActions API, and if
66   // so, delete it.
67   if ('iconIndex' in details) {
68     callback(details);
69     return;
70   }
72   if ('imageData' in details) {
73     var isEmpty = true;
74     for (var i = 0; i < iconSizes.length; i++) {
75       var sizeKey = iconSizes[i].toString();
76       if (sizeKey in details.imageData) {
77         verifyImageData(details.imageData[sizeKey], iconSizes[i]);
78         isEmpty =false;
79       }
80     }
82     if (isEmpty) {
83       // If details.imageData is not dictionary with keys in set {'19', '38'},
84       // it must be an ImageData object.
85       var sizeKey = iconSizes[0].toString();
86       var imageData = details.imageData;
87       details.imageData = {};
88       details.imageData[sizeKey] = imageData;
89       verifyImageData(details.imageData[sizeKey], iconSizes[0]);
90     }
91     callback(SetIconCommon(details));
92     return;
93   }
95   if ('path' in details) {
96     if (typeof details.path == 'object') {
97       details.imageData = {};
98       var isEmpty = true;
99       var processIconSize = function(index) {
100         if (index == iconSizes.length) {
101           delete details.path;
102           if (isEmpty)
103             throw new Error('The path property must not be empty.');
104           callback(SetIconCommon(details));
105           return;
106         }
107         var sizeKey = iconSizes[index].toString();
108         if (!(sizeKey in details.path)) {
109           processIconSize(index + 1);
110           return;
111         }
112         isEmpty = false;
113         loadImagePath(details.path[sizeKey], iconSizes[index],
114           function(imageData) {
115             details.imageData[sizeKey] = imageData;
116             processIconSize(index + 1);
117           });
118       }
119       processIconSize(0);
120     } else if (typeof details.path == 'string') {
121       var sizeKey = iconSizes[0].toString();
122       details.imageData = {};
123       loadImagePath(details.path, iconSizes[0],
124           function(imageData) {
125             details.imageData[sizeKey] = imageData;
126             delete details.path;
127             callback(SetIconCommon(details));
128             return;
129       });
130     }
131     return;
132   }
133   throw new Error('Either the path or imageData property must be specified.');
136 exports.setIcon = setIcon;