1 // Copyright 2014 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.
6 * Returns a function that logs a 'not available' error to the console and
9 * @param {string} messagePrefix text to prepend to the exception message.
11 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) {
12 var message = messagePrefix + ' is not available in packaged apps.';
13 if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
15 console.error(message);
21 * Returns a function that throws a 'not available' error.
23 * @param {string} messagePrefix text to prepend to the exception message.
25 function generateThrowingMethodStub(messagePrefix, opt_messageSuffix) {
26 var message = messagePrefix + ' is not available in packaged apps.';
27 if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
29 throw new Error(message);
34 * Replaces the given methods of the passed in object with stubs that log
35 * 'not available' errors to the console and return undefined.
37 * This should be used on methods attached via non-configurable properties,
38 * such as window.alert. disableGetters should be used when possible, because
39 * it is friendlier towards feature detection.
41 * In most cases, the useThrowingStubs should be false, so the stubs used to
42 * replace the methods log an error to the console, but allow the calling code
43 * to continue. We shouldn't break library code that uses feature detection
44 * responsibly, such as:
45 * if(window.confirm) {
46 * var result = window.confirm('Are you sure you want to delete ...?');
50 * useThrowingStubs should only be true for methods that are deprecated in the
51 * Web platform, and should not be used by a responsible library, even in
52 * conjunction with feature detection. A great example is document.write(), as
53 * the HTML5 specification recommends against using it, and says that its
54 * behavior is unreliable. No reasonable library code should ever use it.
55 * HTML5 spec: http://www.w3.org/TR/html5/dom.html#dom-document-write
57 * @param {Object} object The object with methods to disable. The prototype is
59 * @param {string} objectName The display name to use in the error message
60 * thrown by the stub (this is the name that the object is commonly referred
61 * to by web developers, e.g. "document" instead of "HTMLDocument").
62 * @param {Array<string>} methodNames names of methods to disable.
63 * @param {Boolean} useThrowingStubs if true, the replaced methods will throw
64 * an error instead of silently returning undefined
66 function disableMethods(object, objectName, methodNames, useThrowingStubs) {
67 $Array.forEach(methodNames, function(methodName) {
68 var messagePrefix = objectName + '.' + methodName + '()';
69 object[methodName] = useThrowingStubs ?
70 generateThrowingMethodStub(messagePrefix) :
71 generateDisabledMethodStub(messagePrefix);
76 * Replaces the given properties of the passed in object with stubs that log
77 * 'not available' warnings to the console and return undefined when gotten. If
78 * a property's setter is later invoked, the getter and setter are restored to
81 * @param {Object} object The object with properties to disable. The prototype
83 * @param {string} objectName The display name to use in the error message
84 * thrown by the getter stub (this is the name that the object is commonly
85 * referred to by web developers, e.g. "document" instead of
87 * @param {Array<string>} propertyNames names of properties to disable.
89 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
90 $Array.forEach(propertyNames, function(propertyName) {
91 var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
93 stub._is_platform_app_disabled_getter = true;
94 $Object.defineProperty(object, propertyName, {
98 set: function(value) {
99 var descriptor = $Object.getOwnPropertyDescriptor(this, propertyName);
100 if (!descriptor || !descriptor.get ||
101 descriptor.get._is_platform_app_disabled_getter) {
102 // The stub getter is still defined. Blow-away the property to
103 // restore default getter/setter behaviors and re-create it with the
105 delete this[propertyName];
106 this[propertyName] = value;
108 // Do nothing. If some custom getter (not ours) has been defined,
109 // there would be no way to read back the value stored by a default
110 // setter. Also, the only way to clear a custom getter is to first
111 // delete the property. Therefore, the value we have here should
112 // just go into a black hole.
120 * Replaces the given properties of the passed in object with stubs that log
121 * 'not available' warnings to the console when set.
123 * @param {Object} object The object with properties to disable. The prototype
125 * @param {string} objectName The display name to use in the error message
126 * thrown by the setter stub (this is the name that the object is commonly
127 * referred to by web developers, e.g. "document" instead of
129 * @param {Array<string>} propertyNames names of properties to disable.
131 function disableSetters(object, objectName, propertyNames, opt_messageSuffix) {
132 $Array.forEach(propertyNames, function(propertyName) {
133 var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
135 $Object.defineProperty(object, propertyName, {
146 // Disable benign Document methods.
147 disableMethods(HTMLDocument.prototype, 'document', ['open', 'clear', 'close']);
149 // Replace evil Document methods with exception-throwing stubs.
150 disableMethods(HTMLDocument.prototype, 'document', ['write', 'writeln'], true);
153 Object.defineProperty(window, "history", { value: {} });
154 disableGetters(window.history, 'history',
155 ['back', 'forward', 'go', 'length', 'pushState', 'replaceState']);
158 disableMethods(window, 'window', ['find']);
159 disableMethods(Window.prototype, 'window', ['find']);
161 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to
163 disableMethods(window, 'window', ['alert', 'confirm', 'prompt']);
164 disableMethods(Window.prototype, 'window', ['alert', 'confirm', 'prompt']);
166 // Disable window.*bar.
167 disableGetters(window, 'window',
168 ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar',
171 // Disable window.localStorage.
172 // Sometimes DOM security policy prevents us from doing this (e.g. for data:
173 // URLs) so wrap in try-catch.
175 disableGetters(window, 'window',
177 'Use chrome.storage.local instead.');
180 // Document instance properties that we wish to disable need to be set when
181 // the document begins loading, since only then will the "document" reference
182 // point to the page's document (it will be reset between now and then).
183 // We can't listen for the "readystatechange" event on the document (because
184 // the object that it's dispatched on doesn't exist yet), but we can instead
185 // do it at the window level in the capturing phase.
186 window.addEventListener('readystatechange', function(event) {
187 if (document.readyState != 'loading')
190 // Deprecated document properties from
191 // https://developer.mozilla.org/en/DOM/document.
192 // To deprecate document.all, simply changing its getter and setter would
193 // activate its cache mechanism, and degrade the performance. Here we assign
194 // it first to 'undefined' to avoid this.
195 document.all = undefined;
196 disableGetters(document, 'document',
197 ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor']);
200 // Disable onunload, onbeforeunload.
201 disableSetters(window, 'window', ['onbeforeunload', 'onunload']);
202 disableSetters(Window.prototype, 'window', ['onbeforeunload', 'onunload']);
203 var eventTargetAddEventListener = EventTarget.prototype.addEventListener;
204 EventTarget.prototype.addEventListener = function(type) {
205 if (type === 'unload' || type === 'beforeunload')
206 generateDisabledMethodStub(type)();
208 return $Function.apply(eventTargetAddEventListener, this, arguments);