普通文本  |  269行  |  9.16 KB

// 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/panels/panel_host.h"

#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chrome_page_zoom.h"
#include "chrome/browser/extensions/extension_web_contents_observer.h"
#include "chrome/browser/extensions/window_controller.h"
#include "chrome/browser/favicon/favicon_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/panels/panel.h"
#include "chrome/browser/ui/prefs/prefs_tab_helper.h"
#include "chrome/common/extensions/extension_messages.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/view_type_utils.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/rect.h"

using content::UserMetricsAction;

PanelHost::PanelHost(Panel* panel, Profile* profile)
    : panel_(panel),
      profile_(profile),
      extension_function_dispatcher_(profile, this),
      weak_factory_(this) {
}

PanelHost::~PanelHost() {
}

void PanelHost::Init(const GURL& url) {
  if (url.is_empty())
    return;

  content::WebContents::CreateParams create_params(
      profile_, content::SiteInstance::CreateForURL(profile_, url));
  web_contents_.reset(content::WebContents::Create(create_params));
  extensions::SetViewType(web_contents_.get(), extensions::VIEW_TYPE_PANEL);
  web_contents_->SetDelegate(this);
  content::WebContentsObserver::Observe(web_contents_.get());

  // Needed to give the web contents a Tab ID. Extension APIs
  // expect web contents to have a Tab ID.
  SessionTabHelper::CreateForWebContents(web_contents_.get());
  SessionTabHelper::FromWebContents(web_contents_.get())->SetWindowID(
      panel_->session_id());

  FaviconTabHelper::CreateForWebContents(web_contents_.get());
  PrefsTabHelper::CreateForWebContents(web_contents_.get());
  extensions::ExtensionWebContentsObserver::CreateForWebContents(
      web_contents_.get());

  web_contents_->GetController().LoadURL(
      url, content::Referrer(), content::PAGE_TRANSITION_LINK, std::string());
}

void PanelHost::DestroyWebContents() {
  // Cannot do a web_contents_.reset() because web_contents_.get() will
  // still return the pointer when we CHECK in WebContentsDestroyed (or if
  // we get called back in the middle of web contents destruction, which
  // WebView might do when it detects the web contents is destroyed).
  content::WebContents* contents = web_contents_.release();
  delete contents;
}

gfx::Image PanelHost::GetPageIcon() const {
  if (!web_contents_.get())
    return gfx::Image();

  FaviconTabHelper* favicon_tab_helper =
      FaviconTabHelper::FromWebContents(web_contents_.get());
  CHECK(favicon_tab_helper);
  return favicon_tab_helper->GetFavicon();
}

content::WebContents* PanelHost::OpenURLFromTab(
    content::WebContents* source,
    const content::OpenURLParams& params) {
  // These dispositions aren't really navigations.
  if (params.disposition == SUPPRESS_OPEN ||
      params.disposition == SAVE_TO_DISK ||
      params.disposition == IGNORE_ACTION)
    return NULL;

  // Only allow clicks on links.
  if (params.transition != content::PAGE_TRANSITION_LINK)
    return NULL;

  // Force all links to open in a new tab.
  chrome::NavigateParams navigate_params(profile_,
                                         params.url,
                                         params.transition);
  switch (params.disposition) {
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
    case OFF_THE_RECORD:
      navigate_params.disposition = params.disposition;
      break;
    default:
      navigate_params.disposition = NEW_FOREGROUND_TAB;
      break;
  }
  chrome::Navigate(&navigate_params);
  return navigate_params.target_contents;
}

void PanelHost::NavigationStateChanged(const content::WebContents* source,
                                       unsigned changed_flags) {
  // Only need to update the title if the title changed while not loading,
  // because the title is also updated when loading state changes.
  if ((changed_flags & content::INVALIDATE_TYPE_TAB) ||
      ((changed_flags & content::INVALIDATE_TYPE_TITLE) &&
       !source->IsLoading()))
    panel_->UpdateTitleBar();
}

