<commit_before>// Copyright (c) 2010 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/extensions/extension_popup_api.h"

#include "base/json/json_writer.h"
#include "base/string_util.h"
#include "chrome/browser/extensions/extension_dom_ui.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/url_constants.h"
#include "gfx/point.h"

#if defined(TOOLKIT_VIEWS)
#include "chrome/browser/views/extensions/extension_popup.h"
#include "views/view.h"
#include "views/focus/focus_manager.h"
#endif  // TOOLKIT_VIEWS

namespace extension_popup_module_events {

const char kOnPopupClosed[] = "experimental.popup.onClosed.%d";

}  // namespace extension_popup_module_events

namespace {

// Errors.
const char kBadAnchorArgument[] = "Invalid anchor argument.";
const char kInvalidURLError[] = "Invalid URL.";
const char kNotAnExtension[] = "Not an extension view.";
const char kPopupsDisallowed[] =
    "Popups are only supported from toolstrip or tab-contents views.";

// Keys.
const wchar_t kUrlKey[] = L"url";
const wchar_t kWidthKey[] = L"width";
const wchar_t kHeightKey[] = L"height";
const wchar_t kTopKey[] = L"top";
const wchar_t kLeftKey[] = L"left";
const wchar_t kGiveFocusKey[] = L"giveFocus";
const wchar_t kDomAnchorKey[] = L"domAnchor";
const wchar_t kBorderStyleKey[] = L"borderStyle";

// chrome enumeration values
const char kRectangleChrome[] = "rectangle";

};  // namespace

#if defined(TOOLKIT_VIEWS)
// ExtensionPopupHost objects implement the environment necessary to host
// an ExtensionPopup views for the popup api. Its main job is to handle
// its lifetime and to fire the popup-closed event when the popup is closed.
// Because the close-on-focus-lost behavior is different from page action
// and browser action, it also manages its own focus change listening. The
// difference in close-on-focus-lost is that in the page action and browser
// action cases, the popup closes when the focus leaves the popup or any of its
// children. In this case, the popup closes when the focus leaves the popups
// containing view or any of *its* children.
class ExtensionPopupHost : public ExtensionPopup::Observer,
                           public views::WidgetFocusChangeListener,
                           public base::RefCounted<ExtensionPopupHost>,
                           public NotificationObserver {
 public:
  explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher)
      : dispatcher_(dispatcher), popup_(NULL) {
    AddRef();  // Balanced in DispatchPopupClosedEvent().
    views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this);
  }

  ~ExtensionPopupHost() {
    views::FocusManager::GetWidgetFocusManager()->
        RemoveFocusChangeListener(this);
  }

  void set_popup(ExtensionPopup* popup) {
    popup_ = popup;

    // Now that a popup has been assigned, listen for subsequent popups being
    // created in the same extension - we want to disallow more than one
    // concurrently displayed popup windows.
    registrar_.Add(
        this,
        NotificationType::EXTENSION_HOST_CREATED,
        Source<ExtensionProcessManager>(
            dispatcher_->profile()->GetExtensionProcessManager()));
  }

  // Overridden from ExtensionPopup::Observer
  virtual void ExtensionPopupClosed(ExtensionPopup* popup) {
    // Unregister the automation resource routing registered upon host
    // creation.
    AutomationResourceRoutingDelegate* router =
        GetRoutingFromDispatcher(dispatcher_);
    if (router)
      router->UnregisterRenderViewHost(popup_->host()->render_view_host());

    // The OnPopupClosed event should be sent later to give the popup time to
    // complete closing.
    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
      &ExtensionPopupHost::DispatchPopupClosedEvent));
  }

  virtual void ExtensionHostCreated(ExtensionHost* host) {
    // Pop-up views should share the same automation routing configuration as
    // their hosting views, so register the RenderViewHost of the pop-up with
    // the AutomationResourceRoutingDelegate interface of the dispatcher.
    AutomationResourceRoutingDelegate* router =
        GetRoutingFromDispatcher(dispatcher_);
    if (router)
      router->RegisterRenderViewHost(host->render_view_host());
  }

  virtual void DispatchPopupClosedEvent() {
    PopupEventRouter::OnPopupClosed(
        dispatcher_->profile(), dispatcher_->render_view_host()->routing_id());
    dispatcher_ = NULL;
    Release();  // Balanced in ctor.
  }

  // Overridden from views::WidgetFocusChangeListener
  virtual void NativeFocusWillChange(gfx::NativeView focused_before,
                                     gfx::NativeView focused_now) {
    // If no view is to be focused, then Chrome was deactivated, so hide the
    // popup.
    if (focused_now) {
      gfx::NativeView host_view =
          dispatcher_->delegate()->GetNativeViewOfHost();

      // If the widget hosting the popup contains the newly focused view, then
      // don't dismiss the pop-up. Note that an ExtensionPopup must have an
      // ExtensionHost, but an ExtensionHost may or may not have an
      // ExtensionView view. We check to make sure.
      ExtensionView* view = popup_->host()->view();
      if (view) {
        views::Widget* popup_root_widget = view->GetWidget();
        if (popup_root_widget &&
            popup_root_widget->ContainsNativeView(focused_now))
          return;
      }

      // If the widget or RenderWidgetHostView hosting the extension that
      // launched the pop-up is receiving focus, then don't dismiss the popup.
      views::Widget* host_widget =
          views::Widget::GetWidgetFromNativeView(host_view);
      if (host_widget && host_widget->ContainsNativeView(focused_now))
        return;

      RenderWidgetHostView* render_host_view =
          RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView(
              host_view);
      if (render_host_view &&
          render_host_view->ContainsNativeView(focused_now))
        return;
    }

    // We are careful here to let the current event loop unwind before
    // causing the popup to be closed.
    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(popup_,
        &ExtensionPopup::Close));
  }

  // Overridden from NotificationObserver
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(NotificationType::EXTENSION_HOST_CREATED == type);
    if (NotificationType::EXTENSION_HOST_CREATED == type) {
      Details<ExtensionHost> details_host(details);
      // Disallow multiple pop-ups from the same extension, by closing
      // the presently opened popup during construction of any new popups.
      if (ViewType::EXTENSION_POPUP == details_host->GetRenderViewType() &&
          popup_->host()->extension() == details_host->extension() &&
          Details<ExtensionHost>(popup_->host()) != details) {
        popup_->Close();
      }
    }
  }

 private:
  // Returns the AutomationResourceRoutingDelegate interface for |dispatcher|.
  static AutomationResourceRoutingDelegate*
      GetRoutingFromDispatcher(ExtensionFunctionDispatcher* dispatcher) {
    if (!dispatcher)
      return NULL;

    RenderViewHost* render_view_host = dispatcher->render_view_host();
    RenderViewHostDelegate* delegate =
        render_view_host ? render_view_host->delegate() : NULL;

    return delegate ? delegate->GetAutomationResourceRoutingDelegate() : NULL;
  }

  // A pointer to the dispatcher that handled the request that opened this
  // popup view.
  ExtensionFunctionDispatcher* dispatcher_;

  // A pointer to the popup.
  ExtensionPopup* popup_;

  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost);
};
#endif  // TOOLKIT_VIEWS

PopupShowFunction::PopupShowFunction()
#if defined (TOOLKIT_VIEWS)
    : popup_(NULL)
#endif
{}

void PopupShowFunction::Run() {
#if defined(TOOLKIT_VIEWS)
  if (!RunImpl()) {
    SendResponse(false);
  } else {
    // If the contents of the popup are already available, then immediately
    // send the response.  Otherwise wait for the EXTENSION_POPUP_VIEW_READY
    // notification.
    if (popup_->host() && popup_->host()->document_element_available()) {
      SendResponse(true);
    } else {
      AddRef();
      registrar_.Add(this, NotificationType::EXTENSION_POPUP_VIEW_READY,
                     NotificationService::AllSources());
      registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
                     NotificationService::AllSources());
    }
  }
#else
  SendResponse(false);
#endif
}

bool PopupShowFunction::RunImpl() {
  // Popups may only be displayed from TAB_CONTENTS and EXTENSION_TOOLSTRIP
  // views.
  ViewType::Type view_type =
      dispatcher()->render_view_host()->delegate()->GetRenderViewType();
  if (ViewType::EXTENSION_TOOLSTRIP != view_type &&
      ViewType::TAB_CONTENTS != view_type) {
    error_ = kPopupsDisallowed;
    return false;
  }

  EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
  const ListValue* args = args_as_list();

  std::string url_string;
  EXTENSION_FUNCTION_VALIDATE(args->GetString(0, &url_string));

  DictionaryValue* show_details = NULL;
  EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(1, &show_details));

  DictionaryValue* dom_anchor = NULL;
  EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kDomAnchorKey,
                                                          &dom_anchor));

  int dom_top, dom_left;
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kTopKey,
                                                     &dom_top));
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kLeftKey,
                                                     &dom_left));

  int dom_width, dom_height;
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kWidthKey,
                                                     &dom_width));
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kHeightKey,
                                                     &dom_height));
  EXTENSION_FUNCTION_VALIDATE(dom_top >= 0 && dom_left >= 0 &&
                              dom_width >= 0 && dom_height >= 0);

  // The default behaviour is to give the focus to the pop-up window.
  bool give_focus = true;
  if (show_details->HasKey(kGiveFocusKey)) {
    EXTENSION_FUNCTION_VALIDATE(show_details->GetBoolean(kGiveFocusKey,
                                                         &give_focus));
  }

#if defined(TOOLKIT_VIEWS)
  // The default behaviour is to provide the bubble-chrome to the popup.
  ExtensionPopup::PopupChrome chrome = ExtensionPopup::BUBBLE_CHROME;
  if (show_details->HasKey(kBorderStyleKey)) {
    std::string chrome_string;
    EXTENSION_FUNCTION_VALIDATE(show_details->GetString(kBorderStyleKey,
                                                        &chrome_string));
    if (chrome_string == kRectangleChrome)
      chrome = ExtensionPopup::RECTANGLE_CHROME;
  }
#endif

  GURL url = dispatcher()->url().Resolve(url_string);
  if (!url.is_valid()) {
    error_ = kInvalidURLError;
    return false;
  }

  // Disallow non-extension requests, or requests outside of the requesting
  // extension view's extension.
  const std::string& extension_id = url.host();
  if (extension_id != GetExtension()->id() ||
      !url.SchemeIs(chrome::kExtensionScheme)) {
    error_ = kInvalidURLError;
    return false;
  }

  gfx::Point origin(dom_left, dom_top);
  if (!dispatcher()->render_view_host()->view()) {
    error_ = kNotAnExtension;
    return false;
  }

  gfx::Rect content_bounds =
      dispatcher()->render_view_host()->view()->GetViewBounds();
  origin.Offset(content_bounds.x(), content_bounds.y());
  gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height);

  // Get the correct native window to pass to ExtensionPopup.
  // ExtensionFunctionDispatcher::Delegate may provide a custom implementation
  // of this.
  gfx::NativeWindow window =
      dispatcher()->delegate()->GetCustomFrameNativeWindow();
  if (!window)
    window = GetCurrentBrowser()->window()->GetNativeHandle();

