<commit_before>// 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 "chrome/browser/ui/views/page_info_bubble_view.h"

#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/cert_store.h"
#include "chrome/browser/certificate_viewer.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/info_bubble.h"
#include "chrome/browser/ui/views/toolbar_view.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
#include "views/controls/separator.h"
#include "views/layout/grid_layout.h"
#include "views/widget/widget.h"
#include "views/window/window.h"

namespace {

// Layout constants.
const int kHGapToBorder = 11;
const int kVGapToImage = 10;
const int kVGapToHeadline = 7;
const int kHGapImageToDescription = 6;
const int kTextPaddingRight = 10;
const int kPaddingBelowSeparator = 4;
const int kPaddingAboveSeparator = 13;
const int kIconHorizontalOffset = 27;
const int kIconVerticalOffset = -7;

// The duration of the animation that resizes the bubble once the async
// information is provided through the ModelChanged event.
const int kPageInfoSlideDuration = 300;

// A section contains an image that shows a status (good or bad), a title, an
// optional head-line (in bold) and a description.
class Section : public views::View,
                public views::LinkController {
 public:
  Section(PageInfoBubbleView* owner,
          const PageInfoModel::SectionInfo& section_info,
          const SkBitmap* status_icon,
          bool show_cert);
  virtual ~Section();

  // views::View methods:
  virtual int GetHeightForWidth(int w);
  virtual void Layout();

  // views::LinkController methods:
  virtual void LinkActivated(views::Link* source, int event_flags);

 private:
  // Calculate the layout if |compute_bounds_only|, otherwise does Layout also.
  gfx::Size LayoutItems(bool compute_bounds_only, int width);

  // The view that owns this Section object.
  PageInfoBubbleView* owner_;

  // The information this view represents.
  PageInfoModel::SectionInfo info_;

  views::ImageView* status_image_;
  views::Label* headline_label_;
  views::Label* description_label_;
  views::Link* link_;

  DISALLOW_COPY_AND_ASSIGN(Section);
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// PageInfoBubbleView

PageInfoBubbleView::PageInfoBubbleView(gfx::NativeWindow parent_window,
                                       Profile* profile,
                                       const GURL& url,
                                       const NavigationEntry::SSLStatus& ssl,
                                       bool show_history)
    : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl,
                                            show_history, this)),
      parent_window_(parent_window),
      cert_id_(ssl.cert_id()),
      info_bubble_(NULL),
      help_center_link_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(resize_animation_(this)),
      animation_start_height_(0) {
  if (cert_id_ > 0) {
    scoped_refptr<net::X509Certificate> cert;
    CertStore::GetInstance()->RetrieveCert(cert_id_, &cert);
    // When running with fake certificate (Chrome Frame) or Gears in offline
    // mode, we have no os certificate, so there is no cert to show. Don't
    // bother showing the cert info link in that case.
    if (!cert.get() || !cert->os_cert_handle())
      cert_id_ = 0;
  }
  LayoutSections();
}

PageInfoBubbleView::~PageInfoBubbleView() {
}

void PageInfoBubbleView::ShowCertDialog() {
  ShowCertificateViewerByID(parent_window_, cert_id_);
}

void PageInfoBubbleView::LayoutSections() {
  // Remove all the existing sections.
  RemoveAllChildViews(true);

  views::GridLayout* layout = new views::GridLayout(this);
  SetLayoutManager(layout);
  views::ColumnSet* columns = layout->AddColumnSet(0);
  columns->AddColumn(views::GridLayout::FILL,  // Horizontal resize.
                     views::GridLayout::FILL,  // Vertical resize.
                     1,   // Resize weight.
                     views::GridLayout::USE_PREF,  // Size type.
                     0,   // Ignored for USE_PREF.
                     0);  // Minimum size.
  // Add a column set for aligning the text when it has no icons (such as the
  // help center link).
  columns = layout->AddColumnSet(1);
  columns->AddPaddingColumn(
      0, kHGapToBorder + kIconHorizontalOffset + kHGapImageToDescription);
  columns->AddColumn(views::GridLayout::LEADING,  // Horizontal resize.
                     views::GridLayout::FILL,     // Vertical resize.
                     1,   // Resize weight.
                     views::GridLayout::USE_PREF,  // Size type.
                     0,   // Ignored for USE_PREF.
                     0);  // Minimum size.

  int count = model_.GetSectionCount();
  for (int i = 0; i < count; ++i) {
    PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
    layout->StartRow(0, 0);
    const SkBitmap* icon = model_.GetIconImage(info.icon_id);
    layout->AddView(new Section(this, info, icon, cert_id_ > 0));

    // Add separator after all sections.
    layout->AddPaddingRow(0, kPaddingAboveSeparator);
    layout->StartRow(0, 0);
    layout->AddView(new views::Separator());
    layout->AddPaddingRow(0, kPaddingBelowSeparator);
  }

  // Then add the help center link at the bottom.
  layout->StartRow(0, 1);
  help_center_link_ = new views::Link(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGE_INFO_HELP_CENTER_LINK)));
  help_center_link_->SetController(this);
  layout->AddView(help_center_link_);
}

gfx::Size PageInfoBubbleView::GetPreferredSize() {
  gfx::Size size(views::Window::GetLocalizedContentsSize(
      IDS_PAGEINFOBUBBLE_WIDTH_CHARS, IDS_PAGEINFOBUBBLE_HEIGHT_LINES));
  size.set_height(0);

  int count = model_.GetSectionCount();
  for (int i = 0; i < count; ++i) {
    PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
    const SkBitmap* icon = model_.GetIconImage(info.icon_id);
    Section section(this, info, icon, cert_id_ > 0);
    size.Enlarge(0, section.GetHeightForWidth(size.width()));
  }

  // Calculate how much space the separators take up (with padding).
  views::Separator separator;
  gfx::Size separator_size = separator.GetPreferredSize();
  gfx::Size separator_plus_padding(0, separator_size.height() +
                                      kPaddingAboveSeparator +
                                      kPaddingBelowSeparator);

  // Account for the separators and padding within sections.
  size.Enlarge(0, (count - 1) * separator_plus_padding.height());

  // Account for the Help Center link and the separator above it.
  gfx::Size link_size = help_center_link_->GetPreferredSize();
  size.Enlarge(0, separator_plus_padding.height() +
                  link_size.height());

  if (!resize_animation_.is_animating())
    return size;

  // We are animating from animation_start_height_ to size.
  int target_height = animation_start_height_ + static_cast<int>(
      (size.height() - animation_start_height_) *
      resize_animation_.GetCurrentValue());
  size.set_height(target_height);
  return size;
}

void PageInfoBubbleView::ModelChanged() {
  animation_start_height_ = bounds().height();
  LayoutSections();
  resize_animation_.SetSlideDuration(kPageInfoSlideDuration);
  resize_animation_.Show();
}

void PageInfoBubbleView::LinkActivated(views::Link* source, int event_flags) {
  GURL url = google_util::AppendGoogleLocaleParam(
      GURL(chrome::kPageInfoHelpCenterURL));
  Browser* browser = BrowserList::GetLastActive();
  browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
}

void PageInfoBubbleView::AnimationEnded(const ui::Animation* animation) {
  info_bubble_->SizeToContents();
}

void PageInfoBubbleView::AnimationProgressed(const ui::Animation* animation) {
  info_bubble_->SizeToContents();
}

////////////////////////////////////////////////////////////////////////////////
// Section

Section::Section(PageInfoBubbleView* owner,
                 const PageInfoModel::SectionInfo& section_info,
                 const SkBitmap* state_icon,
                 bool show_cert)
    : owner_(owner),
      info_(section_info),
      status_image_(NULL),
      link_(NULL) {
  if (state_icon) {
    status_image_ = new views::ImageView();
    status_image_->SetImage(*state_icon);
    AddChildView(status_image_);
  }

  headline_label_ = new views::Label(UTF16ToWideHack(info_.headline));
  headline_label_->SetFont(
      headline_label_->font().DeriveFont(0, gfx::Font::BOLD));
  headline_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
  AddChildView(headline_label_);

  description_label_ = new views::Label(UTF16ToWideHack(info_.description));
  description_label_->SetMultiLine(true);
  description_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
  // Allow linebreaking in the middle of words if necessary, so that extremely
  // long hostnames (longer than one line) will still be completely shown.
  description_label_->SetAllowCharacterBreak(true);
  AddChildView(description_label_);

  if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && show_cert) {
    link_ = new views::Link(
        UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)));
    link_->SetController(this);
    AddChildView(link_);
  }
}

Section::~Section() {
}

int Section::GetHeightForWidth(int width) {
  return LayoutItems(true, width).height();
}

void Section::Layout() {
  LayoutItems(false, width());
}

void Section::LinkActivated(views::Link* source, int event_flags) {
  owner_->ShowCertDialog();
}

gfx::Size Section::LayoutItems(bool compute_bounds_only, int width) {
  int x = kHGapToBorder;
  int y = kVGapToImage;

  // Layout the image, head-line and description.
  gfx::Size size;
  if (status_image_) {
      size = status_image_->GetPreferredSize();
    if (!compute_bounds_only)
      status_image_->SetBounds(x, y, size.width(), size.height());
  }
  int image_height = y + size.height();
  x += size.width() + kHGapImageToDescription;
  int w = width - x - kTextPaddingRight;
  y = kVGapToHeadline;
  if (!headline_label_->GetText().empty()) {
    size = headline_label_->GetPreferredSize();
    if (!compute_bounds_only)
      headline_label_->SetBounds(x, y, w > 0 ? w : 0, size.height());
    y += size.height();
  } else {
    if (!compute_bounds_only)
      headline_label_->SetBounds(x, y, 0, 0);
  }
  if (w > 0) {
    int height = description_label_->GetHeightForWidth(w);
    if (!compute_bounds_only)
      description_label_->SetBounds(x, y, w, height);
    y += height;
  } else {
    if (!compute_bounds_only)
      description_label_->SetBounds(x, y, 0, 0);
  }
  if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && link_) {
    size = link_->GetPreferredSize();
    if (!compute_bounds_only)
      link_->SetBounds(x, y, size.width(), size.height());
    y += size.height();
  }

  // Make sure the image is not truncated if the text doesn't contain much.
  y = std::max(y, image_height);
  return gfx::Size(width, y);
}

