Create an unmodified object in a Firefox addon using WebExtensions - javascript

I'm building an addon that works on a page where the canvas element prototype has been modified, this means any new canvas element I create on the page or in the addon with have the same modified state.
Is there a way to get around this? I tried using eval() but it also uses the page "version" of the HTMLCanvasElement.
To get around it in the browser manually I can create an iframe and execute a script inside it that sets a variable on the parent window which I can then of course .call() with the context I want, the problem is that WebExtensions doesn't allow access to variables set by page scripts.

After some more reading I found out that there's a property called wrappedJSObject that allows the content script to access properties set by the page script.
More information: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Content_scripts#Accessing_page_script_objects_from_content_scripts

Related

Iframe access on elements

I use an Iframe with an external API, which I don't control. My goal is to add Javascript code in my Website, to change the style of a few elements in the Iframe. First I considered using
document.getElementById(iframeId).contentWindow.document.getElementById(elementId);
but I soon recognized that it will not be successful because I am getting security issues. I tested a bit and found out when I inspect the element in the Iframe, which I want to change, I am able to get the element simply by using:
document.getElementById(elementId);
I don't understand. Why does this technique only work when I inspect the correct element first? And is there any way I can use this trick for my normal JS backend?

Is it possible to get a DOM element in GTM custom template?

