You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

279 lines
8.8 KiB

10 years ago
10 years ago
10 years ago
10 years ago
  1. /*******************************************************************************
  2. uMatrix - a browser extension to black/white list requests.
  3. Copyright (C) 2014-present The uMatrix/uBlock Origin authors
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see {http://www.gnu.org/licenses/}.
  14. Home: https://github.com/gorhill/uMatrix
  15. */
  16. // For background page or non-background pages
  17. 'use strict';
  18. /******************************************************************************/
  19. /******************************************************************************/
  20. vAPI.T0 = Date.now();
  21. /******************************************************************************/
  22. vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
  23. /******************************************************************************/
  24. vAPI.webextFlavor = {
  25. major: 0,
  26. soup: new Set()
  27. };
  28. (( ) => {
  29. const ua = navigator.userAgent;
  30. const flavor = vAPI.webextFlavor;
  31. const soup = flavor.soup;
  32. const dispatch = function() {
  33. window.dispatchEvent(new CustomEvent('webextFlavor'));
  34. };
  35. // This is always true.
  36. soup.add('ublock').add('webext');
  37. // Whether this is a dev build.
  38. if ( /^\d+\.\d+\.\d+\D/.test(browser.runtime.getManifest().version) ) {
  39. soup.add('devbuild');
  40. }
  41. if ( /\bMobile\b/.test(ua) ) {
  42. soup.add('mobile');
  43. }
  44. // Asynchronous
  45. if (
  46. browser instanceof Object &&
  47. typeof browser.runtime.getBrowserInfo === 'function'
  48. ) {
  49. browser.runtime.getBrowserInfo().then(info => {
  50. flavor.major = parseInt(info.version, 10) || 60;
  51. soup.add(info.vendor.toLowerCase())
  52. .add(info.name.toLowerCase());
  53. if ( soup.has('firefox') && flavor.major < 57 ) {
  54. soup.delete('html_filtering');
  55. }
  56. dispatch();
  57. });
  58. if ( browser.runtime.getURL('').startsWith('moz-extension://') ) {
  59. soup.add('mozilla')
  60. .add('firefox')
  61. .add('user_stylesheet')
  62. .add('html_filtering');
  63. flavor.major = 60;
  64. }
  65. return;
  66. }
  67. // Synchronous -- order of tests is important
  68. let match;
  69. if ( (match = /\bEdge\/(\d+)/.exec(ua)) !== null ) {
  70. flavor.major = parseInt(match[1], 10) || 0;
  71. soup.add('microsoft').add('edge');
  72. } else if ( (match = /\bOPR\/(\d+)/.exec(ua)) !== null ) {
  73. const reEx = /\bChrom(?:e|ium)\/([\d.]+)/;
  74. if ( reEx.test(ua) ) { match = reEx.exec(ua); }
  75. flavor.major = parseInt(match[1], 10) || 0;
  76. soup.add('opera').add('chromium');
  77. } else if ( (match = /\bChromium\/(\d+)/.exec(ua)) !== null ) {
  78. flavor.major = parseInt(match[1], 10) || 0;
  79. soup.add('chromium');
  80. } else if ( (match = /\bChrome\/(\d+)/.exec(ua)) !== null ) {
  81. flavor.major = parseInt(match[1], 10) || 0;
  82. soup.add('google').add('chromium');
  83. } else if ( (match = /\bSafari\/(\d+)/.exec(ua)) !== null ) {
  84. flavor.major = parseInt(match[1], 10) || 0;
  85. soup.add('apple').add('safari');
  86. }
  87. // https://github.com/gorhill/uBlock/issues/3588
  88. if ( soup.has('chromium') && flavor.major >= 66 ) {
  89. soup.add('user_stylesheet');
  90. }
  91. // Don't starve potential listeners
  92. vAPI.setTimeout(dispatch, 97);
  93. })();
  94. /******************************************************************************/
  95. {
  96. const punycode = self.punycode;
  97. const reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
  98. const reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
  99. const reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
  100. const reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
  101. const reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
  102. const reMustNormalizeHostname = /[^0-9a-z._-]/;
  103. vAPI.hostnameFromURI = function(uri) {
  104. let matches = reCommonHostnameFromURL.exec(uri);
  105. if ( matches !== null ) { return matches[1]; }
  106. matches = reAuthorityFromURI.exec(uri);
  107. if ( matches === null ) { return ''; }
  108. const authority = matches[1].slice(2);
  109. if ( reHostFromNakedAuthority.test(authority) ) {
  110. return authority.toLowerCase();
  111. }
  112. matches = reHostFromAuthority.exec(authority);
  113. if ( matches === null ) {
  114. matches = reIPv6FromAuthority.exec(authority);
  115. if ( matches === null ) { return ''; }
  116. }
  117. let hostname = matches[1];
  118. while ( hostname.endsWith('.') ) {
  119. hostname = hostname.slice(0, -1);
  120. }
  121. if ( reMustNormalizeHostname.test(hostname) ) {
  122. hostname = punycode.toASCII(hostname.toLowerCase());
  123. }
  124. return hostname;
  125. };
  126. const reHostnameFromNetworkURL =
  127. /^(?:http|ws|ftp)s?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
  128. vAPI.hostnameFromNetworkURL = function(url) {
  129. const matches = reHostnameFromNetworkURL.exec(url);
  130. return matches !== null ? matches[1] : '';
  131. };
  132. const psl = self.publicSuffixList;
  133. const reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
  134. vAPI.domainFromHostname = function(hostname) {
  135. return reIPAddressNaive.test(hostname)
  136. ? hostname
  137. : psl.getDomain(hostname);
  138. };
  139. vAPI.domainFromURI = function(uri) {
  140. return uri !== ''
  141. ? vAPI.domainFromHostname(vAPI.hostnameFromURI(uri))
  142. : '';
  143. };
  144. }
  145. /******************************************************************************/
  146. vAPI.download = function(details) {
  147. if ( !details.url ) { return; }
  148. const a = document.createElement('a');
  149. a.href = details.url;
  150. a.setAttribute('download', details.filename || '');
  151. a.setAttribute('type', 'text/plain');
  152. a.dispatchEvent(new MouseEvent('click'));
  153. };
  154. /******************************************************************************/
  155. vAPI.getURL = browser.runtime.getURL;
  156. /******************************************************************************/
  157. vAPI.i18n = browser.i18n.getMessage;
  158. // http://www.w3.org/International/questions/qa-scripts#directions
  159. document.body.setAttribute(
  160. 'dir',
  161. ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(vAPI.i18n('@@ui_locale')) !== -1
  162. ? 'rtl'
  163. : 'ltr'
  164. );
  165. /******************************************************************************/
  166. // https://github.com/gorhill/uBlock/issues/3057
  167. // - webNavigation.onCreatedNavigationTarget become broken on Firefox when we
  168. // try to make the popup panel close itself using the original
  169. // `window.open('', '_self').close()`.
  170. vAPI.closePopup = function() {
  171. if ( vAPI.webextFlavor.soup.has('firefox') ) {
  172. window.close();
  173. return;
  174. }
  175. // TODO: try to figure why this was used instead of a plain window.close().
  176. // https://github.com/gorhill/uBlock/commit/b301ac031e0c2e9a99cb6f8953319d44e22f33d2#diff-bc664f26b9c453e0d43a9379e8135c6a
  177. window.open('', '_self').close();
  178. };
  179. /******************************************************************************/
  180. // A localStorage-like object which should be accessible from the
  181. // background page or auxiliary pages.
  182. // This storage is optional, but it is nice to have, for a more polished user
  183. // experience.
  184. // https://github.com/gorhill/uBlock/issues/2824
  185. // Use a dummy localStorage if for some reasons it's not available.
  186. // https://github.com/gorhill/uMatrix/issues/840
  187. // Always use a wrapper to seamlessly handle exceptions
  188. vAPI.localStorage = {
  189. clear: function() {
  190. try {
  191. window.localStorage.clear();
  192. } catch(ex) {
  193. }
  194. },
  195. getItem: function(key) {
  196. try {
  197. return window.localStorage.getItem(key);
  198. } catch(ex) {
  199. }
  200. return null;
  201. },
  202. removeItem: function(key) {
  203. try {
  204. window.localStorage.removeItem(key);
  205. } catch(ex) {
  206. }
  207. },
  208. setItem: function(key, value) {
  209. try {
  210. window.localStorage.setItem(key, value);
  211. } catch(ex) {
  212. }
  213. }
  214. };
  215. /*******************************************************************************
  216. DO NOT:
  217. - Remove the following code
  218. - Add code beyond the following code
  219. Reason:
  220. - https://github.com/gorhill/uBlock/pull/3721
  221. - uBO never uses the return value from injected content scripts
  222. **/
  223. void 0;