How do I detect when TWebBrowser finishes downloading a page? - delphi

How to know if the TWebBrowser already finished to download the page?
My problem is: I can't know when my page was completely downloaded so it can be shown.
I request one page to my webbrowser and I want to show the response only when the page was completely downloaded.

You could try handling the OnDocumentComplete event.
If the site uses scripting to trigger downloading of additional data, you may have to employ more sophisticated methods since the event will fire before the page finishes running all its scripts. In general, the task begins to look like the halting problem. You might wish to refine your definition of "completely downloaded" to exclude certain difficult-to-detect cases.

source: http://www.delphifaq.com/faq/delphi/network/f264.shtml
Indeed, in case of multiple frames, OnDocumentComplete gets fired multiple times. Not every frame fires this event, but each frame that fires a DownloadBegin event will fire a corresponding DocumentComplete event.
How can the 'real completion' be recognized?
The OnDocumentComplete event sends parameter pDisp: IDispatch, which is the IDispatch of the frame (shdocvw) for which DocumentComplete is fired. The top-level frame fires the DocumentComplete in the end.
So, to check if a page is done downloading, you need to check if pDisp is same as the IDispatch of the WebBrowser control.
That's what the code below demonstrates:
procedure IForm1.WebBrowser1Documentccmplete(Sender: Iobject:
const pDisp: Inispatch; var URL: OLEvariant):
var
Curwebrowser : IWebBrowser:
IopWebBrowser: IWebBrowser:
Document : OLEvariant;
WindowName : string:
begin { TForm1.WebBrowser1DocumentComplete }
Curwebrowser := pDisp as IWebBrowser:
TopWebBrowser := (Sender as IWebBrowser).DefaultInterface;
if CurWebrowser=TopWebBrowser then
begin
ShowMessage('Document is complete.')
end
else
begin
Document := CurWebrowser.Document;
WindowName := Document.ParentWindow.Name:
ShowMessage('Frame ' + WindowName + ' is loaded.')
end:
end;

Related

Delphi: WebBrowser's OnDownloadComplete happens multiple times at once

For example in this code:
procedure TForm1.WebBrowser1DownloadComplete(Sender: TObject);
begin
ShowMessage('Download Completed');
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
WebBrowser1.Navigate('http://www.google.com/');
end;
"WebBrowser1DownloadComplete" message appears several times on 1 Navigate.
This is annoying and makes this event almost useless.
Why is this happening? How to avoid this?
Thankyou
Perhaps the OnNavigationComplete2 event handler is more suitable for you application.
Occurs immediately after the Web browser successfully navigates to a
new location.
Write an OnNavigateComplete2 event handler to take specific action when the Web browser successfully navigates to a new resource. The event can occur before the document is fully downloaded, but when it occurs at least part of the document must be received and a viewer for the document created.

How do I avoid the OnDocumentComplete event for embedded iframe elements?

I want to prevent iframe elements from triggering the OnDocumentComplete event every time. For example, a page has 4 iframes, and when I load this page, my OnDocumentComplete event runs 4 times. I want to run OnDocumentComplete just once for every page. How can I do that?
Maybe I could remove or block iframes in TWebBrowser control.
The event OnDocumentComplete is fired for each FRAME/IFRAME in the main document.
If you want to ignore them try this:
procedure TForm1.WebBrowser1DocumentComplete(Sender: TObject;
const pDisp: IDispatch; var URL: OleVariant);
begin
// check that the event is raised for the top-level browser (not frames or iframes)
if pDisp = TWebBrowser(Sender).ControlInterface then
begin
// do something nice...
end;
end;
From Delphi Docs:
Write an OnDocumentComplete event handler to take specific action when a frame or document is fully loaded into the Web browser. For a document without frames, this event occurs once when the document finishes loading. On a document containing multiple frames, this event occurs once for each frame. When the multiple-frame document finishes loading, the Web browser fires the event one final time.
Sender is the Web browser that is loading the document.
pDisp is the Automation interface of the top-level frame or browser.
When loading a document without frames, pDisp is the interface of the
Web browser. When loading a document with multiple frames, this is the
interface of the containing frame, except for the very last time the
event occurs, when it is the interface of the Web browser.

A way to redirect keypresses from TWebBrowser to the ParentForm

