Subversion Repositories fastphp

Rev

Rev 27 | Rev 45 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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