OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 var $console = window.console; |
| 6 |
5 /** | 7 /** |
6 * Returns a function that throws a 'not available' exception when called. | 8 * Returns a function that logs a 'not available' error to the console and |
| 9 * returns undefined. |
7 * | 10 * |
8 * @param {string} messagePrefix text to prepend to the exception message. | 11 * @param {string} messagePrefix text to prepend to the exception message. |
9 */ | 12 */ |
10 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) { | 13 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) { |
| 14 var message = messagePrefix + ' is not available in packaged apps.'; |
| 15 if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix; |
11 return function() { | 16 return function() { |
12 var message = messagePrefix + ' is not available in packaged apps.'; | 17 $console.error(message); |
13 if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix; | 18 return; |
14 throw message; | |
15 }; | 19 }; |
16 } | 20 } |
17 | 21 |
18 /** | 22 /** |
19 * Replaces the given methods of the passed in object with stubs that throw | 23 * Returns a function that throws a 'not available' error. |
20 * 'not available' exceptions when called. | 24 * |
| 25 * @param {string} messagePrefix text to prepend to the exception message. |
| 26 */ |
| 27 function generateThrowingMethodStub(messagePrefix, opt_messageSuffix) { |
| 28 var message = messagePrefix + ' is not available in packaged apps.'; |
| 29 if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix; |
| 30 return function() { |
| 31 throw new Error(message); |
| 32 }; |
| 33 } |
| 34 |
| 35 /** |
| 36 * Replaces the given methods of the passed in object with stubs that log |
| 37 * 'not available' errors to the console and return undefined. |
| 38 * |
| 39 * This should be used on methods attached via non-configurable properties, |
| 40 * such as window.alert. disableGetters should be used when possible, because |
| 41 * it is friendlier towards feature detection. |
| 42 * |
| 43 * In most cases, the useThrowingStubs should be false, so the stubs used to |
| 44 * replace the methods log an error to the console, but allow the calling code |
| 45 * to continue. We shouldn't break library code that uses feature detection |
| 46 * responsibly, such as: |
| 47 * if(window.confirm) { |
| 48 * var result = window.confirm('Are you sure you want to delete ...?'); |
| 49 * ... |
| 50 * } |
| 51 * |
| 52 * useThrowingStubs should only be true for methods that are deprecated in the |
| 53 * Web platform, and should not be used by a responsible library, even in |
| 54 * conjunction with feature detection. A great example is document.write(), as |
| 55 * the HTML5 specification recommends against using it, and says that its |
| 56 * behavior is unreliable. No reasonable library code should ever use it. |
| 57 * HTML5 spec: http://www.w3.org/TR/html5/dom.html#dom-document-write |
21 * | 58 * |
22 * @param {Object} object The object with methods to disable. The prototype is | 59 * @param {Object} object The object with methods to disable. The prototype is |
23 * preferred. | 60 * preferred. |
24 * @param {string} objectName The display name to use in the error message | 61 * @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 | 62 * 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"). | 63 * to by web developers, e.g. "document" instead of "HTMLDocument"). |
27 * @param {Array.<string>} methodNames names of methods to disable. | 64 * @param {Array.<string>} methodNames names of methods to disable. |
| 65 * @param {Boolean} useThrowingStubs if true, the replaced methods will throw |
| 66 * an error instead of silently returning undefined |
28 */ | 67 */ |
29 function disableMethods(object, objectName, methodNames) { | 68 function disableMethods(object, objectName, methodNames, useThrowingStubs) { |
30 $Array.forEach(methodNames, function(methodName) { | 69 $Array.forEach(methodNames, function(methodName) { |
31 object[methodName] = | 70 var messagePrefix = objectName + '.' + methodName + '()'; |
32 generateDisabledMethodStub(objectName + '.' + methodName + '()'); | 71 object[methodName] = useThrowingStubs ? |
| 72 generateThrowingMethodStub(messagePrefix) : |
| 73 generateDisabledMethodStub(messagePrefix); |
33 }); | 74 }); |
34 } | 75 } |
35 | 76 |
36 /** | 77 /** |
37 * Replaces the given properties of the passed in object with stubs that throw | 78 * Replaces the given properties of the passed in object with stubs that log |
38 * 'not available' exceptions when gotten. If a property's setter is later | 79 * 'not available' warnings to the console and return undefined when gotten. If |
39 * invoked, the getter and setter are restored to default behaviors. | 80 * a property's setter is later invoked, the getter and setter are restored to |
| 81 * default behaviors. |
40 * | 82 * |
41 * @param {Object} object The object with properties to disable. The prototype | 83 * @param {Object} object The object with properties to disable. The prototype |
42 * is preferred. | 84 * is preferred. |
43 * @param {string} objectName The display name to use in the error message | 85 * @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 | 86 * 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 | 87 * referred to by web developers, e.g. "document" instead of |
46 * "HTMLDocument"). | 88 * "HTMLDocument"). |
47 * @param {Array.<string>} propertyNames names of properties to disable. | 89 * @param {Array.<string>} propertyNames names of properties to disable. |
48 */ | 90 */ |
49 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) { | 91 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) { |
50 $Array.forEach(propertyNames, function(propertyName) { | 92 $Array.forEach(propertyNames, function(propertyName) { |
51 var stub = generateDisabledMethodStub(objectName + '.' + propertyName, | 93 var stub = generateDisabledMethodStub(objectName + '.' + propertyName, |
52 opt_messageSuffix); | 94 opt_messageSuffix); |
53 stub._is_platform_app_disabled_getter = true; | 95 stub._is_platform_app_disabled_getter = true; |
54 object.__defineGetter__(propertyName, stub); | 96 $Object.defineProperty(object, propertyName, { |
55 | 97 configurable: true, |
56 object.__defineSetter__(propertyName, function(value) { | 98 enumerable: false, |
57 var getter = this.__lookupGetter__(propertyName); | 99 get: stub, |
58 if (!getter || getter._is_platform_app_disabled_getter) { | 100 set: function(value) { |
59 // The stub getter is still defined. Blow-away the property to restore | 101 var descriptor = $Object.getOwnPropertyDescriptor(this, propertyName); |
60 // default getter/setter behaviors and re-create it with the given | 102 if (!descriptor || !descriptor.get || |
61 // value. | 103 descriptor.get._is_platform_app_disabled_getter) { |
62 delete this[propertyName]; | 104 // The stub getter is still defined. Blow-away the property to |
63 this[propertyName] = value; | 105 // restore default getter/setter behaviors and re-create it with the |
64 } else { | 106 // given value. |
65 // Do nothing. If some custom getter (not ours) has been defined, there | 107 delete this[propertyName]; |
66 // would be no way to read back the value stored by a default setter. | 108 this[propertyName] = value; |
67 // Also, the only way to clear a custom getter is to first delete the | 109 } else { |
68 // property. Therefore, the value we have here should just go into a | 110 // Do nothing. If some custom getter (not ours) has been defined, |
69 // black hole. | 111 // there would be no way to read back the value stored by a default |
| 112 // setter. Also, the only way to clear a custom getter is to first |
| 113 // delete the property. Therefore, the value we have here should |
| 114 // just go into a black hole. |
| 115 } |
70 } | 116 } |
71 }); | 117 }); |
72 }); | 118 }); |
73 } | 119 } |
74 | 120 |
75 // Disable document.open|close|write|etc. | 121 /** |
76 disableMethods(HTMLDocument.prototype, 'document', | 122 * Replaces the given properties of the passed in object with stubs that log |
77 ['open', 'clear', 'close', 'write', 'writeln']); | 123 * 'not available' warnings to the console when set. |
| 124 * |
| 125 * @param {Object} object The object with properties to disable. The prototype |
| 126 * is preferred. |
| 127 * @param {string} objectName The display name to use in the error message |
| 128 * thrown by the setter stub (this is the name that the object is commonly |
| 129 * referred to by web developers, e.g. "document" instead of |
| 130 * "HTMLDocument"). |
| 131 * @param {Array.<string>} propertyNames names of properties to disable. |
| 132 */ |
| 133 function disableSetters(object, objectName, propertyNames, opt_messageSuffix) { |
| 134 $Array.forEach(propertyNames, function(propertyName) { |
| 135 var stub = generateDisabledMethodStub(objectName + '.' + propertyName, |
| 136 opt_messageSuffix); |
| 137 $Object.defineProperty(object, propertyName, { |
| 138 configurable: true, |
| 139 enumerable: false, |
| 140 get: function() { |
| 141 return; |
| 142 }, |
| 143 set: stub |
| 144 }); |
| 145 }); |
| 146 } |
| 147 |
| 148 // Disable benign Document methods. |
| 149 disableMethods(HTMLDocument.prototype, 'document', ['open', 'clear', 'close']); |
| 150 |
| 151 // Replace evil Document methods with exception-throwing stubs. |
| 152 disableMethods(HTMLDocument.prototype, 'document', ['write', 'writeln'], true); |
78 | 153 |
79 // Disable history. | 154 // Disable history. |
80 window.history = {}; | 155 window.history = {}; |
81 disableMethods(window.history, 'history', | 156 disableGetters(window.history, 'history', ['back', 'forward', 'go', 'length']); |
82 ['back', 'forward', 'go']); | |
83 disableGetters(window.history, 'history', ['length']); | |
84 | 157 |
85 // Disable find. | 158 // Disable find. |
86 disableMethods(Window.prototype, 'window', ['find']); | 159 disableMethods(Window.prototype, 'window', ['find']); |
87 | 160 |
88 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to | 161 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to |
89 // warn. | 162 // warn. |
90 disableMethods(Window.prototype, 'window', ['alert', 'confirm', 'prompt']); | 163 disableMethods(Window.prototype, 'window', ['alert', 'confirm', 'prompt']); |
91 | 164 |
92 // Disable window.*bar. | 165 // Disable window.*bar. |
93 disableGetters(window, 'window', | 166 disableGetters(window, 'window', |
(...skipping 19 matching lines...) Expand all Loading... |
113 if (document.readyState != 'loading') | 186 if (document.readyState != 'loading') |
114 return; | 187 return; |
115 | 188 |
116 // Deprecated document properties from | 189 // Deprecated document properties from |
117 // https://developer.mozilla.org/en/DOM/document. | 190 // https://developer.mozilla.org/en/DOM/document. |
118 // To deprecate document.all, simply changing its getter and setter would | 191 // To deprecate document.all, simply changing its getter and setter would |
119 // activate its cache mechanism, and degrade the performance. Here we assign | 192 // activate its cache mechanism, and degrade the performance. Here we assign |
120 // it first to 'undefined' to avoid this. | 193 // it first to 'undefined' to avoid this. |
121 document.all = undefined; | 194 document.all = undefined; |
122 disableGetters(document, 'document', | 195 disableGetters(document, 'document', |
123 ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', | 196 ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor']); |
124 'vlinkColor']); | |
125 }, true); | 197 }, true); |
126 | 198 |
127 // Disable onunload, onbeforeunload. | 199 // Disable onunload, onbeforeunload. |
128 Window.prototype.__defineSetter__( | 200 disableSetters(Window.prototype, 'window', ['onbeforeunload', 'onunload']); |
129 'onbeforeunload', generateDisabledMethodStub('onbeforeunload')); | |
130 Window.prototype.__defineSetter__( | |
131 'onunload', generateDisabledMethodStub('onunload')); | |
132 var windowAddEventListener = Window.prototype.addEventListener; | 201 var windowAddEventListener = Window.prototype.addEventListener; |
133 Window.prototype.addEventListener = function(type) { | 202 Window.prototype.addEventListener = function(type) { |
134 if (type === 'unload' || type === 'beforeunload') | 203 if (type === 'unload' || type === 'beforeunload') |
135 generateDisabledMethodStub(type)(); | 204 generateDisabledMethodStub(type)(); |
136 else | 205 else |
137 return $Function.apply(windowAddEventListener, window, arguments); | 206 return $Function.apply(windowAddEventListener, window, arguments); |
138 }; | 207 }; |
OLD | NEW |