blob: 9999fb7fc518bf1c76d05e97354b4add086e340b [file] [log] [blame]
[email protected]f7817822009-09-24 05:11:581// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome_frame/chrome_frame_activex.h"
6
7#include <shdeprecated.h> // for IBrowserService2
8#include <wininet.h>
9
10#include <algorithm>
11
12#include "base/basictypes.h"
13#include "base/command_line.h"
14#include "base/file_util.h"
15#include "base/logging.h"
16#include "base/path_service.h"
17#include "base/process_util.h"
18#include "base/scoped_bstr_win.h"
19#include "base/string_util.h"
20#include "chrome/common/chrome_constants.h"
21#include "chrome/common/chrome_switches.h"
22#include "chrome/test/automation/tab_proxy.h"
23#include "googleurl/src/gurl.h"
24#include "chrome_frame/com_message_event.h"
25#include "chrome_frame/utils.h"
26
27ChromeFrameActivex::ChromeFrameActivex() {
28}
29
30HRESULT ChromeFrameActivex::FinalConstruct() {
31 HRESULT hr = Base::FinalConstruct();
32 if (FAILED(hr))
33 return hr;
34
35 // No need to call FireOnChanged at this point since nobody will be listening.
36 ready_state_ = READYSTATE_LOADING;
37 return S_OK;
38}
39
40ChromeFrameActivex::~ChromeFrameActivex() {
41 // We expect these to be released during a call to SetClientSite(NULL).
42 DCHECK(onmessage_.size() == 0);
43 DCHECK(onloaderror_.size() == 0);
44 DCHECK(onload_.size() == 0);
45 DCHECK(onreadystatechanged_.size() == 0);
46}
47
48LRESULT ChromeFrameActivex::OnCreate(UINT message, WPARAM wparam, LPARAM lparam,
49 BOOL& handled) {
50 Base::OnCreate(message, wparam, lparam, handled);
51 return 0;
52}
53
[email protected]f7817822009-09-24 05:11:5854HRESULT ChromeFrameActivex::GetContainingDocument(IHTMLDocument2** doc) {
55 ScopedComPtr<IOleContainer> container;
56 HRESULT hr = m_spClientSite->GetContainer(container.Receive());
57 if (container)
58 hr = container.QueryInterface(doc);
59 return hr;
60}
61
62HRESULT ChromeFrameActivex::GetDocumentWindow(IHTMLWindow2** window) {
63 ScopedComPtr<IHTMLDocument2> document;
64 HRESULT hr = GetContainingDocument(document.Receive());
65 if (document)
66 hr = document->get_parentWindow(window);
67 return hr;
68}
69
70void ChromeFrameActivex::OnLoad(int tab_handle, const GURL& gurl) {
71 ScopedComPtr<IDispatch> event;
72 std::string url = gurl.spec();
73 if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive())))
74 Fire_onload(event);
75
76 FireEvent(onload_, url);
77
78 HRESULT hr = InvokeScriptFunction(onload_handler_, url);
79
80 if (ready_state_ < READYSTATE_COMPLETE) {
81 ready_state_ = READYSTATE_COMPLETE;
82 FireOnChanged(DISPID_READYSTATE);
83 }
84}
85
86void ChromeFrameActivex::OnLoadFailed(int error_code, const std::string& url) {
87 ScopedComPtr<IDispatch> event;
88 if (SUCCEEDED(CreateDomEvent("event", url, "", event.Receive())))
89 Fire_onloaderror(event);
90
91 FireEvent(onloaderror_, url);
92
93 HRESULT hr = InvokeScriptFunction(onerror_handler_, url);
94}
95
96void ChromeFrameActivex::OnMessageFromChromeFrame(int tab_handle,
97 const std::string& message,
98 const std::string& origin,
99 const std::string& target) {
100 DLOG(INFO) << __FUNCTION__;
101
102 if (target.compare("*") != 0) {
103 bool drop = true;
104
105 if (is_privileged_) {
106 // Forward messages if the control is in privileged mode.
107 ScopedComPtr<IDispatch> message_event;
108 if (SUCCEEDED(CreateDomEvent("message", message, origin,
109 message_event.Receive()))) {
110 ScopedBstr target_bstr(UTF8ToWide(target).c_str());
111 Fire_onprivatemessage(message_event, target_bstr);
112
113 FireEvent(onprivatemessage_, message_event, target_bstr);
114 }
115 } else {
116 if (HaveSameOrigin(target, document_url_)) {
117 drop = false;
118 } else {
119 DLOG(WARNING) << "Dropping posted message since target doesn't match "
120 "the current document's origin. target=" << target;
121 }
122 }
123
124 if (drop)
125 return;
126 }
127
128 ScopedComPtr<IDispatch> message_event;
129 if (SUCCEEDED(CreateDomEvent("message", message, origin,
130 message_event.Receive()))) {
131 Fire_onmessage(message_event);
132
133 FireEvent(onmessage_, message_event);
134
135 ScopedVariant event_var;
136 event_var.Set(static_cast<IDispatch*>(message_event));
137 InvokeScriptFunction(onmessage_handler_, event_var.AsInput());
138 }
139}
140
141void ChromeFrameActivex::OnAutomationServerLaunchFailed(
142 AutomationLaunchResult reason, const std::string& server_version) {
143 Base::OnAutomationServerLaunchFailed(reason, server_version);
144
145 if (reason == AUTOMATION_VERSION_MISMATCH) {
146 DisplayVersionMismatchWarning(m_hWnd, server_version);
147 }
148}
149
150HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object,
151 const std::string& param) {
152 ScopedVariant script_arg(UTF8ToWide(param.c_str()).c_str());
153 return InvokeScriptFunction(script_object, script_arg.AsInput());
154}
155
156HRESULT ChromeFrameActivex::InvokeScriptFunction(const VARIANT& script_object,
157 VARIANT* param) {
158 if (V_VT(&script_object) != VT_DISPATCH) {
159 return S_FALSE;
160 }
161
162 CComPtr<IDispatch> script(script_object.pdispVal);
163 HRESULT hr = script.Invoke1(static_cast<DISPID>(DISPID_VALUE), param);
164 // 0x80020101 == SCRIPT_E_REPORTED.
165 // When the script we're invoking has an error, we get this error back.
166 DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101) << "Failed to invoke script";
167
168 return hr;
169}
170
171HRESULT ChromeFrameActivex::OnDraw(ATL_DRAWINFO& draw_info) { // NO_LINT
172 HRESULT hr = S_OK;
173 int dc_type = ::GetObjectType(draw_info.hicTargetDev);
174 if (dc_type == OBJ_ENHMETADC) {
175 RECT print_bounds = {0};
176 print_bounds.left = draw_info.prcBounds->left;
177 print_bounds.right = draw_info.prcBounds->right;
178 print_bounds.top = draw_info.prcBounds->top;
179 print_bounds.bottom = draw_info.prcBounds->bottom;
180
181 automation_client_->Print(draw_info.hdcDraw, print_bounds);
182 } else {
183 hr = Base::OnDraw(draw_info);
184 }
185
186 return hr;
187}
188
189STDMETHODIMP ChromeFrameActivex::Load(IPropertyBag* bag, IErrorLog* error_log) {
190 DCHECK(bag);
191
192 const wchar_t* event_props[] = {
193 (L"onload"),
194 (L"onloaderror"),
195 (L"onmessage"),
196 (L"onreadystatechanged"),
197 };
198
199 ScopedComPtr<IHTMLObjectElement> obj_element;
200 GetObjectElement(obj_element.Receive());
201
202 ScopedBstr object_id;
203 GetObjectScriptId(obj_element, object_id.Receive());
204
205 ScopedComPtr<IHTMLElement2> element;
206 element.QueryFrom(obj_element);
207 HRESULT hr = S_OK;
208
209 for (int i = 0; SUCCEEDED(hr) && i < arraysize(event_props); ++i) {
210 ScopedBstr prop(event_props[i]);
211 ScopedVariant value;
212 if (SUCCEEDED(bag->Read(prop, value.Receive(), error_log))) {
213 if (value.type() != VT_BSTR ||
214 FAILED(hr = CreateScriptBlockForEvent(element, object_id,
215 V_BSTR(&value), prop))) {
216 DLOG(ERROR) << "Failed to create script block for " << prop
217 << StringPrintf(L"hr=0x%08X, vt=%i", hr, value.type());
218 } else {
219 DLOG(INFO) << "script block created for event " << prop <<
220 StringPrintf(" (0x%08X)", hr) << " connections: " <<
221 ProxyDIChromeFrameEvents<ChromeFrameActivex>::m_vec.GetSize();
222 }
223 } else {
224 DLOG(INFO) << "event property " << prop << " not in property bag";
225 }
226 }
227
228 ScopedVariant src;
229 if (SUCCEEDED(bag->Read(StackBstr(L"src"), src.Receive(), error_log))) {
230 if (src.type() == VT_BSTR) {
231 hr = put_src(V_BSTR(&src));
232 DCHECK(hr != E_UNEXPECTED);
233 }
234 }
235
236 ScopedVariant use_chrome_network;
237 if (SUCCEEDED(bag->Read(StackBstr(L"useChromeNetwork"),
238 use_chrome_network.Receive(), error_log))) {
239 VariantChangeType(use_chrome_network.AsInput(),
240 use_chrome_network.AsInput(),
241 0, VT_BOOL);
242 if (use_chrome_network.type() == VT_BOOL) {
243 hr = put_useChromeNetwork(V_BOOL(&use_chrome_network));
244 DCHECK(hr != E_UNEXPECTED);
245 }
246 }
247
248 DLOG_IF(ERROR, FAILED(hr))
249 << StringPrintf("Failed to load property bag: 0x%08X", hr);
250
251 return hr;
252}
253
254const wchar_t g_activex_mixed_content_error[] = {
255 L"data:text/html,<html><body><b>ChromeFrame Security Error<br><br>"
256 L"Cannot navigate to HTTP url when document URL is HTTPS</body></html>"};
[email protected]2b8fd322009-10-02 00:00:59257
[email protected]f7817822009-09-24 05:11:58258STDMETHODIMP ChromeFrameActivex::put_src(BSTR src) {
259 GURL document_url(GetDocumentUrl());
260 if (document_url.SchemeIsSecure()) {
261 GURL source_url(src);
262 if (!source_url.SchemeIsSecure()) {
263 Base::put_src(ScopedBstr(g_activex_mixed_content_error));
264 return E_ACCESSDENIED;
265 }
266 }
267 return Base::put_src(src);
268}
269
270HRESULT ChromeFrameActivex::IOleObject_SetClientSite(
271 IOleClientSite* client_site) {
272 HRESULT hr = Base::IOleObject_SetClientSite(client_site);
273 if (FAILED(hr) || !client_site) {
274 EventHandlers* handlers[] = {
275 &onmessage_,
276 &onloaderror_,
277 &onload_,
278 &onreadystatechanged_,
279 };
280
281 for (int i = 0; i < arraysize(handlers); ++i)
282 handlers[i]->clear();
283
284 // Drop privileged mode on uninitialization.
285 is_privileged_ = false;
286 } else {
287 ScopedComPtr<IHTMLDocument2> document;
288 GetContainingDocument(document.Receive());
289 if (document) {
290 ScopedBstr url;
291 if (SUCCEEDED(document->get_URL(url.Receive())))
292 WideToUTF8(url, url.Length(), &document_url_);
293 }
294
295 // Probe to see whether the host implements the privileged service.
296 ScopedComPtr<IChromeFramePrivileged> service;
297 HRESULT service_hr = DoQueryService(SID_ChromeFramePrivileged, client_site,
298 service.Receive());
299 if (SUCCEEDED(service_hr) && service) {
300 // Does the host want privileged mode?
301 boolean wants_privileged = false;
302 service_hr = service->GetWantsPrivileged(&wants_privileged);
303
304 if (SUCCEEDED(service_hr) && wants_privileged)
305 is_privileged_ = true;
306 }
307
308 std::wstring chrome_extra_arguments;
309 std::wstring profile_name(GetHostProcessName(false));
310 if (is_privileged_) {
311 // Does the host want to provide extra arguments?
312 ScopedBstr extra_arguments_arg;
313 service_hr = service->GetChromeExtraArguments(
314 extra_arguments_arg.Receive());
315 if (S_OK == service_hr && extra_arguments_arg)
316 chrome_extra_arguments.assign(extra_arguments_arg,
317 extra_arguments_arg.Length());
318
319 ScopedBstr profile_name_arg;
320 service_hr = service->GetChromeProfileName(profile_name_arg.Receive());
321 if (S_OK == service_hr && profile_name_arg)
322 profile_name.assign(profile_name_arg, profile_name_arg.Length());
323 }
324
325 if (!InitializeAutomation(profile_name, chrome_extra_arguments,
326 IsIEInPrivate())) {
327 return E_FAIL;
328 }
329 }
330
331 return hr;
332}
333
334HRESULT ChromeFrameActivex::GetObjectScriptId(IHTMLObjectElement* object_elem,
335 BSTR* id) {
336 DCHECK(object_elem != NULL);
337 DCHECK(id != NULL);
338
339 HRESULT hr = E_FAIL;
340 if (object_elem) {
341 ScopedComPtr<IHTMLElement> elem;
342 hr = elem.QueryFrom(object_elem);
343 if (elem) {
344 hr = elem->get_id(id);
345 }
346 }
347
348 return hr;
349}
350
351HRESULT ChromeFrameActivex::GetObjectElement(IHTMLObjectElement** element) {
352 DCHECK(m_spClientSite);
353 if (!m_spClientSite)
354 return E_UNEXPECTED;
355
356 ScopedComPtr<IOleControlSite> site;
357 HRESULT hr = site.QueryFrom(m_spClientSite);
358 if (site) {
359 ScopedComPtr<IDispatch> disp;
360 hr = site->GetExtendedControl(disp.Receive());
361 if (disp) {
362 hr = disp.QueryInterface(element);
363 } else {
364 DCHECK(FAILED(hr));
365 }
366 }
367
368 return hr;
369}
370
371HRESULT ChromeFrameActivex::CreateScriptBlockForEvent(
372 IHTMLElement2* insert_after, BSTR instance_id, BSTR script,
373 BSTR event_name) {
374 DCHECK(insert_after);
375 DCHECK(::SysStringLen(event_name) > 0); // should always have this
376
377 // This might be 0 if not specified in the HTML document.
378 if (!::SysStringLen(instance_id)) {
379 // TODO(tommi): Should we give ourselves an ID if this happens?
380 NOTREACHED() << "Need to handle this";
381 return E_INVALIDARG;
382 }
383
384 ScopedComPtr<IHTMLDocument2> document;
385 HRESULT hr = GetContainingDocument(document.Receive());
386 if (SUCCEEDED(hr)) {
387 ScopedComPtr<IHTMLElement> element, new_element;
388 document->createElement(StackBstr(L"script"), element.Receive());
389 if (element) {
390 ScopedComPtr<IHTMLScriptElement> script_element;
391 if (SUCCEEDED(hr = script_element.QueryFrom(element))) {
392 script_element->put_htmlFor(instance_id);
393 script_element->put_event(event_name);
394 script_element->put_text(script);
395
396 hr = insert_after->insertAdjacentElement(StackBstr(L"afterEnd"),
397 element,
398 new_element.Receive());
399 }
400 }
401 }
402
403 return hr;
404}
405
406HRESULT ChromeFrameActivex::CreateDomEvent(const std::string& event_type,
407 const std::string& data,
408 const std::string& origin,
409 IDispatch** event) {
410 DCHECK(event_type.length() > 0);
411 DCHECK(event != NULL);
412
413 CComObject<ComMessageEvent>* ev = NULL;
414 HRESULT hr = CComObject<ComMessageEvent>::CreateInstance(&ev);
415 if (SUCCEEDED(hr)) {
416 ev->AddRef();
417
418 ScopedComPtr<IOleContainer> container;
419 m_spClientSite->GetContainer(container.Receive());
420 if (ev->Initialize(container, data, origin, event_type)) {
421 *event = ev;
422 } else {
423 NOTREACHED() << "event->Initialize";
424 ev->Release();
425 hr = E_UNEXPECTED;
426 }
427 }
428
429 return hr;
430}
431
432void ChromeFrameActivex::FireEvent(const EventHandlers& handlers,
433 const std::string& arg) {
434 if (handlers.size()) {
435 ScopedComPtr<IDispatch> event;
436 if (SUCCEEDED(CreateDomEvent("event", arg, "", event.Receive()))) {
437 FireEvent(handlers, event);
438 }
439 }
440}
441
442void ChromeFrameActivex::FireEvent(const EventHandlers& handlers,
443 IDispatch* event) {
444 DCHECK(event != NULL);
445 VARIANT arg = { VT_DISPATCH };
446 arg.pdispVal = event;
447 DISPPARAMS params = { &arg, NULL, 1, 0 };
448 for (EventHandlers::const_iterator it = handlers.begin();
449 it != handlers.end();
450 ++it) {
451 HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT,
452 DISPATCH_METHOD, &params, NULL, NULL, NULL);
453 // 0x80020101 == SCRIPT_E_REPORTED.
454 // When the script we're invoking has an error, we get this error back.
455 DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101)
456 << StringPrintf(L"Failed to invoke script: 0x%08X", hr);
457 }
458}
459
460void ChromeFrameActivex::FireEvent(const EventHandlers& handlers,
461 IDispatch* event, BSTR target) {
462 DCHECK(event != NULL);
463 // Arguments in reverse order to event handler function declaration,
464 // because that's what DISPPARAMS requires.
465 VARIANT args[2] = { { VT_BSTR }, { VT_DISPATCH }, };
466 args[0].bstrVal = target;
467 args[1].pdispVal = event;
468 DISPPARAMS params = { args, NULL, arraysize(args), 0 };
469 for (EventHandlers::const_iterator it = handlers.begin();
470 it != handlers.end();
471 ++it) {
472 HRESULT hr = (*it)->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT,
473 DISPATCH_METHOD, &params, NULL, NULL, NULL);
474 // 0x80020101 == SCRIPT_E_REPORTED.
475 // When the script we're invoking has an error, we get this error back.
476 DLOG_IF(ERROR, FAILED(hr) && hr != 0x80020101)
477 << StringPrintf(L"Failed to invoke script: 0x%08X", hr);
478 }
479}