普通文本  |  293行  |  10.42 KB

// Copyright (c) 2011 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 <gtk/gtk.h>

#include "build/build_config.h"

#include "base/i18n/rtl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/page_info_model.h"
#include "chrome/browser/page_info_window.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
#include "chrome/browser/ui/gtk/browser_window_gtk.h"
#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/info_bubble_gtk.h"
#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
#include "chrome/common/url_constants.h"
#include "content/browser/certificate_viewer.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"

class Profile;

namespace {

class PageInfoBubbleGtk : public PageInfoModel::PageInfoModelObserver,
                          public InfoBubbleGtkDelegate,
                          public NotificationObserver {
 public:
  PageInfoBubbleGtk(gfx::NativeWindow parent,
                    Profile* profile,
                    const GURL& url,
                    const NavigationEntry::SSLStatus& ssl,
                    bool show_history);
  virtual ~PageInfoBubbleGtk();

  // PageInfoModelObserver implementation:
  virtual void ModelChanged();

  // NotificationObserver implementation:
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  // InfoBubbleGtkDelegate implementation:
  virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble,
                                 bool closed_by_escape);

 private:
  // Layouts the different sections retrieved from the model.
  void InitContents();

  // Returns a widget that contains the UI for the passed |section|.
  GtkWidget* CreateSection(const PageInfoModel::SectionInfo& section);

  // Link button callbacks.
  CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnViewCertLinkClicked);
  CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnHelpLinkClicked);

  // The model containing the different sections to display.
  PageInfoModel model_;

  // The url for this dialog. Should be unique among active dialogs.
  GURL url_;

  // The id of the certificate for this page.
  int cert_id_;

  // Parent window.
  GtkWindow* parent_;

  // The virtual box containing the sections.
  GtkWidget* contents_;

  // The widget relative to which we are positioned.
  GtkWidget* anchor_;

  // Provides colors and stuff.
  GtkThemeService* theme_service_;

  // The various elements in the interface we keep track of for theme changes.
  std::vector<GtkWidget*> labels_;
  std::vector<GtkWidget*> links_;

  InfoBubbleGtk* bubble_;

  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleGtk);
};

PageInfoBubbleGtk::PageInfoBubbleGtk(gfx::NativeWindow parent,
                                     Profile* profile,
                                     const GURL& url,
                                     const NavigationEntry::SSLStatus& ssl,
                                     bool show_history)
    : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl,
                                            show_history, this)),
      url_(url),
      cert_id_(ssl.cert_id()),
      parent_(parent),
      contents_(NULL),
      theme_service_(GtkThemeService::GetFrom(profile)) {
  BrowserWindowGtk* browser_window =
      BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent);

  anchor_ = browser_window->
      GetToolbar()->GetLocationBarView()->location_icon_widget();

  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());

  InitContents();

  InfoBubbleGtk::ArrowLocationGtk arrow_location = base::i18n::IsRTL() ?
      InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT :
      InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT;
  bubble_ = InfoBubbleGtk::Show(anchor_,
                                NULL,  // |rect|
                                contents_,
                                arrow_location,
                                true,  // |match_system_theme|
                                true,  // |grab_input|
                                theme_service_,
                                this);  // |delegate|
  if (!bubble_) {
    NOTREACHED();
    return;
  }
}

PageInfoBubbleGtk::~PageInfoBubbleGtk() {
}

void PageInfoBubbleGtk::ModelChanged() {
  InitContents();
}

void PageInfoBubbleGtk::Observe(NotificationType type,
                                const NotificationSource& source,
                                const NotificationDetails& details) {
  DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);

  for (std::vector<GtkWidget*>::iterator it = links_.begin();
       it != links_.end(); ++it) {
    gtk_chrome_link_button_set_use_gtk_theme(
        GTK_CHROME_LINK_BUTTON(*it),
        theme_service_->UseGtkTheme());
  }

  if (theme_service_->UseGtkTheme()) {
    for (std::vector<GtkWidget*>::iterator it = labels_.begin();
         it != labels_.end(); ++it) {
      gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, NULL);
    }
  } else {
    for (std::vector<GtkWidget*>::iterator it = labels_.begin();
         it != labels_.end(); ++it) {
      gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, &gtk_util::kGdkBlack);
    }
  }
}

void PageInfoBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
                                          bool closed_by_escape) {
  delete this;
}