#if defined(TOOLKIT_VIEWS)
  // Pop-up from extension views (ExtensionShelf, etc.), and drop-down when
  // in a TabContents view.
  BubbleBorder::ArrowLocation arrow_location =
      view_type == ViewType::TAB_CONTENTS ?
          BubbleBorder::TOP_LEFT : BubbleBorder::BOTTOM_LEFT;

  // ExtensionPopupHost manages it's own lifetime.
  ExtensionPopupHost* popup_host = new ExtensionPopupHost(dispatcher());
  popup_ = ExtensionPopup::Show(url,
                                GetCurrentBrowser(),
                                dispatcher()->profile(),
                                window,
                                rect,
                                arrow_location,
                                give_focus,
                                false,  // inspect_with_devtools
                                chrome,
                                popup_host);  // ExtensionPopup::Observer

  // popup_host will handle focus change listening and close the popup when
  // focus leaves the containing views hierarchy.
  popup_->set_close_on_lost_focus(false);
  popup_host->set_popup(popup_);
#endif  // defined(TOOLKIT_VIEWS)
  return true;
}

void PopupShowFunction::Observe(NotificationType type,
                                const NotificationSource& source,
                                const NotificationDetails& details) {
#if defined(TOOLKIT_VIEWS)
  DCHECK(type == NotificationType::EXTENSION_POPUP_VIEW_READY ||
         type == NotificationType::EXTENSION_HOST_DESTROYED);
  DCHECK(popup_ != NULL);

  // Wait for notification that the popup view is ready (and onload has been
  // called), before completing the API call.
  if (popup_ && type == NotificationType::EXTENSION_POPUP_VIEW_READY &&
      Details<ExtensionHost>(popup_->host()) == details) {
    SendResponse(true);
    Release();  // Balanced in Run().
  } else if (popup_ && type == NotificationType::EXTENSION_HOST_DESTROYED &&
             Details<ExtensionHost>(popup_->host()) == details) {
    // If the host was destroyed, then report failure, and release the remaining
    // reference.
    SendResponse(false);
    Release();  // Balanced in Run().
  }
#endif  // defined(TOOLKIT_VIEWS)
}

// static
void PopupEventRouter::OnPopupClosed(Profile* profile,
                                     int routing_id) {
  std::string full_event_name = StringPrintf(
      extension_popup_module_events::kOnPopupClosed,
      routing_id);

  profile->GetExtensionMessageService()->DispatchEventToRenderers(
      full_event_name,
      base::JSONWriter::kEmptyArray,
      profile->IsOffTheRecord(),
      GURL());
}
<commit_msg>Quick fix to browser tests failure for http://codereview.chromium.org/1921003 Bug=none Test=none<commit_after>// Copyright (c) 2010 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/extensions/extension_popup_api.h"

#include "base/json/json_writer.h"
#include "base/string_util.h"
#include "chrome/browser/extensions/extension_dom_ui.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_message_service.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/url_constants.h"
#include "gfx/point.h"

#if defined(TOOLKIT_VIEWS)
#include "chrome/browser/views/extensions/extension_popup.h"
#include "views/view.h"
#include "views/focus/focus_manager.h"
#endif  // TOOLKIT_VIEWS

namespace extension_popup_module_events {

const char kOnPopupClosed[] = "experimental.popup.onClosed.%d";

}  // namespace extension_popup_module_events

namespace {

// Errors.
const char kBadAnchorArgument[] = "Invalid anchor argument.";
const char kInvalidURLError[] = "Invalid URL.";
const char kNotAnExtension[] = "Not an extension view.";
const char kPopupsDisallowed[] =
    "Popups are only supported from toolstrip or tab-contents views.";

// Keys.
const wchar_t kUrlKey[] = L"url";
const wchar_t kWidthKey[] = L"width";
const wchar_t kHeightKey[] = L"height";
const wchar_t kTopKey[] = L"top";
const wchar_t kLeftKey[] = L"left";
const wchar_t kGiveFocusKey[] = L"giveFocus";
const wchar_t kDomAnchorKey[] = L"domAnchor";
const wchar_t kBorderStyleKey[] = L"borderStyle";

// chrome enumeration values
const char kRectangleChrome[] = "rectangle";

};  // namespace

#if defined(TOOLKIT_VIEWS)
// ExtensionPopupHost objects implement the environment necessary to host
// an ExtensionPopup views for the popup api. Its main job is to handle
// its lifetime and to fire the popup-closed event when the popup is closed.
// Because the close-on-focus-lost behavior is different from page action
// and browser action, it also manages its own focus change listening. The
// difference in close-on-focus-lost is that in the page action and browser
// action cases, the popup closes when the focus leaves the popup or any of its
// children. In this case, the popup closes when the focus leaves the popups
// containing view or any of *its* children.
class ExtensionPopupHost : public ExtensionPopup::Observer,
                           public views::WidgetFocusChangeListener,
                           public base::RefCounted<ExtensionPopupHost>,
                           public NotificationObserver {
 public:
  explicit ExtensionPopupHost(ExtensionFunctionDispatcher* dispatcher)
      : dispatcher_(dispatcher), popup_(NULL) {
    AddRef();  // Balanced in DispatchPopupClosedEvent().
    views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this);
  }

  ~ExtensionPopupHost() {
    views::FocusManager::GetWidgetFocusManager()->
        RemoveFocusChangeListener(this);
  }

  void set_popup(ExtensionPopup* popup) {
    popup_ = popup;

    // Now that a popup has been assigned, listen for subsequent popups being
    // created in the same extension - we want to disallow more than one
    // concurrently displayed popup windows.
    registrar_.Add(
        this,
        NotificationType::EXTENSION_HOST_CREATED,
        Source<ExtensionProcessManager>(
            dispatcher_->profile()->GetExtensionProcessManager()));
  }

  // Overridden from ExtensionPopup::Observer
  virtual void ExtensionPopupClosed(ExtensionPopup* popup) {
    // Unregister the automation resource routing registered upon host
    // creation.
    AutomationResourceRoutingDelegate* router =
        GetRoutingFromDispatcher(dispatcher_);
    if (router)
      router->UnregisterRenderViewHost(popup_->host()->render_view_host());

    // The OnPopupClosed event should be sent later to give the popup time to
    // complete closing.
    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(this,
      &ExtensionPopupHost::DispatchPopupClosedEvent));
  }

  virtual void ExtensionHostCreated(ExtensionHost* host) {
    // Pop-up views should share the same automation routing configuration as
    // their hosting views, so register the RenderViewHost of the pop-up with
    // the AutomationResourceRoutingDelegate interface of the dispatcher.
    AutomationResourceRoutingDelegate* router =
        GetRoutingFromDispatcher(dispatcher_);
    if (router)
      router->RegisterRenderViewHost(host->render_view_host());
  }

  virtual void DispatchPopupClosedEvent() {
    PopupEventRouter::OnPopupClosed(
        dispatcher_->profile(), dispatcher_->render_view_host()->routing_id());
    dispatcher_ = NULL;
    Release();  // Balanced in ctor.
  }

  // Overridden from views::WidgetFocusChangeListener
  virtual void NativeFocusWillChange(gfx::NativeView focused_before,
                                     gfx::NativeView focused_now) {
    // If no view is to be focused, then Chrome was deactivated, so hide the
    // popup.
    if (focused_now) {
      gfx::NativeView host_view =
          dispatcher_->delegate()->GetNativeViewOfHost();

      // If the widget hosting the popup contains the newly focused view, then
      // don't dismiss the pop-up. Note that an ExtensionPopup must have an
      // ExtensionHost, but an ExtensionHost may or may not have an
      // ExtensionView view. We check to make sure.
      if (popup_) {
        ExtensionView* view = popup_->host()->view();
        if (view) {
          views::Widget* popup_root_widget = view->GetWidget();
          if (popup_root_widget &&
              popup_root_widget->ContainsNativeView(focused_now))
            return;
        }
      }

      // If the widget or RenderWidgetHostView hosting the extension that
      // launched the pop-up is receiving focus, then don't dismiss the popup.
      views::Widget* host_widget =
          views::Widget::GetWidgetFromNativeView(host_view);
      if (host_widget && host_widget->ContainsNativeView(focused_now))
        return;

      RenderWidgetHostView* render_host_view =
          RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView(
              host_view);
      if (render_host_view &&
          render_host_view->ContainsNativeView(focused_now))
        return;
    }

    // We are careful here to let the current event loop unwind before
    // causing the popup to be closed.
    MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(popup_,
        &ExtensionPopup::Close));
  }

  // Overridden from NotificationObserver
  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(NotificationType::EXTENSION_HOST_CREATED == type);
    if (NotificationType::EXTENSION_HOST_CREATED == type) {
      Details<ExtensionHost> details_host(details);
      // Disallow multiple pop-ups from the same extension, by closing
      // the presently opened popup during construction of any new popups.
      if (ViewType::EXTENSION_POPUP == details_host->GetRenderViewType() &&
          popup_->host()->extension() == details_host->extension() &&
          Details<ExtensionHost>(popup_->host()) != details) {
        popup_->Close();
      }
    }
  }

 private:
  // Returns the AutomationResourceRoutingDelegate interface for |dispatcher|.
  static AutomationResourceRoutingDelegate*
      GetRoutingFromDispatcher(ExtensionFunctionDispatcher* dispatcher) {
    if (!dispatcher)
      return NULL;

    RenderViewHost* render_view_host = dispatcher->render_view_host();
    RenderViewHostDelegate* delegate =
        render_view_host ? render_view_host->delegate() : NULL;

    return delegate ? delegate->GetAutomationResourceRoutingDelegate() : NULL;
  }

  // A pointer to the dispatcher that handled the request that opened this
  // popup view.
  ExtensionFunctionDispatcher* dispatcher_;

  // A pointer to the popup.
  ExtensionPopup* popup_;

  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(ExtensionPopupHost);
};
#endif  // TOOLKIT_VIEWS

PopupShowFunction::PopupShowFunction()
#if defined (TOOLKIT_VIEWS)
    : popup_(NULL)
#endif
{}

