1// Copyright (c) 2012 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, actionType, callback) {
9  var img = new Image();
10  img.onerror = function() {
11    console.error('Could not load ' + actionType + ' icon \'' +
12                  path + '\'.');
13  };
14  img.onload = function() {
15    var canvas = document.createElement('canvas');
16    canvas.width = img.width > iconSize ? iconSize : img.width;
17    canvas.height = img.height > iconSize ? iconSize : img.height;
18
19    var canvas_context = canvas.getContext('2d');
20    canvas_context.clearRect(0, 0, canvas.width, canvas.height);
21    canvas_context.drawImage(img, 0, 0, canvas.width, canvas.height);
22    var imageData = canvas_context.getImageData(0, 0, canvas.width,
23                                                canvas.height);
24    callback(imageData);
25  };
26  img.src = path;
27}
28
29function verifyImageData(imageData, iconSize) {
30  // Verify that this at least looks like an ImageData element.
31  // Unfortunately, we cannot use instanceof because the ImageData
32  // constructor is not public.
33  //
34  // We do this manually instead of using JSONSchema to avoid having these
35  // properties show up in the doc.
36  if (!('width' in imageData) ||
37      !('height' in imageData) ||
38      !('data' in imageData)) {
39    throw new Error(
40        'The imageData property must contain an ImageData object or' +
41        ' dictionary of ImageData objects.');
42  }
43
44  if (imageData.width > iconSize ||
45      imageData.height > iconSize) {
46    throw new Error(
47        'The imageData property must contain an ImageData object that ' +
48        'is no larger than ' + iconSize + ' pixels square.');
49  }
50}
51
52function setIcon(details, callback, name, parameters, actionType) {
53  var iconSizes = [19, 38];
54  if ('iconIndex' in details) {
55    sendRequest(name, [details, callback], parameters);
56  } else if ('imageData' in details) {
57    if (typeof details.imageData == 'object') {
58      var isEmpty = true;
59      for (var i = 0; i < iconSizes.length; i++) {
60        var sizeKey = iconSizes[i].toString();
61        if (sizeKey in details.imageData) {
62          verifyImageData(details.imageData[sizeKey], iconSizes[i]);
63          isEmpty =false;
64        }
65      }
66
67      if (!isEmpty) {
68        sendRequest(name, [details, callback], parameters,
69                    {nativeFunction: SetIconCommon});
70      } else {
71        // If details.imageData is not dictionary with keys in set {'19', '38'},
72        // it must be an ImageData object.
73        var sizeKey = iconSizes[0].toString();
74        var imageData = details.imageData;
75        details.imageData = {};
76        details.imageData[sizeKey] = imageData;
77        verifyImageData(details.imageData[sizeKey], iconSizes[0]);
78        sendRequest(name, [details, callback], parameters,
79                    {nativeFunction: SetIconCommon});
80     }
81    } else {
82      throw new Error('imageData property has unexpected type.');
83    }
84  } else if ('path' in details) {
85    if (typeof details.path == 'object') {
86      details.imageData = {};
87      var isEmpty = true;
88      var processIconSize = function(index) {
89        if (index == iconSizes.length) {
90          delete details.path;
91          if (isEmpty)
92            throw new Error('The path property must not be empty.');
93          sendRequest(name, [details, callback], parameters,
94                      {nativeFunction: SetIconCommon});
95          return;
96        }
97        var sizeKey = iconSizes[index].toString();
98        if (!(sizeKey in details.path)) {
99          processIconSize(index + 1);
100          return;
101        }
102        isEmpty = false;
103        loadImagePath(details.path[sizeKey], iconSizes[index], actionType,
104          function(imageData) {
105            details.imageData[sizeKey] = imageData;
106            processIconSize(index + 1);
107          });
108      }
109
110      processIconSize(0);
111    } else if (typeof details.path == 'string') {
112      var sizeKey = iconSizes[0].toString();
113      details.imageData = {};
114      loadImagePath(details.path, iconSizes[0], actionType,
115          function(imageData) {
116            details.imageData[sizeKey] = imageData;
117            delete details.path;
118            sendRequest(name, [details, callback], parameters,
119                        {nativeFunction: SetIconCommon});
120      });
121    } else {
122      throw new Error('The path property should contain either string or ' +
123                      'dictionary of strings.');
124    }
125  } else {
126    throw new Error(
127        'Either the path or imageData property must be specified.');
128  }
129}
130
131exports.setIcon = setIcon;
132