external_tab_container_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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/browser/ui/views/external_tab_container_win.h" 6 7#include <string> 8 9#include "base/bind.h" 10#include "base/bind_helpers.h" 11#include "base/debug/trace_event.h" 12#include "base/i18n/rtl.h" 13#include "base/logging.h" 14#include "base/memory/scoped_ptr.h" 15#include "base/string16.h" 16#include "base/time.h" 17#include "base/utf_string_conversions.h" 18#include "base/win/win_util.h" 19#include "chrome/app/chrome_command_ids.h" 20#include "chrome/app/chrome_dll_resource.h" 21#include "chrome/browser/automation/automation_provider.h" 22#include "chrome/browser/debugger/devtools_toggle_action.h" 23#include "chrome/browser/debugger/devtools_window.h" 24#include "chrome/browser/file_select_helper.h" 25#include "chrome/browser/history/history_tab_helper.h" 26#include "chrome/browser/history/history_types.h" 27#include "chrome/browser/infobars/infobar_tab_helper.h" 28#include "chrome/browser/profiles/profile.h" 29#include "chrome/browser/repost_form_warning_controller.h" 30#include "chrome/browser/themes/theme_service.h" 31#include "chrome/browser/ui/app_modal_dialogs/javascript_dialog_creator.h" 32#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" 33#include "chrome/browser/ui/browser.h" 34#include "chrome/browser/ui/browser_window.h" 35#include "chrome/browser/ui/tab_contents/tab_contents.h" 36#include "chrome/browser/ui/tab_modal_confirm_dialog.h" 37#include "chrome/browser/ui/views/infobars/infobar_container_view.h" 38#include "chrome/browser/ui/views/tab_contents/render_view_context_menu_win.h" 39#include "chrome/common/automation_messages.h" 40#include "chrome/common/chrome_constants.h" 41#include "chrome/common/chrome_notification_types.h" 42#include "chrome/common/render_messages.h" 43#include "chrome/common/url_constants.h" 44#include "content/public/browser/load_notification_details.h" 45#include "content/public/browser/native_web_keyboard_event.h" 46#include "content/public/browser/navigation_details.h" 47#include "content/public/browser/navigation_entry.h" 48#include "content/public/browser/notification_service.h" 49#include "content/public/browser/render_process_host.h" 50#include "content/public/browser/render_view_host.h" 51#include "content/public/browser/web_contents.h" 52#include "content/public/browser/web_intents_dispatcher.h" 53#include "content/public/common/bindings_policy.h" 54#include "content/public/common/frame_navigate_params.h" 55#include "content/public/common/page_transition_types.h" 56#include "content/public/common/page_zoom.h" 57#include "content/public/common/renderer_preferences.h" 58#include "content/public/common/ssl_status.h" 59#include "grit/generated_resources.h" 60#include "grit/locale_settings.h" 61#include "third_party/WebKit/Source/Platform/chromium/public/WebReferrerPolicy.h" 62#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h" 63#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" 64#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" 65#include "ui/base/events/event_utils.h" 66#include "ui/base/l10n/l10n_util.h" 67#include "ui/base/models/menu_model.h" 68#include "ui/base/view_prop.h" 69#include "ui/views/controls/webview/webview.h" 70#include "ui/views/layout/grid_layout.h" 71#include "ui/views/win/hwnd_message_handler.h" 72 73using content::BrowserThread; 74using content::LoadNotificationDetails; 75using content::NativeWebKeyboardEvent; 76using content::NavigationController; 77using content::NavigationEntry; 78using content::OpenURLParams; 79using content::RenderViewHost; 80using content::SSLStatus; 81using content::WebContents; 82using ui::ViewProp; 83using WebKit::WebCString; 84using WebKit::WebReferrerPolicy; 85using WebKit::WebSecurityPolicy; 86using WebKit::WebString; 87 88static const char kWindowObjectKey[] = "ChromeWindowObject"; 89 90namespace { 91 92// Convert ui::MenuModel into a serializable form for Chrome Frame 93ContextMenuModel* ConvertMenuModel(const ui::MenuModel* ui_model) { 94 ContextMenuModel* new_model = new ContextMenuModel; 95 96 const int index_base = ui_model->GetFirstItemIndex(NULL); 97 const int item_count = ui_model->GetItemCount(); 98 new_model->items.reserve(item_count); 99 for (int i = 0; i < item_count; ++i) { 100 const int index = index_base + i; 101 if (ui_model->IsVisibleAt(index)) { 102 ContextMenuModel::Item item; 103 item.type = ui_model->GetTypeAt(index); 104 item.item_id = ui_model->GetCommandIdAt(index); 105 item.label = ui_model->GetLabelAt(index); 106 item.checked = ui_model->IsItemCheckedAt(index); 107 item.enabled = ui_model->IsEnabledAt(index); 108 if (item.type == ui::MenuModel::TYPE_SUBMENU) 109 item.submenu = ConvertMenuModel(ui_model->GetSubmenuModelAt(index)); 110 111 new_model->items.push_back(item); 112 } 113 } 114 115 return new_model; 116} 117 118} // namespace 119 120base::LazyInstance<ExternalTabContainerWin::PendingTabs> 121 ExternalTabContainerWin::pending_tabs_ = LAZY_INSTANCE_INITIALIZER; 122 123ExternalTabContainerWin::ExternalTabContainerWin( 124 AutomationProvider* automation, 125 AutomationResourceMessageFilter* filter) 126 : views::NativeWidgetWin(new views::Widget), 127 automation_(automation), 128 tab_contents_container_(NULL), 129 tab_handle_(0), 130 ignore_next_load_notification_(false), 131 automation_resource_message_filter_(filter), 132 load_requests_via_automation_(false), 133 handle_top_level_requests_(false), 134 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), 135 pending_(false), 136 focus_manager_(NULL), 137 external_tab_view_(NULL), 138 unload_reply_message_(NULL), 139 route_all_top_level_navigations_(false), 140 is_popup_window_(false) { 141} 142 143// static 144scoped_refptr<ExternalTabContainer> 145 ExternalTabContainerWin::RemovePendingExternalTab(uintptr_t cookie) { 146 PendingTabs& pending_tabs = pending_tabs_.Get(); 147 PendingTabs::iterator index = pending_tabs.find(cookie); 148 if (index != pending_tabs.end()) { 149 scoped_refptr<ExternalTabContainer> container = (*index).second; 150 pending_tabs.erase(index); 151 return container; 152 } 153 154 NOTREACHED() << "Failed to find ExternalTabContainer for cookie: " 155 << cookie; 156 return NULL; 157} 158 159bool ExternalTabContainerWin::Init(Profile* profile, 160 HWND parent, 161 const gfx::Rect& bounds, 162 DWORD style, 163 bool load_requests_via_automation, 164 bool handle_top_level_requests, 165 content::WebContents* existing_contents, 166 const GURL& initial_url, 167 const GURL& referrer, 168 bool infobars_enabled, 169 bool route_all_top_level_navigations) { 170 if (IsWindow(GetNativeView())) { 171 NOTREACHED(); 172 return false; 173 } 174 175 load_requests_via_automation_ = load_requests_via_automation; 176 handle_top_level_requests_ = handle_top_level_requests; 177 route_all_top_level_navigations_ = route_all_top_level_navigations; 178 179 GetMessageHandler()->set_window_style(WS_POPUP | WS_CLIPCHILDREN); 180 181 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); 182 params.bounds = bounds; 183 params.native_widget = this; 184 GetWidget()->Init(params); 185 if (!IsWindow(GetNativeView())) { 186 NOTREACHED(); 187 return false; 188 } 189 190 // TODO(jcampan): limit focus traversal to contents. 191 192 prop_.reset(new ViewProp(GetNativeView(), kWindowObjectKey, this)); 193 194 if (existing_contents) { 195 existing_contents->GetController().SetBrowserContext(profile); 196 } else { 197 existing_contents = 198 WebContents::Create(profile, NULL, MSG_ROUTING_NONE, NULL); 199 existing_contents->GetRenderViewHost()->AllowBindings( 200 content::BINDINGS_POLICY_EXTERNAL_HOST); 201 } 202 203 existing_contents->SetDelegate(this); 204 existing_contents->GetMutableRendererPrefs()-> 205 browser_handles_non_local_top_level_requests = handle_top_level_requests; 206 207 NavigationController* controller = &existing_contents->GetController(); 208 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 209 content::Source<NavigationController>(controller)); 210 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, 211 content::Source<NavigationController>(controller)); 212 registrar_.Add(this, 213 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, 214 content::Source<WebContents>(existing_contents)); 215 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_DELETED, 216 content::NotificationService::AllSources()); 217 registrar_.Add(this, content::NOTIFICATION_RENDER_VIEW_HOST_CREATED, 218 content::NotificationService::AllSources()); 219 220 content::WebContentsObserver::Observe(existing_contents); 221 222 // TODO(avi): Rather than create a TabContents, attach desired tab helpers. 223 tab_contents_.reset( 224 TabContents::Factory::CreateTabContents(existing_contents)); 225 226 if (!infobars_enabled) { 227 InfoBarTabHelper* infobar_tab_helper = 228 InfoBarTabHelper::FromWebContents(existing_contents); 229 infobar_tab_helper->set_infobars_enabled(false); 230 } 231 232 // Start loading initial URL 233 if (!initial_url.is_empty()) { 234 // Navigate out of context since we don't have a 'tab_handle_' yet. 235 MessageLoop::current()->PostTask( 236 FROM_HERE, 237 base::Bind(&ExternalTabContainerWin::Navigate, 238 weak_factory_.GetWeakPtr(), 239 initial_url, referrer)); 240 } 241 242 // We need WS_POPUP to be on the window during initialization, but 243 // once initialized we apply the requested style which may or may not 244 // include the popup bit. 245 // Note that it's important to do this before we call SetParent since 246 // during the SetParent call we will otherwise get a WA_ACTIVATE call 247 // that causes us to steal the current focus. 248 SetWindowLong( 249 GetNativeView(), GWL_STYLE, 250 (GetWindowLong(GetNativeView(), GWL_STYLE) & ~WS_POPUP) | style); 251 252 // Now apply the parenting and style 253 if (parent) 254 SetParent(GetNativeView(), parent); 255 256 ::ShowWindow(existing_contents->GetNativeView(), SW_SHOWNA); 257 258 LoadAccelerators(); 259 SetupExternalTabView(); 260 BlockedContentTabHelper::FromWebContents(existing_contents)-> 261 set_delegate(this); 262 return true; 263} 264 265void ExternalTabContainerWin::Uninitialize() { 266 registrar_.RemoveAll(); 267 if (tab_contents_.get()) { 268 UnregisterRenderViewHost( 269 tab_contents_->web_contents()->GetRenderViewHost()); 270 271 if (GetWidget()->GetRootView()) 272 GetWidget()->GetRootView()->RemoveAllChildViews(true); 273 274 content::NotificationService::current()->Notify( 275 chrome::NOTIFICATION_EXTERNAL_TAB_CLOSED, 276 content::Source<NavigationController>( 277 &tab_contents_->web_contents()->GetController()), 278 content::Details<ExternalTabContainer>(this)); 279 280 tab_contents_.reset(NULL); 281 } 282 283 if (focus_manager_) { 284 focus_manager_->UnregisterAccelerators(this); 285 focus_manager_ = NULL; 286 } 287 288 external_tab_view_ = NULL; 289 request_context_ = NULL; 290 tab_contents_container_ = NULL; 291} 292 293bool ExternalTabContainerWin::Reinitialize( 294 AutomationProvider* automation_provider, 295 AutomationResourceMessageFilter* filter, 296 gfx::NativeWindow parent_window) { 297 if (!automation_provider || !filter) { 298 NOTREACHED(); 299 return false; 300 } 301 302 automation_ = automation_provider; 303 automation_resource_message_filter_ = filter; 304 // Wait for the automation channel to be initialized before resuming pending 305 // render views and sending in the navigation state. 306 MessageLoop::current()->PostTask( 307 FROM_HERE, base::Bind(&ExternalTabContainerWin::OnReinitialize, 308 weak_factory_.GetWeakPtr())); 309 310 if (parent_window) 311 SetParent(GetNativeView(), parent_window); 312 return true; 313} 314 315WebContents* ExternalTabContainerWin::GetWebContents() const { 316 return tab_contents_.get() ? tab_contents_->web_contents() : NULL; 317} 318 319TabContents* ExternalTabContainerWin::GetTabContents() { 320 return tab_contents_.get(); 321} 322 323gfx::NativeView ExternalTabContainerWin::GetExternalTabNativeView() const { 324 return GetNativeView(); 325} 326 327void ExternalTabContainerWin::SetTabHandle(int handle) { 328 tab_handle_ = handle; 329} 330 331int ExternalTabContainerWin::GetTabHandle() const { 332 return tab_handle_; 333} 334 335bool ExternalTabContainerWin::ExecuteContextMenuCommand(int command) { 336 if (!external_context_menu_.get()) { 337 NOTREACHED(); 338 return false; 339 } 340 341 switch (command) { 342 case IDS_CONTENT_CONTEXT_SAVEAUDIOAS: 343 case IDS_CONTENT_CONTEXT_SAVEVIDEOAS: 344 case IDS_CONTENT_CONTEXT_SAVEIMAGEAS: 345 case IDS_CONTENT_CONTEXT_SAVELINKAS: { 346 NOTREACHED(); // Should be handled in host. 347 break; 348 } 349 } 350 351 external_context_menu_->ExecuteCommand(command); 352 return true; 353} 354 355void ExternalTabContainerWin::RunUnloadHandlers(IPC::Message* reply_message) { 356 if (!automation_) { 357 delete reply_message; 358 return; 359 } 360 361 // If we have a pending unload message, then just respond back to this 362 // request and continue processing the previous unload message. 363 if (unload_reply_message_) { 364 AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); 365 automation_->Send(reply_message); 366 return; 367 } 368 369 unload_reply_message_ = reply_message; 370 bool wait_for_unload_handlers = 371 tab_contents_.get() && 372 Browser::RunUnloadEventsHelper(tab_contents_->web_contents()); 373 if (!wait_for_unload_handlers) { 374 AutomationMsg_RunUnloadHandlers::WriteReplyParams(reply_message, true); 375 automation_->Send(reply_message); 376 unload_reply_message_ = NULL; 377 } 378} 379 380void ExternalTabContainerWin::ProcessUnhandledAccelerator(const MSG& msg) { 381 NativeWebKeyboardEvent keyboard_event(msg); 382 unhandled_keyboard_event_handler_.HandleKeyboardEvent(keyboard_event, 383 focus_manager_); 384} 385 386void ExternalTabContainerWin::FocusThroughTabTraversal( 387 bool reverse, 388 bool restore_focus_to_view) { 389 DCHECK(tab_contents_.get()); 390 if (tab_contents_.get()) 391 tab_contents_->web_contents()->Focus(); 392 393 // The tab_contents_ member can get destroyed in the context of the call to 394 // TabContentsViewViews::Focus() above. This method eventually calls SetFocus 395 // on the native window, which could end up dispatching messages like 396 // WM_DESTROY for the external tab. 397 if (tab_contents_.get() && restore_focus_to_view) 398 tab_contents_->web_contents()->FocusThroughTabTraversal(reverse); 399} 400 401// static 402bool ExternalTabContainerWin::IsExternalTabContainer(HWND window) { 403 return ViewProp::GetValue(window, kWindowObjectKey) != NULL; 404} 405 406// static 407ExternalTabContainer* 408 ExternalTabContainerWin::GetExternalContainerFromNativeWindow( 409 gfx::NativeView native_window) { 410 ExternalTabContainer* tab_container = NULL; 411 if (native_window) { 412 tab_container = reinterpret_cast<ExternalTabContainer*>( 413 ViewProp::GetValue(native_window, kWindowObjectKey)); 414 } 415 return tab_container; 416} 417//////////////////////////////////////////////////////////////////////////////// 418// ExternalTabContainer, content::WebContentsDelegate implementation: 419 420WebContents* ExternalTabContainerWin::OpenURLFromTab( 421 WebContents* source, 422 const OpenURLParams& params) { 423 if (pending()) { 424 pending_open_url_requests_.push_back(params); 425 return NULL; 426 } 427 428 switch (params.disposition) { 429 case CURRENT_TAB: 430 case SINGLETON_TAB: 431 case NEW_FOREGROUND_TAB: 432 case NEW_BACKGROUND_TAB: 433 case NEW_POPUP: 434 case NEW_WINDOW: 435 case SAVE_TO_DISK: 436 if (automation_) { 437 GURL referrer = GURL(WebSecurityPolicy::generateReferrerHeader( 438 params.referrer.policy, 439 params.url, 440 WebString::fromUTF8(params.referrer.url.spec())).utf8()); 441 automation_->Send(new AutomationMsg_OpenURL(tab_handle_, 442 params.url, 443 referrer, 444 params.disposition)); 445 // TODO(ananta) 446 // We should populate other fields in the 447 // ViewHostMsg_FrameNavigate_Params structure. Another option could be 448 // to refactor the UpdateHistoryForNavigation function in WebContents. 449 content::FrameNavigateParams nav_params; 450 nav_params.referrer = content::Referrer(referrer, 451 params.referrer.policy); 452 nav_params.url = params.url; 453 nav_params.page_id = -1; 454 nav_params.transition = content::PAGE_TRANSITION_LINK; 455 456 HistoryTabHelper* history_tab_helper = 457 HistoryTabHelper::FromWebContents(tab_contents_->web_contents()); 458 const history::HistoryAddPageArgs& add_page_args = 459 history_tab_helper->CreateHistoryAddPageArgs( 460 params.url, base::Time::Now(), 461 false /* did_replace_entry */, nav_params); 462 history_tab_helper->UpdateHistoryForNavigation(add_page_args); 463 464 return tab_contents_->web_contents(); 465 } 466 break; 467 default: 468 NOTREACHED(); 469 break; 470 } 471 472 return NULL; 473} 474 475void ExternalTabContainerWin::NavigationStateChanged(const WebContents* source, 476 unsigned changed_flags) { 477 if (automation_) { 478 NavigationInfo nav_info; 479 if (InitNavigationInfo(&nav_info, content::NAVIGATION_TYPE_NAV_IGNORE, 0)) 480 automation_->Send(new AutomationMsg_NavigationStateChanged( 481 tab_handle_, changed_flags, nav_info)); 482 } 483} 484 485void ExternalTabContainerWin::AddNewContents(WebContents* source, 486 WebContents* new_contents, 487 WindowOpenDisposition disposition, 488 const gfx::Rect& initial_pos, 489 bool user_gesture, 490 bool* was_blocked) { 491 if (!automation_) { 492 DCHECK(pending_); 493 LOG(ERROR) << "Invalid automation provider. Dropping new contents notify"; 494 delete new_contents; 495 return; 496 } 497 498 scoped_refptr<ExternalTabContainerWin> new_container; 499 // If the host is a browser like IE8, then the URL being navigated to in the 500 // new tab contents could potentially navigate back to Chrome from a new 501 // IE process. We support full tab mode only for IE and hence we use that as 502 // a determining factor in whether the new ExternalTabContainer instance is 503 // created as pending or not. 504 if (!route_all_top_level_navigations_) { 505 new_container = new ExternalTabContainerWin(NULL, NULL); 506 } else { 507 // Reuse the same tab handle here as the new container instance is a dummy 508 // instance which does not have an automation client connected at the other 509 // end. 510 new_container = new TemporaryPopupExternalTabContainerWin( 511 automation_, automation_resource_message_filter_.get()); 512 new_container->SetTabHandle(tab_handle_); 513 } 514 515 // Make sure that ExternalTabContainer instance is initialized with 516 // an unwrapped Profile. 517 Profile* profile = 518 Profile::FromBrowserContext(new_contents->GetBrowserContext())-> 519 GetOriginalProfile(); 520 bool result = new_container->Init(profile, 521 NULL, 522 initial_pos, 523 WS_CHILD, 524 load_requests_via_automation_, 525 handle_top_level_requests_, 526 new_contents, 527 GURL(), 528 GURL(), 529 true, 530 route_all_top_level_navigations_); 531 532 if (result) { 533 if (route_all_top_level_navigations_) { 534 return; 535 } 536 uintptr_t cookie = reinterpret_cast<uintptr_t>(new_container.get()); 537 pending_tabs_.Get()[cookie] = new_container; 538 new_container->set_pending(true); 539 new_container->set_is_popup_window(disposition == NEW_POPUP); 540 AttachExternalTabParams attach_params_; 541 attach_params_.cookie = static_cast<uint64>(cookie); 542 attach_params_.dimensions = initial_pos; 543 attach_params_.user_gesture = user_gesture; 544 attach_params_.disposition = disposition; 545 attach_params_.profile_name = WideToUTF8( 546 profile->GetPath().DirName().BaseName().value()); 547 automation_->Send(new AutomationMsg_AttachExternalTab( 548 tab_handle_, attach_params_)); 549 } else { 550 NOTREACHED(); 551 } 552} 553 554void ExternalTabContainerWin::WebContentsCreated(WebContents* source_contents, 555 int64 source_frame_id, 556 const GURL& target_url, 557 WebContents* new_contents) { 558 if (!load_requests_via_automation_) 559 return; 560 561 RenderViewHost* rvh = new_contents->GetRenderViewHost(); 562 DCHECK(rvh != NULL); 563 564 // Register this render view as a pending render view, i.e. any network 565 // requests initiated by this render view would be serviced when the 566 // external host connects to the new external tab instance. 567 RegisterRenderViewHostForAutomation(rvh, true); 568} 569 570void ExternalTabContainerWin::CloseContents(content::WebContents* source) { 571 if (!automation_) 572 return; 573 574 if (unload_reply_message_) { 575 AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, 576 true); 577 automation_->Send(unload_reply_message_); 578 unload_reply_message_ = NULL; 579 } else { 580 automation_->Send(new AutomationMsg_CloseExternalTab(tab_handle_)); 581 } 582} 583 584void ExternalTabContainerWin::MoveContents(WebContents* source, 585 const gfx::Rect& pos) { 586 if (automation_ && is_popup_window_) 587 automation_->Send(new AutomationMsg_MoveWindow(tab_handle_, pos)); 588} 589 590content::WebContents* ExternalTabContainerWin::GetConstrainingWebContents( 591 content::WebContents* source) { 592 return source; 593} 594 595ExternalTabContainerWin::~ExternalTabContainerWin() { 596 Uninitialize(); 597} 598 599bool ExternalTabContainerWin::IsPopupOrPanel(const WebContents* source) const { 600 return is_popup_window_; 601} 602 603void ExternalTabContainerWin::UpdateTargetURL(WebContents* source, 604 int32 page_id, 605 const GURL& url) { 606 if (automation_) { 607 string16 url_string = CA2W(url.spec().c_str()); 608 automation_->Send( 609 new AutomationMsg_UpdateTargetUrl(tab_handle_, url_string)); 610 } 611} 612 613void ExternalTabContainerWin::ContentsZoomChange(bool zoom_in) { 614} 615 616bool ExternalTabContainerWin::TakeFocus(content::WebContents* source, 617 bool reverse) { 618 if (automation_) { 619 automation_->Send(new AutomationMsg_TabbedOut(tab_handle_, 620 base::win::IsShiftPressed())); 621 } 622 623 return true; 624} 625 626bool ExternalTabContainerWin::CanDownload(RenderViewHost* render_view_host, 627 int request_id, 628 const std::string& request_method) { 629 if (load_requests_via_automation_) { 630 if (automation_) { 631 // In case the host needs to show UI that needs to take the focus. 632 ::AllowSetForegroundWindow(ASFW_ANY); 633 634 BrowserThread::PostTask( 635 BrowserThread::IO, FROM_HERE, 636 base::Bind( 637 base::IgnoreResult( 638 &AutomationResourceMessageFilter::SendDownloadRequestToHost), 639 automation_resource_message_filter_.get(), 0, tab_handle_, 640 request_id)); 641 } 642 } else { 643 DLOG(WARNING) << "Downloads are only supported with host browser network " 644 "stack enabled."; 645 } 646 647 // Never allow downloads. 648 return false; 649} 650 651void ExternalTabContainerWin::RegisterRenderViewHostForAutomation( 652 RenderViewHost* render_view_host, 653 bool pending_view) { 654 if (render_view_host) { 655 AutomationResourceMessageFilter::RegisterRenderView( 656 render_view_host->GetProcess()->GetID(), 657 render_view_host->GetRoutingID(), 658 GetTabHandle(), 659 automation_resource_message_filter_, 660 pending_view); 661 } 662} 663 664void ExternalTabContainerWin::RegisterRenderViewHost( 665 RenderViewHost* render_view_host) { 666 // RenderViewHost instances that are to be associated with this 667 // ExternalTabContainer should share the same resource request automation 668 // settings. 669 RegisterRenderViewHostForAutomation( 670 render_view_host, 671 false); // Network requests should not be handled later. 672} 673 674void ExternalTabContainerWin::UnregisterRenderViewHost( 675 RenderViewHost* render_view_host) { 676 // Undo the resource automation registration performed in 677 // ExternalTabContainerWin::RegisterRenderViewHost. 678 if (render_view_host) { 679 AutomationResourceMessageFilter::UnRegisterRenderView( 680 render_view_host->GetProcess()->GetID(), 681 render_view_host->GetRoutingID()); 682 } 683} 684 685content::JavaScriptDialogCreator* 686ExternalTabContainerWin::GetJavaScriptDialogCreator() { 687 return GetJavaScriptDialogCreatorInstance(); 688} 689 690bool ExternalTabContainerWin::HandleContextMenu( 691 const content::ContextMenuParams& params) { 692 if (!automation_) { 693 NOTREACHED(); 694 return false; 695 } 696 external_context_menu_.reset(RenderViewContextMenuViews::Create( 697 web_contents(), params)); 698 static_cast<RenderViewContextMenuWin*>( 699 external_context_menu_.get())->SetExternal(); 700 external_context_menu_->Init(); 701 external_context_menu_->UpdateMenuItemStates(); 702 703 scoped_ptr<ContextMenuModel> context_menu_model( 704 ConvertMenuModel(&external_context_menu_->menu_model())); 705 706 POINT screen_pt = { params.x, params.y }; 707 MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1); 708 709 MiniContextMenuParams ipc_params; 710 ipc_params.screen_x = screen_pt.x; 711 ipc_params.screen_y = screen_pt.y; 712 ipc_params.link_url = params.link_url; 713 ipc_params.unfiltered_link_url = params.unfiltered_link_url; 714 ipc_params.src_url = params.src_url; 715 ipc_params.page_url = params.page_url; 716 ipc_params.keyword_url = params.keyword_url; 717 ipc_params.frame_url = params.frame_url; 718 719 bool rtl = base::i18n::IsRTL(); 720 automation_->Send( 721 new AutomationMsg_ForwardContextMenuToExternalHost(tab_handle_, 722 *context_menu_model, 723 rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN, ipc_params)); 724 725 return true; 726} 727 728bool ExternalTabContainerWin::PreHandleKeyboardEvent( 729 content::WebContents* source, 730 const NativeWebKeyboardEvent& event, 731 bool* is_keyboard_shortcut) { 732 return false; 733} 734 735void ExternalTabContainerWin::HandleKeyboardEvent( 736 content::WebContents* source, 737 const NativeWebKeyboardEvent& event) { 738 ProcessUnhandledKeyStroke(event.os_event.hwnd, event.os_event.message, 739 event.os_event.wParam, event.os_event.lParam); 740} 741 742void ExternalTabContainerWin::BeforeUnloadFired(WebContents* tab, 743 bool proceed, 744 bool* proceed_to_fire_unload) { 745 *proceed_to_fire_unload = true; 746 747 if (!automation_) { 748 delete unload_reply_message_; 749 unload_reply_message_ = NULL; 750 return; 751 } 752 753 if (!unload_reply_message_) { 754 NOTREACHED() << "**** NULL unload reply message pointer."; 755 return; 756 } 757 758 if (!proceed) { 759 AutomationMsg_RunUnloadHandlers::WriteReplyParams(unload_reply_message_, 760 false); 761 automation_->Send(unload_reply_message_); 762 unload_reply_message_ = NULL; 763 *proceed_to_fire_unload = false; 764 } 765} 766 767void ExternalTabContainerWin::ShowRepostFormWarningDialog(WebContents* source) { 768 TabModalConfirmDialog::Create(new RepostFormWarningController(source), 769 source); 770} 771 772void ExternalTabContainerWin::RunFileChooser( 773 WebContents* tab, 774 const content::FileChooserParams& params) { 775 FileSelectHelper::RunFileChooser(tab, params); 776} 777 778void ExternalTabContainerWin::EnumerateDirectory(WebContents* tab, 779 int request_id, 780 const FilePath& path) { 781 FileSelectHelper::EnumerateDirectory(tab, request_id, path); 782} 783 784void ExternalTabContainerWin::JSOutOfMemory(WebContents* tab) { 785 Browser::JSOutOfMemoryHelper(tab); 786} 787 788void ExternalTabContainerWin::RegisterProtocolHandler( 789 WebContents* tab, 790 const std::string& protocol, 791 const GURL& url, 792 const string16& title, 793 bool user_gesture) { 794 Browser::RegisterProtocolHandlerHelper(tab, protocol, url, title, 795 user_gesture, NULL); 796} 797 798void ExternalTabContainerWin::RegisterIntentHandler( 799 WebContents* tab, 800 const webkit_glue::WebIntentServiceData& data, 801 bool user_gesture) { 802 Browser::RegisterIntentHandlerHelper(tab, data, user_gesture); 803} 804 805void ExternalTabContainerWin::WebIntentDispatch( 806 WebContents* tab, 807 content::WebIntentsDispatcher* intents_dispatcher) { 808 // TODO(binji) How do we want to display the WebIntentPicker bubble if there 809 // is no BrowserWindow? 810 delete intents_dispatcher; 811} 812 813void ExternalTabContainerWin::FindReply(WebContents* tab, 814 int request_id, 815 int number_of_matches, 816 const gfx::Rect& selection_rect, 817 int active_match_ordinal, 818 bool final_update) { 819 Browser::FindReplyHelper(tab, request_id, number_of_matches, selection_rect, 820 active_match_ordinal, final_update); 821} 822 823void ExternalTabContainerWin::RequestMediaAccessPermission( 824 content::WebContents* web_contents, 825 const content::MediaStreamRequest* request, 826 const content::MediaResponseCallback& callback) { 827 Browser::RequestMediaAccessPermissionHelper(web_contents, request, callback); 828} 829 830bool ExternalTabContainerWin::OnMessageReceived(const IPC::Message& message) { 831 bool handled = true; 832 IPC_BEGIN_MESSAGE_MAP(ExternalTabContainerWin, message) 833 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_ForwardMessageToExternalHost, 834 OnForwardMessageToExternalHost) 835 IPC_MESSAGE_UNHANDLED(handled = false) 836 IPC_END_MESSAGE_MAP() 837 return handled; 838} 839 840void ExternalTabContainerWin::DidFailProvisionalLoad( 841 int64 frame_id, 842 bool is_main_frame, 843 const GURL& validated_url, 844 int error_code, 845 const string16& error_description, 846 content::RenderViewHost* render_view_host) { 847 if (automation_) { 848 automation_->Send(new AutomationMsg_NavigationFailed( 849 tab_handle_, error_code, validated_url)); 850 } 851 ignore_next_load_notification_ = true; 852} 853 854void ExternalTabContainerWin::OnForwardMessageToExternalHost( 855 const std::string& message, 856 const std::string& origin, 857 const std::string& target) { 858 if (automation_) { 859 automation_->Send(new AutomationMsg_ForwardMessageToExternalHost( 860 tab_handle_, message, origin, target)); 861 } 862} 863 864//////////////////////////////////////////////////////////////////////////////// 865// ExternalTabContainer, NotificationObserver implementation: 866 867void ExternalTabContainerWin::Observe( 868 int type, 869 const content::NotificationSource& source, 870 const content::NotificationDetails& details) { 871 if (!automation_) 872 return; 873 874 static const int kHttpClientErrorStart = 400; 875 static const int kHttpServerErrorEnd = 510; 876 877 switch (type) { 878 case content::NOTIFICATION_LOAD_STOP: { 879 const LoadNotificationDetails* load = 880 content::Details<LoadNotificationDetails>(details).ptr(); 881 if (load && content::PageTransitionIsMainFrame(load->origin)) { 882 TRACE_EVENT_END_ETW("ExternalTabContainerWin::Navigate", 0, 883 load->url.spec()); 884 automation_->Send(new AutomationMsg_TabLoaded(tab_handle_, 885 load->url)); 886 } 887 break; 888 } 889 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: { 890 if (ignore_next_load_notification_) { 891 ignore_next_load_notification_ = false; 892 return; 893 } 894 895 const content::LoadCommittedDetails* commit = 896 content::Details<content::LoadCommittedDetails>(details).ptr(); 897 898 if (commit->http_status_code >= kHttpClientErrorStart && 899 commit->http_status_code <= kHttpServerErrorEnd) { 900 automation_->Send(new AutomationMsg_NavigationFailed( 901 tab_handle_, commit->http_status_code, commit->entry->GetURL())); 902 903 ignore_next_load_notification_ = true; 904 } else { 905 NavigationInfo navigation_info; 906 // When the previous entry index is invalid, it will be -1, which 907 // will still make the computation come out right (navigating to the 908 // 0th entry will be +1). 909 if (InitNavigationInfo(&navigation_info, commit->type, 910 commit->previous_entry_index - 911 tab_contents_->web_contents()-> 912 GetController().GetLastCommittedEntryIndex())) 913 automation_->Send(new AutomationMsg_DidNavigate(tab_handle_, 914 navigation_info)); 915 } 916 break; 917 } 918 case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { 919 if (load_requests_via_automation_) { 920 RenderViewHost* rvh = content::Details<RenderViewHost>(details).ptr(); 921 RegisterRenderViewHostForAutomation(rvh, false); 922 } 923 break; 924 } 925 case content::NOTIFICATION_RENDER_VIEW_HOST_DELETED: { 926 if (load_requests_via_automation_) { 927 RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr(); 928 UnregisterRenderViewHost(rvh); 929 } 930 break; 931 } 932 case content::NOTIFICATION_RENDER_VIEW_HOST_CREATED: { 933 if (load_requests_via_automation_) { 934 RenderViewHost* rvh = content::Source<RenderViewHost>(source).ptr(); 935 RegisterRenderViewHostForAutomation(rvh, false); 936 } 937 break; 938 } 939 default: 940 NOTREACHED(); 941 } 942} 943 944//////////////////////////////////////////////////////////////////////////////// 945// ExternalTabContainer, views::NativeWidgetWin overrides: 946 947bool ExternalTabContainerWin::PreHandleMSG(UINT message, 948 WPARAM w_param, 949 LPARAM l_param, 950 LRESULT* result) { 951 if (message == WM_DESTROY) { 952 prop_.reset(); 953 Uninitialize(); 954 } 955 return false; 956} 957 958void ExternalTabContainerWin::PostHandleMSG(UINT message, 959 WPARAM w_param, 960 LPARAM l_param) { 961 // Grab a reference here which will be released in OnFinalMessage 962 if (message == WM_CREATE) 963 AddRef(); 964} 965 966void ExternalTabContainerWin::OnFinalMessage(HWND window) { 967 GetWidget()->OnNativeWidgetDestroyed(); 968 // Release the reference which we grabbed in WM_CREATE. 969 Release(); 970} 971 972//////////////////////////////////////////////////////////////////////////////// 973// ExternalTabContainer, private: 974bool ExternalTabContainerWin::ProcessUnhandledKeyStroke(HWND window, 975 UINT message, 976 WPARAM wparam, 977 LPARAM lparam) { 978 if (!automation_) { 979 return false; 980 } 981 if ((wparam == VK_TAB) && !base::win::IsCtrlPressed()) { 982 // Tabs are handled separately (except if this is Ctrl-Tab or 983 // Ctrl-Shift-Tab) 984 return false; 985 } 986 987 // Send this keystroke to the external host as it could be processed as an 988 // accelerator there. If the host does not handle this accelerator, it will 989 // reflect the accelerator back to us via the ProcessUnhandledAccelerator 990 // method. 991 MSG msg = {0}; 992 msg.hwnd = window; 993 msg.message = message; 994 msg.wParam = wparam; 995 msg.lParam = lparam; 996 automation_->Send(new AutomationMsg_HandleAccelerator(tab_handle_, msg)); 997 return true; 998} 999 1000bool ExternalTabContainerWin::InitNavigationInfo( 1001 NavigationInfo* nav_info, 1002 content::NavigationType nav_type, 1003 int relative_offset) { 1004 DCHECK(nav_info); 1005 NavigationEntry* entry = 1006 tab_contents_->web_contents()->GetController().GetActiveEntry(); 1007 // If this is very early in the game then we may not have an entry. 1008 if (!entry) 1009 return false; 1010 1011 nav_info->navigation_type = nav_type; 1012 nav_info->relative_offset = relative_offset; 1013 nav_info->navigation_index = 1014 tab_contents_->web_contents()->GetController().GetCurrentEntryIndex(); 1015 nav_info->url = entry->GetURL(); 1016 nav_info->referrer = entry->GetReferrer().url; 1017 nav_info->title = UTF16ToWideHack(entry->GetTitle()); 1018 if (nav_info->title.empty()) 1019 nav_info->title = UTF8ToWide(nav_info->url.spec()); 1020 1021 nav_info->security_style = entry->GetSSL().security_style; 1022 int content_status = entry->GetSSL().content_status; 1023 nav_info->displayed_insecure_content = 1024 !!(content_status & SSLStatus::DISPLAYED_INSECURE_CONTENT); 1025 nav_info->ran_insecure_content = 1026 !!(content_status & SSLStatus::RAN_INSECURE_CONTENT); 1027 return true; 1028} 1029 1030SkColor ExternalTabContainerWin::GetInfoBarSeparatorColor() const { 1031 return ThemeService::GetDefaultColor(ThemeService::COLOR_TOOLBAR_SEPARATOR); 1032} 1033 1034void ExternalTabContainerWin::InfoBarContainerStateChanged(bool is_animating) { 1035 if (external_tab_view_) 1036 external_tab_view_->Layout(); 1037} 1038 1039bool ExternalTabContainerWin::DrawInfoBarArrows(int* x) const { 1040 return false; 1041} 1042 1043bool ExternalTabContainerWin::AcceleratorPressed( 1044 const ui::Accelerator& accelerator) { 1045 std::map<ui::Accelerator, int>::const_iterator iter = 1046 accelerator_table_.find(accelerator); 1047 DCHECK(iter != accelerator_table_.end()); 1048 1049 if (!tab_contents_.get() || 1050 !tab_contents_->web_contents()->GetRenderViewHost()) { 1051 NOTREACHED(); 1052 return false; 1053 } 1054 1055 RenderViewHost* host = tab_contents_->web_contents()->GetRenderViewHost(); 1056 int command_id = iter->second; 1057 switch (command_id) { 1058 case IDC_ZOOM_PLUS: 1059 host->Zoom(content::PAGE_ZOOM_IN); 1060 break; 1061 case IDC_ZOOM_NORMAL: 1062 host->Zoom(content::PAGE_ZOOM_RESET); 1063 break; 1064 case IDC_ZOOM_MINUS: 1065 host->Zoom(content::PAGE_ZOOM_OUT); 1066 break; 1067 case IDC_DEV_TOOLS: 1068 DevToolsWindow::ToggleDevToolsWindow( 1069 tab_contents_->web_contents()->GetRenderViewHost(), 1070 false, 1071 DEVTOOLS_TOGGLE_ACTION_SHOW); 1072 break; 1073 case IDC_DEV_TOOLS_CONSOLE: 1074 DevToolsWindow::ToggleDevToolsWindow( 1075 tab_contents_->web_contents()->GetRenderViewHost(), 1076 false, 1077 DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); 1078 break; 1079 case IDC_DEV_TOOLS_INSPECT: 1080 DevToolsWindow::ToggleDevToolsWindow( 1081 tab_contents_->web_contents()->GetRenderViewHost(), 1082 false, 1083 DEVTOOLS_TOGGLE_ACTION_INSPECT); 1084 break; 1085 case IDC_DEV_TOOLS_TOGGLE: 1086 DevToolsWindow::ToggleDevToolsWindow( 1087 tab_contents_->web_contents()->GetRenderViewHost(), 1088 false, 1089 DEVTOOLS_TOGGLE_ACTION_TOGGLE); 1090 break; 1091 default: 1092 NOTREACHED() << "Unsupported accelerator: " << command_id; 1093 return false; 1094 } 1095 return true; 1096} 1097 1098bool ExternalTabContainerWin::CanHandleAccelerators() const { 1099 return true; 1100} 1101 1102void ExternalTabContainerWin::Navigate(const GURL& url, const GURL& referrer) { 1103 if (!tab_contents_.get()) { 1104 NOTREACHED(); 1105 return; 1106 } 1107 1108 TRACE_EVENT_BEGIN_ETW("ExternalTabContainerWin::Navigate", 0, url.spec()); 1109 1110 tab_contents_->web_contents()->GetController().LoadURL( 1111 url, content::Referrer(referrer, WebKit::WebReferrerPolicyDefault), 1112 content::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); 1113} 1114 1115bool ExternalTabContainerWin::OnGoToEntryOffset(int offset) { 1116 if (load_requests_via_automation_) { 1117 if (automation_) { 1118 automation_->Send(new AutomationMsg_RequestGoToHistoryEntryOffset( 1119 tab_handle_, offset)); 1120 } 1121 return false; 1122 } 1123 1124 return true; 1125} 1126 1127void ExternalTabContainerWin::LoadAccelerators() { 1128 HACCEL accelerator_table = AtlLoadAccelerators(IDR_CHROMEFRAME); 1129 DCHECK(accelerator_table); 1130 1131 // We have to copy the table to access its contents. 1132 int count = CopyAcceleratorTable(accelerator_table, 0, 0); 1133 if (count == 0) { 1134 // Nothing to do in that case. 1135 return; 1136 } 1137 1138 scoped_array<ACCEL> scoped_accelerators(new ACCEL[count]); 1139 ACCEL* accelerators = scoped_accelerators.get(); 1140 DCHECK(accelerators != NULL); 1141 1142 CopyAcceleratorTable(accelerator_table, accelerators, count); 1143 1144 focus_manager_ = GetWidget()->GetFocusManager(); 1145 DCHECK(focus_manager_); 1146 1147 // Let's fill our own accelerator table. 1148 for (int i = 0; i < count; ++i) { 1149 ui::Accelerator accelerator( 1150 static_cast<ui::KeyboardCode>(accelerators[i].key), 1151 ui::GetModifiersFromACCEL(accelerators[i])); 1152 accelerator_table_[accelerator] = accelerators[i].cmd; 1153 1154 // Also register with the focus manager. 1155 if (focus_manager_) { 1156 focus_manager_->RegisterAccelerator( 1157 accelerator, ui::AcceleratorManager::kNormalPriority, this); 1158 } 1159 } 1160} 1161 1162void ExternalTabContainerWin::OnReinitialize() { 1163 if (load_requests_via_automation_) { 1164 RenderViewHost* rvh = tab_contents_->web_contents()->GetRenderViewHost(); 1165 if (rvh) { 1166 AutomationResourceMessageFilter::ResumePendingRenderView( 1167 rvh->GetProcess()->GetID(), rvh->GetRoutingID(), 1168 tab_handle_, automation_resource_message_filter_); 1169 } 1170 } 1171 1172 NavigationStateChanged(web_contents(), 0); 1173 ServicePendingOpenURLRequests(); 1174} 1175 1176void ExternalTabContainerWin::ServicePendingOpenURLRequests() { 1177 DCHECK(pending()); 1178 1179 set_pending(false); 1180 1181 for (size_t index = 0; index < pending_open_url_requests_.size(); 1182 ++index) { 1183 const OpenURLParams& url_request = pending_open_url_requests_[index]; 1184 OpenURLFromTab(web_contents(), url_request); 1185 } 1186 pending_open_url_requests_.clear(); 1187} 1188 1189void ExternalTabContainerWin::SetupExternalTabView() { 1190 // Create a TabContentsContainer to handle focus cycling using Tab and 1191 // Shift-Tab. 1192 tab_contents_container_ = new views::WebView(tab_contents_->profile()); 1193 1194 // The views created here will be destroyed when the ExternalTabContainer 1195 // widget is torn down. 1196 external_tab_view_ = new views::View(); 1197 1198 InfoBarContainerView* info_bar_container = 1199 new InfoBarContainerView(this, NULL); 1200 InfoBarTabHelper* infobar_tab_helper = 1201 InfoBarTabHelper::FromWebContents(tab_contents_->web_contents()); 1202 info_bar_container->ChangeTabContents(infobar_tab_helper); 1203 1204 views::GridLayout* layout = new views::GridLayout(external_tab_view_); 1205 // Give this column an identifier of 0. 1206 views::ColumnSet* columns = layout->AddColumnSet(0); 1207 columns->AddColumn(views::GridLayout::FILL, 1208 views::GridLayout::FILL, 1209 1, 1210 views::GridLayout::USE_PREF, 1211 0, 1212 0); 1213 1214 external_tab_view_->SetLayoutManager(layout); 1215 1216 layout->StartRow(0, 0); 1217 layout->AddView(info_bar_container); 1218 layout->StartRow(1, 0); 1219 layout->AddView(tab_contents_container_); 1220 GetWidget()->SetContentsView(external_tab_view_); 1221 // Note that SetTabContents must be called after AddChildView is called 1222 tab_contents_container_->SetWebContents(web_contents()); 1223} 1224 1225// static 1226ExternalTabContainer* ExternalTabContainer::Create( 1227 AutomationProvider* automation_provider, 1228 AutomationResourceMessageFilter* filter) { 1229 return new ExternalTabContainerWin(automation_provider, filter); 1230} 1231 1232// static 1233ExternalTabContainer* ExternalTabContainer::GetContainerForTab( 1234 HWND tab_window) { 1235 HWND parent_window = ::GetParent(tab_window); 1236 if (!::IsWindow(parent_window)) { 1237 return NULL; 1238 } 1239 if (!ExternalTabContainerWin::IsExternalTabContainer(parent_window)) { 1240 return NULL; 1241 } 1242 ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>( 1243 ViewProp::GetValue(parent_window, kWindowObjectKey)); 1244 return container; 1245} 1246 1247// static 1248scoped_refptr<ExternalTabContainer> ExternalTabContainer::RemovePendingTab( 1249 uintptr_t cookie) { 1250 return ExternalTabContainerWin::RemovePendingExternalTab(cookie); 1251} 1252 1253/////////////////////////////////////////////////////////////////////////////// 1254// TemporaryPopupExternalTabContainerWin 1255 1256TemporaryPopupExternalTabContainerWin::TemporaryPopupExternalTabContainerWin( 1257 AutomationProvider* automation, 1258 AutomationResourceMessageFilter* filter) 1259 : ExternalTabContainerWin(automation, filter) { 1260} 1261 1262TemporaryPopupExternalTabContainerWin::~TemporaryPopupExternalTabContainerWin( 1263 ) { 1264 DVLOG(1) << __FUNCTION__; 1265} 1266 1267WebContents* TemporaryPopupExternalTabContainerWin::OpenURLFromTab( 1268 WebContents* source, 1269 const OpenURLParams& params) { 1270 if (!automation_) 1271 return NULL; 1272 1273 OpenURLParams forward_params = params; 1274 if (params.disposition == CURRENT_TAB) { 1275 DCHECK(route_all_top_level_navigations_); 1276 forward_params.disposition = NEW_FOREGROUND_TAB; 1277 } 1278 WebContents* new_contents = 1279 ExternalTabContainerWin::OpenURLFromTab(source, forward_params); 1280 // support only one navigation for a dummy tab before it is killed. 1281 ::DestroyWindow(GetNativeView()); 1282 return new_contents; 1283} 1284