Unable to access document property from webpage using chrome extension? - javascript

So there is a new API, proposed as a W3C standard, but to use it you need to have this extension for now.
The extension adds an additional document key named monetization, which you can access with document.monetization. And also, the website must have a payment pointer to be able to access it.
I'm trying to access it with an extension I am developing, but I get an undefined error. Here's my manifest.json
{
"manifest_version": 2,
"name": "Test Ext",
"description": "Test Description",
"version": "1.0.0",
"browser_action": {
"default_icon": "icon.png",
"default_pop": "popup.html",
"default_title": "A popup will come here."
},
"permissions": ["activeTab"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["app.js"]
}
]
}
and in my app.js, I made a simple script to check if document.monetization is loaded.
const tid = setInterval( function () {
if (document.monetization === undefined) return;
console.log('Accessible', document.monetization);
clearInterval( tid );
}, 100 );
But it's not working. How do you manage this?

As we can see in the source code of that extension document.monetization is an expando property on a standard DOM document interface, this property is not a part of DOM, it's essentially a JavaScript object so it's not accessible directly from a content script which runs in an isolated world - all JavaScript objects/variables/expandos are isolated so the page scripts can't see the JS objects of content scripts and vice versa.
In Chrome to access such an expando property you need to run the code in page context and then use standard DOM messaging via CustomEvent to coordinate the code in page context and the content script as shown in a sibling answer in the same topic.
In Firefox you can use wrappedJSObject e.g. document.wrappedJSObject.monetization

Related

Firefox Extension / Webextensions: Why doesn't the MDN example of Connection Based Messaging work?

This is the first time I read about writing Firefox extensions.
What I need is obviously only viable via WebExtensions and both a background and a contentscript. I actually only want to write all open tabs as links in a new tab and then File->Save it. Another alternative Idea was to put it into a JSON Object and save that through a dialog, then I probably could even spare the contentscript but I haven't found anything in the API to download a JSON Object via asking the user to download it via Download Dialog.
Whatever. I think I need to communicate with the content-script then.
I tried to run the following example, but it is not working. When I load the manifest file and open the debugger for extensions, it doesn't log anything and nothing has happened except that the variables myPort and portFromCS seem to be declared without any value.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#connection-based_messaging
// manifest.json
{
"manifest_version": 2,
"name": "Save Open Tabs",
"version": "1.0",
"description": "Save my tabs",
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content.js"]
}
],
"permissions": [
"activeTab",
"tabs"
]
}
// content.js
let myPort=browser.runtime.connect({name:"port-from-cs"});
myPort.postMessage({greeting: "hello from content script"});
myPort.onMessage.addListener((m) => {
console.log("In content script, received message from background script: ");
console.log(m.greeting);
});
// background.js
let portFromCS;
function connected(p) {
portFromCS = p;
portFromCS.postMessage({greeting: "hi there content script!"});
portFromCS.onMessage.addListener((m) => {
portFromCS.postMessage({greeting: "In background script, received message from content script:" + m.greeting});
});
}
browser.runtime.onConnect.addListener(connected);
Why doesn't the example work? Maybe wrong URL matching in the manifest file?

Content script not listening to message event

I am developing my first browser extension for my website.
What this extension should basically do is to have a browser action which opens a pop-up where you can edit specific cookie values for the current page.
However, cookie A can exist on the page / while cookie B can exist on the page /checkout. So I don't want to list every cookie inside the pop-up, only the one which is active on the current page.
So, I searched the documentation and found that in order to communicate between web page and add-on you have to use the message system as described here
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Communicating_with_the_web_page
To do so, my website has a JavaScript file which is loaded on every page. In this JavaScript I'm executing the following code
window.postMessage({type: 'FROM_PAGE', data: visibleCookies}, '*');
This piece of code is definitely executed, because I put a console.log before or after that statement, I can see that it's being logged.
Now, in my content script I want to listen to this by executing the following code
// experimentManager.js
console.log('testd');
window.addEventListener('message', (event) => {
console.log(event);
if (event.source !== window) {
return;
}
});
However, the console.log(event); is never executed. The listener is never activated. When I press the browser action so that the popup opens testd is logged into console, but still, the listener doesn't get any events. It's just not getting executed.
I don't know which files are relevant, but this is my manifest.json
// manifest.json
{
"manifest_version": 2,
"name": "My first addon",
"version": "1.0",
"description": "My first addon description",
"icons": {
"48": "icons/icon.png"
},
"browser_action": {
"default_icon": "icons/icon.png",
"default_title": "My first addon",
"default_popup": "popup/manage_experiment.html",
"browser_style": true
},
"permissions": [
"tabs",
"cookies",
"<all_urls>"
],
"content_scripts": [
{
"matches": ["*://*.mydomain/*"],
"js": ["experimentManager.js"]
}
]
}
And inside the pop-up script I'm executing this code among other things
browser.tabs.executeScript({file: "/content_scripts/experimentManager.js"})
.then(manageExperiments)
.catch(handleError);
which is probably the reason why the console.log('testd') gets executed, but nothing else?
What am I missing?

