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.
5 var $console
= window
.console
;
8 * Returns a function that logs a 'not available' error to the console and
11 * @param {string} messagePrefix text to prepend to the exception message.
13 function generateDisabledMethodStub(messagePrefix
, opt_messageSuffix
) {
14 var message
= messagePrefix
+ ' is not available in packaged apps.';
15 if (opt_messageSuffix
) message
= message
+ ' ' + opt_messageSuffix
;
17 $console
.error(message
);
23 * Returns a function that throws a 'not available' error.
25 * @param {string} messagePrefix text to prepend to the exception message.
27 function generateThrowingMethodStub(messagePrefix
, opt_messageSuffix
) {
28 var message
= messagePrefix
+ ' is not available in packaged apps.';
29 if (opt_messageSuffix
) message
= message
+ ' ' + opt_messageSuffix
;
31 throw new Error(message
);
36 * Replaces the given methods of the passed in object with stubs that log
37 * 'not available' errors to the console and return undefined.
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.
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 ...?');
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
59 * @param {Object} object The object with methods to disable. The prototype is
61 * @param {string} objectName The display name to use in the error message
62 * thrown by the stub (this is the name that the object is commonly referred
63 * to by web developers, e.g. "document" instead of "HTMLDocument").
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
68 function disableMethods(object
, objectName
, methodNames
, useThrowingStubs
) {
69 $Array
.forEach(methodNames
, function(methodName
) {
70 var messagePrefix
= objectName
+ '.' + methodName
+ '()';
71 object
[methodName
] = useThrowingStubs
?
72 generateThrowingMethodStub(messagePrefix
) :
73 generateDisabledMethodStub(messagePrefix
);
78 * Replaces the given properties of the passed in object with stubs that log
79 * 'not available' warnings to the console and return undefined when gotten. If
80 * a property's setter is later invoked, the getter and setter are restored to
83 * @param {Object} object The object with properties to disable. The prototype
85 * @param {string} objectName The display name to use in the error message
86 * thrown by the getter stub (this is the name that the object is commonly
87 * referred to by web developers, e.g. "document" instead of
89 * @param {Array.<string>} propertyNames names of properties to disable.
91 function disableGetters(object
, objectName
, propertyNames
, opt_messageSuffix
) {
92 $Array
.forEach(propertyNames
, function(propertyName
) {
93 var stub
= generateDisabledMethodStub(objectName
+ '.' + propertyName
,
95 stub
._is_platform_app_disabled_getter
= true;
96 $Object
.defineProperty(object
, propertyName
, {
100 set: function(value
) {
101 var descriptor
= $Object
.getOwnPropertyDescriptor(this, propertyName
);
102 if (!descriptor
|| !descriptor
.get ||
103 descriptor
.get._is_platform_app_disabled_getter
) {
104 // The stub getter is still defined. Blow-away the property to
105 // restore default getter/setter behaviors and re-create it with the
107 delete this[propertyName
];
108 this[propertyName
] = value
;
110 // Do nothing. If some custom getter (not ours) has been defined,
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.
122 * Replaces the given properties of the passed in object with stubs that log
123 * 'not available' warnings to the console when set.
125 * @param {Object} object The object with properties to disable. The prototype
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
131 * @param {Array.<string>} propertyNames names of properties to disable.
133 function disableSetters(object
, objectName
, propertyNames
, opt_messageSuffix
) {
134 $Array
.forEach(propertyNames
, function(propertyName
) {
135 var stub
= generateDisabledMethodStub(objectName
+ '.' + propertyName
,
137 $Object
.defineProperty(object
, propertyName
, {
148 // Disable benign Document methods.
149 disableMethods(HTMLDocument
.prototype, 'document', ['open', 'clear', 'close']);
151 // Replace evil Document methods with exception-throwing stubs.
152 disableMethods(HTMLDocument
.prototype, 'document', ['write', 'writeln'], true);
155 Object
.defineProperty(window
, "history", { value
: {} });
156 disableGetters(window
.history
, 'history', ['back', 'forward', 'go', 'length']);
159 disableMethods(Window
.prototype, 'window', ['find']);
161 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to
163 disableMethods(Window
.prototype, 'window', ['alert', 'confirm', 'prompt']);
165 // Disable window.*bar.
166 disableGetters(window
, 'window',
167 ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar',
170 // Disable window.localStorage.
171 // Sometimes DOM security policy prevents us from doing this (e.g. for data:
172 // URLs) so wrap in try-catch.
174 disableGetters(window
, 'window',
176 'Use chrome.storage.local instead.');
179 // Document instance properties that we wish to disable need to be set when
180 // the document begins loading, since only then will the "document" reference
181 // point to the page's document (it will be reset between now and then).
182 // We can't listen for the "readystatechange" event on the document (because
183 // the object that it's dispatched on doesn't exist yet), but we can instead
184 // do it at the window level in the capturing phase.
185 window
.addEventListener('readystatechange', function(event
) {
186 if (document
.readyState
!= 'loading')
189 // Deprecated document properties from
190 // https://developer.mozilla.org/en/DOM/document.
191 // To deprecate document.all, simply changing its getter and setter would
192 // activate its cache mechanism, and degrade the performance. Here we assign
193 // it first to 'undefined' to avoid this.
194 document
.all
= undefined;
195 disableGetters(document
, 'document',
196 ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor']);
199 // Disable onunload, onbeforeunload.
200 disableSetters(Window
.prototype, 'window', ['onbeforeunload', 'onunload']);
201 var windowAddEventListener
= Window
.prototype.addEventListener
;
202 Window
.prototype.addEventListener = function(type
) {
203 if (type
=== 'unload' || type
=== 'beforeunload')
204 generateDisabledMethodStub(type
)();
206 return $Function
.apply(windowAddEventListener
, window
, arguments
);