<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/gtk/status_bubble_gtk.h"

#include <gtk/gtk.h>

#include <algorithm>

#include "base/i18n/rtl.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/gtk/gtk_theme_provider.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/rounded_window.h"
#include "chrome/browser/ui/gtk/slide_animator_gtk.h"
#include "chrome/common/notification_service.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/text/text_elider.h"

namespace {

// Inner padding between the border and the text label.
const int kInternalTopBottomPadding = 1;
const int kInternalLeftRightPadding = 2;

// The radius of the edges of our bubble.
const int kCornerSize = 3;

// Milliseconds before we hide the status bubble widget when you mouseout.
const int kHideDelay = 250;

// How close the mouse can get to the infobubble before it starts sliding
// off-screen.
const int kMousePadding = 20;

}  // namespace

StatusBubbleGtk::StatusBubbleGtk(Profile* profile)
    : theme_provider_(GtkThemeProvider::GetFrom(profile)),
      padding_(NULL),
      label_(NULL),
      flip_horizontally_(false),
      y_offset_(0),
      download_shelf_is_visible_(false),
      last_mouse_location_(0, 0),
      last_mouse_left_content_(false),
      ignore_next_left_content_(false) {
  InitWidgets();

  theme_provider_->InitThemesFor(this);
  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());
}

StatusBubbleGtk::~StatusBubbleGtk() {
  container_.Destroy();
}

void StatusBubbleGtk::SetStatus(const string16& status_text_wide) {
  std::string status_text = UTF16ToUTF8(status_text_wide);
  if (status_text_ == status_text)
    return;

  status_text_ = status_text;
  if (!status_text_.empty())
    SetStatusTextTo(status_text_);
  else if (!url_text_.empty())
    SetStatusTextTo(url_text_);
  else
    SetStatusTextTo(std::string());
}

void StatusBubbleGtk::SetURL(const GURL& url, const string16& languages) {
  url_ = url;
  languages_ = languages;

  // If we want to clear a displayed URL but there is a status still to
  // display, display that status instead.
  if (url.is_empty() && !status_text_.empty()) {
    url_text_ = std::string();
    SetStatusTextTo(status_text_);
    return;
  }

  SetStatusTextToURL();
}

void StatusBubbleGtk::SetStatusTextToURL() {
  GtkWidget* parent = gtk_widget_get_parent(container_.get());

  // It appears that parent can be NULL (probably only during shutdown).
  if (!parent || !GTK_WIDGET_REALIZED(parent))
    return;

  int desired_width = parent->allocation.width;
  if (!expanded()) {
    expand_timer_.Stop();
    expand_timer_.Start(base::TimeDelta::FromMilliseconds(kExpandHoverDelay),
                        this, &StatusBubbleGtk::ExpandURL);
    // When not expanded, we limit the size to one third the browser's
    // width.
    desired_width /= 3;
  }

  // TODO(tc): We don't actually use gfx::Font as the font in the status
  // bubble.  We should extend ui::ElideUrl to take some sort of pango font.
  url_text_ = UTF16ToUTF8(ui::ElideUrl(url_, gfx::Font(), desired_width,
                          UTF16ToWideHack(languages_)));
  SetStatusTextTo(url_text_);
}

void StatusBubbleGtk::Show() {
  // If we were going to hide, stop.
  hide_timer_.Stop();

  gtk_widget_show_all(container_.get());
  if (container_->window)
    gdk_window_raise(container_->window);
}

void StatusBubbleGtk::Hide() {
  // If we were going to expand the bubble, stop.
  expand_timer_.Stop();
  expand_animation_.reset();

  gtk_widget_hide_all(container_.get());
}

void StatusBubbleGtk::SetStatusTextTo(const std::string& status_utf8) {
  if (status_utf8.empty()) {
    hide_timer_.Stop();
    hide_timer_.Start(base::TimeDelta::FromMilliseconds(kHideDelay),
                      this, &StatusBubbleGtk::Hide);
  } else {
    gtk_label_set_text(GTK_LABEL(label_), status_utf8.c_str());
    GtkRequisition req;
    gtk_widget_size_request(label_, &req);
    desired_width_ = req.width;

    UpdateLabelSizeRequest();

    if (!last_mouse_left_content_) {
      // Show the padding and label to update our requisition and then
      // re-process the last mouse event -- if the label was empty before or the
      // text changed, our size will have changed and we may need to move
      // ourselves away from the pointer now.
      gtk_widget_show_all(padding_);
      MouseMoved(last_mouse_location_, false);
    }
    Show();
  }
}

void StatusBubbleGtk::MouseMoved(
    const gfx::Point& location, bool left_content) {
  if (left_content && ignore_next_left_content_) {
    ignore_next_left_content_ = false;
    return;
  }

  last_mouse_location_ = location;
  last_mouse_left_content_ = left_content;

  if (!GTK_WIDGET_REALIZED(container_.get()))
    return;

  GtkWidget* parent = gtk_widget_get_parent(container_.get());
  if (!parent || !GTK_WIDGET_REALIZED(parent))
    return;

  int old_y_offset = y_offset_;
  bool old_flip_horizontally = flip_horizontally_;

  if (left_content) {
    SetFlipHorizontally(false);
    y_offset_ = 0;
  } else {
    GtkWidget* toplevel = gtk_widget_get_toplevel(container_.get());
    if (!toplevel || !GTK_WIDGET_REALIZED(toplevel))
      return;

    bool ltr = !base::i18n::IsRTL();

    GtkRequisition requisition;
    gtk_widget_size_request(container_.get(), &requisition);

    // Get our base position (that is, not including the current offset)
    // relative to the origin of the root window.
    gint toplevel_x = 0, toplevel_y = 0;
    gdk_window_get_position(toplevel->window, &toplevel_x, &toplevel_y);
    gfx::Rect parent_rect =
        gtk_util::GetWidgetRectRelativeToToplevel(parent);
    gfx::Rect bubble_rect(
        toplevel_x + parent_rect.x() +
            (ltr ? 0 : parent->allocation.width - requisition.width),
        toplevel_y + parent_rect.y() +
            parent->allocation.height - requisition.height,
        requisition.width,
        requisition.height);

    int left_threshold =
        bubble_rect.x() - bubble_rect.height() - kMousePadding;
    int right_threshold =
        bubble_rect.right() + bubble_rect.height() + kMousePadding;
    int top_threshold = bubble_rect.y() - kMousePadding;

    if (((ltr && location.x() < right_threshold) ||
         (!ltr && location.x() > left_threshold)) &&
        location.y() > top_threshold) {
      if (download_shelf_is_visible_) {
        SetFlipHorizontally(true);
        y_offset_ = 0;
      } else {
        SetFlipHorizontally(false);
        int distance = std::max(ltr ?
                                    location.x() - right_threshold :
                                    left_threshold - location.x(),
                                top_threshold - location.y());
        y_offset_ = std::min(-1 * distance, requisition.height);
      }
    } else {
      SetFlipHorizontally(false);
      y_offset_ = 0;
    }
  }

  if (y_offset_ != old_y_offset || flip_horizontally_ != old_flip_horizontally)
    gtk_widget_queue_resize_no_redraw(parent);
}

void StatusBubbleGtk::UpdateDownloadShelfVisibility(bool visible) {
  download_shelf_is_visible_ = visible;
}

void StatusBubbleGtk::Observe(NotificationType type,
                              const NotificationSource& source,
                              const NotificationDetails& details) {
  if (type == NotificationType::BROWSER_THEME_CHANGED) {
    UserChangedTheme();
  }
}

void StatusBubbleGtk::InitWidgets() {
  bool ltr = !base::i18n::IsRTL();

  label_ = gtk_label_new(NULL);

  padding_ = gtk_alignment_new(0, 0, 1, 1);
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
      kInternalTopBottomPadding, kInternalTopBottomPadding,
      kInternalLeftRightPadding + (ltr ? 0 : kCornerSize),
      kInternalLeftRightPadding + (ltr ? kCornerSize : 0));
  gtk_container_add(GTK_CONTAINER(padding_), label_);

  container_.Own(gtk_event_box_new());
  gtk_widget_set_no_show_all(container_.get(), TRUE);
  gtk_util::ActAsRoundedWindow(
      container_.get(), gtk_util::kGdkWhite, kCornerSize,
      gtk_util::ROUNDED_TOP_RIGHT,
      gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT);
  gtk_widget_set_name(container_.get(), "status-bubble");
  gtk_container_add(GTK_CONTAINER(container_.get()), padding_);

  // We need to listen for mouse motion events, since a fast-moving pointer may
  // enter our window without us getting any motion events on the browser near
  // enough for us to run away.
  gtk_widget_add_events(container_.get(), GDK_POINTER_MOTION_MASK |
                                          GDK_ENTER_NOTIFY_MASK);
  g_signal_connect(container_.get(), "motion-notify-event",
                   G_CALLBACK(HandleMotionNotifyThunk), this);
  g_signal_connect(container_.get(), "enter-notify-event",
                   G_CALLBACK(HandleEnterNotifyThunk), this);

  UserChangedTheme();
}

void StatusBubbleGtk::UserChangedTheme() {
  if (theme_provider_->UseGtkTheme()) {
    gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, NULL);
    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL);
  } else {
    // TODO(erg): This is the closest to "text that will look good on a
    // toolbar" that I can find. Maybe in later iterations of the theme system,
    // there will be a better color to pick.
    GdkColor bookmark_text =
        theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
    gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, &bookmark_text);

    GdkColor toolbar_color =
        theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR);
    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color);
  }

  gtk_util::SetRoundedWindowBorderColor(container_.get(),
                                        theme_provider_->GetBorderColor());
}

void StatusBubbleGtk::SetFlipHorizontally(bool flip_horizontally) {
  if (flip_horizontally == flip_horizontally_)
    return;

  flip_horizontally_ = flip_horizontally;

  bool ltr = !base::i18n::IsRTL();
  bool on_left = (ltr && !flip_horizontally) || (!ltr && flip_horizontally);

  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
      kInternalTopBottomPadding, kInternalTopBottomPadding,
      kInternalLeftRightPadding + (on_left ? 0 : kCornerSize),
      kInternalLeftRightPadding + (on_left ? kCornerSize : 0));
  // The rounded window code flips these arguments if we're RTL.
  gtk_util::SetRoundedWindowEdgesAndBorders(
      container_.get(),
      kCornerSize,
      flip_horizontally ?
          gtk_util::ROUNDED_TOP_LEFT :
          gtk_util::ROUNDED_TOP_RIGHT,
      gtk_util::BORDER_TOP |
          (flip_horizontally ? gtk_util::BORDER_LEFT : gtk_util::BORDER_RIGHT));
  gtk_widget_queue_draw(container_.get());
}

