In any YouTube video page (Gangnam Style for example), some elements can't be accessed by their XPath. From example, I'm trying to access the "Show more" button
by getting its XPath from the Inspect Window
and using this code
btn=$x('//*[#id="widget_bounds"]/div[2]/div[4]/div[7]/div[3]/span[1]');
but I get nothing, or more precisely, an empty list:
I've never encountered this problem before, are they using some sort of obfuscation trickery to prevent the item from being accessed?
Is there a way to work around it?
The content you want to reach is in an iframe on the page. BUT the problem is you will not be able to get to the content because the Same Origin Policy is going to prevent it.
document.getElementById("I0_1392927253257").contentWindow.document
SecurityError: Blocked a frame with origin "http://www.youtube.com" from accessing a cross-origin frame.
Check out #sashoalm's link below to change the context of iframe in Chrome.
Related
I am trying to develop a web application which has functionality similar to the Element Selector in developer tools in Chrome.
I need to open any URL from other different domains, inside a container in my app, and the page should appear exactly as it appears in normal browser, with all it styles. Then I should be able to hover over the elements in the opened page, highlight it, and display some property information about this element.
I created a simple Angular app and used Iframe tag to open the third party page. But, when I try to attach event handlers, with addEventListener, to the DOM elements in the Iframe, I get the cross-domain error message.
Uncaught DOMException: Blocked a frame with origin
"http://localhost:4200" from accessing a cross-origin frame.
I read some posts in stackoverflow like “Cross site contents cannot be read by JavaScript” and “If you don't have control over the framed site, you cannot circumvent the cross-domain policy.” For more details check the links:
Get DOM content of cross-domain iframe
Cross domain iframe issue
Following is my code:
<iframe id="pFrame" onload="frameLoad();" src="" />
function frameLoad() {
let frame = document.getElementById("pFrame");
let iframeWindow = frame.contentWindow;
iframeWindow.addEventListener("mouseover", mouseOver, true);
iframeWindow.addEventListener("mouseout", mouseOut, true);
iframeWindow.addEventListener("click", onClick, true);
}
Do you have any advice or maybe suggest different technique to solve this problem?
Thanks
Have you tried looking the Mozilla Goggles project ?
It has an integrated editor, and their inspector seems to be very powerful.
Plus it don't require any application on your computer.
I am trying to get elements by the Chrome developer console using the function document.querySelectorAll, the point is that it does not return any element, however I see the elements on the Elements tabs.
I was wondering whether someone has faced similar issues. Shall I change some options on the browser configuration?
By the way, the Chrome version is 63 on MAC. In addition, the page I am working on has an iframe html tag, may this be the reason of the strange behavior?
This is what I get from the Developer Console
And this is what I get from the elements tabs:
There aren't any browser settings that would affect document.querySelectorAll(). It's pretty core functionality.
You mentioned an iframe, so it's likely that is the source of the confusion. When using iframes, you can't access or modify the contents of the iframe directly from the outer level. To the outer level, it's essentially a black box. This is due to sandboxing that the browser does.
The exception to this is if the iframe and the main page are on the same domain (e.g., http://example.com/page1 and http://example.com/page2).
If they are both on the same domain, then you can access it's window with contentWindow:
const iframe = document.querySelector('iframe');
iframe.contentWindow // the window for the iframe
From there, you can access its document, and run querySelectorAll() against that:
iframe.contentWindow.document.querySelectorAll('div');
That will get all of the div elements in the iframe.
Using the Chrome Dev Tools console, I'm trying to select an element inside an iframe on the page. Is there a way to do this programmatically without having to select the frame in the frames dropdown in order to set the console context to that frame first? Assuming the target iframe is frames[1], and the element inside that iframe has an id of "some-elem", the following does not seem to work:
frames[1].document.getElementById('some-elem');
I think you want contentDocument instead of document (see this related question).
Note that this will only work if iframe and main document are in the same domain. Otherwise, you are attempting cross-site scripting and it will be blocked by the browser.
So I've read about the HTML5 sandbox property and I understand that if I want to prevent an iframe redirect its parent window I can use the sandbox property leaving allow-top-navigation out. However when this is done, if the iframe was originally relying on top level redirection, what happens in its place is that it redirects to a blank page, effectively breaking navigation.
Can I prevent the iframe from tinkering its parent window while still allowing "top level" redirects, only letting these work within the context of the iframe instead of being top level?
Edit: For context, I'm working with a third party and its page has a form with a target _top. If the iframe is sandboxed, upon submitting the form users get a blank page, if it's not sandboxed the entire page is redirected. I'm looking for something that would allow to submit the form and show the result within the iframe itself.
With HTML5 the iframe sandbox attribute was added.
At the time of writing this works on Chrome, Safari, Firefox and recent versions of IE and Opera but does pretty much what you want:
Allows the iframe content to be treated as being from the same origin as the containing document
<iframe src="url" sandbox="allow-same-origin"></iframe>
Browser Compatibility
Some Useful links
w3schools for sandbox
developer.mozilla.org iframe
-
You can use the onbeforeunload property and determine if you wan to redirect or not.
Here is the docs page for it
Basically what I would try is this:
Make a function that adds the sandbox attribute with everything, just leaving out the allow-top-navigation, to the iframe
Bind a function to the onbeforeunload property of the iframe that calls the function that adds the sandbox attribute (be sure not to return anything because a dialog will pop-up)
This should work because the request is made in the iframe first, and then we can prevent it from carrying over to our top level window.
Another thing you should check is if you maybe left out the allow-formsoption, which can cause what you are describing.
Please let me know if any of this worked.
The scene: I'm writing an embeddable widget. It takes the form of a <script> tag, which builds an iframe containing everything it needs to display. The iframe has no src, and the script writes to it with theIframe.contentWindow.document.write(). This keeps the widget contained, and keeps element ids and script from conflicting with the page on which the widget is embedded.
The trick: The widget has to be able to change its size. To do this, it sets its containing iframe's style.height. This requires access to the outer page's DOM. In Firefox and IE, this is allowed, because the iframe's document and the outer document are considered to share an origin.
The twist: In Safari, however, the two documents are considered not to share an origin. The inner document is considered to be at about:blank, while the outer document is clearly using a different protocol and "domain" (if blank can be considered the domain).
The question: How can I build an iframe programmatically whose document Safari/WebKit will consider to have the same origin as the document of the window creating it?
Edit: After further experimentation, I can't find a way to programmatically create an iframe whose location is not about:blank regardless of whether I change its contents.
If I create the frame with document.createElement(), give it a src which points to a real HTML resource on the same origin called "foo.html", and document.body.appendChild() it, Safari's console shows the element as expected in the DOM, but the contents of the page do not appear, and the document is listed in the sidebar as "about:blank".
If I include the HTML for the iframe directly in the page, the contents of foo.html appear, and "foo.html" appears in the sidebar.
If I insert the HTML using document.write(), I get the same result as with document.body.appendChild().
Both programmatic versions work in Firefox.
The best suggestion I could give is to have the iframe set to a blank page on the same server (ie blank.html) and then edit the content. A pain in the rear, I know but it's a workaround.
You could also try
iframe.contentDocument.open("replace");
iframe.contentDocument.write("<b>This is some content</b>");
iframe.contentDocument.close();
However, I'm not sure if that only works in IE. Sorry I couldn't be more helpful than that.
Aha. This seems to be a bug in WebKit. When an iframe is created programmatically, its src attribute is ignored. Instead, the frame defaults to about:blank and must be directed to a URL to point elsewhere. For example:
theIframe.contentWindow.location = theIframe.src