Google Extension: Access to DOM in iframe of a different domain - javascript

So I've found several pages on here, as well as various blog posts that seem to do pretty much exactly what I want to do, but they are all a few years old and seem really easy but don't work.
As the title says, On thisdomain.com there is a iframe from thatdomain.com and I want to get the value in a div in that iframe.
Manifest.json
{
"manifest_version": 1,
"name": "MyExtention",
"version": "1.0",
"description": "Nothing Yet",
"permissions": [
"storage",
"tabs",
"unlimitedStorage",
"webRequest",
"webNavigation",
"*://*.match-both-iframe-and-main-domain.com/*",
"*://*/*"
],
"background": {
"scripts": ["listener.js"],
"persistent": true
},
"content_scripts":
[
{
"matches": ["*://*.matchnothing.shshdjdjffkdj.com/*"],
"js": ["mainscript.js"],
"all_frames": true
}
]
}
The content script url matches nothing because it is fired from a listener (which works). Basically it waits for a request from one of 2 urls before it activates.
listener.js
var chrome = chrome || {};
var callback = function(listenerRes) {
console.log(listenerRes.url);
if (listenerRes.url.indexOf("listenurl1") > -1 ||
listenerRes.url.indexOf("listenurl2") > -1) {
chrome.tabs.get(listenerRes.tabId, function(tab) {
chrome.tabs.executeScript(tab.id, {file: "mainscript.js"});
});
}
};
chrome.webRequest.onBeforeRequest.addListener( callback, {urls: ["*://*.google.com/*"]} );
mainscript.js
var chrome = chrome || {};
... // helper functions and such
var iframe = document.getElementsByid('myiframe');
// Get all data.
var datas = [];
try {
datas = iframe.contentWindow.document.getElementsByClassName('mydata'); // error is here
}catch(e){
console.log(e);
}
... // do stuff with the data
On the commented line it throws a "Blocked a frame with origin URL from accessing a cross-origin frame."
So I am under the impression that some combination of all_frames = true, the chrome.tabs.executeScript, and the domains in the permissions should allow for this to work. But it doesn't.
It might be important to note, the reason for this listener is because the iframe isnt on the page to start.
Please help, Im an experienced web developer but this is my 1st foray into Chrome Extentions.

Related

Chrome Send Message and Listener Not Working