namespace browser {

void ShowPageInfoBubble(gfx::NativeWindow parent,
                        Profile* profile,
                        const GURL& url,
                        const NavigationEntry::SSLStatus& ssl,
                        bool show_history) {
  // Find where to point the bubble at.
  BrowserView* browser_view =
      BrowserView::GetBrowserViewForNativeWindow(parent);
  gfx::Point point;
  if (base::i18n::IsRTL()) {
    int width = browser_view->toolbar()->location_bar()->width();
    point = gfx::Point(width - kIconHorizontalOffset, 0);
  }
  point.Offset(0, kIconVerticalOffset);
  views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(),
                                    &point);
  gfx::Rect bounds = browser_view->toolbar()->location_bar()->bounds();
  bounds.set_origin(point);
  bounds.set_width(kIconHorizontalOffset);

  // Show the bubble.
  PageInfoBubbleView* page_info_bubble =
      new PageInfoBubbleView(parent, profile, url, ssl, show_history);
  InfoBubble* info_bubble =
      InfoBubble::Show(browser_view->GetWidget(), bounds,
                       BubbleBorder::TOP_LEFT,
                       page_info_bubble, page_info_bubble);
  page_info_bubble->set_info_bubble(info_bubble);
}

}
<commit_msg>For chromeos, ensure page info bubble is closed when link is clicked.<commit_after>// 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 "chrome/browser/ui/views/page_info_bubble_view.h"

#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/cert_store.h"
#include "chrome/browser/certificate_viewer.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/info_bubble.h"
#include "chrome/browser/ui/views/toolbar_view.h"
#include "chrome/common/url_constants.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_util.h"
#include "views/controls/image_view.h"
#include "views/controls/label.h"
#include "views/controls/separator.h"
#include "views/layout/grid_layout.h"
#include "views/widget/widget.h"
#include "views/window/window.h"

namespace {

// Layout constants.
const int kHGapToBorder = 11;
const int kVGapToImage = 10;
const int kVGapToHeadline = 7;
const int kHGapImageToDescription = 6;
const int kTextPaddingRight = 10;
const int kPaddingBelowSeparator = 4;
const int kPaddingAboveSeparator = 13;
const int kIconHorizontalOffset = 27;
const int kIconVerticalOffset = -7;

// The duration of the animation that resizes the bubble once the async
// information is provided through the ModelChanged event.
const int kPageInfoSlideDuration = 300;

// A section contains an image that shows a status (good or bad), a title, an
// optional head-line (in bold) and a description.
class Section : public views::View,
                public views::LinkController {
 public:
  Section(PageInfoBubbleView* owner,
          const PageInfoModel::SectionInfo& section_info,
          const SkBitmap* status_icon,
          bool show_cert);
  virtual ~Section();

  // views::View methods:
  virtual int GetHeightForWidth(int w);
  virtual void Layout();

  // views::LinkController methods:
  virtual void LinkActivated(views::Link* source, int event_flags);

 private:
  // Calculate the layout if |compute_bounds_only|, otherwise does Layout also.
  gfx::Size LayoutItems(bool compute_bounds_only, int width);

  // The view that owns this Section object.
  PageInfoBubbleView* owner_;

  // The information this view represents.
  PageInfoModel::SectionInfo info_;

  views::ImageView* status_image_;
  views::Label* headline_label_;
  views::Label* description_label_;
  views::Link* link_;

  DISALLOW_COPY_AND_ASSIGN(Section);
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// PageInfoBubbleView

PageInfoBubbleView::PageInfoBubbleView(gfx::NativeWindow parent_window,
                                       Profile* profile,
                                       const GURL& url,
                                       const NavigationEntry::SSLStatus& ssl,
                                       bool show_history)
    : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl,
                                            show_history, this)),
      parent_window_(parent_window),
      cert_id_(ssl.cert_id()),
      info_bubble_(NULL),
      help_center_link_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(resize_animation_(this)),
      animation_start_height_(0) {
  if (cert_id_ > 0) {
    scoped_refptr<net::X509Certificate> cert;
    CertStore::GetInstance()->RetrieveCert(cert_id_, &cert);
    // When running with fake certificate (Chrome Frame) or Gears in offline
    // mode, we have no os certificate, so there is no cert to show. Don't
    // bother showing the cert info link in that case.
    if (!cert.get() || !cert->os_cert_handle())
      cert_id_ = 0;
  }
  LayoutSections();
}

PageInfoBubbleView::~PageInfoBubbleView() {
}

void PageInfoBubbleView::ShowCertDialog() {
  ShowCertificateViewerByID(parent_window_, cert_id_);
}

void PageInfoBubbleView::LayoutSections() {
  // Remove all the existing sections.
  RemoveAllChildViews(true);

  views::GridLayout* layout = new views::GridLayout(this);
  SetLayoutManager(layout);
  views::ColumnSet* columns = layout->AddColumnSet(0);
  columns->AddColumn(views::GridLayout::FILL,  // Horizontal resize.
                     views::GridLayout::FILL,  // Vertical resize.
                     1,   // Resize weight.
                     views::GridLayout::USE_PREF,  // Size type.
                     0,   // Ignored for USE_PREF.
                     0);  // Minimum size.
  // Add a column set for aligning the text when it has no icons (such as the
  // help center link).
  columns = layout->AddColumnSet(1);
  columns->AddPaddingColumn(
      0, kHGapToBorder + kIconHorizontalOffset + kHGapImageToDescription);
  columns->AddColumn(views::GridLayout::LEADING,  // Horizontal resize.
                     views::GridLayout::FILL,     // Vertical resize.
                     1,   // Resize weight.
                     views::GridLayout::USE_PREF,  // Size type.
                     0,   // Ignored for USE_PREF.
                     0);  // Minimum size.

  int count = model_.GetSectionCount();
  for (int i = 0; i < count; ++i) {
    PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
    layout->StartRow(0, 0);
    const SkBitmap* icon = model_.GetIconImage(info.icon_id);
    layout->AddView(new Section(this, info, icon, cert_id_ > 0));

    // Add separator after all sections.
    layout->AddPaddingRow(0, kPaddingAboveSeparator);
    layout->StartRow(0, 0);
    layout->AddView(new views::Separator());
    layout->AddPaddingRow(0, kPaddingBelowSeparator);
  }

  // Then add the help center link at the bottom.
  layout->StartRow(0, 1);
  help_center_link_ = new views::Link(
      UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGE_INFO_HELP_CENTER_LINK)));
  help_center_link_->SetController(this);
  layout->AddView(help_center_link_);
}

gfx::Size PageInfoBubbleView::GetPreferredSize() {
  gfx::Size size(views::Window::GetLocalizedContentsSize(
      IDS_PAGEINFOBUBBLE_WIDTH_CHARS, IDS_PAGEINFOBUBBLE_HEIGHT_LINES));
  size.set_height(0);

  int count = model_.GetSectionCount();
  for (int i = 0; i < count; ++i) {
    PageInfoModel::SectionInfo info = model_.GetSectionInfo(i);
    const SkBitmap* icon = model_.GetIconImage(info.icon_id);
    Section section(this, info, icon, cert_id_ > 0);
    size.Enlarge(0, section.GetHeightForWidth(size.width()));
  }

  // Calculate how much space the separators take up (with padding).
  views::Separator separator;
  gfx::Size separator_size = separator.GetPreferredSize();
  gfx::Size separator_plus_padding(0, separator_size.height() +
                                      kPaddingAboveSeparator +
                                      kPaddingBelowSeparator);

  // Account for the separators and padding within sections.
  size.Enlarge(0, (count - 1) * separator_plus_padding.height());

  // Account for the Help Center link and the separator above it.
  gfx::Size link_size = help_center_link_->GetPreferredSize();
  size.Enlarge(0, separator_plus_padding.height() +
                  link_size.height());

  if (!resize_animation_.is_animating())
    return size;

  // We are animating from animation_start_height_ to size.
  int target_height = animation_start_height_ + static_cast<int>(
      (size.height() - animation_start_height_) *
      resize_animation_.GetCurrentValue());
  size.set_height(target_height);
  return size;
}

void PageInfoBubbleView::ModelChanged() {
  animation_start_height_ = bounds().height();
  LayoutSections();
  resize_animation_.SetSlideDuration(kPageInfoSlideDuration);
  resize_animation_.Show();
}

void PageInfoBubbleView::LinkActivated(views::Link* source, int event_flags) {
  // We want to make sure the info bubble closes once the link is activated.  So
  // we close it explicitly rather than relying on a side-effect of opening a
  // new tab (see http://crosbug.com/10186).
  info_bubble_->Close();

  GURL url = google_util::AppendGoogleLocaleParam(
      GURL(chrome::kPageInfoHelpCenterURL));
  Browser* browser = BrowserList::GetLastActive();
  browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
}

void PageInfoBubbleView::AnimationEnded(const ui::Animation* animation) {
  info_bubble_->SizeToContents();
}

void PageInfoBubbleView::AnimationProgressed(const ui::Animation* animation) {
  info_bubble_->SizeToContents();
}

////////////////////////////////////////////////////////////////////////////////
// Section

Section::Section(PageInfoBubbleView* owner,
                 const PageInfoModel::SectionInfo& section_info,
                 const SkBitmap* state_icon,
                 bool show_cert)
    : owner_(owner),
      info_(section_info),
      status_image_(NULL),
      link_(NULL) {
  if (state_icon) {
    status_image_ = new views::ImageView();
    status_image_->SetImage(*state_icon);
    AddChildView(status_image_);
  }

  headline_label_ = new views::Label(UTF16ToWideHack(info_.headline));
  headline_label_->SetFont(
      headline_label_->font().DeriveFont(0, gfx::Font::BOLD));
  headline_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
  AddChildView(headline_label_);

  description_label_ = new views::Label(UTF16ToWideHack(info_.description));
  description_label_->SetMultiLine(true);
  description_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
  // Allow linebreaking in the middle of words if necessary, so that extremely
  // long hostnames (longer than one line) will still be completely shown.
  description_label_->SetAllowCharacterBreak(true);
  AddChildView(description_label_);

  if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && show_cert) {
    link_ = new views::Link(
        UTF16ToWide(l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)));
    link_->SetController(this);
    AddChildView(link_);
  }
}

Section::~Section() {
}

int Section::GetHeightForWidth(int width) {
  return LayoutItems(true, width).height();
}

void Section::Layout() {
  LayoutItems(false, width());
}

void Section::LinkActivated(views::Link* source, int event_flags) {
  owner_->ShowCertDialog();
}