void StatusBubbleGtk::ExpandURL() {
  start_width_ = label_->allocation.width;
  expand_animation_.reset(new ui::SlideAnimation(this));
  expand_animation_->SetTweenType(ui::Tween::LINEAR);
  expand_animation_->Show();

  SetStatusTextToURL();
}

void StatusBubbleGtk::UpdateLabelSizeRequest() {
  if (!expanded() || !expand_animation_->is_animating()) {
    gtk_widget_set_size_request(label_, -1, -1);
    return;
  }

  int new_width = start_width_ +
      (desired_width_ - start_width_) * expand_animation_->GetCurrentValue();
  gtk_widget_set_size_request(label_, new_width, -1);
}

// See http://crbug.com/68897 for why we have to handle this event.
gboolean StatusBubbleGtk::HandleEnterNotify(GtkWidget* sender,
                                            GdkEventCrossing* event) {
  ignore_next_left_content_ = true;
  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
  return FALSE;
}

gboolean StatusBubbleGtk::HandleMotionNotify(GtkWidget* sender,
                                             GdkEventMotion* event) {
  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
  return FALSE;
}

void StatusBubbleGtk::AnimationEnded(const ui::Animation* animation) {
  UpdateLabelSizeRequest();
}

void StatusBubbleGtk::AnimationProgressed(const ui::Animation* animation) {
  UpdateLabelSizeRequest();
}
<commit_msg>[gtk] un-break status bubble.<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/gtk/status_bubble_gtk.h"

#include <gtk/gtk.h>

#include <algorithm>

#include "base/i18n/rtl.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/ui/gtk/gtk_theme_provider.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/gtk/rounded_window.h"
#include "chrome/browser/ui/gtk/slide_animator_gtk.h"
#include "chrome/common/notification_service.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/base/text/text_elider.h"

namespace {

// Inner padding between the border and the text label.
const int kInternalTopBottomPadding = 1;
const int kInternalLeftRightPadding = 2;

// The radius of the edges of our bubble.
const int kCornerSize = 3;

// Milliseconds before we hide the status bubble widget when you mouseout.
const int kHideDelay = 250;

// How close the mouse can get to the infobubble before it starts sliding
// off-screen.
const int kMousePadding = 20;

}  // namespace

StatusBubbleGtk::StatusBubbleGtk(Profile* profile)
    : theme_provider_(GtkThemeProvider::GetFrom(profile)),
      padding_(NULL),
      label_(NULL),
      flip_horizontally_(false),
      y_offset_(0),
      download_shelf_is_visible_(false),
      last_mouse_location_(0, 0),
      last_mouse_left_content_(false),
      ignore_next_left_content_(false) {
  InitWidgets();

  theme_provider_->InitThemesFor(this);
  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
                 NotificationService::AllSources());
}

StatusBubbleGtk::~StatusBubbleGtk() {
  container_.Destroy();
}

void StatusBubbleGtk::SetStatus(const string16& status_text_wide) {
  std::string status_text = UTF16ToUTF8(status_text_wide);
  if (status_text_ == status_text)
    return;

  status_text_ = status_text;
  if (!status_text_.empty())
    SetStatusTextTo(status_text_);
  else if (!url_text_.empty())
    SetStatusTextTo(url_text_);
  else
    SetStatusTextTo(std::string());
}

void StatusBubbleGtk::SetURL(const GURL& url, const string16& languages) {
  url_ = url;
  languages_ = languages;

  // If we want to clear a displayed URL but there is a status still to
  // display, display that status instead.
  if (url.is_empty() && !status_text_.empty()) {
    url_text_ = std::string();
    SetStatusTextTo(status_text_);
    return;
  }

  SetStatusTextToURL();
}

void StatusBubbleGtk::SetStatusTextToURL() {
  GtkWidget* parent = gtk_widget_get_parent(container_.get());

  // It appears that parent can be NULL (probably only during shutdown).
  if (!parent || !GTK_WIDGET_REALIZED(parent))
    return;

  int desired_width = parent->allocation.width;
  if (!expanded()) {
    expand_timer_.Stop();
    expand_timer_.Start(base::TimeDelta::FromMilliseconds(kExpandHoverDelay),
                        this, &StatusBubbleGtk::ExpandURL);
    // When not expanded, we limit the size to one third the browser's
    // width.
    desired_width /= 3;
  }

  // TODO(tc): We don't actually use gfx::Font as the font in the status
  // bubble.  We should extend ui::ElideUrl to take some sort of pango font.
  url_text_ = UTF16ToUTF8(ui::ElideUrl(url_, gfx::Font(), desired_width,
                          UTF16ToWideHack(languages_)));
  SetStatusTextTo(url_text_);
}

void StatusBubbleGtk::Show() {
  // If we were going to hide, stop.
  hide_timer_.Stop();

  gtk_widget_show(container_.get());
  if (container_->window)
    gdk_window_raise(container_->window);
}

void StatusBubbleGtk::Hide() {
  // If we were going to expand the bubble, stop.
  expand_timer_.Stop();
  expand_animation_.reset();

  gtk_widget_hide(container_.get());
}

void StatusBubbleGtk::SetStatusTextTo(const std::string& status_utf8) {
  if (status_utf8.empty()) {
    hide_timer_.Stop();
    hide_timer_.Start(base::TimeDelta::FromMilliseconds(kHideDelay),
                      this, &StatusBubbleGtk::Hide);
  } else {
    gtk_label_set_text(GTK_LABEL(label_), status_utf8.c_str());
    GtkRequisition req;
    gtk_widget_size_request(label_, &req);
    desired_width_ = req.width;

    UpdateLabelSizeRequest();

    if (!last_mouse_left_content_) {
      // Show the padding and label to update our requisition and then
      // re-process the last mouse event -- if the label was empty before or the
      // text changed, our size will have changed and we may need to move
      // ourselves away from the pointer now.
      gtk_widget_show_all(padding_);
      MouseMoved(last_mouse_location_, false);
    }
    Show();
  }
}

void StatusBubbleGtk::MouseMoved(
    const gfx::Point& location, bool left_content) {
  if (left_content && ignore_next_left_content_) {
    ignore_next_left_content_ = false;
    return;
  }

  last_mouse_location_ = location;
  last_mouse_left_content_ = left_content;

  if (!GTK_WIDGET_REALIZED(container_.get()))
    return;

  GtkWidget* parent = gtk_widget_get_parent(container_.get());
  if (!parent || !GTK_WIDGET_REALIZED(parent))
    return;

  int old_y_offset = y_offset_;
  bool old_flip_horizontally = flip_horizontally_;

  if (left_content) {
    SetFlipHorizontally(false);
    y_offset_ = 0;
  } else {
    GtkWidget* toplevel = gtk_widget_get_toplevel(container_.get());
    if (!toplevel || !GTK_WIDGET_REALIZED(toplevel))
      return;

    bool ltr = !base::i18n::IsRTL();

    GtkRequisition requisition;
    gtk_widget_size_request(container_.get(), &requisition);

    // Get our base position (that is, not including the current offset)
    // relative to the origin of the root window.
    gint toplevel_x = 0, toplevel_y = 0;
    gdk_window_get_position(toplevel->window, &toplevel_x, &toplevel_y);
    gfx::Rect parent_rect =
        gtk_util::GetWidgetRectRelativeToToplevel(parent);
    gfx::Rect bubble_rect(
        toplevel_x + parent_rect.x() +
            (ltr ? 0 : parent->allocation.width - requisition.width),
        toplevel_y + parent_rect.y() +
            parent->allocation.height - requisition.height,
        requisition.width,
        requisition.height);

    int left_threshold =
        bubble_rect.x() - bubble_rect.height() - kMousePadding;
    int right_threshold =
        bubble_rect.right() + bubble_rect.height() + kMousePadding;
    int top_threshold = bubble_rect.y() - kMousePadding;

    if (((ltr && location.x() < right_threshold) ||
         (!ltr && location.x() > left_threshold)) &&
        location.y() > top_threshold) {
      if (download_shelf_is_visible_) {
        SetFlipHorizontally(true);
        y_offset_ = 0;
      } else {
        SetFlipHorizontally(false);
        int distance = std::max(ltr ?
                                    location.x() - right_threshold :
                                    left_threshold - location.x(),
                                top_threshold - location.y());
        y_offset_ = std::min(-1 * distance, requisition.height);
      }
    } else {
      SetFlipHorizontally(false);
      y_offset_ = 0;
    }
  }

  if (y_offset_ != old_y_offset || flip_horizontally_ != old_flip_horizontally)
    gtk_widget_queue_resize_no_redraw(parent);
}

void StatusBubbleGtk::UpdateDownloadShelfVisibility(bool visible) {
  download_shelf_is_visible_ = visible;
}

void StatusBubbleGtk::Observe(NotificationType type,
                              const NotificationSource& source,
                              const NotificationDetails& details) {
  if (type == NotificationType::BROWSER_THEME_CHANGED) {
    UserChangedTheme();
  }
}

void StatusBubbleGtk::InitWidgets() {
  bool ltr = !base::i18n::IsRTL();

  label_ = gtk_label_new(NULL);

  padding_ = gtk_alignment_new(0, 0, 1, 1);
  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
      kInternalTopBottomPadding, kInternalTopBottomPadding,
      kInternalLeftRightPadding + (ltr ? 0 : kCornerSize),
      kInternalLeftRightPadding + (ltr ? kCornerSize : 0));
  gtk_container_add(GTK_CONTAINER(padding_), label_);
  gtk_widget_show_all(padding_);

  container_.Own(gtk_event_box_new());
  gtk_widget_set_no_show_all(container_.get(), TRUE);
  gtk_util::ActAsRoundedWindow(
      container_.get(), gtk_util::kGdkWhite, kCornerSize,
      gtk_util::ROUNDED_TOP_RIGHT,
      gtk_util::BORDER_TOP | gtk_util::BORDER_RIGHT);
  gtk_widget_set_name(container_.get(), "status-bubble");
  gtk_container_add(GTK_CONTAINER(container_.get()), padding_);

  // We need to listen for mouse motion events, since a fast-moving pointer may
  // enter our window without us getting any motion events on the browser near
  // enough for us to run away.
  gtk_widget_add_events(container_.get(), GDK_POINTER_MOTION_MASK |
                                          GDK_ENTER_NOTIFY_MASK);
  g_signal_connect(container_.get(), "motion-notify-event",
                   G_CALLBACK(HandleMotionNotifyThunk), this);
  g_signal_connect(container_.get(), "enter-notify-event",
                   G_CALLBACK(HandleEnterNotifyThunk), this);

  UserChangedTheme();
}