I have seen many similar questions, but none have solved my issue. I have a page running that has a specific div that changes every 5 seconds. I have a function in my popup.js script, which contacts my contentscript.js and asks for the value of that div. I am having a weird problem where the results from my content script on my localhost server are totally fine, but running this on the same html hosted on my domain is throwing a Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist, which leads to an undefined value . I know people said they had solutions such as disabling other extensions, but that is not a feasible solution since I plan on publishing this extension and can't force users to do that in order for it to work. I know the domain hosted on AWS works totally fine, since I can work around the website and make api calls to it through Postman etc.
popup.js
var currentNumber = 1000;
var lastUpdated = 1000;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "getNumber"}, function(response) {
currentNumber = response.current;
lastUpdated = response.lastUp;
});
contentscript.js
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request.method == "getNumber") {
var currentNumber = document.getElementById("1MainPart_lbUsersInLineAheadOfYou").innerText;
var lastUpdated = document.getElementById("MainPart_lbLastUpdateTimeText").innerText;
sendResponse({current: currentNumber, lastUp : lastUpdated})
return true;
}
return true;
});
manifest.json
{
"manifest_version": 2,
"name": "Extension",
"version": "0.1.0",
"permissions": [
"activeTab",
"tabs",
"storage",
"http://localhost/*",
"*Link to my domain*"
],
"content_scripts": [{
"js": ["contentscript.js"],
"matches": ["http://localhost/*", "*Link to my domain*"]
}],
"externally_connectable": {
"ids": [*inserted id*],
"matches": ["*Link to my domain*"],
"accepts_tls_channel_id": false
},
"background" : {
"scripts" : ["backgroundscript.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
Well according to the docs under...
https://developer.chrome.com/docs/extensions/reference/runtime/#event-onMessageExternal
It says...
onMessageExternal
runtime.onMessageExternal.addListener(listener: function)
Fired when a message is sent from another extension/app (by
sendMessage). Cannot be used in a content script.
So that only leaves a background script.

chrome extension inject javascript at top of head before any other js

What I want to do.
Using a Chrome extension I need to inject a script into the page context in such a way that the injected script runs before any other javascript on the page.
Why do I need this?
I need to hijack all console commands for a specific page so my extension can listen to the messages.
My current issue
Currently I am catching some of the messages logged by the page but not all of them, specifically, all messages from web-directory-132a3f16cf1ea31e167fdf5294387073.js are not being caught. After some digging I discovered that the web-directory-132a3f16cf1ea31e167fdf5294387073.js is also hijacking console but doing so before my script has a chance to.
As a visual, if I look at the network tab after loading the page, I see this:
My injected script is consoleInterceptor.js. It correctly captures the output of the js files that are loaded here accept web-directory-132a3f16cf1ea31e167fdf5294387073.js
Inside of web-directory-132a3f16cf1ea31e167fdf5294387073.js is some code something like this:
....
_originalLogger: t.default.Logger,
...
// do stuff with logging ....
this._originalLogger[e].apply(this._originalLogger, s),
What I think the problem is
It seems to me that, web-directory-132a3f16cf1ea31e167fdf5294387073.js is grabbing the standard console functions and storing them internally before my script has had a chance to replace them with my own versions. So even though my script works, the web-directory-132a3f16cf1ea31e167fdf5294387073.js still uses the original standard console functions it saved.
Note that web-directory-132a3f16cf1ea31e167fdf5294387073.js is an ember application and I dont see any simple way to hook into that code to overwrite those functions too but Im open to that as a solution.
My current code:
manifest.js
...
"web_accessible_resources": [
"js/ajaxInterceptor.js",
"js/consoleInterceptor.js"
],
"version" : "5.2",
"manifest_version": 2,
"permissions": [
"<all_urls>",
"tabs",
"activeTab",
"storage",
"webNavigation",
"unlimitedStorage",
"notifications",
"clipboardWrite",
"downloads",
"tabCapture",
"cookies",
"browsingData",
"webRequest",
"*://*/*",
"gcm",
"contextMenus",
"management"
],
"externally_connectable": {
"matches": ["*://apps.mypurecloud.com/*","*://*.cloudfront.net/*"]
},
...
background.js
var options = {url: [{hostContains: 'apps.mypurecloud.com'}]};
chrome.webNavigation.onCommitted.addListener(function(details) {
// first inject the chrome extension's id
chrome.tabs.executeScript(details.tabId, {
code: "var chromeExtensionId = " + JSON.stringify(chrome.runtime.id)
});
// then inject the script which will use the dynamically added extension id
// to talk to the extension
chrome.tabs.executeScript(details.tabId, {
file: 'js/injectConsoleInterceptor.js'
});
},
options
);
chrome.runtime.onMessageExternal.addListener(
function(msg, sender, sendResponse) {
if(msg.action === 'console_intercepted'){
_this.processConsoleMessage(sender, msg.details.method, msg.details.arguments);
}
});
injectConsoleInterceptor.js
var interceptorScript = document.createElement('script');
interceptorScript.src = chrome.extension.getURL('js/consoleInterceptor.js');
interceptorScript.onload = function(){this.remove();};
(document.head || document.documentElement).prepend(interceptorScript);
consoleInterceptor.js
if(!window.hasConsoleInterceptor){
window.hasConsoleInterceptor = true;
console.log('overriding console functions');
var originals ={};
var console = window.console;
if (console){
function interceptConsoleMethod(method){
originals[method] = console[method];
console[method] = function(){
// send the data to the extension
// chromeExtensionId should be injected into the page separately and before this script
var data = {
action: 'console_intercepted',
details: {
method: method,
arguments: arguments
}
};
chrome.runtime.sendMessage(chromeExtensionId, data);
originals[method].apply(console, arguments)
}
}
// an array of the methods we want to observe
var methods = ['assert', 'count', 'debug', 'dir', 'dirxml', 'error', 'group','groupCollapsed','groupEnd','info','log', 'profile', 'profileEnd','time','timeEnd','timeStamp','trace','warn','table'];
for (var i = 0; i < methods.length; i++){
interceptConsoleMethod(methods[i])
}
console.log('Successfully overridden console functions: '+methods.join(','));
}
}
My question
What can I do to make consoleInterceptor.js run before web-directory-132a3f16cf1ea31e167fdf5294387073.js loads so that web-directory-132a3f16cf1ea31e167fdf5294387073.js uses my modified console functions rather than the default browser console funcitons?
You can try this.In manifest.json file:
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["script/inject.js"],
"run_at":"document_start"
}
]
In inject.js:
var ss = document.createElement("script");
ss.innerHTML= "xxx";
document.documentElement.appendChild(ss);

Send messages from Chrome Extension to External Webpage

I have written a chrome extension that successfully records all of a users scrolling data (timestamp with pixel amount). This data is currently logging in the console.
My next step however is causing me a bit of trouble.
I want to then send every log of data that is created to an external webpage to be displayed. ie, the page will auto update and archive every new timestamped entry/pixel counter the extension records.
I've been able to communicate between the extension and webpage, by passing a simple message through a DOM event. But i'm having trouble getting the extension to send the scroll data it's collecting.
I feel like i'm close, and i've read Google's Messaging API's but I feel a bit lost.
Here's my code so far:
Manifest.json
{
"manifest_version": 2,
"name": "Caressing the Silver Rectangle",
"description": "Measures Jesse Bowling's distance scrolled in pixels on Google Chrome",
"version": "1.1",
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"scroller.js"
],
"run_at": "document_start"
}
],
"background": {
"scripts": ["background.js"]
},
"externally_connectable": {
"matches": ["http://*/*jessebowling.space/caressingthesilverrectangle/*"]
},
"browser_action": {
"default_icon":"icon.png",
"default_title": "Caressing the Silver Rectangle",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>",
"background",
"tabs",
"storage"
]
}
Background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("background.js got a message")
console.log(request);
console.log(sender);
sendResponse("bar");
}
);
Scroller.js
/*jslint devel: true */
// The ID of the extension we want to talk to.
var caressingthesilverrectangleId =
"http://*/*jessebowling.space/caressingthesilverrectangle/*"
var totalScroll;
var lastKnownScrollPos = 0;
window.addEventListener("scroll", function () {
"use strict";
console.log(lastKnownScrollPos);
totalScroll += Math.abs(window.scrollY - lastKnownScrollPos);
lastKnownScrollPos = window.scrollY;
chrome.storage.sync({ scroll: totalScroll });
function (response) {
console.log(response);
}
});
// send data through a DOM event
document.dispatchEvent(new CustomEvent('csEvent', {detail:
totalScroll}));
// Make a simple request:
chrome.runtime.sendMessage(caressingthesilverrectangleId,
{getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
chrome.runtime.sendMessage(caressingthesilverrectangleId,
{activateLasers: true});
});
// Start a long-running conversation:
var port = chrome.runtime.connect(caressingthesilverrectangleId);
port.postMessage(chrome.storage.sync({ scroll: totalScroll }));
I'm guessing the part where you do
var port = chrome.runtime.connect(caressingthesilverrectangleId);
port.postMessage(chrome.storage.sync({
scroll: totalScroll
}));
is where you are trying to send the message. However the message you are sending is the result of chrome.storage.sync, which is always undefined. You might want to split these two calls up so you actually send some data like:
var port = chrome.runtime.connect(caressingthesilverrectangleId);
chrome.storage.sync({
scroll: totalScroll
});
port.postMessage({
scroll: totalScroll
});

