Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / renderer / resources / platform_app.js
blobcaf935a3ca5f03dba118208bbd07344f8ba3a23f
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 /**
6  * Returns a function that logs a 'not available' error to the console and
7  * returns undefined.
8  *
9  * @param {string} messagePrefix text to prepend to the exception message.
10  */
11 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) {
12   var message = messagePrefix + ' is not available in packaged apps.';
13   if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
14   return function() {
15     console.error(message);
16     return;
17   };
20 /**
21  * Returns a function that throws a 'not available' error.
22  *
23  * @param {string} messagePrefix text to prepend to the exception message.
24  */
25 function generateThrowingMethodStub(messagePrefix, opt_messageSuffix) {
26   var message = messagePrefix + ' is not available in packaged apps.';
27   if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
28   return function() {
29     throw new Error(message);
30   };
33 /**
34  * Replaces the given methods of the passed in object with stubs that log
35  * 'not available' errors to the console and return undefined.
36  *
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.
40  *
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 ...?');
47  *       ...
48  *     }
49  *
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
56  *
57  * @param {Object} object The object with methods to disable. The prototype is
58  *     preferred.
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
65  */
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);
72   });
75 /**
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
79  * default behaviors.
80  *
81  * @param {Object} object The object with properties to disable. The prototype
82  *     is preferred.
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
86  *     "HTMLDocument").
87  * @param {Array<string>} propertyNames names of properties to disable.
88  */
89 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
90   $Array.forEach(propertyNames, function(propertyName) {
91     var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
92                                           opt_messageSuffix);
93     stub._is_platform_app_disabled_getter = true;
94     $Object.defineProperty(object, propertyName, {
95       configurable: true,
96       enumerable: false,
97       get: stub,
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
104           // given value.
105           delete this[propertyName];
106           this[propertyName] = value;
107         } else {
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.
113         }
114       }
115     });
116   });
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
124  *     is preferred.
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
128  *     "HTMLDocument").
129  * @param {Array<string>} propertyNames names of properties to disable.
130  */
131 function disableSetters(object, objectName, propertyNames, opt_messageSuffix) {
132   $Array.forEach(propertyNames, function(propertyName) {
133     var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
134                                           opt_messageSuffix);
135     $Object.defineProperty(object, propertyName, {
136       configurable: true,
137       enumerable: false,
138       get: function() {
139         return;
140       },
141       set: stub
142     });
143   });
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);
152 // Disable history.
153 Object.defineProperty(window, "history", { value: {} });
154 disableGetters(window.history, 'history',
155     ['back', 'forward', 'go', 'length', 'pushState', 'replaceState']);
157 // Disable find.
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
162 // warn.
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',
169      'toolbar']);
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.
174 try {
175   disableGetters(window, 'window',
176       ['localStorage'],
177       'Use chrome.storage.local instead.');
178 } catch (e) {}
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')
188     return;
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']);
198 }, true);
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)();
207   else
208     return $Function.apply(eventTargetAddEventListener, this, arguments);