void PopupShowFunction::Run() {
#if defined(TOOLKIT_VIEWS)
  if (!RunImpl()) {
    SendResponse(false);
  } else {
    // If the contents of the popup are already available, then immediately
    // send the response.  Otherwise wait for the EXTENSION_POPUP_VIEW_READY
    // notification.
    if (popup_->host() && popup_->host()->document_element_available()) {
      SendResponse(true);
    } else {
      AddRef();
      registrar_.Add(this, NotificationType::EXTENSION_POPUP_VIEW_READY,
                     NotificationService::AllSources());
      registrar_.Add(this, NotificationType::EXTENSION_HOST_DESTROYED,
                     NotificationService::AllSources());
    }
  }
#else
  SendResponse(false);
#endif
}

bool PopupShowFunction::RunImpl() {
  // Popups may only be displayed from TAB_CONTENTS and EXTENSION_TOOLSTRIP
  // views.
  ViewType::Type view_type =
      dispatcher()->render_view_host()->delegate()->GetRenderViewType();
  if (ViewType::EXTENSION_TOOLSTRIP != view_type &&
      ViewType::TAB_CONTENTS != view_type) {
    error_ = kPopupsDisallowed;
    return false;
  }

  EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST));
  const ListValue* args = args_as_list();

  std::string url_string;
  EXTENSION_FUNCTION_VALIDATE(args->GetString(0, &url_string));

  DictionaryValue* show_details = NULL;
  EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(1, &show_details));

  DictionaryValue* dom_anchor = NULL;
  EXTENSION_FUNCTION_VALIDATE(show_details->GetDictionary(kDomAnchorKey,
                                                          &dom_anchor));

  int dom_top, dom_left;
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kTopKey,
                                                     &dom_top));
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kLeftKey,
                                                     &dom_left));

  int dom_width, dom_height;
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kWidthKey,
                                                     &dom_width));
  EXTENSION_FUNCTION_VALIDATE(dom_anchor->GetInteger(kHeightKey,
                                                     &dom_height));
  EXTENSION_FUNCTION_VALIDATE(dom_top >= 0 && dom_left >= 0 &&
                              dom_width >= 0 && dom_height >= 0);

  // The default behaviour is to give the focus to the pop-up window.
  bool give_focus = true;
  if (show_details->HasKey(kGiveFocusKey)) {
    EXTENSION_FUNCTION_VALIDATE(show_details->GetBoolean(kGiveFocusKey,
                                                         &give_focus));
  }

#if defined(TOOLKIT_VIEWS)
  // The default behaviour is to provide the bubble-chrome to the popup.
  ExtensionPopup::PopupChrome chrome = ExtensionPopup::BUBBLE_CHROME;
  if (show_details->HasKey(kBorderStyleKey)) {
    std::string chrome_string;
    EXTENSION_FUNCTION_VALIDATE(show_details->GetString(kBorderStyleKey,
                                                        &chrome_string));
    if (chrome_string == kRectangleChrome)
      chrome = ExtensionPopup::RECTANGLE_CHROME;
  }
#endif

  GURL url = dispatcher()->url().Resolve(url_string);
  if (!url.is_valid()) {
    error_ = kInvalidURLError;
    return false;
  }

  // Disallow non-extension requests, or requests outside of the requesting
  // extension view's extension.
  const std::string& extension_id = url.host();
  if (extension_id != GetExtension()->id() ||
      !url.SchemeIs(chrome::kExtensionScheme)) {
    error_ = kInvalidURLError;
    return false;
  }

  gfx::Point origin(dom_left, dom_top);
  if (!dispatcher()->render_view_host()->view()) {
    error_ = kNotAnExtension;
    return false;
  }

  gfx::Rect content_bounds =
      dispatcher()->render_view_host()->view()->GetViewBounds();
  origin.Offset(content_bounds.x(), content_bounds.y());
  gfx::Rect rect(origin.x(), origin.y(), dom_width, dom_height);

  // Get the correct native window to pass to ExtensionPopup.
  // ExtensionFunctionDispatcher::Delegate may provide a custom implementation
  // of this.
  gfx::NativeWindow window =
      dispatcher()->delegate()->GetCustomFrameNativeWindow();
  if (!window)
    window = GetCurrentBrowser()->window()->GetNativeHandle();

#if defined(TOOLKIT_VIEWS)
  // Pop-up from extension views (ExtensionShelf, etc.), and drop-down when
  // in a TabContents view.
  BubbleBorder::ArrowLocation arrow_location =
      view_type == ViewType::TAB_CONTENTS ?
          BubbleBorder::TOP_LEFT : BubbleBorder::BOTTOM_LEFT;

  // ExtensionPopupHost manages it's own lifetime.
  ExtensionPopupHost* popup_host = new ExtensionPopupHost(dispatcher());
  popup_ = ExtensionPopup::Show(url,
                                GetCurrentBrowser(),
                                dispatcher()->profile(),
                                window,
                                rect,
                                arrow_location,
                                give_focus,
                                false,  // inspect_with_devtools
                                chrome,
                                popup_host);  // ExtensionPopup::Observer

  // popup_host will handle focus change listening and close the popup when
  // focus leaves the containing views hierarchy.
  popup_->set_close_on_lost_focus(false);
  popup_host->set_popup(popup_);
#endif  // defined(TOOLKIT_VIEWS)
  return true;
}

void PopupShowFunction::Observe(NotificationType type,
                                const NotificationSource& source,
                                const NotificationDetails& details) {
#if defined(TOOLKIT_VIEWS)
  DCHECK(type == NotificationType::EXTENSION_POPUP_VIEW_READY ||
         type == NotificationType::EXTENSION_HOST_DESTROYED);
  DCHECK(popup_ != NULL);

  // Wait for notification that the popup view is ready (and onload has been
  // called), before completing the API call.
  if (popup_ && type == NotificationType::EXTENSION_POPUP_VIEW_READY &&
      Details<ExtensionHost>(popup_->host()) == details) {
    SendResponse(true);
    Release();  // Balanced in Run().
  } else if (popup_ && type == NotificationType::EXTENSION_HOST_DESTROYED &&
             Details<ExtensionHost>(popup_->host()) == details) {
    // If the host was destroyed, then report failure, and release the remaining
    // reference.
    SendResponse(false);
    Release();  // Balanced in Run().
  }
#endif  // defined(TOOLKIT_VIEWS)
}

// static
void PopupEventRouter::OnPopupClosed(Profile* profile,
                                     int routing_id) {
  std::string full_event_name = StringPrintf(
      extension_popup_module_events::kOnPopupClosed,
      routing_id);

  profile->GetExtensionMessageService()->DispatchEventToRenderers(
      full_event_name,
      base::JSONWriter::kEmptyArray,
      profile->IsOffTheRecord(),
      GURL());
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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/gtk/content_setting_bubble_gtk.h"

#include "app/l10n_util.h"
#include "base/i18n/rtl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/blocked_content_container.h"
#include "chrome/browser/content_setting_bubble_model.h"
#include "chrome/browser/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/options/content_settings_window_gtk.h"
#include "chrome/browser/host_content_settings_map.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/content_settings.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "gfx/gtk_util.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "webkit/glue/plugins/plugin_list.h"

// Padding between content and edge of info bubble.
static const int kContentBorder = 7;

ContentSettingBubbleGtk::ContentSettingBubbleGtk(
    GtkWidget* anchor,
    InfoBubbleGtkDelegate* delegate,
    ContentSettingBubbleModel* content_setting_bubble_model,
    Profile* profile,
    TabContents* tab_contents)
    : anchor_(anchor),
      profile_(profile),
      tab_contents_(tab_contents),
      delegate_(delegate),
      content_setting_bubble_model_(content_setting_bubble_model),
      info_bubble_(NULL) {
  registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
                 Source<TabContents>(tab_contents));
  BuildBubble();
}

ContentSettingBubbleGtk::~ContentSettingBubbleGtk() {
}

void ContentSettingBubbleGtk::Close() {
  if (info_bubble_)
    info_bubble_->Close();
}

void ContentSettingBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
                                                bool closed_by_escape) {
  delegate_->InfoBubbleClosing(info_bubble, closed_by_escape);
  delete this;
}

void ContentSettingBubbleGtk::Observe(NotificationType type,
                                      const NotificationSource& source,
                                      const NotificationDetails& details) {
  DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
  DCHECK(source == Source<TabContents>(tab_contents_));
  tab_contents_ = NULL;
}