void StatusBubbleGtk::UserChangedTheme() {
  if (theme_provider_->UseGtkTheme()) {
    gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, NULL);
    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, NULL);
  } else {
    // TODO(erg): This is the closest to "text that will look good on a
    // toolbar" that I can find. Maybe in later iterations of the theme system,
    // there will be a better color to pick.
    GdkColor bookmark_text =
        theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
    gtk_widget_modify_fg(label_, GTK_STATE_NORMAL, &bookmark_text);

    GdkColor toolbar_color =
        theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR);
    gtk_widget_modify_bg(container_.get(), GTK_STATE_NORMAL, &toolbar_color);
  }

  gtk_util::SetRoundedWindowBorderColor(container_.get(),
                                        theme_provider_->GetBorderColor());
}

void StatusBubbleGtk::SetFlipHorizontally(bool flip_horizontally) {
  if (flip_horizontally == flip_horizontally_)
    return;

  flip_horizontally_ = flip_horizontally;

  bool ltr = !base::i18n::IsRTL();
  bool on_left = (ltr && !flip_horizontally) || (!ltr && flip_horizontally);

  gtk_alignment_set_padding(GTK_ALIGNMENT(padding_),
      kInternalTopBottomPadding, kInternalTopBottomPadding,
      kInternalLeftRightPadding + (on_left ? 0 : kCornerSize),
      kInternalLeftRightPadding + (on_left ? kCornerSize : 0));
  // The rounded window code flips these arguments if we're RTL.
  gtk_util::SetRoundedWindowEdgesAndBorders(
      container_.get(),
      kCornerSize,
      flip_horizontally ?
          gtk_util::ROUNDED_TOP_LEFT :
          gtk_util::ROUNDED_TOP_RIGHT,
      gtk_util::BORDER_TOP |
          (flip_horizontally ? gtk_util::BORDER_LEFT : gtk_util::BORDER_RIGHT));
  gtk_widget_queue_draw(container_.get());
}

void StatusBubbleGtk::ExpandURL() {
  start_width_ = label_->allocation.width;
  expand_animation_.reset(new ui::SlideAnimation(this));
  expand_animation_->SetTweenType(ui::Tween::LINEAR);
  expand_animation_->Show();

  SetStatusTextToURL();
}

void StatusBubbleGtk::UpdateLabelSizeRequest() {
  if (!expanded() || !expand_animation_->is_animating()) {
    gtk_widget_set_size_request(label_, -1, -1);
    return;
  }

  int new_width = start_width_ +
      (desired_width_ - start_width_) * expand_animation_->GetCurrentValue();
  gtk_widget_set_size_request(label_, new_width, -1);
}

// See http://crbug.com/68897 for why we have to handle this event.
gboolean StatusBubbleGtk::HandleEnterNotify(GtkWidget* sender,
                                            GdkEventCrossing* event) {
  ignore_next_left_content_ = true;
  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
  return FALSE;
}

gboolean StatusBubbleGtk::HandleMotionNotify(GtkWidget* sender,
                                             GdkEventMotion* event) {
  MouseMoved(gfx::Point(event->x_root, event->y_root), false);
  return FALSE;
}

void StatusBubbleGtk::AnimationEnded(const ui::Animation* animation) {
  UpdateLabelSizeRequest();
}

void StatusBubbleGtk::AnimationProgressed(const ui::Animation* animation) {
  UpdateLabelSizeRequest();
}
<|endoftext|>


<commit_before>#include <hdmarker/subpattern.hpp>
#include <ucalib/ucalib.hpp>
#include <cliini/cliini.hpp>

#include <opencv2/highgui.hpp>

using namespace cv;
using namespace cliini;
using namespace hdmarker;

cliini_opt opts[] = {
  {
    "help",
    0, //argcount
    0, //argcount
    CLIINI_NONE, //type
    0, //flags
    'h'
  },
  {
    "imgs",
    1, //argcount min
    CLIINI_ARGCOUNT_ANY, //argcount max
    CLIINI_STRING, //type
    0, //flags
    'i',
    NULL,
    "read imgs and detect calibration points with HDMarkers",
    "[file1] [file2] ..."
  },
  {
    "store",
    1, //argcount min
    1, //argcount max
    CLIINI_STRING, //type
    0, //flags
    's',
    NULL,
    "store detected calibration points",
    "<file>"
  },
  {
    "load",
    1, //argcount min
    1, //argcount max
    CLIINI_STRING, //type
    0, //flags
    'l',
    NULL,
    "load stored calibration points and execute calibration",
    "<file>"
  }
};

cliini_optgroup group = {
  opts,
  NULL,
  sizeof(opts)/sizeof(*opts),
  0,
  0
};

int main(int argc, const char *argv[])
{
  Marker::init();

  cliargs args(argc, argv, NULL);

  std::vector<Corner> corners_rough;
  std::vector<Corner> corners;

  double unit_size = 39.94; //marker size in mm
  double unit_size_res = unit_size;

  for(int i=0;i<args["imgs"].count();i++) {
    Mat img = imread(args["imgs"].str(i));
    Marker::detect(img, corners_rough);
    hdmarker_detect_subpattern(img, corners_rough, corners, 3, &unit_size_res, NULL, NULL, 0);
  }

  std::vector<Point2f> ipoints_v(corners.size());
  std::vector<Point2f> wpoints_v(corners.size());

  for(int ci=0;ci<corners.size();ci++) {
    ipoints_v[ci] = corners[ci].p;
    Point2f w_2d = unit_size_res*Point2f(corners[ci].id.x, corners[ci].id.y);
    wpoints_v[ci] = Point2f(w_2d.x, w_2d.y);
  }

  if (args["store"].valid())
  {
    FileStorage fs(args["store"].str(), FileStorage::WRITE);

    fs << "img_points" << ipoints_v;
    fs << "world_points" << wpoints_v;
  }
  if (args["load"].valid())
  {
    FileStorage fs(args["load"].str(), FileStorage::READ);

    fs["img_points"] >> ipoints_v;
    fs["world_points"] >> wpoints_v;
  }

  //TODO calibration...

  return EXIT_SUCCESS;
}
<commit_msg>fix sample file<commit_after>#include <hdmarker/subpattern.hpp>
#include <ucalib/ucalib.hpp>
#include <cliini/cliini.hpp>

#include <opencv2/highgui.hpp>

using namespace cv;
using namespace cliini;
using namespace hdmarker;

cliini_opt opts[] = {
  {
    "help",
    0, //argcount
    0, //argcount
    CLIINI_NONE, //type
    0, //flags
    'h'
  },
  {
    "imgs",
    1, //argcount min
    CLIINI_ARGCOUNT_ANY, //argcount max
    CLIINI_STRING, //type
    0, //flags
    'i',
    NULL,
    "read imgs and detect calibration points with HDMarkers",
    "[file1] [file2] ..."
  },
  {
    "store",
    1, //argcount min
    1, //argcount max
    CLIINI_STRING, //type
    0, //flags
    's',
    NULL,
    "store detected calibration points",
    "<file>"
  },
  {
    "load",
    1, //argcount min
    1, //argcount max
    CLIINI_STRING, //type
    0, //flags
    'l',
    NULL,
    "load stored calibration points and execute calibration",
    "<file>"
  }
};

cliini_optgroup group = {
  opts,
  NULL,
  sizeof(opts)/sizeof(*opts),
  0,
  0
};

