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