Subversion Repositories fastphp

Rev

Rev 25 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
8 daniel-mar 1
unit WebBrowserUtils;
2
 
3
interface
4
 
5
uses
6
  // In case ShDocVw_TLB can't be found, use ShDocVw
7
  Windows, ShDocVw_TLB, SysUtils, Forms;
8
 
9
procedure WaitForBrowser(AWebBrowser: TWebbrowser);
10
 
11
function WebBrowserLoadHTML(AWebBrowser: TWebBrowser; const AHTML: string;
12
                            const AFakeURL: string=''): boolean;
13
 
14
type
15
  TWebBrowserEx = class helper for TWebBrowser
16
  public
17
    procedure Clear;
18
    procedure Wait;
19
    function LoadHTML(const HTML: string; const AFakeURL: string=''): boolean;
20
  end;
21
 
22
implementation
23
 
24
uses
25
  ActiveX, urlmon;
26
 
27
type
28
  (*
29
  ILoadHTMLMoniker = interface(IMoniker)
30
    ['{DCAE3F41-9B38-40EB-B7D0-4AF0FBFBE5AB}']
31
    procedure InitLoader(sContent, sBaseUrl: string);
32
  end;
33
  *)
34
  TLoadHTMLMoniker = class (TInterfacedObject, IMoniker{, ILoadHTMLMoniker})
35
  private
36
    m_stream: IStream;
37
    m_sBaseName: string;
38
  public
39
    procedure InitLoader(sContent, sBaseUrl: string);
40
    {$REGION 'IMoniker members'}
41
    function BindToObject(const bc: IBindCtx; const mkToLeft: IMoniker;
42
      const iidResult: TIID; out vResult): HResult; stdcall;
43
    function BindToStorage(const bc: IBindCtx; const mkToLeft: IMoniker;
44
      const iid: TIID; out vObj): HResult; stdcall;
45
    function Reduce(const bc: IBindCtx; dwReduceHowFar: Longint;
46
      mkToLeft: PIMoniker; out mkReduced: IMoniker): HResult; stdcall;
47
    function ComposeWith(const mkRight: IMoniker; fOnlyIfNotGeneric: BOOL;
48
      out mkComposite: IMoniker): HResult; stdcall;
49
    function Enum(fForward: BOOL; out enumMoniker: IEnumMoniker): HResult;
50
      stdcall;
51
    function IsEqual(const mkOtherMoniker: IMoniker): HResult; stdcall;
52
    function Hash(out dwHash: Longint): HResult; stdcall;
53
    function IsRunning(const bc: IBindCtx; const mkToLeft: IMoniker;
54
      const mkNewlyRunning: IMoniker): HResult; stdcall;
55
    function GetTimeOfLastChange(const bc: IBindCtx; const mkToLeft: IMoniker;
56
      out filetime: TFileTime): HResult; stdcall;
57
    function Inverse(out mk: IMoniker): HResult; stdcall;
58
    function CommonPrefixWith(const mkOther: IMoniker;
59
      out mkPrefix: IMoniker): HResult; stdcall;
60
    function RelativePathTo(const mkOther: IMoniker;
61
      out mkRelPath: IMoniker): HResult; stdcall;
62
    function GetDisplayName(const bc: IBindCtx; const mkToLeft: IMoniker;
63
      out pszDisplayName: POleStr): HResult; stdcall;
64
    function ParseDisplayName(const bc: IBindCtx; const mkToLeft: IMoniker;
65
      pszDisplayName: POleStr; out chEaten: Longint;
66
      out mkOut: IMoniker): HResult; stdcall;
67
    function IsSystemMoniker(out dwMksys: Longint): HResult; stdcall;
68
    {$ENDREGION}
69
 
70
    {$REGION 'IPersistStream members'}
71
    function IsDirty: HResult; stdcall;
72
    function Load(const stm: IStream): HResult; stdcall;
73
    function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;
74
    function GetSizeMax(out cbSize: Largeint): HResult; stdcall;
75
    {$ENDREGION}
76
 
77
    {$REGION 'IPersist members'}
78
    function GetClassID(out classID: TCLSID): HResult; stdcall;
79
    {$ENDREGION}
80
  end;
81
 
82
 
83
// http://stackoverflow.com/questions/12605323/globalalloc-causes-my-delphi-app-hang
84
function StrToGlobalHandle(const aText: string): HGLOBAL;
85
var
86
  ptr: PChar;
87
begin
88
  Result := 0;
89
  if aText <> '' then
90
  begin