gfx::Size Section::LayoutItems(bool compute_bounds_only, int width) {
  int x = kHGapToBorder;
  int y = kVGapToImage;

  // Layout the image, head-line and description.
  gfx::Size size;
  if (status_image_) {
      size = status_image_->GetPreferredSize();
    if (!compute_bounds_only)
      status_image_->SetBounds(x, y, size.width(), size.height());
  }
  int image_height = y + size.height();
  x += size.width() + kHGapImageToDescription;
  int w = width - x - kTextPaddingRight;
  y = kVGapToHeadline;
  if (!headline_label_->GetText().empty()) {
    size = headline_label_->GetPreferredSize();
    if (!compute_bounds_only)
      headline_label_->SetBounds(x, y, w > 0 ? w : 0, size.height());
    y += size.height();
  } else {
    if (!compute_bounds_only)
      headline_label_->SetBounds(x, y, 0, 0);
  }
  if (w > 0) {
    int height = description_label_->GetHeightForWidth(w);
    if (!compute_bounds_only)
      description_label_->SetBounds(x, y, w, height);
    y += height;
  } else {
    if (!compute_bounds_only)
      description_label_->SetBounds(x, y, 0, 0);
  }
  if (info_.type == PageInfoModel::SECTION_INFO_IDENTITY && link_) {
    size = link_->GetPreferredSize();
    if (!compute_bounds_only)
      link_->SetBounds(x, y, size.width(), size.height());
    y += size.height();
  }

  // Make sure the image is not truncated if the text doesn't contain much.
  y = std::max(y, image_height);
  return gfx::Size(width, y);
}

namespace browser {

void ShowPageInfoBubble(gfx::NativeWindow parent,
                        Profile* profile,
                        const GURL& url,
                        const NavigationEntry::SSLStatus& ssl,
                        bool show_history) {
  // Find where to point the bubble at.
  BrowserView* browser_view =
      BrowserView::GetBrowserViewForNativeWindow(parent);
  gfx::Point point;
  if (base::i18n::IsRTL()) {
    int width = browser_view->toolbar()->location_bar()->width();
    point = gfx::Point(width - kIconHorizontalOffset, 0);
  }
  point.Offset(0, kIconVerticalOffset);
  views::View::ConvertPointToScreen(browser_view->toolbar()->location_bar(),
                                    &point);
  gfx::Rect bounds = browser_view->toolbar()->location_bar()->bounds();
  bounds.set_origin(point);
  bounds.set_width(kIconHorizontalOffset);

  // Show the bubble.
  PageInfoBubbleView* page_info_bubble =
      new PageInfoBubbleView(parent, profile, url, ssl, show_history);
  InfoBubble* info_bubble =
      InfoBubble::Show(browser_view->GetWidget(), bounds,
                       BubbleBorder::TOP_LEFT,
                       page_info_bubble, page_info_bubble);
  page_info_bubble->set_info_bubble(info_bubble);
}

}
<|endoftext|>


<commit_before>// 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 "chrome/browser/ui/webui/downloads_dom_handler.h"

#include <algorithm>
#include <functional>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/singleton.h"
#include "base/string_piece.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/fileicon_source.h"
#include "chrome/browser/ui/webui/fileicon_source_cros.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/browser/download/download_item.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/user_metrics.h"
#include "grit/generated_resources.h"
#include "ui/gfx/image/image.h"

namespace {

// Maximum number of downloads to show. TODO(glen): Remove this and instead
// stuff the downloads down the pipe slowly.
static const int kMaxDownloads = 150;

// Sort DownloadItems into descending order by their start time.
class DownloadItemSorter : public std::binary_function<DownloadItem*,
                                                       DownloadItem*,
                                                       bool> {
 public:
  bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) {
    return lhs->start_time() > rhs->start_time();
  }
};

}  // namespace

class DownloadsDOMHandler::OriginalDownloadManagerObserver
    : public DownloadManager::Observer {
 public:
  explicit OriginalDownloadManagerObserver(
      DownloadManager::Observer* observer,
      Profile* original_profile)
      : observer_(observer) {
    original_profile_download_manager_ = original_profile->GetDownloadManager();
    original_profile_download_manager_->AddObserver(this);
  }

  virtual ~OriginalDownloadManagerObserver() {
    if (original_profile_download_manager_)
      original_profile_download_manager_->RemoveObserver(this);
  }

  // Observer interface.
  virtual void ModelChanged() {
    observer_->ModelChanged();
  }

  virtual void ManagerGoingDown() {
    original_profile_download_manager_ = NULL;
  }

 private:
  // The DownloadsDOMHandler for the off-the-record profile.
  DownloadManager::Observer* observer_;

  // The original profile's download manager.
  DownloadManager* original_profile_download_manager_;
};

DownloadsDOMHandler::DownloadsDOMHandler(DownloadManager* dlm)
    : search_text_(),
      download_manager_(dlm),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
  // Create our fileicon data source.
  Profile::FromBrowserContext(dlm->browser_context())->
      GetChromeURLDataManager()->AddDataSource(
#if defined(OS_CHROMEOS)
      new FileIconSourceCros());
#else
      new FileIconSource());
#endif  // OS_CHROMEOS
}

DownloadsDOMHandler::~DownloadsDOMHandler() {
  ClearDownloadItems();
  download_manager_->RemoveObserver(this);
}

// DownloadsDOMHandler, public: -----------------------------------------------

void DownloadsDOMHandler::Init() {
  download_manager_->AddObserver(this);

  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  Profile* original_profile = profile->GetOriginalProfile();
  if (original_profile != profile) {
    original_download_manager_observer_.reset(
        new OriginalDownloadManagerObserver(this, original_profile));
  }
}

void DownloadsDOMHandler::RegisterMessages() {
  web_ui_->RegisterMessageCallback("getDownloads",
      NewCallback(this, &DownloadsDOMHandler::HandleGetDownloads));
  web_ui_->RegisterMessageCallback("openFile",
      NewCallback(this, &DownloadsDOMHandler::HandleOpenFile));

  web_ui_->RegisterMessageCallback("drag",
      NewCallback(this, &DownloadsDOMHandler::HandleDrag));

  web_ui_->RegisterMessageCallback("saveDangerous",
      NewCallback(this, &DownloadsDOMHandler::HandleSaveDangerous));
  web_ui_->RegisterMessageCallback("discardDangerous",
      NewCallback(this, &DownloadsDOMHandler::HandleDiscardDangerous));
  web_ui_->RegisterMessageCallback("show",
      NewCallback(this, &DownloadsDOMHandler::HandleShow));
  web_ui_->RegisterMessageCallback("togglepause",
      NewCallback(this, &DownloadsDOMHandler::HandlePause));
  web_ui_->RegisterMessageCallback("resume",
      NewCallback(this, &DownloadsDOMHandler::HandlePause));
  web_ui_->RegisterMessageCallback("remove",
      NewCallback(this, &DownloadsDOMHandler::HandleRemove));
  web_ui_->RegisterMessageCallback("cancel",
      NewCallback(this, &DownloadsDOMHandler::HandleCancel));
  web_ui_->RegisterMessageCallback("clearAll",
      NewCallback(this, &DownloadsDOMHandler::HandleClearAll));
  web_ui_->RegisterMessageCallback("openDownloadsFolder",
      NewCallback(this, &DownloadsDOMHandler::HandleOpenDownloadsFolder));
}

void DownloadsDOMHandler::OnDownloadUpdated(DownloadItem* download) {
  // Get the id for the download. Our downloads are sorted latest to first,
  // and the id is the index into that list. We should be careful of sync
  // errors between the UI and the download_items_ list (we may wish to use
  // something other than 'id').
  OrderedDownloads::iterator it = find(download_items_.begin(),
                                       download_items_.end(),
                                       download);
  if (it == download_items_.end())
    return;

  if (download->state() == DownloadItem::REMOVING) {
    (*it)->RemoveObserver(this);
    *it = NULL;
    // A later ModelChanged() notification will change the WebUI's
    // view of the downloads list.
    return;
  }

  const int id = static_cast<int>(it - download_items_.begin());

  ListValue results_value;
  results_value.Append(download_util::CreateDownloadItemValue(download, id));
  web_ui_->CallJavascriptFunction("downloadUpdated", results_value);
}

// A download has started or been deleted. Query our DownloadManager for the
// current set of downloads.
void DownloadsDOMHandler::ModelChanged() {
  ClearDownloadItems();
  download_manager_->SearchDownloads(WideToUTF16(search_text_),
                                     &download_items_);
  // If we have a parent profile, let it add its downloads to the results.
  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  if (profile->GetOriginalProfile() != profile) {
    profile->GetOriginalProfile()->GetDownloadManager()->SearchDownloads(
        WideToUTF16(search_text_), &download_items_);
  }

  sort(download_items_.begin(), download_items_.end(), DownloadItemSorter());

  // Remove any extension downloads.
  for (size_t i = 0; i < download_items_.size();) {
    if (ChromeDownloadManagerDelegate::IsExtensionDownload(download_items_[i]))
      download_items_.erase(download_items_.begin() + i);
    else
      i++;
  }

  // Add ourself to all download items as an observer.
  for (OrderedDownloads::iterator it = download_items_.begin();
       it != download_items_.end(); ++it) {
    if (static_cast<int>(it - download_items_.begin()) > kMaxDownloads)
      break;

    // TODO(rdsmith): Convert to DCHECK()s when http://crbug.com/84508 is
    // fixed.
    // We should never see anything that isn't already in the history.
    CHECK(*it);
    CHECK_NE(DownloadItem::kUninitializedHandle, (*it)->db_handle());

    (*it)->AddObserver(this);
  }

  SendCurrentDownloads();
}

void DownloadsDOMHandler::HandleGetDownloads(const ListValue* args) {
  std::wstring new_search = UTF16ToWideHack(ExtractStringValue(args));
  if (search_text_.compare(new_search) != 0) {
    search_text_ = new_search;
    ModelChanged();
  } else {
    SendCurrentDownloads();
  }

  download_manager_->CheckForHistoryFilesRemoval();
}

void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->OpenDownload();
}

void DownloadsDOMHandler::HandleDrag(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file) {
    IconManager* im = g_browser_process->icon_manager();
    gfx::Image* icon = im->LookupIcon(file->GetUserVerifiedFilePath(),
                                      IconLoader::NORMAL);
    gfx::NativeView view = web_ui_->tab_contents()->GetNativeView();
    {
      // Enable nested tasks during DnD, while |DragDownload()| blocks.
      MessageLoop::ScopedNestableTaskAllower allower(MessageLoop::current());
      download_util::DragDownload(file, icon, view);
    }
  }
}

void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->DangerousDownloadValidated();
}

void DownloadsDOMHandler::HandleDiscardDangerous(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
}

void DownloadsDOMHandler::HandleShow(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->ShowDownloadInShell();
}

void DownloadsDOMHandler::HandlePause(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->TogglePause();
}

