How do I prevent the browser from preloading the &

2020-07-06 06:20发布

问题:

I have written a user script extension for Chrome with JavaScript in order to prevent video and audio tags from downloading automatically on pageload

This is the code:

var videoTags = document.getElementsByTagName("Video");
var i;
for(i=0; i<videoTags.length; i++)
{
    videoTags[i].setAttribute("preload", "none");
    videoTags[i].removeAttribute("autoplay");
}

var audioTags = document.getElementsByTagName("audio");
var i;
for(i=0; i<audioTags.length; i++)
{
    audioTags[i].setAttribute("preload", "none");
    audioTags[i].removeAttribute("autoplay");
}

And this is the manifest.json file:

   {
      "content_scripts": [ {
      "exclude_globs": [  ],
      "exclude_matches": [  ],
      "include_globs": [ "*" ],
      "js": [ "script.js" ],
      "matches": [ "http://*/*", "https://*/*" ],
      "run_at": "document_start"
   } ],
      "converted_from_user_script": true,
      "description": "",
      "key": "an2xaeZJluPfnpmcsHPXI4aajQPL1cBm5C2kKjaQwXA=",
      "name": "test.user.js",
      "version": "1.0"
  }

The problem is that my script runs after a moment and until then the browser (Chrome) downloads a part of video/audio file.

回答1:

One dirty solution that seems to work is to use a MutationObserver from your userscript, once you're sure it does run at document-start.

This TamperMonkey script does work for me :

// ==UserScript==
// @name         Block videos preloading
// @include      *
// @run-at document-start
// ==/UserScript==

(function() {
  'use strict';
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      var nodes = mutation.addedNodes;
      for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].nodeName == "VIDEO") {
          nodes[i].setAttribute('preload', 'none');
          nodes[i].removeAttribute('autoplay');
        }
      }
    })
  });
  observer.observe(document.documentElement, {
    childList: true,
    subtree: true
  });

})();

You may want to call observer.disconnect() on DOMContentLoaded, if you're not expecting other video elements for being inserted afterwards by scripts.



回答2:

Check if what you think really happens. It should be impossible, because both "run_at": "document_start" and "run_at": "document_end" should make your code run before anything is loaded.

From the documentation in developer.chrome.com:

In the case of "document_start", the files are injected after any files from css, but before any other DOM is constructed or any other script is run.

In the case of "document_end", the files are injected immediately after the DOM is complete, but before subresources like images and frames have loaded.

In addition, because your code runs in document_start, the document.getElementsByTagName("Video") should fail because no DOM is even constructed yet.

Try debugging your code (start with checking for errors in the console). Also, read more about the "run_at" attribute here: https://developer.chrome.com/extensions/content_scripts#run_at



回答3:

Try storing <audio>, <video> src value, then remove src attribute from <audio>, <video> elements for setting preload, autoplay attributes; using DOMContentLoaded event

The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <script>
    var sources = [];
    document.addEventListener("DOMContentLoaded", function(event) {
      var media = document.querySelectorAll("audio, video");
      [].forEach.call(media, function(el) {
        if (el.src) {
          sources.push(el.src);
          el.removeAttribute("src");
        }
        var src = el.querySelectorAll("source");
        if (src.length) {
          [].forEach.call(src, function(source) {
            sources.push(source.src);
            source.removeAttribute("src");
          });
        };
      });
      console.log(sources);
    });
  </script>
</head>
<body style="height:270px">
  <video src="http://mirrors.creativecommons.org/movingimages/webm/ScienceCommonsJesseDylan_240p.webm" controls></video>
 <audio controls>
    <source src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Micronesia_National_Anthem.ogg" type="video/ogg" />
  </audio>
</body>
</html>


Edit, Updated

can you test it in user script ?

Utilizing content_scripts, "run_at": "document_start" in manifest.json returned expected results as a chromium, chrome extension; that is, src attribute of <audio>, <video>, <source> elements should be removed from document.

manifest.json

{
  "manifest_version": 2,
  "name": "blockmedia",
  "description": "remove src from audio, video elements",
  "version": "1.0",
  "permissions": ["<all_urls>"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["script.js"],
      "run_at": "document_start"
    }
  ]
}

script.js

var sources = [];
document.addEventListener("DOMContentLoaded", function(event) {
  var media = document.querySelectorAll("audio, video");
  [].forEach.call(media, function(el) {
    if (el.src) {
      sources.push(el.src);
      el.removeAttribute("src");
    }
    var src = el.querySelectorAll("source");
    if (src.length) {
      [].forEach.call(src, function(source) {
        sources.push(source.src);
        source.removeAttribute("src");
      });
    };
  });
 console.log(sources);
});



回答4:

You're battling against the Chrome parser preloader.

Even if you inject a content script at document start, using Mutation Observer or another technique suggested above, you still cannot action the HTML before the browser preloader.

Read this article, which helped me.

https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/

To achieve what you want to do, you're going to need to use the Chrome webRequest API to block the request.

https://developer.chrome.com/extensions/webRequest

You will need to filter requests according to the media type and then search the headers for the file types you want to block.