1// Copyright 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.
4
5// Custom bindings for the notifications API.
6//
7var binding = require('binding').Binding.create('notifications');
8
9var sendRequest = require('sendRequest').sendRequest;
10var imageUtil = require('imageUtil');
11var lastError = require('lastError');
12var notificationsPrivate = requireNative('notifications_private');
13
14function imageDataSetter(context, key) {
15  var f = function(val) {
16    this[key] = val;
17  };
18  return $Function.bind(f, context);
19}
20
21// A URL Spec is an object with the following keys:
22//  path: The resource to be downloaded.
23//  width: (optional) The maximum width of the image to be downloaded in device
24//         pixels.
25//  height: (optional) The maximum height of the image to be downloaded in
26//          device pixels.
27//  callback: A function to be called when the URL is complete. It
28//    should accept an ImageData object and set the appropriate
29//    field in |notificationDetails|.
30function getUrlSpecs(imageSizes, notificationDetails) {
31  var urlSpecs = [];
32
33  // |iconUrl| might be optional for notification updates.
34  if (notificationDetails.iconUrl) {
35    $Array.push(urlSpecs, {
36      path: notificationDetails.iconUrl,
37      width: imageSizes.icon.width * imageSizes.scaleFactor,
38      height: imageSizes.icon.height * imageSizes.scaleFactor,
39      callback: imageDataSetter(notificationDetails, 'iconBitmap')
40    });
41  }
42
43  // |appIconMaskUrl| is optional.
44  if (notificationDetails.appIconMaskUrl) {
45    $Array.push(urlSpecs, {
46      path: notificationDetails.appIconMaskUrl,
47      width: imageSizes.appIconMask.width * imageSizes.scaleFactor,
48      height: imageSizes.appIconMask.height * imageSizes.scaleFactor,
49      callback: imageDataSetter(notificationDetails, 'appIconMaskBitmap')
50    });
51  }
52
53  // |imageUrl| is optional.
54  if (notificationDetails.imageUrl) {
55    $Array.push(urlSpecs, {
56      path: notificationDetails.imageUrl,
57      width: imageSizes.image.width * imageSizes.scaleFactor,
58      height: imageSizes.image.height * imageSizes.scaleFactor,
59      callback: imageDataSetter(notificationDetails, 'imageBitmap')
60    });
61  }
62
63  // Each button has an optional icon.
64  var buttonList = notificationDetails.buttons;
65  if (buttonList && typeof buttonList.length === 'number') {
66    var numButtons = buttonList.length;
67    for (var i = 0; i < numButtons; i++) {
68      if (buttonList[i].iconUrl) {
69        $Array.push(urlSpecs, {
70          path: buttonList[i].iconUrl,
71          width: imageSizes.buttonIcon.width * imageSizes.scaleFactor,
72          height: imageSizes.buttonIcon.height * imageSizes.scaleFactor,
73          callback: imageDataSetter(buttonList[i], 'iconBitmap')
74        });
75      }
76    }
77  }
78
79  return urlSpecs;
80}
81
82function replaceNotificationOptionURLs(notification_details, callback) {
83  var imageSizes = notificationsPrivate.GetNotificationImageSizes();
84  var url_specs = getUrlSpecs(imageSizes, notification_details);
85  if (!url_specs.length) {
86    callback(true);
87    return;
88  }
89
90  var errors = 0;
91
92  imageUtil.loadAllImages(url_specs, {
93    onerror: function(index) {
94      errors++;
95    },
96    oncomplete: function(imageData) {
97      if (errors > 0) {
98        callback(false);
99        return;
100      }
101      for (var index = 0; index < url_specs.length; index++) {
102        var url_spec = url_specs[index];
103        url_spec.callback(imageData[index]);
104      }
105      callback(true);
106    }
107  });
108}
109
110function genHandle(name, failure_function) {
111  return function(id, input_notification_details, callback) {
112    // TODO(dewittj): Remove this hack. This is used as a way to deep
113    // copy a complex JSON object.
114    var notification_details = JSON.parse(
115        JSON.stringify(input_notification_details));
116    var that = this;
117    replaceNotificationOptionURLs(notification_details, function(success) {
118      if (success) {
119        sendRequest(that.name,
120            [id, notification_details, callback],
121            that.definition.parameters);
122        return;
123      }
124      lastError.run(name,
125                    'Unable to download all specified images.',
126                    null,
127                    failure_function, [callback, id])
128    });
129  };
130}
131
132var handleCreate = genHandle('notifications.create',
133                             function(callback, id) { callback(id); });
134var handleUpdate = genHandle('notifications.update',
135                             function(callback, id) { callback(false); });
136
137var notificationsCustomHook = function(bindingsAPI, extensionId) {
138  var apiFunctions = bindingsAPI.apiFunctions;
139  apiFunctions.setHandleRequest('create', handleCreate);
140  apiFunctions.setHandleRequest('update', handleUpdate);
141};
142
143binding.registerCustomHook(notificationsCustomHook);
144
145exports.binding = binding.generate();
146