void DownloadsDOMHandler::HandleRemove(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file) {
    // TODO(rdsmith): Change to DCHECK when http://crbug.com/84508 is fixed.
    CHECK_NE(DownloadItem::kUninitializedHandle, file->db_handle());
    file->Remove();
  }
}

void DownloadsDOMHandler::HandleCancel(const ListValue* args) {
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Cancel(true);
}

void DownloadsDOMHandler::HandleClearAll(const ListValue* args) {
  download_manager_->RemoveAllDownloads();

  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  // If this is an incognito downloader, clear All should clear main download
  // manager as well.
  if (profile->GetOriginalProfile() != profile)
    profile->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
}

void DownloadsDOMHandler::HandleOpenDownloadsFolder(const ListValue* args) {
  FilePath path = DownloadPrefs::FromDownloadManager(download_manager_)->
      download_path();

#if defined(OS_MACOSX)
  // Must be called from the UI thread on Mac.
  platform_util::OpenItem(path);
#else
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&platform_util::OpenItem, path));
#endif
}

// DownloadsDOMHandler, private: ----------------------------------------------

void DownloadsDOMHandler::SendCurrentDownloads() {
  ListValue results_value;
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    int index = static_cast<int>(it - download_items_.begin());
    if (index > kMaxDownloads)
      break;
    if (!*it)
      continue;
    results_value.Append(download_util::CreateDownloadItemValue(*it, index));
  }

  web_ui_->CallJavascriptFunction("downloadsList", results_value);
}

void DownloadsDOMHandler::ClearDownloadItems() {
  // Clear out old state and remove self as observer for each download.
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    if (!*it)
      continue;
    (*it)->RemoveObserver(this);
  }
  download_items_.clear();
}

DownloadItem* DownloadsDOMHandler::GetDownloadById(int id) {
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    if (static_cast<int>(it - download_items_.begin() == id)) {
      return (*it);
    }
  }

  return NULL;
}

DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const ListValue* args) {
  int id;
  if (ExtractIntegerValue(args, &id)) {
    return GetDownloadById(id);
  }
  return NULL;
}
<commit_msg>UMA in DownloadsDOMHandler<commit_after>// 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 "chrome/browser/ui/webui/downloads_dom_handler.h"

#include <algorithm>
#include <functional>

#include "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
#include "base/string_piece.h"
#include "base/threading/thread.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_history.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_util.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/fileicon_source.h"
#include "chrome/browser/ui/webui/fileicon_source_cros.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/url_constants.h"
#include "content/browser/browser_thread.h"
#include "content/browser/download/download_item.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/user_metrics.h"
#include "grit/generated_resources.h"
#include "ui/gfx/image/image.h"

namespace {

// Maximum number of downloads to show. TODO(glen): Remove this and instead
// stuff the downloads down the pipe slowly.
static const int kMaxDownloads = 150;

// Sort DownloadItems into descending order by their start time.
class DownloadItemSorter : public std::binary_function<DownloadItem*,
                                                       DownloadItem*,
                                                       bool> {
 public:
  bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) {
    return lhs->start_time() > rhs->start_time();
  }
};

enum DownloadsDOMEvent {
  DOWNLOADS_DOM_EVENT_GET_DOWNLOADS = 0,
  DOWNLOADS_DOM_EVENT_OPEN_FILE = 1,
  DOWNLOADS_DOM_EVENT_DRAG = 2,
  DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS = 3,
  DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS = 4,
  DOWNLOADS_DOM_EVENT_SHOW = 5,
  DOWNLOADS_DOM_EVENT_PAUSE = 6,
  DOWNLOADS_DOM_EVENT_REMOVE = 7,
  DOWNLOADS_DOM_EVENT_CANCEL = 8,
  DOWNLOADS_DOM_EVENT_CLEAR_ALL = 9,
  DOWNLOADS_DOM_EVENT_OPEN_FOLDER = 10,
  DOWNLOADS_DOM_EVENT_MAX
};

void CountDownloadsDOMEvents(DownloadsDOMEvent event) {
  UMA_HISTOGRAM_ENUMERATION("Download.DOMEvent",
                            event,
                            DOWNLOADS_DOM_EVENT_MAX);
}

}  // namespace

class DownloadsDOMHandler::OriginalDownloadManagerObserver
    : public DownloadManager::Observer {
 public:
  explicit OriginalDownloadManagerObserver(
      DownloadManager::Observer* observer,
      Profile* original_profile)
      : observer_(observer) {
    original_profile_download_manager_ = original_profile->GetDownloadManager();
    original_profile_download_manager_->AddObserver(this);
  }

  virtual ~OriginalDownloadManagerObserver() {
    if (original_profile_download_manager_)
      original_profile_download_manager_->RemoveObserver(this);
  }

  // Observer interface.
  virtual void ModelChanged() {
    observer_->ModelChanged();
  }

  virtual void ManagerGoingDown() {
    original_profile_download_manager_ = NULL;
  }

 private:
  // The DownloadsDOMHandler for the off-the-record profile.
  DownloadManager::Observer* observer_;

  // The original profile's download manager.
  DownloadManager* original_profile_download_manager_;
};

DownloadsDOMHandler::DownloadsDOMHandler(DownloadManager* dlm)
    : search_text_(),
      download_manager_(dlm),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
  // Create our fileicon data source.
  Profile::FromBrowserContext(dlm->browser_context())->
      GetChromeURLDataManager()->AddDataSource(
#if defined(OS_CHROMEOS)
      new FileIconSourceCros());
#else
      new FileIconSource());
#endif  // OS_CHROMEOS
}

DownloadsDOMHandler::~DownloadsDOMHandler() {
  ClearDownloadItems();
  download_manager_->RemoveObserver(this);
}

// DownloadsDOMHandler, public: -----------------------------------------------

void DownloadsDOMHandler::Init() {
  download_manager_->AddObserver(this);

  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  Profile* original_profile = profile->GetOriginalProfile();
  if (original_profile != profile) {
    original_download_manager_observer_.reset(
        new OriginalDownloadManagerObserver(this, original_profile));
  }
}

void DownloadsDOMHandler::RegisterMessages() {
  web_ui_->RegisterMessageCallback("getDownloads",
      NewCallback(this, &DownloadsDOMHandler::HandleGetDownloads));
  web_ui_->RegisterMessageCallback("openFile",
      NewCallback(this, &DownloadsDOMHandler::HandleOpenFile));

  web_ui_->RegisterMessageCallback("drag",
      NewCallback(this, &DownloadsDOMHandler::HandleDrag));

  web_ui_->RegisterMessageCallback("saveDangerous",
      NewCallback(this, &DownloadsDOMHandler::HandleSaveDangerous));
  web_ui_->RegisterMessageCallback("discardDangerous",
      NewCallback(this, &DownloadsDOMHandler::HandleDiscardDangerous));
  web_ui_->RegisterMessageCallback("show",
      NewCallback(this, &DownloadsDOMHandler::HandleShow));
  web_ui_->RegisterMessageCallback("togglepause",
      NewCallback(this, &DownloadsDOMHandler::HandlePause));
  web_ui_->RegisterMessageCallback("resume",
      NewCallback(this, &DownloadsDOMHandler::HandlePause));
  web_ui_->RegisterMessageCallback("remove",
      NewCallback(this, &DownloadsDOMHandler::HandleRemove));
  web_ui_->RegisterMessageCallback("cancel",
      NewCallback(this, &DownloadsDOMHandler::HandleCancel));
  web_ui_->RegisterMessageCallback("clearAll",
      NewCallback(this, &DownloadsDOMHandler::HandleClearAll));
  web_ui_->RegisterMessageCallback("openDownloadsFolder",
      NewCallback(this, &DownloadsDOMHandler::HandleOpenDownloadsFolder));
}

void DownloadsDOMHandler::OnDownloadUpdated(DownloadItem* download) {
  // Get the id for the download. Our downloads are sorted latest to first,
  // and the id is the index into that list. We should be careful of sync
  // errors between the UI and the download_items_ list (we may wish to use
  // something other than 'id').
  OrderedDownloads::iterator it = find(download_items_.begin(),
                                       download_items_.end(),
                                       download);
  if (it == download_items_.end())
    return;

  if (download->state() == DownloadItem::REMOVING) {
    (*it)->RemoveObserver(this);
    *it = NULL;
    // A later ModelChanged() notification will change the WebUI's
    // view of the downloads list.
    return;
  }

  const int id = static_cast<int>(it - download_items_.begin());

  ListValue results_value;
  results_value.Append(download_util::CreateDownloadItemValue(download, id));
  web_ui_->CallJavascriptFunction("downloadUpdated", results_value);
}

// A download has started or been deleted. Query our DownloadManager for the
// current set of downloads.
void DownloadsDOMHandler::ModelChanged() {
  ClearDownloadItems();
  download_manager_->SearchDownloads(WideToUTF16(search_text_),
                                     &download_items_);
  // If we have a parent profile, let it add its downloads to the results.
  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  if (profile->GetOriginalProfile() != profile) {
    profile->GetOriginalProfile()->GetDownloadManager()->SearchDownloads(
        WideToUTF16(search_text_), &download_items_);
  }

  sort(download_items_.begin(), download_items_.end(), DownloadItemSorter());

  // Remove any extension downloads.
  for (size_t i = 0; i < download_items_.size();) {
    if (ChromeDownloadManagerDelegate::IsExtensionDownload(download_items_[i]))
      download_items_.erase(download_items_.begin() + i);
    else
      i++;
  }

  // Add ourself to all download items as an observer.
  for (OrderedDownloads::iterator it = download_items_.begin();
       it != download_items_.end(); ++it) {
    if (static_cast<int>(it - download_items_.begin()) > kMaxDownloads)
      break;

    // TODO(rdsmith): Convert to DCHECK()s when http://crbug.com/84508 is
    // fixed.
    // We should never see anything that isn't already in the history.
    CHECK(*it);
    CHECK_NE(DownloadItem::kUninitializedHandle, (*it)->db_handle());

    (*it)->AddObserver(this);
  }

  SendCurrentDownloads();
}

void DownloadsDOMHandler::HandleGetDownloads(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
  std::wstring new_search = UTF16ToWideHack(ExtractStringValue(args));
  if (search_text_.compare(new_search) != 0) {
    search_text_ = new_search;
    ModelChanged();
  } else {
    SendCurrentDownloads();
  }

  download_manager_->CheckForHistoryFilesRemoval();
}

void DownloadsDOMHandler::HandleOpenFile(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FILE);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->OpenDownload();
}