91
    Result := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT, (length(aText) + 1) * SizeOf(Char));
92
    if Result <> 0 then
93
    begin
94
      ptr := GlobalLock(Result);
95
      if Assigned(ptr) then
96
      begin
97
        StrCopy(ptr, PChar(aText));
98
        GlobalUnlock(Result);
99
      end
100
    end;
101
  end;
102
end;
103
 
104
procedure WaitForBrowser(AWebBrowser: TWebbrowser);
105
begin
106
  while (AWebBrowser.ReadyState <> READYSTATE_COMPLETE) and
107
        (not Assigned(Application) or not Application.Terminated) do
108
  begin
109
    if Assigned(Application) then Application.ProcessMessages;
110
    Sleep(50);
111
  end;
112
end;
113
 
114
function WebBrowserLoadHTML(AWebBrowser: TWebBrowser; const AHTML: string;
115
                            const AFakeURL: string=''): boolean;
116
var
117
  bindctx: IBindCtx;
118
  pPM: IPersistMoniker;
119
  loader: TLoadHTMLMoniker;
120
  url: string;
121
begin
122
  if AFakeURL <> '' then
123
    url := AFakeURL
124
  else if AWebBrowser.LocationURL <> '' then
125
    url := AWebBrowser.LocationURL
126
  else
127
    url := 'about:blank';
128
 
129
  if AWebBrowser.Document = nil then
130
  begin
131
    AWebBrowser.Navigate('about:blank');
132
    WaitForBrowser(AWebBrowser);
133
  end;
134
 
135
  pPM := AWebBrowser.Document as IPersistMoniker;
136
  if (pPM = nil) then Exit(false);
137
 
138
  bindctx := nil;
139
  CreateBindCtx(0, bindctx);
140
  if (bindctx = nil) then Exit(false);
141
 
142
  try
143
    loader := TLoadHTMLMoniker.Create;
144
    loader.InitLoader(AHTML, url);
145
  except
146
    Exit(false);
147
  end;
148
 
149
  result := pPM.Load(true, loader, bindctx, STGM_READ) = S_OK;
150
 
151
  if not result and Assigned(loader) then FreeAndNil(loader);
152
end;
153
 
154
{ TLoadHTMLMoniker }
155
 
156
// TLoadHTMLMoniker. Translated from C# to Delphi by Daniel Marschall
157
// Resources:
158
// - http://stackoverflow.com/questions/40927080/relative-urls-in-a-twebbrowser-containing-custom-html-code
159
// - https://github.com/kuza55/csexwb2/blob/master/General_Classes/LoadHTMLMoniker.cs
160
// - https://github.com/kuza55/csexwb2/blob/master/cEXWB.cs#L1769
161
 
162
procedure TLoadHTMLMoniker.InitLoader(sContent, sBaseUrl: string);
163
resourcestring
164
  SCannotAllocMemory = 'Cannot create IStream.';
165
var
166
  hr: integer;
167
begin
168
  m_sBaseName := sBaseUrl;
169
  hr := CreateStreamOnHGlobal(StrToGlobalHandle(sContent), true, m_stream);
170
  if ((hr <> S_OK) or (m_stream = nil)) then raise Exception.Create(SCannotAllocMemory);
171
end;
172
 
173
function TLoadHTMLMoniker.GetDisplayName(const bc: IBindCtx;
174
  const mkToLeft: IMoniker; out pszDisplayName: POleStr): HResult;
175
var
176
  bufSize: integer;
177
  wTest: WideString;
178
begin
179
//  pszDisplayName := PWideChar(WideString(m_sBaseName));
180
 
181
  // I am not sure if that is correct......
182
  bufSize := (Length(m_sBaseName)+1) * SizeOf(WideChar);
183
  pszDisplayName := CoTaskMemAlloc(bufSize);
184
  wTest := m_sBaseName;
185
  CopyMemory(pszDisplayName, PWideChar(wTest), bufSize);
186
 
187
  result := S_OK;
188
end;
189
 
190
function TLoadHTMLMoniker.BindToStorage(const bc: IBindCtx;
191
  const mkToLeft: IMoniker; const iid: TIID; out vObj): HResult;
192
const
193
  IID_IStream: TGUID = '{0000000C-0000-0000-C000-000000000046}';
194
begin
195
  if IsEqualIID(iid, IID_IStream) then
196
  begin
197
    IStream(vObj) := m_stream;
198
    result := S_OK;
199
  end
200
  else
201
  begin
202
    IStream(vObj) := nil;
203
    result := E_NOINTERFACE;