int main(int argc, const char *argv[])
{
  Marker::init();

  cliargs args(argc, argv, &group);

  std::vector<Corner> corners_rough;
  std::vector<Corner> corners;

  double unit_size = 39.94; //marker size in mm
  double unit_size_res = unit_size;

  for(int i=0;i<args["imgs"].count();i++) {
    Mat img = imread(args["imgs"].str(i), CV_LOAD_IMAGE_GRAYSCALE);
    printf("detect img %s\n", args["imgs"].str(i).c_str());
    corners_rough.resize(0);
    Marker::detect(img, corners_rough);
    Mat debug;
    corners.resize(0);
    hdmarker_detect_subpattern(img, corners_rough, corners, 3, &unit_size_res, &debug, NULL, 0);
    imwrite((args["imgs"].str(i)+".detect.png").c_str(), debug);

    std::vector<Point2f> ipoints_v(corners.size());
    std::vector<Point2f> wpoints_v(corners.size());

    for(int ci=0;ci<corners.size();ci++) {
      ipoints_v[ci] = corners[ci].p;
      Point2f w_2d = unit_size_res*Point2f(corners[ci].id.x, corners[ci].id.y);
      wpoints_v[ci] = Point2f(w_2d.x, w_2d.y);
    }

    FileStorage fs(args["store"].str()+".pointcache", FileStorage::WRITE);

    fs << "img_points" << ipoints_v;
    fs << "world_points" << wpoints_v;
  }

  /*if (args["store"].valid())
  {
    FileStorage fs(args["store"].str(), FileStorage::WRITE);

    fs << "img_points" << ipoints_v;
    fs << "world_points" << wpoints_v;
  }
  if (args["load"].valid())
  {
    FileStorage fs(args["load"].str(), FileStorage::READ);

    fs["img_points"] >> ipoints_v;
    fs["world_points"] >> wpoints_v;
  }*/

  //TODO calibration...

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "fly/types/json/json.hpp"

#include <algorithm>
#include <cmath>
#include <ios>
#include <iterator>
#include <limits>
#include <utility>

namespace fly {

//==================================================================================================
Json::Json(const JsonTraits::null_type &value) noexcept : m_value(value)
{
}

//==================================================================================================
Json::Json(const_reference json) noexcept : m_value(json.m_value)
{
}

//==================================================================================================
Json::Json(Json &&json) noexcept : m_value(std::move(json.m_value))
{
    json.m_value = nullptr;
}

//==================================================================================================
Json::Json(const std::initializer_list<Json> &initializer) noexcept : m_value()
{
    auto object_test = [](const_reference json)
    {
        return json.is_object_like();
    };

    if (std::all_of(initializer.begin(), initializer.end(), object_test))
    {
        m_value = JsonTraits::object_type();
        auto &value = std::get<JsonTraits::object_type>(m_value);

        for (auto it = initializer.begin(); it != initializer.end(); ++it)
        {
            auto key = std::move(std::get<JsonTraits::string_type>((*it)[0].m_value));
            value.emplace(std::move(key), std::move((*it)[1]));
        }
    }
    else
    {
        m_value = JsonTraits::array_type();
        auto &value = std::get<JsonTraits::array_type>(m_value);

        for (auto it = initializer.begin(); it != initializer.end(); ++it)
        {
            value.emplace_back(std::move(*it));
        }
    }
}

//==================================================================================================
Json::~Json()
{
    std::vector<Json> stack;
    stack.reserve(size());

    if (is_object())
    {
        for (auto &&json : std::get<JsonTraits::object_type>(m_value))
        {
            stack.push_back(std::move(json.second));
        }
    }
    else if (is_array())
    {
        auto &&json = std::get<JsonTraits::array_type>(m_value);
        std::move(json.begin(), json.end(), std::back_inserter(stack));
    }

    while (!stack.empty())
    {
        Json json(std::move(stack.back()));
        stack.pop_back();

        stack.reserve(stack.size() + json.size());

        if (json.is_object())
        {
            for (auto &&child : std::get<JsonTraits::object_type>(json.m_value))
            {
                stack.push_back(std::move(child.second));
            }
        }
        else if (json.is_array())
        {
            auto &&child = std::get<JsonTraits::array_type>(json.m_value);
            std::move(child.begin(), child.end(), std::back_inserter(stack));
            child.clear();
        }
    }
}

//==================================================================================================
Json::reference Json::operator=(Json json) noexcept
{
    m_value = std::move(json.m_value);
    return *this;
}

//==================================================================================================
bool Json::is_null() const
{
    return std::holds_alternative<JsonTraits::null_type>(m_value);
}

//==================================================================================================
bool Json::is_string() const
{
    return std::holds_alternative<JsonTraits::string_type>(m_value);
}

//==================================================================================================
bool Json::is_object() const
{
    return std::holds_alternative<JsonTraits::object_type>(m_value);
}

//==================================================================================================
bool Json::is_object_like() const
{
    const auto *value = std::get_if<JsonTraits::array_type>(&m_value);

    return (value != nullptr) && (value->size() == 2) && value->at(0).is_string();
}

//==================================================================================================
bool Json::is_array() const
{
    return std::holds_alternative<JsonTraits::array_type>(m_value);
}

//==================================================================================================
bool Json::is_boolean() const
{
    return std::holds_alternative<JsonTraits::boolean_type>(m_value);
}

//==================================================================================================
bool Json::is_signed_integer() const
{
    return std::holds_alternative<JsonTraits::signed_type>(m_value);
}

//==================================================================================================
bool Json::is_unsigned_integer() const
{
    return std::holds_alternative<JsonTraits::unsigned_type>(m_value);
}

//==================================================================================================
bool Json::is_float() const
{
    return std::holds_alternative<JsonTraits::float_type>(m_value);
}

//==================================================================================================
Json::operator JsonTraits::null_type() const noexcept(false)
{
    if (is_null())
    {
        return std::get<JsonTraits::null_type>(m_value);
    }
    else
    {
        throw JsonException(*this, "JSON type is not null");
    }
}

//==================================================================================================
Json::reference Json::operator[](size_type index)
{
    if (is_null())
    {
        m_value = JsonTraits::array_type();
    }

    if (is_array())
    {
        auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            value.resize(index + 1);
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
Json::const_reference Json::operator[](size_type index) const
{
    return at(index);
}

//==================================================================================================
Json::reference Json::at(size_type index)
{
    if (is_array())
    {
        auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            throw JsonException(*this, String::format("Given index (%d) not found", index));
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
Json::const_reference Json::at(size_type index) const
{
    if (is_array())
    {
        const auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            throw JsonException(*this, String::format("Given index (%d) not found", index));
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
bool Json::empty() const
{
    auto visitor = [](const auto &value) -> bool
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            return true;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            return value.empty();
        }
        else
        {
            return false;
        }
    };

    return std::visit(std::move(visitor), m_value);
}

//==================================================================================================
Json::size_type Json::size() const
{
    auto visitor = [](const auto &value) -> size_type
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            return 0;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            return value.size();
        }
        else
        {
            return 1;
        }
    };

    return std::visit(std::move(visitor), m_value);
}

//==================================================================================================
void Json::clear()
{
    auto visitor = [](auto &value)
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            value.clear();
        }
        else if constexpr (std::is_same_v<T, JsonTraits::boolean_type>)
        {
            value = false;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::signed_type> ||
            std::is_same_v<T, JsonTraits::unsigned_type> ||
            std::is_same_v<T, JsonTraits::float_type>)
        {
            value = static_cast<T>(0);
        }
    };

    std::visit(std::move(visitor), m_value);
}

//==================================================================================================
void Json::swap(reference json)
{
    std::swap(m_value, json.m_value);
}

//==================================================================================================
Json::iterator Json::begin()
{
    return iterator(this, iterator::Position::Begin);
}

//==================================================================================================
Json::const_iterator Json::begin() const
{
    return cbegin();
}

//==================================================================================================
Json::const_iterator Json::cbegin() const
{
    return const_iterator(this, const_iterator::Position::Begin);
}

//==================================================================================================
Json::iterator Json::end()
{
    return iterator(this, iterator::Position::End);
}

//==================================================================================================
Json::const_iterator Json::end() const
{
    return cend();
}

//==================================================================================================
Json::const_iterator Json::cend() const
{
    return const_iterator(this, const_iterator::Position::End);
}

//==================================================================================================
bool operator==(Json::const_reference json1, Json::const_reference json2)
{
    auto visitor = [](const auto &value1, const auto &value2) -> bool
    {
        using T = std::decay_t<decltype(value1)>;
        using U = std::decay_t<decltype(value2)>;

        if constexpr (
            (std::is_same_v<T, JsonTraits::float_type> && JsonTraits::is_number_v<U>) ||
            (std::is_same_v<U, JsonTraits::float_type> && JsonTraits::is_number_v<T>))
        {
            constexpr auto epsilon = std::numeric_limits<T>::epsilon();

            const auto fvalue1 = static_cast<JsonTraits::float_type>(value1);
            const auto fvalue2 = static_cast<JsonTraits::float_type>(value2);

            return std::abs(fvalue1 - fvalue2) <= epsilon;
        }
        else if constexpr (JsonTraits::is_number_v<T> && JsonTraits::is_number_v<U>)
        {
            return value1 == static_cast<T>(value2);
        }
        else if constexpr (std::is_same_v<T, U>)
        {
            return value1 == value2;
        }
        else
        {
            return false;
        }
    };

    return std::visit(std::move(visitor), json1.m_value, json2.m_value);
}

//==================================================================================================
bool operator!=(Json::const_reference json1, Json::const_reference json2)
{
    return !(json1 == json2);
}

//==================================================================================================
std::ostream &operator<<(std::ostream &stream, Json::const_reference json)
{
    auto serialize_string = [&stream](const JsonTraits::string_type &value)
    {
        const auto end = value.cend();
        stream << '"';

        for (auto it = value.cbegin(); it != end;)
        {
            Json::write_escaped_charater(stream, it, end);
        }

        stream << '"';
    };

    auto visitor = [&stream, &serialize_string](const auto &value)
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            stream << "null";
        }
        else if constexpr (std::is_same_v<T, JsonTraits::string_type>)
        {
            serialize_string(value);
        }
        else if constexpr (std::is_same_v<T, JsonTraits::object_type>)
        {
            stream << '{';

            for (auto it = value.begin(); it != value.end();)
            {
                serialize_string(it->first);
                stream << ':' << it->second;

                if (++it != value.end())
                {
                    stream << ',';
                }
            }

            stream << '}';
        }
        else if constexpr (std::is_same_v<T, JsonTraits::array_type>)
        {
            stream << '[';

            for (auto it = value.begin(); it != value.end();)
            {
                stream << *it;

                if (++it != value.end())
                {
                    stream << ',';
                }
            }

            stream << ']';
        }
        else if constexpr (std::is_same_v<T, JsonTraits::boolean_type>)
        {
            stream << (value ? "true" : "false");
        }
        else
        {
            stream << value;
        }
    };

    std::visit(std::move(visitor), json.m_value);
    return stream;
}

//==================================================================================================
JsonTraits::string_type Json::validate_string(const JsonTraits::string_type &str)
{
    stringstream_type stream;

    const auto end = str.cend();

    for (auto it = str.cbegin(); it != end;)
    {
        if (*it == '\\')
        {
            read_escaped_character(stream, it, end);
        }
        else
        {
            validate_character(stream, it++);
        }
    }

    return stream.str();
}

//==================================================================================================
void Json::read_escaped_character(
    stringstream_type &stream,
    JsonTraits::string_type::const_iterator &it,
    const JsonTraits::string_type::const_iterator &end)
{
    if (++it == end)
    {
        throw JsonException("Expected escaped character after reverse solidus");
    }

    switch (*it)
    {
        case '\"':
        case '\\':
        case '/':
            stream << *it;
            break;

        case 'b':
            stream << '\b';
            break;

        case 'f':
            stream << '\f';
            break;

        case 'n':
            stream << '\n';
            break;

        case 'r':
            stream << '\r';
            break;

        case 't':
            stream << '\t';
            break;

        case 'u':
            // The input sequence is expected to begin with the reverse solidus character.
            if (auto value = JsonTraits::StringType::unescape_codepoint(--it, end); value)
            {
                stream << std::move(value.value());
            }
            else
            {
                throw JsonException("Could not parse escaped Unicode sequence");
            }

            // The iterator is already incremented past the escaped character sequence.
            return;

        default:
            throw JsonException(
                String::format("Invalid escape character '%c'", static_cast<char>(*it)));
    }

    ++it;
}

//==================================================================================================
void Json::write_escaped_charater(
    std::ostream &stream,
    JsonTraits::string_type::const_iterator &it,
    const JsonTraits::string_type::const_iterator &end)
{
    switch (*it)
    {
        case '\"':
            stream << "\\\"";
            break;

        case '\\':
            stream << "\\\\";
            break;

        case '\b':
            stream << "\\b";
            break;

        case '\f':
            stream << "\\f";
            break;

        case '\n':
            stream << "\\n";
            break;

        case '\r':
            stream << "\\r";
            break;

        case '\t':
            stream << "\\t";
            break;

        default:
            stream << JsonTraits::StringType::escape_codepoint<'u'>(it, end).value();
            return;
    }

    ++it;
}

//==================================================================================================
void Json::validate_character(
    stringstream_type &stream,
    const JsonTraits::string_type::const_iterator &it)
{
    const std::uint8_t ch = static_cast<std::uint8_t>(*it);

    if ((ch <= 0x1f) || (ch == 0x22) || (ch == 0x5c))
    {
        throw JsonException(
            String::format("Character '%c' must be escaped", static_cast<char>(*it)));
    }

    stream << *it;
}

} // namespace fly
<commit_msg>Remove now-unneeded static character casts<commit_after>#include "fly/types/json/json.hpp"

