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.

185 lines
6.1 KiB

  1. /*******************************************************************************
  2. uMatrix - a browser extension to block requests.
  3. Copyright (C) 2017-present Raymond Hill
  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
  17. 'use strict';
  18. /******************************************************************************/
  19. (function() {
  20. // https://github.com/gorhill/uBlock/issues/2950
  21. // Firefox 56 does not normalize URLs to ASCII, uBO must do this itself.
  22. // https://bugzilla.mozilla.org/show_bug.cgi?id=945240
  23. const evalMustPunycode = function() {
  24. return vAPI.webextFlavor.soup.has('firefox') &&
  25. vAPI.webextFlavor.major < 57;
  26. };
  27. let mustPunycode = evalMustPunycode();
  28. // The real actual webextFlavor value may not be set in stone, so listen
  29. // for possible future changes.
  30. window.addEventListener('webextFlavor', ( ) => {
  31. mustPunycode = evalMustPunycode();
  32. }, { once: true });
  33. const denormalizeTypes = function(aa) {
  34. if ( aa.length === 0 ) {
  35. return Array.from(vAPI.net.validTypes);
  36. }
  37. const out = new Set();
  38. let i = aa.length;
  39. while ( i-- ) {
  40. let type = aa[i];
  41. if ( vAPI.net.validTypes.has(type) ) {
  42. out.add(type);
  43. }
  44. if ( type === 'image' && vAPI.net.validTypes.has('imageset') ) {
  45. out.add('imageset');
  46. }
  47. if ( type === 'sub_frame' ) {
  48. out.add('object');
  49. }
  50. }
  51. return Array.from(out);
  52. };
  53. const punycode = self.punycode;
  54. const reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
  55. const parsedURL = new URL('about:blank');
  56. vAPI.net.normalizeDetails = function(details) {
  57. if ( mustPunycode && !reAsciiHostname.test(details.url) ) {
  58. parsedURL.href = details.url;
  59. details.url = details.url.replace(
  60. parsedURL.hostname,
  61. punycode.toASCII(parsedURL.hostname)
  62. );
  63. }
  64. const type = details.type;
  65. // https://github.com/gorhill/uBlock/issues/1493
  66. // Chromium 49+/WebExtensions support a new request type: `ping`,
  67. // which is fired as a result of using `navigator.sendBeacon`.
  68. if ( type === 'ping' ) {
  69. details.type = 'beacon';
  70. return;
  71. }
  72. if ( type === 'imageset' ) {
  73. details.type = 'image';
  74. return;
  75. }
  76. // https://github.com/uBlockOrigin/uBlock-issues/issues/345
  77. // Re-categorize an embedded object as a `sub_frame` if its
  78. // content type is that of a HTML document.
  79. if ( type === 'object' && Array.isArray(details.responseHeaders) ) {
  80. for ( const header of details.responseHeaders ) {
  81. if ( header.name.toLowerCase() === 'content-type' ) {
  82. if ( header.value.startsWith('text/html') ) {
  83. details.type = 'sub_frame';
  84. }
  85. break;
  86. }
  87. }
  88. }
  89. };
  90. vAPI.net.denormalizeFilters = function(filters) {
  91. const urls = filters.urls || [ '<all_urls>' ];
  92. let types = filters.types;
  93. if ( Array.isArray(types) ) {
  94. types = denormalizeTypes(types);
  95. }
  96. if (
  97. (vAPI.net.validTypes.has('websocket')) &&
  98. (types === undefined || types.indexOf('websocket') !== -1) &&
  99. (urls.indexOf('<all_urls>') === -1)
  100. ) {
  101. if ( urls.indexOf('ws://*/*') === -1 ) {
  102. urls.push('ws://*/*');
  103. }
  104. if ( urls.indexOf('wss://*/*') === -1 ) {
  105. urls.push('wss://*/*');
  106. }
  107. }
  108. return { types, urls };
  109. };
  110. })();
  111. /******************************************************************************/
  112. // Related issues:
  113. // - https://github.com/gorhill/uBlock/issues/1327
  114. // - https://github.com/uBlockOrigin/uBlock-issues/issues/128
  115. // - https://bugzilla.mozilla.org/show_bug.cgi?id=1503721
  116. vAPI.net.onBeforeReady = (function() {
  117. let pendings;
  118. const handler = function(details) {
  119. if ( pendings === undefined ) { return; }
  120. if ( details.tabId < 0 ) { return; }
  121. //console.log(`Deferring tab ${details.tabId}: ${details.type} ${details.url}`);
  122. const pending = {
  123. details: Object.assign({}, details),
  124. resolve: undefined,
  125. promise: undefined
  126. };
  127. pending.promise = new Promise(function(resolve) {
  128. pending.resolve = resolve;
  129. });
  130. pendings.push(pending);
  131. return pending.promise;
  132. };
  133. return {
  134. start: function() {
  135. pendings = [];
  136. browser.webRequest.onBeforeRequest.addListener(
  137. handler,
  138. { urls: [ 'http://*/*', 'https://*/*' ] },
  139. [ 'blocking' ]
  140. );
  141. },
  142. stop: function(resolver) {
  143. if ( pendings === undefined ) { return; }
  144. for ( const pending of pendings ) {
  145. const details = pending.details;
  146. vAPI.net.normalizeDetails(details);
  147. //console.log(`Processing tab ${details.tabId}: ${details.type} ${details.url}`);
  148. pending.resolve(resolver(details));
  149. }
  150. pendings = undefined;
  151. },
  152. };
  153. })();
  154. /******************************************************************************/