void ContentSettingBubbleGtk::BuildBubble() {
  GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom(profile_);

  GtkWidget* bubble_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
  gtk_container_set_border_width(GTK_CONTAINER(bubble_content), kContentBorder);

  const ContentSettingBubbleModel::BubbleContent& content =
      content_setting_bubble_model_->bubble_content();
  if (!content.title.empty()) {
    // Add the content label.
    GtkWidget* label = gtk_label_new(content.title.c_str());
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    gtk_box_pack_start(GTK_BOX(bubble_content), label, FALSE, FALSE, 0);
  }

  const std::set<std::string>& plugins = content.resource_identifiers;
  if (!plugins.empty()) {
    GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);

    for (std::set<std::string>::const_iterator it = plugins.begin();
        it != plugins.end(); ++it) {
      std::string name;
      NPAPI::PluginList::PluginMap groups;
      NPAPI::PluginList::Singleton()->GetPluginGroups(false, &groups);
      if (groups.find(*it) != groups.end())
        name = UTF16ToUTF8(groups[*it]->GetGroupName());
      else
        name = *it;

      GtkWidget* label = gtk_label_new(name.c_str());
      GtkWidget* label_box = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0);

      gtk_box_pack_start(GTK_BOX(list_content),
                         label_box,
                         FALSE, FALSE, 0);
    }
    gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE,
                       gtk_util::kControlSpacing);
  }

  if (content_setting_bubble_model_->content_type() ==
      CONTENT_SETTINGS_TYPE_POPUPS) {
    const std::vector<ContentSettingBubbleModel::PopupItem>& popup_items =
        content.popup_items;
    GtkWidget* table = gtk_table_new(popup_items.size(), 2, FALSE);
    int row = 0;
    for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
         i(popup_items.begin()); i != popup_items.end(); ++i, ++row) {
      GtkWidget* image = gtk_image_new();
      if (!i->bitmap.empty()) {
        GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&i->bitmap);
        gtk_image_set_from_pixbuf(GTK_IMAGE(image), icon_pixbuf);
        g_object_unref(icon_pixbuf);

        // We stuff the image in an event box so we can trap mouse clicks on the
        // image (and launch the popup).
        GtkWidget* event_box = gtk_event_box_new();
        gtk_container_add(GTK_CONTAINER(event_box), image);

        popup_icons_[event_box] = i -popup_items.begin();
        g_signal_connect(event_box, "button_press_event",
                         G_CALLBACK(OnPopupIconButtonPressThunk), this);
        gtk_table_attach(GTK_TABLE(table), event_box, 0, 1, row, row + 1,
                         GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2,
                         gtk_util::kControlSpacing / 2);
      }

      GtkWidget* button = gtk_chrome_link_button_new(i->title.c_str());
      popup_links_[button] = i -popup_items.begin();
      g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClickedThunk),
                       this);
      gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1,
                       GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2,
                       gtk_util::kControlSpacing / 2);
    }

    gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0);
  }

  if (content_setting_bubble_model_->content_type() !=
      CONTENT_SETTINGS_TYPE_COOKIES) {
    const ContentSettingBubbleModel::RadioGroup& radio_group =
        content.radio_group;
    for (ContentSettingBubbleModel::RadioItems::const_iterator i =
         radio_group.radio_items.begin();
         i != radio_group.radio_items.end(); ++i) {
      GtkWidget* radio =
          radio_group_gtk_.empty() ?
              gtk_radio_button_new_with_label(NULL, i->c_str()) :
              gtk_radio_button_new_with_label_from_widget(
                  GTK_RADIO_BUTTON(radio_group_gtk_[0]),
                  i->c_str());
      gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0);
      if (i - radio_group.radio_items.begin() == radio_group.default_item) {
        // We must set the default value before we attach the signal handlers
        // or pain occurs.
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
      }
      radio_group_gtk_.push_back(radio);
    }
    for (std::vector<GtkWidget*>::const_iterator i = radio_group_gtk_.begin();
         i != radio_group_gtk_.end(); ++i) {
      // We can attach signal handlers now that all defaults are set.
      g_signal_connect(*i, "toggled", G_CALLBACK(OnRadioToggledThunk), this);
    }
    if (!radio_group_gtk_.empty()) {
      gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(), FALSE,
                         FALSE, 0);
    }
  }

  for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i =
       content.domain_lists.begin();
       i != content.domain_lists.end(); ++i) {
    // Put each list into its own vbox to allow spacing between lists.
    GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);

    GtkWidget* label = gtk_label_new(i->title.c_str());
    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    GtkWidget* label_box = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(list_content), label_box, FALSE, FALSE, 0);
    for (std::set<std::string>::const_iterator j = i->hosts.begin();
         j != i->hosts.end(); ++j) {
      gtk_box_pack_start(GTK_BOX(list_content),
                         gtk_util::IndentWidget(gtk_util::CreateBoldLabel(*j)),
                         FALSE, FALSE, 0);
    }
    gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE,
                       gtk_util::kControlSpacing);
  }

  if (!content.clear_link.empty()) {
    GtkWidget* clear_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* clear_link = gtk_chrome_link_button_new(
        content.clear_link.c_str());
    g_signal_connect(clear_link, "clicked", G_CALLBACK(OnClearLinkClickedThunk),
                     this);
    gtk_box_pack_start(GTK_BOX(clear_link_box), clear_link, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), clear_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  if (!content.info_link.empty()) {
    GtkWidget* info_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* info_link = gtk_chrome_link_button_new(
        content.info_link.c_str());
    g_signal_connect(info_link, "clicked", G_CALLBACK(OnInfoLinkClickedThunk),
                     this);
    gtk_box_pack_start(GTK_BOX(info_link_box), info_link, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), info_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  if (!content.load_plugins_link_title.empty()) {
    GtkWidget* load_plugins_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* load_plugins_link = gtk_chrome_link_button_new(
        content.load_plugins_link_title.c_str());
    gtk_widget_set_sensitive(load_plugins_link,
                             content.load_plugins_link_enabled);
    g_signal_connect(load_plugins_link, "clicked",
                     G_CALLBACK(OnLoadPluginsLinkClickedThunk), this);
    gtk_box_pack_start(GTK_BOX(load_plugins_link_box), load_plugins_link,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), load_plugins_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  GtkWidget* bottom_box = gtk_hbox_new(FALSE, 0);

  GtkWidget* manage_link =
      gtk_chrome_link_button_new(content.manage_link.c_str());
  g_signal_connect(manage_link, "clicked", G_CALLBACK(OnManageLinkClickedThunk),
                   this);
  gtk_box_pack_start(GTK_BOX(bottom_box), manage_link, FALSE, FALSE, 0);

  GtkWidget* button = gtk_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_DONE).c_str());
  g_signal_connect(button, "clicked", G_CALLBACK(OnCloseButtonClickedThunk),
                   this);
  gtk_box_pack_end(GTK_BOX(bottom_box), button, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(bubble_content), bottom_box, FALSE, FALSE, 0);
  gtk_widget_grab_focus(bottom_box);
  gtk_widget_grab_focus(button);

  InfoBubbleGtk::ArrowLocationGtk arrow_location =
      !base::i18n::IsRTL() ?
      InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT :
      InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT;
  info_bubble_ = InfoBubbleGtk::Show(
      anchor_,
      NULL,
      bubble_content,
      arrow_location,
      true,  // match_system_theme
      true,  // grab_input
      theme_provider,
      this);
}

void ContentSettingBubbleGtk::OnPopupIconButtonPress(
    GtkWidget* icon_event_box,
    GdkEventButton* event) {
  PopupMap::iterator i(popup_icons_.find(icon_event_box));
  DCHECK(i != popup_icons_.end());
  content_setting_bubble_model_->OnPopupClicked(i->second);
  // The views interface implicitly closes because of the launching of a new
  // window; we need to do that explicitly.
  Close();
}

void ContentSettingBubbleGtk::OnPopupLinkClicked(GtkWidget* button) {
  PopupMap::iterator i(popup_links_.find(button));
  DCHECK(i != popup_links_.end());
  content_setting_bubble_model_->OnPopupClicked(i->second);
  // The views interface implicitly closes because of the launching of a new
  // window; we need to do that explicitly.
  Close();
}

void ContentSettingBubbleGtk::OnRadioToggled(GtkWidget* widget) {
  for (ContentSettingBubbleGtk::RadioGroupGtk::const_iterator i =
       radio_group_gtk_.begin();
       i != radio_group_gtk_.end(); ++i) {
    if (widget == *i) {
      content_setting_bubble_model_->OnRadioClicked(
          i - radio_group_gtk_.begin());
      return;
    }
  }
  NOTREACHED() << "unknown radio toggled";
}

void ContentSettingBubbleGtk::OnCloseButtonClicked(GtkWidget *button) {
  Close();
}

void ContentSettingBubbleGtk::OnManageLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnManageLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnClearLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnClearLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnInfoLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnInfoLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnLoadPluginsLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnLoadPluginsLinkClicked();
  Close();
}
<commit_msg>GTK: Elide most strings in the Linux content settings bubbles.<commit_after>// Copyright (c) 2010 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/gtk/content_setting_bubble_gtk.h"

#include "app/l10n_util.h"
#include "app/text_elider.h"
#include "base/i18n/rtl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/blocked_content_container.h"
#include "chrome/browser/content_setting_bubble_model.h"
#include "chrome/browser/gtk/gtk_chrome_link_button.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "chrome/browser/gtk/options/content_settings_window_gtk.h"
#include "chrome/browser/host_content_settings_map.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/content_settings.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_type.h"
#include "gfx/gtk_util.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "webkit/glue/plugins/plugin_list.h"

namespace {

// Padding between content and edge of info bubble.
const int kContentBorder = 7;

// The maximum width of a title entry in the content box. We elide anything
// longer than this.
const int kMaxLinkPixelSize = 500;

std::string BuildElidedText(const std::string& input) {
  return UTF16ToUTF8(gfx::ElideText(
      UTF8ToUTF16(input),
      gfx::Font(),
      kMaxLinkPixelSize,
      false));
}

}  // namespace

ContentSettingBubbleGtk::ContentSettingBubbleGtk(
    GtkWidget* anchor,
    InfoBubbleGtkDelegate* delegate,
    ContentSettingBubbleModel* content_setting_bubble_model,
    Profile* profile,
    TabContents* tab_contents)
    : anchor_(anchor),
      profile_(profile),
      tab_contents_(tab_contents),
      delegate_(delegate),
      content_setting_bubble_model_(content_setting_bubble_model),
      info_bubble_(NULL) {
  registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
                 Source<TabContents>(tab_contents));
  BuildBubble();
}

ContentSettingBubbleGtk::~ContentSettingBubbleGtk() {
}

void ContentSettingBubbleGtk::Close() {
  if (info_bubble_)
    info_bubble_->Close();
}

void ContentSettingBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble,
                                                bool closed_by_escape) {
  delegate_->InfoBubbleClosing(info_bubble, closed_by_escape);
  delete this;
}

void ContentSettingBubbleGtk::Observe(NotificationType type,
                                      const NotificationSource& source,
                                      const NotificationDetails& details) {
  DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
  DCHECK(source == Source<TabContents>(tab_contents_));
  tab_contents_ = NULL;
}