#include <algorithm>
#include <cmath>
#include <ios>
#include <iterator>
#include <limits>
#include <utility>

namespace fly {

//==================================================================================================
Json::Json(const JsonTraits::null_type &value) noexcept : m_value(value)
{
}

//==================================================================================================
Json::Json(const_reference json) noexcept : m_value(json.m_value)
{
}

//==================================================================================================
Json::Json(Json &&json) noexcept : m_value(std::move(json.m_value))
{
    json.m_value = nullptr;
}

//==================================================================================================
Json::Json(const std::initializer_list<Json> &initializer) noexcept : m_value()
{
    auto object_test = [](const_reference json)
    {
        return json.is_object_like();
    };

    if (std::all_of(initializer.begin(), initializer.end(), object_test))
    {
        m_value = JsonTraits::object_type();
        auto &value = std::get<JsonTraits::object_type>(m_value);

        for (auto it = initializer.begin(); it != initializer.end(); ++it)
        {
            auto key = std::move(std::get<JsonTraits::string_type>((*it)[0].m_value));
            value.emplace(std::move(key), std::move((*it)[1]));
        }
    }
    else
    {
        m_value = JsonTraits::array_type();
        auto &value = std::get<JsonTraits::array_type>(m_value);

        for (auto it = initializer.begin(); it != initializer.end(); ++it)
        {
            value.emplace_back(std::move(*it));
        }
    }
}

//==================================================================================================
Json::~Json()
{
    std::vector<Json> stack;
    stack.reserve(size());

    if (is_object())
    {
        for (auto &&json : std::get<JsonTraits::object_type>(m_value))
        {
            stack.push_back(std::move(json.second));
        }
    }
    else if (is_array())
    {
        auto &&json = std::get<JsonTraits::array_type>(m_value);
        std::move(json.begin(), json.end(), std::back_inserter(stack));
    }

    while (!stack.empty())
    {
        Json json(std::move(stack.back()));
        stack.pop_back();

        stack.reserve(stack.size() + json.size());

        if (json.is_object())
        {
            for (auto &&child : std::get<JsonTraits::object_type>(json.m_value))
            {
                stack.push_back(std::move(child.second));
            }
        }
        else if (json.is_array())
        {
            auto &&child = std::get<JsonTraits::array_type>(json.m_value);
            std::move(child.begin(), child.end(), std::back_inserter(stack));
            child.clear();
        }
    }
}

//==================================================================================================
Json::reference Json::operator=(Json json) noexcept
{
    m_value = std::move(json.m_value);
    return *this;
}

//==================================================================================================
bool Json::is_null() const
{
    return std::holds_alternative<JsonTraits::null_type>(m_value);
}

//==================================================================================================
bool Json::is_string() const
{
    return std::holds_alternative<JsonTraits::string_type>(m_value);
}

//==================================================================================================
bool Json::is_object() const
{
    return std::holds_alternative<JsonTraits::object_type>(m_value);
}

//==================================================================================================
bool Json::is_object_like() const
{
    const auto *value = std::get_if<JsonTraits::array_type>(&m_value);

    return (value != nullptr) && (value->size() == 2) && value->at(0).is_string();
}

//==================================================================================================
bool Json::is_array() const
{
    return std::holds_alternative<JsonTraits::array_type>(m_value);
}

//==================================================================================================
bool Json::is_boolean() const
{
    return std::holds_alternative<JsonTraits::boolean_type>(m_value);
}

//==================================================================================================
bool Json::is_signed_integer() const
{
    return std::holds_alternative<JsonTraits::signed_type>(m_value);
}

//==================================================================================================
bool Json::is_unsigned_integer() const
{
    return std::holds_alternative<JsonTraits::unsigned_type>(m_value);
}

//==================================================================================================
bool Json::is_float() const
{
    return std::holds_alternative<JsonTraits::float_type>(m_value);
}

//==================================================================================================
Json::operator JsonTraits::null_type() const noexcept(false)
{
    if (is_null())
    {
        return std::get<JsonTraits::null_type>(m_value);
    }
    else
    {
        throw JsonException(*this, "JSON type is not null");
    }
}

//==================================================================================================
Json::reference Json::operator[](size_type index)
{
    if (is_null())
    {
        m_value = JsonTraits::array_type();
    }

    if (is_array())
    {
        auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            value.resize(index + 1);
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
Json::const_reference Json::operator[](size_type index) const
{
    return at(index);
}

//==================================================================================================
Json::reference Json::at(size_type index)
{
    if (is_array())
    {
        auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            throw JsonException(*this, String::format("Given index (%d) not found", index));
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
Json::const_reference Json::at(size_type index) const
{
    if (is_array())
    {
        const auto &value = std::get<JsonTraits::array_type>(m_value);

        if (index >= value.size())
        {
            throw JsonException(*this, String::format("Given index (%d) not found", index));
        }

        return value.at(index);
    }

    throw JsonException(*this, "JSON type invalid for operator[index]");
}

//==================================================================================================
bool Json::empty() const
{
    auto visitor = [](const auto &value) -> bool
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            return true;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            return value.empty();
        }
        else
        {
            return false;
        }
    };

    return std::visit(std::move(visitor), m_value);
}

//==================================================================================================
Json::size_type Json::size() const
{
    auto visitor = [](const auto &value) -> size_type
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            return 0;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            return value.size();
        }
        else
        {
            return 1;
        }
    };

    return std::visit(std::move(visitor), m_value);
}

//==================================================================================================
void Json::clear()
{
    auto visitor = [](auto &value)
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (
            std::is_same_v<T, JsonTraits::string_type> ||
            std::is_same_v<T, JsonTraits::object_type> || std::is_same_v<T, JsonTraits::array_type>)
        {
            value.clear();
        }
        else if constexpr (std::is_same_v<T, JsonTraits::boolean_type>)
        {
            value = false;
        }
        else if constexpr (
            std::is_same_v<T, JsonTraits::signed_type> ||
            std::is_same_v<T, JsonTraits::unsigned_type> ||
            std::is_same_v<T, JsonTraits::float_type>)
        {
            value = static_cast<T>(0);
        }
    };

    std::visit(std::move(visitor), m_value);
}

//==================================================================================================
void Json::swap(reference json)
{
    std::swap(m_value, json.m_value);
}

//==================================================================================================
Json::iterator Json::begin()
{
    return iterator(this, iterator::Position::Begin);
}

//==================================================================================================
Json::const_iterator Json::begin() const
{
    return cbegin();
}

//==================================================================================================
Json::const_iterator Json::cbegin() const
{
    return const_iterator(this, const_iterator::Position::Begin);
}

//==================================================================================================
Json::iterator Json::end()
{
    return iterator(this, iterator::Position::End);
}

//==================================================================================================
Json::const_iterator Json::end() const
{
    return cend();
}

//==================================================================================================
Json::const_iterator Json::cend() const
{
    return const_iterator(this, const_iterator::Position::End);
}

//==================================================================================================
bool operator==(Json::const_reference json1, Json::const_reference json2)
{
    auto visitor = [](const auto &value1, const auto &value2) -> bool
    {
        using T = std::decay_t<decltype(value1)>;
        using U = std::decay_t<decltype(value2)>;

        if constexpr (
            (std::is_same_v<T, JsonTraits::float_type> && JsonTraits::is_number_v<U>) ||
            (std::is_same_v<U, JsonTraits::float_type> && JsonTraits::is_number_v<T>))
        {
            constexpr auto epsilon = std::numeric_limits<T>::epsilon();

            const auto fvalue1 = static_cast<JsonTraits::float_type>(value1);
            const auto fvalue2 = static_cast<JsonTraits::float_type>(value2);

            return std::abs(fvalue1 - fvalue2) <= epsilon;
        }
        else if constexpr (JsonTraits::is_number_v<T> && JsonTraits::is_number_v<U>)
        {
            return value1 == static_cast<T>(value2);
        }
        else if constexpr (std::is_same_v<T, U>)
        {
            return value1 == value2;
        }
        else
        {
            return false;
        }
    };

    return std::visit(std::move(visitor), json1.m_value, json2.m_value);
}

//==================================================================================================
bool operator!=(Json::const_reference json1, Json::const_reference json2)
{
    return !(json1 == json2);
}

//==================================================================================================
std::ostream &operator<<(std::ostream &stream, Json::const_reference json)
{
    auto serialize_string = [&stream](const JsonTraits::string_type &value)
    {
        const auto end = value.cend();
        stream << '"';

        for (auto it = value.cbegin(); it != end;)
        {
            Json::write_escaped_charater(stream, it, end);
        }

        stream << '"';
    };

    auto visitor = [&stream, &serialize_string](const auto &value)
    {
        using T = std::decay_t<decltype(value)>;

        if constexpr (std::is_same_v<T, JsonTraits::null_type>)
        {
            stream << "null";
        }
        else if constexpr (std::is_same_v<T, JsonTraits::string_type>)
        {
            serialize_string(value);
        }
        else if constexpr (std::is_same_v<T, JsonTraits::object_type>)
        {
            stream << '{';

            for (auto it = value.begin(); it != value.end();)
            {
                serialize_string(it->first);
                stream << ':' << it->second;

                if (++it != value.end())
                {
                    stream << ',';
                }
            }

            stream << '}';
        }
        else if constexpr (std::is_same_v<T, JsonTraits::array_type>)
        {
            stream << '[';

            for (auto it = value.begin(); it != value.end();)
            {
                stream << *it;

                if (++it != value.end())
                {
                    stream << ',';
                }
            }

            stream << ']';
        }
        else if constexpr (std::is_same_v<T, JsonTraits::boolean_type>)
        {
            stream << (value ? "true" : "false");
        }
        else
        {
            stream << value;
        }
    };

    std::visit(std::move(visitor), json.m_value);
    return stream;
}

//==================================================================================================
JsonTraits::string_type Json::validate_string(const JsonTraits::string_type &str)
{
    stringstream_type stream;

    const auto end = str.cend();

    for (auto it = str.cbegin(); it != end;)
    {
        if (*it == '\\')
        {
            read_escaped_character(stream, it, end);
        }
        else
        {
            validate_character(stream, it++);
        }
    }

    return stream.str();
}

