Matching and replacing a word whenever a page loads - firefox

I have this code that need to run when my addon starts, needs to look for word and then replace it with different work if it finds it.
var contents=document.body.innerHTML;
var idx=contents.search("hello");
if(idx) {
contents=contents.replace("hey",'<span id="highlight">hey</span>');
document.body.innerHTML=contents;
document.getElementById('highlight').scrollIntoView();
};
But I keep on getting error on console
Traceback (most recent call last):
File "resource://jid1-qntppxcxwbpwlw-at-jetpack/lib/main.js", line 5, in
var contents=document.body.innerHTML;
ReferenceError: document is not defined

Judging by the error message you are using the Add-on SDK. Your main.js module runs in a separate context - it isn't attached to any window or document. So the variable document is not defined of course.
You apparently want to change the contents of web pages - that's what content scripts are for. You either use page-mod (it will run the content script for all new pages being opened) or you use tab.attach() to run the content script for existing tabs.

The document is not ready when the code is run. Inside your body tag you need to put onload='ready();' (or whatever you want to call your function) then define function ready() { } in your script, with the code you wrote inside that function.

Related

How and in what way do content scripts share content scoped variables across different web pages?

There are some key parts of the MDN content script documentation I am having trouble understanding regarding variable scope:
There is only one global scope per frame, per extension. This means that variables from one content script can directly be accessed by another content script, regardless of how the content script was loaded.
This paragraph may also be relevant (my italics) given the questions below:
... you can ask the browser to load a content script whenever the browser loads a page whose URL matches a given pattern.
I assume (testing all this has proved difficult, please bear with me) that this means:
The content script code will be run every time a new page is loaded that matches the URLs provided in manifest.json (in my case "matches": [<"all_urls">]
The content script code will run every time a page is refreshed/reloaded.
The content script code will not be run when a user switches between already open tabs (requires listening to window focus or tabs.onActivated events).
The variables initialized on one page via the content script share a global scope with those variables initialized by the same content script on another page.
Given the following code,
background-script.js:
let contentPort = null
async function connectToActiveTab () {
let tab = await browser.tabs.query({ active: true, currentWindow: true })
tab = tab[0]
const tabName = `tab-${tab.id}`
if (contentPort) {
contentPort.disconnect()
}
contentPort = await browser.tabs.connect(tab.id, { name: tabName })
}
// listening to onCreated probably not needed so long as content script reliably sends 'connecToActiveTab' message
browser.tabs.onCreated.addListener(connectToActiveTab)
browser.runtime.onMessage.addListener(connectToActiveTab)
content-script.js
let contentPort = null
function connectionHandler (port, info) {
if (contentPort && contentPort.name !== port.name) {
// if content port doesn't match port we have changed tabs/windows
contentPort.disconnect()
}
if (!contentPort) {
contentPort = port
contentPort.onMessage.addListener(messageHandler)
}
}
async function main () {
// should always be true when a new page opens since content script code is run on each new page, testing has shown inconsistent results
if (!contentPort) {
await browser.runtime.sendMessage({ type: 'connectToActiveTab' })
}
}
browser.runtime.onConnect.addListener(connectionHandler)
main()
And from assumptions 1 and 4, I also assume:
The existing contentPort (if defined) will fire a disconnect event (which I could handle in the background script) and be replaced by a new connection to the currently active tab each time a new tab is opened.
The behaviour I have seen in Firefox while testing has so far been a bit erratic and I think I may be doing some things wrong. So now, here are the specific questions:
Are all of my 5 assumptions true? If not, which ones are not?
Is firing the disconnect() event unnecessary, since I should rely on Firefox to properly clean up and close existing connections without manually firing a disconnect event once the original contentPort variable is overwritten? (the code here would suggest otherwise)
Are the connect() methods synchronous, thus negating the need for await and asynchronous functions given the example code?
The tabs.connect() examples don't use await but neither the MDN runtime or connect docs explicitly say whether the methods are synchronous or not.
Thanks for taking the time to go through these deep dive questions regarding content script behaviour, my hope is that clear and concise answers to these could perhaps be added to the SO extension FAQ pages/knowledge base.

Firefox: run functions from Javascript through an extension

At the present I'm pasting a Javascript into the console of FF and I'm calling the functions from the console:
function fill (i){
if(i==1){
SINGLE_START();
}
else if(i==2){
DUAL_START();
}
else if(i==3){
INTEGRATED_START();
}
else{
alert("=======================\n Tool Filler\n=======================\n\n1 or 2");
}
}
It is used to scrape the content of the website and e.g. create a file or generate an email from certain parts of the website, e.g.:
function SINGLE_START(){
//Focus:
let d = $(document).activeElement.contentDocument.activeElement.contentDocument.activeElement.contentDocument;
etc.
I thougt, there could be a way to use it through an extension and so I installed Tampermonkey and saved the script as userscript within the extension. But than I have a problem that I'm not able to call the desired function from the script as I need it, not just start the script as the website loads.
Does anyone has an idea how to call the functions one by one from within Tampermonkey (or Greasemonkey), or any other extension?
Thanks in advance!
This is because Tampermonkey scripts run in isolated context. There are two kinds:
1. No special privilegies
If you're not using any special GM functions that are unlocked by #grant GM_doThisAndThat and instead you use #grant none, then what internally happens is something like this:
function TheScript() {
// Here is your script that you added to tampermonkey
}
TheScript();
This if you have a function there, it is only available in the script. You need to explicitly expose it to window context:
function fill (i){
... code here ...
}
window.myFill = fill;
Then in console you write myFill(42) and it will execute.
I would recommend that you avoid assigning fill to window since it's a name that could likely conflict with something, but that's up to you.
2. Special privilegies
If you're using some of the GM features, you need to add #grant unsafeWindow and then assign to unsafeWindow variable. Be careful what you expose like this, you don't want to allow the website to access any of the GM_function features as they could access your private data on other websites and your computer!

Is there a way to make this 'undefined' object safe?

On a page in a web app, a loading screen/widget continue to appear after the user leaves the text field.
The app is using version 1.6.0.3. I looked at the most recent version of Prototype 1.7.3 and I did not find that function.
I also tested other instances when this method is called. If there is no user input, the widget does not hang-up.
This error is displayed in the console of Chrome’s developer tools.
at E (framework.pack.js:1699)
at Function.stopObserving (framework.pack.js:1732)
at A.hide (ui.pack.js:13410)
at A.render (ui.pack.js:13591)
at A.updateChoices (ui.pack.js:13650)
at A.onComplete (ui.pack.js:13786)
at Object.oncomplete (framework.pack.js:76)
at framework.pack.js:2748
The specific method in questions seems to be in the Prototype.js file this =>
if (element._prototypeEventID) return element._prototypeEventID[0];
arguments.callee.id = arguments.callee.id || 1;
return element._prototypeEventID = [++arguments.callee.id];
}
I expect the loading widget to disappear after the save is done, but it is still on the page. The console of Chrome's developer tools also has a second error:
validateFormCheck # common.js:1031
It looks like the getEventID method is being called where the undefined warning/error triggers.
In version 1.6.0.3 getEventID is only called in createWrapper and stopObserving, I see stopObserving is in the call stack that you posted so let's go with that one.
stopObserving() takes 1 required parameter and 2 optional parameters (element, eventName, handler) if you only pass the element to the function it looks it up and then deletes all the PrototypeJS observers attached to that element. If you pass eventName and/or handler as well stopObserving will only specifically delete the observer you tell it to.
That being said, if the element is removed from the DOM before stopObserving is called this could cause the error you are seeing.
2 fixes that could work
move the call to stopObserving() above the call to remove()
comment out the call to stopObserving() and see if page behaves like you want it to

Cannot call loaded script object handler twice. Why?

I am thoroughly stumped by this problem. I have some standard functions that I saved in a compiled AppleScript file, let's call it functions.scpt. In another script, I use load script to load a script object to functions.scpt:
set funcs to load script "path/to/functions.scpt
I have one handler in functions.scpt called icon(). For whatever reason, if I try to call this function more than once in my other script, I get an error. Here's some quick example code:
--called once
funcs's icon()
--called twice
funcs' icon()
Whenever I do this, I get the following error: error "«script» doesn’t understand the “icon” message." number -1708 from «script» to «class icon».
Why can't I call the same function from a loaded script object twice? How can I fix this? I need to call it multiple times.
Thank you to #CRGreen and #jweaks. I actually just found the answer on this thread: https://stackoverflow.com/a/13256042/2884386
I had set a variable icon within the handler, which was overwriting the handler. So when the other script tried to access it the second time, it wasn't pointing to the handler, but to the variable. I rewrote the internal variables, and that fixed it.
So, this is (as an example) correct:
on icon()
set _icon to "path/to/icon.png"
return POSIX file _icon
end icon
Whereas this is incorrect:
on icon()
set icon to "path/to/icon.png
return POSIX file icon
end icon

How to Program xulrunner application to display output file or command line?

My question is in response to this article
https://developer.mozilla.org/En/How_to_check_the_security_state_of_an_XMLHTTPRequest_over_SSL
I have downloaded and configured the xulrunner the only problem I'm getting to run javascript given in the link to display it output. Using xulrunner i want to know how can i produce an output as a headerless command-line program not gui.
var httpRequest = Components.classes["#mozilla.org/xmlextr/xmlhttprequest;1"].createInstance();
// Disable alert popups on SSL error
httpRequest.mozBackgroundRequest = true;
httpRequest.open("GET", "https://developer.mozilla.org/", true);
httpRequest.onreadystatechange = function (aEvt) {
if (httpRequest.readyState == 4) {
// Print security state of request
dumpSecurityInfo(httpRequest.channel);
}
};
httpRequest.send(null);
In the above code taken from the same link i want to see the output of function on my command screen or even a writing the information to file would do.
Do i have to change something in *.xul file extension.? I'm new to using xulrunner some help would be very helpful for me.
To print something to the console you use the dump() function. If your code runs in the context of a window you will need to change browser.dom.window.dump.enabled preference to true. XPCOM components can simply call dump() without changing this preference.

Resources