void PanelHost::AddNewContents(content::WebContents* source,
                               content::WebContents* new_contents,
                               WindowOpenDisposition disposition,
                               const gfx::Rect& initial_pos,
                               bool user_gesture,
                               bool* was_blocked) {
  chrome::NavigateParams navigate_params(profile_, new_contents->GetURL(),
                                         content::PAGE_TRANSITION_LINK);
  navigate_params.target_contents = new_contents;

  // Force all links to open in a new tab, even if they were trying to open a
  // window.
  navigate_params.disposition =
      disposition == NEW_BACKGROUND_TAB ? disposition : NEW_FOREGROUND_TAB;

  navigate_params.window_bounds = initial_pos;
  navigate_params.user_gesture = user_gesture;
  navigate_params.extension_app_id = panel_->extension_id();
  chrome::Navigate(&navigate_params);
}

void PanelHost::ActivateContents(content::WebContents* contents) {
  panel_->Activate();
}

void PanelHost::DeactivateContents(content::WebContents* contents) {
  panel_->Deactivate();
}

void PanelHost::LoadingStateChanged(content::WebContents* source) {
  bool is_loading = source->IsLoading();
  panel_->LoadingStateChanged(is_loading);
}

void PanelHost::CloseContents(content::WebContents* source) {
  panel_->Close();
}

void PanelHost::MoveContents(content::WebContents* source,
                             const gfx::Rect& pos) {
  panel_->SetBounds(pos);
}

bool PanelHost::IsPopupOrPanel(const content::WebContents* source) const {
  return true;
}

void PanelHost::ContentsZoomChange(bool zoom_in) {
  Zoom(zoom_in ? content::PAGE_ZOOM_IN : content::PAGE_ZOOM_OUT);
}

void PanelHost::HandleKeyboardEvent(
    content::WebContents* source,
    const content::NativeWebKeyboardEvent& event) {
  return panel_->HandleKeyboardEvent(event);
}

void PanelHost::WebContentsFocused(content::WebContents* contents) {
  panel_->WebContentsFocused(contents);
}

void PanelHost::ResizeDueToAutoResize(content::WebContents* web_contents,
                                      const gfx::Size& new_size) {
  panel_->OnContentsAutoResized(new_size);
}

void PanelHost::RenderViewCreated(content::RenderViewHost* render_view_host) {
  extensions::WindowController* window = GetExtensionWindowController();
  render_view_host->Send(new ExtensionMsg_UpdateBrowserWindowId(
      render_view_host->GetRoutingID(), window->GetWindowId()));
}

void PanelHost::RenderProcessGone(base::TerminationStatus status) {
  CloseContents(web_contents_.get());
}

void PanelHost::WebContentsDestroyed(content::WebContents* web_contents) {
  // Web contents should only be destroyed by us.
  CHECK(!web_contents_.get());

  // Close the panel after we return to the message loop (not immediately,
  // otherwise, it may destroy this object before the stack has a chance
  // to cleanly unwind.)
  base::MessageLoop::current()->PostTask(
      FROM_HERE,
      base::Bind(&PanelHost::ClosePanel, weak_factory_.GetWeakPtr()));
}

void PanelHost::ClosePanel() {
  panel_->Close();
}

bool PanelHost::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(PanelHost, message)
    IPC_MESSAGE_HANDLER(ExtensionHostMsg_Request, OnRequest)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void PanelHost::OnRequest(const ExtensionHostMsg_Request_Params& params) {
  if (!web_contents_.get())
    return;

  extension_function_dispatcher_.Dispatch(params,
                                          web_contents_->GetRenderViewHost());
}

extensions::WindowController* PanelHost::GetExtensionWindowController() const {
  return panel_->extension_window_controller();
}

content::WebContents* PanelHost::GetAssociatedWebContents() const {
  return web_contents_.get();
}

void PanelHost::Reload() {
  content::RecordAction(UserMetricsAction("Reload"));
  web_contents_->GetController().Reload(true);
}

void PanelHost::ReloadIgnoringCache() {
  content::RecordAction(UserMetricsAction("ReloadIgnoringCache"));
  web_contents_->GetController().ReloadIgnoringCache(true);
}

void PanelHost::StopLoading() {
  content::RecordAction(UserMetricsAction("Stop"));
  web_contents_->Stop();
}

void PanelHost::Zoom(content::PageZoom zoom) {
  chrome_page_zoom::Zoom(web_contents_.get(), zoom);
}