There is page with economic calendar.
Scenario:
I am loading page in browser. For example this: http://www.dukascopy.com/swiss/english/marketwatch/calendars/eccalendar/
Look through it. And if there is interesting data for me, I click button and save all html with loaded iframe-data for parsing.
The problem is that necessary data on this page loaded with iframe. I read here that chrome denies iframe access with js-injects. But I can easy access necessary tables with "inspect element" from right-click menu. Is it possible to access it without js-injects? Just like automatic "inspect DOM element" or inner HTML?
I solved this issue in pyside (python qt webkit interface) this way:
def print_content():
res = web.page().mainFrame().childFrames()
for i in res:
s = i.documentElement().toOuterXml()
print(s)
But now I want do it from chrome(chromium) extension. Is there similar functional in modern chrome(chromium)?
For example:
chrome.web.page().mainFrame().childFrames() etc...
UPD:
Tried recomendation. Corrected manifest and add to content script:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
var res = document.querySelectorAll("iframe");
var len = res.length;
for (var i = 0; i < len; i++) {
//alert(myStringArray[i]);
console.log(res[i].contentDocument);
//Do something
}
//console.log(res);
Getting this error:
Error in event handler for (unknown): Error: Blocked a frame with origin "dukascopy.com"; from accessing a cross-origin frame. at chrome-extension://bgoddjjeokncninlaacmjamgkohmcecb/content.js:19:23 at extensions::messaging:323:11 at Function.target.(anonymous function) (extensions::SafeBuiltins:19:14) at Event.dispatchToListener (extensions::event_bindings:386:22) at Event.dispatch_ (extensions::event_bindings:371:27) at Event.dispatch (extensions::event_bindings:392:17) at dispatchOnDisconnect
You need to inject your content script into the inner frame to access it. This is perfectly possible, it's just that the outer document's script cannot access the iframe contents.
This question covers how to do this in case of manifest-based injection.
For programmatic injection, you can pass all_frames: true in InjectDetails object for chrome.tabs.executeScript.
Related
How can I communicate from a JavaScript code of a webpage to the main code of the add-on?
For example, something like this: If some element is clicked, in the corresponding event handler of the page script, which is the syntax that can be used to send some message to the main code?
Specifically, something like this, where the frame now must be replaced by a generic webpage. Is it possible?
Edit: I have tried the suggested code, but how I had said, the application returns this error:
console.error: sherlock:
Message: ReferenceError: document is not defined
Stack:
A coding exception was thrown in a Promise resolution callback.
See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise
Full message: ReferenceError: document is not defined
Previously my question, I had infact tried something similar without any effect.
Yes it is possible.
document.onload = function() {
var elementYouWant = document.getElementById("someID");
elementYouWant.onclick = console.log("Yup.. It was clicked..");
};
Reference.
The answer to the question is not as trivial as it may seem at first sight. I had also thought of a logic of the type described in the Pogrindis' response.
But here, in the case of interaction between the main script (i.e. that of the add-on) and generic script of arbitrary documents, the pattern is different.
In summary, the interaction takes place in this way:
It is required the API page-mod.
Through the property includes of the object PageMod you create a reference to the document, specifying the URI (wildcards are allowed).
Via the contentScriptFile property it is set the URL of the .js file that will act as a vehicle between the main code and that of the document.
Here's an example that refers to the specific needs of the context in which I am. We have:
an add-on code (the main code);
a Sidebar type html document (gui1.html) loaded in the file that I
use as a simple UI (I advise against the use of Frames, since it does
not support many typical HTML features - eg the click on a link,
etc.) containing a link to a second document (gui2.html) which will then
be loaded into the browser tab (I needed this trick because the
Sidebar does not support localStorage, while it is necessary for me);
a script in the document.
We must create an exchange of information between the two elements. In my case the exchange is unidirectional, from the page script to the main one.
Here's the code (main.js):
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "resource://path/to/document/gui2.html",
contentScriptFile: data.url("listen.js"),
onAttach: function(worker) {
worker.port.on("gotElement", function(elementContent) {
console.log(elementContent);
});
}
});
and in the html page script:
<script type="text/javascript">
[...]
SOWIN = (navigator.userAgent.toLowerCase().indexOf("win") > -1) ? "win" : "nix";
if (SOWIN == "win") {
window.postMessage("win","*");
} else {
window.postMessage("Linux","*");
}
[...]
</script>
Finally in the JS file (listen.js) to be attached to the page script:
window.addEventListener('message', function(event) {
self.port.emit("gotElement", event.data);
}, false);
This is just a small example, but logic I would say that it is clear. The uploaded content scripts are not accessible directly from main.js (i.e. the add-on), but you can create a bidirectional communication through the exchange of messages. To achieve this we have to put ourselves in listening the event Attach of the page-mod. Then, it is passed a worker object to the listener; that worker may be used by the add-on for the exchange of messages.
Here are the references to have an exhaustive picture:
Interacting with page scripts
Communicating with other scripts
page-mod
port
Communicating using "port"
postMessage
Communicating using postMessage
I'm developing a chrome extension and bumped into a big problem.
I'm using content scripts to inject my javascript code on a web site. The web site has an iframe.
I can change the source code of the iframe but don't seem to get any access to the iframe's contentWindow property. I need it to insert text at the current carret position.
So basically this code works perfectly in the context of the page:
$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text');
But when I try it to run in the context of my chrome extension I get this error:
TypeError: Cannot read property 'document' of undefined
What's strange is that I can access the html of the iframe. So this code works perfectly from the chrome extension:
$("#iframe1").contents().find('div').html('test')
I tried putting "all_frames": true in the manifest file but no luck :(
To understand why your code does not work, I include a fragment of my previous answer:
Content scripts do not have any access to a page's global window object. For content scripts, the following applies:
The window variable does not refer to the page's global object. Instead, it refers to a new context, a "layer" over the page. The page's DOM is fully accessible. #execution-environment
Given a document consisting of <iframe id="frameName" src="http://domain/"></iframe>:
Access to the contents of a frame is restricted by the Same origin policy of the page; the permissions of your extension does not relax the policy.
frames[0] and frames['frameName'], (normally referring to the the frame's containing global window object) is undefined.
var iframe = document.getElementById('frameName');
iframe.contentDocument returns a document object of the containing frame, because content scripts have access to the DOM of a page. This property is null when the Same origin policy applies.
iframe.contentDocument.defaultView (refers to the window object associated with the document) is undefined.
iframe.contentWindow is undefined.
Solution for same-origin frames
In your case, either of the following will work:
// jQuery:
$("#iframe1").contents()[0].execCommand( ... );
// VanillaJS
document.getElementById("iframe1").contentDocument.execCommand( ... );
// "Unlock" contentWindow property by injecting code in context of page
var s = document.createElement('script');
s.textContent = 'document.getElementById("iframe1").contentWindow.document.execCommand( ... );';
document.head.appendChild(s);
Generic solution
The generic solution is using "all_frames": true in the manifest file, and use something like this:
if (window != top) {
parent.postMessage({fromExtension:true}, '*');
addEventListener('message', function(event) {
if (event.data && event.data.inserHTML) {
document.execCommand('insertHTML', false, event.data.insertHTML);
}
});
} else {
var test_html = 'test string';
// Explanation of injection at https://stackoverflow.com/a/9517879/938089 :
// Run code in the context of the page, so that the `contentWindow`
// property becomes accessible
var script = document.createElement('script');
script.textContent = '(' + function(s_html) {
addEventListener('message', function(event) {
if (event.data.fromExtension === true) {
var iframe = document.getElementById('iframe1');
if (iframe && (iframe.contentWindow === event.source)) {
// Window recognised, post message back
iframe.contentWindow.postMessage({insertHTML: s_html}, '*');
}
}
});
} + ')(' + JSON.stringify(test_html) + ');';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
}
This demo is for educational purposes only, do not use this demo in a real extension. Why? Because it uses postMessage to pass messages around. These events can also be generated by the client, which causes a security leak (XSS: arbitrary HTML injection).
The alternative to postMessage is Chrome's message API. For a demo, see this answer. You won't be able to compare the window objects though. What you can do is to rely the window.name property. The window.name property is automatically set to the value of the iframe's name attribute (just once, when the iframe is loaded).
I've an extension, and an XUL file inside it (let's call it A). XUL file contains an <iframe>, where is loaded some web page (let's call it B). B is loaded from the different domain.
A is parent to B. I want to send a message from within B to A using window.parent.postMessage().
I'm getting the following exception:
... permission denied to B to call method ChromeWindow.postMessage
How to fix that error? If there is no way to do that, how can I pass message from B to A?
I am using Firefox 16.0.1 under Windows 7.
I had a very similar problem,
it's just I had a html-popup (local) that couldn't send 'postMessage' to my xul-background-task.
I think I got it to work,
strangely enough by initiating a MessageEvent of my own (the very same thing postMessage does)
but with a (I believe obsolete) fallback.. in short: I brewed something together from MDN and other sites ;)
My script in the content:
var Communicator =
{
postMessage: function(data)
{
// We need some element to attach the event to, since "window" wont work
// let's just use a fallback JSON-stringified textnode
var request = document.createTextNode(JSON.stringify(data));
// and as always attach it to the current contentwindow
document.head.appendChild(request);
// Now let's make a MessageEvent of our own, just like window.postMessage would do
var event = document.createEvent("MessageEvent");
event.initMessageEvent ("own_message", true, false, data, window.location, 0, window, null);
// you might want to change "own_message" back to "message" or whatever you like
//and there we go
request.dispatchEvent(event);
}
}
And instead of window.postMessage(data) now use Communicator.postMessage(data)
that's all!
Now in my overlay there's nothing but our good old
addEventListener('own_message', someMessageFunc, false, true);
//or maybe even "message" just like originally
Hopefully this will work for you, too (didn't check that on iframes...)
You should check the type of iframe B
Edit:
Apparently you must flag your chrome as contentaccessible, and take into consideration the security.
Just posting in case someone faced the same problem.
Succeeded in posting message from within B to A using events as described here.
But it is not answer, because window.parent.postMessage() still doesn't work as intended.
I have an iframe FB app. We have three places where we develop it: My localhost, stage server where we test the app, production server. Localhost and production have HTTPS. Localhost and stage apps have sandbox mode enabled. All versions of app are identical, code is the same. Stage and production are totally the same server machine with the same settings except of the HTTPS.
Now what is happening only at my stage server app: When I click on something where jQuery UI dialog should be summoned, it raises following error in my Firebug: Permission denied to access property 'Arbiter'. No dialog is summoned then. It's raised in somehow dynamically loaded canvas_proxy.php, within this code:
/**
* Parses the fragment and calls Arbiter.inform(method, params)
*
* #author ptarjan
*/
function doFragmentSend() {
var
location = window.location.toString(),
fragment = location.substr(location.indexOf('#') + 1),
params = {},
parts = fragment.split('&'),
i,
pair;
lowerPageDomain();
for (i=0; i<parts.length; i++) {
pair = parts[i].split('=', 2);
params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
var p = params.relation ? resolveRelation(params.relation) : parent.parent;
// The user is not inside a frame (probably testing on their own domain)
if (p == parent || !p.Arbiter || !p.JSON) {
return;
}
p.Arbiter.inform(
'Connect.Unsafe.'+params.method,
p.JSON.parse(params.params),
getBehavior(p, params.behavior));
}
The line if (p == parent || !p.Arbiter || !p.JSON) { raises it. My script code linking the JS API looks like this:
<script src="https://connect.facebook.net/en_US/all.js#appId=APPID"></script>
Have anyone any clue why this could be happening? I found this and this, but these issues doesn't seem to be helpful to me (or I just don't get it). Could it be because of the HTTPS? Why it worked the day before yesterday? I am desperate :-(
whenever you have a permission denied message and you are dealing with frames or iframes, it's a document domain issue. One document belongs to domain x and the other is domain y. And notice that www.domain.com and domain.com are not the same document domains!
When you are tapping into the DOM of one framed document from another one, (whether it is for the purpose of changing the values of a page element or simply reading the values of some hidden variable or url etc), you will get a permission denied message unless both framed documents are served from the same/identical domains.
So, if one frame belongs to www.mydomain.com and the other happens to be just mydomain.com or www.someotherdomain.com, you get that bloody permission denied error.
And there is no way around it. And If there were, the identity theft problem would have sky-rocketed in no time.
I want to get at the 'outerHTML' of a node I've captured using document.evaluate (ie xPath) from a node on another web page that is from a different domain. I.e. I have a Firefox tab running my javascript that is trying to access the content of a second tab. I dont have control over the content of the web page in the second tab.
I used importNode along with the answer to a similar question...
How do I do OuterHTML in firefox?
I am able to do other cross domain manipulation, but cant get importNode to work. I only need this to work in Firefox.
This is where I've got to so far - get error message: "Access to property denied code: 1010" ...
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserWrite");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var recordNodeClone = currentFrame.document.importNode(recordNode, true);
var fosterParentNode = document.createElement('div');
//Error for line below: Access to property denied" code: "1010
fosterParentNode.appendChild( recordNodeClone );
var recordNodeOuterHTML = fosterParentNode.innerHTML;
console.log("fosterParentNode=%o", fosterParentNode);
console.log("fosterParentNode.innerHTML=%o", fosterParentNode.innerHTML);