I'm trying to convert my overlay add-on to restartless.
I can't access the bookmarks panel (on the sidebar) in SeaMonkey, in order to load my overlay UI.
Specifically, I need to do load my overlay to the bm-panel.xul similar to the following:
myListener.document.loadOverlay("chrome://myBookmarksPanelOverlay.xul");
For that, I need the window of bm-panel.xul but I only have the main window of the browser.
SeaMonkey has a different structure from Firefox, so the following example
var sidebarPanels = window.document.getElementById('sidebar');
which is in documentation, does not work for SeaMonkey.
I can see the bm-panel.xul window in the Dom Inspector, but I can't get to it with Javascript.
I was able to access only the sidebar panels but that's as far as I can go:
var sidebarPanels = window.document.getElementById('sidebar-panels');
How do I access the bookmarksPanel page itself?
I'm not 100% certain if your question is really limited to just finding the bookmarksPanel. Thus, this answer contains generic information about accessing the bookmarksPanel, viewing the DOM, accessing the sidebar from an overlay or restartless extension, and obtaining a window reference.
Accessing the bookmarksPanel
The following should get you a reference to the <page id="bookmarksPanel">:
var sidebarDocument = document.getElementById("sidebar").contentDocument;
var bookmarksPanelElement = sidebarDocument.getElementById("bookmarksPanel")
Note that you need to use the getElementById() in the sidebarDocument, not the main window.document.getElementById() which will not search into the sidebar.
Viewing the DOM
If you are having issues with knowing what the DOM structure is for a particular element, I would suggest that you install DOM Inspector (for SeaMonkey) (to view the DOM) and Element Inspector (which allows you to shift-right-click on an element and open the DOM Inspector on that element).
This is an example of the DOM Inspector viewing a Bookmark Sidebar in Firefox:
Accessing the Sidebar From an Overlay or Restartless Extension
Quoting from MDN: "Sidebar: Accessing the sidebar from a browser.xul script":
Accessing the sidebar from a browser.xul script
The sidebar content is always in a document separate from the main browser document (the sidebar is actually implemented as a XUL browser element). This means you can't directly access the sidebar content from a script referenced from a browser.xul overlay.
To access your sidebar's window or document objects, you need to use the contentWindow or contentDocument properties of document.getElementById("sidebar") respectively. For example the code below calls a function defined in the sidebar's context:
var sidebarWindow = document.getElementById("sidebar").contentWindow;
// Verify that our sidebar is open at this moment:
if (sidebarWindow.location.href ==
"chrome://yourextension/content/whatever.xul") {
// call "yourNotificationFunction" in the sidebar's context:
sidebarWindow.yourNotificationFunction(anyArguments);
}
Depending on how the current code you are running was started (e.g. UI button), you may need to obtain the current browser window.
Copying significantly from another answer of mine, you can obtain that by:
Obtaining a reference to the most recent window:
Firefox add-ons generally run in a scope where the global window object is not defined (if it is defined depends on how the portion of your code that is currently running was entered). Even if it is defined, it is often not defined as the window which you are expecting (the window of the current tab). You will probably need to obtain a reference to the window object for the most recently accessed window/tab.
If a browser window exists (in some instances you could be running where no browser window exists, yet, e.g. at start-up), you can obtain a reference to the most recent browser window, document, and gBrowser with:
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add/remove a "/" to comment/un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
//* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
if (typeof document === "undefined") {
//If there is no document defined, get it
var document = window.content.document;
}
if (typeof gBrowser === "undefined") {
//If there is no gBrowser defined, get it
var gBrowser = window.gBrowser;
}
If you are running the code in response to an event (e.g. a button command event), you can obtain the current window with:
var window = event.view
The lack of having the global window object available, or having it reference something other than what you are expecting, is something that many people encounter as a problem when writing Firefox add-ons.
Note: If you are wanting to be natively compatible with multi-process Firefox (Electrolysis, or e10s), then gaining access to the contents of the current document is more complex. There are shims in place which should make your code continue to work with multi-process Firefox for some time, but they may/will eventually go away.
References:
nsIWindowMediator
Working with windows in chrome code
SDK: window/utils
SDK: windows
Multiprocess Firefox
Working with multiprocess Firefox
Large portions of this were copied from my earlier answers, including this link.
This may not be the only way or the best, but here is what I am doing:
Find the sidebar element (sidebar-panels)
search the sidebar childNodes for the element with attribute src=chrome://communicator/content/bookmarks/bm-panel.xul
Searching the childNodes must be delayed until the relevant frames are loaded, and the search itself is a recursive iteration over all elements.
following is a minimal stripped code:
var sidebarE = domWindow.document.getElementById('sidebar-panels');
setTimeout(function() {
var sbPanelE = searchChildNodes(sidebarE);
}, 350);
function searchChildNodes (aElement) {
var stackNew = [];
var current;
var i, lenArr;
iterate(aElement);
function iterate(current) {
var childrenE = current.childNodes;
for (var i = 0, lenArr = childrenE.length; i < lenArr; i++) {
iterate(childrenE[i]);
foundE = checkElement(childrenE[i]);
if (e.nodeType == 1){ // Element node
if (e.getAttribute('src') == loc){
stackNew.push({ //pass args via object or array
element: childrenE[i],
});
return;
}
}
}
}
for (i=0;i<stackNew.length ;i++ ){
var itm = stackNew[i].element;
return itm;
}
} // searchChildNodes
Related
Currently, electron 5.0 removed the ability to use nativeWindowOpen with nodeIntegration, so it's impossible to use ReactDOM.createPortal on the document.body returned by window.open. They didn't put any workaround, and I've tried everything found in other answers, like:
IPC back the DOM node to the new remote.BrowserWindow, it doesn't send back a valid DOM node to attach to
webContents.executeJavascript, with either (function(){ return window })() document or body, makes the process hang
I just don't seem to be able to do the equivalent using BrowserWindow:
// this is what works in Electron 4 with a warning see https://github.com/electron/electron/pull/15193
const container = window.open("about:blank", "someuniqueid"); // window = current main window
const stylesheet = document.createElement('link');
stylesheet.rel = 'stylesheet';
stylesheet.href = document.querySelector('link').href; // document = current main window
container.document.body.appendChild(stylesheet);
// [... edited for brevity]
return createPortal(
props.children(container), // children = render props, pass the window to the children
container.document.body
)
nothing is able to return a valid DOM node from the created window. require('electron') also doesn't work for obvious reasons (nodeIntegration is always disabled)
I try to add an eventListener to my extension. I want to execute a function everytime a tab is active (got clicked by the user) or is new loaded.
I tried this:
window.addEventListener("DOMContentLoaded", checkHost(), false);
It gives me the error
Uncaught Reference Error: window is not defined
It drives me cracy, i can't find examples on the web. Please help me.
For those wanting to use the window object, you can create it using this code:
var { viewFor } = require("sdk/view/core");
var window = viewFor(require("sdk/windows").browserWindows[0]);
This code can be found on MDN at: https://developer.mozilla.org/en-US/Add-ons/SDK/High-Level_APIs/windows
Via your current status: http://builder.addons.mozilla.org/package/206579/latest
The online builder is an online IDE for developing with the Addon-SDK, where window isn't in global scope -- it's not any specific window.
You can include the tabs module and listen for ready events or activate (a tab is now focused) events, which may be what you want.
let tabs = require('sdk/tabs');
tabs.on('ready', function (tab) {
console.log(tab.url + ' is ready!');
});
The issue is that Firefox extensions do not run in the context of any particular window. As such, they often do not have the window object defined, or it is defined as something which you are not expecting if you are not familiar with writing extension code. This is particularly true if you are approaching this from the point of view of writing JavaScript for use within an HTML page. Extensions operate in a significantly larger context which includes the entire browser and all windows and tabs. Thus, there is no automatically appropriate window to use as the window object. In the context of an extension, each HTML page is just a part of the whole.
You can obtain each primary browser window through the use of nsIWindowMediator. The following function, from MDN, will run the function you pass to it once for each open window:
Components.utils.import("resource://gre/modules/Services.jsm");
function forEachOpenWindow(todo) // Apply a function to all open browser windows
{
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
}
You will often want to find the window for the most recent browser/tab which was accessed by the user. The following code will define and set the window variable to the most recently used browser/tab. It will work either in the Add-on SDK, or in overlay/bootstrap extensions depending on which portion you un-comment.
For more information about using windows in a Firefox extension, you should see Working with windows in chrome code.
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
//*/
}
Alternately using Services.jsm to access nsIWindowMediator:
/* Overlay and bootstrap:
Components.utils.import("resource://gre/modules/Services.jsm");
//*/
if (window === null || typeof window !== "object") {
//If you do not already have a window reference, you need to obtain one:
// Add a "/" to un-comment the code appropriate for your add-on type.
/* Add-on SDK:
var window = require('sdk/window/utils').getMostRecentBrowserWindow();
//*/
/* Overlay and bootstrap (from almost any context/scope):
var window = Services.wm.getMostRecentWindow("navigator:browser");
//*/
}
You have to put the eventlistener inside a script referenced in the browser.xul overlay:
Try it like this:
window.addEventListener("DOMContentLoaded", function() {checkHost();}, false);
This question already has answers here:
Access variables and functions defined in page context using a content script
(6 answers)
Closed 9 months ago.
I would like to make a Chrome extension that provides a new object inside window. When a web page is viewed in a browser with the extension loaded, I would like window.mything to be available via Javascript. The window.mything object will have some functions that I will define in the extension, and these functions should be callable from console.log or any Javascript file when the page is viewed in a browser with the extension enabled.
I was able to successfully inject a Javascript file into the page by using a Content Script:
var s = document.createElement("script");
s.src = chrome.extension.getURL("mything.js");
document.getElementsByTagName("head")[0].appendChild(s);
mything.js looks like this:
window.mything = {thing: true};
console.log(window);
Whenever a page loads, I see the entire window object as I expect it to be in the console. However, I can't interact with the window.mything object from the console. It seems at if the injected script hasn't really modified the global window object.
How can I modify the global window object from a Chrome extension?
You can't, not directly. From the content scripts documentation:
However, content scripts have some limitations. They cannot:
Use chrome.* APIs (except for parts of chrome.extension)
Use variables or functions defined by their extension's pages
Use variables or functions defined by web pages or by other content scripts
(emphasis added)
The window object the content script sees is not the same window object that the page sees.
You can pass messages via the DOM, however, by using the window.postMessage method. Both your page and content script listen to the message event, and whenever you call window.postMessage from one of those places, the other will receive it. There's an example of this on the "Content Scripts" documentation page.
edit:
You could potentially add some methods to the page by injecting a script from the content script. It still wouldn't be able to communicate back with the rest of the extension though, without using something like postMessage, but you could at least add some things to the page's window
var elt = document.createElement("script");
elt.innerHTML = "window.foo = {bar:function(){/*whatever*/}};"
document.head.appendChild(elt);
After hours trying different attempts and facing security issues like CORS, I found ways to edit the window object on Chrome, Firefox and Safari. You need to use different strategies for each one:
Chrome
Add your script to content_scripts.
Inside your script file, append a script to the page and make it run your custom code inline. Like this:
;(function() {
function script() {
// your main code here
window.foo = 'bar'
}
function inject(fn) {
const script = document.createElement('script')
script.text = `(${fn.toString()})();`
document.documentElement.appendChild(script)
}
inject(script)
})()
Firefox
On Firefox, the solution above doesn't work due to a Content-Security-Policy error. But the following workaround is currently working, at least for now:
Add 2 scripts to content_scripts, e.g. inject.js and script.js
The inject script will get the full absolute url of the script.js file and load it:
;(function() {
const b = typeof browser !== 'undefined' ? browser : chrome
const script = document.createElement('script')
script.src = b.runtime.getURL('script.js')
document.documentElement.appendChild(script)
})()
Your script.js will contain your main code:
;(function() {
// your main code here
window.foo = 'bar'
})()
Safari
It's very similar to Firefox.
Create 2 javascript files, e.g. inject.js and script.js
The inject script will get the full absolute url of the script.js file and load it:
;(function() {
const script = document.createElement('script')
script.src = safari.extension.baseURI + 'script.js'
document.documentElement.appendChild(script)
})()
Your script.js will contain your main code:
;(function() {
// your main code here
window.foo = 'bar'
})()
Source code
See full code here: https://github.com/brunolemos/simplified-twitter
As others have pointed out, context scripts do not run in the same context as the page's, so, to access the correct window, you need to inject code into the page.
Here's my take at it:
function codeToInject() {
// Do here whatever your script requires. For example:
window.foo = "bar";
}
function embed(fn) {
const script = document.createElement("script");
script.text = `(${fn.toString()})();`;
document.documentElement.appendChild(script);
}
embed(codeToInject);
Clean and easy to use. Whatever you need to run in the page's context, put it in codeToInject() (you may call it whatever you prefer). The embed() function takes care of packaging your function and sending it to run in the page.
What the embed() function does is to create a script tag in the page and embed the function codeToInject() into it as an IIFE. The browser will immediately execute the new script tag as soon as it's appended to the document and your injected code will run in the context of the page, as intended.
A chrome extension's content_script runs within its own context which is separate from the window. You can inject a script into the page though so it runs in the same context as the page's window, like this: Chrome extension - retrieving global variable from webpage
I was able to call methods on the window object and modify window properties by essentially adding a script.js to the page's DOM:
var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
(document.head||document.documentElement).appendChild(s);
s.onload = function() {
s.remove();
};
and creating custom event listeners in that injected script file:
document.addEventListener('_my_custom_event', function(e) {
// do whatever you'd like! Like access the window obj
window.myData = e.detail.my_event_data;
})
and dispatching that event in the content_script:
var foo = 'bar'
document.dispatchEvent(new CustomEvent('_save_OG_Editor', {
'detail': {
'my_event_data': foo
}
}))
or vice versa; dispatch events in script.js and listen for them in your extension's content_script (like the above link illustrates well).
Just be sure to add your injected script within your extension's files, and add the script file's path to your manifest within "web_accessible_resources" or you'll get an error.
Hope that helps someone \ (•◡•) /
I've been playing around with this. I found that I can interact with the window object of the browser by wrapping my javascript into a window.location= call.
var myInjectedJs = "window.foo='This exists in the \'real\' window object';"
window.location = "javascript:" + myInjectedJs;
var myInjectedJs2 = "window.bar='So does this.';"
window.location = "javascript:" + myInjectedJs2;
It works, but only for the last instance of window.location being set. If you access the document's window object, it will have a variable "bar" but not "foo"
Thanks to the other answers here, this is what I'm using:
((source)=>{
const script = document.createElement("script");
script.text = `(${source.toString()})();`;
document.documentElement.appendChild(script);
})(function (){
// Your code here
// ...
})
Works great, no issues.
Content Scripts can call window methods which can then be used to mutate the window object. This is easier than <script> tag injection and works even when the <head> and <body> haven't yet been parsed (e.g. when using run_at: document_start).
// In Content Script
window.addEventListener('load', loadEvent => {
let window = loadEvent.currentTarget;
window.document.title='You changed me!';
});
I'm developing a Firefox extension which places a button in the status bar. When the button is clicked, the extension injects some Javascript into the current page. This Javascript has a function that I would like to invoke with some parameters. I've managed injecting the code, I've inspected the page through Firebug and verified that the JS has been injected. How can I call a Javascript function in the page from my extension?
--More information
Here's the code that I'm using to inject my Javascript:
var doc = window.content.document;
//Add the script
var visibilityJS = doc.createElement("script");
visibilityJS.setAttribute("type", "text/javascript");
visibilityJS.setAttribute("charset", "UTF-8");
visibilityJS.setAttribute("src", "chrome://visibility/content/scripts/visibility.js");
head.appendChild(visibilityJS);
//Call the function
alert("Executing testfunction");
window.content.document.defaultView.testFunction();
..and the code inside my JS file that i'm going to inject. i.e. visibility.js
window.testFunction = function() {
alert("Message");
}
Thanks.
This worked. I don't know the technicalities. I got part of the solution from Felix and part of it from here.
window.content.document.defaultView.wrappedJSObject.testFunction();
If you declare a global variable in your injected code (or explicitly set a property of the window object), then one way do get a reference to this element from your extension, is via the gBrowser object:
gBrowser.contentDocument.defaultView.yourObject
^-- HTML document ^
object |-- window object
Be careful though, when you use window and document inside your code. Depending on the context it might refer to the Firefox window or the website window object.
I can get window.document but how can I get document.window? I need to know how to do this in all browsers.
You can go with document.defaultView if you’re sure its a window and its okay to skip Microsoft browsers before IE 9.
A cross browser solution is complicated, here's how dojo does it (from window.js::get()):
// In some IE versions (at least 6.0), document.parentWindow does not return a
// reference to the real window object (maybe a copy), so we must fix it as well
// We use IE specific execScript to attach the real window reference to
// document._parentWindow for later use
if(has("ie") && window !== document.parentWindow){
/*
In IE 6, only the variable "window" can be used to connect events (others
may be only copies).
*/
doc.parentWindow.execScript("document._parentWindow = window;", "Javascript");
//to prevent memory leak, unset it after use
//another possibility is to add an onUnload handler which seems overkill to me (liucougar)
var win = doc._parentWindow;
doc._parentWindow = null;
return win; // Window
}
return doc.parentWindow || doc.defaultView; // Window
has("ie") returns true for IE (and false otherwise)
Well, this is the solution I went with. It works, but I hate it.
getScope : function(element) {
var iframes = top.$$('iframe');
var iframe = iframes.find(function(element, i) {
return top[i.id] ? top[i.id].document == element.ownerDocument : false;
}.bind(this, element));
return iframe ? top[iframe.id] : top;
}
I opted to inject the DOCUMENT token from #angular/platform-browser:
import { DOCUMENT } from '#angular/platform-browser'
and then access the parent:
constructor(#Inject(DOCUMENT) private document: any) {
}
public ngOnInit() {
// this.document.defaultView || this.document.parentWindow;
}
first off let's be clear. this sort of thing is often necessary when you are working with frames, iframes, and multiple windows, and so "the window is just the global object" is an unsatisfying answer if all you have a handle to is a document from another window than the one you are in.
second, unfortunately there is no direct way of getting at the window object. there are indirect ways.
the primary mechanism to use is window.name. when creating a window or a frame from some parent window, you can usually give it a unique name. any scripts inside that window can get at window.name. any scripts outside the window can get at the window.name of all its child windows.
to get more specific than that requires more info about the specific situation. however in any situation where the child scripts can communicate with parent scripts or vice versa, they can always identify each other by name, and this is usually enough.
The Window object is the top level object in the JavaScript hierarchy, so just refer to it as window
Edit:
Original answer before Promote JS effort. JavaScript technologies overview on Mozilla Developer Network says:
In a browser environment, this global object is the window object.
Edit 2:
After reading the author's comment to his question (and getting downvotes), this seems to be related to the iframe's document window. Take a look at window.parent and window.top and maybe compare them to infer your document window.
if (window.parent != window.top) {
// we're deeper than one down
}