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. 4 5var SetIconCommon = requireNative('setIcon').SetIconCommon; 6var sendRequest = require('sendRequest').sendRequest; 7 8function 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; 17 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; 26} 27 28function 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 } 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} 50 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 */ 61function 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 } 71 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 } 81 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 } 94 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.'); 134} 135 136exports.setIcon = setIcon; 137