//==================================================================================================
void Json::read_escaped_character(
    stringstream_type &stream,
    JsonTraits::string_type::const_iterator &it,
    const JsonTraits::string_type::const_iterator &end)
{
    if (++it == end)
    {
        throw JsonException("Expected escaped character after reverse solidus");
    }

    switch (*it)
    {
        case '\"':
        case '\\':
        case '/':
            stream << *it;
            break;

        case 'b':
            stream << '\b';
            break;

        case 'f':
            stream << '\f';
            break;

        case 'n':
            stream << '\n';
            break;

        case 'r':
            stream << '\r';
            break;

        case 't':
            stream << '\t';
            break;

        case 'u':
            // The input sequence is expected to begin with the reverse solidus character.
            if (auto value = JsonTraits::StringType::unescape_codepoint(--it, end); value)
            {
                stream << std::move(value.value());
            }
            else
            {
                throw JsonException("Could not parse escaped Unicode sequence");
            }

            // The iterator is already incremented past the escaped character sequence.
            return;

        default:
            throw JsonException(String::format("Invalid escape character '%c'", *it));
    }

    ++it;
}

//==================================================================================================
void Json::write_escaped_charater(
    std::ostream &stream,
    JsonTraits::string_type::const_iterator &it,
    const JsonTraits::string_type::const_iterator &end)
{
    switch (*it)
    {
        case '\"':
            stream << "\\\"";
            break;

        case '\\':
            stream << "\\\\";
            break;

        case '\b':
            stream << "\\b";
            break;

        case '\f':
            stream << "\\f";
            break;

        case '\n':
            stream << "\\n";
            break;

        case '\r':
            stream << "\\r";
            break;

        case '\t':
            stream << "\\t";
            break;

        default:
            stream << JsonTraits::StringType::escape_codepoint<'u'>(it, end).value();
            return;
    }

    ++it;
}

//==================================================================================================
void Json::validate_character(
    stringstream_type &stream,
    const JsonTraits::string_type::const_iterator &it)
{
    const std::uint8_t ch = static_cast<std::uint8_t>(*it);

    if ((ch <= 0x1f) || (ch == 0x22) || (ch == 0x5c))
    {
        throw JsonException(String::format("Character '%c' must be escaped", *it));
    }

    stream << *it;
}

} // namespace fly
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 <windows.h>
#include <shlwapi.h>

#include "base/file_util.h"
#include "chrome/app/breakpad_win.h"
#include "chrome/app/client_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/result_codes.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/util_constants.h"

namespace {
// The entry point signature of chrome.dll.
typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);

typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();

// Not generic, we only handle strings up to 128 chars.
bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) {
  BYTE out[128 * sizeof(wchar_t)];
  DWORD size = sizeof(out);
  DWORD type = 0;
  if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size))
    return false;
  if (type != REG_SZ)
    return false;
  value->assign(reinterpret_cast<wchar_t*>(out));
  return true;
}

// Gets chrome version according to the load path. |exe_path| must be the
// backslash terminated directory of the current chrome.exe.
bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
                std::wstring* version) {
  HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
                                                            HKEY_LOCAL_MACHINE;
  HKEY key;
  if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
    return false;

  // If 'new_chrome.exe' is present it means chrome was auto-updated while
  // running. We need to consult the opv value so we can load the old dll.
  // TODO(cpu) : This is solving the same problem as the environment variable
  // so one of them will eventually be deprecated.
  std::wstring new_chrome_exe(exe_path);
  new_chrome_exe.append(installer_util::kChromeNewExe);
  if (::PathFileExistsW(new_chrome_exe.c_str()) &&
      ReadRegistryStr(key, google_update::kRegOldVersionField, version)) {
    ::RegCloseKey(key);
    return true;
  }

  bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version);
  ::RegCloseKey(key);
  return ret;
}

// Gets the path of the current exe with a trailing backslash.
std::wstring GetExecutablePath() {
  wchar_t path[MAX_PATH];
  ::GetModuleFileNameW(NULL, path, MAX_PATH);
  if (!::PathRemoveFileSpecW(path))
    return std::wstring();
  std::wstring exe_path(path);
  return exe_path.append(L"\\");
}

// Not generic, we only handle strings up to 128 chars.
bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
  wchar_t out[128];
  DWORD count = sizeof(out)/sizeof(out[0]);
  DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
  if ((rv == 0) || (rv >= count))
    return false;
  *value = out;
  return true;
}

bool IsRunningHeadless() {
  return (0 != ::GetEnvironmentVariableW(L"CHROME_HEADLESS", NULL, 0));
}

// Expects that |dir| has a trailing backslash. |dir| is modified so it
// contains the full path that was tried. Caller must check for the return
// value not being null to dermine if this path contains a valid dll.
HMODULE LoadChromeWithDirectory(std::wstring* dir) {
  ::SetCurrentDirectoryW(dir->c_str());
  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
#ifdef _WIN64
  if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
      switches::kNaClBrokerProcess) ||
      (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
      switches::kNaClLoaderProcess)) {
    // Load the 64-bit DLL when running in a 64-bit process.
    dir->append(installer_util::kChromeNaCl64Dll);
  } else {
    // Only NaCl broker and loader can be launched as Win64 processes.
    NOTREACHED();
    return NULL;
  }
#else
  dir->append(installer_util::kChromeDll);
#endif

#ifdef NDEBUG
  // Experimental pre-reading optimization
  // The idea is to pre read significant portion of chrome.dll in advance
  // so that subsequent hard page faults are avoided.
  if (!cmd_line.HasSwitch(switches::kProcessType) &&
      (IsRunningHeadless() || InstallUtil::IsChromeFrameProcess())) {
    // The kernel brings in 8 pages for the code section at a time and 4 pages
    // for other sections. We can skip over these pages to avoid a soft page
    // fault which may not occur during code execution. However skipping 4K at
    // a time still has better performance over 32K and 16K according to data.
    // TODO(ananta)
    // Investigate this and tune.
    const size_t kStepSize = 4 * 1024;

    DWORD pre_read_size = 0;
    DWORD pre_read_step_size = kStepSize;
    DWORD pre_read = 1;

    HKEY key = NULL;
    if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
                       0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
      DWORD unused = sizeof(pre_read_size);
      RegQueryValueEx(key, L"PreReadSize", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read_size), &unused);
      RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused);
      RegQueryValueEx(key, L"PreRead", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read), &unused);
      RegCloseKey(key);
      key = NULL;
    }
    if (pre_read) {
      file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
    }
  }
#endif  // NDEBUG

  return ::LoadLibraryExW(dir->c_str(), NULL,
                          LOAD_WITH_ALTERED_SEARCH_PATH);
}

// Set did_run "dr" in omaha's client state for this product.
bool SetDidRunState(const wchar_t* guid, const wchar_t* value) {
  std::wstring key_path(google_update::kRegPathClientState);
  key_path.append(L"\\").append(guid);
  HKEY reg_key;
  if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
                        REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
                        &reg_key, NULL) == ERROR_SUCCESS) {
    // Note that the length here must be in bytes and must account for the
    // terminating null char.
    ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ,
                     reinterpret_cast<const BYTE *>(value),
                     (::lstrlenW(value) + 1) * sizeof(wchar_t));
    ::RegCloseKey(reg_key);
    return true;
  }
  return false;
}

bool RecordDidRun(const wchar_t* guid) {
  return SetDidRunState(guid, L"1");
}

bool ClearDidRun(const wchar_t* guid) {
  return SetDidRunState(guid, L"0");
}

}
//=============================================================================

MainDllLoader::MainDllLoader() : dll_(NULL) {
}

MainDllLoader::~MainDllLoader() {
#ifdef PURIFY
  // We should never unload the dll. There is only risk and no gain from
  // doing so. The singleton dtors have been already run by AtExitManager.
  ::FreeLibrary(dll_);
#endif
}

// Loading chrome is an interesting afair. First we try loading from the current
// directory to support run-what-you-compile and other development scenarios.
// If that fails then we look at the 'CHROME_VERSION' env variable to determine
// if we should stick with an older dll version even if a new one is available
// to support upgrade-in-place scenarios, and if that fails we finally we look
// at the registry which should point us to the latest version.
HMODULE MainDllLoader::Load(std::wstring* version, std::wstring* file) {
  std::wstring dir(GetExecutablePath());
  *file = dir;
  HMODULE dll = LoadChromeWithDirectory(file);
  if (dll)
    return dll;

  if (!EnvQueryStr(
          BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
          version)) {
    std::wstring reg_path(GetRegistryPath());
    // Look into the registry to find the latest version.
    if (!GetVersion(dir.c_str(), reg_path.c_str(), version))
      return NULL;
  }

  *file = dir;
  file->append(*version).append(L"\\");
  return LoadChromeWithDirectory(file);
}

// Launching is a matter of loading the right dll, setting the CHROME_VERSION
// environment variable and just calling the entry point. Derived classes can
// add custom code in the OnBeforeLaunch callback.
int MainDllLoader::Launch(HINSTANCE instance,
                          sandbox::SandboxInterfaceInfo* sbox_info) {
  std::wstring version;
  std::wstring file;
  dll_ = Load(&version, &file);
  if (!dll_)
    return ResultCodes::MISSING_DATA;

  ::SetEnvironmentVariableW(
      BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
      version.c_str());

  InitCrashReporterWithDllPath(file);
  OnBeforeLaunch(version);

  DLL_MAIN entry_point =
      reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
  if (!entry_point)
    return ResultCodes::BAD_PROCESS_TYPE;

  int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
  return OnBeforeExit(rc);
}

void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
  RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
      reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
          ::GetProcAddress(dll_,
                           "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
  if (!relaunch_function) {
    LOG(ERROR) << "Could not find exported function "
               << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
  } else {
    relaunch_function();
  }
}

//=============================================================================

class ChromeDllLoader : public MainDllLoader {
 public:
  virtual std::wstring GetRegistryPath() {
    std::wstring key(google_update::kRegPathClients);
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    key.append(L"\\").append(dist->GetAppGuid());
    return key;
  }

  virtual void OnBeforeLaunch(const std::wstring& version) {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    RecordDidRun(dist->GetAppGuid().c_str());
  }

  virtual int OnBeforeExit(int return_code) {
    // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
    // so we need to reset the did_run signal so omaha does not count
    // this run as active usage.
    if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
      BrowserDistribution* dist = BrowserDistribution::GetDistribution();
      ClearDidRun(dist->GetAppGuid().c_str());
    }
    return return_code;
  }
};

//=============================================================================

class ChromiumDllLoader : public MainDllLoader {
 public:
  virtual std::wstring GetRegistryPath() {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    return dist->GetVersionKey();
  }
};

MainDllLoader* MakeMainDllLoader() {
#if defined(GOOGLE_CHROME_BUILD)
  return new ChromeDllLoader();
#else
  return new ChromiumDllLoader();
#endif
}
<commit_msg>After verifying the startup win though more testing, removing the guards around the pre-reading optimization.<commit_after>// Copyright (c) 2006-2008 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 <windows.h>
#include <shlwapi.h>