First question. Help format it, if needed, please.
Context
I have a TWebBrowser in the main form that is used to behave like it was a printer.
So I load some HTML text in it as user do some commands in the real printer...
I want the user to be able to click and select text from the WebBrowser.
Problem
When the user clicks in the WebBrowser some of the shortcuts registered from the actions don't work anymore. For example, there is an action with shortcut F7. If the user clicks in the WebBrowser and presses F7, it does not invoke my shortcut.
I know that this is by design of the WebBrowser.
So, I thought: I want to send every key combination back to the form.
The question is: How?
If it was another control, I could use a perform(WM_KeyDown,...) in the OnKeyDown event.
Alternatives or suggestions would be appreciated too. I am very tired these past 2 days, so I could be missing something.
Derivate TWebBrowser with an implementation of IDocHostUIHandler or use the famous EmbeddedWB
Implement the interface with an event OnTranslateAccelerator called in TranslateAccelerator
Set the event on your brwoser instance
Detect your key(s) like this:
function TBrowserPageIE.DoTranslateAccelerator(const lpMsg: PMSG; const pguidCmdGroup: PGUID; const nCmdID: DWORD): HRESULT;
begin
result := S_FALSE;
if lpMsg.message = WM_KEYDOWN then begin
if lpMsg.wParam = VK_F7 then begin
// do something here...
result := S_OK;
end;
end;
end;
An option that I tested and worked is to trap the key_code on HTML/javascript and then send that to the form using changing the document title. I will let it here hoping that help someone...
You will need add the javascript to trap the keys in the Header of the HTML page like this:
<script = ''javascript''>
function keypresed() {
var tecla=window.event.keyCode;
document.title = "Command"+tecla;
event.keyCode=0;
event.returnValue=false;
}
document.onkeydown=keypresed;
</script>
Then in Webbrowser you use the onTitleChangeEvent to use the key.
var
s:string;
begin
if Copy(Text,0,7) = 'Command' then
begin
//get the key...
s:= Copy(Text,8,Length(Text));
// if before the webbrowser get the focus edit1 was the focused control, you will need remove that focus first...
dummy.setfocus;
edit1.setfocus;
//perform keydown
keybd_event(StrToInt(s), 1,0,0)
end;
end;
Well, this can be used to perform any other custom command. :)

Detect TWebBrowser refresh event in Delphi 2009

I am using a TWebBrowser component which I use to load XML documents into which are linked to a XSL file.
I have a default page I display when no XML document is loaded. However, if the user deletes the XML file whilst it is open in the browser and then refreshes I get the standard resource could not be found error. What I would like to do instead is if the page cannot be loaded, check that the file exists, and if it doesn't just load the default page again.
I have tried using the OnNavigateError and OnBeforeNavigate2 events however they does not seem to trigger on a refresh.
Any idea's?
There is an onRefresh event which is exposed by the TWebBrowser replacement TEmbeddedWB. This version also exposes many other features which are otherwise hidden by the TWebBrowser component.
This is a bit of a cludge but it works in my tests, using the standard TWebBrowser component.
What I did was override the F5 key in the form's OnKeyUp event. By setting the form's KeyPreview property to True, you can call your own refresh. Seeing as the TWebBrowser.Refresh method doesn't appear to call any of the navigation events (as you said in your question), I call the TWebBrowser.Navigate event myself, which does fire the events.
You need to store the URL, which I imagine you're already doing. But if not, then in the BeforeNavigate2 event, you are given the URL as a parameter. So store this in a variable or on-screen control. Then, when F5 is pressed (or the Refresh button if you have put one on screen), simpy navigate to that URL again. The OnBeforeNavigate2, OnNavigateComplete2 and OnDocumentComplete events are all fired again, allowing you the opportunity to perform your test and put your placeholder page up instead of IEs default error page.
procedure TForm1.WebBrowser1BeforeNavigate2(Sender: TObject;
const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
Headers: OleVariant; var Cancel: WordBool);
begin
Edit1.Text := URL;
end;
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_F5) then
begin
WebBrowser1.Navigate(Edit1.Text);
end;
end;

How to attach an event to IHTMLDocument2 link elements in Delphi?

I'm using this code to get all the links from an IHTMLDocument2:
procedure DoDocumentComplete(const pDisp: IDispatch; var URL: OleVariant);
var
Document:IHTMLDocument2;
Body:IHTMLElement;
Links:IHTMLElementCollection;
i:integer;
tmp:IHTMLElement;
begin
try
Document := (pDisp as IWebbrowser2).Document AS IHTMLDocument2;
Body := Document.body;
Links := Document.links;
for i := 0 to (Links.length-1) do
begin
tmp := (Links.item(i, 0) as IHTMLElement);
//tmp.onclick := HOW SHOULD I ADD THE CALLBACK HERE?
//ShowMessage(tmp.innerText);
end;
except
on E : Exception do
ShowMessage(E.ClassName+' error raised, with message : '+E.Message);
end;
end;
How could I attach a function/procedure to .onclick to do a simple task like show an alert with the anchor text when the link is clicked?
One way is to sink events from the TWebBrowser with an object which implements IDispatch (like http://groups.google.com/group/borland.public.delphi.oleautomation/msg/a57d99e0e52c78ce)
you would set
tmp.onclick := TEventObject.Create(callbackProcedure) as IDispatch;
I wouldn't recommend using the onXXX-handlers (like onClick) directly as this will replace any previouly attached handler. This can actually change/destroy behavior of the page. If you are working with a web page which is not under your control you better use attachEvent:
(tmp as IHTMLElement2).attachEvent('onclick', callbackProcedureDisp);
And don't forget to detach with detachEvent:
(tmp as IHTMLElement2).detachEvent('onclick', callbackProcedureDisp);
Attention: it is possible to attach the same handler multiple times. In this case your handler would also be called multiple times.
If you are only interested in onclick you could just add one handler to the root element and don't have to travel through all elements. MSDN states the event bubbles, so you could just attach one event handler to the document element and check the srcElement member of the IHTMLEventObj every time the event fires.

Resources