void DownloadsDOMHandler::HandleDrag(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
  DownloadItem* file = GetDownloadByValue(args);
  if (file) {
    IconManager* im = g_browser_process->icon_manager();
    gfx::Image* icon = im->LookupIcon(file->GetUserVerifiedFilePath(),
                                      IconLoader::NORMAL);
    gfx::NativeView view = web_ui_->tab_contents()->GetNativeView();
    {
      // Enable nested tasks during DnD, while |DragDownload()| blocks.
      MessageLoop::ScopedNestableTaskAllower allower(MessageLoop::current());
      download_util::DragDownload(file, icon, view);
    }
  }
}

void DownloadsDOMHandler::HandleSaveDangerous(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->DangerousDownloadValidated();
}

void DownloadsDOMHandler::HandleDiscardDangerous(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
}

void DownloadsDOMHandler::HandleShow(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->ShowDownloadInShell();
}

void DownloadsDOMHandler::HandlePause(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->TogglePause();
}

void DownloadsDOMHandler::HandleRemove(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_REMOVE);
  DownloadItem* file = GetDownloadByValue(args);
  if (file) {
    // TODO(rdsmith): Change to DCHECK when http://crbug.com/84508 is fixed.
    CHECK_NE(DownloadItem::kUninitializedHandle, file->db_handle());
    file->Remove();
  }
}

void DownloadsDOMHandler::HandleCancel(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
  DownloadItem* file = GetDownloadByValue(args);
  if (file)
    file->Cancel(true);
}

void DownloadsDOMHandler::HandleClearAll(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CLEAR_ALL);
  download_manager_->RemoveAllDownloads();

  Profile* profile =
      Profile::FromBrowserContext(download_manager_->browser_context());
  // If this is an incognito downloader, clear All should clear main download
  // manager as well.
  if (profile->GetOriginalProfile() != profile)
    profile->GetOriginalProfile()->GetDownloadManager()->RemoveAllDownloads();
}

void DownloadsDOMHandler::HandleOpenDownloadsFolder(const ListValue* args) {
  CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_OPEN_FOLDER);
  FilePath path = DownloadPrefs::FromDownloadManager(download_manager_)->
      download_path();

#if defined(OS_MACOSX)
  // Must be called from the UI thread on Mac.
  platform_util::OpenItem(path);
#else
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableFunction(&platform_util::OpenItem, path));
#endif
}

// DownloadsDOMHandler, private: ----------------------------------------------

void DownloadsDOMHandler::SendCurrentDownloads() {
  ListValue results_value;
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    int index = static_cast<int>(it - download_items_.begin());
    if (index > kMaxDownloads)
      break;
    if (!*it)
      continue;
    results_value.Append(download_util::CreateDownloadItemValue(*it, index));
  }

  web_ui_->CallJavascriptFunction("downloadsList", results_value);
}

void DownloadsDOMHandler::ClearDownloadItems() {
  // Clear out old state and remove self as observer for each download.
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    if (!*it)
      continue;
    (*it)->RemoveObserver(this);
  }
  download_items_.clear();
}

DownloadItem* DownloadsDOMHandler::GetDownloadById(int id) {
  for (OrderedDownloads::iterator it = download_items_.begin();
      it != download_items_.end(); ++it) {
    if (static_cast<int>(it - download_items_.begin() == id)) {
      return (*it);
    }
  }

  return NULL;
}

DownloadItem* DownloadsDOMHandler::GetDownloadByValue(const ListValue* args) {
  int id;
  if (ExtractIntegerValue(args, &id)) {
    return GetDownloadById(id);
  }
  return NULL;
}
<|endoftext|>


<commit_before>// 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 "chrome/browser/ui/webui/extension_icon_source.h"

#include "base/callback.h"
#include "base/ref_counted_memory.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/url_constants.h"
#include "grit/theme_resources.h"
#include "googleurl/src/gurl.h"
#include "skia/ext/image_operations.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/skbitmap_operations.h"
#include "webkit/glue/image_decoder.h"

namespace {

scoped_refptr<RefCountedMemory> BitmapToMemory(SkBitmap* image) {
  std::vector<unsigned char> output;
  gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &output);

  scoped_refptr<RefCountedBytes> image_bytes(new RefCountedBytes);
  image_bytes->data.resize(output.size());
  std::copy(output.begin(), output.end(), image_bytes->data.begin());
  return image_bytes;
}

void DesaturateImage(SkBitmap* image) {
  color_utils::HSL shift = {-1, 0, 0.6};
  *image = SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
}

SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
  webkit_glue::ImageDecoder decoder;
  SkBitmap* decoded = new SkBitmap();
  *decoded = decoder.Decode(data, size);
  return decoded;
}

SkBitmap* LoadImageByResourceId(int resource_id) {
  std::string contents = ResourceBundle::GetSharedInstance()
      .GetRawDataResource(resource_id).as_string();

  // Convert and return it.
  const unsigned char* data =
      reinterpret_cast<const unsigned char*>(contents.data());
  return ToBitmap(data, contents.length());
}

}  // namespace

ExtensionIconSource::ExtensionIconSource(Profile* profile)
    : DataSource(chrome::kChromeUIExtensionIconHost, MessageLoop::current()),
      profile_(profile),
      next_tracker_id_(0) {
  tracker_.reset(new ImageLoadingTracker(this));
}

struct ExtensionIconSource::ExtensionIconRequest {
  int request_id;
  const Extension* extension;
  bool grayscale;
  Extension::Icons size;
  ExtensionIconSet::MatchType match;
};

ExtensionIconSource::~ExtensionIconSource() {
  // Clean up all the temporary data we're holding for requests.
  std::map<int, ExtensionIconRequest*>::iterator i;
  for (i = request_map_.begin(); i != request_map_.end(); i++) {
    delete i->second;
    request_map_.erase(i);
  }
}

// static
GURL ExtensionIconSource::GetIconURL(const Extension* extension,
                                            Extension::Icons icon_size,
                                            ExtensionIconSet::MatchType match,
                                            bool grayscale) {
  GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
                                   chrome::kChromeUIExtensionIconURL,
                                   extension->id().c_str(),
                                   icon_size,
                                   match,
                                   grayscale ? "?grayscale=true" : ""));
  CHECK(icon_url.is_valid());
  return icon_url;
}

std::string ExtensionIconSource::GetMimeType(const std::string&) const {
  // We need to explicitly return a mime type, otherwise if the user tries to
  // drag the image they get no extension.
  return "image/png";
}

void ExtensionIconSource::StartDataRequest(const std::string& path,
                                           bool is_off_the_record,
                                           int request_id) {
  // This is where everything gets started. First, parse the request and make
  // the request data available for later.
  if (!ParseData(path, request_id)) {
    SendDefaultResponse(request_id);
    return;
  }

  ExtensionIconRequest* request = GetData(request_id);
  ExtensionResource icon =
      request->extension->GetIconResource(request->size, request->match);

  if (icon.relative_path().empty())
    LoadIconFailed(request_id);
  else
    LoadExtensionImage(icon, request_id);
}

void ExtensionIconSource::LoadIconFailed(int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  ExtensionResource icon =
      request->extension->GetIconResource(request->size, request->match);

  if (request->size == Extension::EXTENSION_ICON_BITTY)
    LoadFaviconImage(request_id);
  else
    LoadDefaultImage(request_id);
}

SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
  if (!default_app_data_.get())
    default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));

  return default_app_data_.get();
}

SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
  if (!default_extension_data_.get())
    default_extension_data_.reset(
        LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));

  return default_extension_data_.get();
}

void ExtensionIconSource::FinalizeImage(SkBitmap* image,
                                        int request_id) {
  if (GetData(request_id)->grayscale)
    DesaturateImage(image);

  ClearData(request_id);
  SendResponse(request_id, BitmapToMemory(image));
}

void ExtensionIconSource::LoadDefaultImage(int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  SkBitmap* decoded = NULL;

  if (request->extension->is_app())
    decoded = GetDefaultAppImage();
  else
    decoded = GetDefaultExtensionImage();

  *decoded = skia::ImageOperations::Resize(
      *decoded, skia::ImageOperations::RESIZE_LANCZOS3,
      request->size, request->size);

  FinalizeImage(decoded, request_id);
}

void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
                                             int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  tracker_map_[next_tracker_id_++] = request_id;
  tracker_->LoadImage(request->extension,
                      icon,
                      gfx::Size(request->size, request->size),
                      ImageLoadingTracker::DONT_CACHE);
}

void ExtensionIconSource::LoadFaviconImage(int request_id) {
  FaviconService* favicon_service =
      profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
  // Fall back to the default icons if the service isn't available.
  if (favicon_service == NULL) {
    LoadDefaultImage(request_id);
    return;
  }

  GURL favicon_url = GetData(request_id)->extension->GetFullLaunchURL();
  FaviconService::Handle handle = favicon_service->GetFaviconForURL(
      favicon_url,
      history::FAVICON,
      &cancelable_consumer_,
      NewCallback(this, &ExtensionIconSource::OnFaviconDataAvailable));
  cancelable_consumer_.SetClientData(favicon_service, handle, request_id);
}

void ExtensionIconSource::OnFaviconDataAvailable(
    FaviconService::Handle request_handle,
    history::FaviconData favicon) {
  int request_id = cancelable_consumer_.GetClientData(
      profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), request_handle);
  ExtensionIconRequest* request = GetData(request_id);

  // Fallback to the default icon if there wasn't a favicon.
  if (!favicon.is_valid()) {
    LoadDefaultImage(request_id);
    return;
  }

  if (!request->grayscale) {
    // If we don't need a grayscale image, then we can bypass FinalizeImage
    // to avoid unnecessary conversions.
    ClearData(request_id);
    SendResponse(request_id, favicon.image_data);
  } else {
    FinalizeImage(ToBitmap(favicon.image_data->front(),
                           favicon.image_data->size()), request_id);
  }
}

void ExtensionIconSource::OnImageLoaded(SkBitmap* image,
                                        const ExtensionResource& resource,
                                        int index) {
  int request_id = tracker_map_[index];
  tracker_map_.erase(tracker_map_.find(index));

  if (!image || image->empty())
    LoadIconFailed(request_id);
  else
    FinalizeImage(image, request_id);
}