void PageInfoBubbleGtk::InitContents() {
  if (!contents_) {
    contents_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing);
    gtk_container_set_border_width(GTK_CONTAINER(contents_),
                                   gtk_util::kContentAreaBorder);
  } else {
    labels_.clear();
    links_.clear();
    gtk_util::RemoveAllChildren(contents_);
  }

  for (int i = 0; i < model_.GetSectionCount(); i++) {
    gtk_box_pack_start(GTK_BOX(contents_),
                       CreateSection(model_.GetSectionInfo(i)),
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(contents_),
                       gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  GtkWidget* help_link = gtk_chrome_link_button_new(
      l10n_util::GetStringUTF8(IDS_PAGE_INFO_HELP_CENTER_LINK).c_str());
  links_.push_back(help_link);
  GtkWidget* help_link_hbox = gtk_hbox_new(FALSE, 0);
  // Stick it in an hbox so it doesn't expand to the whole width.
  gtk_box_pack_start(GTK_BOX(help_link_hbox), help_link, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(contents_), help_link_hbox, FALSE, FALSE, 0);
  g_signal_connect(help_link, "clicked",
                   G_CALLBACK(OnHelpLinkClickedThunk), this);

  theme_service_->InitThemesFor(this);
  gtk_widget_show_all(contents_);
}

GtkWidget* PageInfoBubbleGtk::CreateSection(
    const PageInfoModel::SectionInfo& section) {
  GtkWidget* section_box = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);

  GdkPixbuf* pixbuf = *model_.GetIconImage(section.icon_id);
  if (pixbuf) {
    GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf);
    gtk_box_pack_start(GTK_BOX(section_box), image, FALSE, FALSE, 0);
    gtk_misc_set_alignment(GTK_MISC(image), 0, 0);
  }

  GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
  gtk_box_pack_start(GTK_BOX(section_box), vbox, TRUE, TRUE, 0);

  if (!section.headline.empty()) {
    GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.headline).c_str());
    gtk_label_set_selectable(GTK_LABEL(label), TRUE);
    labels_.push_back(label);
    PangoAttrList* attributes = pango_attr_list_new();
    pango_attr_list_insert(attributes,
                           pango_attr_weight_new(PANGO_WEIGHT_BOLD));
    gtk_label_set_attributes(GTK_LABEL(label), attributes);
    pango_attr_list_unref(attributes);
    gtk_util::SetLabelWidth(label, 400);
    // Allow linebreaking in the middle of words if necessary, so that extremely
    // long hostnames (longer than one line) will still be completely shown.
    gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR);
    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
  }
  GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.description).c_str());
  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
  labels_.push_back(label);
  gtk_util::SetLabelWidth(label, 400);
  // Allow linebreaking in the middle of words if necessary, so that extremely
  // long hostnames (longer than one line) will still be completely shown.
  gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

  if (section.type == PageInfoModel::SECTION_INFO_IDENTITY && cert_id_ > 0) {
    GtkWidget* view_cert_link = gtk_chrome_link_button_new(
        l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str());
    links_.push_back(view_cert_link);
    GtkWidget* cert_link_hbox = gtk_hbox_new(FALSE, 0);
    // Stick it in an hbox so it doesn't expand to the whole width.
    gtk_box_pack_start(GTK_BOX(cert_link_hbox), view_cert_link,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), cert_link_hbox, FALSE, FALSE, 0);
    g_signal_connect(view_cert_link, "clicked",
                     G_CALLBACK(OnViewCertLinkClickedThunk), this);
  }

  return section_box;
}

void PageInfoBubbleGtk::OnViewCertLinkClicked(GtkWidget* widget) {
  ShowCertificateViewerByID(GTK_WINDOW(parent_), cert_id_);
  bubble_->Close();
}

void PageInfoBubbleGtk::OnHelpLinkClicked(GtkWidget* widget) {
  GURL url = google_util::AppendGoogleLocaleParam(
      GURL(chrome::kPageInfoHelpCenterURL));
  Browser* browser = BrowserList::GetLastActive();
  browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
  bubble_->Close();
}

}  // namespace

namespace browser {

void ShowPageInfoBubble(gfx::NativeWindow parent,
                        Profile* profile,
                        const GURL& url,
                        const NavigationEntry::SSLStatus& ssl,
                        bool show_history) {
  new PageInfoBubbleGtk(parent, profile, url, ssl, show_history);
}

}  // namespace browser