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