#include "base/file_util.h"
#include "chrome/app/breakpad_win.h"
#include "chrome/app/client_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/result_codes.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/util_constants.h"

namespace {
// The entry point signature of chrome.dll.
typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*);

typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();

// Not generic, we only handle strings up to 128 chars.
bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) {
  BYTE out[128 * sizeof(wchar_t)];
  DWORD size = sizeof(out);
  DWORD type = 0;
  if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size))
    return false;
  if (type != REG_SZ)
    return false;
  value->assign(reinterpret_cast<wchar_t*>(out));
  return true;
}

// Gets chrome version according to the load path. |exe_path| must be the
// backslash terminated directory of the current chrome.exe.
bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path,
                std::wstring* version) {
  HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER :
                                                            HKEY_LOCAL_MACHINE;
  HKEY key;
  if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS)
    return false;

  // If 'new_chrome.exe' is present it means chrome was auto-updated while
  // running. We need to consult the opv value so we can load the old dll.
  // TODO(cpu) : This is solving the same problem as the environment variable
  // so one of them will eventually be deprecated.
  std::wstring new_chrome_exe(exe_path);
  new_chrome_exe.append(installer_util::kChromeNewExe);
  if (::PathFileExistsW(new_chrome_exe.c_str()) &&
      ReadRegistryStr(key, google_update::kRegOldVersionField, version)) {
    ::RegCloseKey(key);
    return true;
  }

  bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version);
  ::RegCloseKey(key);
  return ret;
}

// Gets the path of the current exe with a trailing backslash.
std::wstring GetExecutablePath() {
  wchar_t path[MAX_PATH];
  ::GetModuleFileNameW(NULL, path, MAX_PATH);
  if (!::PathRemoveFileSpecW(path))
    return std::wstring();
  std::wstring exe_path(path);
  return exe_path.append(L"\\");
}

// Not generic, we only handle strings up to 128 chars.
bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) {
  wchar_t out[128];
  DWORD count = sizeof(out)/sizeof(out[0]);
  DWORD rv = ::GetEnvironmentVariableW(key_name, out, count);
  if ((rv == 0) || (rv >= count))
    return false;
  *value = out;
  return true;
}

// Expects that |dir| has a trailing backslash. |dir| is modified so it
// contains the full path that was tried. Caller must check for the return
// value not being null to dermine if this path contains a valid dll.
HMODULE LoadChromeWithDirectory(std::wstring* dir) {
  ::SetCurrentDirectoryW(dir->c_str());
  const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
#ifdef _WIN64
  if ((cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
      switches::kNaClBrokerProcess) ||
      (cmd_line.GetSwitchValueASCII(switches::kProcessType) ==
      switches::kNaClLoaderProcess)) {
    // Load the 64-bit DLL when running in a 64-bit process.
    dir->append(installer_util::kChromeNaCl64Dll);
  } else {
    // Only NaCl broker and loader can be launched as Win64 processes.
    NOTREACHED();
    return NULL;
  }
#else
  dir->append(installer_util::kChromeDll);
#endif

#ifdef NDEBUG
  // Experimental pre-reading optimization
  // The idea is to pre read significant portion of chrome.dll in advance
  // so that subsequent hard page faults are avoided.
  if (!cmd_line.HasSwitch(switches::kProcessType)) {
    // The kernel brings in 8 pages for the code section at a time and 4 pages
    // for other sections. We can skip over these pages to avoid a soft page
    // fault which may not occur during code execution. However skipping 4K at
    // a time still has better performance over 32K and 16K according to data.
    // TODO(ananta)
    // Investigate this and tune.
    const size_t kStepSize = 4 * 1024;

    DWORD pre_read_size = 0;
    DWORD pre_read_step_size = kStepSize;
    DWORD pre_read = 1;

    HKEY key = NULL;
    if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Google\\ChromeFrame",
                       0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
      DWORD unused = sizeof(pre_read_size);
      RegQueryValueEx(key, L"PreReadSize", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read_size), &unused);
      RegQueryValueEx(key, L"PreReadStepSize", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read_step_size), &unused);
      RegQueryValueEx(key, L"PreRead", NULL, NULL,
                      reinterpret_cast<LPBYTE>(&pre_read), &unused);
      RegCloseKey(key);
      key = NULL;
    }
    if (pre_read) {
      file_util::PreReadImage(dir->c_str(), pre_read_size, pre_read_step_size);
    }
  }
#endif  // NDEBUG

  return ::LoadLibraryExW(dir->c_str(), NULL,
                          LOAD_WITH_ALTERED_SEARCH_PATH);
}

// Set did_run "dr" in omaha's client state for this product.
bool SetDidRunState(const wchar_t* guid, const wchar_t* value) {
  std::wstring key_path(google_update::kRegPathClientState);
  key_path.append(L"\\").append(guid);
  HKEY reg_key;
  if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL,
                        REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
                        &reg_key, NULL) == ERROR_SUCCESS) {
    // Note that the length here must be in bytes and must account for the
    // terminating null char.
    ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ,
                     reinterpret_cast<const BYTE *>(value),
                     (::lstrlenW(value) + 1) * sizeof(wchar_t));
    ::RegCloseKey(reg_key);
    return true;
  }
  return false;
}

bool RecordDidRun(const wchar_t* guid) {
  return SetDidRunState(guid, L"1");
}

bool ClearDidRun(const wchar_t* guid) {
  return SetDidRunState(guid, L"0");
}

}
//=============================================================================

MainDllLoader::MainDllLoader() : dll_(NULL) {
}

MainDllLoader::~MainDllLoader() {
#ifdef PURIFY
  // We should never unload the dll. There is only risk and no gain from
  // doing so. The singleton dtors have been already run by AtExitManager.
  ::FreeLibrary(dll_);
#endif
}

// Loading chrome is an interesting afair. First we try loading from the current
// directory to support run-what-you-compile and other development scenarios.
// If that fails then we look at the 'CHROME_VERSION' env variable to determine
// if we should stick with an older dll version even if a new one is available
// to support upgrade-in-place scenarios, and if that fails we finally we look
// at the registry which should point us to the latest version.
HMODULE MainDllLoader::Load(std::wstring* version, std::wstring* file) {
  std::wstring dir(GetExecutablePath());
  *file = dir;
  HMODULE dll = LoadChromeWithDirectory(file);
  if (dll)
    return dll;

  if (!EnvQueryStr(
          BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
          version)) {
    std::wstring reg_path(GetRegistryPath());
    // Look into the registry to find the latest version.
    if (!GetVersion(dir.c_str(), reg_path.c_str(), version))
      return NULL;
  }

  *file = dir;
  file->append(*version).append(L"\\");
  return LoadChromeWithDirectory(file);
}

// Launching is a matter of loading the right dll, setting the CHROME_VERSION
// environment variable and just calling the entry point. Derived classes can
// add custom code in the OnBeforeLaunch callback.
int MainDllLoader::Launch(HINSTANCE instance,
                          sandbox::SandboxInterfaceInfo* sbox_info) {
  std::wstring version;
  std::wstring file;
  dll_ = Load(&version, &file);
  if (!dll_)
    return ResultCodes::MISSING_DATA;

  ::SetEnvironmentVariableW(
      BrowserDistribution::GetDistribution()->GetEnvVersionKey().c_str(),
      version.c_str());

  InitCrashReporterWithDllPath(file);
  OnBeforeLaunch(version);

  DLL_MAIN entry_point =
      reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
  if (!entry_point)
    return ResultCodes::BAD_PROCESS_TYPE;

  int rc = entry_point(instance, sbox_info, ::GetCommandLineW());
  return OnBeforeExit(rc);
}

void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
  RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
      reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
          ::GetProcAddress(dll_,
                           "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
  if (!relaunch_function) {
    LOG(ERROR) << "Could not find exported function "
               << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
  } else {
    relaunch_function();
  }
}

//=============================================================================

class ChromeDllLoader : public MainDllLoader {
 public:
  virtual std::wstring GetRegistryPath() {
    std::wstring key(google_update::kRegPathClients);
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    key.append(L"\\").append(dist->GetAppGuid());
    return key;
  }

  virtual void OnBeforeLaunch(const std::wstring& version) {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    RecordDidRun(dist->GetAppGuid().c_str());
  }

  virtual int OnBeforeExit(int return_code) {
    // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
    // so we need to reset the did_run signal so omaha does not count
    // this run as active usage.
    if (ResultCodes::NORMAL_EXIT_CANCEL == return_code) {
      BrowserDistribution* dist = BrowserDistribution::GetDistribution();
      ClearDidRun(dist->GetAppGuid().c_str());
    }
    return return_code;
  }
};

//=============================================================================

class ChromiumDllLoader : public MainDllLoader {
 public:
  virtual std::wstring GetRegistryPath() {
    BrowserDistribution* dist = BrowserDistribution::GetDistribution();
    return dist->GetVersionKey();
  }
};

MainDllLoader* MakeMainDllLoader() {
#if defined(GOOGLE_CHROME_BUILD)
  return new ChromeDllLoader();
#else
  return new ChromiumDllLoader();
#endif
}
<|endoftext|>


<commit_before><commit_msg>[GTTF] Make UI test framework less crashy by adding more NULL-checks.<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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/views/combo_box.h"

#include "base/gfx/native_theme.h"
#include "base/gfx/rect.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/gfx/chrome_font.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"

// Limit how small a combobox can be.
static const int kMinComboboxWidth = 148;

// Add a couple extra pixels to the widths of comboboxes and combobox
// dropdowns so that text isn't too crowded.
static const int kComboboxExtraPaddingX = 6;