void ContentSettingBubbleGtk::BuildBubble() {
  GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom(profile_);

  GtkWidget* bubble_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
  gtk_container_set_border_width(GTK_CONTAINER(bubble_content), kContentBorder);

  const ContentSettingBubbleModel::BubbleContent& content =
      content_setting_bubble_model_->bubble_content();
  if (!content.title.empty()) {
    // Add the content label.
    GtkWidget* label = gtk_label_new(content.title.c_str());
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    gtk_box_pack_start(GTK_BOX(bubble_content), label, FALSE, FALSE, 0);
  }

  const std::set<std::string>& plugins = content.resource_identifiers;
  if (!plugins.empty()) {
    GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);

    for (std::set<std::string>::const_iterator it = plugins.begin();
        it != plugins.end(); ++it) {
      std::string name;
      NPAPI::PluginList::PluginMap groups;
      NPAPI::PluginList::Singleton()->GetPluginGroups(false, &groups);
      if (groups.find(*it) != groups.end())
        name = UTF16ToUTF8(groups[*it]->GetGroupName());
      else
        name = *it;

      GtkWidget* label = gtk_label_new(BuildElidedText(name).c_str());
      GtkWidget* label_box = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0);

      gtk_box_pack_start(GTK_BOX(list_content),
                         label_box,
                         FALSE, FALSE, 0);
    }
    gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE,
                       gtk_util::kControlSpacing);
  }

  if (content_setting_bubble_model_->content_type() ==
      CONTENT_SETTINGS_TYPE_POPUPS) {
    const std::vector<ContentSettingBubbleModel::PopupItem>& popup_items =
        content.popup_items;
    GtkWidget* table = gtk_table_new(popup_items.size(), 2, FALSE);
    int row = 0;
    for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator
         i(popup_items.begin()); i != popup_items.end(); ++i, ++row) {
      GtkWidget* image = gtk_image_new();
      if (!i->bitmap.empty()) {
        GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&i->bitmap);
        gtk_image_set_from_pixbuf(GTK_IMAGE(image), icon_pixbuf);
        g_object_unref(icon_pixbuf);

        // We stuff the image in an event box so we can trap mouse clicks on the
        // image (and launch the popup).
        GtkWidget* event_box = gtk_event_box_new();
        gtk_container_add(GTK_CONTAINER(event_box), image);

        popup_icons_[event_box] = i -popup_items.begin();
        g_signal_connect(event_box, "button_press_event",
                         G_CALLBACK(OnPopupIconButtonPressThunk), this);
        gtk_table_attach(GTK_TABLE(table), event_box, 0, 1, row, row + 1,
                         GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2,
                         gtk_util::kControlSpacing / 2);
      }

      GtkWidget* button = gtk_chrome_link_button_new(
          BuildElidedText(i->title).c_str());
      popup_links_[button] = i -popup_items.begin();
      g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClickedThunk),
                       this);
      gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1,
                       GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2,
                       gtk_util::kControlSpacing / 2);
    }

    gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0);
  }

  if (content_setting_bubble_model_->content_type() !=
      CONTENT_SETTINGS_TYPE_COOKIES) {
    const ContentSettingBubbleModel::RadioGroup& radio_group =
        content.radio_group;
    for (ContentSettingBubbleModel::RadioItems::const_iterator i =
         radio_group.radio_items.begin();
         i != radio_group.radio_items.end(); ++i) {
      std::string elided = BuildElidedText(*i);
      GtkWidget* radio =
          radio_group_gtk_.empty() ?
              gtk_radio_button_new_with_label(NULL, elided.c_str()) :
              gtk_radio_button_new_with_label_from_widget(
                  GTK_RADIO_BUTTON(radio_group_gtk_[0]),
                  elided.c_str());
      gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0);
      if (i - radio_group.radio_items.begin() == radio_group.default_item) {
        // We must set the default value before we attach the signal handlers
        // or pain occurs.
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
      }
      radio_group_gtk_.push_back(radio);
    }
    for (std::vector<GtkWidget*>::const_iterator i = radio_group_gtk_.begin();
         i != radio_group_gtk_.end(); ++i) {
      // We can attach signal handlers now that all defaults are set.
      g_signal_connect(*i, "toggled", G_CALLBACK(OnRadioToggledThunk), this);
    }
    if (!radio_group_gtk_.empty()) {
      gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(), FALSE,
                         FALSE, 0);
    }
  }

  for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i =
       content.domain_lists.begin();
       i != content.domain_lists.end(); ++i) {
    // Put each list into its own vbox to allow spacing between lists.
    GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);

    GtkWidget* label = gtk_label_new(BuildElidedText(i->title).c_str());
    gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    GtkWidget* label_box = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(list_content), label_box, FALSE, FALSE, 0);
    for (std::set<std::string>::const_iterator j = i->hosts.begin();
         j != i->hosts.end(); ++j) {
      gtk_box_pack_start(GTK_BOX(list_content),
                         gtk_util::IndentWidget(gtk_util::CreateBoldLabel(*j)),
                         FALSE, FALSE, 0);
    }
    gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE,
                       gtk_util::kControlSpacing);
  }

  if (!content.clear_link.empty()) {
    GtkWidget* clear_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* clear_link = gtk_chrome_link_button_new(
        content.clear_link.c_str());
    g_signal_connect(clear_link, "clicked", G_CALLBACK(OnClearLinkClickedThunk),
                     this);
    gtk_box_pack_start(GTK_BOX(clear_link_box), clear_link, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), clear_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  if (!content.info_link.empty()) {
    GtkWidget* info_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* info_link = gtk_chrome_link_button_new(
        content.info_link.c_str());
    g_signal_connect(info_link, "clicked", G_CALLBACK(OnInfoLinkClickedThunk),
                     this);
    gtk_box_pack_start(GTK_BOX(info_link_box), info_link, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), info_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  if (!content.load_plugins_link_title.empty()) {
    GtkWidget* load_plugins_link_box = gtk_hbox_new(FALSE, 0);
    GtkWidget* load_plugins_link = gtk_chrome_link_button_new(
        content.load_plugins_link_title.c_str());
    gtk_widget_set_sensitive(load_plugins_link,
                             content.load_plugins_link_enabled);
    g_signal_connect(load_plugins_link, "clicked",
                     G_CALLBACK(OnLoadPluginsLinkClickedThunk), this);
    gtk_box_pack_start(GTK_BOX(load_plugins_link_box), load_plugins_link,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), load_plugins_link_box,
                       FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(),
                       FALSE, FALSE, 0);
  }

  GtkWidget* bottom_box = gtk_hbox_new(FALSE, 0);

  GtkWidget* manage_link =
      gtk_chrome_link_button_new(content.manage_link.c_str());
  g_signal_connect(manage_link, "clicked", G_CALLBACK(OnManageLinkClickedThunk),
                   this);
  gtk_box_pack_start(GTK_BOX(bottom_box), manage_link, FALSE, FALSE, 0);

  GtkWidget* button = gtk_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_DONE).c_str());
  g_signal_connect(button, "clicked", G_CALLBACK(OnCloseButtonClickedThunk),
                   this);
  gtk_box_pack_end(GTK_BOX(bottom_box), button, FALSE, FALSE, 0);

  gtk_box_pack_start(GTK_BOX(bubble_content), bottom_box, FALSE, FALSE, 0);
  gtk_widget_grab_focus(bottom_box);
  gtk_widget_grab_focus(button);

  InfoBubbleGtk::ArrowLocationGtk arrow_location =
      !base::i18n::IsRTL() ?
      InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT :
      InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT;
  info_bubble_ = InfoBubbleGtk::Show(
      anchor_,
      NULL,
      bubble_content,
      arrow_location,
      true,  // match_system_theme
      true,  // grab_input
      theme_provider,
      this);
}

void ContentSettingBubbleGtk::OnPopupIconButtonPress(
    GtkWidget* icon_event_box,
    GdkEventButton* event) {
  PopupMap::iterator i(popup_icons_.find(icon_event_box));
  DCHECK(i != popup_icons_.end());
  content_setting_bubble_model_->OnPopupClicked(i->second);
  // The views interface implicitly closes because of the launching of a new
  // window; we need to do that explicitly.
  Close();
}

void ContentSettingBubbleGtk::OnPopupLinkClicked(GtkWidget* button) {
  PopupMap::iterator i(popup_links_.find(button));
  DCHECK(i != popup_links_.end());
  content_setting_bubble_model_->OnPopupClicked(i->second);
  // The views interface implicitly closes because of the launching of a new
  // window; we need to do that explicitly.
  Close();
}

void ContentSettingBubbleGtk::OnRadioToggled(GtkWidget* widget) {
  for (ContentSettingBubbleGtk::RadioGroupGtk::const_iterator i =
       radio_group_gtk_.begin();
       i != radio_group_gtk_.end(); ++i) {
    if (widget == *i) {
      content_setting_bubble_model_->OnRadioClicked(
          i - radio_group_gtk_.begin());
      return;
    }
  }
  NOTREACHED() << "unknown radio toggled";
}

void ContentSettingBubbleGtk::OnCloseButtonClicked(GtkWidget *button) {
  Close();
}

void ContentSettingBubbleGtk::OnManageLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnManageLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnClearLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnClearLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnInfoLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnInfoLinkClicked();
  Close();
}

void ContentSettingBubbleGtk::OnLoadPluginsLinkClicked(GtkWidget* button) {
  content_setting_bubble_model_->OnLoadPluginsLinkClicked();
  Close();
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Tests the MetricsService stat recording to make sure that the numbers are
// what we expect.

#include <string>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_service_mock_builder.h"
#include "chrome/browser/prefs/pref_value_store.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/json_pref_store.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"

class MetricsServiceTest : public UITest {
 public:
  MetricsServiceTest() : UITest() {
    // We need to show the window so web content type tabs load.
    show_window_ = true;
  }

  // Open a few tabs of random content
  void OpenTabs() {
    scoped_refptr<BrowserProxy> window = automation()->GetBrowserWindow(0);
    ASSERT_TRUE(window.get());

    FilePath page1_path;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &page1_path));
    page1_path = page1_path.AppendASCII("title2.html");
    ASSERT_TRUE(window->AppendTab(net::FilePathToFileURL(page1_path)));

    FilePath page2_path;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &page2_path));
    page2_path = page2_path.AppendASCII("iframe.html");
    ASSERT_TRUE(window->AppendTab(net::FilePathToFileURL(page2_path)));
  }

  // Get a PrefService whose contents correspond to the Local State file
  // that was saved by the app as it closed.  The caller takes ownership of the
  // returned PrefService object.
  PrefService* GetLocalState() {
    FilePath path = user_data_dir().Append(chrome::kLocalStateFilename);
    return PrefServiceMockBuilder().WithUserFilePrefs(path).Create();
  }
};

TEST_F(MetricsServiceTest, CloseRenderersNormally) {
  OpenTabs();
  QuitBrowser();

  scoped_ptr<PrefService> local_state(GetLocalState());
  local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
  local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
  EXPECT_TRUE(local_state->GetBoolean(prefs::kStabilityExitedCleanly));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityLaunchCount));
  EXPECT_EQ(3, local_state->GetInteger(prefs::kStabilityPageLoadCount));
  EXPECT_EQ(0, local_state->GetInteger(prefs::kStabilityRendererCrashCount));
}

TEST_F(MetricsServiceTest, CrashRenderers) {
  // This doesn't make sense to test in single process mode.
  if (ProxyLauncher::in_process_renderer())
    return;

  OpenTabs();

  {
    // Limit the lifetime of various automation proxies used here. We must
    // destroy them before calling QuitBrowser.

    scoped_refptr<BrowserProxy> window = automation()->GetBrowserWindow(0);
    ASSERT_TRUE(window.get());

    // Kill the process for one of the tabs.
    scoped_refptr<TabProxy> tab(window->GetTab(1));
    ASSERT_TRUE(tab.get());

    // We can get crash dumps on Windows always, Linux when breakpad is
    // enabled, and all platforms for official Google Chrome builds.
#if defined(OS_WIN) || defined(USE_LINUX_BREAKPAD) || \
    defined(GOOGLE_CHROME_BUILD)
    expected_crashes_ = 1;
#endif
    ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutCrashURL)));
  }

  // Give the browser a chance to notice the crashed tab.
  base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());

  QuitBrowser();

  scoped_ptr<PrefService> local_state(GetLocalState());
  local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
  local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
  EXPECT_TRUE(local_state->GetBoolean(prefs::kStabilityExitedCleanly));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityLaunchCount));
  EXPECT_EQ(4, local_state->GetInteger(prefs::kStabilityPageLoadCount));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityRendererCrashCount));
}
<commit_msg>Disabling Crash Renderers test, it fails always on offical trunk builders Review URL: http://codereview.chromium.org/6578031<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.