bool ExtensionIconSource::ParseData(const std::string& path,
                                    int request_id) {
  // Extract the parameters from the path by lower casing and splitting.
  std::string path_lower = StringToLowerASCII(path);
  std::vector<std::string> path_parts;

  base::SplitString(path_lower, '/', &path_parts);
  if (path_lower.empty() || path_parts.size() < 3)
    return false;

  std::string size_param = path_parts.at(1);
  std::string match_param = path_parts.at(2);
  match_param = match_param.substr(0, match_param.find('?'));

  // The icon size and match types are encoded as string representations of
  // their enum values, so to get the enum back, we read the string as an int
  // and then cast to the enum.
  Extension::Icons size;
  int size_num;
  if (!base::StringToInt(size_param, &size_num))
    return false;
  size = static_cast<Extension::Icons>(size_num);

  ExtensionIconSet::MatchType match_type;
  int match_num;
  if (!base::StringToInt(match_param, &match_num))
    return false;
  match_type = static_cast<ExtensionIconSet::MatchType>(match_num);

  std::string extension_id = path_parts.at(0);
  const Extension* extension =
      profile_->GetExtensionService()->GetExtensionById(extension_id, true);
  if (!extension)
    return false;

  bool grayscale = path_lower.find("grayscale=true") != std::string::npos;

  SetData(request_id, extension, grayscale, size, match_type);

  return true;
}

void ExtensionIconSource::SendDefaultResponse(int request_id) {
  // We send back the default application icon (not resized or desaturated)
  // as the default response, like when there is no data.
  ClearData(request_id);
  SendResponse(request_id, BitmapToMemory(GetDefaultAppImage()));
}

void ExtensionIconSource::SetData(int request_id,
                                  const Extension* extension,
                                  bool grayscale,
                                  Extension::Icons size,
                                  ExtensionIconSet::MatchType match) {
  ExtensionIconRequest* request = new ExtensionIconRequest();
  request->request_id = request_id;
  request->extension = extension;
  request->grayscale = grayscale;
  request->size = size;
  request->match = match;
  request_map_[request_id] = request;
}

ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
    int request_id) {
  return request_map_[request_id];
}

void ExtensionIconSource::ClearData(int request_id) {
  std::map<int, ExtensionIconRequest*>::iterator i =
      request_map_.find(request_id);
  if (i == request_map_.end())
    return;

  delete i->second;
  request_map_.erase(i);
}
<commit_msg>Coverity: Fix an invalidated iterator case and refactor to use the STLDeleteValues helper function.<commit_after>// 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 "chrome/browser/ui/webui/extension_icon_source.h"

#include "base/callback.h"
#include "base/ref_counted_memory.h"
#include "base/stl_util-inl.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/task.h"
#include "base/threading/thread.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/url_constants.h"
#include "grit/theme_resources.h"
#include "googleurl/src/gurl.h"
#include "skia/ext/image_operations.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/skbitmap_operations.h"
#include "webkit/glue/image_decoder.h"

namespace {

scoped_refptr<RefCountedMemory> BitmapToMemory(SkBitmap* image) {
  std::vector<unsigned char> output;
  gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &output);

  scoped_refptr<RefCountedBytes> image_bytes(new RefCountedBytes);
  image_bytes->data.resize(output.size());
  std::copy(output.begin(), output.end(), image_bytes->data.begin());
  return image_bytes;
}

void DesaturateImage(SkBitmap* image) {
  color_utils::HSL shift = {-1, 0, 0.6};
  *image = SkBitmapOperations::CreateHSLShiftedBitmap(*image, shift);
}

SkBitmap* ToBitmap(const unsigned char* data, size_t size) {
  webkit_glue::ImageDecoder decoder;
  SkBitmap* decoded = new SkBitmap();
  *decoded = decoder.Decode(data, size);
  return decoded;
}

SkBitmap* LoadImageByResourceId(int resource_id) {
  std::string contents = ResourceBundle::GetSharedInstance()
      .GetRawDataResource(resource_id).as_string();

  // Convert and return it.
  const unsigned char* data =
      reinterpret_cast<const unsigned char*>(contents.data());
  return ToBitmap(data, contents.length());
}

}  // namespace

ExtensionIconSource::ExtensionIconSource(Profile* profile)
    : DataSource(chrome::kChromeUIExtensionIconHost, MessageLoop::current()),
      profile_(profile),
      next_tracker_id_(0) {
  tracker_.reset(new ImageLoadingTracker(this));
}

struct ExtensionIconSource::ExtensionIconRequest {
  int request_id;
  const Extension* extension;
  bool grayscale;
  Extension::Icons size;
  ExtensionIconSet::MatchType match;
};

ExtensionIconSource::~ExtensionIconSource() {
  // Clean up all the temporary data we're holding for requests.
  STLDeleteValues(&request_map_);
}

// static
GURL ExtensionIconSource::GetIconURL(const Extension* extension,
                                            Extension::Icons icon_size,
                                            ExtensionIconSet::MatchType match,
                                            bool grayscale) {
  GURL icon_url(base::StringPrintf("%s%s/%d/%d%s",
                                   chrome::kChromeUIExtensionIconURL,
                                   extension->id().c_str(),
                                   icon_size,
                                   match,
                                   grayscale ? "?grayscale=true" : ""));
  CHECK(icon_url.is_valid());
  return icon_url;
}

std::string ExtensionIconSource::GetMimeType(const std::string&) const {
  // We need to explicitly return a mime type, otherwise if the user tries to
  // drag the image they get no extension.
  return "image/png";
}

void ExtensionIconSource::StartDataRequest(const std::string& path,
                                           bool is_off_the_record,
                                           int request_id) {
  // This is where everything gets started. First, parse the request and make
  // the request data available for later.
  if (!ParseData(path, request_id)) {
    SendDefaultResponse(request_id);
    return;
  }

  ExtensionIconRequest* request = GetData(request_id);
  ExtensionResource icon =
      request->extension->GetIconResource(request->size, request->match);

  if (icon.relative_path().empty())
    LoadIconFailed(request_id);
  else
    LoadExtensionImage(icon, request_id);
}

void ExtensionIconSource::LoadIconFailed(int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  ExtensionResource icon =
      request->extension->GetIconResource(request->size, request->match);

  if (request->size == Extension::EXTENSION_ICON_BITTY)
    LoadFaviconImage(request_id);
  else
    LoadDefaultImage(request_id);
}

SkBitmap* ExtensionIconSource::GetDefaultAppImage() {
  if (!default_app_data_.get())
    default_app_data_.reset(LoadImageByResourceId(IDR_APP_DEFAULT_ICON));

  return default_app_data_.get();
}

SkBitmap* ExtensionIconSource::GetDefaultExtensionImage() {
  if (!default_extension_data_.get())
    default_extension_data_.reset(
        LoadImageByResourceId(IDR_EXTENSION_DEFAULT_ICON));

  return default_extension_data_.get();
}

void ExtensionIconSource::FinalizeImage(SkBitmap* image,
                                        int request_id) {
  if (GetData(request_id)->grayscale)
    DesaturateImage(image);

  ClearData(request_id);
  SendResponse(request_id, BitmapToMemory(image));
}

void ExtensionIconSource::LoadDefaultImage(int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  SkBitmap* decoded = NULL;

  if (request->extension->is_app())
    decoded = GetDefaultAppImage();
  else
    decoded = GetDefaultExtensionImage();

  *decoded = skia::ImageOperations::Resize(
      *decoded, skia::ImageOperations::RESIZE_LANCZOS3,
      request->size, request->size);

  FinalizeImage(decoded, request_id);
}

void ExtensionIconSource::LoadExtensionImage(const ExtensionResource& icon,
                                             int request_id) {
  ExtensionIconRequest* request = GetData(request_id);
  tracker_map_[next_tracker_id_++] = request_id;
  tracker_->LoadImage(request->extension,
                      icon,
                      gfx::Size(request->size, request->size),
                      ImageLoadingTracker::DONT_CACHE);
}

void ExtensionIconSource::LoadFaviconImage(int request_id) {
  FaviconService* favicon_service =
      profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
  // Fall back to the default icons if the service isn't available.
  if (favicon_service == NULL) {
    LoadDefaultImage(request_id);
    return;
  }

  GURL favicon_url = GetData(request_id)->extension->GetFullLaunchURL();
  FaviconService::Handle handle = favicon_service->GetFaviconForURL(
      favicon_url,
      history::FAVICON,
      &cancelable_consumer_,
      NewCallback(this, &ExtensionIconSource::OnFaviconDataAvailable));
  cancelable_consumer_.SetClientData(favicon_service, handle, request_id);
}

void ExtensionIconSource::OnFaviconDataAvailable(
    FaviconService::Handle request_handle,
    history::FaviconData favicon) {
  int request_id = cancelable_consumer_.GetClientData(
      profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), request_handle);
  ExtensionIconRequest* request = GetData(request_id);

  // Fallback to the default icon if there wasn't a favicon.
  if (!favicon.is_valid()) {
    LoadDefaultImage(request_id);
    return;
  }

  if (!request->grayscale) {
    // If we don't need a grayscale image, then we can bypass FinalizeImage
    // to avoid unnecessary conversions.
    ClearData(request_id);
    SendResponse(request_id, favicon.image_data);
  } else {
    FinalizeImage(ToBitmap(favicon.image_data->front(),
                           favicon.image_data->size()), request_id);
  }
}

void ExtensionIconSource::OnImageLoaded(SkBitmap* image,
                                        const ExtensionResource& resource,
                                        int index) {
  int request_id = tracker_map_[index];
  tracker_map_.erase(tracker_map_.find(index));

  if (!image || image->empty())
    LoadIconFailed(request_id);
  else
    FinalizeImage(image, request_id);
}

bool ExtensionIconSource::ParseData(const std::string& path,
                                    int request_id) {
  // Extract the parameters from the path by lower casing and splitting.
  std::string path_lower = StringToLowerASCII(path);
  std::vector<std::string> path_parts;

  base::SplitString(path_lower, '/', &path_parts);
  if (path_lower.empty() || path_parts.size() < 3)
    return false;

  std::string size_param = path_parts.at(1);
  std::string match_param = path_parts.at(2);
  match_param = match_param.substr(0, match_param.find('?'));

  // The icon size and match types are encoded as string representations of
  // their enum values, so to get the enum back, we read the string as an int
  // and then cast to the enum.
  Extension::Icons size;
  int size_num;
  if (!base::StringToInt(size_param, &size_num))
    return false;
  size = static_cast<Extension::Icons>(size_num);

  ExtensionIconSet::MatchType match_type;
  int match_num;
  if (!base::StringToInt(match_param, &match_num))
    return false;
  match_type = static_cast<ExtensionIconSet::MatchType>(match_num);

  std::string extension_id = path_parts.at(0);
  const Extension* extension =
      profile_->GetExtensionService()->GetExtensionById(extension_id, true);
  if (!extension)
    return false;

  bool grayscale = path_lower.find("grayscale=true") != std::string::npos;

  SetData(request_id, extension, grayscale, size, match_type);

  return true;
}

void ExtensionIconSource::SendDefaultResponse(int request_id) {
  // We send back the default application icon (not resized or desaturated)
  // as the default response, like when there is no data.
  ClearData(request_id);
  SendResponse(request_id, BitmapToMemory(GetDefaultAppImage()));
}

