blob: 78f5941fe495400356a81e22b436df5697e70941 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/external_tab_container_win.h"
#include <string>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/debug/trace_event.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/debugger/devtools_toggle_action.h"
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/file_select_helper.h"
#include "chrome/browser/history/history_tab_helper.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/infobars/infobar_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/repost_form_warning_controller.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_creator.h"
#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "chrome/browser/ui/tab_modal_confirm_dialog.h"
#include "chrome/browser/ui/views/infobars/infobar_container_view.h"
#include "chrome/browser/ui/views/tab_contents/render_view_context_menu_win.h"
#include "chrome/common/automation_messages.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/load_notification_details.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_intents_dispatcher.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/frame_navigate_params.h"
#include "content/public/common/page_transition_types.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/common/ssl_status.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "third_party/WebKit/Source/Platform/chromium/public/WebReferrerPolicy.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/menu_model.h"
#include "ui/base/view_prop.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/layout/grid_layout.h"
#include "ui/views/win/hwnd_message_handler.h"
using content::BrowserThread;
using content::LoadNotificationDetails;
using content::NativeWebKeyboardEvent;
using content::NavigationController;
using content::NavigationEntry;
using content::OpenURLParams;
using content::RenderViewHost;
using content::SSLStatus;
using content::WebContents;
using ui::ViewProp;
using WebKit::WebCString;
using WebKit::WebReferrerPolicy;
using WebKit::WebSecurityPolicy;
using WebKit::WebString;
static const char kWindowObjectKey[] = "ChromeWindowObject";
namespace {
// Convert ui::MenuModel into a serializable form for Chrome Frame
ContextMenuModel* ConvertMenuModel(const ui::MenuModel* ui_model) {
ContextMenuModel* new_model = new ContextMenuModel;
const int index_base = ui_model->GetFirstItemIndex(NULL);
const int item_count = ui_model->GetItemCount();
new_model->items.reserve(item_count);
for (int i = 0; i < item_count; ++i) {
const int index = index_base + i;
if (ui_model->IsVisibleAt(index)) {
ContextMenuModel::Item item;
item.type = ui_model->GetTypeAt(index);
item.item_id = ui_model->GetCommandIdAt(index);
item.label = ui_model->GetLabelAt(index);
item.checked = ui_model->IsItemCheckedAt(index);
item.enabled = ui_model->IsEnabledAt(index);
if (item.type == ui::MenuModel::TYPE_SUBMENU)
item.submenu = ConvertMenuModel(ui_model->GetSubmenuModelAt(index));
new_model->items.push_back(item);
}
}
return new_model;
}
} // namespace
base::LazyInstance<ExternalTabContainerWin::PendingTabs>
ExternalTabContainerWin::pending_tabs_ = LAZY_INSTANCE_INITIALIZER;
ExternalTabContainerWin::ExternalTabContainerWin(
AutomationProvider* automation,
AutomationResourceMessageFilter* filter)
: views::NativeWidgetWin(new views::Widget),
automation_(automation),
tab_contents_container_(NULL),
tab_handle_(0),
ignore_next_load_notification_(false),
automation_resource_message_filter_(filter),
load_requests_via_automation_(false),
handle_top_level_requests_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
pending_(false),
focus_manager_(NULL),
external_tab_view_(NULL),
unload_reply_message_(NULL),
route_all_top_level_navigations_(false),
is_popup_window_(false) {
}
// static
scoped_refptr<ExternalTabContainer>
ExternalTabContainerWin::RemovePendingExternalTab(uintptr_t cookie) {
PendingTabs& pending_tabs = pending_tabs_.Get();
PendingTabs::iterator index = pending_tabs.find(cookie);
if (index != pending_tabs.end()) {
scoped_refptr<ExternalTabContainer> container = (*index).second;
pending_tabs.erase(index);
return container;
}
NOTREACHED() << "Failed to find ExternalTabContainer for cookie: "
<< cookie;
return NULL;
}
bool ExternalTabContainerWin::Init(Profile* profile,
HWND parent,
const gfx::Rect& bounds,
DWORD style,
bool load_requests_via_automation,
bool handle_top_level_requests,
TabContents* existing_contents,
const GURL& initial_url,
const GURL& referrer,
bool infobars_enabled,
bool route_all_top_level_navigations) {
if (IsWindow(GetNativeView())) {
NOTREACHED();
return false;
}
load_requests_via_automation_ = load_requests_via_automation;
handle_top_level_requests_ = handle_top_level_requests;
route_all_top_level_navigations_ = route_all_top_level_navigations;
GetMessageHandler()->set_window_style(WS_POPUP | WS_CLIPCHILDREN);
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
params.bounds = bounds;
params.native_widget = this;
GetWidget()->Init(params);
if (!IsWindow(GetNativeView())) {
NOTREACHED();
return false;
}
// TODO(jcampan): limit focus traversal to contents.
prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this));
if (existing_contents) {
tab_contents_.reset(existing_contents);
tab_contents_->web_contents()->GetController().SetBrowserContext(profile);
} else {
WebContents* new_contents = WebContents::Create(
profile, NULL, MSG_ROUTING_NONE, NULL);
tab_contents_.reset(TabContents::Factory::CreateTabContents(new_contents));
}
if (!infobars_enabled)
tab_contents_->infobar_tab_helper()->set_infobars_enabled(false);
tab_contents_->web_contents()->SetDelegate(this);
tab_contents_->web_contents()->
GetMutableRendererPrefs()->browser_handles_non_local_top_level_requests =
handle_top_level_requests;
if (!existing_contents) {
tab_contents_->web_contents()->GetRenderViewHost()->AllowBindings(
content::BINDINGS_POLICY_EXTERNAL_HOST);
}
NavigationController* controller =
&tab_contents_->web_contents()->GetController();
registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<NavigationController>(controller));
registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
content::Source<NavigationController>(controller));
registrar_.Add(this,
content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
content::Source<WebContents>(tab_contents_->web_contents()));
registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_DELETED,
content::NotificationService::AllSources());
registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CREATED,
content::NotificationService::AllSources());
content::WebContentsObserver::Observe(tab_contents_->web_contents());
// Start loading initial URL
if (!initial_url.is_empty()) {
// Navigate out of context since we don't have a 'tab_handle_' yet.
MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&ExternalTabContainerWin::Navigate,
weak_factory_.GetWeakPtr(),
initial_url, referrer));
}
// We need WS_POPUP to be on the window during initialization, but
// once initialized we apply the requested style which may or may not
// include the popup bit.
// Note that it's important to do this before we call SetParent since
// during the SetParent call we will otherwise get a WA_ACTIVATE call
// that causes us to steal the current focus.
SetWindowLong(
GetNativeView(), GWL_STYLE,
(GetWindowLong(GetNativeView(), GWL_STYLE) & ~WS_POPUP) | style);
// Now apply the parenting and style
if (parent)
SetParent(GetNativeView(), parent);
::ShowWindow(tab_contents_->web_contents()->GetNativeView(), SW_SHOWNA);
LoadAccelerators();
SetupExternalTabView();
BlockedContentTabHelper::FromWebContents(tab_contents_->web_contents())->
set_delegate(this);
return true;
}
void ExternalTabContainerWin::Uninitialize() {
registrar_.RemoveAll();
if (tab_contents_.get()) {
UnregisterRenderViewHost(
tab_contents_->web_contents()->GetRenderViewHost());
if (GetWidget()->GetRootView())
GetWidget()->GetRootView()->RemoveAllChildViews(true);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTERNAL_TAB_CLOSED,
content::Source<NavigationController>(
&tab_contents_->web_contents()->GetController()),
content::Details<ExternalTabContainer>(this));
tab_contents_.reset(NULL);
}
if (focus_manager_) {
focus_manager_->UnregisterAccelerators(this);
focus_manager_ = NULL;
}
external_tab_view_ = NULL;
request_context_ = NULL;
tab_contents_container_ = NULL;
}
bool ExternalTabContainerWin::Reinitialize(
AutomationProvider* automation_provider,
AutomationResourceMessageFilter* filter,
gfx::NativeWindow parent_window) {
if (!automation_provider || !filter) {
NOTREACHED();
return false;
}
automation_ = automation_provider;
automation_resource_message_filter_ = filter;
// Wait for the automation channel to be initialized before resuming pending
// render views and sending in the navigation state.
MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&ExternalTabContainerWin::OnReinitialize,
weak_factory_.GetWeakPtr()));
if (parent_window)
SetParent(GetNativeView(), parent_window);
return true;
}
WebContents* ExternalTabContainerWin::GetWebContents() const {
return tab_contents_.get() ? tab_contents_->web_contents() : NULL;
}
TabContents* ExternalTabContainerWin::GetTabContents() {
return tab_contents_.get();
}
gfx::NativeView ExternalTabContainerWin::GetExternalTabNativeView() const {
return GetNativeView();
}
void ExternalTabContainerWin::SetTabHandle(int handle) {
tab_handle_ = handle;
}
int ExternalTabContainerWin::GetTabHandle() const {
return tab_handle_;
}
bool ExternalTabContainerWin::ExecuteContextMenuCommand(int command) {
if (!external_context_menu_.get()) {
NOTREACHED();
return false;
}
switch (command) {
case IDS_CONTENT_CONTEXT_SAVEAUDIOAS:
case IDS_CONTENT_CONTEXT_SAVEVIDEOAS:
case IDS_CONTENT_CONTEXT_SAVEIMAGEAS:
case IDS_CONTENT_CONTEXT_SAVELINKAS: {
NOTREACHED(); // Should be handled in host.
break;
}
}
external_context_menu_->ExecuteCommand(command);
return true;
}
void ExternalTabContainerWin::RunUnloadHandlers(IPC::Message* reply_message) {
if (!automation_) {
delete reply_message;
return;
}
// If we have a pending unload message, then just respond back to this
// request and continue processing the previous unload message.
if (unload_reply_message_) {
AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true);
automation_->Send(reply_message);
return;
}
unload_reply_message_ = reply_message;
bool wait_for_unload_handlers =
tab_contents_.get() &&
Browser::RunUnloadEventsHelper(tab_contents_->web_contents());
if (!wait_for_unload_handlers) {
AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true);
automation_->Send(reply_message);
unload_reply_message_ = NULL;
}
}
void ExternalTabContainerWin::ProcessUnhandledAccelerator(const MSG& msg) {
NativeWebKeyboardEvent keyboard_event(msg);
unhandled_keyboard_event_handler_.HandleKeyboardEvent(keyboard_event,
focus_manager_);
}
void ExternalTabContainerWin::FocusThroughTabTraversal(
bool reverse,
bool restore_focus_to_view) {
DCHECK(tab_contents_.get());
if (tab_contents_.get())
tab_contents_->web_contents()->Focus();
// The tab_contents_ member can get destroyed in the context of the call to
// TabContentsViewViews::Focus() above. This method eventually calls SetFocus
// on the native window, which could end up dispatching messages like
// WM_DESTROY for the external tab.
if (tab_contents_.get() && restore_focus_to_view)
tab_contents_->web_contents()->FocusThroughTabTraversal(reverse);
}
// static
bool ExternalTabContainerWin::IsExternalTabContainer(HWND window) {
return ViewProp::GetValue(window, kWindowObjectKey) != NULL;
}
// static
ExternalTabContainer*
ExternalTabContainerWin::GetExternalContainerFromNativeWindow(
gfx::NativeView native_window) {
ExternalTabContainer* tab_container = NULL;
if (native_window) {
tab_container = reinterpret_cast<ExternalTabContainer*>(
ViewProp::GetValue(native_window, kWindowObjectKey));
}
return tab_container;
}
////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, content::WebContentsDelegate implementation:
WebContents* ExternalTabContainerWin::OpenURLFromTab(
WebContents* source,
const OpenURLParams& params) {
if (pending()) {
pending_open_url_requests_.push_back(params);
return NULL;
}
switch (params.disposition) {
case CURRENT_TAB:
case SINGLETON_TAB:
case NEW_FOREGROUND_TAB:
case NEW_BACKGROUND_TAB:
case NEW_POPUP:
case NEW_WINDOW:
case SAVE_TO_DISK:
if (automation_) {
GURL referrer = GURL(WebSecurityPolicy::generateReferrerHeader(
params.referrer.policy,
params.url,
WebString::fromUTF8(params.referrer.url.spec())).utf8());
automation_->Send(new AutomationMsg_OpenURL(tab_handle_,
params.url,
referrer,
params.disposition));
// TODO(ananta)
// We should populate other fields in the
// ViewHostMsg_FrameNavigate_Params structure. Another option could be
// to refactor the UpdateHistoryForNavigation function in WebContents.
content::FrameNavigateParams nav_params;
nav_params.referrer = content::Referrer(referrer,
params.referrer.policy);
nav_params.url = params.url;
nav_params.page_id = -1;
nav_params.transition = content::PAGE_TRANSITION_LINK;
content::LoadCommittedDetails details;
details.did_replace_entry = false;
const history::HistoryAddPageArgs& add_page_args =
tab_contents_->history_tab_helper()->
CreateHistoryAddPageArgs(params.url, details, nav_params);
tab_contents_->history_tab_helper()->
UpdateHistoryForNavigation(add_page_args);
return tab_contents_->web_contents();
}
break;
default:
NOTREACHED();
break;
}
return NULL;
}
void ExternalTabContainerWin::NavigationStateChanged(const WebContents* source,
unsigned changed_flags) {
if (automation_) {
NavigationInfo nav_info;
if (InitNavigationInfo(&nav_info, content::NAVIGATION_TYPE_NAV_IGNORE, 0))
automation_->Send(new AutomationMsg_NavigationStateChanged(
tab_handle_, changed_flags, nav_info));
}
}
void ExternalTabContainerWin::AddNewContents(WebContents* source,
WebContents* new_contents,
WindowOpenDisposition disposition,
const gfx::Rect& initial_pos,
bool user_gesture,
bool* was_blocked) {
if (!automation_) {
DCHECK(pending_);
LOG(ERROR) << "Invalid automation provider. Dropping new contents notify";
delete new_contents;
return;
}
scoped_refptr<ExternalTabContainerWin> new_container;
// If the host is a browser like IE8, then the URL being navigated to in the
// new tab contents could potentially navigate back to Chrome from a new
// IE process. We support full tab mode only for IE and hence we use that as
// a determining factor in whether the new ExternalTabContainer instance is
// created as pending or not.
if (!route_all_top_level_navigations_) {
new_container = new ExternalTabContainerWin(NULL, NULL);
} else {
// Reuse the same tab handle here as the new container instance is a dummy
// instance which does not have an automation client connected at the other
// end.
new_container = new TemporaryPopupExternalTabContainerWin(
automation_, automation_resource_message_filter_.get());
new_container->SetTabHandle(tab_handle_);
}
// Make sure that ExternalTabContainer instance is initialized with
// an unwrapped Profile.
scoped_ptr<TabContents> tab_contents(
TabContents::Factory::CreateTabContents(new_contents));
bool result = new_container->Init(
tab_contents->profile()->GetOriginalProfile(),
NULL,
initial_pos,
WS_CHILD,
load_requests_via_automation_,
handle_top_level_requests_,
tab_contents.get(),
GURL(),
GURL(),
true,
route_all_top_level_navigations_);
if (result) {
Profile* profile = tab_contents->profile();
tab_contents.release(); // Ownership has been transferred.
if (route_all_top_level_navigations_) {
return;
}
uintptr_t cookie = reinterpret_cast<uintptr_t>(new_container.get());
pending_tabs_.Get()[cookie] = new_container;
new_container->set_pending(true);
new_container->set_is_popup_window(disposition == NEW_POPUP);
AttachExternalTabParams attach_params_;
attach_params_.cookie = static_cast<uint64>(cookie);
attach_params_.dimensions = initial_pos;
attach_params_.user_gesture = user_gesture;
attach_params_.disposition = disposition;
attach_params_.profile_name = WideToUTF8(
profile->GetPath().DirName().BaseName().value());
automation_->Send(new AutomationMsg_AttachExternalTab(
tab_handle_, attach_params_));
} else {
NOTREACHED();
}
}
void ExternalTabContainerWin::WebContentsCreated(WebContents* source_contents,
int64 source_frame_id,
const GURL& target_url,
WebContents* new_contents) {
if (!load_requests_via_automation_)
return;
RenderViewHost* rvh = new_contents->GetRenderViewHost();
DCHECK(rvh != NULL);
// Register this render view as a pending render view, i.e. any network
// requests initiated by this render view would be serviced when the
// external host connects to the new external tab instance.
RegisterRenderViewHostForAutomation(rvh, true);
}
void ExternalTabContainerWin::CloseContents(content::WebContents* source) {
if (!automation_)
return;
if (unload_reply_message_) {
AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_,
true);
automation_->Send(unload_reply_message_);
unload_reply_message_ = NULL;
} else {
automation_->Send(new AutomationMsg_CloseExternalTab(tab_handle_));
}
}
void ExternalTabContainerWin::MoveContents(WebContents* source,
const gfx::Rect& pos) {
if (automation_ && is_popup_window_)
automation_->Send(new AutomationMsg_MoveWindow(tab_handle_, pos));
}
content::WebContents* ExternalTabContainerWin::GetConstrainingWebContents(
content::WebContents* source) {
return source;
}
ExternalTabContainerWin::~ExternalTabContainerWin() {
Uninitialize();
}
bool ExternalTabContainerWin::IsPopupOrPanel(const WebContents* source) const {
return is_popup_window_;
}
void ExternalTabContainerWin::UpdateTargetURL(WebContents* source,
int32 page_id,
const GURL& url) {
if (automation_) {
string16 url_string = CA2W(url.spec().c_str());
automation_->Send(
new AutomationMsg_UpdateTargetUrl(tab_handle_, url_string));
}
}
void ExternalTabContainerWin::ContentsZoomChange(bool zoom_in) {
}
bool ExternalTabContainerWin::TakeFocus(content::WebContents* source,
bool reverse) {
if (automation_) {
automation_->Send(new AutomationMsg_TabbedOut(tab_handle_,
base::win::IsShiftPressed()));
}
return true;
}
bool ExternalTabContainerWin::CanDownload(RenderViewHost* render_view_host,
int request_id,
const std::string& request_method) {
if (load_requests_via_automation_) {
if (automation_) {
// In case the host needs to show UI that needs to take the focus.
::AllowSetForegroundWindow(ASFW_ANY);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
base::IgnoreResult(
&AutomationResourceMessageFilter::SendDownloadRequestToHost),
automation_resource_message_filter_.get(), 0, tab_handle_,
request_id));
}
} else {
DLOG(WARNING) << "Downloads are only supported with host browser network "
"stack enabled.";
}
// Never allow downloads.
return false;
}
void ExternalTabContainerWin::RegisterRenderViewHostForAutomation(
RenderViewHost* render_view_host,
bool pending_view) {
if (render_view_host) {
AutomationResourceMessageFilter::RegisterRenderView(
render_view_host->GetProcess()->GetID(),
render_view_host->GetRoutingID(),
GetTabHandle(),
automation_resource_message_filter_,
pending_view);
}
}
void ExternalTabContainerWin::RegisterRenderViewHost(
RenderViewHost* render_view_host) {
// RenderViewHost instances that are to be associated with this
// ExternalTabContainer should share the same resource request automation
// settings.
RegisterRenderViewHostForAutomation(
render_view_host,
false); // Network requests should not be handled later.
}
void ExternalTabContainerWin::UnregisterRenderViewHost(
RenderViewHost* render_view_host) {
// Undo the resource automation registration performed in
// ExternalTabContainerWin::RegisterRenderViewHost.
if (render_view_host) {
AutomationResourceMessageFilter::UnRegisterRenderView(
render_view_host->GetProcess()->GetID(),
render_view_host->GetRoutingID());
}
}
content::JavaScriptDialogCreator*
ExternalTabContainerWin::GetJavaScriptDialogCreator() {
return GetJavaScriptDialogCreatorInstance();
}
bool ExternalTabContainerWin::HandleContextMenu(
const content::ContextMenuParams& params) {
if (!automation_) {
NOTREACHED();
return false;
}
external_context_menu_.reset(RenderViewContextMenuViews::Create(
web_contents(), params));
static_cast<RenderViewContextMenuWin*>(
external_context_menu_.get())->SetExternal();
external_context_menu_->Init();
external_context_menu_->UpdateMenuItemStates();
scoped_ptr<ContextMenuModel> context_menu_model(
ConvertMenuModel(&external_context_menu_->menu_model()));
POINT screen_pt = { params.x, params.y };
MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);
MiniContextMenuParams ipc_params;
ipc_params.screen_x = screen_pt.x;
ipc_params.screen_y = screen_pt.y;
ipc_params.link_url = params.link_url;
ipc_params.unfiltered_link_url = params.unfiltered_link_url;
ipc_params.src_url = params.src_url;
ipc_params.page_url = params.page_url;
ipc_params.keyword_url = params.keyword_url;
ipc_params.frame_url = params.frame_url;
bool rtl = base::i18n::IsRTL();
automation_->Send(
new AutomationMsg_ForwardContextMenuToExternalHost(tab_handle_,
*context_menu_model,
rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN, ipc_params));
return true;
}
bool ExternalTabContainerWin::PreHandleKeyboardEvent(
content::WebContents* source,
const NativeWebKeyboardEvent& event,
bool* is_keyboard_shortcut) {
return false;
}
void ExternalTabContainerWin::HandleKeyboardEvent(
content::WebContents* source,
const NativeWebKeyboardEvent& event) {
ProcessUnhandledKeyStroke(event.os_event.hwnd, event.os_event.message,
event.os_event.wParam, event.os_event.lParam);
}
void ExternalTabContainerWin::BeforeUnloadFired(WebContents* tab,
bool proceed,
bool* proceed_to_fire_unload) {
*proceed_to_fire_unload = true;
if (!automation_) {
delete unload_reply_message_;
unload_reply_message_ = NULL;
return;
}
if (!unload_reply_message_) {
NOTREACHED() << "**** NULL unload reply message pointer.";
return;
}
if (!proceed) {
AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_,
false);
automation_->Send(unload_reply_message_);
unload_reply_message_ = NULL;
*proceed_to_fire_unload = false;
}
}
void ExternalTabContainerWin::ShowRepostFormWarningDialog(WebContents* source) {
TabModalConfirmDialog::Create(new RepostFormWarningController(source),
TabContents::FromWebContents(source));
}
void ExternalTabContainerWin::RunFileChooser(
WebContents* tab,
const content::FileChooserParams& params) {
FileSelectHelper::RunFileChooser(tab, params);
}
void ExternalTabContainerWin::EnumerateDirectory(WebContents* tab,
int request_id,
const FilePath& path) {
FileSelectHelper::EnumerateDirectory(tab, request_id, path);
}
void ExternalTabContainerWin::JSOutOfMemory(WebContents* tab) {
Browser::JSOutOfMemoryHelper(tab);
}
void ExternalTabContainerWin::RegisterProtocolHandler(
WebContents* tab,
const std::string& protocol,
const GURL& url,
const string16& title,
bool user_gesture) {
Browser::RegisterProtocolHandlerHelper(tab, protocol, url, title,
user_gesture, NULL);
}
void ExternalTabContainerWin::RegisterIntentHandler(
WebContents* tab,
const webkit_glue::WebIntentServiceData& data,
bool user_gesture) {
Browser::RegisterIntentHandlerHelper(tab, data, user_gesture);
}
void ExternalTabContainerWin::WebIntentDispatch(
WebContents* tab,
content::WebIntentsDispatcher* intents_dispatcher) {
// TODO(binji) How do we want to display the WebIntentPicker bubble if there
// is no BrowserWindow?
delete intents_dispatcher;
}
void ExternalTabContainerWin::FindReply(WebContents* tab,
int request_id,
int number_of_matches,
const gfx::Rect& selection_rect,
int active_match_ordinal,
bool final_update) {
Browser::FindReplyHelper(tab, request_id, number_of_matches, selection_rect,
active_match_ordinal, final_update);
}
void ExternalTabContainerWin::RequestMediaAccessPermission(
content::WebContents* web_contents,
const content::MediaStreamRequest* request,
const content::MediaResponseCallback& callback) {
Browser::RequestMediaAccessPermissionHelper(web_contents, request, callback);
}
bool ExternalTabContainerWin::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(ExternalTabContainerWin, message)
IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ForwardMessageToExternalHost,
OnForwardMessageToExternalHost)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void ExternalTabContainerWin::DidFailProvisionalLoad(
int64 frame_id,
bool is_main_frame,
const GURL& validated_url,
int error_code,
const string16& error_description,
content::RenderViewHost* render_view_host) {
automation_->Send(new AutomationMsg_NavigationFailed(
tab_handle_, error_code, validated_url));
ignore_next_load_notification_ = true;
}
void ExternalTabContainerWin::OnForwardMessageToExternalHost(
const std::string& message,
const std::string& origin,
const std::string& target) {
if (automation_) {
automation_->Send(new AutomationMsg_ForwardMessageToExternalHost(
tab_handle_, message, origin, target));
}
}
////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, NotificationObserver implementation:
void ExternalTabContainerWin::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (!automation_)
return;
static const int kHttpClientErrorStart = 400;
static const int kHttpServerErrorEnd = 510;
switch (type) {
case content::NOTIFICATION_LOAD_STOP: {
const LoadNotificationDetails* load =
content::Details<LoadNotificationDetails>(details).ptr();
if (load && content::PageTransitionIsMainFrame(load->origin)) {
TRACE_EVENT_END_ETW("ExternalTabContainerWin::Navigate", 0,
load->url.spec());
automation_->Send(new AutomationMsg_TabLoaded(tab_handle_,
load->url));
}
break;
}
case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
if (ignore_next_load_notification_) {
ignore_next_load_notification_ = false;
return;
}
const content::LoadCommittedDetails* commit =
content::Details<content::LoadCommittedDetails>(details).ptr();
if (commit->http_status_code >= kHttpClientErrorStart &&
commit->http_status_code <= kHttpServerErrorEnd) {
automation_->Send(new AutomationMsg_NavigationFailed(
tab_handle_, commit->http_status_code, commit->entry->GetURL()));
ignore_next_load_notification_ = true;
} else {
NavigationInfo navigation_info;
// When the previous entry index is invalid, it will be -1, which
// will still make the computation come out right (navigating to the
// 0th entry will be +1).
if (InitNavigationInfo(&navigation_info, commit->type,
commit->previous_entry_index -
tab_contents_->web_contents()->
GetController().GetLastCommittedEntryIndex()))
automation_->Send(new AutomationMsg_DidNavigate(tab_handle_,
navigation_info));
}
break;
}
case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
if (load_requests_via_automation_) {
RenderViewHost* rvh = content::Details<RenderViewHost>(details).ptr();
RegisterRenderViewHostForAutomation(rvh, false);
}
break;
}
case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: {
if (load_requests_via_automation_) {
RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr();
UnregisterRenderViewHost(rvh);
}
break;
}
case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED: {
if (load_requests_via_automation_) {
RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr();
RegisterRenderViewHostForAutomation(rvh, false);
}
break;
}
default:
NOTREACHED();
}
}
////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::NativeWidgetWin overrides:
bool ExternalTabContainerWin::PreHandleMSG(UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT* result) {
if (message == WM_DESTROY) {
prop_.reset();
Uninitialize();
}
return false;
}
void ExternalTabContainerWin::PostHandleMSG(UINT message,
WPARAM w_param,
LPARAM l_param) {
// Grab a reference here which will be released in OnFinalMessage
if (message == WM_CREATE)
AddRef();
}
void ExternalTabContainerWin::OnFinalMessage(HWND window) {
GetWidget()->OnNativeWidgetDestroyed();
// Release the reference which we grabbed in WM_CREATE.
Release();
}
////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, private:
bool ExternalTabContainerWin::ProcessUnhandledKeyStroke(HWND window,
UINT message,
WPARAM wparam,
LPARAM lparam) {
if (!automation_) {
return false;
}
if ((wparam == VK_TAB) && !base::win::IsCtrlPressed()) {
// Tabs are handled separately (except if this is Ctrl-Tab or
// Ctrl-Shift-Tab)
return false;
}
// Send this keystroke to the external host as it could be processed as an
// accelerator there. If the host does not handle this accelerator, it will
// reflect the accelerator back to us via the ProcessUnhandledAccelerator
// method.
MSG msg = {0};
msg.hwnd = window;
msg.message = message;
msg.wParam = wparam;
msg.lParam = lparam;
automation_->Send(new AutomationMsg_HandleAccelerator(tab_handle_, msg));
return true;
}
bool ExternalTabContainerWin::InitNavigationInfo(
NavigationInfo* nav_info,
content::NavigationType nav_type,
int relative_offset) {
DCHECK(nav_info);
NavigationEntry* entry =
tab_contents_->web_contents()->GetController().GetActiveEntry();
// If this is very early in the game then we may not have an entry.
if (!entry)
return false;
nav_info->navigation_type = nav_type;
nav_info->relative_offset = relative_offset;
nav_info->navigation_index =
tab_contents_->web_contents()->GetController().GetCurrentEntryIndex();
nav_info->url = entry->GetURL();
nav_info->referrer = entry->GetReferrer().url;
nav_info->title = UTF16ToWideHack(entry->GetTitle());
if (nav_info->title.empty())
nav_info->title = UTF8ToWide(nav_info->url.spec());
nav_info->security_style = entry->GetSSL().security_style;
int content_status = entry->GetSSL().content_status;
nav_info->displayed_insecure_content =
!!(content_status & SSLStatus::DISPLAYED_INSECURE_CONTENT);
nav_info->ran_insecure_content =
!!(content_status & SSLStatus::RAN_INSECURE_CONTENT);
return true;
}
SkColor ExternalTabContainerWin::GetInfoBarSeparatorColor() const {
return ThemeService::GetDefaultColor(ThemeService::COLOR_TOOLBAR_SEPARATOR);
}
void ExternalTabContainerWin::InfoBarContainerStateChanged(bool is_animating) {
if (external_tab_view_)
external_tab_view_->Layout();
}
bool ExternalTabContainerWin::DrawInfoBarArrows(int* x) const {
return false;
}
bool ExternalTabContainerWin::AcceleratorPressed(
const ui::Accelerator& accelerator) {
std::map<ui::Accelerator, int>::const_iterator iter =
accelerator_table_.find(accelerator);
DCHECK(iter != accelerator_table_.end());
if (!tab_contents_.get() ||
!tab_contents_->web_contents()->GetRenderViewHost()) {
NOTREACHED();
return false;
}
RenderViewHost* host = tab_contents_->web_contents()->GetRenderViewHost();
int command_id = iter->second;
switch (command_id) {
case IDC_ZOOM_PLUS:
host->Zoom(content::PAGE_ZOOM_IN);
break;
case IDC_ZOOM_NORMAL:
host->Zoom(content::PAGE_ZOOM_RESET);
break;
case IDC_ZOOM_MINUS:
host->Zoom(content::PAGE_ZOOM_OUT);
break;
case IDC_DEV_TOOLS:
DevToolsWindow::ToggleDevToolsWindow(
tab_contents_->web_contents()->GetRenderViewHost(),
false,
DEVTOOLS_TOGGLE_ACTION_SHOW);
break;
case IDC_DEV_TOOLS_CONSOLE:
DevToolsWindow::ToggleDevToolsWindow(
tab_contents_->web_contents()->GetRenderViewHost(),
false,
DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE);
break;
case IDC_DEV_TOOLS_INSPECT:
DevToolsWindow::ToggleDevToolsWindow(
tab_contents_->web_contents()->GetRenderViewHost(),
false,
DEVTOOLS_TOGGLE_ACTION_INSPECT);
break;
case IDC_DEV_TOOLS_TOGGLE:
DevToolsWindow::ToggleDevToolsWindow(
tab_contents_->web_contents()->GetRenderViewHost(),
false,
DEVTOOLS_TOGGLE_ACTION_TOGGLE);
break;
default:
NOTREACHED() << "Unsupported accelerator: " << command_id;
return false;
}
return true;
}
bool ExternalTabContainerWin::CanHandleAccelerators() const {
return true;
}
void ExternalTabContainerWin::Navigate(const GURL& url, const GURL& referrer) {
if (!tab_contents_.get()) {
NOTREACHED();
return;
}
TRACE_EVENT_BEGIN_ETW("ExternalTabContainerWin::Navigate", 0, url.spec());
tab_contents_->web_contents()->GetController().LoadURL(
url, content::Referrer(referrer, WebKit::WebReferrerPolicyDefault),
content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
}
bool ExternalTabContainerWin::OnGoToEntryOffset(int offset) {
if (load_requests_via_automation_) {
automation_->Send(new AutomationMsg_RequestGoToHistoryEntryOffset(
tab_handle_, offset));
return false;
}
return true;
}
void ExternalTabContainerWin::LoadAccelerators() {
HACCEL accelerator_table = AtlLoadAccelerators(IDR_CHROMEFRAME);
DCHECK(accelerator_table);
// We have to copy the table to access its contents.
int count = CopyAcceleratorTable(accelerator_table, 0, 0);
if (count == 0) {
// Nothing to do in that case.
return;
}
scoped_array<ACCEL> scoped_accelerators(new ACCEL[count]);
ACCEL* accelerators = scoped_accelerators.get();
DCHECK(accelerators != NULL);
CopyAcceleratorTable(accelerator_table, accelerators, count);
focus_manager_ = GetWidget()->GetFocusManager();
DCHECK(focus_manager_);
// Let's fill our own accelerator table.
for (int i = 0; i < count; ++i) {
ui::Accelerator accelerator(
static_cast<ui::KeyboardCode>(accelerators[i].key),
ui::GetModifiersFromACCEL(accelerators[i]));
accelerator_table_[accelerator] = accelerators[i].cmd;
// Also register with the focus manager.
if (focus_manager_) {
focus_manager_->RegisterAccelerator(
accelerator, ui::AcceleratorManager::kNormalPriority, this);
}
}
}
void ExternalTabContainerWin::OnReinitialize() {
if (load_requests_via_automation_) {
RenderViewHost* rvh = tab_contents_->web_contents()->GetRenderViewHost();
if (rvh) {
AutomationResourceMessageFilter::ResumePendingRenderView(
rvh->GetProcess()->GetID(), rvh->GetRoutingID(),
tab_handle_, automation_resource_message_filter_);
}
}
NavigationStateChanged(web_contents(), 0);
ServicePendingOpenURLRequests();
}
void ExternalTabContainerWin::ServicePendingOpenURLRequests() {
DCHECK(pending());
set_pending(false);
for (size_t index = 0; index < pending_open_url_requests_.size();
++index) {
const OpenURLParams& url_request = pending_open_url_requests_[index];
OpenURLFromTab(web_contents(), url_request);
}
pending_open_url_requests_.clear();
}
void ExternalTabContainerWin::SetupExternalTabView() {
// Create a TabContentsContainer to handle focus cycling using Tab and
// Shift-Tab.
tab_contents_container_ = new views::WebView(tab_contents_->profile());
// The views created here will be destroyed when the ExternalTabContainer
// widget is torn down.
external_tab_view_ = new views::View();
InfoBarContainerView* info_bar_container = new InfoBarContainerView(this);
info_bar_container->ChangeTabContents(tab_contents_->infobar_tab_helper());
views::GridLayout* layout = new views::GridLayout(external_tab_view_);
// Give this column an identifier of 0.
views::ColumnSet* columns = layout->AddColumnSet(0);
columns->AddColumn(views::GridLayout::FILL,
views::GridLayout::FILL,
1,
views::GridLayout::USE_PREF,
0,
0);
external_tab_view_->SetLayoutManager(layout);
layout->StartRow(0, 0);
layout->AddView(info_bar_container);
layout->StartRow(1, 0);
layout->AddView(tab_contents_container_);
GetWidget()->SetContentsView(external_tab_view_);
// Note that SetTabContents must be called after AddChildView is called
tab_contents_container_->SetWebContents(web_contents());
}
// static
ExternalTabContainer* ExternalTabContainer::Create(
AutomationProvider* automation_provider,
AutomationResourceMessageFilter* filter) {
return new ExternalTabContainerWin(automation_provider, filter);
}
// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
HWND tab_window) {
HWND parent_window = ::GetParent(tab_window);
if (!::IsWindow(parent_window)) {
return NULL;
}
if (!ExternalTabContainerWin::IsExternalTabContainer(parent_window)) {
return NULL;
}
ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
ViewProp::GetValue(parent_window, kWindowObjectKey));
return container;
}
// static
scoped_refptr<ExternalTabContainer> ExternalTabContainer::RemovePendingTab(
uintptr_t cookie) {
return ExternalTabContainerWin::RemovePendingExternalTab(cookie);
}
///////////////////////////////////////////////////////////////////////////////
// TemporaryPopupExternalTabContainerWin
TemporaryPopupExternalTabContainerWin::TemporaryPopupExternalTabContainerWin(
AutomationProvider* automation,
AutomationResourceMessageFilter* filter)
: ExternalTabContainerWin(automation, filter) {
}
TemporaryPopupExternalTabContainerWin::~TemporaryPopupExternalTabContainerWin(
) {
DVLOG(1) << __FUNCTION__;
}
WebContents* TemporaryPopupExternalTabContainerWin::OpenURLFromTab(
WebContents* source,
const OpenURLParams& params) {
if (!automation_)
return NULL;
OpenURLParams forward_params = params;
if (params.disposition == CURRENT_TAB) {
DCHECK(route_all_top_level_navigations_);
forward_params.disposition = NEW_FOREGROUND_TAB;
}
WebContents* new_contents =
ExternalTabContainerWin::OpenURLFromTab(source, forward_params);
// support only one navigation for a dummy tab before it is killed.
::DestroyWindow(GetNativeView());
return new_contents;
}