Description
So I've read through the GTM Custom template APIs; but I don't find anything regarding getting (e.g.) a div element. I've tried copying document from window using copyFromWindow() or calling document.getElementById through callInWindow(), but both times, I always get this error message when I add document to the permissions:
The value must not start with any key predefined on global scope (i.e. in Window), or any JavaScript keywords.
Question
Is there anyway that it is possible to call a DOM element through custom GTM tags?
My goal is to create a tag that can inject video's in selected div elements. I could do this by creating a custom HTML tag, but I don't want our marketing guy to always go through code. So a tag that asks him for the target ID and video url could make it easier
No. Preventing access to the DOM (and controlling access to the global namespace) is pretty much the point of sandboxed Javascript. Since GTM is not supposed to interfere with the DOM, this should not usually be a problem (well, that's what Google thinks at least).
If you need to learn about some property of a DOM object, you can get this (i.e. the property, not the object itself) via a DOM type variable and pass it in as a data item by creating a field that accept variables.
Simo has a bit about this in his inital write-up on custom templates (you have to scroll down quite a bit). While Google has added a number of APIs since, I doubt that a DOM API is coming up, because that would pretty much defy the purpose of sandboxed Javascript.

When do I load a browser frame script (e10s)?

I'm trying to port one of my Firefox extensions to support Electrolysis (e10s). My extension grabs some page data and puts it on the clipboard via a context-menu item that the user can click. Based on the message manager documentation, there are 3 types of message managers available:
Global
Window
Browser
Since my add-on is context specific, the last one seems like the one I want to use. The problem is that I don't fully know when to load the frame script. A simplified version of my context menu item's action handling code looks like this:
onContext: function() {
let browserMM = gBrowser.selectedBrowser.messageManager;
browserMM.loadFrameScript("chrome://myaddon/content/frame-script.js", true);
browserMM.sendAsyncMessage("myaddon#myaddon.com:get-page-info", json);
}
Loading the frame script here seemed like the best idea to me since (a) the frame script isn't guaranteed to get used on every page and (b) I figured that frame scripts are loaded once and only once per <browser>. The second theory isn't correct it seems; each time I call loadFrameScript, a new copy gets loaded. Even load-protection logic (i.e. only creating the frame script functions if they don't already exist) doesn't seem to fix the problem.
So, my problem is that each time the context menu item is accessed, a new copy of the frame script gets loaded. And since my frame script adds a message listener, I get duplicate messages on subsequent calls of the context menu item.
When should I load browser frame scripts? Loading it once on add-on initialization doesn't seem to work well, since it only loads on the first <browser> (I want this code to execute when asked for by any subsequent <browser>). But loading it on demand appears to duplicate things.
Are there other strategies I'm missing here?
Even load-protection logic (i.e. only creating the frame script functions if they don't already exist)
Frame scripts are a bit tricky, scripts for each tab share a global object but have a separate scope, akin to being evaluated inside their own function block. So if you add it multiple times to a tab then each gets evaluated in a separate scope.
Instead you might want to track the browser objects that already have your frame script attached with a WeakMap. Although I think there also is some property to enumerate the loaded frame scripts.
Loading it once on add-on initialization doesn't seem to work well
If you want that, then use the global message manager and attach a delayed frame script, that'll get attached to all current and future tabs. Of course that will consume more memory than just attaching it to tabs that really need it.
browserMM.loadFrameScript("chrome://myaddon/content/frame-script.js", true);
You don't really need to set the delayed flag to true if you run it on a specific browser, that only makes sense for broadcasting message manager which may get additional children in the future.

Firefox Addon to manipulate DOM without giving the website access to the changes

I write a Mozilla Firefox Addon, that lets me comment websites: When I open a website and click somewhere, the plugin creates a <div> box at this location, where I can enter a comment text. Later, when I open the website again, the plugin automatically puts my previously created comment boxes at the places they where before. (Similar to a comment feature in many PDF readers, etc.)
This leads to a security problem: A website could use an event listener to listen to the creation of new <div> elements and read their content, allowing it to read my private comments.
How can I solve this security issue? Basically, I want a Firefox addon to put private content in a website, while the website should not be able to access this content via JavaScript. (Unless I want it to.)
I could listen to listeners and detach them as soon as the website attaches them - but that does sound like a solid solution.
Is there a security concept in order to make my addon the authority over DOM changes, respectively, to control the access to certain elements?
Alternatively, would it be possible to implement some sort of overlay, which would not be an actual part of the websites DOM but only accessible by the addon?
Similar security problems should occur with other addons. How do they solve it?
If you inject the DOM in a document, the document will always be able to manipulate it, you can't really do much about it. You can either:
1) Don't inject your comment directly in the document, but just a placeholder were there is the first words of the comment, or an image version of the comment (you can generate that with canvas), leave the full ones in your JavaScript Add-on scope, that is not accessible from the page: when you click to edit or add, you can open a panel instead and do the editing there.
2) Inject an iframe, if you have your page remotely in another domain shouldn't be a problem at all, the parent's document can't access to the iframe; but also viceversa: you need to attach content script to your iframe in order to talk with your add-on code, and then you can use your add-on code to send and receive messages from both iframe and parent's document.
If you use a local resource:// document, I'm afraid you need a terrible workaround instead, and you need to use sandbox policies to avoid that the parent's document can communicate with the iframe itself. See my reply here: Firefox Addon SDK: Loading addon file into iframe
3) Use CSS: you can apply a CSS to a document via contentStyle and contentStyleFile in page-mods. The CSS attached in this way can't be inspected by the document itself, and you could use content to add your text to the page, without actually adding DOM that can be inspected. So, your style for instance could be:
span#comment-12::after{
content: 'Hello World';
}
Where the DOM you add could be:
<div><span id='comment-12'></span></div>
If the page tries to inspect the content of the span, it will get an empty text node; and because from the page itself the stylesheet added in this way cannot be inspected, they cannot the styles rules to get the text.
Not sure if there are alternatives, those are the solutions that pop to my mind.
Add-ons that do similar things implement some combination of a whitelist / blacklist feature where the add-on user either specifies which sites they want the action to happen on, or a range of sites they don't want it to happen on. As an add-on author, you would create this and perhaps provide a sensible default configuration. Adblock Plus does something similar.
Create an iframe and bind all your events to the new DOM. By giving it a different domain to the website, you will prevent them from listening in to events and changes.
Addons can use use the anonymous content API used by the devtools to create its node highlighter overlays.
Although the operations supported on anonymous content are fairly limited, so it may or may not be sufficient for your use-case.

iFrame Access Problem

I have data in a iframe. When a user performs an action - I want this want this data to be collated and sent "inside" another iframe inside this iframe.
I have having a bit trouble achieving this and was wondering whether its possible and if it is, how I can do it ?
Thx
A quick solution would be to pass instructions (actions, variables, etc) as query variables in a url. You can use your outside script to change the iFrame reference and a script inside the iFrame can read back those variables and do something with them.
Basically starting with http://someurl.com/page.html in your iFrame and dynamically changing it to http://someurl.com/page.html?action=do-something&with=this. Since we're talking Javascript, you can force the iFrame to refresh asynchronously, giving the illusion that it's loading new, interactive data.
I has this problem a while ago and did not find any solution. As I remember, you cannot control an inner frame from its owner due to some security concerns.
Later Edit: you might be able to synchronize them by using an intermediate component (such a database) - this is how I ended with my issue

Categories

Resources