// Tests the MetricsService stat recording to make sure that the numbers are
// what we expect.

#include <string>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/prefs/pref_service_mock_builder.h"
#include "chrome/browser/prefs/pref_value_store.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/json_pref_store.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"

class MetricsServiceTest : public UITest {
 public:
  MetricsServiceTest() : UITest() {
    // We need to show the window so web content type tabs load.
    show_window_ = true;
  }

  // Open a few tabs of random content
  void OpenTabs() {
    scoped_refptr<BrowserProxy> window = automation()->GetBrowserWindow(0);
    ASSERT_TRUE(window.get());

    FilePath page1_path;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &page1_path));
    page1_path = page1_path.AppendASCII("title2.html");
    ASSERT_TRUE(window->AppendTab(net::FilePathToFileURL(page1_path)));

    FilePath page2_path;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &page2_path));
    page2_path = page2_path.AppendASCII("iframe.html");
    ASSERT_TRUE(window->AppendTab(net::FilePathToFileURL(page2_path)));
  }

  // Get a PrefService whose contents correspond to the Local State file
  // that was saved by the app as it closed.  The caller takes ownership of the
  // returned PrefService object.
  PrefService* GetLocalState() {
    FilePath path = user_data_dir().Append(chrome::kLocalStateFilename);
    return PrefServiceMockBuilder().WithUserFilePrefs(path).Create();
  }
};

TEST_F(MetricsServiceTest, CloseRenderersNormally) {
  OpenTabs();
  QuitBrowser();

  scoped_ptr<PrefService> local_state(GetLocalState());
  local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
  local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
  EXPECT_TRUE(local_state->GetBoolean(prefs::kStabilityExitedCleanly));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityLaunchCount));
  EXPECT_EQ(3, local_state->GetInteger(prefs::kStabilityPageLoadCount));
  EXPECT_EQ(0, local_state->GetInteger(prefs::kStabilityRendererCrashCount));
}

TEST_F(MetricsServiceTest, DISABLED_CrashRenderers) {
  // This doesn't make sense to test in single process mode.
  if (ProxyLauncher::in_process_renderer())
    return;

  OpenTabs();

  {
    // Limit the lifetime of various automation proxies used here. We must
    // destroy them before calling QuitBrowser.

    scoped_refptr<BrowserProxy> window = automation()->GetBrowserWindow(0);
    ASSERT_TRUE(window.get());

    // Kill the process for one of the tabs.
    scoped_refptr<TabProxy> tab(window->GetTab(1));
    ASSERT_TRUE(tab.get());

    // We can get crash dumps on Windows always, Linux when breakpad is
    // enabled, and all platforms for official Google Chrome builds.
#if defined(OS_WIN) || defined(USE_LINUX_BREAKPAD) || \
    defined(GOOGLE_CHROME_BUILD)
    expected_crashes_ = 1;
#endif
    ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutCrashURL)));
  }

  // Give the browser a chance to notice the crashed tab.
  base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());

  QuitBrowser();

  scoped_ptr<PrefService> local_state(GetLocalState());
  local_state->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
  local_state->RegisterIntegerPref(prefs::kStabilityLaunchCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityPageLoadCount, 0);
  local_state->RegisterIntegerPref(prefs::kStabilityRendererCrashCount, 0);
  EXPECT_TRUE(local_state->GetBoolean(prefs::kStabilityExitedCleanly));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityLaunchCount));
  EXPECT_EQ(4, local_state->GetInteger(prefs::kStabilityPageLoadCount));
  EXPECT_EQ(1, local_state->GetInteger(prefs::kStabilityRendererCrashCount));
}
<|endoftext|>


<commit_before>/*
 * ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have purchased from
 * Numenta, Inc. a separate commercial license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Our own set object, to beat Python, at least when computing intersection.
 */

#ifndef NTA_MATH_SET_HPP
#define NTA_MATH_SET_HPP

#include <vector>

namespace nta {

  //--------------------------------------------------------------------------------
  // SET
  //
  // Represents the set with an indicator function stored in a bit array.
  //
  // T is an unsigned integral type.
  // T_byte has the size of a byte.
  //
  // Test from Python:
  // Mac PowerBook 2.8 GHz Core 2 Duo, 10.6.3, -O3 -DNDEBUG, gcc 4.2.1 (Apple 5659)
  // m = 50000, n1 = 40, n2 = 10000: 0.00274658203125 0.00162267684937 1.69262415516
  // m = 50000, n1 = 80, n2 = 10000: 0.00458002090454 0.00179862976074 2.54639448568
  // m = 50000, n1 = 200, n2 = 10000: 0.0124213695526 0.00241708755493 5.13898204774
  // m = 50000, n1 = 500, n2 = 10000: 0.0339875221252 0.00330281257629 10.2904785967
  // m = 50000, n1 = 1000, n2 = 10000: 0.0573344230652 0.00443959236145 12.9143440202
  // m = 50000, n1 = 2500, n2 = 10000: 0.155576944351 0.00838160514832 18.5617124164
  // m = 50000, n1 = 5000, n2 = 10000: 0.256726026535 0.0143656730652 17.8707969595
  //--------------------------------------------------------------------------------
  template <typename T =size_t, typename T_byte =unsigned char>
  class Set
  {
  private:
    T m; // max value of non-zero indices
    T n; // number of non-zeros in s
    std::vector<T_byte> s; // indicator of the non-zeros

  public:
    // For Python binding
    inline Set()
    {}

    /**
     * Constructs from a list of n element indices ss, each element being
     * in the interval [0,m[.
     */
    inline Set(T _m, T _n, T* ss)
      : m(_m),
        n(_n),
        s(m/8 + (m % 8 == 0 ? 0 : 1))
    {
      construct(m, n, ss);
    }

    inline void construct(T _m, T _n, T* ss)
    {
      m = _m;
      n = _n;
      s.resize(m/8 + (m % 8 == 0 ? 0 : 1));

      for (T i = 0; i != n; ++i)
        s[ss[i] / 8] |= 1 << (ss[i] % 8);
    }

    inline Set(const Set& o)
      : s(o.s)
    {}

    inline Set& operator=(const Set& o)
    {
      s = o.s;
      return *this;
    }

    inline T n_elements() const { return n; }
    inline T max_index() const { return m; }
    inline T n_bytes() const { return s.size(); }

    /**
     * Computes the intersection between this and another set (n2, s2).
     * n2 is the number of element in the second s, s2 is a pointer to
     * the first element, which needs to be stored contiguously. s2 needs
     * to store the indices of the elements: (2,7,11) is the set of those
     * 3 elements.
     * r is the result set, and is also a list of element indices.
     * This method also returns an integer, which is the number of elements
     * in the intersection (so that r can be allocated once, and its first
     * few positions reused over and over again).
     *
     * NOTE: for best performance, have n2 << n
     */
    inline T intersection(T n2, T* s2, T* r) const
    {
      /*
      if (n < n2) {
        std::cout << "Calling nta::Set::intersection "
                  << "with small set: for best performance, "
                  << "call with smaller set as argument"
                  << std::endl;
      }
      */

      T* rr = r;
      for (T* i = s2, *i_end = s2 + n2; i != i_end; ++i) {
        *r = *i;
        r += (s[*i >> 3] & (1 << (*i % 8))) / (1 << (*i % 8));
      }
      return (T) (r - rr);
    }
  };

} // end namespace nta

//--------------------------------------------------------------------------------
#endif //NTA_MATH_SET_HPP
<commit_msg>add note of deprecation, write tests to decide<commit_after>/*
 * ---------------------------------------------------------------------
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2013, Numenta, Inc.  Unless you have purchased from
 * Numenta, Inc. a separate commercial license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 * ---------------------------------------------------------------------
 */

/** @file
 * Our own set object, to beat Python, at least when computing intersection.
 * TODO: this file is currently superceded by built-in python set(), keeping as a reference,
 * and we should test which is faster for intersection workload, which is heavily used.
 */

#ifndef NTA_MATH_SET_HPP
#define NTA_MATH_SET_HPP

#include <vector>

namespace nta {

  //--------------------------------------------------------------------------------
  // SET
  //
  // Represents the set with an indicator function stored in a bit array.
  //
  // T is an unsigned integral type.
  // T_byte has the size of a byte.
  //
  // Test from Python:
  // Mac PowerBook 2.8 GHz Core 2 Duo, 10.6.3, -O3 -DNDEBUG, gcc 4.2.1 (Apple 5659)
  // m = 50000, n1 = 40, n2 = 10000: 0.00274658203125 0.00162267684937 1.69262415516
  // m = 50000, n1 = 80, n2 = 10000: 0.00458002090454 0.00179862976074 2.54639448568
  // m = 50000, n1 = 200, n2 = 10000: 0.0124213695526 0.00241708755493 5.13898204774
  // m = 50000, n1 = 500, n2 = 10000: 0.0339875221252 0.00330281257629 10.2904785967
  // m = 50000, n1 = 1000, n2 = 10000: 0.0573344230652 0.00443959236145 12.9143440202
  // m = 50000, n1 = 2500, n2 = 10000: 0.155576944351 0.00838160514832 18.5617124164
  // m = 50000, n1 = 5000, n2 = 10000: 0.256726026535 0.0143656730652 17.8707969595
  //--------------------------------------------------------------------------------
  template <typename T =size_t, typename T_byte =unsigned char>
  class Set
  {
  private:
    T m; // max value of non-zero indices
    T n; // number of non-zeros in s
    std::vector<T_byte> s; // indicator of the non-zeros

  public:
    // For Python binding
    inline Set()
    {}

    /**
     * Constructs from a list of n element indices ss, each element being
     * in the interval [0,m[.
     */
    inline Set(T _m, T _n, T* ss)
      : m(_m),
        n(_n),
        s(m/8 + (m % 8 == 0 ? 0 : 1))
    {
      construct(m, n, ss);
    }

    inline void construct(T _m, T _n, T* ss)
    {
      m = _m;
      n = _n;
      s.resize(m/8 + (m % 8 == 0 ? 0 : 1));

      for (T i = 0; i != n; ++i)
        s[ss[i] / 8] |= 1 << (ss[i] % 8);
    }

    inline Set(const Set& o)
      : s(o.s)
    {}

