blob: 202128b9947bcce7b1f66d82af52a52dfd8186d2 [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// Implementation of ChromeActiveDocument
6#include "chrome_frame/chrome_active_document.h"
7
8#include <hlink.h>
9#include <htiface.h>
[email protected]a1800e82009-11-19 00:53:2310#include <initguid.h>
[email protected]f7817822009-09-24 05:11:5811#include <mshtmcid.h>
12#include <shdeprecated.h>
13#include <shlguid.h>
14#include <shobjidl.h>
15#include <tlogstg.h>
16#include <urlmon.h>
17#include <wininet.h>
18
19#include "base/command_line.h"
20#include "base/file_util.h"
21#include "base/logging.h"
22#include "base/path_service.h"
23#include "base/process_util.h"
24#include "base/registry.h"
25#include "base/scoped_variant_win.h"
26#include "base/string_tokenizer.h"
27#include "base/string_util.h"
28#include "base/thread.h"
29#include "base/thread_local.h"
30
31#include "grit/generated_resources.h"
32#include "chrome/browser/tab_contents/tab_contents.h"
33#include "chrome/common/chrome_constants.h"
34#include "chrome/common/navigation_types.h"
35#include "chrome/test/automation/browser_proxy.h"
36#include "chrome/test/automation/tab_proxy.h"
[email protected]b36a9f92009-10-19 17:34:5737#include "chrome_frame/bho.h"
[email protected]f7817822009-09-24 05:11:5838#include "chrome_frame/utils.h"
39
40const wchar_t kChromeAttachExternalTabPrefix[] = L"attach_external_tab";
41
42static const wchar_t kUseChromeNetworking[] = L"UseChromeNetworking";
43static const wchar_t kHandleTopLevelRequests[] =
44 L"HandleTopLevelRequests";
45
[email protected]a1800e82009-11-19 00:53:2346DEFINE_GUID(CGID_DocHostCmdPriv, 0x000214D4L, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0,
47 0x46);
48
49
[email protected]f7817822009-09-24 05:11:5850base::ThreadLocalPointer<ChromeActiveDocument> g_active_doc_cache;
51
52bool g_first_launch_by_process_ = true;
53
54ChromeActiveDocument::ChromeActiveDocument()
55 : first_navigation_(true),
56 is_automation_client_reused_(false) {
[email protected]3eb07da2010-02-01 19:48:3657 url_fetcher_.set_frame_busting(false);
[email protected]a1800e82009-11-19 00:53:2358 memset(&navigation_info_, 0, sizeof(navigation_info_));
[email protected]f7817822009-09-24 05:11:5859}
60
61HRESULT ChromeActiveDocument::FinalConstruct() {
62 // If we have a cached ChromeActiveDocument instance in TLS, then grab
63 // ownership of the cached document's automation client. This is an
64 // optimization to get Chrome active documents to load faster.
65 ChromeActiveDocument* cached_document = g_active_doc_cache.Get();
66 if (cached_document) {
67 DCHECK(automation_client_.get() == NULL);
[email protected]b0febbf2009-11-12 17:49:3568 automation_client_ = cached_document->automation_client_.release();
[email protected]f7817822009-09-24 05:11:5869 DLOG(INFO) << "Reusing automation client instance from "
70 << cached_document;
71 DCHECK(automation_client_.get() != NULL);
[email protected]3eb07da2010-02-01 19:48:3672 automation_client_->Reinitialize(this, &url_fetcher_);
[email protected]f7817822009-09-24 05:11:5873 is_automation_client_reused_ = true;
74 } else {
75 // The FinalConstruct implementation in the ChromeFrameActivexBase class
76 // i.e. Base creates an instance of the ChromeFrameAutomationClient class
77 // and initializes it, which would spawn a new Chrome process, etc.
78 // We don't want to be doing this if we have a cached document, whose
79 // automation client instance can be reused.
80 HRESULT hr = Base::FinalConstruct();
81 if (FAILED(hr))
82 return hr;
83 }
84
85 bool chrome_network = GetConfigBool(false, kUseChromeNetworking);
86 bool top_level_requests = GetConfigBool(true, kHandleTopLevelRequests);
87 automation_client_->set_use_chrome_network(chrome_network);
88 automation_client_->set_handle_top_level_requests(top_level_requests);
89
90 find_dialog_.Init(automation_client_.get());
91
92 enabled_commands_map_[OLECMDID_PRINT] = true;
93 enabled_commands_map_[OLECMDID_FIND] = true;
94 enabled_commands_map_[OLECMDID_CUT] = true;
95 enabled_commands_map_[OLECMDID_COPY] = true;
96 enabled_commands_map_[OLECMDID_PASTE] = true;
97 enabled_commands_map_[OLECMDID_SELECTALL] = true;
98 return S_OK;
99}
100
101ChromeActiveDocument::~ChromeActiveDocument() {
102 DLOG(INFO) << __FUNCTION__;
103 if (find_dialog_.IsWindow()) {
104 find_dialog_.DestroyWindow();
105 }
[email protected]3eb07da2010-02-01 19:48:36106 // ChromeFramePlugin
107 Base::Uninitialize();
[email protected]f7817822009-09-24 05:11:58108}
109
110// Override DoVerb
111STDMETHODIMP ChromeActiveDocument::DoVerb(LONG verb,
112 LPMSG msg,
113 IOleClientSite* active_site,
114 LONG index,
115 HWND parent_window,
116 LPCRECT pos) {
117 // IE will try and in-place activate us in some cases. This happens when
118 // the user opens a new IE window with a URL that has us as the DocObject.
119 // Here we refuse to be activated in-place and we will force IE to UIActivate
120 // us.
121 if (OLEIVERB_INPLACEACTIVATE == verb) {
122 return E_NOTIMPL;
123 }
124 // Check if we should activate as a docobject or not
125 // (client supports IOleDocumentSite)
126 if (doc_site_) {
127 switch (verb) {
[email protected]1bb5f892009-10-06 01:44:57128 case OLEIVERB_SHOW: {
129 ScopedComPtr<IDocHostUIHandler> doc_host_handler;
130 doc_host_handler.QueryFrom(doc_site_);
131 if (doc_host_handler.get()) {
132 doc_host_handler->ShowUI(DOCHOSTUITYPE_BROWSE, this, this, NULL, NULL);
133 }
134 }
[email protected]f7817822009-09-24 05:11:58135 case OLEIVERB_OPEN:
136 case OLEIVERB_UIACTIVATE:
137 if (!m_bUIActive) {
138 return doc_site_->ActivateMe(NULL);
139 }
140 break;
141 }
142 }
143 return IOleObjectImpl<ChromeActiveDocument>::DoVerb(verb,
144 msg,
145 active_site,
146 index,
147 parent_window,
148 pos);
149}
150
[email protected]f7817822009-09-24 05:11:58151// Override IOleInPlaceActiveObjectImpl::OnDocWindowActivate
152STDMETHODIMP ChromeActiveDocument::OnDocWindowActivate(BOOL activate) {
153 DLOG(INFO) << __FUNCTION__;
154 return S_OK;
155}
156
157STDMETHODIMP ChromeActiveDocument::TranslateAccelerator(MSG* msg) {
158 DLOG(INFO) << __FUNCTION__;
159 if (msg == NULL)
160 return E_POINTER;
161
162 if (msg->message == WM_KEYDOWN && msg->wParam == VK_TAB) {
163 HWND focus = ::GetFocus();
164 if (focus != m_hWnd && !::IsChild(m_hWnd, focus)) {
165 // The call to SetFocus triggers a WM_SETFOCUS that makes the base class
166 // set focus to the correct element in Chrome.
167 ::SetFocus(m_hWnd);
168 return S_OK;
169 }
170 }
171
172 return S_FALSE;
173}
174// Override IPersistStorageImpl::IsDirty
175STDMETHODIMP ChromeActiveDocument::IsDirty() {
176 DLOG(INFO) << __FUNCTION__;
177 return S_FALSE;
178}
179
[email protected]b95f550f2009-11-19 05:35:22180void ChromeActiveDocument::OnAutomationServerReady() {
181 Base::OnAutomationServerReady();
182 Base::GiveFocusToChrome();
183}
184
[email protected]f7817822009-09-24 05:11:58185STDMETHODIMP ChromeActiveDocument::Load(BOOL fully_avalable,
186 IMoniker* moniker_name,
187 LPBC bind_context,
188 DWORD mode) {
189 if (NULL == moniker_name) {
190 return E_INVALIDARG;
191 }
[email protected]a1800e82009-11-19 00:53:23192
193 ScopedComPtr<IOleClientSite> client_site;
194 if (bind_context) {
195 ScopedComPtr<IUnknown> site;
196 bind_context->GetObjectParam(SZ_HTML_CLIENTSITE_OBJECTPARAM,
197 site.Receive());
198 if (site)
199 client_site.QueryFrom(site);
200 }
201
202 if (client_site) {
203 SetClientSite(client_site);
204 }
205
[email protected]7e3544b2010-01-22 00:02:34206 Bho* chrome_frame_bho = Bho::GetCurrentThreadBhoInstance();
207
208 // If the original URL contains an anchor, then the URL queried
209 // from the moniker does not contain the anchor. To workaround
210 // this we retrieve the URL from our BHO.
211 std::wstring url = GetActualUrlFromMoniker(
212 moniker_name, bind_context,
213 chrome_frame_bho ? chrome_frame_bho->url() : std::wstring());
[email protected]f7817822009-09-24 05:11:58214
[email protected]37346bc2009-09-25 22:54:33215 // The is_new_navigation variable indicates if this a navigation initiated
216 // by typing in a URL for e.g. in the IE address bar, or from Chrome by
217 // a window.open call from javascript, in which case the current IE tab
218 // will attach to an existing ExternalTabContainer instance.
[email protected]f7817822009-09-24 05:11:58219 bool is_new_navigation = true;
[email protected]37346bc2009-09-25 22:54:33220 bool is_chrome_protocol = false;
[email protected]f7817822009-09-24 05:11:58221
[email protected]37346bc2009-09-25 22:54:33222 if (!ParseUrl(url, &is_new_navigation, &is_chrome_protocol, &url)) {
223 DLOG(WARNING) << __FUNCTION__ << " Failed to parse url:" << url;
[email protected]f7817822009-09-24 05:11:58224 return E_INVALIDARG;
225 }
226
[email protected]37346bc2009-09-25 22:54:33227 if (!LaunchUrl(url, is_new_navigation)) {
228 NOTREACHED() << __FUNCTION__ << " Failed to launch url:" << url;
229 return E_INVALIDARG;
[email protected]f7817822009-09-24 05:11:58230 }
231
232 if (!is_chrome_protocol) {
[email protected]3eb07da2010-02-01 19:48:36233 url_fetcher_.UseMonikerForUrl(moniker_name, bind_context, url);
[email protected]f7817822009-09-24 05:11:58234 }
235
236 UMA_HISTOGRAM_CUSTOM_COUNTS("ChromeFrame.FullTabLaunchType",
237 is_chrome_protocol, 0, 1, 2);
238 return S_OK;
239}
240
241STDMETHODIMP ChromeActiveDocument::Save(IMoniker* moniker_name,
242 LPBC bind_context,
243 BOOL remember) {
244 return E_NOTIMPL;
245}
246
247STDMETHODIMP ChromeActiveDocument::SaveCompleted(IMoniker* moniker_name,
248 LPBC bind_context) {
249 return E_NOTIMPL;
250}
251
252STDMETHODIMP ChromeActiveDocument::GetCurMoniker(IMoniker** moniker_name) {
253 return E_NOTIMPL;
254}
255
256STDMETHODIMP ChromeActiveDocument::GetClassID(CLSID* class_id) {
257 if (NULL == class_id) {
258 return E_POINTER;
259 }
260 *class_id = GetObjectCLSID();
261 return S_OK;
262}
263
264STDMETHODIMP ChromeActiveDocument::QueryStatus(const GUID* cmd_group_guid,
265 ULONG number_of_commands,
266 OLECMD commands[],
267 OLECMDTEXT* command_text) {
268 DLOG(INFO) << __FUNCTION__;
269 for (ULONG command_index = 0; command_index < number_of_commands;
270 command_index++) {
271 DLOG(INFO) << "Command id = " << commands[command_index].cmdID;
272 if (enabled_commands_map_.find(commands[command_index].cmdID) !=
273 enabled_commands_map_.end()) {
274 commands[command_index].cmdf = OLECMDF_ENABLED;
275 }
276 }
277 return S_OK;
278}
279
280STDMETHODIMP ChromeActiveDocument::Exec(const GUID* cmd_group_guid,
281 DWORD command_id,
282 DWORD cmd_exec_opt,
283 VARIANT* in_args,
284 VARIANT* out_args) {
285 DLOG(INFO) << __FUNCTION__ << " Cmd id =" << command_id;
286 // Bail out if we have been uninitialized.
287 if (automation_client_.get() && automation_client_->tab()) {
288 return ProcessExecCommand(cmd_group_guid, command_id, cmd_exec_opt,
289 in_args, out_args);
290 }
[email protected]e3200932009-10-09 21:33:03291 return OLECMDERR_E_NOTSUPPORTED;
[email protected]f7817822009-09-24 05:11:58292}
293
[email protected]a1800e82009-11-19 00:53:23294STDMETHODIMP ChromeActiveDocument::LoadHistory(IStream* stream,
295 IBindCtx* bind_context) {
296 // Read notes in ChromeActiveDocument::SaveHistory
297 DCHECK(stream);
298 LARGE_INTEGER offset = {0};
299 ULARGE_INTEGER cur_pos = {0};
300 STATSTG statstg = {0};
301
302 stream->Seek(offset, STREAM_SEEK_CUR, &cur_pos);
303 stream->Stat(&statstg, STATFLAG_NONAME);
304
305 DWORD url_size = statstg.cbSize.LowPart - cur_pos.LowPart;
306 ScopedBstr url_bstr;
307 DWORD bytes_read = 0;
308 stream->Read(url_bstr.AllocateBytes(url_size), url_size, &bytes_read);
309 std::wstring url(url_bstr);
310
311 bool is_new_navigation = true;
312 bool is_chrome_protocol = false;
313
314 if (!ParseUrl(url, &is_new_navigation, &is_chrome_protocol, &url)) {
315 DLOG(WARNING) << __FUNCTION__ << " Failed to parse url:" << url;
316 return E_INVALIDARG;
317 }
318
319 if (!LaunchUrl(url, is_new_navigation)) {
320 NOTREACHED() << __FUNCTION__ << " Failed to launch url:" << url;
321 return E_INVALIDARG;
322 }
323 return S_OK;
324}
325
326STDMETHODIMP ChromeActiveDocument::SaveHistory(IStream* stream) {
327 // TODO(sanjeevr): We need to fetch the entire list of navigation entries
328 // from Chrome and persist it in the stream. And in LoadHistory we need to
329 // pass this list back to Chrome which will recreate the list. This will allow
330 // Back-Forward navigation to anchors to work correctly when we navigate to a
331 // page outside of ChromeFrame and then come back.
332 if (!stream) {
333 NOTREACHED();
334 return E_INVALIDARG;
335 }
336
337 LARGE_INTEGER offset = {0};
338 ULARGE_INTEGER new_pos = {0};
339 DWORD written = 0;
340 std::wstring url = UTF8ToWide(navigation_info_.url.spec());
341 return stream->Write(url.c_str(), (url.length() + 1) * sizeof(wchar_t),
342 &written);
343}
344
345STDMETHODIMP ChromeActiveDocument::SetPositionCookie(DWORD position_cookie) {
346 int index = static_cast<int>(position_cookie);
347 navigation_info_.navigation_index = index;
348 automation_client_->NavigateToIndex(index);
349 return S_OK;
350}
351
352STDMETHODIMP ChromeActiveDocument::GetPositionCookie(DWORD* position_cookie) {
353 if (!position_cookie)
354 return E_INVALIDARG;
355
356 *position_cookie = navigation_info_.navigation_index;
357 return S_OK;
358}
359
[email protected]f7817822009-09-24 05:11:58360STDMETHODIMP ChromeActiveDocument::GetUrlForEvents(BSTR* url) {
361 if (NULL == url) {
362 return E_POINTER;
363 }
364 *url = ::SysAllocString(url_);
365 return S_OK;
366}
367
[email protected]a1800e82009-11-19 00:53:23368STDMETHODIMP ChromeActiveDocument::GetAddressBarUrl(BSTR* url) {
369 return GetUrlForEvents(url);
370}
371
[email protected]f7817822009-09-24 05:11:58372HRESULT ChromeActiveDocument::IOleObject_SetClientSite(
373 IOleClientSite* client_site) {
374 if (client_site == NULL) {
375 ChromeActiveDocument* cached_document = g_active_doc_cache.Get();
376 if (cached_document) {
377 DCHECK(this == cached_document);
378 g_active_doc_cache.Set(NULL);
379 cached_document->Release();
380 }
[email protected]dda7d9c2009-11-11 23:01:47381
382 ScopedComPtr<IDocHostUIHandler> doc_host_handler;
383 doc_host_handler.QueryFrom(doc_site_);
384
385 if (doc_host_handler.get()) {
386 doc_host_handler->HideUI();
387 }
388
389 doc_site_.Release();
390 in_place_frame_.Release();
[email protected]f7817822009-09-24 05:11:58391 }
[email protected]a1800e82009-11-19 00:53:23392
393 if (client_site != m_spClientSite)
394 return Base::IOleObject_SetClientSite(client_site);
395
396 return S_OK;
[email protected]f7817822009-09-24 05:11:58397}
398
399
400HRESULT ChromeActiveDocument::ActiveXDocActivate(LONG verb) {
401 HRESULT hr = S_OK;
402 m_bNegotiatedWnd = TRUE;
403 if (!m_bInPlaceActive) {
404 hr = m_spInPlaceSite->CanInPlaceActivate();
405 if (FAILED(hr)) {
406 return hr;
407 }
408 m_spInPlaceSite->OnInPlaceActivate();
409 }
410 m_bInPlaceActive = TRUE;
411 // get location in the parent window,
412 // as well as some information about the parent
413 ScopedComPtr<IOleInPlaceUIWindow> in_place_ui_window;
414 frame_info_.cb = sizeof(OLEINPLACEFRAMEINFO);
415 HWND parent_window = NULL;
416 if (m_spInPlaceSite->GetWindow(&parent_window) == S_OK) {
417 in_place_frame_.Release();
418 RECT position_rect = {0};
419 RECT clip_rect = {0};
420 m_spInPlaceSite->GetWindowContext(in_place_frame_.Receive(),
421 in_place_ui_window.Receive(),
422 &position_rect,
423 &clip_rect,
424 &frame_info_);
425 if (!m_bWndLess) {
426 if (IsWindow()) {
427 ::ShowWindow(m_hWnd, SW_SHOW);
428 SetFocus();
429 } else {
430 m_hWnd = Create(parent_window, position_rect);
431 }
432 }
433 SetObjectRects(&position_rect, &clip_rect);
434 }
435
436 ScopedComPtr<IOleInPlaceActiveObject> in_place_active_object(this);
437
438 // Gone active by now, take care of UIACTIVATE
439 if (DoesVerbUIActivate(verb)) {
440 if (!m_bUIActive) {
441 m_bUIActive = TRUE;
442 hr = m_spInPlaceSite->OnUIActivate();
443 if (FAILED(hr)) {
444 return hr;
445 }
446 // set ourselves up in the host
447 if (in_place_active_object) {
448 if (in_place_frame_) {
449 in_place_frame_->SetActiveObject(in_place_active_object, NULL);
450 }
451 if (in_place_ui_window) {
452 in_place_ui_window->SetActiveObject(in_place_active_object, NULL);
453 }
454 }
455 }
456 }
457 m_spClientSite->ShowObject();
[email protected]f7817822009-09-24 05:11:58458 return S_OK;
459}
460
461void ChromeActiveDocument::OnNavigationStateChanged(int tab_handle, int flags,
462 const IPC::NavigationInfo& nav_info) {
463 // TODO(joshia): handle INVALIDATE_TAB,INVALIDATE_LOAD etc.
464 DLOG(INFO) << __FUNCTION__ << std::endl << " Flags: " << flags
465 << "Url: " << nav_info.url <<
466 ", Title: " << nav_info.title <<
467 ", Type: " << nav_info.navigation_type << ", Relative Offset: " <<
[email protected]62bb18dc12009-11-25 01:34:08468 nav_info.relative_offset << ", Index: " << nav_info.navigation_index;
[email protected]f7817822009-09-24 05:11:58469
470 UpdateNavigationState(nav_info);
471}
472
473void ChromeActiveDocument::OnUpdateTargetUrl(int tab_handle,
474 const std::wstring& new_target_url) {
475 if (in_place_frame_) {
476 in_place_frame_->SetStatusText(new_target_url.c_str());
477 }
478}
479
480bool IsFindAccelerator(const MSG& msg) {
481 // TODO(robertshield): This may not stand up to localization. Fix if this
482 // is the case.
483 return msg.message == WM_KEYDOWN && msg.wParam == 'F' &&
484 win_util::IsCtrlPressed() &&
485 !(win_util::IsAltPressed() || win_util::IsShiftPressed());
486}
487
488void ChromeActiveDocument::OnAcceleratorPressed(int tab_handle,
489 const MSG& accel_message) {
490 bool handled_accel = false;
491 if (in_place_frame_ != NULL) {
492 handled_accel = (S_OK == in_place_frame_->TranslateAcceleratorW(
493 const_cast<MSG*>(&accel_message), 0));
494 }
495
496 if (!handled_accel) {
497 if (IsFindAccelerator(accel_message)) {
498 // Handle the showing of the find dialog explicitly.
499 OnFindInPage();
[email protected]2b8fd322009-10-02 00:00:59500 } else {
501 Base::OnAcceleratorPressed(tab_handle, accel_message);
[email protected]f7817822009-09-24 05:11:58502 }
503 } else {
504 DLOG(INFO) << "IE handled accel key " << accel_message.wParam;
505 }
506}
507
508void ChromeActiveDocument::OnTabbedOut(int tab_handle, bool reverse) {
509 DLOG(INFO) << __FUNCTION__;
510 if (in_place_frame_) {
511 MSG msg = { NULL, WM_KEYDOWN, VK_TAB };
512 in_place_frame_->TranslateAcceleratorW(&msg, 0);
513 }
514}
515
516void ChromeActiveDocument::OnDidNavigate(int tab_handle,
517 const IPC::NavigationInfo& nav_info) {
518 DLOG(INFO) << __FUNCTION__ << std::endl << "Url: " << nav_info.url <<
519 ", Title: " << nav_info.title <<
520 ", Type: " << nav_info.navigation_type << ", Relative Offset: " <<
521 nav_info.relative_offset << ", Index: " << nav_info.navigation_index;
522
523 // This could be NULL if the active document instance is being destroyed.
524 if (!m_spInPlaceSite) {
525 DLOG(INFO) << __FUNCTION__ << "m_spInPlaceSite is NULL. Returning";
526 return;
527 }
528
529 UpdateNavigationState(nav_info);
530}
531
532void ChromeActiveDocument::UpdateNavigationState(
533 const IPC::NavigationInfo& new_navigation_info) {
[email protected]a1800e82009-11-19 00:53:23534 HRESULT hr = S_OK;
[email protected]f7817822009-09-24 05:11:58535 bool is_title_changed = (navigation_info_.title != new_navigation_info.title);
[email protected]f7817822009-09-24 05:11:58536 bool is_ssl_state_changed =
537 (navigation_info_.security_style != new_navigation_info.security_style) ||
538 (navigation_info_.has_mixed_content !=
539 new_navigation_info.has_mixed_content);
540
[email protected]f7817822009-09-24 05:11:58541 if (is_ssl_state_changed) {
542 int lock_status = SECURELOCK_SET_UNSECURE;
[email protected]a1800e82009-11-19 00:53:23543 switch (new_navigation_info.security_style) {
[email protected]f7817822009-09-24 05:11:58544 case SECURITY_STYLE_AUTHENTICATION_BROKEN:
545 lock_status = SECURELOCK_SET_SECUREUNKNOWNBIT;
546 break;
547 case SECURITY_STYLE_AUTHENTICATED:
[email protected]a1800e82009-11-19 00:53:23548 lock_status = new_navigation_info.has_mixed_content ?
[email protected]f7817822009-09-24 05:11:58549 SECURELOCK_SET_MIXED : SECURELOCK_SET_SECUREUNKNOWNBIT;
550 break;
551 default:
552 break;
553 }
554
555 ScopedVariant secure_lock_status(lock_status);
556 IEExec(&CGID_ShellDocView, INTERNAL_CMDID_SET_SSL_LOCK,
557 OLECMDEXECOPT_DODEFAULT, secure_lock_status.AsInput(), NULL);
558 }
559
[email protected]a1800e82009-11-19 00:53:23560 // Ideally all navigations should come to Chrome Frame so that we can call
561 // BeforeNavigate2 on installed BHOs and give them a chance to cancel the
562 // navigation. However, in practice what happens is as below:
563 // The very first navigation that happens in CF happens via a Load or a
564 // LoadHistory call. In this case, IE already has the correct information for
565 // its travel log as well address bar. For other internal navigations (navs
566 // that only happen within Chrome such as anchor navigations) we need to
567 // update IE's internal state after the fact. In the case of internal
568 // navigations, we notify the BHOs but ignore the should_cancel flag.
[email protected]6f526082010-01-28 19:36:58569
570 // Another case where we need to issue BeforeNavigate2 calls is as below:-
571 // We get notified after the fact, when navigations are initiated within
572 // Chrome via window.open calls. These navigations are handled by creating
573 // an external tab container within chrome and then connecting to it from IE.
574 // We still want to update the address bar/history, etc, to ensure that
575 // the special URL used by Chrome to indicate this is updated correctly.
576 bool is_internal_navigation = ((new_navigation_info.navigation_index > 0) &&
[email protected]a1800e82009-11-19 00:53:23577 (new_navigation_info.navigation_index !=
[email protected]6f526082010-01-28 19:36:58578 navigation_info_.navigation_index)) ||
579 StartsWith(static_cast<BSTR>(url_), kChromeAttachExternalTabPrefix,
580 false);
[email protected]f7817822009-09-24 05:11:58581
[email protected]a1800e82009-11-19 00:53:23582 if (new_navigation_info.url.is_valid()) {
583 url_.Allocate(UTF8ToWide(new_navigation_info.url.spec()).c_str());
584 }
585
586 if (is_internal_navigation) {
587 ScopedComPtr<IDocObjectService> doc_object_svc;
[email protected]f7817822009-09-24 05:11:58588 ScopedComPtr<IWebBrowserEventsService> web_browser_events_svc;
589 DoQueryService(__uuidof(web_browser_events_svc), m_spClientSite,
590 web_browser_events_svc.Receive());
[email protected]62bb18dc12009-11-25 01:34:08591 // web_browser_events_svc can be NULL on IE6.
[email protected]f7817822009-09-24 05:11:58592 if (web_browser_events_svc) {
[email protected]f7817822009-09-24 05:11:58593 VARIANT_BOOL should_cancel = VARIANT_FALSE;
594 web_browser_events_svc->FireBeforeNavigate2Event(&should_cancel);
[email protected]a1800e82009-11-19 00:53:23595 }
596
597 // We need to tell IE that we support navigation so that IE will query us
598 // for IPersistHistory and call GetPositionCookie to save our navigation
599 // index.
600 ScopedVariant html_window(static_cast<IUnknown*>(
601 static_cast<IHTMLWindow2*>(this)));
602 IEExec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0,
603 html_window.AsInput(), NULL);
604
605 // We pass the HLNF_INTERNALJUMP flag to INTERNAL_CMDID_FINALIZE_TRAVEL_LOG
606 // since we want to make IE treat all internal navigations within this page
607 // (including anchor navigations and subframe navigations) as anchor
608 // navigations. This will ensure that IE calls GetPositionCookie
609 // to save the current position cookie in the travel log and then call
610 // SetPositionCookie when the user hits Back/Forward to come back here.
611 ScopedVariant internal_navigation(HLNF_INTERNALJUMP);
612 IEExec(&CGID_Explorer, INTERNAL_CMDID_FINALIZE_TRAVEL_LOG, 0,
613 internal_navigation.AsInput(), NULL);
614
615 // We no longer need to lie to IE. If we lie persistently to IE, then
616 // IE reuses us for new navigations.
617 IEExec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0, NULL, NULL);
618
619 if (doc_object_svc) {
620 // Now call the FireNavigateCompleteEvent which makes IE update the text
621 // in the address-bar.
622 doc_object_svc->FireNavigateComplete2(this, 0);
623 doc_object_svc->FireDocumentComplete(this, 0);
624 } else if (web_browser_events_svc) {
[email protected]f7817822009-09-24 05:11:58625 web_browser_events_svc->FireNavigateComplete2Event();
[email protected]a1800e82009-11-19 00:53:23626 web_browser_events_svc->FireDocumentCompleteEvent();
[email protected]f7817822009-09-24 05:11:58627 }
628 }
[email protected]37346bc2009-09-25 22:54:33629
[email protected]a1800e82009-11-19 00:53:23630 if (is_title_changed) {
631 ScopedVariant title(new_navigation_info.title.c_str());
632 IEExec(NULL, OLECMDID_SETTITLE, OLECMDEXECOPT_DONTPROMPTUSER,
633 title.AsInput(), NULL);
634 }
635
636 // It is important that we only update the navigation_info_ after we have
637 // finalized the travel log. This is because IE will ask for information
638 // such as navigation index when the travel log is finalized and we need
639 // supply the old index and not the new one.
640 navigation_info_ = new_navigation_info;
[email protected]37346bc2009-09-25 22:54:33641 // Update the IE zone here. Ideally we would like to do it when the active
642 // document is activated. However that does not work at times as the frame we
643 // get there is not the actual frame which handles the command.
644 IEExec(&CGID_Explorer, SBCMDID_MIXEDZONE, 0, NULL, NULL);
[email protected]f7817822009-09-24 05:11:58645}
646
647void ChromeActiveDocument::OnFindInPage() {
648 TabProxy* tab = GetTabProxy();
649 if (tab) {
650 if (!find_dialog_.IsWindow()) {
651 find_dialog_.Create(m_hWnd);
652 }
653
654 find_dialog_.ShowWindow(SW_SHOW);
655 }
656}
657
658void ChromeActiveDocument::OnViewSource() {
659 DCHECK(navigation_info_.url.is_valid());
660 std::string url_to_open = "view-source:";
661 url_to_open += navigation_info_.url.spec();
[email protected]b36a9f92009-10-19 17:34:57662 OnOpenURL(0, GURL(url_to_open), GURL(), NEW_WINDOW);
[email protected]f7817822009-09-24 05:11:58663}
664
[email protected]37346bc2009-09-25 22:54:33665void ChromeActiveDocument::OnDetermineSecurityZone(const GUID* cmd_group_guid,
666 DWORD command_id,
667 DWORD cmd_exec_opt,
668 VARIANT* in_args,
669 VARIANT* out_args) {
670 if (out_args != NULL) {
671 out_args->vt = VT_UI4;
672 out_args->ulVal = URLZONE_INTERNET;
673 }
674}
675
[email protected]b36a9f92009-10-19 17:34:57676void ChromeActiveDocument::OnOpenURL(int tab_handle,
677 const GURL& url_to_open,
678 const GURL& referrer,
[email protected]f7817822009-09-24 05:11:58679 int open_disposition) {
680 // If the disposition indicates that we should be opening the URL in the
681 // current tab, then we can reuse the ChromeFrameAutomationClient instance
682 // maintained by the current ChromeActiveDocument instance. We cache this
683 // instance so that it can be used by the new ChromeActiveDocument instance
684 // which may be instantiated for handling the new URL.
685 if (open_disposition == CURRENT_TAB) {
686 // Grab a reference to ensure that the document remains valid.
687 AddRef();
688 g_active_doc_cache.Set(this);
689 }
690
[email protected]b36a9f92009-10-19 17:34:57691 Base::OnOpenURL(tab_handle, url_to_open, referrer, open_disposition);
[email protected]f7817822009-09-24 05:11:58692}
693
[email protected]f7817822009-09-24 05:11:58694bool ChromeActiveDocument::PreProcessContextMenu(HMENU menu) {
695 ScopedComPtr<IBrowserService> browser_service;
696 ScopedComPtr<ITravelLog> travel_log;
[email protected]a1800e82009-11-19 00:53:23697 GetBrowserServiceAndTravelLog(browser_service.Receive(),
698 travel_log.Receive());
699 if (!browser_service || !travel_log)
[email protected]f7817822009-09-24 05:11:58700 return true;
701
702 if (SUCCEEDED(travel_log->GetTravelEntry(browser_service, TLOG_BACK, NULL))) {
703 EnableMenuItem(menu, IDS_CONTENT_CONTEXT_BACK, MF_BYCOMMAND | MF_ENABLED);
704 } else {
705 EnableMenuItem(menu, IDS_CONTENT_CONTEXT_BACK, MF_BYCOMMAND | MFS_DISABLED);
706 }
707
[email protected]f7817822009-09-24 05:11:58708 if (SUCCEEDED(travel_log->GetTravelEntry(browser_service, TLOG_FORE, NULL))) {
709 EnableMenuItem(menu, IDS_CONTENT_CONTEXT_FORWARD,
710 MF_BYCOMMAND | MF_ENABLED);
711 } else {
712 EnableMenuItem(menu, IDS_CONTENT_CONTEXT_FORWARD,
713 MF_BYCOMMAND | MFS_DISABLED);
714 }
715
716 // Call base class (adds 'About' item)
717 return Base::PreProcessContextMenu(menu);
718}
719
[email protected]35f13ab2009-12-16 23:59:17720bool ChromeActiveDocument::HandleContextMenuCommand(UINT cmd,
721 const IPC::ContextMenuParams& params) {
[email protected]f7817822009-09-24 05:11:58722 ScopedComPtr<IWebBrowser2> web_browser2;
723 DoQueryService(SID_SWebBrowserApp, m_spClientSite, web_browser2.Receive());
724
725 switch (cmd) {
726 case IDS_CONTENT_CONTEXT_BACK:
727 web_browser2->GoBack();
728 break;
729
730 case IDS_CONTENT_CONTEXT_FORWARD:
731 web_browser2->GoForward();
732 break;
733
734 case IDS_CONTENT_CONTEXT_RELOAD:
735 web_browser2->Refresh();
736 break;
737
738 default:
[email protected]35f13ab2009-12-16 23:59:17739 return Base::HandleContextMenuCommand(cmd, params);
[email protected]f7817822009-09-24 05:11:58740 }
741
742 return true;
743}
744
745HRESULT ChromeActiveDocument::IEExec(const GUID* cmd_group_guid,
746 DWORD command_id, DWORD cmd_exec_opt,
747 VARIANT* in_args, VARIANT* out_args) {
748 HRESULT hr = E_FAIL;
[email protected]37346bc2009-09-25 22:54:33749
[email protected]f7817822009-09-24 05:11:58750 ScopedComPtr<IOleCommandTarget> frame_cmd_target;
[email protected]37346bc2009-09-25 22:54:33751
752 ScopedComPtr<IOleInPlaceSite> in_place_site(m_spInPlaceSite);
[email protected]6ebfda422009-11-12 23:30:54753 if (!in_place_site.get() && m_spClientSite != NULL) {
[email protected]37346bc2009-09-25 22:54:33754 in_place_site.QueryFrom(m_spClientSite);
755 }
756
757 if (in_place_site)
758 hr = frame_cmd_target.QueryFrom(in_place_site);
[email protected]f7817822009-09-24 05:11:58759
760 if (frame_cmd_target)
761 hr = frame_cmd_target->Exec(cmd_group_guid, command_id, cmd_exec_opt,
762 in_args, out_args);
763
764 return hr;
765}
[email protected]37346bc2009-09-25 22:54:33766
767bool ChromeActiveDocument::IsUrlZoneRestricted(const std::wstring& url) {
768 if (security_manager_.get() == NULL) {
769 HRESULT hr = CoCreateInstance(
770 CLSID_InternetSecurityManager,
771 NULL,
772 CLSCTX_ALL,
773 IID_IInternetSecurityManager,
774 reinterpret_cast<void**>(security_manager_.Receive()));
775
776 if (FAILED(hr)) {
777 NOTREACHED() << __FUNCTION__
778 << " Failed to create InternetSecurityManager. Error: 0x%x"
779 << hr;
780 return true;
781 }
782 }
783
784 DWORD zone = URLZONE_UNTRUSTED;
785 security_manager_->MapUrlToZone(url.c_str(), &zone, 0);
786 return zone == URLZONE_UNTRUSTED;
787}
788
789bool ChromeActiveDocument::ParseUrl(const std::wstring& url,
790 bool* is_new_navigation,
791 bool* is_chrome_protocol,
792 std::wstring* parsed_url) {
793 if (!is_new_navigation || !is_chrome_protocol|| !parsed_url) {
794 NOTREACHED() << __FUNCTION__ << " Invalid arguments";
795 return false;
796 }
797
798 std::wstring initial_url = url;
799
800 *is_chrome_protocol = StartsWith(initial_url, kChromeProtocolPrefix,
801 false);
802
803 *is_new_navigation = true;
804
805 if (*is_chrome_protocol) {
806 initial_url.erase(0, lstrlen(kChromeProtocolPrefix));
807 *is_new_navigation =
808 !StartsWith(initial_url, kChromeAttachExternalTabPrefix, false);
809 }
810
[email protected]10bf1a72009-10-01 16:00:21811 if (!IsValidUrlScheme(initial_url, is_privileged_)) {
[email protected]37346bc2009-09-25 22:54:33812 DLOG(WARNING) << __FUNCTION__ << " Disallowing navigation to url: "
813 << url;
814 return false;
815 }
816
817 if (IsUrlZoneRestricted(initial_url)) {
818 DLOG(WARNING) << __FUNCTION__
819 << " Disallowing navigation to restricted url: "
820 << initial_url;
821 return false;
822 }
823
824 *parsed_url = initial_url;
825 return true;
826}
827
828bool ChromeActiveDocument::LaunchUrl(const std::wstring& url,
829 bool is_new_navigation) {
[email protected]6f526082010-01-28 19:36:58830 url_.Reset(::SysAllocString(url.c_str()));
831
[email protected]37346bc2009-09-25 22:54:33832 if (!is_new_navigation) {
833 WStringTokenizer tokenizer(url, L"&");
834 // Skip over kChromeAttachExternalTabPrefix
835 tokenizer.GetNext();
836
837 intptr_t external_tab_cookie = 0;
838
839 if (tokenizer.GetNext())
840 StringToInt(tokenizer.token(),
841 reinterpret_cast<int*>(&external_tab_cookie));
842
843 if (external_tab_cookie == 0) {
844 NOTREACHED() << "invalid url for attach tab: " << url;
845 return false;
846 }
847
848 automation_client_->AttachExternalTab(external_tab_cookie);
849 } else {
850 // Initiate navigation before launching chrome so that the url will be
851 // cached and sent with launch settings.
[email protected]62bb18dc12009-11-25 01:34:08852 if (url_.Length()) {
853 std::string utf8_url;
854 WideToUTF8(url_, url_.Length(), &utf8_url);
[email protected]b36a9f92009-10-19 17:34:57855
[email protected]62bb18dc12009-11-25 01:34:08856 std::string referrer;
857 Bho* chrome_frame_bho = Bho::GetCurrentThreadBhoInstance();
858 if (chrome_frame_bho)
859 referrer = chrome_frame_bho->referrer();
[email protected]a1800e82009-11-19 00:53:23860
[email protected]62bb18dc12009-11-25 01:34:08861 if (!automation_client_->InitiateNavigation(utf8_url,
862 referrer,
863 is_privileged_)) {
864 DLOG(ERROR) << "Invalid URL: " << url;
865 Error(L"Invalid URL");
866 url_.Reset();
867 return false;
[email protected]37346bc2009-09-25 22:54:33868 }
[email protected]62bb18dc12009-11-25 01:34:08869
870 DLOG(INFO) << "Url is " << url_;
[email protected]37346bc2009-09-25 22:54:33871 }
872 }
873
[email protected]3eb07da2010-02-01 19:48:36874 if (is_automation_client_reused_)
875 return true;
[email protected]37346bc2009-09-25 22:54:33876
[email protected]3eb07da2010-02-01 19:48:36877 automation_client_->SetUrlFetcher(&url_fetcher_);
878
879 if (InitializeAutomation(GetHostProcessName(false), L"", IsIEInPrivate()))
880 return true;
881
882 return false;
[email protected]37346bc2009-09-25 22:54:33883}
[email protected]1bb5f892009-10-06 01:44:57884
885HRESULT ChromeActiveDocument::SetPageFontSize(const GUID* cmd_group_guid,
886 DWORD command_id,
887 DWORD cmd_exec_opt,
888 VARIANT* in_args,
889 VARIANT* out_args) {
890 if (!automation_client_.get()) {
891 NOTREACHED() << "Invalid automtion client";
892 return E_FAIL;
893 }
894
895 switch (command_id) {
896 case IDM_BASELINEFONT1:
897 automation_client_->SetPageFontSize(SMALLEST_FONT);
898 break;
899
900 case IDM_BASELINEFONT2:
901 automation_client_->SetPageFontSize(SMALL_FONT);
902 break;
903
904 case IDM_BASELINEFONT3:
905 automation_client_->SetPageFontSize(MEDIUM_FONT);
906 break;
907
908 case IDM_BASELINEFONT4:
909 automation_client_->SetPageFontSize(LARGE_FONT);
910 break;
911
912 case IDM_BASELINEFONT5:
913 automation_client_->SetPageFontSize(LARGEST_FONT);
914 break;
915
916 default:
917 NOTREACHED() << "Invalid font size command: "
918 << command_id;
919 return E_FAIL;
920 }
921
922 // Forward the command back to IEFrame with group set to
923 // CGID_ExplorerBarDoc. This is probably needed to update the menu state to
924 // indicate that the font size was set. This currently fails with error
925 // 0x80040104.
926 // TODO(iyengar)
927 // Do some investigation into why this Exec call fails.
928 IEExec(&CGID_ExplorerBarDoc, command_id, cmd_exec_opt, NULL, NULL);
929 return S_OK;
930}
931
[email protected]f9cc4c452009-10-13 14:56:38932void ChromeActiveDocument::OnGoToHistoryEntryOffset(int tab_handle,
933 int offset) {
[email protected]a1800e82009-11-19 00:53:23934 DLOG(INFO) << __FUNCTION__ << " - offset:" << offset;
935
[email protected]f9cc4c452009-10-13 14:56:38936 ScopedComPtr<IBrowserService> browser_service;
[email protected]a1800e82009-11-19 00:53:23937 ScopedComPtr<ITravelLog> travel_log;
938 GetBrowserServiceAndTravelLog(browser_service.Receive(),
939 travel_log.Receive());
940
941 if (browser_service && travel_log)
942 travel_log->Travel(browser_service, offset);
943}
944
945HRESULT ChromeActiveDocument::GetBrowserServiceAndTravelLog(
946 IBrowserService** browser_service, ITravelLog** travel_log) {
947 DCHECK(browser_service || travel_log);
948 ScopedComPtr<IBrowserService> browser_service_local;
949 HRESULT hr = DoQueryService(SID_SShellBrowser, m_spClientSite,
950 browser_service_local.Receive());
951 if (!browser_service_local) {
952 NOTREACHED() << "DoQueryService for IBrowserService failed: " << hr;
953 return hr;
[email protected]f9cc4c452009-10-13 14:56:38954 }
[email protected]a1800e82009-11-19 00:53:23955
956 if (travel_log) {
957 hr = browser_service_local->GetTravelLog(travel_log);
958 DLOG_IF(INFO, !travel_log) << "browser_service->GetTravelLog failed: "
959 << hr;
960 }
961
962 if (browser_service)
963 *browser_service = browser_service_local.Detach();
964
965 return hr;
[email protected]f9cc4c452009-10-13 14:56:38966}