Roll src/third_party/WebKit 395ff9e:18c0088 (svn 192995:192997)
[chromium-blink-merge.git] / extensions / renderer / resources / platform_app.js
blobb34a303e04895cdc9b248f555682d6428ab6a281
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;
7 /**
8  * Returns a function that logs a 'not available' error to the console and
9  * returns undefined.
10  *
11  * @param {string} messagePrefix text to prepend to the exception message.
12  */
13 function generateDisabledMethodStub(messagePrefix, opt_messageSuffix) {
14   var message = messagePrefix + ' is not available in packaged apps.';
15   if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
16   return function() {
17     $console.error(message);
18     return;
19   };
22 /**
23  * Returns a function that throws a 'not available' error.
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   };
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
58  *
59  * @param {Object} object The object with methods to disable. The prototype is
60  *     preferred.
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
67  */
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);
74   });
77 /**
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
81  * default behaviors.
82  *
83  * @param {Object} object The object with properties to disable. The prototype
84  *     is preferred.
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
88  *     "HTMLDocument").
89  * @param {Array<string>} propertyNames names of properties to disable.
90  */
91 function disableGetters(object, objectName, propertyNames, opt_messageSuffix) {
92   $Array.forEach(propertyNames, function(propertyName) {
93     var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
94                                           opt_messageSuffix);
95     stub._is_platform_app_disabled_getter = true;
96     $Object.defineProperty(object, propertyName, {
97       configurable: true,
98       enumerable: false,
99       get: stub,
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
106           // given value.
107           delete this[propertyName];
108           this[propertyName] = value;
109         } else {
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.
115         }
116       }
117     });
118   });
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
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   });
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);
154 // Disable history.
155 Object.defineProperty(window, "history", { value: {} });
156 disableGetters(window.history, 'history',
157     ['back', 'forward', 'go', 'length', 'pushState', 'replaceState']);
159 // Disable find.
160 disableMethods(Window.prototype, 'window', ['find']);
162 // Disable modal dialogs. Shell windows disable these anyway, but it's nice to
163 // warn.
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.prototype, 'window', ['onbeforeunload', 'onunload']);
202 var windowAddEventListener = Window.prototype.addEventListener;
203 Window.prototype.addEventListener = function(type) {
204   if (type === 'unload' || type === 'beforeunload')
205     generateDisabledMethodStub(type)();
206   else
207     return $Function.apply(windowAddEventListener, window, arguments);