Home | History | Annotate | Download | only in extensions
      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 
      5 /**
      6  * Returns a function that throws a 'not available' exception when called.
      7  *
      8  * @param {string} messagePrefix text to prepend to the exception message.
      9  */
     10 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) {
     11   return function() {
     12     var message = messagePrefix + ' is not available in packaged apps.';
     13     if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
     14     throw message;
     15   };
     16 }
     17 
     18 /**
     19  * Replaces the given methods of the passed in object with stubs that throw
     20  * 'not available' exceptions when called.
     21  *
     22  * @param {Object} object The object with methods to disable. The prototype is
     23  *     preferred.
     24  * @param {string} objectName The display name to use in the error message
     25  *     thrown by the stub (this is the name that the object is commonly referred
     26  *     to by web developers, e.g. "document" instead of "HTMLDocument").
     27  * @param {Array.<string>} methodNames names of methods to disable.
     28  */
     29 function disableMethods(object, objectName, methodNames) {
     30   $Array.forEach(methodNames, function(methodName) {
     31     object[methodName] =
     32         generateDisabledMethodStub(objectName + '.' + methodName + '()');
     33   });
     34 }
     35 
     36 /**
     37  * Replaces the given properties of the passed in object with stubs that throw
     38  * 'not available' exceptions when gotten.  If a property's setter is later
     39  * invoked, the getter and setter are restored to default behaviors.
     40  *
     41  * @param {Object} object The object with properties to disable. The prototype
     42  *     is preferred.
     43  * @param {string} objectName The display name to use in the error message
     44  *     thrown by the getter stub (this is the name that the object is commonly
     45  *     referred to by web developers, e.g. "document" instead of
     46  *     "HTMLDocument").
     47  * @param {Array.<string>} propertyNames names of properties to disable.
     48  */
     49 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
     50   $Array.forEach(propertyNames, function(propertyName) {
     51     var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
     52                                           opt_messageSuffix);
     53     stub._is_platform_app_disabled_getter = true;
     54     object.__defineGetter__(propertyName, stub);
     55 
     56     object.__defineSetter__(propertyName, function(value) {
     57       var getter = this.__lookupGetter__(propertyName);
     58       if (!getter || getter._is_platform_app_disabled_getter) {
     59         // The stub getter is still defined.  Blow-away the property to restore
     60         // default getter/setter behaviors and re-create it with the given
     61         // value.
     62         delete this[propertyName];
     63         this[propertyName] = value;
     64       } else {
     65         // Do nothing.  If some custom getter (not ours) has been defined, there
     66         // would be no way to read back the value stored by a default setter.
     67         // Also, the only way to clear a custom getter is to first delete the
     68         // property.  Therefore, the value we have here should just go into a
     69         // black hole.
     70       }
     71     });
     72   });
     73 }
     74 
     75 // Disable document.open|close|write|etc.
     76 disableMethods(HTMLDocument.prototype, 'document',
     77     ['open', 'clear', 'close', 'write', 'writeln']);
     78 
     79 // Disable history.
     80 window.history = {};
     81 disableMethods(window.history, 'history',
     82     ['back', 'forward', 'go']);
     83 disableGetters(window.history, 'history', ['length']);
     84 
     85 // Disable find.
     86 disableMethods(Window.prototype, 'window', ['find']);
     87 
     88 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to
     89 // warn.
     90 disableMethods(Window.prototype, 'window', ['alert', 'confirm', 'prompt']);
     91 
     92 // Disable window.*bar.
     93 disableGetters(window, 'window',
     94     ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar',
     95      'toolbar']);
     96 
     97 // Disable window.localStorage.
     98 // Sometimes DOM security policy prevents us from doing this (e.g. for data:
     99 // URLs) so wrap in try-catch.
    100 try {
    101   disableGetters(window, 'window',
    102       ['localStorage'],
    103       'Use chrome.storage.local instead.');
    104 } catch (e) {}
    105 
    106 // Document instance properties that we wish to disable need to be set when
    107 // the document begins loading, since only then will the "document" reference
    108 // point to the page's document (it will be reset between now and then).
    109 // We can't listen for the "readystatechange" event on the document (because
    110 // the object that it's dispatched on doesn't exist yet), but we can instead
    111 // do it at the window level in the capturing phase.
    112 window.addEventListener('readystatechange', function(event) {
    113   if (document.readyState != 'loading')
    114     return;
    115 
    116   // Deprecated document properties from
    117   // https://developer.mozilla.org/en/DOM/document.
    118   // To deprecate document.all, simply changing its getter and setter would
    119   // activate its cache mechanism, and degrade the performance. Here we assign
    120   // it first to 'undefined' to avoid this.
    121   document.all = undefined;
    122   disableGetters(document, 'document',
    123       ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor',
    124        'vlinkColor']);
    125 }, true);
    126 
    127 // Disable onunload, onbeforeunload.
    128 Window.prototype.__defineSetter__(
    129     'onbeforeunload', generateDisabledMethodStub('onbeforeunload'));
    130 Window.prototype.__defineSetter__(
    131     'onunload', generateDisabledMethodStub('onunload'));
    132 var windowAddEventListener = Window.prototype.addEventListener;
    133 Window.prototype.addEventListener = function(type) {
    134   if (type === 'unload' || type === 'beforeunload')
    135     generateDisabledMethodStub(type)();
    136   else
    137     return $Function.apply(windowAddEventListener, window, arguments);
    138 };
    139