I've never tried to create WebExtensions before. I'm trying to create a Helper for one web-site. I map content script on the appropriate page, but the web-site use only asynchronous calls to navigate between pages. In ff's console I see that all calls are marked as xhr. So my script is invoked only on reloading the particular page manually, but not while navigating.
Is it possible to map xhr calls and content scripts?
Should I use a kind of interceptor to do so? Is there a best practice to resolve this?
The best solution I came out with is to extend the content script's visibility in manifest to the whole web-site and to check constantly path in the script:
var enabledPages = [
"/page1",
"/page2"
];
setInterval(function() {
if(enabledPages.indexOf(window.location.pathname) > -1){
// logic
}
}, 1000);
Related
I'm creating a Firefox add-on with the Firefox Add-on SDK. This add-on does two things:
Inject a content script into every page with sdk/page-mod.
Add a context menu item using sdk/context-menu.
I want that when user clicks the context menu item, the add-on will call functions in the content script which was loaded by PageMod().
Unless your page-mod script is doing other things, it sounds like it might be more appropriate to load it using the context-menu contentScript or contentScriptFile properties. Alternately, load the portions of it that are needed by the context menu using this methodology. How best to split the script you are using depends on what you are actually doing. Without more information from you it is difficult to provide specific recommendations.
Communicating between content scripts loaded at different times or by different methods:
There is no method of directly doing what you desire. Content scripts that are not loaded at the same time by the same methodology are loaded into different contexts. They are unable to directly call functions between them. Multiple content scripts which are loaded at the same time and the same methodology share the same context/scope and can directly call functions between them.
However, you can communicate between content scripts. If they are not loaded into the same page, then you will need to communicate from one content script to another by using your main add-on script to first receive a message from one content script. Then, your main add-on script will need to send a second message (potentially containing exactly the same data) to the second content script. In other words, your main add-on code would need to relay the message between the two content scripts.
For content scripts that are loaded into the same page via different methods (e.g. one with page-mod and another as a context menu item – the situation in which you are interested), you can communicate directly between them using the DOM postMessage() API or a CustomEvent. Either can be used to send whatever JSON serializable data you desire between the two scripts. The DOM postMessage() API provides for more security, but is a bit more complex. With it you must also filter out any other "message" events that are sent on it by random code. It should probably be used if you are going to have code in a released add-on execute functions based on the content of the messages. This is a security issue which will depend on exactly what you are doing with the messages.
Example:
The following code will load a page-mod script into every page that matches "*.mozilla.org". It also creates a context menu item in those same pages which is displayed on links. Clicking on the context menu item will send an event from the context-menu content script with data containing the URL for which the context menu was displayed. The custom event will be received by the page-mod script. The page-mod script will then issue an alert with the URL for the link.
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "*.mozilla.org",
contentScript: 'function contextMenuAlert(href) {'
+ ' window.alert("The context menu click on a link with URL:\\n" + href);'
+ '};'
+ 'window.addEventListener("myAddonId-contextMenu-clicked",'
+ ' function(event){contextMenuAlert(event.detail);});'
});
//Context menu
let cm = require("sdk/context-menu");
cm.Item({
label: "Alert link URL",
context: [
cm.URLContext(["*.mozilla.org"]),
cm.SelectorContext("a[href]")
],
contentScript: 'self.on("click", function (node, data) {'
+ ' var event = new CustomEvent("myAddonId-contextMenu-clicked",'
+ ' {detail:node.href});'
+ ' window.dispatchEvent(event);'
+ '});'
});
The above code produces a context menu that looks like:
When clicked on, the page-mod added content script initiates the following alert:
Using the message sent to choose from multiple different functions:
The information passed through the event can be expanded to allow multiple different functions to be called depending on the content. One method of doing this is to send an object as the message. One property of the object can be the function desired and another can be data to use in that function. My answers to the following questions contain examples of doing this:
Add menu item created with the sdk/context-menu API to the top of the context menu: This answer has code which uses the same passed message to indicate that either a click was made on a context menu item and pass the URL on which the context menu item was clicked, or to tell the main script that the context menu is about to be displayed so it can be modified.
How to console.log from ChromeWorker (alternative to dump): This answer shows sending a message that will result in a call to one of a variety of different functions and pass data to the function which was called. It was implemented as a way of using console methods from a worker with just console.log("message"). I'd code this one a bit differently were I doing it today, but it works and demonstrates the concept.
Because the documentation on MDN (here and here) was not very clear on content script to content script communication, I have updated the pages I found on which it was discussed. I have also added the above code as an example.
I want to prevent JavaScript redirects in Firefox for one domain (youtube.com), and I was wondering whether there's a plugin that will do it. I'm trying to use NoScript, and I'd like to allow scripts globally because I don't want to disable most JavaScript, but this seems to just allow JavaScript redirects. Is there a way for me to just disable JavaScript redirects (or ideally, display a prompt)?
The only other way I can think of doing it is to write my own extension that messes around with window.onbeforeunload and window.unload, but ideally I'd like to use an existing addon.
var {utils: Cu, classes: Cc, instances: Ci, results: Cr} = Components
Cu.import('resource://gre/modules/Services.jsm');
var myobserve = function(aSubject, aTopic, aData) {
var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
if (httpChannel.loadFlags & Ci.nsIHttpChannel.LOAD_REPLACE) {
//its a redirect, lets block it
httpChannel.cancel(Cr.NS_BINDING_ABORTED);
}
}
I use the presence of flags to test for redirect. Here are some notes i took on flags awhile back, im not totally sure of how accurate these notes are:
if has LOAD_DOCUMENT_URI it usually also has LOAD_INITIAL_DOCUMENT_URI if its top window, but on view source we just see LOAD_DOCUMENT_URI. If frame we just see LOAD_DOCUMENT_URI. js css files etc (i think just some imgs fire http modify, not sure, maybe all but not if cached) come in with LOAD_CLASSIFY_URI or no flags (0)
note however, that if there is a redirect, you will get the LOAD_DOC_URI and the LOAD_INIT_DOC_URI on initial and then on consequent redirects until final redirect. All redirects have LOAD_REPLACE flag tho
note: i think all imgs throw http-on-modify but cahced images dont. images can also have LOAD_REPLACE flag
Of course to start observing:
Services.obs.addObserver(myobserve, 'http-on-modify-request', false);
and to stop:
Services.obs.removeObserver(myobserve, 'http-on-modify-request', false);
In main.js I am opening a tab and attaching a script with page-mod. The html file that is being opened it has a bunch of regular includes.
Sort of have two issues.
The script from page mod does not get attached until after those other scripts are loaded, and also
the regular scripts can't access variables defined in the script that is attached with page mod.
You have to send messages.
Directly
In your page-mod, send a message:
page-mod.js
window.postMessage(projectUniqueId + '|' + message, domain);
If your page needs to work with all domains (being a plug-in), you might need '*' as a domain.
tab-attach.js
window.addEventListener('message',function(event){
var words = event.data.split('|');
if (words[0] == projectUniqueId){
handle(words[1]);
}
});
The script from page mod does not get attached until after those other scripts are loaded,
Specifying contentScriptWhen: 'ready' in the page-mod constructor should "[l]oad content scripts once DOM content has been loaded, corresponding to the DOMContentLoaded event"
the regular scripts can't access variables defined in the script that is attached with page mod.
Have a look at Expose objects to page scripts. You need to use
var contentScriptObject = {"greeting" : "hello from add-on"};
unsafeWindow.clonedContentScriptObject = cloneInto(contentScriptObject, unsafeWindow);
in the content script to make the object accessible.
I'm looking for a way to load a part of an external page (possibly selected by an id in the external page) into a div. Something similar to Ajax.Updater, but with the option of specifying an id to look for in the external page.
Does anything like this exist in prototype. I've been googling for examples without luck. If I can't find it soon I'll have to do some "gymnastics" with Ajax.Request and some function tied to onSuccess.
You could do something like this, though it is by no means an elegant solution.
new Ajax.Updater("container", 'www.babes.com', {
onSuccess: function() {
$("container").update( $('idOfSomeLoadedElement') );
}
});
I don't think there is an actual elegant way of doing this purely in js. Ideally, you'd make your AJAX request only for what you need. You might be able to do some server-side stuff to lop out what you don't need (basically, offload the onsuccess functionality above to the server).
Instead of AJAX you might get by with an iframe.
// dollar function calls Element.extend
var iframe = $(document.createElement('iframe'));
// control how and what it loads
iframe.addEventListener('onLoad', function() {
$('container').update(iframe.contentDocument.select('#someID').first());
});
iframe.setAttribute('src', 'http://URL');
// add it as invisible, content isn't loaded until then
iframe.setAttribute('hidden', true);
document.body.appendChild(iframe);
From a minimal test it's clear that you'll have to be as concious about cross-origin policies as any AJAX method, that is, it's a PITA.
We're trying to use Stackoverflow's excellent WMD / Markdown editor (http://blog.stackoverflow.com/2009/01/updated-wmd-editor/, http://github.com/derobins/wmd/tree/master) on a Symfony project.
This works excellent on textareas without any AJAX involved. But when we have to include wmd.js first, and then later on user interaction (i.e. "click on link") have the textarea loaded via AJAX we utterly fail to make WMD work, Firebug is giving us
elem is null
addEvent()()wmd.js (Linie 110)
setupEvents()()wmd.js (Linie 1790)
init()()wmd.js (Linie 1970)
previewManager()()wmd.js (Linie 1987)
loadListener()()wmd.js (Linie 1763)
[Break on this error] if (elem.attachEvent) {
on loading the page (i.e. before textarea loading).
Symfony's AJAX Loader seems to eval() everything between tags. We tried including the whole script directly between those tags, we tried escaping this and that, but had no success with differnt errors coming up.
At this point we think we have to include the script in the normal page and after the AJAX call we have to initiate WMD manually - what functions do we have to call? Are we completely off track and need to use a different approach?
Thank you!
You can use my version. Simply put the constructor call, config values and the call to the start() method in the callback function that handles the results you get from the AJAX request. My version of mooWMD.
drobins fork of wmd on github is solving the AJAX issues as well.
I had this same issue and I initialize WMD once my textarea has been added to the page asynchronously.
Heres my code:
function loadTextEditor()
{
var instances = [];
if (!Attacklab || !Attacklab.wmd) {
alert("WMD hasn't finished loading!");
return;
}
/***** build the preview manager *****/
var textArea = document.getElementById('postcontent');
var previewPane = document.getElementById('postPreview');
var panes = {input:textArea, preview:previewPane, output:null};
var previewManager = new Attacklab.wmd.previewManager(panes);
/***** build the editor and tell it to refresh the preview after commands *****/
var editor = new Attacklab.wmd.editor(textArea,previewManager.refresh);
// save everything so we can destroy it all later
instances.push({ta:textarea, div:previewDiv, ed:editor, pm:previewManager});
}