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