Home | History | Annotate | Download | only in extensions
      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 //
      7 var binding = require('binding').Binding.create('notifications');
      8 
      9 var sendRequest = require('sendRequest').sendRequest;
     10 var imageUtil = require('imageUtil');
     11 var lastError = require('lastError');
     12 var notificationsPrivate = requireNative('notifications_private');
     13 
     14 function 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|.
     30 function 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   // |imageUrl| is optional.
     44   if (notificationDetails.imageUrl) {
     45     $Array.push(urlSpecs, {
     46       path: notificationDetails.imageUrl,
     47       width: imageSizes.image.width * imageSizes.scaleFactor,
     48       height: imageSizes.image.height * imageSizes.scaleFactor,
     49       callback: imageDataSetter(notificationDetails, 'imageBitmap')
     50     });
     51   }
     52 
     53   // Each button has an optional icon.
     54   var buttonList = notificationDetails.buttons;
     55   if (buttonList && typeof buttonList.length === 'number') {
     56     var numButtons = buttonList.length;
     57     for (var i = 0; i < numButtons; i++) {
     58       if (buttonList[i].iconUrl) {
     59         $Array.push(urlSpecs, {
     60           path: buttonList[i].iconUrl,
     61           width: imageSizes.buttonIcon.width * imageSizes.scaleFactor,
     62           height: imageSizes.buttonIcon.height * imageSizes.scaleFactor,
     63           callback: imageDataSetter(buttonList[i], 'iconBitmap')
     64         });
     65       }
     66     }
     67   }
     68 
     69   return urlSpecs;
     70 }
     71 
     72 function replaceNotificationOptionURLs(notification_details, callback) {
     73   var imageSizes = notificationsPrivate.GetNotificationImageSizes();
     74   var url_specs = getUrlSpecs(imageSizes, notification_details);
     75   if (!url_specs.length) {
     76     callback(true);
     77     return;
     78   }
     79 
     80   var errors = 0;
     81 
     82   imageUtil.loadAllImages(url_specs, {
     83     onerror: function(index) {
     84       errors++;
     85     },
     86     oncomplete: function(imageData) {
     87       if (errors > 0) {
     88         callback(false);
     89         return;
     90       }
     91       for (var index = 0; index < url_specs.length; index++) {
     92         var url_spec = url_specs[index];
     93         url_spec.callback(imageData[index]);
     94       }
     95       callback(true);
     96     }
     97   });
     98 }
     99 
    100 function genHandle(name, failure_function) {
    101   return function(id, input_notification_details, callback) {
    102     // TODO(dewittj): Remove this hack. This is used as a way to deep
    103     // copy a complex JSON object.
    104     var notification_details = JSON.parse(
    105         JSON.stringify(input_notification_details));
    106     var that = this;
    107     replaceNotificationOptionURLs(notification_details, function(success) {
    108       if (success) {
    109         sendRequest(that.name,
    110             [id, notification_details, callback],
    111             that.definition.parameters);
    112         return;
    113       }
    114       lastError.run(name,
    115                     'Unable to download all specified images.',
    116                     null,
    117                     failure_function, [callback, id])
    118     });
    119   };
    120 }
    121 
    122 var handleCreate = genHandle('notifications.create',
    123                              function(callback, id) { callback(id); });
    124 var handleUpdate = genHandle('notifications.update',
    125                              function(callback, id) { callback(false); });
    126 
    127 var notificationsCustomHook = function(bindingsAPI, extensionId) {
    128   var apiFunctions = bindingsAPI.apiFunctions;
    129   apiFunctions.setHandleRequest('create', handleCreate);
    130   apiFunctions.setHandleRequest('update', handleUpdate);
    131 };
    132 
    133 binding.registerCustomHook(notificationsCustomHook);
    134 
    135 exports.binding = binding.generate();
    136