{
* UHTMLEvents.pas
*
* Provides event sinks for the web browser control. Sinks for the browser's
* document and window objects are implemented.
*
* v1.0 of 18 Oct 2007 - Original version.
*
*
* ***** BEGIN LICENSE BLOCK *****
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
* the specific language governing rights and limitations under the License.
*
* The Original Code is UHTMLEvents.pas
*
* The Initial Developer of the Original Code is Peter Johnson
* (http://www.delphidabbler.com/).
*
* Portions created by the Initial Developer are Copyright (C) 2007 Peter
* Johnson. All Rights Reserved.
*
* Contributor(s): None
*
* ***** END LICENSE BLOCK *****
}
unit UHTMLEvents;
{$WARN UNSAFE_TYPE OFF}
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
// Delphi
MSHTML, ActiveX,
// Project
USimpleDispatch;
type
{
THTMLEventInfo:
Class that provides information about a HTML event.
}
THTMLEventInfo = class(TObject)
private
fCanCancel: Boolean;
{Value of CanCancel property}
fCancelled: Boolean;
{Value of Cancelled property}
fArgs: IHTMLEventObj;
{Value of Args property}
fDispatchId: Integer;
{Value of DispatchId property}
procedure SetCancelled(const Value: Boolean);
{Write accessor for Cancelled property. Sets property only if CanCancel
property is True.
@param Value [in] New property value.
}
function GetName: string;
{Read accessor for Name property.
@return Name of event.
}
public
constructor Create(const DispatchID: Integer;
const Args: IHTMLEventObj; const CanCancel: Boolean);
{Class constructor. Sets up object.
@param DispatchId [in] Dispatch ID of the event.
@param Args [in] Information about the event provided by browser.
@param CanCancel [in] Flag indicating whether event can be cancelled or
not.
}
property DispatchId: Integer read fDispatchId;
{Dispatch ID of the event}
property Name: string read GetName;
{Name of the event. (Same as Args.type_)}
property CanCancel: Boolean read fCanCancel;
{Flag indicating whether event can be cancelled. When true the Cancelled
property cannot be set}
property Cancelled: Boolean read fCancelled write SetCancelled
default False;
{Flag indicating whether the event should be cancelled. Set to true to
cancel the event. If CanCancel is false the value of Cancelled cannot be
changed and is always False}
property Args: IHTMLEventObj read fArgs;
{Object exposing information about the event provided the browser object}
end;
{
THTMLEvent:
Type of the OnEvent event.
@param Sender [in] Reference to object that triggered the event.
@param EventInfo [in] Object providing information about the event.
}
THTMLEvent = procedure(Sender: TObject; const EventInfo: THTMLEventInfo)
of object;
{
TAbstractHTMLEventSink:
Abstract base class for non reference counted HTML event sinks. Provides
core functionality and some helper methods.
}
TAbstractHTMLEventSink = class(TSimpleDispatch,
IUnknown, IDispatch
)
private
fSinkIID: TGUID;
{Supported event interface}
fSource: IDispatch;
{Reference to event source object}
fConnectionCookie: Integer;
{Unique value that identifies connection between event source and sink}
fOnEvent: THTMLEvent;
{Reference to any OnEvent handler}
protected
procedure DoInvoke(var InvokeInfo: TInvokeInfo); override;
{Dispatches events via abstract DispatchEvent method after checking for
valid context.
@param InvokeInfo [in/out] Information about method / property
invocation. Structure is updated with information event invocation.
}
procedure DispatchEvent(var InvokeInfo: TInvokeInfo); virtual; abstract;
{Abstract method called when a event is to be dispatched.
@param InvokeInfo [in/out] Information about event invocation. Structure
is to be updated with information result of invocation.
}
procedure GetEventArgParam(var InvokeInfo: TInvokeInfo;
out EventArgs: IHTMLEventObj);
{Gets the single IHTMLEventObj parameter of a "standard" HTML event.
Notifies caller of error if there is more than one parameter or if
parameter is wrong type.
@param InvokeInfo [in/out] Information about method invocation.
Structure is updated with information about any error.
@param EventArgs [out] Set to object giving information information
about event arguments or nil if parameter is not valid.
}
procedure DispatchStdEvent(var InvokeInfo: TInvokeInfo;
const CanCancel: Boolean);
{Triggers given "standard" event, passing information about the event.
Sets function result if event is a function. Notifies an error if method
call is not a valid standard event.
@param InvokeInfo [in/out] Information about event method invocation.
Structure is updated with information about return value and any error
condition.
@param CanCancel [in] Flag indicating whether event can be cancelled.
}
{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
{Checks the specified interface is supported by this object. If so
reference to interface is passed out. Supported interfaces are IUnknown,
IDispatch and interface implemented by event sink.
@param [in] IID specifies interface being queried.
@param [out] Reference to interface implementation or nil if not
supported.
@result S_OK if interface supported or E_NOINTERFACE if not supported.
}
public
constructor Create(const SinkIID: TGUID);
{Class constructor. Sets up the object.
@param SinkIID [in] Interface of events to be sunk by this object.
}
destructor Destroy; override;
{Class destructor. Disconnects the sink from the event source and tears
down the object.
}
procedure Connect(const Source: IDispatch);
{Connects the event sink to the object that is the source of the events.
@param Source [in] Reference to source object.
}
procedure Disconnect;
{Disconnects the event sink from the event source object.
}
property OnEvent: THTMLEvent read fOnEvent write fOnEvent;
{Event triggered when the browser notifies the sink of a "standard" event}
end;
const
// Event dispids from HTMLDocumentEvents2 dispinterface
cDocEventOnHelp = -2147418102;
cDocEventOnClick = -600;
cDocEventOnDblClick = -601;
cDocEventOnKeyDown = -602;
cDocEventOnKeyUp = -604;
cDocEventOnKeyPress = -603;
cDocEventOnMouseDown = -605;
cDocEventOnMouseMove = -606;
cDocEventOnMouseUp = -607;
cDocEventOnMouseOut = -2147418103;
cDocEventOnMouseOver = -2147418104;
cDocEventOnReadyStateChange = -609;
cDocEventOnBeforeUpdate = -2147418108;
cDocEventOnAfterUpdate = -2147418107;
cDocEventOnRowExit = -2147418106;
cDocEventOnRowEnter = -2147418105;
cDocEventOnDragStart = -2147418101;
cDocEventOnSelectStart = -2147418100;
cDocEventOnErrorUpdate = -2147418099;
cDocEventOnContextMenu = 1023;
cDocEventOnStop = 1026;
cDocEventOnRowsDelete = -2147418080;
cDocEventOnRowsInserted = -2147418079;
cDocEventOnCellChange = -2147418078;
cDocEventOnPropertyChange = -2147418093;
cDocEventOnDatasetChanged = -2147418098;
cDocEventOnDataAvailable = -2147418097;
cDocEventOnDatasetComplete = -2147418096;
cDocEventOnBeforeEditFocus = 1027;
type
{
THTMLDocEventSink:
Event sink for HTML document events. Sinks HTMLDocumentEvents2 events.
}
THTMLDocEventSink = class(TAbstractHTMLEventSink,
IUnknown, IDispatch
)
protected
procedure DispatchEvent(var InvokeInfo: TInvokeInfo); override;
{Dispatches HTMLDocumentEvents2 events.
@param InvokeInfo [in] Structure containing about event to be
dispatched. Fields of this structure are updated to notify result of
event invocation.
}
public
constructor Create;
{Class constructor. Sets up object to sink HTMLDocumentEvents2 events.
}
end;
const
// Event dispids from HTMLDocumentEvents2 dispinterface
cWdwEventOnLoad = 1003;
cWdwEventOnUnload = 1008;
cWdwEventOnHelp = -2147418102;
cWdwEventOnFocus = -2147418111;
cWdwEventOnBlur = -2147418112;
cWdwEventOnError = 1002;
cWdwEventOnResize = 1016;
cWdwEventOnScroll = 1014;
cWdwEventOnBeforeUnload = 1017;
cWdwEventOnBeforePrint = 1024;
cWdwEventOnAfterPrint = 1025;
type
{
THTMLWdwErrorEvent:
Type of the OnError event.
@param Sender [in] Reference to object that triggered the event.
@param Desc [in] Description of error.
@param URL [in] URL of document triggering error.
@param Line [in] Line number where error occurred.
}
THTMLWdwErrorEvent = procedure(Sender: TObject; const Desc, URL: string;
const Line: Integer) of object;
{
THTMLDocEventSink:
Event sink for HTML document events. Sinks HTMLDocumentEvents2 events.
}
THTMLWdwEventSink = class(TAbstractHTMLEventSink,
IUnknown, IDispatch
)
private
fOnError: THTMLWdwErrorEvent;
{Reference to any OnError event handler}
protected
procedure DispatchEvent(var InvokeInfo: TInvokeInfo); override;
{Dispatches HTMLWindowEvents2 events.
@param InvokeInfo [in] Structure containing about event to be
dispatched. Fields of this structure are updated to notify result of
event invocation.
}
public
constructor Create;
{Class constructor. Sets up object to sink HTMLWindowEvents2 events.
}
property OnError: THTMLWdwErrorEvent read fOnError write fOnError;
{Event triggered when browser's onerror event is invoked}
end;
implementation
uses
// Project
SysUtils, Variants, Windows, ComObj;
{ THTMLEventInfo }
constructor THTMLEventInfo.Create(const DispatchID: Integer;
const Args: IHTMLEventObj; const CanCancel: Boolean);
{Class constructor. Sets up object.
@param DispatchId [in] Dispatch ID of the event.
@param Args [in] Information about the event provided by browser.
@param CanCancel [in] Flag indicating whether event can be cancelled or not.
}
begin
inherited Create;
// Initialise properties
fDispatchId := DispatchID;
fArgs := Args;
fCanCancel := CanCancel;
fCancelled := False;
end;
function THTMLEventInfo.GetName: string;
{Read accessor for Name property.
@return Name of event.
}
begin
Result := fArgs.type_;
end;
procedure THTMLEventInfo.SetCancelled(const Value: Boolean);
{Write accessor for Cancelled property. Sets property only if CanCancel
property is True.
@param Value [in] New property value.
}
begin
if CanCancel then
fCancelled := Value;
end;
{ TAbstractHTMLEventSink }
procedure TAbstractHTMLEventSink.Connect(const Source: IDispatch);
{Connects the event sink to the object that is the source of the events.
@param Source [in] Reference to source object.
}
begin
Disconnect;
fSource := Source;
InterfaceConnect(fSource, fSinkIID, Self, fConnectionCookie);
end;
constructor TAbstractHTMLEventSink.Create(const SinkIID: TGUID);
{Class constructor. Sets up the object.
@param SinkIID [in] Interface of events to be sunk by this object.
}
begin
inherited Create;
fSinkIID := SinkIID;
end;
destructor TAbstractHTMLEventSink.Destroy;
{Class destructor. Disconnects the sink from the event source and tears down
the object.
}
begin
Disconnect;
inherited;
end;
procedure TAbstractHTMLEventSink.Disconnect;
{Disconnects the event sink from the event source object.
}
begin
if Assigned(fSource) then
begin
InterfaceDisconnect(fSource, fSinkIID, fConnectionCookie);
fSource := nil;
end;
end;
procedure TAbstractHTMLEventSink.DispatchStdEvent(var InvokeInfo: TInvokeInfo;
const CanCancel: Boolean);
{Triggers given "standard" event, passing information about the event. Sets
function result if event is a function. Notifies an error if method call is
not a valid standard event.
@param InvokeInfo [in/out] Information about event method invocation.
Structure is updated with information about return value and any error
condition.
@param CanCancel [in] Flag indicating whether event can be cancelled.
}
var
EventArgs: IHTMLEventObj; // object encapsulating event arguments
EventInfo: THTMLEventInfo; // object providing information about event
begin
// Get event arguments from method's parameter list: stores any error code in
// InvokeInfo
GetEventArgParam(InvokeInfo, EventArgs);
if not Succeeded(InvokeInfo.SCode) then
Exit;
// Create object to store info about the event
EventInfo := THTMLEventInfo.Create(
InvokeInfo.DispatchID, EventArgs, CanCancel
);
try
// Trigger the event
if Assigned(fOnEvent) then
fOnEvent(Self, EventInfo);
// Store whether event is cancelled as return value only if event is
// cancelable.
if EventInfo.CanCancel then
InvokeInfo.FnResult := not EventInfo.Cancelled
finally
FreeAndNil(EventInfo);
end;
end;
procedure TAbstractHTMLEventSink.DoInvoke(var InvokeInfo: TInvokeInfo);
{Dispatches events via abstract DispatchEvent method after checking for valid
context.
@param InvokeInfo [in/out] Information about method / property invocation.
Structure is updated with information event invocation.
}
begin
// We only accept method calls, not properties
if InvokeInfo.Flags and DISPATCH_METHOD = 0 then
raise Exception.Create( // ** do not localise
'TAbstractHTMLEventSink does not support properties'
);
// We don't handle named parameters
if InvokeInfo.Params.cNamedArgs > 0 then
raise Exception.Create( // ** do not localise
'TAbstractHTMLEventSink does not support named methods'
);
DispatchEvent(InvokeInfo);
end;
procedure TAbstractHTMLEventSink.GetEventArgParam(var InvokeInfo: TInvokeInfo;
out EventArgs: IHTMLEventObj);
{Gets the single IHTMLEventObj parameter of a "standard" HTML event. Notifies
caller of error if there is more than one parameter or if parameter is wrong
type.
@param InvokeInfo [in/out] Information about method invocation. Structure is
updated with information about any error.
@param EventArgs [out] Set to object giving information information about
event arguments or nil if parameter is not valid.
}
begin
EventArgs := nil;
if InvokeInfo.Params.cArgs = 1 then
// Get interface IHTMLEventObj interface from only param
// GetValidParam sets error info in InvokeInfo if param not valid
GetValidParam(InvokeInfo, 0, varDispatch, IHTMLEventObj, EventArgs)
else
// Incorrect number of params: set error code accordingly
InvokeInfo.SCode := DISP_E_BADPARAMCOUNT;
end;
function TAbstractHTMLEventSink.QueryInterface(const IID: TGUID;
out Obj): HResult;
{Checks the specified interface is supported by this object. If so reference
to interface is passed out. Supported interfaces are IUnknown, IDispatch and
interface implemented by event sink.
@param [in] IID specifies interface being queried.
@param [out] Reference to interface implementation or nil if not supported.
@return S_OK if interface supported or E_NOINTERFACE if not supported.
}
begin
Result := S_OK;
if GetInterface(IID, Obj) then
Exit;
if IsEqualGUID(fSinkIID, IID) and GetInterface(IDispatch, Obj) then
Exit;
Result := E_NOINTERFACE;
Pointer(Obj) := nil;
end;
{ THTMLDocEventSink }
constructor THTMLDocEventSink.Create;
{Class constructor. Sets up object to sink HTMLDocumentEvents2 events.
}
begin
inherited Create(HTMLDocumentEvents2);
end;
procedure THTMLDocEventSink.DispatchEvent(var InvokeInfo: TInvokeInfo);
{Dispatches HTMLDocumentEvents2 events.
@param InvokeInfo [in] Structure containing about event to be dispatched.
Fields of this structure are updated to notify result of event invocation.
}
begin
inherited;
// Dispatch events
case InvokeInfo.DispatchID of
cDocEventOnHelp: DispatchStdEvent(InvokeInfo, True);
cDocEventOnClick: DispatchStdEvent(InvokeInfo, True);
cDocEventOnDblClick: DispatchStdEvent(InvokeInfo, True);
cDocEventOnKeyDown: DispatchStdEvent(InvokeInfo, False);
cDocEventOnKeyUp: DispatchStdEvent(InvokeInfo, False);
cDocEventOnKeyPress: DispatchStdEvent(InvokeInfo, True);
cDocEventOnMouseDown: DispatchStdEvent(InvokeInfo, False);
cDocEventOnMouseMove: DispatchStdEvent(InvokeInfo, False);
cDocEventOnMouseUp: DispatchStdEvent(InvokeInfo, False);
cDocEventOnMouseOut: DispatchStdEvent(InvokeInfo, False);
cDocEventOnMouseOver: DispatchStdEvent(InvokeInfo, False);
cDocEventOnReadyStateChange: DispatchStdEvent(InvokeInfo, False);
cDocEventOnBeforeUpdate: DispatchStdEvent(InvokeInfo, True);
cDocEventOnAfterUpdate: DispatchStdEvent(InvokeInfo, False);
cDocEventOnRowExit: DispatchStdEvent(InvokeInfo, True);
cDocEventOnRowEnter: DispatchStdEvent(InvokeInfo, False);
cDocEventOnDragStart: DispatchStdEvent(InvokeInfo, True);
cDocEventOnSelectStart: DispatchStdEvent(InvokeInfo, True);
cDocEventOnErrorUpdate: DispatchStdEvent(InvokeInfo, True);
cDocEventOnContextMenu: DispatchStdEvent(InvokeInfo, True);
cDocEventOnStop: DispatchStdEvent(InvokeInfo, True);
cDocEventOnRowsDelete: DispatchStdEvent(InvokeInfo, False);
cDocEventOnRowsInserted: DispatchStdEvent(InvokeInfo, False);
cDocEventOnCellChange: DispatchStdEvent(InvokeInfo, False);
cDocEventOnPropertyChange: DispatchStdEvent(InvokeInfo, False);
cDocEventOnDatasetChanged: DispatchStdEvent(InvokeInfo, False);
cDocEventOnDataAvailable: DispatchStdEvent(InvokeInfo, False);
cDocEventOnDatasetComplete: DispatchStdEvent(InvokeInfo, False);
cDocEventOnBeforeEditFocus: DispatchStdEvent(InvokeInfo, False);
else
InvokeInfo.SCode := DISP_E_MEMBERNOTFOUND;
end;
end;
{ THTMLWdwEventSink }
constructor THTMLWdwEventSink.Create;
{Class constructor. Sets up object to sink HTMLWindowEvents2 events.
}
begin
inherited Create(HTMLWindowEvents2);
end;
procedure THTMLWdwEventSink.DispatchEvent(var InvokeInfo: TInvokeInfo);
{Dispatches HTMLWindowEvents2 events.
@param InvokeInfo [in] Structure containing about event to be dispatched.
Fields of this structure are updated to notify result of event invocation.
}
procedure DispatchErrorEvent;
{Dispatches the OnError event.
}
var
Description: WideString; // description of error
URL: WideString; // URL of document that encountered error
Line: Integer; // line number of error in document
V: OleVariant; // value of each validated parameter
begin
// NOTE: parameters are stored in reverse order in InvokeInfo.Params.rgvarg.
// check required number of params
if InvokeInfo.Params.cArgs <> 3 then
begin
InvokeInfo.SCode := DISP_E_BADPARAMCOUNT;
Exit;
end;
// validate 1st (description) param: must be string
if not GetValidParam(InvokeInfo, 2, varOleStr, V) then
Exit;
Description := V;
// validate 2nd (URL) param: must be string
if not GetValidParam(InvokeInfo, 1, varOleStr, V) then
Exit;
URL := V;
// validate 3rd (Line) param: must be integer
if not GetValidParam(InvokeInfo, 0, varInteger, V) then
Exit;
Line := V;
// Everything's OK: trigger any assigned event
if Assigned(fOnError) then
fOnError(Self, Description, URL, Line);
end;
begin
inherited;
// Dispatch events
case InvokeInfo.DispatchID of
cWdwEventOnLoad: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnUnload: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnHelp: DispatchStdEvent(InvokeInfo, True);
cWdwEventOnFocus: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnBlur: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnError: DispatchErrorEvent;
cWdwEventOnResize: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnScroll: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnBeforeUnload: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnBeforePrint: DispatchStdEvent(InvokeInfo, False);
cWdwEventOnAfterPrint: DispatchStdEvent(InvokeInfo, False);
else
InvokeInfo.SCode := DISP_E_MEMBERNOTFOUND;
end;
end;
end.