    inline Set& operator=(const Set& o)
    {
      s = o.s;
      return *this;
    }

    inline T n_elements() const { return n; }
    inline T max_index() const { return m; }
    inline T n_bytes() const { return s.size(); }

    /**
     * Computes the intersection between this and another set (n2, s2).
     * n2 is the number of element in the second s, s2 is a pointer to
     * the first element, which needs to be stored contiguously. s2 needs
     * to store the indices of the elements: (2,7,11) is the set of those
     * 3 elements.
     * r is the result set, and is also a list of element indices.
     * This method also returns an integer, which is the number of elements
     * in the intersection (so that r can be allocated once, and its first
     * few positions reused over and over again).
     *
     * NOTE: for best performance, have n2 << n
     */
    inline T intersection(T n2, T* s2, T* r) const
    {
      /*
      if (n < n2) {
        std::cout << "Calling nta::Set::intersection "
                  << "with small set: for best performance, "
                  << "call with smaller set as argument"
                  << std::endl;
      }
      */

      T* rr = r;
      for (T* i = s2, *i_end = s2 + n2; i != i_end; ++i) {
        *r = *i;
        r += (s[*i >> 3] & (1 << (*i % 8))) / (1 << (*i % 8));
      }
      return (T) (r - rr);
    }
  };

} // end namespace nta

//--------------------------------------------------------------------------------
#endif //NTA_MATH_SET_HPP
<|endoftext|>


<commit_before>#include "mh.h"
#include <random>

template<typename T>
void pc(const T &x, const char *s="unspecified") {
    auto it = std::begin(x);
    std::string t = std::to_string(*it);
    while(++it != x.end()) {
        t += ',';
        t += std::to_string(*it);
    }
    std::fprintf(stderr, "Container %s contains: %s\n", s, t.data());
}

using namespace sketch;
using namespace mh;

int main(int argc, char *argv[]) {
    size_t nelem = argc == 1 ? 1000: size_t(std::strtoull(argv[1], nullptr, 10));
    double olap_frac = argc < 3 ? 0.1: std::atof(argv[2]);
    size_t ss = argc < 4 ? 10: size_t(std::strtoull(argv[1], nullptr, 10));
    RangeMinHash<uint64_t> rm1(ss), rm2(ss);
    std::mt19937_64 mt(1337);
    size_t olap_n = (olap_frac * nelem);
    olap_frac = static_cast<double>(olap_n) / nelem;
    for(size_t i = 0; i < olap_n; ++i) {
        auto v = mt();
        rm1.add(v); rm2.add(v);
        //pc(rm1);
    }
    std::fprintf(stderr, "olap_n: %zu. nelem: %zu\n", olap_n, nelem);
    for(size_t i = nelem - olap_n; i--;rm1.add(mt()));
    for(size_t i = nelem - olap_n; i--;rm2.add(mt()));
    pc(rm1, "rm1");
    pc(rm2, "rm2");
    size_t is = intersection_size(rm1, rm2);
    double ji = rm1.jaccard_index(rm2);
    double true_ji = double(olap_n) / (nelem * 2 - olap_n);
    std::fprintf(stderr, "sketch is: %zu. sketch ji: %lf. True: %lf\n", is, ji, true_ji);
    is = intersection_size(rm1, rm1);
    ji = rm1.jaccard_index(rm1);
    std::fprintf(stderr, "sketch is: %zu. sketch ji: %lf. True: 1.000000\n", is, ji);
}
<commit_msg>Whitespace.<commit_after>#include "mh.h"
#include <random>

template<typename T>
void pc(const T &x, const char *s="unspecified") {
    auto it = std::begin(x);
    std::string t = std::to_string(*it);
    while(++it != x.end()) {
        t += ',';
        t += std::to_string(*it);
    }
    std::fprintf(stderr, "Container %s contains: %s\n", s, t.data());
}

using namespace sketch;
using namespace mh;

int main(int argc, char *argv[]) {
    size_t nelem = argc == 1 ? 10000: size_t(std::strtoull(argv[1], nullptr, 10));
    double olap_frac = argc < 3 ? 0.1: std::atof(argv[2]);
    size_t ss = argc < 4 ? 10: size_t(std::strtoull(argv[1], nullptr, 10));
    RangeMinHash<uint64_t> rm1(ss), rm2(ss);
    std::mt19937_64 mt(1337);
    size_t olap_n = (olap_frac * nelem);
    olap_frac = static_cast<double>(olap_n) / nelem;
    for(size_t i = 0; i < olap_n; ++i) {
        auto v = mt();
        rm1.addh(v); rm2.addh(v);
        //pc(rm1);
    }
    std::fprintf(stderr, "olap_n: %zu. nelem: %zu\n", olap_n, nelem);
    for(size_t i = nelem - olap_n; i--;rm1.addh(mt()));
    for(size_t i = nelem - olap_n; i--;rm2.addh(mt()));
    pc(rm1, "rm1");
    pc(rm2, "rm2");
    size_t is = intersection_size(rm1, rm2);
    double ji = rm1.jaccard_index(rm2);
    double true_ji = double(olap_n) / (nelem * 2 - olap_n);
    std::fprintf(stderr, "sketch is: %zu. sketch ji: %lf. True: %lf\n", is, ji, true_ji);
    is = intersection_size(rm1, rm1);
    ji = rm1.jaccard_index(rm1);
    std::fprintf(stderr, "sketch is: %zu. sketch ji: %lf. True: 1.000000\n", is, ji);
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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/gtk/go_button_gtk.h"

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/location_bar_view_gtk.h"
#include "chrome/browser/profile.h"
#include "chrome/common/notification_service.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

GoButtonGtk::GoButtonGtk(LocationBarViewGtk* location_bar, Browser* browser)
    : location_bar_(location_bar),
      browser_(browser),
      button_delay_(0),
      stop_timer_(this),
      intended_mode_(MODE_GO),
      visible_mode_(MODE_GO),
      state_(BS_NORMAL),
      theme_provider_(browser ?
                      GtkThemeProvider::GetFrom(browser->profile()) : NULL),
      go_(theme_provider_, IDR_GO, IDR_GO_P, IDR_GO_H, 0),
      stop_(theme_provider_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, 0),
      widget_(gtk_button_new()) {
  gtk_widget_set_size_request(widget_.get(),
                              gdk_pixbuf_get_width(go_.pixbufs(0)),
                              gdk_pixbuf_get_height(go_.pixbufs(0)));

  gtk_widget_set_app_paintable(widget_.get(), TRUE);
  // We effectively double-buffer by virtue of having only one image...
  gtk_widget_set_double_buffered(widget_.get(), FALSE);

  g_signal_connect(G_OBJECT(widget_.get()), "expose-event",
                   G_CALLBACK(OnExpose), this);
  g_signal_connect(G_OBJECT(widget_.get()), "enter",
                   G_CALLBACK(OnEnter), this);
  g_signal_connect(G_OBJECT(widget_.get()), "leave",
                   G_CALLBACK(OnLeave), this);
  g_signal_connect(G_OBJECT(widget_.get()), "clicked",
                   G_CALLBACK(OnClicked), this);
  GTK_WIDGET_UNSET_FLAGS(widget_.get(), GTK_CAN_FOCUS);

  SetTooltip();

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

GoButtonGtk::~GoButtonGtk() {
  widget_.Destroy();
}

void GoButtonGtk::ChangeMode(Mode mode, bool force) {
  intended_mode_ = mode;

  // If the change is forced, or the user isn't hovering the icon, or it's safe
  // to change it to the other image type, make the change immediately;
  // otherwise we'll let it happen later.
  if (force || (state() != BS_HOT) || ((mode == MODE_STOP) ?
      stop_timer_.empty() : (visible_mode_ != MODE_STOP))) {
    stop_timer_.RevokeAll();
    gtk_widget_queue_draw(widget_.get());
    SetTooltip();
    visible_mode_ = mode;

    UpdateThemeButtons();
  }
}

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

  GtkThemeProvider* provider = static_cast<GtkThemeProvider*>(
      Source<GtkThemeProvider>(source).ptr());
  DCHECK(provider == theme_provider_);
  UpdateThemeButtons();
}

Task* GoButtonGtk::CreateButtonTimerTask() {
  return stop_timer_.NewRunnableMethod(&GoButtonGtk::OnButtonTimer);
}

void GoButtonGtk::OnButtonTimer() {
  stop_timer_.RevokeAll();
  ChangeMode(intended_mode_, true);
}

// static
gboolean GoButtonGtk::OnExpose(GtkWidget* widget,
                               GdkEventExpose* e,
                               GoButtonGtk* button) {
  if (button->theme_provider_->UseGtkTheme()) {
    return FALSE;
  } else {
    if (button->visible_mode_ == MODE_GO) {
      return button->go_.OnExpose(widget, e);
    } else {
      return button->stop_.OnExpose(widget, e);
    }
  }
}

// static
gboolean GoButtonGtk::OnEnter(GtkButton* widget, GoButtonGtk* button) {
  DCHECK_EQ(BS_NORMAL, button->state());
  button->state_ = BS_HOT;
  return TRUE;
}

// static
gboolean GoButtonGtk::OnLeave(GtkButton* widget, GoButtonGtk* button) {
  // It's possible on shutdown for a "leave" event to be emitted twice in a row
  // for this button.  I'm not sure if this is a gtk quirk or something wrong
  // with our usage, but it's harmless.  I'm commenting out this DCHECK for now.
  // and adding a LOG(WARNING) instead.
  // See http://www.crbug.com/10851 for details.
  // DCHECK_EQ(BS_HOT, button->state());
  if (button->state() != BS_HOT)
    LOG(WARNING) << "Button state should be BS_HOT when leaving.";
  button->state_ = BS_NORMAL;
  button->ChangeMode(button->intended_mode_, true);
  return TRUE;
}

// static
gboolean GoButtonGtk::OnClicked(GtkButton* widget, GoButtonGtk* button) {
  if (button->visible_mode_ == MODE_STOP) {
    if (button->browser_)
      button->browser_->Stop();

    // The user has clicked, so we can feel free to update the button,
    // even if the mouse is still hovering.
    button->ChangeMode(MODE_GO, true);
  } else if (button->visible_mode_ == MODE_GO && button->stop_timer_.empty()) {
    // If the go button is visible and not within the double click timer, go.
    if (button->browser_)
      button->browser_->ExecuteCommand(IDC_GO);

    // Figure out the system double-click time.
    if (button->button_delay_ == 0) {
      GtkSettings* settings = gtk_settings_get_default();
      g_object_get(G_OBJECT(settings),
                   "gtk-double-click-time",
                   &button->button_delay_,
                   NULL);
    }

    // Stop any existing timers.
    button->stop_timer_.RevokeAll();

    // Start a timer - while this timer is running, the go button
    // cannot be changed to a stop button. We do not set intended_mode_
    // to MODE_STOP here as we want to wait for the browser to tell
    // us that it has started loading (and this may occur only after
    // some delay).
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                            button->CreateButtonTimerTask(),
                                            button->button_delay_);
  }

  return TRUE;
}