Open array of links

I am working on simple Chrome Extension with the aim of opening every link on a page with the class of entry. Currently, I have this....
manifest.json:
{
"manifest_version": 2,
"name": "Hello World",
"description": "A simple Chrome Extension",
"version": "1.0",
"background": {
"scripts": ["openlinks.js"],
"persistent": true
},
"permissions": [
"tabs",
"http://*/",
"https://*/"
],
"browser_action": {
"default_icon": "logo.png"
}
}
openlinks.js:
chrome.browserAction.onClicked.addListener(function(tab) {
var linkArray = ['https://www.google.com', 'http://www.bbc.co.uk']; // your links
for (var i = 0; i < linkArray.length; i++) {
chrome.tabs.create({
url: linkArray[i]
});
}
});
Now I am trying to replace the array of sample links with an array of links from the current tab. Is it just a case of using standard JavaScript or jQuery to achieve this?
Take a look at Chrome Extensions Overview # Architecture, because you'll need both an Event Page and a Content Script to make this happen.
Here's an outline of how I would go about solving this:
Manifest structure (Event Page + activeTab permission).
"background": { "scripts": ["bg.js"], "persistent": false },
"permissions": ["activeTab"],
"browser_action": {},
When the browser action is clicked, the browser grants permission to access the current tab, which we use to inject the script. See Content Scripts # Programmatic Injection.
// bg.js
chrome.browserAction.onClicked.addListener(tab =>
chrome.tabs.executeScript({file: 'content.js'});
});
The content script has permission to access the DOM and use message passing, but is restricted from most of the extension APIs (in particular chrome.tabs).
// content.js
message = {}
message.links = [...document.querySelectorAll(
'div.question-summary a.question-hyperlink')].map(e=>e.href);
chrome.runtime.sendMessage(message);
The background page listens for the message.
// bg.js
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
request.links.forEach(link => chrome.tabs.create({url: link});
});

