Comments (34)
Targetting the iframe appears to be just a matter of getting a new context that is pointing at the iframe target. In Chromedp each page can have several targets. Each target has a type ("page" for the main page, "background_page" for Chrome extensions, and "iframe" for iframes).
You can get a list of the page targets by calling chromedp.Targets
with your main context ctx
, and this will return an array of *target.Info
objects. The important fields in those objects are Type
, Url
, and TargetID
. Since you may have multiple iframes on a single page, you will want to iterate through all the targets and get the one with Type
iframe and a matching Url
. Once you have that, you can use the TargetID
to create a new context that targets the iframe.
You can create a new context from a parent context that will let you target subtargets within a page as long as you specify the ContextOption
chromedp.WithTargetId
along with the TargetID
for the frame you are after. From the documentation, it looks like when you call chromedp.NewContext
with a parent context but no ContextOption
, it just creates a new tab in the same browser, but including WithTargetId
when you call NewContext
will let you access targets in the already loaded page.
Once you have the new context pointing at the iframe, you would use it just like you would the parent context.
Here's how I am getting an iframe context:
func getIframeContext(ctx context.Context, uriPart string) context.Context {
targets, _ := chromedp.Targets(ctx)
var tgt *target.Info
for _, t := range targets {
fmt.Println(t.Title, "|", t.Type, "|", t.URL, "|", t.TargetID)
if t.Type == "iframe" && strings.Contains(t.URL, uriPart) {
tgt = t
}
}
if tgt != nil {
ictx, _ := chromedp.NewContext(ctx, chromedp.WithTargetID(tgt.TargetID))
return ictx
}
return nil
}
And here's how you would use it.
ictx := getIframeContext(ctx, "/substring/in/iframe/uri/")
selector := "a.LinkInIframe"
script := fmt.Sprintf("document.querySelector(\"%s\").href;", selector)
var b []byte
_ = chromedp.Run(
ictx, // <-- instead of ctx
chromedp.WaitVisible(selector, chromedp.ByQuery),
chromedp.Evaluate(script, &b),
)
fmt.Println("href in iframe:", string(b))
Also, the --disable-web-security
flag is NOT required for this to work.
from chromedp.
We're working on a redesigned version of chromedp; among many changes and improvements, it will have proper support for iframes. See the refactor
branch if you want to take a peek. It should be ready soon, but still needs quite a bit of work to be usable.
from chromedp.
@ysmood nice project, I decide to use rod instead of chromedp
from chromedp.
Yes, the basics work, but some of the extras like Click and WaitVisible do not
If you can't even click the element inside an iframe, I feel it's better to say "basically doesn't work".
Maybe you can borrow some ideas of how rod handles it, you can do all the page operations with iframe. Not just iframe, you have to handle shadow DOM separately too:
https://github.com/ysmood/rod/blob/d6517f762538ac739aa27232110095e7436e708b/element_test.go#L36-L40
from chromedp.
I don't have a clear idea either. If we decide to not add new API, I think we should instead add docs and examples to explain how to deal with iframes.
from chromedp.
@jerblack Now that is some quality answer. Thank you so very much, I'll go try this out right away. Happy new year :)
from chromedp.
Apologies all for the silence here. I realise this is a problem for many people and that the passing of time can be frustrating.
We recently added a bit of new API for #463, to enable running queries on a specific node instead of always running them on the document root node.
It just occurred to me that we could use the same API for iframes. That is, using the FromNode
query option with an iframe node should then run the query on that iframe's document node.
I've implemented that in the comment above, and it works. You can take a look at TestQueryIframe
for a working example:
Lines 278 to 286 in 49daeb6
The API for this is a bit clunky; it requires two calls to chromedp.Run
, which I'm not very happy about. I'll be thinking about a nicer way to do this at a high level. Any API ideas welcome, but please keep it concise and provide a short example.
from chromedp.
@zhangguanzhang If you use rod it's like:
https://github.com/go-rod/rod/blob/e549401/examples_test.go#L67-L81
// Example_search shows how to use Search to get element inside nested iframes or shadow DOMs.
// It works the same as https://developers.google.com/web/tools/chrome-devtools/dom#search
func Example_search() {
page := rod.New().Connect().Page("https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe")
// get the code mirror editor inside the iframe and click it
page.Search(".CodeMirror").Click()
}
How rod works is to use the ObjectID
of the window
element inside the iframe: https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObjectId
Here is a comparison of the examples between rod and chromedp: https://github.com/go-rod/rod/tree/master/lib/examples/compare-chromedp
from chromedp.
Ok, all queries now work with iframes; see the PR above. Please try it out and let me know how it works.
from chromedp.
var iframes []*cdp.Node
if err := Run(ctx, Nodes(iframe
, &iframes, ByQuery)); err != nil {
t.Fatal(err)
}
if err := Run(ctx,
WaitVisible(#form
, ByID, FromNode(iframes[0])),
); err != nil {
t.Fatal(err)
}
How would you use this? I am having trouble implementing this into my app. I am trying to access a cc input field and would like to send the information to the field without leaving my current page.
from chromedp.
Please stay tuned to the codebase. There are new high level actions for Inspect
'ing nodes that are forthcoming. Because they are a combination of low-level protocol API calls, and Javascript, we've held off on releasing them (also because they have not been production ready yet).
from chromedp.
@kenshaw maybe there is any workaround that could be used now? Thank you!
from chromedp.
@kenshaw: Thank you very much for the "chromedp". It's fantastic. I would like to know any progress on this issue. What is equivalent in chromedp, for switching to the iframe as compared to selenium.
selenium code to switch to iframe: "driver.switchTo().frame()"
from chromedp.
@yogesh-desai if it's not yet implemented you always may try to use Javascript to access frames, but you would probably need to disable chrome security
from chromedp.
@CleanCodeNinja: Could you please point me out to any link to understand it in detail? I am a newbie to web-domain so please feel free to suggest any required details/ input.
Also, you can check the google-doc to understand what I want to do. Thanks
from chromedp.
Having worked with nightmare.js, I can tell you that accessing content in iframes is blocked in web browsers. Its a cross-site protection, and I've never found a way to disable it - though I haven't tried with the developer protocol.
A workaround is to get the source url of the given iframe and then navigate to it, now you have full access to the iframe's DOM.
from chromedp.
@nelsonkhan navigate to iframe url would be a good approach. If you are using Chrome It's also possible to access iframes like you usually do it with "--disable-web-security" chrome command line flag.
from chromedp.
@nelsonkhan : Thank you. Yes, after googling about iframes, I also found that "same domain policy" is for security and safety.
@dreadfulangel : I have used the --disable-web-security
option and was able to handle iframes.
from chromedp.
from chromedp.
I understand that we can target the iframe with an Evaluate
call, but that means we can't use all the nice functions like Click()
that come with chromedp
. Is there some way to set the default target frame in chromedp
so that the iframe becomes the "root" of all operations?
from chromedp.
That is amazing @mvdan. Thank you!
from chromedp.
An update: I don't know if it really ever will make sense to have chromedp.*
actions deal with all iframes. It's all "all or nothing" aspect, and trying to make a simpler API that allows "selection" of specific iframes is going to be more cumbersome than just doing direct CDP commands/queries.
from chromedp.
@mvdan do you have any thoughts on what we should do here? I keep thinking about this, but I haven't come up with any stable API that would make any kind of sense within the context of the project.
from chromedp.
something like this?
self.driver.switch_to.frame(frame_reference=self.driver.find_element_by_xpath(xpath="//iframe[@name='editor_body']"))
https://stackoverflow.com/questions/28814916/how-do-i-select-elements-inside-an-iframe-with-xpath
from chromedp.
@pu369 -- we're open to suggestions, however what you linked to is a Selenium instance using the WebElement API, which is fundamentally different than driving Chrome raw. If I remember correctly, chromedriver
did a whole lot of hacks that I'd rather avoid doing with chromedp
. Please note, you can more-or-less do the above already (ie, load a iframe
via a xpath query, and then switch to it), you just need to do it in two parts.
from chromedp.
@kenshaw How would you go by doing that? Can't find any examples
from chromedp.
//my ugly example to deal with iframes:
package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"github.com/chromedp/chromedp"
)
func main() {
Testiframe()
}
func Testiframe() {
//static file server
wd, _ := os.Getwd()
http.Handle("/", http.FileServer(http.Dir(wd)))
http.Handle("/index", writeHTML(`<!doctype html>
//show chrome
options := []chromedp.ExecAllocatorOption{
chromedp.Flag("headless", false),
chromedp.Flag("hide-scrollbars", false),
chromedp.Flag("mute-audio", false),
chromedp.UserAgent(`Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36`),
}
options = append(chromedp.DefaultExecAllocatorOptions[:], options...)
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)
defer cancel()
ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
var outer, url, frameurl string
var ok bool
if err := chromedp.Run(ctx,
chromedp.Navigate("http://localhost:9090/index"),
chromedp.Sleep(2*time.Second),
chromedp.WaitVisible("iframe", chromedp.ByQuery),
chromedp.AttributeValue("iframe", "src", &outer, &ok),
chromedp.Location(&url),
); err != nil {
panic(err)
}
fmt.Println("Outer:", outer, " url:", url)
frameurl = url[0:strings.LastIndex(url, "/")] + "/" + outer
fmt.Println("frameurl", frameurl)
if err := chromedp.Run(ctx,
chromedp.Navigate(frameurl),
chromedp.Sleep(2*time.Second),
chromedp.WaitVisible("#clickme"),
chromedp.Click("#clickme", chromedp.ByID),
chromedp.Sleep(2*time.Second),
); err != nil {
panic(err)
}
}
func writeHTML(content string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//utf-8
w.Header().Set("Content-Type", "text/html; charset=utf-8")
io.WriteString(w, strings.TrimSpace(content))
})
}
from chromedp.
@pu369 -- we're open to suggestions, however what you linked to is a Selenium instance using the WebElement API, which is fundamentally different than driving Chrome raw. If I remember correctly,
chromedriver
did a whole lot of hacks that I'd rather avoid doing withchromedp
. Please note, you can more-or-less do the above already (ie, load aiframe
via a xpath query, and then switch to it), you just need to do it in two parts.
Hi,
I'm interested in this. How do you indeed go about doing the "switch to it" part ? Loading an iframe via an xpath query is simple enough, but I'm getting confused as to how one would switch to it, creating a new child context and such.
Edit for clarity: previous answer navigates to the frame while parsing the url attribute. If I simply want to, say, click the first input element inside an iframe in the page, how would I make my current context the iframe's before I can click and simply jump back to parent context ?
from chromedp.
So the only way to do this is disable --disable-web-security and use evaluate? In my case iFrame is not loaded when disabling web security and it's not on the target list.
So is there some way to make new target from Frame ID for example?
from chromedp.
Err, I made a mistake in that code above; WaitVisible
doesn't seem to work, but WaitReady
does work. I'll investigate that separately. In any case, the node selectors work, which was the main blocker.
from chromedp.
Apologies all for the silence here. I realise this is a problem for many people and that the passing of time can be frustrating.
We recently added a bit of new API for #463, to enable running queries on a specific node instead of always running them on the document root node.
It just occurred to me that we could use the same API for iframes. That is, using the
FromNode
query option with an iframe node should then run the query on that iframe's document node.I've implemented that in the comment above, and it works. You can take a look at
TestQueryIframe
for a working example:Lines 278 to 286 in 49daeb6
The API for this is a bit clunky; it requires two calls to
chromedp.Run
, which I'm not very happy about. I'll be thinking about a nicer way to do this at a high level. Any API ideas welcome, but please keep it concise and provide a short example.
This is an great update, but some functions still not work with FromNode
,such as Click
, right?
from chromedp.
Yes, the basics work, but some of the extras like Click and WaitVisible do not - I assume some extra code to handle iframe coordinates is needed.
from chromedp.
in some iframe, None of the following methods worked, but I typed a JS expression in the browser's Console and it worked
chromedp.EvaluateAsDevTools
chromedp.Evaluate
runtime.Evaluate
from chromedp.
Now, what should I do? I can execute JavaScript in a specified context (for example, in an iframe).
from chromedp.
Related Issues (20)
- Navigate Hangup with custom url scheme HOT 1
- page.StopLoading() cannot stop navigate
- chrome failed to start with no detail error
- Screenshot from remote browser
- context canceled even with new context HOT 1
- Download events being omitted on the page level but chromedp listens for it on the Browser level HOT 1
- Image not showing up in header
- Can't use proxy and open multiple tabs ?
- Target.targetCrashed > errorCode 11 with chromedp.Navigate() in Docker container environnement HOT 7
- Is it possible to use the net/http client in chromedp ? HOT 1
- GetOuterHTML().WithPierce(true) not returning <iframe> contents
- How to execute JavaScript in a specified context? HOT 1
- How to set the state of ShadowDOM from closed to open?
- Question: condition for set FooterTemplate
- Is it possible to capture error messages related to CORS, CSP violations, mixed-content violations, etc.?
- Can I Capture Raw HTTP Data?
- How to start chrome in arm environment?Are there any other plans?
- Why can't I listen to my iframe's network requests
- How i get dpi of chrome
- Import cookie using json
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from chromedp.