void GoButtonGtk::SetTooltip() {
  if (visible_mode_ == MODE_GO) {
    // |location_bar_| can be NULL in tests.
    std::wstring current_text(
        location_bar_ ?  location_bar_->location_entry()->GetText() :
        L"");
    if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
      l10n_util::WrapStringWithLTRFormatting(&current_text);
    }

    // TODO(pkasting): http://b/868940 Use the right strings at the right
    // times by asking the autocomplete system what to do.  Don't hardcode
    // "Google" as the search provider name.
    gtk_widget_set_tooltip_text(
        widget_.get(),
        true ? l10n_util::GetStringFUTF8(
            IDS_TOOLTIP_GO_SITE, WideToUTF16(current_text)).c_str() :
        l10n_util::GetStringFUTF8(IDS_TOOLTIP_GO_SEARCH, UTF8ToUTF16("Google"),
                                  WideToUTF16(current_text)).c_str());
  } else {
    gtk_widget_set_tooltip_text(
        widget_.get(), l10n_util::GetStringUTF8(IDS_TOOLTIP_STOP).c_str());
  }
}

void GoButtonGtk::UpdateThemeButtons() {
  if (theme_provider_->UseGtkTheme()) {
    // TODO(erg): Waiting for Glen to make a version of these that don't have a
    // button border on it.
    if (intended_mode_ == MODE_GO) {
      gtk_button_set_image(
          GTK_BUTTON(widget_.get()),
          gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
    } else {
      gtk_button_set_image(
          GTK_BUTTON(widget_.get()),
          gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_BUTTON));
    }

    gtk_widget_set_size_request(widget_.get(), -1, -1);
    gtk_widget_set_app_paintable(widget_.get(), FALSE);
    gtk_widget_set_double_buffered(widget_.get(), TRUE);
  } else {
    gtk_widget_set_size_request(widget_.get(),
                                gdk_pixbuf_get_width(go_.pixbufs(0)),
                                gdk_pixbuf_get_height(go_.pixbufs(0)));

    gtk_widget_set_app_paintable(widget_.get(), TRUE);
    // We effectively double-buffer by virtue of having only one image...
    gtk_widget_set_double_buffered(widget_.get(), FALSE);
  }
}
<commit_msg>Fix linux unit tests by checking for NULL that's impossible in real code, but needed in unit tests.<commit_after>// Copyright (c) 2009 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/gtk/go_button_gtk.h"

#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/browser/gtk/location_bar_view_gtk.h"
#include "chrome/browser/profile.h"
#include "chrome/common/notification_service.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

GoButtonGtk::GoButtonGtk(LocationBarViewGtk* location_bar, Browser* browser)
    : location_bar_(location_bar),
      browser_(browser),
      button_delay_(0),
      stop_timer_(this),
      intended_mode_(MODE_GO),
      visible_mode_(MODE_GO),
      state_(BS_NORMAL),
      theme_provider_(browser ?
                      GtkThemeProvider::GetFrom(browser->profile()) : NULL),
      go_(theme_provider_, IDR_GO, IDR_GO_P, IDR_GO_H, 0),
      stop_(theme_provider_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, 0),
      widget_(gtk_button_new()) {
  gtk_widget_set_size_request(widget_.get(),
                              gdk_pixbuf_get_width(go_.pixbufs(0)),
                              gdk_pixbuf_get_height(go_.pixbufs(0)));

  gtk_widget_set_app_paintable(widget_.get(), TRUE);
  // We effectively double-buffer by virtue of having only one image...
  gtk_widget_set_double_buffered(widget_.get(), FALSE);

  g_signal_connect(G_OBJECT(widget_.get()), "expose-event",
                   G_CALLBACK(OnExpose), this);
  g_signal_connect(G_OBJECT(widget_.get()), "enter",
                   G_CALLBACK(OnEnter), this);
  g_signal_connect(G_OBJECT(widget_.get()), "leave",
                   G_CALLBACK(OnLeave), this);
  g_signal_connect(G_OBJECT(widget_.get()), "clicked",
                   G_CALLBACK(OnClicked), this);
  GTK_WIDGET_UNSET_FLAGS(widget_.get(), GTK_CAN_FOCUS);

  SetTooltip();

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

GoButtonGtk::~GoButtonGtk() {
  widget_.Destroy();
}

void GoButtonGtk::ChangeMode(Mode mode, bool force) {
  intended_mode_ = mode;

  // If the change is forced, or the user isn't hovering the icon, or it's safe
  // to change it to the other image type, make the change immediately;
  // otherwise we'll let it happen later.
  if (force || (state() != BS_HOT) || ((mode == MODE_STOP) ?
      stop_timer_.empty() : (visible_mode_ != MODE_STOP))) {
    stop_timer_.RevokeAll();
    gtk_widget_queue_draw(widget_.get());
    SetTooltip();
    visible_mode_ = mode;

    UpdateThemeButtons();
  }
}

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

  GtkThemeProvider* provider = static_cast<GtkThemeProvider*>(
      Source<GtkThemeProvider>(source).ptr());
  DCHECK(provider == theme_provider_);
  UpdateThemeButtons();
}

Task* GoButtonGtk::CreateButtonTimerTask() {
  return stop_timer_.NewRunnableMethod(&GoButtonGtk::OnButtonTimer);
}

void GoButtonGtk::OnButtonTimer() {
  stop_timer_.RevokeAll();
  ChangeMode(intended_mode_, true);
}

// static
gboolean GoButtonGtk::OnExpose(GtkWidget* widget,
                               GdkEventExpose* e,
                               GoButtonGtk* button) {
  if (button->theme_provider_ && button->theme_provider_->UseGtkTheme()) {
    return FALSE;
  } else {
    if (button->visible_mode_ == MODE_GO) {
      return button->go_.OnExpose(widget, e);
    } else {
      return button->stop_.OnExpose(widget, e);
    }
  }
}

// static
gboolean GoButtonGtk::OnEnter(GtkButton* widget, GoButtonGtk* button) {
  DCHECK_EQ(BS_NORMAL, button->state());
  button->state_ = BS_HOT;
  return TRUE;
}

// static
gboolean GoButtonGtk::OnLeave(GtkButton* widget, GoButtonGtk* button) {
  // It's possible on shutdown for a "leave" event to be emitted twice in a row
  // for this button.  I'm not sure if this is a gtk quirk or something wrong
  // with our usage, but it's harmless.  I'm commenting out this DCHECK for now.
  // and adding a LOG(WARNING) instead.
  // See http://www.crbug.com/10851 for details.
  // DCHECK_EQ(BS_HOT, button->state());
  if (button->state() != BS_HOT)
    LOG(WARNING) << "Button state should be BS_HOT when leaving.";
  button->state_ = BS_NORMAL;
  button->ChangeMode(button->intended_mode_, true);
  return TRUE;
}

// static
gboolean GoButtonGtk::OnClicked(GtkButton* widget, GoButtonGtk* button) {
  if (button->visible_mode_ == MODE_STOP) {
    if (button->browser_)
      button->browser_->Stop();

    // The user has clicked, so we can feel free to update the button,
    // even if the mouse is still hovering.
    button->ChangeMode(MODE_GO, true);
  } else if (button->visible_mode_ == MODE_GO && button->stop_timer_.empty()) {
    // If the go button is visible and not within the double click timer, go.
    if (button->browser_)
      button->browser_->ExecuteCommand(IDC_GO);

    // Figure out the system double-click time.
    if (button->button_delay_ == 0) {
      GtkSettings* settings = gtk_settings_get_default();
      g_object_get(G_OBJECT(settings),
                   "gtk-double-click-time",
                   &button->button_delay_,
                   NULL);
    }

    // Stop any existing timers.
    button->stop_timer_.RevokeAll();

    // Start a timer - while this timer is running, the go button
    // cannot be changed to a stop button. We do not set intended_mode_
    // to MODE_STOP here as we want to wait for the browser to tell
    // us that it has started loading (and this may occur only after
    // some delay).
    MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                            button->CreateButtonTimerTask(),
                                            button->button_delay_);
  }

  return TRUE;
}

void GoButtonGtk::SetTooltip() {
  if (visible_mode_ == MODE_GO) {
    // |location_bar_| can be NULL in tests.
    std::wstring current_text(
        location_bar_ ?  location_bar_->location_entry()->GetText() :
        L"");
    if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
      l10n_util::WrapStringWithLTRFormatting(&current_text);
    }

    // TODO(pkasting): http://b/868940 Use the right strings at the right
    // times by asking the autocomplete system what to do.  Don't hardcode
    // "Google" as the search provider name.
    gtk_widget_set_tooltip_text(
        widget_.get(),
        true ? l10n_util::GetStringFUTF8(
            IDS_TOOLTIP_GO_SITE, WideToUTF16(current_text)).c_str() :
        l10n_util::GetStringFUTF8(IDS_TOOLTIP_GO_SEARCH, UTF8ToUTF16("Google"),
                                  WideToUTF16(current_text)).c_str());
  } else {
    gtk_widget_set_tooltip_text(
        widget_.get(), l10n_util::GetStringUTF8(IDS_TOOLTIP_STOP).c_str());
  }
}

void GoButtonGtk::UpdateThemeButtons() {
  if (theme_provider_ && theme_provider_->UseGtkTheme()) {
    // TODO(erg): Waiting for Glen to make a version of these that don't have a
    // button border on it.
    if (intended_mode_ == MODE_GO) {
      gtk_button_set_image(
          GTK_BUTTON(widget_.get()),
          gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
    } else {
      gtk_button_set_image(
          GTK_BUTTON(widget_.get()),
          gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_BUTTON));
    }

    gtk_widget_set_size_request(widget_.get(), -1, -1);
    gtk_widget_set_app_paintable(widget_.get(), FALSE);
    gtk_widget_set_double_buffered(widget_.get(), TRUE);
  } else {
    gtk_widget_set_size_request(widget_.get(),
                                gdk_pixbuf_get_width(go_.pixbufs(0)),
                                gdk_pixbuf_get_height(go_.pixbufs(0)));

    gtk_widget_set_app_paintable(widget_.get(), TRUE);
    // We effectively double-buffer by virtue of having only one image...
    gtk_widget_set_double_buffered(widget_.get(), FALSE);
  }
}
<|endoftext|>