chrome.tabs.executeScript: How to get access to variable from content script in background script?

How to get access to variable app from content script app.js in background script background.js?
Here is how I try it (background.js):
chrome.tabs.executeScript(null, { file: "app.js" }, function() {
app.getSettings('authorizeInProgress'); //...
});
Here is what I get:
Here is manifest.json:
{
"name": "ctrl-vk",
"version": "0.1.3",
"manifest_version": 2,
"description": "Chrome extension for ctrl+v insertion of images to vk.com",
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
"run_at": "document_end"
}],
"web_accessible_resources": [
"jquery-1.9.1.min.js"
],
"permissions" : [
"tabs",
"http://*/*",
"https://*/*"
],
"background": {
"persistent": false,
"scripts": ["background.js"]
}
}
Full code for instance, at github
https://github.com/MaxLord/ctrl-vk/tree/with_bug
To avoid above error use following code
if (tab.url.indexOf("chrome-devtools://") == -1) {
chrome.tabs.executeScript(tabId, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
}
instead of
chrome.tabs.executeScript(null, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
Explanation
chrome://extensions/ page also fires chrome.tabs.onUpdated event, to avoid it we have to add a filter to skip all dev-tool pages.
(Would've submitted this as comment to the accepted answer but still lack the required reputation)
You should also give the tabId to chrome.tabs.executeScript as first argument when you have it. Otherwise you risk user switching windows/tabs right after requesting a URL and background.js doing executeScript against wrong page.
While fairly obvious on hindsight it threw me for a loop when I got that same error message "Cannot access contents of url "chrome-devtools://.." even though my chrome.tabs.onUpdated eventhandler was checking that the page user requested had some specific domain name just before doing the executeScript call.
So keep in mind, chrome.tabs.executeScript(null,..) runs the script in active window, even if the active window might be developer tools inspector.
We should notice that, in the manifest cofigļ¼š
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
in the "matches" part, only http, https are matched, so if you load your extension in page like: 'chrome://extensions/', or 'file:///D:xxx', that error will occur.
You may load your extension in the page with the url 'http://'; or add more rules in your 'matches' array.

Categories