void ExtensionIconSource::SetData(int request_id,
                                  const Extension* extension,
                                  bool grayscale,
                                  Extension::Icons size,
                                  ExtensionIconSet::MatchType match) {
  ExtensionIconRequest* request = new ExtensionIconRequest();
  request->request_id = request_id;
  request->extension = extension;
  request->grayscale = grayscale;
  request->size = size;
  request->match = match;
  request_map_[request_id] = request;
}

ExtensionIconSource::ExtensionIconRequest* ExtensionIconSource::GetData(
    int request_id) {
  return request_map_[request_id];
}

void ExtensionIconSource::ClearData(int request_id) {
  std::map<int, ExtensionIconRequest*>::iterator i =
      request_map_.find(request_id);
  if (i == request_map_.end())
    return;

  delete i->second;
  request_map_.erase(i);
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  TextureFloat.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "Core/App.h"
#include "Render/RenderFacade.h"
#include "Debug/DebugFacade.h"
#include "Render/Util/RawMeshLoader.h"
#include "Render/Util/ShapeBuilder.h"
#include "Time/Clock.h"
#include "glm/mat4x4.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "shaders.h"

using namespace Oryol;
using namespace Oryol::Core;
using namespace Oryol::Render;
using namespace Oryol::Resource;
using namespace Oryol::Debug;
using namespace Oryol::Time;

class TextureFloatApp : public App {
public:
    virtual AppState::Code OnInit();
    virtual AppState::Code OnRunning();
    virtual AppState::Code OnCleanup();

private:
    glm::mat4 computeMVP(const glm::vec2& angles);

    RenderFacade* render = nullptr;
    DebugFacade* debug = nullptr;
    Resource::Id renderTarget;
    Resource::Id offscreenDrawState;
    Resource::Id copyDrawState;

    glm::mat4 view;
    glm::mat4 proj;
    float32 time = 0.0f;
    Time::TimePoint lastFrameTimePoint;
};
OryolMain(TextureFloatApp);

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnInit() {
    // setup rendering system
    this->render = RenderFacade::CreateSingle(RenderSetup::Windowed(512, 512, "Oryol Float Texture Sample"));
    this->render->AttachLoader(RawMeshLoader::Create());
    this->debug = DebugFacade::CreateSingle();

    // check required extensions
    if (!this->render->Supports(Feature::TextureFloat)) {
        o_error("ERROR: float_texture extension required!\n");
    }

    // create an offscreen float render target, same size as display,
    // configure texture sampler with point-filtering
    auto rtSetup = TextureSetup::AsRelSizeRenderTarget("rt", 1.0f, 1.0f, PixelFormat::RGBA32F, PixelFormat::None);
    rtSetup.MagFilter = TextureFilterMode::Nearest;
    rtSetup.MinFilter = TextureFilterMode::Nearest;
    this->renderTarget = this->render->CreateResource(rtSetup);

    // fullscreen mesh, we'll reuse this several times
    Id fullscreenMesh = this->render->CreateResource(MeshSetup::CreateFullScreenQuad("fsMesh"));

    // setup draw state for offscreen rendering to float render target
    Id offscreenProg = this->render->CreateResource(Shaders::Offscreen::CreateSetup());
    DrawStateSetup offscreenSetup("dsOffscreen", fullscreenMesh, offscreenProg, 0);
    this->offscreenDrawState = this->render->CreateResource(offscreenSetup);
    this->render->ReleaseResource(offscreenProg);

    // fullscreen-copy mesh, shader and draw state
    Id copyProg = this->render->CreateResource(Shaders::Copy::CreateSetup());
    this->copyDrawState = this->render->CreateResource(DrawStateSetup("dsCopy", fullscreenMesh, copyProg, 0));
    this->render->ReleaseResource(copyProg);
    this->render->ReleaseResource(fullscreenMesh);

    // setup static transform matrices
    const float32 fbWidth = this->render->GetDisplayAttrs().FramebufferWidth;
    const float32 fbHeight = this->render->GetDisplayAttrs().FramebufferHeight;
    this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 5.0f);
    this->view = glm::mat4();

    return App::OnInit();
}

//------------------------------------------------------------------------------
glm::mat4
TextureFloatApp::computeMVP(const glm::vec2& angles) {
    glm::mat4 modelTform = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -3.0f));
    modelTform = glm::rotate(modelTform, angles.x, glm::vec3(1.0f, 0.0f, 0.0f));
    modelTform = glm::rotate(modelTform, angles.y, glm::vec3(0.0f, 1.0f, 0.0f));
    return this->proj * this->view * modelTform;
}

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnRunning() {
    // render one frame
    if (this->render->BeginFrame()) {

        this->time += 1.0f / 60.0f;

        // render plasma to offscreen render target
        this->render->ApplyOffscreenRenderTarget(this->renderTarget);
        this->render->ApplyDrawState(this->offscreenDrawState);
        this->render->ApplyVariable(Shaders::Offscreen::Time, this->time);
        this->render->Draw(0);

        // copy fullscreen quad
        this->render->ApplyDefaultRenderTarget();
        this->render->ApplyDrawState(this->copyDrawState);
        this->render->ApplyVariable(Shaders::Copy::Texture, this->renderTarget);
        this->render->Draw(0);

        this->debug->DrawTextBuffer();
        this->render->EndFrame();
    }

    TimePoint curTime = Clock::Now();
    Duration frameTime = curTime - this->lastFrameTimePoint;
    this->lastFrameTimePoint = curTime;

    this->debug->PrintF("%.3fms", frameTime.AsMilliSeconds());

    // continue running or quit?
    return render->QuitRequested() ? AppState::Cleanup : AppState::Running;
}

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnCleanup() {
    // cleanup everything
    this->render->ReleaseResource(this->offscreenDrawState);
    this->render->ReleaseResource(this->copyDrawState);
    this->render->ReleaseResource(this->renderTarget);
    this->render = nullptr;
    this->debug = nullptr;
    DebugFacade::DestroySingle();
    RenderFacade::DestroySingle();
    return App::OnCleanup();
}
<commit_msg>TextureFloat sample comments<commit_after>//------------------------------------------------------------------------------
//  TextureFloat.cc
//------------------------------------------------------------------------------
#include "Pre.h"
#include "Core/App.h"
#include "Render/RenderFacade.h"
#include "Debug/DebugFacade.h"
#include "Render/Util/RawMeshLoader.h"
#include "Render/Util/ShapeBuilder.h"
#include "Time/Clock.h"
#include "glm/mat4x4.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "shaders.h"

using namespace Oryol;
using namespace Oryol::Core;
using namespace Oryol::Render;
using namespace Oryol::Resource;
using namespace Oryol::Debug;
using namespace Oryol::Time;

class TextureFloatApp : public App {
public:
    virtual AppState::Code OnInit();
    virtual AppState::Code OnRunning();
    virtual AppState::Code OnCleanup();

private:
    glm::mat4 computeMVP(const glm::vec2& angles);

    RenderFacade* render = nullptr;
    DebugFacade* debug = nullptr;
    Resource::Id renderTarget;
    Resource::Id offscreenDrawState;
    Resource::Id copyDrawState;

    glm::mat4 view;
    glm::mat4 proj;
    float32 time = 0.0f;
    Time::TimePoint lastFrameTimePoint;
};
OryolMain(TextureFloatApp);

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnInit() {
    // setup rendering system
    this->render = RenderFacade::CreateSingle(RenderSetup::Windowed(512, 512, "Oryol Float Texture Sample"));
    this->render->AttachLoader(RawMeshLoader::Create());
    this->debug = DebugFacade::CreateSingle();

    // check required extensions
    if (!this->render->Supports(Feature::TextureFloat)) {
        o_error("ERROR: float_texture extension required!\n");
    }

    // create an offscreen float render target, same size as display,
    // configure texture sampler with point-filtering
    auto rtSetup = TextureSetup::AsRelSizeRenderTarget("rt", 1.0f, 1.0f, PixelFormat::RGBA32F, PixelFormat::None);
    rtSetup.MagFilter = TextureFilterMode::Nearest;
    rtSetup.MinFilter = TextureFilterMode::Nearest;
    this->renderTarget = this->render->CreateResource(rtSetup);

    // fullscreen mesh, we'll reuse this several times
    Id fullscreenMesh = this->render->CreateResource(MeshSetup::CreateFullScreenQuad("fsMesh"));

    // setup draw state for offscreen rendering to float render target
    Id offscreenProg = this->render->CreateResource(Shaders::Offscreen::CreateSetup());
    DrawStateSetup offscreenSetup("dsOffscreen", fullscreenMesh, offscreenProg, 0);
    this->offscreenDrawState = this->render->CreateResource(offscreenSetup);
    this->render->ReleaseResource(offscreenProg);

    // fullscreen-copy mesh, shader and draw state
    Id copyProg = this->render->CreateResource(Shaders::Copy::CreateSetup());
    this->copyDrawState = this->render->CreateResource(DrawStateSetup("dsCopy", fullscreenMesh, copyProg, 0));
    this->render->ReleaseResource(copyProg);
    this->render->ReleaseResource(fullscreenMesh);

    // setup static transform matrices
    const float32 fbWidth = this->render->GetDisplayAttrs().FramebufferWidth;
    const float32 fbHeight = this->render->GetDisplayAttrs().FramebufferHeight;
    this->proj = glm::perspectiveFov(glm::radians(45.0f), fbWidth, fbHeight, 0.01f, 5.0f);
    this->view = glm::mat4();

    return App::OnInit();
}

//------------------------------------------------------------------------------
glm::mat4
TextureFloatApp::computeMVP(const glm::vec2& angles) {
    glm::mat4 modelTform = glm::translate(glm::mat4(), glm::vec3(0.0f, 0.0f, -3.0f));
    modelTform = glm::rotate(modelTform, angles.x, glm::vec3(1.0f, 0.0f, 0.0f));
    modelTform = glm::rotate(modelTform, angles.y, glm::vec3(0.0f, 1.0f, 0.0f));
    return this->proj * this->view * modelTform;
}

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnRunning() {
    // render one frame
    if (this->render->BeginFrame()) {

        this->time += 1.0f / 60.0f;

        // render plasma to offscreen render target
        this->render->ApplyOffscreenRenderTarget(this->renderTarget);
        this->render->ApplyDrawState(this->offscreenDrawState);
        this->render->ApplyVariable(Shaders::Plasma::Time, this->time);
        this->render->Draw(0);

        // copy fullscreen quad
        /*
        this->render->ApplyDefaultRenderTarget();
        this->render->ApplyDrawState(this->copyDrawState);
        this->render->ApplyVariable(Shaders::Copy::Texture, this->renderTarget);
        this->render->Draw(0);
        */

        this->debug->DrawTextBuffer();
        this->render->EndFrame();
    }

    TimePoint curTime = Clock::Now();
    Duration frameTime = curTime - this->lastFrameTimePoint;
    this->lastFrameTimePoint = curTime;

    this->debug->PrintF("%.3fms", frameTime.AsMilliSeconds());

    // continue running or quit?
    return render->QuitRequested() ? AppState::Cleanup : AppState::Running;
}

