问题
Is there a way to replace the complete HTML, and add additional javascript files with mozilla addon SDK?
With chrome it can be done by running the script at "document_start", stop the window and replace the complete HTML with an XHR response. I don't understand why is this so complicated, but I can live with that. As far as I understand the addon SDK has a page-mod module, which is about "running scripts in the context of web pages whose URL matches a given pattern." So in theory this should be done with the page-mod module, but I did not find any example which completely overrides the whole HTML. In theory the window.stop and replacing the complete HTML should work here too, but I am not able to access the addon (backend) files from the context of the webpage (frontend). By chrome this is done via "web_accessible_resources" in the manifest and chrome.extension.getURL. By firefox I cannot use anything related to the addon SDK by frontend, so self.data.url does not work...
回答1:
I tried to send the file contents this way (since the addon file access was denied by firefox from the frontend):
index.js
var pageMod = require("sdk/page-mod");
var data = require("sdk/self").data;
pageMod.PageMod({
include: ["http://example.com/", "http://example.com/index.html"],
contentScriptFile: "./inject.js",
contentScriptWhen: "start",
onAttach: function (worker){
console.log("injector attached");
worker.port.emit("inject", {
"index.html": data.load("client/index.html"),
"main.js": data.load("client/main.js")
});
}
});
inject.js
!function () {
self.port.on("inject", function (files) {
console.log("injector trigger");
if (typeof hasRun == 'undefined') {
console.log("injecting");
hasRun = true;
window.stop();
var html = files["index.html"].replace("<script src=\"./main.js\"></script>", "<script>"+files["main.js"]+"</script>");
document.documentElement.innerHTML = html;
}
});
}();
If I replace the HTML with hello world, then it works. So the HTML appears to be invalid, but I got no error message and the console shows an empty HTML skeleton (I got an empty page). The same index.html and main.js code works by a chrome plugin, which I want to use in firefox. The only extra thing by the chrome plugin, that some js file is cancelled out, so they are surely not loaded before stopping the window. I tried to do the same, but maybe it did not work, I don't know.
index.js
let { Ci, Cu } = require('chrome');
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var observer = {
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference
]),
observe: function (subject, topic, data) {
if (topic == "http-on-opening-request" &&
subject instanceof Ci.nsIHttpChannel) {
var uri = subject.URI;
if (uri.host == "example.com" && /some\.js/.test(uri.path))
subject.cancel();
}
}
};
Services.obs.addObserver(observer, "http-on-opening-request", true);
The subject.cancel() runs by the some.js file. If I add logging:
console.log("cancelling", uri.path);
subject.cancel();
console.log("cancelled");
Then the "cancelled" does not appear in the console, just the "cancelling, /some.js". I don't know whether this is normal, but I got no error message.
By trying this with a regular HTML webpage
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
//<![CDATA[
!function (){
window.stop();
var html = '<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n</head>\n<body>\n <script>console.log("loaded");</script>\ntext\n</body>\n</html>';
document.documentElement.innerHTML = html;
}();
//]]>
</script>
</body>
</html>
I got an unterminated string literal syntax error, which is even funnier. I guess somehow the injector code does not work properly if it contains javascript, so this is not an addon related issue I think. If I use <\/script>
then it is okay, but the console.log("loaded");
script does not run. By chrome it does run, so that is the problem I think.
I added jquery to the "contentScriptFile" and used $("html").html(html)
instead document.documentElement.innerHTML = html
. It runs the scripts now, but still does not work properly.
Added some fix by cancel():
let { Ci, Cu, Cr } = require('chrome');
//...
subject.cancel(Cr.NS_BINDING_ABORTED);
cancel appears to have a required status parameter, but it silently fails when it does not get it. Now cancelling files works too, but the plugin still fails somewhere. :S
I think the injector I wrote works perfectly, and the problem is with the injected HTML and js files. I don't intend to debug them (since $.load uses eval, so it would be very hard), I sent the code to the chrome plugin developers, maybe they can fix it somehow.
来源:https://stackoverflow.com/questions/30931703/replace-complete-html-by-mozilla-addon-sdk