namespace views {

ComboBox::ComboBox(Model* model)
    : model_(model), selected_item_(0), listener_(NULL), content_width_(0) {
}

ComboBox::~ComboBox() {
}

void ComboBox::SetListener(Listener* listener) {
  listener_ = listener;
}

gfx::Size ComboBox::GetPreferredSize() {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return gfx::Size();

  COMBOBOXINFO cbi;
  memset(reinterpret_cast<unsigned char*>(&cbi), 0, sizeof(cbi));
  cbi.cbSize = sizeof(cbi);
  ::SendMessage(hwnd, CB_GETCOMBOBOXINFO, 0, reinterpret_cast<LPARAM>(&cbi));
  gfx::Rect rect_item(cbi.rcItem);
  gfx::Rect rect_button(cbi.rcButton);
  gfx::Size border = gfx::NativeTheme::instance()->GetThemeBorderSize(
      gfx::NativeTheme::MENULIST);

  // The padding value of '3' is the xy offset from the corner of the control
  // to the corner of rcItem.  It does not seem to be queryable from the theme.
  // It is consistent on all versions of Windows from 2K to Vista, and is
  // invariant with respect to the combobox border size.  We could conceivably
  // get this number from rect_item.x, but it seems fragile to depend on position
  // here, inside of the layout code.
  const int kItemOffset = 3;
  int item_to_button_distance = std::max(kItemOffset - border.width(), 0);

  // The cx computation can be read as measuring from left to right.
  int pref_width = std::max(kItemOffset + content_width_ +
                                kComboboxExtraPaddingX +
                                item_to_button_distance + rect_button.width() +
                                 border.width(), kMinComboboxWidth);
  // The two arguments to ::max below should be typically be equal.
  int pref_height = std::max(rect_item.height() + 2 * kItemOffset,
                             rect_button.height() + 2 * border.height());
  return gfx::Size(pref_width, pref_height);
}

HWND ComboBox::CreateNativeControl(HWND parent_container) {
  HWND r = ::CreateWindowEx(GetAdditionalExStyle(), L"COMBOBOX", L"",
                            WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST,
                            0, 0, width(), height(),
                            parent_container, NULL, NULL, NULL);
  HFONT font = ResourceBundle::GetSharedInstance().
      GetFont(ResourceBundle::BaseFont).hfont();
  SendMessage(r, WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE);
  UpdateComboBoxFromModel(r);
  return r;
}

LRESULT ComboBox::OnCommand(UINT code, int id, HWND source) {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return 0;

  if (code == CBN_SELCHANGE && source == hwnd) {
    LRESULT r = ::SendMessage(hwnd, CB_GETCURSEL, 0, 0);
    if (r != CB_ERR) {
      int prev_selected_item = selected_item_;
      selected_item_ = static_cast<int>(r);
      if (listener_)
        listener_->ItemChanged(this, prev_selected_item, selected_item_);
    }
  }
  return 0;
}

LRESULT ComboBox::OnNotify(int w_param, LPNMHDR l_param) {
  return 0;
}

void ComboBox::UpdateComboBoxFromModel(HWND hwnd) {
  ::SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
  ChromeFont font = ResourceBundle::GetSharedInstance().GetFont(
      ResourceBundle::BaseFont);
  int max_width = 0;
  int num_items = model_->GetItemCount(this);
  for (int i = 0; i < num_items; ++i) {
    const std::wstring& text = model_->GetItemAt(this, i);

    // Inserting the Unicode formatting characters if necessary so that the
    // text is displayed correctly in right-to-left UIs.
    std::wstring localized_text;
    const wchar_t* text_ptr = text.c_str();
    if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text))
      text_ptr = localized_text.c_str();

    ::SendMessage(hwnd, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(text_ptr));
    max_width = std::max(max_width, font.GetStringWidth(text));

  }
  content_width_ = max_width;

  if (num_items > 0) {
    ::SendMessage(hwnd, CB_SETCURSEL, selected_item_, 0);

    // Set the width for the drop down while accounting for the scrollbar and
    // borders.
    if (num_items > ComboBox_GetMinVisible(hwnd))
      max_width += ::GetSystemMetrics(SM_CXVSCROLL);
    // SM_CXEDGE would not be correct here, since the dropdown is flat, not 3D.
    int kComboboxDropdownBorderSize = 1;
    max_width += 2 * kComboboxDropdownBorderSize + kComboboxExtraPaddingX;
    ::SendMessage(hwnd, CB_SETDROPPEDWIDTH, max_width, 0);
  }
}

void ComboBox::ModelChanged() {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return;
  selected_item_ = std::min(0, model_->GetItemCount(this));
  UpdateComboBoxFromModel(hwnd);
}

void ComboBox::SetSelectedItem(int index) {
  selected_item_ = index;
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return;

  // Note that we use CB_SETCURSEL and not CB_SELECTSTRING because on RTL
  // locales the strings we get from our ComboBox::Model might be augmented
  // with Unicode directionality marks before we insert them into the combo box
  // and therefore we can not assume that the string we get from
  // ComboBox::Model can be safely searched for and selected (which is what
  // CB_SELECTSTRING does).
  ::SendMessage(hwnd, CB_SETCURSEL, selected_item_, 0);
}

int ComboBox::GetSelectedItem() {
  return selected_item_;
}

}  // namespace views

<commit_msg>Fix a crash on 64-bit systems when an external message hook is installed. The proxying for CB_GETCOMBOXINFO is wrong and will crash. Instead, call GetComboBoxInfo.<commit_after>// Copyright (c) 2006-2008 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/views/combo_box.h"

#include "base/gfx/native_theme.h"
#include "base/gfx/rect.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/gfx/chrome_font.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/resource_bundle.h"

// Limit how small a combobox can be.
static const int kMinComboboxWidth = 148;

// Add a couple extra pixels to the widths of comboboxes and combobox
// dropdowns so that text isn't too crowded.
static const int kComboboxExtraPaddingX = 6;

namespace views {

ComboBox::ComboBox(Model* model)
    : model_(model), selected_item_(0), listener_(NULL), content_width_(0) {
}

ComboBox::~ComboBox() {
}

void ComboBox::SetListener(Listener* listener) {
  listener_ = listener;
}

gfx::Size ComboBox::GetPreferredSize() {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return gfx::Size();

  COMBOBOXINFO cbi;
  memset(reinterpret_cast<unsigned char*>(&cbi), 0, sizeof(cbi));
  cbi.cbSize = sizeof(cbi);
  // Note: Don't use CB_GETCOMBOBOXINFO since that crashes on WOW64 systems
  // when you have a global message hook installed.
  GetComboBoxInfo(hwnd, &cbi);
  gfx::Rect rect_item(cbi.rcItem);
  gfx::Rect rect_button(cbi.rcButton);
  gfx::Size border = gfx::NativeTheme::instance()->GetThemeBorderSize(
      gfx::NativeTheme::MENULIST);

  // The padding value of '3' is the xy offset from the corner of the control
  // to the corner of rcItem.  It does not seem to be queryable from the theme.
  // It is consistent on all versions of Windows from 2K to Vista, and is
  // invariant with respect to the combobox border size.  We could conceivably
  // get this number from rect_item.x, but it seems fragile to depend on position
  // here, inside of the layout code.
  const int kItemOffset = 3;
  int item_to_button_distance = std::max(kItemOffset - border.width(), 0);

  // The cx computation can be read as measuring from left to right.
  int pref_width = std::max(kItemOffset + content_width_ +
                                kComboboxExtraPaddingX +
                                item_to_button_distance + rect_button.width() +
                                 border.width(), kMinComboboxWidth);
  // The two arguments to ::max below should be typically be equal.
  int pref_height = std::max(rect_item.height() + 2 * kItemOffset,
                             rect_button.height() + 2 * border.height());
  return gfx::Size(pref_width, pref_height);
}

HWND ComboBox::CreateNativeControl(HWND parent_container) {
  HWND r = ::CreateWindowEx(GetAdditionalExStyle(), L"COMBOBOX", L"",
                            WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST,
                            0, 0, width(), height(),
                            parent_container, NULL, NULL, NULL);
  HFONT font = ResourceBundle::GetSharedInstance().
      GetFont(ResourceBundle::BaseFont).hfont();
  SendMessage(r, WM_SETFONT, reinterpret_cast<WPARAM>(font), FALSE);
  UpdateComboBoxFromModel(r);
  return r;
}

LRESULT ComboBox::OnCommand(UINT code, int id, HWND source) {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return 0;

  if (code == CBN_SELCHANGE && source == hwnd) {
    LRESULT r = ::SendMessage(hwnd, CB_GETCURSEL, 0, 0);
    if (r != CB_ERR) {
      int prev_selected_item = selected_item_;
      selected_item_ = static_cast<int>(r);
      if (listener_)
        listener_->ItemChanged(this, prev_selected_item, selected_item_);
    }
  }
  return 0;
}

LRESULT ComboBox::OnNotify(int w_param, LPNMHDR l_param) {
  return 0;
}

void ComboBox::UpdateComboBoxFromModel(HWND hwnd) {
  ::SendMessage(hwnd, CB_RESETCONTENT, 0, 0);
  ChromeFont font = ResourceBundle::GetSharedInstance().GetFont(
      ResourceBundle::BaseFont);
  int max_width = 0;
  int num_items = model_->GetItemCount(this);
  for (int i = 0; i < num_items; ++i) {
    const std::wstring& text = model_->GetItemAt(this, i);

    // Inserting the Unicode formatting characters if necessary so that the
    // text is displayed correctly in right-to-left UIs.
    std::wstring localized_text;
    const wchar_t* text_ptr = text.c_str();
    if (l10n_util::AdjustStringForLocaleDirection(text, &localized_text))
      text_ptr = localized_text.c_str();

    ::SendMessage(hwnd, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(text_ptr));
    max_width = std::max(max_width, font.GetStringWidth(text));

  }
  content_width_ = max_width;

  if (num_items > 0) {
    ::SendMessage(hwnd, CB_SETCURSEL, selected_item_, 0);

    // Set the width for the drop down while accounting for the scrollbar and
    // borders.
    if (num_items > ComboBox_GetMinVisible(hwnd))
      max_width += ::GetSystemMetrics(SM_CXVSCROLL);
    // SM_CXEDGE would not be correct here, since the dropdown is flat, not 3D.
    int kComboboxDropdownBorderSize = 1;
    max_width += 2 * kComboboxDropdownBorderSize + kComboboxExtraPaddingX;
    ::SendMessage(hwnd, CB_SETDROPPEDWIDTH, max_width, 0);
  }
}

void ComboBox::ModelChanged() {
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return;
  selected_item_ = std::min(0, model_->GetItemCount(this));
  UpdateComboBoxFromModel(hwnd);
}

void ComboBox::SetSelectedItem(int index) {
  selected_item_ = index;
  HWND hwnd = GetNativeControlHWND();
  if (!hwnd)
    return;

  // Note that we use CB_SETCURSEL and not CB_SELECTSTRING because on RTL
  // locales the strings we get from our ComboBox::Model might be augmented
  // with Unicode directionality marks before we insert them into the combo box
  // and therefore we can not assume that the string we get from
  // ComboBox::Model can be safely searched for and selected (which is what
  // CB_SELECTSTRING does).
  ::SendMessage(hwnd, CB_SETCURSEL, selected_item_, 0);
}

int ComboBox::GetSelectedItem() {
  return selected_item_;
}

}  // namespace views

<|endoftext|>