//------------------------------------------------------------------------------
AppState::Code
TextureFloatApp::OnCleanup() {
    // cleanup everything
    this->render->ReleaseResource(this->offscreenDrawState);
    this->render->ReleaseResource(this->copyDrawState);
    this->render->ReleaseResource(this->renderTarget);
    this->render = nullptr;
    this->debug = nullptr;
    DebugFacade::DestroySingle();
    RenderFacade::DestroySingle();
    return App::OnCleanup();
}
<|endoftext|>


<commit_before>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2018 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "AboutDialog.hpp"

#include <QGraphicsDropShadowEffect>

#include "Application.hpp"

namespace Sn
{
AboutDialog::AboutDialog(QWidget* parent) :
	QDialog(parent)
{
	setObjectName(QLatin1String("about-dialog"));

	setAttribute(Qt::WA_DeleteOnClose);
	setWindowTitle(tr("About Sielo"));
	setWindowIcon(Application::getAppIcon("ic_sielo"));
	resize(636, 432);
	setupUI();
	setupAbout();
	setupPartners();
	updateLabels();

	if (m_images.count() == 1) {
		m_previous->setEnabled(false);
		m_next->setEnabled(false);
	}

	connect(m_previous, &QPushButton::clicked, this, &AboutDialog::previous);
	connect(m_next, &QPushButton::clicked, this, &AboutDialog::next);
}

AboutDialog::~AboutDialog()
{
	// Empty
}

void AboutDialog::previous()
{
	if (m_currentIndex == 0)
		return;

	m_next->setText(tr("Next"));
	m_previous->setText(tr("Previous"));

	--m_currentIndex;
	updateLabels();
	m_next->setEnabled(true);

	if (m_currentIndex == 0) {
		m_previous->setEnabled(false);
		m_next->setText(tr("Our partners"));
	}
}

void AboutDialog::next()
{
	if (m_currentIndex == m_images.count() - 1)
		return;

	m_next->setText(tr("Next"));
	m_previous->setText(tr("Previous"));

	++m_currentIndex;
	updateLabels();
	m_previous->setEnabled(true);

	if (m_currentIndex == m_images.count() - 1)
		m_next->setEnabled(false);

	if (m_currentIndex == 1)
		m_previous->setText(tr("What's Sielo"));
}

void AboutDialog::setupUI()
{
	m_layout = new QVBoxLayout(this);
	m_buttonsLayout = new QHBoxLayout();

	m_image = new QLabel(this);
	m_image->setAlignment(Qt::AlignCenter);
	m_image->setObjectName(QLatin1String("partners-image"));

	m_desc = new QLabel(this);
	m_desc->setWordWrap(true);
	m_desc->setObjectName(QLatin1String("partners-description"));

	m_spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
	m_previous = new QPushButton(tr("Previous"), this);
	m_next = new QPushButton(tr("Our partners"), this);

	m_buttonsLayout->addItem(m_spacer);
	m_buttonsLayout->addWidget(m_previous);
	m_buttonsLayout->addWidget(m_next);

	m_layout->addWidget(m_image);
	m_layout->addWidget(m_desc);
	m_layout->addLayout(m_buttonsLayout);

	QGraphicsDropShadowEffect* effect{new QGraphicsDropShadowEffect(this)};
	effect->setBlurRadius(6);
	effect->setOffset(0, 2);
	effect->setColor(QColor(0, 0, 0));
	m_image->setGraphicsEffect(effect);
}

void AboutDialog::setupAbout()
{
	m_images << ":icons/other/aboutsielo.png";

	QString aboutSielo = tr("
Sielo (version: %1, WebEngine: %2)

"
		"Sielo is a powerful web browser with a unique concept: tabs spaces."
		"Sielo has many contribution and I would like to give special thanks
"
		"to Hotaru for always being here, Echostorm for his awesome contribution,
"
		"Kaktus for the design and all other contributors!
"
		"
Copyright © 2018 Victor DENIS
"
		"admin@feldrise.com
").arg(Application::currentVersion).arg(qVersion());

	m_descs << aboutSielo;
}

void AboutDialog::setupPartners()
{
	m_images << ":data/partners/bs.jpg"
		<< ":data/partners/lmdpc.jpg";

	QString bsDesc = tr("Bit-Studio.com

"
		"Created in 2011, this concept aims to promote promising IT "
		"projects in order to help them take off.
"
		"After a few years of operation, the project was put on hold. "
		"It was too much work for one man. In 2018, a team was formed "
		"and Bit-Studio was reborn from its ashes, stronger than ever.
"
		"From then on, discover regularly, in preview, the mastodons of "
		"tomorrow on Bit-Studio.com.");

	QString lmdpcDesc = tr("Le Monde Du PC

"
		"\"Le Monde Du PC\", put online in September 2017, is a site "
		"that aims to share our knowledge so that, for example, some "
		"subjects in computing are not only understood by those who "
		"master them. Today, the team is complete and a 2.0 version will "
		"normally be released in September.");

	m_descs << bsDesc
		<< lmdpcDesc;
}

void AboutDialog::updateLabels()
{
	m_image->setPixmap(QPixmap(m_images[m_currentIndex]));
	m_desc->setText(m_descs[m_currentIndex]);
}
}
<commit_msg>[Fix] About Dialog button<commit_after>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2018 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "AboutDialog.hpp"

#include <QGraphicsDropShadowEffect>

#include "Application.hpp"

namespace Sn
{
AboutDialog::AboutDialog(QWidget* parent) :
	QDialog(parent)
{
	setObjectName(QLatin1String("about-dialog"));

	setAttribute(Qt::WA_DeleteOnClose);
	setWindowTitle(tr("About Sielo"));
	setWindowIcon(Application::getAppIcon("ic_sielo"));
	resize(636, 432);
	setupUI();
	setupAbout();
	setupPartners();
	updateLabels();

	if (m_images.count() == 1) {
		m_previous->setEnabled(false);
		m_next->setEnabled(false);
	}

	connect(m_previous, &QPushButton::clicked, this, &AboutDialog::previous);
	connect(m_next, &QPushButton::clicked, this, &AboutDialog::next);
}

AboutDialog::~AboutDialog()
{
	// Empty
}

void AboutDialog::previous()
{
	if (m_currentIndex == 0)
		return;

	m_next->setText(tr("Next"));
	m_previous->setText(tr("Previous"));

	--m_currentIndex;
	updateLabels();
	m_next->setEnabled(true);

	if (m_currentIndex == 0) {
		m_previous->setEnabled(false);
		m_next->setText(tr("Our partners"));
	}
	if (m_currentIndex == 1)
		m_previous->setText(tr("What's Sielo"));
}

void AboutDialog::next()
{
	if (m_currentIndex == m_images.count() - 1)
		return;

	m_next->setText(tr("Next"));
	m_previous->setText(tr("Previous"));

	++m_currentIndex;
	updateLabels();
	m_previous->setEnabled(true);

	if (m_currentIndex == m_images.count() - 1)
		m_next->setEnabled(false);

	if (m_currentIndex == 1)
		m_previous->setText(tr("What's Sielo"));
}

void AboutDialog::setupUI()
{
	m_layout = new QVBoxLayout(this);
	m_buttonsLayout = new QHBoxLayout();

	m_image = new QLabel(this);
	m_image->setAlignment(Qt::AlignCenter);
	m_image->setObjectName(QLatin1String("partners-image"));

	m_desc = new QLabel(this);
	m_desc->setWordWrap(true);
	m_desc->setObjectName(QLatin1String("partners-description"));

	m_spacer = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum);
	m_previous = new QPushButton(tr("Previous"), this);
	m_next = new QPushButton(tr("Our partners"), this);

	m_buttonsLayout->addItem(m_spacer);
	m_buttonsLayout->addWidget(m_previous);
	m_buttonsLayout->addWidget(m_next);

	m_layout->addWidget(m_image);
	m_layout->addWidget(m_desc);
	m_layout->addLayout(m_buttonsLayout);

	QGraphicsDropShadowEffect* effect{new QGraphicsDropShadowEffect(this)};
	effect->setBlurRadius(6);
	effect->setOffset(0, 2);
	effect->setColor(QColor(0, 0, 0));
	m_image->setGraphicsEffect(effect);
}

void AboutDialog::setupAbout()
{
	m_images << ":icons/other/aboutsielo.png";

	QString aboutSielo = tr("
Sielo (version: %1, WebEngine: %2)

"
		"Sielo is a powerful web browser with a unique concept: tabs spaces."
		"Sielo has many contribution and I would like to give special thanks
"
		"to Hotaru for always being here, Echostorm for his awesome contribution,
"
		"Kaktus for the design and all other contributors!
"
		"
Copyright © 2018 Victor DENIS
"
		"admin@feldrise.com
").arg(Application::currentVersion).arg(qVersion());

	m_descs << aboutSielo;
}

void AboutDialog::setupPartners()
{
	m_images << ":data/partners/bs.jpg"
		<< ":data/partners/lmdpc.jpg";

	QString bsDesc = tr("Bit-Studio.com

"
		"Created in 2011, this concept aims to promote promising IT "
		"projects in order to help them take off.
"
		"After a few years of operation, the project was put on hold. "
		"It was too much work for one man. In 2018, a team was formed "
		"and Bit-Studio was reborn from its ashes, stronger than ever.
"
		"From then on, discover regularly, in preview, the mastodons of "
		"tomorrow on Bit-Studio.com.");

	QString lmdpcDesc = tr("Le Monde Du PC

"
		"\"Le Monde Du PC\", put online in September 2017, is a site "
		"that aims to share our knowledge so that, for example, some "
		"subjects in computing are not only understood by those who "
		"master them. Today, the team is complete and a 2.0 version will "
		"normally be released in September.");

	m_descs << bsDesc
		<< lmdpcDesc;
}

void AboutDialog::updateLabels()
{
	m_image->setPixmap(QPixmap(m_images[m_currentIndex]));
	m_desc->setText(m_descs[m_currentIndex]);
}
}
<|endoftext|>