204
  end;
205
end;
206
 
207
{$REGION 'Not implemented'}
208
function TLoadHTMLMoniker.BindToObject(const bc: IBindCtx;
209
  const mkToLeft: IMoniker; const iidResult: TIID; out vResult): HResult;
210
begin
211
  result := E_NOTIMPL;
212
end;
213
 
214
function TLoadHTMLMoniker.CommonPrefixWith(const mkOther: IMoniker;
215
  out mkPrefix: IMoniker): HResult;
216
begin
217
  result := E_NOTIMPL;
218
end;
219
 
220
function TLoadHTMLMoniker.ComposeWith(const mkRight: IMoniker;
221
  fOnlyIfNotGeneric: BOOL; out mkComposite: IMoniker): HResult;
222
begin
223
  result := E_NOTIMPL;
224
end;
225
 
226
function TLoadHTMLMoniker.Enum(fForward: BOOL;
227
  out enumMoniker: IEnumMoniker): HResult;
228
begin
229
  result := E_NOTIMPL;
230
end;
231
 
232
function TLoadHTMLMoniker.GetClassID(out classID: TCLSID): HResult;
233
begin
234
  result := E_NOTIMPL;
235
end;
236
 
237
function TLoadHTMLMoniker.GetSizeMax(out cbSize: Largeint): HResult;
238
begin
239
  result := E_NOTIMPL;
240
end;
241
 
242
function TLoadHTMLMoniker.GetTimeOfLastChange(const bc: IBindCtx;
243
  const mkToLeft: IMoniker; out filetime: TFileTime): HResult;
244
begin
245
  result := E_NOTIMPL;
246
end;
247
 
248
function TLoadHTMLMoniker.Hash(out dwHash: Integer): HResult;
249
begin
250
  result := E_NOTIMPL;
251
end;
252
 
253
function TLoadHTMLMoniker.Inverse(out mk: IMoniker): HResult;
254
begin
255
  result := E_NOTIMPL;
256
end;
257
 
258
function TLoadHTMLMoniker.IsDirty: HResult;
259
begin
260
  result := E_NOTIMPL;
261
end;
262
 
263
function TLoadHTMLMoniker.IsEqual(const mkOtherMoniker: IMoniker): HResult;
264
begin
265
  result := E_NOTIMPL;
266
end;
267
 
268
function TLoadHTMLMoniker.IsRunning(const bc: IBindCtx; const mkToLeft,
269
  mkNewlyRunning: IMoniker): HResult;
270
begin
271
  result := E_NOTIMPL;
272
end;
273
 
274
function TLoadHTMLMoniker.IsSystemMoniker(out dwMksys: Integer): HResult;
275
begin
276
  result := E_NOTIMPL;
277
end;
278
 
279
function TLoadHTMLMoniker.Load(const stm: IStream): HResult;
280
begin
281
  result := E_NOTIMPL;
282
end;
283
 
284
function TLoadHTMLMoniker.ParseDisplayName(const bc: IBindCtx;
285
  const mkToLeft: IMoniker; pszDisplayName: POleStr; out chEaten: Integer;
286
  out mkOut: IMoniker): HResult;
287
begin
288
  result := E_NOTIMPL;
289
end;
290
 
291
function TLoadHTMLMoniker.Reduce(const bc: IBindCtx; dwReduceHowFar: Integer;
292
  mkToLeft: PIMoniker; out mkReduced: IMoniker): HResult;
293
begin
294
  result := E_NOTIMPL;
295
end;
296
 
297
function TLoadHTMLMoniker.RelativePathTo(const mkOther: IMoniker;
298
  out mkRelPath: IMoniker): HResult;
299
begin
300
  result := E_NOTIMPL;
301
end;
302
 
303
function TLoadHTMLMoniker.Save(const stm: IStream; fClearDirty: BOOL): HResult;
304
begin
305
  result := E_NOTIMPL;
306
end;
307
{$ENDREGION}
308
 
309
{ TWebBrowserEx }
310
 
311
procedure TWebBrowserEx.Wait;
312
begin
313
  WaitForBrowser(Self);
314
end;
315
 
316
function TWebBrowserEx.LoadHTML(const HTML: string; const AFakeURL: string=''): boolean;
317
begin
318
  result := WebBrowserLoadHTML(Self, HTML, AFakeURL);
319
  Self.Wait;
320
end;
321
 
322
procedure TWebBrowserEx.Clear;
323
begin
324
  Self.LoadHTML('', 'about:blank');
325
end;
326
 
327
end.