Access DOM elements data document->Iframe ->Iframe->method chrome extension

I'm trying to access some data from an iframe nested within an iframe, from developers console:
Object.keys(document.getElementById("contentBody").
contentDocument.getElementById('rawContent').
contentDocument.defaultView.window.messages)
["29c736c0ed25463c8436f4990ab6c6ec.zip",
"235819a8cf11488e83f0336603b71711.zip",
"66c9260590834d9698568c8a676ef406.zip",
"fae95e31cb424cd6ad21302217ef2cdc.zip",
"f554f712141047aa9aa24f765073e305.zip",
"e5c41819578240e0868f43ab6301aeb3.zip"]
That's what I expect back, but I've tried to get that very same info from a google chrome extension that I'm developing and for some reason I cant access messages array, this is the manifest file and contentscript.js (I've tried everything that came to my mind and searching for a few hours without success :/):
content.js
var iframeContentBody = document.getElementById('contentBody');
var innerDocContentBody = iframeContentBody.contentDocument;
var iframeRawContent = innerDocContentBody.getElementById('rawContent');
var innerDocRawContent = iframeRawContent.contentDocument; // iframeRawContent is undefined here
console.log(iframeRawContent.messages); // this prints undefined
manifest:
{
"manifest_version": 2,
"name": "Read Comments",
"description": "Read all comments from the current forum",
"version": "1.0",
"content_scripts": [{
"matches": ["*://*.forum.net/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Read Comments"
},
"permissions": ["activeTab", "tabs"]
}
Gists to set everything up:
HTML Example
after downloading and placing these 3 files in the same folder, run this:
python -m SimpleHTTPServer 80 # You may need to run it with sudo
then go to localhost/test.html and you're all set, if you test the line that I posted in the console you should see [1,2,3]
Extension example
this is the extension code
Developers console:
Chrome extension with "all_frames": true
Hacky solution: Partial solution
In this gist there is a way to do it, it's hard to detect when the iframe has been loaded, and it's harded to detect when the iframe inside the another iframe has been loaded, so a setTimeout gives enough time to get it done, then adding a script element to the dom seems to bypass all security measures that chrome extensions may have and it does get the content of the attribute without any other issue, still this seems hacky and it's not what I'm trying to do, I'm looking for a clean solution or a clean way to access the dom of a nested iframe as the example code states...
Thanks, any suggestion is welcome.
This was my solution after all, between what we talk over comments and my research over docs and other threads:
Content script:
(function () {
document.addEventListener("DOMContentLoaded", function () {
contentBody = document.getElementById("contentBody");
contentBody.addEventListener("load", function () {
rawContent = contentBody.contentDocument.getElementById("rawContent");
if (rawContent) {
var s = document.createElement("script");
s.src = chrome.extension.getURL('injected.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
}
});
});
})();
Injected file:
keys = Object.keys(document.getElementById("contentBody").contentDocument.getElementById("rawContent").contentDocument.defaultView.window.messages);
console.log(keys);
Manifest:
{
"manifest_version": 2,
"name": "Read Comments",
"description": "Read all comments from the current forum",
"version": "0.0.1",
"content_scripts": [{
"matches": ["*://localhost/*"],
"run_at": "document_start",
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Read Comments"
},
"permissions": [
],
"web_accessible_resources": ["content.js", "injected.js"]
}
As a simple explanation the main issue was the asyc load of iframes and the moment when the extension code ran, so after listening to a lot of events and discarding the ones that doesn't have the required elements on the dom everything went fine...
For completeness, here’s a version with "all_frames":true. There are two problems to work around: (1) getting messages from the inner frame to the top and (2) getting messages from the isolated world of the webpage to the isolated world of the content script (I assume you’re wanting to do more than just write messages to the console). This solves both at once by using postMessage.
if ( window.top !== window.parent ) {
var s = document.createElement("script");
s.textContent = "postMessage(messages,'*');";
s.onload = function() {
this.parentNode.removeChild(this);
};
document.head.appendChild(s);
} else if ( window.top === window ) {
addEventListener('message',function(e) {
console.log(e.data);
});
}
I must confess I’ve not actually tested it out. You may need to try making the injected script send a message from the webpage.

Develop a chrome extension to count <input> tags of currently accessed webpage

Hey I'm new to javascript and chrome extension developing. I'm trying to develop a chrome extension which use browser action to count the number of tags in currently active tab of the Google Chrome browser.I can use getElementsByTagName.lenght method to calculate the number of tags and I know that I can use console API to access the DOM of a webpage. But I have no idea how to call that API from my javascript file.Do you guys know anything regarding this ?
To get access to the current page DOM you need to write a content script.
1. Specify the content script in manifest.json
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"]
}
]
If you need to inject the script sometimes use Programmatic Injection by specifying permissions field:
{
"name": "My extension",
...
"permissions": [
"activeTab"
],
...
}
2.I would prefer the latter in this case.In popup.js add the code:
function printResult(result){
//or You can print the result using innerHTML
console.log(result);
}
chrome.tabs.executeScript(null, { file: 'content.js' },function(result){
printResult(result);
});
3.In content script u have access to current page/active tab DOM.
var result=document.getElementsByTagName("input").length;

Communication between scripts in Chrome Extension

I know there are many variations of this question already in existence here, but none of them seem to work for me.
Details:
I'm writing an extension that pulls some email data from emails you send in gmail. In order to achieve this I am using this version of Gmailr https://github.com/joscha/gmailr.
In effect, I have three content scripts: Gmailr.js and main.js (which are pretty much identical to those in the link above) allow me to pull out the information I'm looking for. Then content.js I use to send a message to the background page of the extension.
The problem is that from gmailr.js and main.js I cannot use any of the Chrome APIs, and I'm not really sure why, so I can't send messages from these back to the background page.
That is why I made content.js which can communicate with the background page. However, it does not seem to be able to see anything the other content scripts do. For example, main.js inserts a div at the top of the page. When I try to attach an event listener to a button in this div from content.js, I am told that no such element exists.
How can I get the data pulled out by main.js to be seen by content.js? (I also tried to put the data in local storage, then trigger a custom event listener to tell content.js to read local storage, but no luck because they don't seem to be able to hear each other's event being triggered).
Any insight or alternatives are much appreciated.
(I can post code if necessary, but it's fragmented and long)
My manifest file:
{
"manifest_version": 2,
"name": "Email extractor",
"description": "Extracts data from emails",
"version": "1.0",
"background": {
"script": "background.js"
},
"content_scripts": [
{
"matches": [
"*://mail.google.com/*",
"*://*/*"
],
"js": [
"lib/yepnope.js/yepnope.1.5.4-min.js",
"lib/bootstrap.js",
"main.js",
"gmailr.js",
"content.js"
],
"css": [
"main.css"
],
"run_at": "document_end"
}
],
"permissions": [
"tabs",
"storage",
"background",
"*://mail.google.com/*",
"*://*/*"
],
"browser_action": {
"default_icon": "img/icon.png",
"default_popup": "popup.html"
},
"web_accessible_resources" : [
"writeForm.js",
"disp.js",
"/calendar/jsDatePick.min.1.3.js",
"/calendar/jsDatePick_ltr.min.css",
"lib/gmailr.js",
"lib/jquery-bbq/jquery.ba-bbq.min.js",
"content.js",
"main.js",
"background.js"
]
}
This is main.js:
Gmailr.init(function(G) {
sender = G.emailAddress();
G.insertTop($("<div id='gmailr'><span></span> <span id='status'></span>)");
el = document.getElementById("testid");
el.addEventListener('click', mg, false);
var status = function(msg) {
G.$('#gmailr #status').html(msg); };
G.observe(Gmailr.EVENT_COMPOSE, function(details) {
....
status(" user: " + user);
console.log('user:', user);
//now try to send a message to the background page
//this always returns the error that method sendMessage does not exist for undefined
chrome.runtime.sendMessage({greeting: "test from gmailr"}, function(response) {
console.log("did it send?");
});
});
});
gmailr.js is quite long and is also not my own code but it can be seen here: http://pastebin.com/pK4EG9vh
Hi perhaps 3 likely reason to your problem :
The way you send messages to bgp from main.js and gmailr.js are perhaps wrong because you must arrive to communicate from any content script to your bgp. (in your manifest content script key the gmailr.js is missing). Show us your code it would help.
You seems to have a problem with the moment you search from content.js to access to the element created in main.js. Do you try to access your element with the jQuery $("").on() method ? A simple test must be to declare a function in one cs and to use it in another. If it's not working it's a manifest problem. The order you declare .js file in manifest content script key is important also.
try to in the manifest content script array "run_at":"document_end"
Hope it help !

Categories

Resources