platform_app.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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.
  4. var logging = requireNative('logging');
  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. };
  18. }
  19. /**
  20. * Returns a function that throws a 'not available' error.
  21. *
  22. * @param {string} messagePrefix text to prepend to the exception message.
  23. */
  24. function generateThrowingMethodStub(messagePrefix, opt_messageSuffix) {
  25. var message = messagePrefix + ' is not available in packaged apps.';
  26. if (opt_messageSuffix) message = message + ' ' + opt_messageSuffix;
  27. return function() {
  28. throw new Error(message);
  29. };
  30. }
  31. /**
  32. * Replaces the given methods of the passed in object with stubs that log
  33. * 'not available' errors to the console and return undefined.
  34. *
  35. * This should be used on methods attached via non-configurable properties,
  36. * such as window.alert. disableGetters should be used when possible, because
  37. * it is friendlier towards feature detection.
  38. *
  39. * In most cases, the useThrowingStubs should be false, so the stubs used to
  40. * replace the methods log an error to the console, but allow the calling code
  41. * to continue. We shouldn't break library code that uses feature detection
  42. * responsibly, such as:
  43. * if(window.confirm) {
  44. * var result = window.confirm('Are you sure you want to delete ...?');
  45. * ...
  46. * }
  47. *
  48. * useThrowingStubs should only be true for methods that are deprecated in the
  49. * Web platform, and should not be used by a responsible library, even in
  50. * conjunction with feature detection. A great example is document.write(), as
  51. * the HTML5 specification recommends against using it, and says that its
  52. * behavior is unreliable. No reasonable library code should ever use it.
  53. * HTML5 spec: http://www.w3.org/TR/html5/dom.html#dom-document-write
  54. *
  55. * @param {Object} object The object with methods to disable. The prototype is
  56. * preferred.
  57. * @param {string} objectName The display name to use in the error message
  58. * thrown by the stub (this is the name that the object is commonly referred
  59. * to by web developers, e.g. "document" instead of "HTMLDocument").
  60. * @param {Array<string>} methodNames names of methods to disable.
  61. * @param {Boolean} useThrowingStubs if true, the replaced methods will throw
  62. * an error instead of silently returning undefined
  63. */
  64. function disableMethods(object, objectName, methodNames, useThrowingStubs) {
  65. $Array.forEach(methodNames, function(methodName) {
  66. logging.DCHECK($Object.getOwnPropertyDescriptor(object, methodName),
  67. objectName + ': ' + methodName);
  68. var messagePrefix = objectName + '.' + methodName + '()';
  69. $Object.defineProperty(object, methodName, {
  70. configurable: false,
  71. enumerable: false,
  72. value: useThrowingStubs ?
  73. generateThrowingMethodStub(messagePrefix) :
  74. generateDisabledMethodStub(messagePrefix)
  75. });
  76. });
  77. }
  78. /**
  79. * Replaces the given properties of the passed in object with stubs that log
  80. * 'not available' warnings to the console and return undefined when gotten. If
  81. * a property's setter is later invoked, the getter and setter are restored to
  82. * default behaviors.
  83. *
  84. * @param {Object} object The object with properties to disable. The prototype
  85. * is preferred.
  86. * @param {string} objectName The display name to use in the error message
  87. * thrown by the getter stub (this is the name that the object is commonly
  88. * referred to by web developers, e.g. "document" instead of
  89. * "HTMLDocument").
  90. * @param {Array<string>} propertyNames names of properties to disable.
  91. * @param {?string=} opt_messageSuffix An optional suffix for the message.
  92. * @param {boolean=} opt_ignoreMissingProperty True if we allow disabling
  93. * getters for non-existent properties.
  94. */
  95. function disableGetters(object, objectName, propertyNames, opt_messageSuffix,
  96. opt_ignoreMissingProperty) {
  97. $Array.forEach(propertyNames, function(propertyName) {
  98. logging.DCHECK(opt_ignoreMissingProperty ||
  99. $Object.getOwnPropertyDescriptor(object, propertyName),
  100. objectName + ': ' + propertyName);
  101. var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
  102. opt_messageSuffix);
  103. stub._is_platform_app_disabled_getter = true;
  104. $Object.defineProperty(object, propertyName, {
  105. configurable: true,
  106. enumerable: false,
  107. get: stub,
  108. set: function(value) {
  109. var descriptor = $Object.getOwnPropertyDescriptor(this, propertyName);
  110. if (!descriptor || !descriptor.get ||
  111. descriptor.get._is_platform_app_disabled_getter) {
  112. // The stub getter is still defined. Blow-away the property to
  113. // restore default getter/setter behaviors and re-create it with the
  114. // given value.
  115. delete this[propertyName];
  116. this[propertyName] = value;
  117. } else {
  118. // Do nothing. If some custom getter (not ours) has been defined,
  119. // there would be no way to read back the value stored by a default
  120. // setter. Also, the only way to clear a custom getter is to first
  121. // delete the property. Therefore, the value we have here should
  122. // just go into a black hole.
  123. }
  124. }
  125. });
  126. });
  127. }
  128. /**
  129. * Replaces the given properties of the passed in object with stubs that log
  130. * 'not available' warnings to the console when set.
  131. *
  132. * @param {Object} object The object with properties to disable. The prototype
  133. * is preferred.
  134. * @param {string} objectName The display name to use in the error message
  135. * thrown by the setter stub (this is the name that the object is commonly
  136. * referred to by web developers, e.g. "document" instead of
  137. * "HTMLDocument").
  138. * @param {Array<string>} propertyNames names of properties to disable.
  139. */
  140. function disableSetters(object, objectName, propertyNames, opt_messageSuffix) {
  141. $Array.forEach(propertyNames, function(propertyName) {
  142. logging.DCHECK($Object.getOwnPropertyDescriptor(object, propertyName),
  143. objectName + ': ' + propertyName);
  144. var stub = generateDisabledMethodStub(objectName + '.' + propertyName,
  145. opt_messageSuffix);
  146. $Object.defineProperty(object, propertyName, {
  147. configurable: false,
  148. enumerable: false,
  149. get: function() {
  150. return;
  151. },
  152. set: stub
  153. });
  154. });
  155. }
  156. // Disable benign Document methods.
  157. disableMethods(Document.prototype, 'document', ['open', 'close']);
  158. disableMethods(Document.prototype, 'document', ['clear']);
  159. // Replace evil Document methods with exception-throwing stubs.
  160. disableMethods(Document.prototype, 'document', ['write', 'writeln'], true);
  161. // Disable history.
  162. Object.defineProperty(window, "history", { value: {} });
  163. // Note: we just blew away the history object, so we need to ignore the fact
  164. // that these properties aren't defined on the object.
  165. disableGetters(window.history, 'history',
  166. ['back', 'forward', 'go', 'length', 'pushState', 'replaceState', 'state'],
  167. null, true);
  168. // Disable find.
  169. disableMethods(window, 'window', ['find']);
  170. // Disable modal dialogs. Shell windows disable these anyway, but it's nice to
  171. // warn.
  172. disableMethods(window, 'window', ['alert', 'confirm', 'prompt']);
  173. // Disable window.*bar.
  174. disableGetters(window, 'window',
  175. ['locationbar', 'menubar', 'personalbar', 'scrollbars', 'statusbar',
  176. 'toolbar']);
  177. // Disable window.localStorage.
  178. // Sometimes DOM security policy prevents us from doing this (e.g. for data:
  179. // URLs) so wrap in try-catch.
  180. try {
  181. disableGetters(window, 'window',
  182. ['localStorage'],
  183. 'Use chrome.storage.local instead.');
  184. } catch (e) {}
  185. function disableDeprectatedDocumentFunction() {
  186. // Deprecated document properties from
  187. // https://developer.mozilla.org/en/DOM/document.
  188. // Disable document.all so that platform apps can not access.
  189. delete Document.prototype.all
  190. disableGetters(document, 'document',
  191. ['alinkColor', 'all', 'bgColor', 'fgColor', 'linkColor', 'vlinkColor'],
  192. null, true);
  193. }
  194. // The new document may or may not already have been created when this script is
  195. // executed. In the second case, the current document is still the initial empty
  196. // document. There are no way to know whether 'document' refers to the old one
  197. // or the new one. That's why, the deprecated document properties needs to be
  198. // disabled on the current document and potentially on the new one, if it gets
  199. // created.
  200. disableDeprectatedDocumentFunction();
  201. window.addEventListener('readystatechange', function(event) {
  202. if (document.readyState == 'loading')
  203. disableDeprectatedDocumentFunction();
  204. }, true);
  205. // Disable onunload, onbeforeunload.
  206. disableSetters(window, 'window', ['onbeforeunload', 'onunload']);
  207. var eventTargetAddEventListener = EventTarget.prototype.addEventListener;
  208. EventTarget.prototype.addEventListener = function(type) {
  209. var args = $Array.slice(arguments);
  210. // Note: Force conversion to a string in order to catch any funny attempts
  211. // to pass in something that evals to 'unload' but wouldn't === 'unload'.
  212. var type = (args[0] += '');
  213. if (type === 'unload' || type === 'beforeunload')
  214. generateDisabledMethodStub(type)();
  215. else
  216. return $Function.apply(eventTargetAddEventListener, this, args);
  217. };