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

#include "chrome/browser/views/bookmark_bubble_view.h"

#include "chrome/app/chrome_dll_resource.h"
#include "chrome/app/theme/theme_resources.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/bookmark_editor_view.h"
#include "chrome/browser/views/info_bubble.h"
#include "chrome/browser/views/standard_layout.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/views/button.h"
#include "chrome/views/checkbox.h"
#include "chrome/views/native_button.h"
#include "chrome/views/text_field.h"
#include "generated_resources.h"

using ChromeViews::ComboBox;
using ChromeViews::ColumnSet;
using ChromeViews::GridLayout;
using ChromeViews::Label;
using ChromeViews::Link;
using ChromeViews::NativeButton;
using ChromeViews::View;

// Color of the title.
static const SkColor kTitleColor = SkColorSetRGB(6, 45, 117);

// Padding between "Title:" and the actual title.
static const int kTitlePadding = 4;

// Minimum width for the fields - they will push out the size of the bubble if
// necessary. This should be big enough so that the field pushes the right side
// of the bubble far enough so that the edit button's left edge is to the right
// of the field's left edge.
static const int kMinimumFieldSize = 180;

// Max number of most recently used folders.
static const size_t kMaxMRUFolders = 5;

// Bubble close image.
static SkBitmap* kCloseImage = NULL;

// RecentlyUsedFoldersModel ---------------------------------------------------

BookmarkBubbleView::RecentlyUsedFoldersModel::RecentlyUsedFoldersModel(
    BookmarkModel* bb_model, BookmarkNode* node)
      // Use + 2 to account for bookmark bar and other node.
    : nodes_(bb_model->GetMostRecentlyModifiedGroups(kMaxMRUFolders + 2)),
      node_parent_index_(0) {
  // TODO(sky): bug 1173415 add a separator in the combobox here.

  // We special case the placement of these, so remove them from the list, then
  // fix up the order.
  RemoveNode(bb_model->GetBookmarkBarNode());
  RemoveNode(bb_model->other_node());
  RemoveNode(node->GetParent());

  // Make the parent the first item, unless it's the bookmark bar or other node.
  if (node->GetParent() != bb_model->GetBookmarkBarNode() &&
      node->GetParent() != bb_model->other_node()) {
    nodes_.insert(nodes_.begin(), node->GetParent());
  }

  // Make sure we only have kMaxMRUFolders in the first chunk.
  if (nodes_.size() > kMaxMRUFolders)
    nodes_.erase(nodes_.begin() + kMaxMRUFolders, nodes_.end());

  // And put the bookmark bar and other nodes at the end of the list.
  nodes_.push_back(bb_model->GetBookmarkBarNode());
  nodes_.push_back(bb_model->other_node());

  node_parent_index_ = static_cast<int>(
      find(nodes_.begin(), nodes_.end(), node->GetParent()) - nodes_.begin());
}

int BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemCount(
    ComboBox* source) {
  return static_cast<int>(nodes_.size() + 1);
}

std::wstring BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemAt(
    ComboBox* source, int index) {
  if (index == nodes_.size())
    return l10n_util::GetString(IDS_BOOMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER);
  return nodes_[index]->GetTitle();
}

BookmarkNode* BookmarkBubbleView::RecentlyUsedFoldersModel::GetNodeAt(
    int index) {
  return nodes_[index];
}

void BookmarkBubbleView::RecentlyUsedFoldersModel::RemoveNode(
    BookmarkNode* node) {
  std::vector<BookmarkNode*>::iterator i =
      find(nodes_.begin(), nodes_.end(), node);
  if (i != nodes_.end())
    nodes_.erase(i);
}

// BookmarkBubbleView ---------------------------------------------------------

// static
void BookmarkBubbleView::Show(HWND parent,
                              const gfx::Rect& bounds,
                              InfoBubbleDelegate* delegate,
                              Profile* profile,
                              const GURL& url,
                              bool newly_bookmarked) {
  BookmarkBubbleView* view = new BookmarkBubbleView(delegate, profile, url,
                                                    newly_bookmarked);
  InfoBubble::Show(parent, bounds, view, view);
  GURL url_ptr(url);
  NotificationService::current()->Notify(
      NOTIFY_BOOKMARK_BUBBLE_SHOWN,
      Source<Profile>(profile->GetOriginalProfile()),
      Details<GURL>(&url_ptr));
  view->BubbleShown();
}

BookmarkBubbleView::~BookmarkBubbleView() {
  SetNodeTitleFromTextField();
}

void BookmarkBubbleView::DidChangeBounds(const CRect& previous,
                                         const CRect& current) {
  Layout();
}

void BookmarkBubbleView::BubbleShown() {
  DCHECK(GetViewContainer());
  ChromeViews::FocusManager* focus_manager =
      ChromeViews::FocusManager::GetFocusManager(GetViewContainer()->GetHWND());
  focus_manager->RegisterAccelerator(
      ChromeViews::Accelerator(VK_RETURN, false, false, false), this);

  title_tf_->RequestFocus();
  title_tf_->SelectAll();
}

bool BookmarkBubbleView::AcceleratorPressed(
    const ChromeViews::Accelerator& accelerator) {
  if (accelerator.GetKeyCode() != VK_RETURN)
    return false;

  Close();
  return true;
}

BookmarkBubbleView::BookmarkBubbleView(InfoBubbleDelegate* delegate,
                                       Profile* profile,
                                       const GURL& url,
                                       bool newly_bookmarked)
    : delegate_(delegate),
      profile_(profile),
      url_(url),
      newly_bookmarked_(newly_bookmarked),
      parent_model_(profile_->GetBookmarkModel(),
                    profile_->GetBookmarkModel()->GetNodeByURL(url)) {
  Init();
}

void BookmarkBubbleView::Init() {
  if (!kCloseImage) {
    kCloseImage = ResourceBundle::GetSharedInstance().GetBitmapNamed(
      IDR_INFO_BUBBLE_CLOSE);
  }

  remove_link_ = new Link(l10n_util::GetString(
      IDS_BOOMARK_BUBBLE_REMOVE_BOOKMARK));
  remove_link_->SetController(this);

  edit_button_ = new NativeButton(
      l10n_util::GetString(IDS_BOOMARK_BUBBLE_OPTIONS));
  edit_button_->SetListener(this);

  close_button_ = new NativeButton(l10n_util::GetString(IDS_CLOSE));
  close_button_->SetListener(this);

  parent_combobox_ = new ComboBox(&parent_model_);
  parent_combobox_->SetSelectedItem(parent_model_.node_parent_index());
  parent_combobox_->SetListener(this);

  Label* title_label = new Label(l10n_util::GetString(
      newly_bookmarked_ ? IDS_BOOMARK_BUBBLE_PAGE_BOOKMARKED :
                          IDS_BOOMARK_BUBBLE_PAGE_BOOKMARK));
  title_label->SetFont(
      ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
  title_label->SetColor(kTitleColor);

  GridLayout* layout = new GridLayout(this);
  SetLayoutManager(layout);

  ColumnSet* cs = layout->AddColumnSet(0);

  // Top (title) row.
  cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
                0, 0);
  cs->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
                0, 0);

  // Middle (input field) rows.
  cs = layout->AddColumnSet(2);
  cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
                GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
                GridLayout::USE_PREF, 0, kMinimumFieldSize);

  // Bottom (buttons) row.
  cs = layout->AddColumnSet(3);
  cs->AddPaddingColumn(1, kRelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
                GridLayout::USE_PREF, 0, 0);
  // We subtract 2 to account for the natural button padding, and
  // to bring the separation visually in line with the row separation
  // height.
  cs->AddPaddingColumn(0, kRelatedButtonHSpacing - 2);
  cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
                GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, 0);
  layout->AddView(title_label);
  layout->AddView(remove_link_);

  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
  layout->StartRow(0, 2);
  layout->AddView(
      new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_TITLE_TEXT)));
  title_tf_ = new ChromeViews::TextField();
  title_tf_->SetText(GetTitle());
  layout->AddView(title_tf_);

  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);

  layout->StartRow(0, 2);
  layout->AddView(
      new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_FOLDER_TEXT)));
  layout->AddView(parent_combobox_);
  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);

  layout->StartRow(0, 3);
  layout->AddView(edit_button_);
  layout->AddView(close_button_);
}

std::wstring BookmarkBubbleView::GetTitle() {
  BookmarkModel* bookmark_model= profile_->GetBookmarkModel();
  BookmarkNode* node = bookmark_model->GetNodeByURL(url_);
  if (node)
    return node->GetTitle();
  else
    NOTREACHED();
  return std::wstring();
}

void BookmarkBubbleView::ButtonPressed(ChromeViews::NativeButton* sender) {
  if (sender == edit_button_) {
    UserMetrics::RecordAction(L"BookmarkBubble_Edit", profile_);
    ShowEditor();
  } else {
    DCHECK(sender == close_button_);
    Close();
  }
  // WARNING: we've most likely been deleted when CloseWindow returns.
}

void BookmarkBubbleView::LinkActivated(Link* source, int event_flags) {
  DCHECK(source == remove_link_);
  RemoveBookmark();
}

void BookmarkBubbleView::ItemChanged(ComboBox* combo_box,
                                     int prev_index,
                                     int new_index) {
  if (new_index + 1 == parent_model_.GetItemCount(parent_combobox_)) {
    UserMetrics::RecordAction(L"BookmarkBubble_EditFromCombobox", profile_);

    ShowEditor();
    return;
  }
  BookmarkModel* model = profile_->GetBookmarkModel();
  BookmarkNode* node = model->GetNodeByURL(url_);
  if (node) {
    BookmarkNode* new_parent = parent_model_.GetNodeAt(new_index);
    if (new_parent != node->GetParent()) {
      UserMetrics::RecordAction(L"BookmarkBubble_ChangeParent", profile_);
      model->Move(node, new_parent, new_parent->GetChildCount());
    }
  }
}

void BookmarkBubbleView::InfoBubbleClosing(InfoBubble* info_bubble) {
  if (delegate_)
    delegate_->InfoBubbleClosing(info_bubble);
  NotificationService::current()->Notify(
      NOTIFY_BOOKMARK_BUBBLE_HIDDEN,
      Source<Profile>(profile_->GetOriginalProfile()),
      NotificationService::NoDetails());
}

bool BookmarkBubbleView::CloseOnEscape() {
  return delegate_ ? delegate_->CloseOnEscape() : true;
}

void BookmarkBubbleView::Close() {
  static_cast<InfoBubble*>(GetViewContainer())->Close();
}

void BookmarkBubbleView::RemoveBookmark() {
  UserMetrics::RecordAction(L"BookmarkBubble_Unstar", profile_);

  GURL url = url_;
  BookmarkModel* model = profile_->GetBookmarkModel();
  // Close first, then notify the service. That way we know we won't be
  // visible and don't have to worry about some other window becoming
  // activated and deleting us before we invoke Close.
  Close();
  // WARNING: we've likely been deleted.
  if (model)
    model->SetURLStarred(url, std::wstring(), false);
}

void BookmarkBubbleView::ShowEditor() {
  // Parent the editor to our root ancestor (not the root we're in, as that
  // is the info bubble and will close shortly).
  HWND parent = GetAncestor(GetViewContainer()->GetHWND(), GA_ROOTOWNER);

  // We're about to show the bookmark editor. When the bookmark editor closes
  // we want the browser to become active. HWNDViewContainer::Hide() does a
  // hide in a such way that activation isn't changed, which means when we
  // close Windows gets confused as to who it should give active status to.
  // We explicitly hide the bookmark bubble window in such a way that
  // activation status changes. That way, when the editor closes, activation
  // is properly restored to the browser.
  ShowWindow(GetViewContainer()->GetHWND(), SW_HIDE);

  // Even though we just hid the window, we need to invoke Close to schedule
  // the delete and all that.
  Close();

  BookmarkEditorView::Show(parent, profile_, url_, title_);
}

void BookmarkBubbleView::SetNodeTitleFromTextField() {
  BookmarkModel* model = profile_->GetBookmarkModel();
  BookmarkNode* node = model->GetNodeByURL(url_);
  if (node) {
    const std::wstring new_title = title_tf_->GetText();
    if (new_title != node->GetTitle()) {
      model->SetTitle(node, new_title);
      UserMetrics::RecordAction(L"BookmarkBubble_ChangeTitleInBubble",
                                profile_);
    }
  }
}
<commit_msg>Fixed bookmark bubble bug. If you changed the title, then clicked edit the title would revert.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/views/bookmark_bubble_view.h"

#include "chrome/app/chrome_dll_resource.h"
#include "chrome/app/theme/theme_resources.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/bookmark_editor_view.h"
#include "chrome/browser/views/info_bubble.h"
#include "chrome/browser/views/standard_layout.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/l10n_util.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/views/button.h"
#include "chrome/views/checkbox.h"
#include "chrome/views/native_button.h"
#include "chrome/views/text_field.h"
#include "generated_resources.h"

using ChromeViews::ComboBox;
using ChromeViews::ColumnSet;
using ChromeViews::GridLayout;
using ChromeViews::Label;
using ChromeViews::Link;
using ChromeViews::NativeButton;
using ChromeViews::View;

// Color of the title.
static const SkColor kTitleColor = SkColorSetRGB(6, 45, 117);

// Padding between "Title:" and the actual title.
static const int kTitlePadding = 4;

// Minimum width for the fields - they will push out the size of the bubble if
// necessary. This should be big enough so that the field pushes the right side
// of the bubble far enough so that the edit button's left edge is to the right
// of the field's left edge.
static const int kMinimumFieldSize = 180;

// Max number of most recently used folders.
static const size_t kMaxMRUFolders = 5;

// Bubble close image.
static SkBitmap* kCloseImage = NULL;

// RecentlyUsedFoldersModel ---------------------------------------------------

BookmarkBubbleView::RecentlyUsedFoldersModel::RecentlyUsedFoldersModel(
    BookmarkModel* bb_model, BookmarkNode* node)
      // Use + 2 to account for bookmark bar and other node.
    : nodes_(bb_model->GetMostRecentlyModifiedGroups(kMaxMRUFolders + 2)),
      node_parent_index_(0) {
  // TODO(sky): bug 1173415 add a separator in the combobox here.

  // We special case the placement of these, so remove them from the list, then
  // fix up the order.
  RemoveNode(bb_model->GetBookmarkBarNode());
  RemoveNode(bb_model->other_node());
  RemoveNode(node->GetParent());

  // Make the parent the first item, unless it's the bookmark bar or other node.
  if (node->GetParent() != bb_model->GetBookmarkBarNode() &&
      node->GetParent() != bb_model->other_node()) {
    nodes_.insert(nodes_.begin(), node->GetParent());
  }

  // Make sure we only have kMaxMRUFolders in the first chunk.
  if (nodes_.size() > kMaxMRUFolders)
    nodes_.erase(nodes_.begin() + kMaxMRUFolders, nodes_.end());

  // And put the bookmark bar and other nodes at the end of the list.
  nodes_.push_back(bb_model->GetBookmarkBarNode());
  nodes_.push_back(bb_model->other_node());

  node_parent_index_ = static_cast<int>(
      find(nodes_.begin(), nodes_.end(), node->GetParent()) - nodes_.begin());
}

int BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemCount(
    ComboBox* source) {
  return static_cast<int>(nodes_.size() + 1);
}

std::wstring BookmarkBubbleView::RecentlyUsedFoldersModel::GetItemAt(
    ComboBox* source, int index) {
  if (index == nodes_.size())
    return l10n_util::GetString(IDS_BOOMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER);
  return nodes_[index]->GetTitle();
}

BookmarkNode* BookmarkBubbleView::RecentlyUsedFoldersModel::GetNodeAt(
    int index) {
  return nodes_[index];
}

void BookmarkBubbleView::RecentlyUsedFoldersModel::RemoveNode(
    BookmarkNode* node) {
  std::vector<BookmarkNode*>::iterator i =
      find(nodes_.begin(), nodes_.end(), node);
  if (i != nodes_.end())
    nodes_.erase(i);
}

// BookmarkBubbleView ---------------------------------------------------------

// static
void BookmarkBubbleView::Show(HWND parent,
                              const gfx::Rect& bounds,
                              InfoBubbleDelegate* delegate,
                              Profile* profile,
                              const GURL& url,
                              bool newly_bookmarked) {
  BookmarkBubbleView* view = new BookmarkBubbleView(delegate, profile, url,
                                                    newly_bookmarked);
  InfoBubble::Show(parent, bounds, view, view);
  GURL url_ptr(url);
  NotificationService::current()->Notify(
      NOTIFY_BOOKMARK_BUBBLE_SHOWN,
      Source<Profile>(profile->GetOriginalProfile()),
      Details<GURL>(&url_ptr));
  view->BubbleShown();
}

BookmarkBubbleView::~BookmarkBubbleView() {
  SetNodeTitleFromTextField();
}

void BookmarkBubbleView::DidChangeBounds(const CRect& previous,
                                         const CRect& current) {
  Layout();
}

void BookmarkBubbleView::BubbleShown() {
  DCHECK(GetViewContainer());
  ChromeViews::FocusManager* focus_manager =
      ChromeViews::FocusManager::GetFocusManager(GetViewContainer()->GetHWND());
  focus_manager->RegisterAccelerator(
      ChromeViews::Accelerator(VK_RETURN, false, false, false), this);

  title_tf_->RequestFocus();
  title_tf_->SelectAll();
}

bool BookmarkBubbleView::AcceleratorPressed(
    const ChromeViews::Accelerator& accelerator) {
  if (accelerator.GetKeyCode() != VK_RETURN)
    return false;

  Close();
  return true;
}

BookmarkBubbleView::BookmarkBubbleView(InfoBubbleDelegate* delegate,
                                       Profile* profile,
                                       const GURL& url,
                                       bool newly_bookmarked)
    : delegate_(delegate),
      profile_(profile),
      url_(url),
      newly_bookmarked_(newly_bookmarked),
      parent_model_(profile_->GetBookmarkModel(),
                    profile_->GetBookmarkModel()->GetNodeByURL(url)) {
  Init();
}

void BookmarkBubbleView::Init() {
  if (!kCloseImage) {
    kCloseImage = ResourceBundle::GetSharedInstance().GetBitmapNamed(
      IDR_INFO_BUBBLE_CLOSE);
  }

  remove_link_ = new Link(l10n_util::GetString(
      IDS_BOOMARK_BUBBLE_REMOVE_BOOKMARK));
  remove_link_->SetController(this);

  edit_button_ = new NativeButton(
      l10n_util::GetString(IDS_BOOMARK_BUBBLE_OPTIONS));
  edit_button_->SetListener(this);

  close_button_ = new NativeButton(l10n_util::GetString(IDS_CLOSE));
  close_button_->SetListener(this);

  parent_combobox_ = new ComboBox(&parent_model_);
  parent_combobox_->SetSelectedItem(parent_model_.node_parent_index());
  parent_combobox_->SetListener(this);

  Label* title_label = new Label(l10n_util::GetString(
      newly_bookmarked_ ? IDS_BOOMARK_BUBBLE_PAGE_BOOKMARKED :
                          IDS_BOOMARK_BUBBLE_PAGE_BOOKMARK));
  title_label->SetFont(
      ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
  title_label->SetColor(kTitleColor);

  GridLayout* layout = new GridLayout(this);
  SetLayoutManager(layout);

  ColumnSet* cs = layout->AddColumnSet(0);

  // Top (title) row.
  cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
                0, 0);
  cs->AddPaddingColumn(1, kUnrelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0, GridLayout::USE_PREF,
                0, 0);

  // Middle (input field) rows.
  cs = layout->AddColumnSet(2);
  cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
                GridLayout::USE_PREF, 0, 0);
  cs->AddPaddingColumn(0, kRelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
                GridLayout::USE_PREF, 0, kMinimumFieldSize);

  // Bottom (buttons) row.
  cs = layout->AddColumnSet(3);
  cs->AddPaddingColumn(1, kRelatedControlHorizontalSpacing);
  cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
                GridLayout::USE_PREF, 0, 0);
  // We subtract 2 to account for the natural button padding, and
  // to bring the separation visually in line with the row separation
  // height.
  cs->AddPaddingColumn(0, kRelatedButtonHSpacing - 2);
  cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0,
                GridLayout::USE_PREF, 0, 0);

  layout->StartRow(0, 0);
  layout->AddView(title_label);
  layout->AddView(remove_link_);

  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);
  layout->StartRow(0, 2);
  layout->AddView(
      new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_TITLE_TEXT)));
  title_tf_ = new ChromeViews::TextField();
  title_tf_->SetText(GetTitle());
  layout->AddView(title_tf_);

  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);

  layout->StartRow(0, 2);
  layout->AddView(
      new Label(l10n_util::GetString(IDS_BOOMARK_BUBBLE_FOLDER_TEXT)));
  layout->AddView(parent_combobox_);
  layout->AddPaddingRow(0, kRelatedControlSmallVerticalSpacing);

  layout->StartRow(0, 3);
  layout->AddView(edit_button_);
  layout->AddView(close_button_);
}

std::wstring BookmarkBubbleView::GetTitle() {
  BookmarkModel* bookmark_model= profile_->GetBookmarkModel();
  BookmarkNode* node = bookmark_model->GetNodeByURL(url_);
  if (node)
    return node->GetTitle();
  else
    NOTREACHED();
  return std::wstring();
}

void BookmarkBubbleView::ButtonPressed(ChromeViews::NativeButton* sender) {
  if (sender == edit_button_) {
    UserMetrics::RecordAction(L"BookmarkBubble_Edit", profile_);
    ShowEditor();
  } else {
    DCHECK(sender == close_button_);
    Close();
  }
  // WARNING: we've most likely been deleted when CloseWindow returns.
}

void BookmarkBubbleView::LinkActivated(Link* source, int event_flags) {
  DCHECK(source == remove_link_);
  RemoveBookmark();
}

void BookmarkBubbleView::ItemChanged(ComboBox* combo_box,
                                     int prev_index,
                                     int new_index) {
  if (new_index + 1 == parent_model_.GetItemCount(parent_combobox_)) {
    UserMetrics::RecordAction(L"BookmarkBubble_EditFromCombobox", profile_);

    ShowEditor();
    return;
  }
  BookmarkModel* model = profile_->GetBookmarkModel();
  BookmarkNode* node = model->GetNodeByURL(url_);
  if (node) {
    BookmarkNode* new_parent = parent_model_.GetNodeAt(new_index);
    if (new_parent != node->GetParent()) {
      UserMetrics::RecordAction(L"BookmarkBubble_ChangeParent", profile_);
      model->Move(node, new_parent, new_parent->GetChildCount());
    }
  }
}

void BookmarkBubbleView::InfoBubbleClosing(InfoBubble* info_bubble) {
  if (delegate_)
    delegate_->InfoBubbleClosing(info_bubble);
  NotificationService::current()->Notify(
      NOTIFY_BOOKMARK_BUBBLE_HIDDEN,
      Source<Profile>(profile_->GetOriginalProfile()),
      NotificationService::NoDetails());
}

bool BookmarkBubbleView::CloseOnEscape() {
  return delegate_ ? delegate_->CloseOnEscape() : true;
}

void BookmarkBubbleView::Close() {
  static_cast<InfoBubble*>(GetViewContainer())->Close();
}

void BookmarkBubbleView::RemoveBookmark() {
  UserMetrics::RecordAction(L"BookmarkBubble_Unstar", profile_);

  GURL url = url_;
  BookmarkModel* model = profile_->GetBookmarkModel();
  // Close first, then notify the service. That way we know we won't be
  // visible and don't have to worry about some other window becoming
  // activated and deleting us before we invoke Close.
  Close();
  // WARNING: we've likely been deleted.
  if (model)
    model->SetURLStarred(url, std::wstring(), false);
}

void BookmarkBubbleView::ShowEditor() {
  // The user may have edited the title, commit it now.
  SetNodeTitleFromTextField();

  // Parent the editor to our root ancestor (not the root we're in, as that
  // is the info bubble and will close shortly).
  HWND parent = GetAncestor(GetViewContainer()->GetHWND(), GA_ROOTOWNER);

  // We're about to show the bookmark editor. When the bookmark editor closes
  // we want the browser to become active. HWNDViewContainer::Hide() does a
  // hide in a such way that activation isn't changed, which means when we
  // close Windows gets confused as to who it should give active status to.
  // We explicitly hide the bookmark bubble window in such a way that
  // activation status changes. That way, when the editor closes, activation
  // is properly restored to the browser.
  ShowWindow(GetViewContainer()->GetHWND(), SW_HIDE);

  // Even though we just hid the window, we need to invoke Close to schedule
  // the delete and all that.
  Close();

  BookmarkEditorView::Show(parent, profile_, url_, title_);
}

void BookmarkBubbleView::SetNodeTitleFromTextField() {
  BookmarkModel* model = profile_->GetBookmarkModel();
  BookmarkNode* node = model->GetNodeByURL(url_);
  if (node) {
    const std::wstring new_title = title_tf_->GetText();
    if (new_title != node->GetTitle()) {
      model->SetTitle(node, new_title);
      UserMetrics::RecordAction(L"BookmarkBubble_ChangeTitleInBubble",
                                profile_);
    }
  }
}
<|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/app/chrome_dll_resource.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/escape.h"
#include "net/test/test_server.h"

struct IsSearchProviderTestData;

class SearchProviderTest : public UITest {
 protected:
  SearchProviderTest();

  IsSearchProviderTestData StartIsSearchProviderInstalledTest(
      BrowserProxy* browser_proxy,
      const char* host,
      const char* expected_result);

  void FinishIsSearchProviderInstalledTest(
      const IsSearchProviderTestData& data);

  net::TestServer test_server_;

 private:
  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
};

SearchProviderTest::SearchProviderTest()
    : test_server_(net::TestServer::TYPE_HTTP,
                   FilePath(FILE_PATH_LITERAL("chrome/test/data"))) {
  // Enable the search provider additions.
  launch_arguments_.AppendSwitch(switches::kEnableSearchProviderApiV2);

  // Map all hosts to our local server.
  std::string host_rule("MAP * " + test_server_.host_port_pair().ToString());
  launch_arguments_.AppendSwitchASCII(switches::kHostRules, host_rule);
}

struct IsSearchProviderTestData {
  IsSearchProviderTestData() {
  }

  IsSearchProviderTestData(TabProxy* t,
                           std::string h,
                           GURL url)
      : tab(t),
        host(h),
        test_url(url) {
  }

  scoped_refptr<TabProxy> tab;
  std::string host;
  GURL test_url;
};

IsSearchProviderTestData SearchProviderTest::StartIsSearchProviderInstalledTest(
    BrowserProxy* browser_proxy,
    const char* host,
    const char* expected_result) {
  // Set-up a new tab for the navigation.
  int num_tabs = 0;
  if (!browser_proxy->GetTabCount(&num_tabs)) {
    ADD_FAILURE() << "BrowserProxy::GetTabCount failed.";
    return IsSearchProviderTestData();
  }

  GURL blank(chrome::kAboutBlankURL);
  if (!browser_proxy->AppendTab(blank)) {
    ADD_FAILURE() << "BrowserProxy::AppendTab failed.";
    return IsSearchProviderTestData();
  }

  scoped_refptr<TabProxy> tab(browser_proxy->GetTab(num_tabs));
  if (!tab.get()) {
    ADD_FAILURE() << "BrowserProxy::GetTab for the new tab failed.";
    return IsSearchProviderTestData();
  }

  // Go to the test page.
  GURL local_url =
      test_server_.GetURL("files/is_search_provider_installed.html");
  GURL test_url(std::string("http://") + host + local_url.path() +
                "#" + expected_result);
  EXPECT_TRUE(tab->NavigateToURLAsync(test_url));

  // Bundle up information needed to verify the result.
  return IsSearchProviderTestData(tab, host, test_url);
}

void SearchProviderTest::FinishIsSearchProviderInstalledTest(
    const IsSearchProviderTestData& data) {
  ASSERT_TRUE(data.tab.get());

  std::string cookie_name = data.host + "testResult";
  std::string escaped_value =
      WaitUntilCookieNonEmpty(data.tab, data.test_url,
                              cookie_name.c_str(), action_max_timeout_ms());

  // Unescapes and normalizes the actual result.
  std::string value = UnescapeURLComponent(
      escaped_value,
      UnescapeRule::NORMAL | UnescapeRule::SPACES |
      UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS);
  value += "\n";
  ReplaceSubstringsAfterOffset(&value, 0, "\r", "");
  EXPECT_STREQ("1\n", value.c_str());
}

TEST_F(SearchProviderTest, TestIsSearchProviderInstalled) {
  ASSERT_TRUE(test_server_.Start());

  // Use the default search provider, other installed search provider, and
  // one not installed as well. (Note that yahoo isn't tested because the
  // its host name varies a lot for different locales unlike Google and Bing,
  // which would make the test fail depending on the machine's locale.)
  const char* test_hosts[] = { "www.google.com",
                               "www.bing.com",
                               "localhost" };
  const char* expected_results[] = { "2",
                                     "1",
                                     "0" };
  COMPILE_ASSERT(arraysize(test_hosts) == arraysize(expected_results),
                 there_should_be_a_result_for_each_host);
  IsSearchProviderTestData test_data[2 * arraysize(test_hosts)];

  // Start results for the normal mode.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i] = StartIsSearchProviderInstalledTest(
        browser, test_hosts[i], expected_results[i]);
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }

  // Start tests for incognito mode (and verify the result is 0).
  ASSERT_TRUE(browser->RunCommand(IDC_NEW_INCOGNITO_WINDOW));
  scoped_refptr<BrowserProxy> incognito(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(incognito.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i + arraysize(test_hosts)] = StartIsSearchProviderInstalledTest(
        incognito, test_hosts[i], "0");
    FinishIsSearchProviderInstalledTest(test_data[i + arraysize(test_hosts)]);
  }

  // The following should be re-enabled. At the moment, there are problems with
  // doing all of these queries in parallel -- see http://crbug.com/60043.
#if 0
  // Remove the calls to FinishIsSearchProviderInstalledTest above when
  // re-enabling this code.

  // Do the verification.
  for (size_t i = 0; i < arraysize(test_data); ++i) {
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }
#endif
}
<commit_msg>This change moves the test_server_.Start from the test body to the test constructor. This needs to be done so the browser launch arguments can include the correct port in the mapping rules.<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/app/chrome_dll_resource.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/escape.h"
#include "net/test/test_server.h"

struct IsSearchProviderTestData;

class SearchProviderTest : public UITest {
 protected:
  SearchProviderTest();

  IsSearchProviderTestData StartIsSearchProviderInstalledTest(
      BrowserProxy* browser_proxy,
      const char* host,
      const char* expected_result);

  void FinishIsSearchProviderInstalledTest(
      const IsSearchProviderTestData& data);

  net::TestServer test_server_;
  bool test_server_started_;

 private:
  DISALLOW_COPY_AND_ASSIGN(SearchProviderTest);
};

SearchProviderTest::SearchProviderTest()
    : test_server_(net::TestServer::TYPE_HTTP,
                   FilePath(FILE_PATH_LITERAL("chrome/test/data"))),
      test_server_started_(false) {
  // The test_server is started in the constructor (rather than the test body)
  // so the mapping rules below can include the ephemeral port number.
  test_server_started_ = test_server_.Start();
  if (!test_server_started_)
    return;

  // Enable the search provider additions.
  launch_arguments_.AppendSwitch(switches::kEnableSearchProviderApiV2);

  // Map all hosts to our local server.
  std::string host_rule("MAP * " + test_server_.host_port_pair().ToString());
  launch_arguments_.AppendSwitchASCII(switches::kHostRules, host_rule);
}

struct IsSearchProviderTestData {
  IsSearchProviderTestData() {
  }

  IsSearchProviderTestData(TabProxy* t,
                           std::string h,
                           GURL url)
      : tab(t),
        host(h),
        test_url(url) {
  }

  scoped_refptr<TabProxy> tab;
  std::string host;
  GURL test_url;
};

IsSearchProviderTestData SearchProviderTest::StartIsSearchProviderInstalledTest(
    BrowserProxy* browser_proxy,
    const char* host,
    const char* expected_result) {
  // Set-up a new tab for the navigation.
  int num_tabs = 0;
  if (!browser_proxy->GetTabCount(&num_tabs)) {
    ADD_FAILURE() << "BrowserProxy::GetTabCount failed.";
    return IsSearchProviderTestData();
  }

  GURL blank(chrome::kAboutBlankURL);
  if (!browser_proxy->AppendTab(blank)) {
    ADD_FAILURE() << "BrowserProxy::AppendTab failed.";
    return IsSearchProviderTestData();
  }

  scoped_refptr<TabProxy> tab(browser_proxy->GetTab(num_tabs));
  if (!tab.get()) {
    ADD_FAILURE() << "BrowserProxy::GetTab for the new tab failed.";
    return IsSearchProviderTestData();
  }

  // Go to the test page.
  GURL local_url =
      test_server_.GetURL("files/is_search_provider_installed.html");
  GURL test_url(std::string("http://") + host + local_url.path() +
                "#" + expected_result);
  EXPECT_TRUE(tab->NavigateToURLAsync(test_url));

  // Bundle up information needed to verify the result.
  return IsSearchProviderTestData(tab, host, test_url);
}

void SearchProviderTest::FinishIsSearchProviderInstalledTest(
    const IsSearchProviderTestData& data) {
  ASSERT_TRUE(data.tab.get());

  std::string cookie_name = data.host + "testResult";
  std::string escaped_value =
      WaitUntilCookieNonEmpty(data.tab, data.test_url,
                              cookie_name.c_str(), action_max_timeout_ms());

  // Unescapes and normalizes the actual result.
  std::string value = UnescapeURLComponent(
      escaped_value,
      UnescapeRule::NORMAL | UnescapeRule::SPACES |
      UnescapeRule::URL_SPECIAL_CHARS | UnescapeRule::CONTROL_CHARS);
  value += "\n";
  ReplaceSubstringsAfterOffset(&value, 0, "\r", "");
  EXPECT_STREQ("1\n", value.c_str());
}

TEST_F(SearchProviderTest, TestIsSearchProviderInstalled) {
  ASSERT_TRUE(test_server_started_);

  // Use the default search provider, other installed search provider, and
  // one not installed as well. (Note that yahoo isn't tested because the
  // its host name varies a lot for different locales unlike Google and Bing,
  // which would make the test fail depending on the machine's locale.)
  const char* test_hosts[] = { "www.google.com",
                               "www.bing.com",
                               "localhost" };
  const char* expected_results[] = { "2",
                                     "1",
                                     "0" };
  COMPILE_ASSERT(arraysize(test_hosts) == arraysize(expected_results),
                 there_should_be_a_result_for_each_host);
  IsSearchProviderTestData test_data[2 * arraysize(test_hosts)];

  // Start results for the normal mode.
  scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i] = StartIsSearchProviderInstalledTest(
        browser, test_hosts[i], expected_results[i]);
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }

  // Start tests for incognito mode (and verify the result is 0).
  ASSERT_TRUE(browser->RunCommand(IDC_NEW_INCOGNITO_WINDOW));
  scoped_refptr<BrowserProxy> incognito(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(incognito.get());
  for (size_t i = 0; i < arraysize(test_hosts); ++i) {
    test_data[i + arraysize(test_hosts)] = StartIsSearchProviderInstalledTest(
        incognito, test_hosts[i], "0");
    FinishIsSearchProviderInstalledTest(test_data[i + arraysize(test_hosts)]);
  }

  // The following should be re-enabled. At the moment, there are problems with
  // doing all of these queries in parallel -- see http://crbug.com/60043.
#if 0
  // Remove the calls to FinishIsSearchProviderInstalledTest above when
  // re-enabling this code.

  // Do the verification.
  for (size_t i = 0; i < arraysize(test_data); ++i) {
    FinishIsSearchProviderInstalledTest(test_data[i]);
  }
#endif
}
<|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 "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"

namespace {

// Provides a UI Test that lets us take the browser to a url, and
// wait for a cookie value to be set before closing the page.
class UrlFetchTest : public UITest {
 public:
  UrlFetchTest() {
    show_window_ = true;
    dom_automation_enabled_ = true;
  }
  struct UrlFetchTestResult {
    std::string cookie_value;
    std::string javascript_variable;
  };

  void RunTest(const GURL& url, const char *waitCookieName,
               const char *waitCookieValue, const wchar_t *varToFetch,
               UrlFetchTestResult *result) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    tab->NavigateToURL(url);

    if (waitCookieName) {
      if (waitCookieValue) {
        bool completed = WaitUntilCookieValue(tab.get(), url, waitCookieName,
                                              3000, UITest::test_timeout_ms(),
                                              waitCookieValue);
        ASSERT_TRUE(completed);
      } else {
        result->cookie_value = WaitUntilCookieNonEmpty(
            tab.get(), url, waitCookieName, 3000, UITest::test_timeout_ms());
        ASSERT_TRUE(result->cookie_value.length());
      }
    }
    if (varToFetch) {
      std::wstring script = StringPrintf(
          L"window.domAutomationController.send(%ls);", varToFetch);

      std::wstring value;
      bool success = tab->ExecuteAndExtractString(L"", script, &value);
      ASSERT_TRUE(success);
      result->javascript_variable = WideToUTF8(value);
    }
  }
};

bool writeValueToFile(std::string value, std::wstring filePath) {
  int retval = file_util::WriteFile(
      FilePath::FromWStringHack(filePath), value.c_str(), value.length());
  return retval == static_cast<int>(value.length());
}

// To actually do anything useful, this test should have a url
// passed on the command line, eg.
//
// --url=http://foo.bar.com
//
// Additional arguments:
//
// --wait_cookie_name=<name>
//   Waits for a cookie named <name> to be set before exiting successfully.
//
// --wait_cookie_value=<value>
//   In conjunction with --wait_cookie_name, this waits for a specific value
//   to be set. (Incompatible with --wait_cookie_output)
//
// --wait_cookie_output=<filepath>
//   In conjunction with --wait_cookie_name, this saves the cookie value to
//   a file at the given path. (Incompatible with --wait_cookie_value)
//
// --jsvar=<name>
//   At the end of the test, fetch the named javascript variable from the page.
//
// --jsvar_output=<filepath>
//   Write the value of the variable named by '--jsvar' to a file at the given
//   path.
TEST_F(UrlFetchTest, UrlFetch) {
  const CommandLine *cmdLine = CommandLine::ForCurrentProcess();

  if (!cmdLine->HasSwitch(L"url")) {
    return;
  }

  std::string cookieName =
    WideToASCII(cmdLine->GetSwitchValue(L"wait_cookie_name"));
  std::string cookieValue =
    WideToASCII(cmdLine->GetSwitchValue(L"wait_cookie_value"));

  std::wstring jsvar = cmdLine->GetSwitchValue(L"jsvar");

  UrlFetchTestResult result;
  RunTest(GURL(WideToASCII(cmdLine->GetSwitchValue(L"url"))),
          cookieName.length() > 0 ? cookieName.c_str() : NULL,
          cookieValue.length() > 0 ? cookieValue.c_str() : NULL,
          jsvar.length() > 0 ? jsvar.c_str() : NULL,
          &result);

  // Write out the cookie if requested
  std::wstring cookieOutputPath =
    cmdLine->GetSwitchValue(L"wait_cookie_output");
  if (cookieOutputPath.length() > 0) {
    ASSERT_TRUE(writeValueToFile(result.cookie_value, cookieOutputPath));
  }

  // Write out the JS Variable if requested
  std::wstring jsvarOutputPath = cmdLine->GetSwitchValue(L"jsvar_output");
  if (jsvarOutputPath.length() > 0) {
    ASSERT_TRUE(writeValueToFile(result.javascript_variable, jsvarOutputPath));
  }
}

}  // namespace
<commit_msg>Addding reference build results for DOM perf 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 "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/string_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"

namespace {

// Provides a UI Test that lets us take the browser to a url, and
// wait for a cookie value to be set before closing the page.
class UrlFetchTest : public UITest {
 public:
  UrlFetchTest() {
    show_window_ = true;
    dom_automation_enabled_ = true;
  }
  struct UrlFetchTestResult {
    std::string cookie_value;
    std::string javascript_variable;
  };

  void SetUp() {
    const CommandLine *cmdLine = CommandLine::ForCurrentProcess();
    if (cmdLine->HasSwitch(L"reference_build")) {
      FilePath dir;
      PathService::Get(chrome::DIR_TEST_TOOLS, &dir);
      dir = dir.AppendASCII("reference_build");
#if defined(OS_WIN)
      dir = dir.AppendASCII("chrome");
#elif defined(OS_LINUX)
      dir = dir.AppendASCII("chrome_linux");
#elif defined(OS_MACOSX)
      dir = dir.AppendASCII("chrome_mac");
#endif
      browser_directory_ = dir;
    }
    UITest::SetUp();
  }

  void RunTest(const GURL& url, const char *waitCookieName,
               const char *waitCookieValue, const wchar_t *varToFetch,
               UrlFetchTestResult *result) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    tab->NavigateToURL(url);

    if (waitCookieName) {
      if (waitCookieValue) {
        bool completed = WaitUntilCookieValue(tab.get(), url, waitCookieName,
                                              3000, UITest::test_timeout_ms(),
                                              waitCookieValue);
        ASSERT_TRUE(completed);
      } else {
        result->cookie_value = WaitUntilCookieNonEmpty(
            tab.get(), url, waitCookieName, 3000, UITest::test_timeout_ms());
        ASSERT_TRUE(result->cookie_value.length());
      }
    }
    if (varToFetch) {
      std::wstring script = StringPrintf(
          L"window.domAutomationController.send(%ls);", varToFetch);

      std::wstring value;
      bool success = tab->ExecuteAndExtractString(L"", script, &value);
      ASSERT_TRUE(success);
      result->javascript_variable = WideToUTF8(value);
    }
  }
};

bool writeValueToFile(std::string value, std::wstring filePath) {
  int retval = file_util::WriteFile(
      FilePath::FromWStringHack(filePath), value.c_str(), value.length());
  return retval == static_cast<int>(value.length());
}

// To actually do anything useful, this test should have a url
// passed on the command line, eg.
//
// --url=http://foo.bar.com
//
// Additional arguments:
//
// --wait_cookie_name=<name>
//   Waits for a cookie named <name> to be set before exiting successfully.
//
// --wait_cookie_value=<value>
//   In conjunction with --wait_cookie_name, this waits for a specific value
//   to be set. (Incompatible with --wait_cookie_output)
//
// --wait_cookie_output=<filepath>
//   In conjunction with --wait_cookie_name, this saves the cookie value to
//   a file at the given path. (Incompatible with --wait_cookie_value)
//
// --jsvar=<name>
//   At the end of the test, fetch the named javascript variable from the page.
//
// --jsvar_output=<filepath>
//   Write the value of the variable named by '--jsvar' to a file at the given
//   path.
//
// --reference_build
//   Use the reference build of chrome for the test.
TEST_F(UrlFetchTest, UrlFetch) {
  const CommandLine *cmdLine = CommandLine::ForCurrentProcess();

  if (!cmdLine->HasSwitch(L"url")) {
    return;
  }

  std::string cookieName =
    WideToASCII(cmdLine->GetSwitchValue(L"wait_cookie_name"));
  std::string cookieValue =
    WideToASCII(cmdLine->GetSwitchValue(L"wait_cookie_value"));

  std::wstring jsvar = cmdLine->GetSwitchValue(L"jsvar");

  UrlFetchTestResult result;
  RunTest(GURL(WideToASCII(cmdLine->GetSwitchValue(L"url"))),
          cookieName.length() > 0 ? cookieName.c_str() : NULL,
          cookieValue.length() > 0 ? cookieValue.c_str() : NULL,
          jsvar.length() > 0 ? jsvar.c_str() : NULL,
          &result);

  // Write out the cookie if requested
  std::wstring cookieOutputPath =
    cmdLine->GetSwitchValue(L"wait_cookie_output");
  if (cookieOutputPath.length() > 0) {
    ASSERT_TRUE(writeValueToFile(result.cookie_value, cookieOutputPath));
  }

  // Write out the JS Variable if requested
  std::wstring jsvarOutputPath = cmdLine->GetSwitchValue(L"jsvar_output");
  if (jsvarOutputPath.length() > 0) {
    ASSERT_TRUE(writeValueToFile(result.javascript_variable, jsvarOutputPath));
  }
}

}  // namespace
<|endoftext|>


<commit_before>//===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "PropertyDeclarationCheck.h"
#include <algorithm>
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Regex.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace objc {

namespace {

// For StandardProperty the naming style is 'lowerCamelCase'.
// For CategoryProperty especially in categories of system class,
// to avoid naming conflict, the suggested naming style is
// 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
enum NamingStyle {
  StandardProperty = 1,
  CategoryProperty = 2,
};

/// The acronyms are aggregated from multiple sources including
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
///
/// Keep this list sorted.
constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = {
    "[2-9]G",
    "ACL",
    "API",
    "AR",
    "ARGB",
    "ASCII",
    "AV",
    "BGRA",
    "CA",
    "CF",
    "CG",
    "CI",
    "CRC",
    "CV",
    "CMYK",
    "DNS",
    "FPS",
    "FTP",
    "GIF",
    "GL",
    "GPS",
    "GUID",
    "HD",
    "HDR",
    "HMAC",
    "HTML",
    "HTTP",
    "HTTPS",
    "HUD",
    "ID",
    "JPG",
    "JS",
    "LAN",
    "LZW",
    "MAC",
    "MD",
    "MDNS",
    "MIDI",
    "NS",
    "OS",
    "PDF",
    "PIN",
    "PNG",
    "POI",
    "PSTN",
    "PTR",
    "QA",
    "QOS",
    "RGB",
    "RGBA",
    "RGBX",
    "RIPEMD",
    "ROM",
    "RPC",
    "RTF",
    "RTL",
    "SC",
    "SDK",
    "SHA",
    "SSO",
    "TCP",
    "TIFF",
    "TTS",
    "UI",
    "URI",
    "URL",
    "UUID",
    "VC",
    "VOIP",
    "VPN",
    "VR",
    "W",
    "WAN",
    "X",
    "XML",
    "Y",
    "Z",
};

/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
/// 'camelCase' or 'abc_camelCase'. For other cases the users need to
/// come up with a proper name by their own.
/// FIXME: provide fix for snake_case to snakeCase
FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
  auto Name = Decl->getName();
  auto NewName = Decl->getName().str();
  size_t Index = 0;
  if (Style == CategoryProperty) {
    Index = Name.find_first_of('_') + 1;
    NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
  }
  if (Index < Name.size()) {
    NewName[Index] = tolower(NewName[Index]);
    if (NewName != Name) {
      return FixItHint::CreateReplacement(
          CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
          llvm::StringRef(NewName));
    }
  }
  return FixItHint();
}

std::string AcronymsGroupRegex(llvm::ArrayRef<std::string> EscapedAcronyms) {
  return "(" +
         llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "s?|") +
         "s?)";
}

std::string validPropertyNameRegex(llvm::ArrayRef<std::string> EscapedAcronyms,
                                   bool UsedInMatcher) {
  // Allow any of these names:
  // foo
  // fooBar
  // url
  // urlString
  // URL
  // URLString
  // bundleID
  std::string StartMatcher = UsedInMatcher ? "::" : "^";
  std::string AcronymsMatcher = AcronymsGroupRegex(EscapedAcronyms);
  return StartMatcher + "(" + AcronymsMatcher + "[A-Z]?)?[a-z]+[a-z0-9]*(" +
         AcronymsMatcher + "|([A-Z][a-z0-9]+)|A|I)*$";
}

bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
  auto RegexExp = llvm::Regex("^[a-zA-Z]+_[a-zA-Z0-9][a-zA-Z0-9_]+$");
  return RegexExp.match(PropertyName);
}

bool prefixedPropertyNameValid(llvm::StringRef PropertyName,
                               llvm::ArrayRef<std::string> Acronyms) {
  size_t Start = PropertyName.find_first_of('_');
  assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
  auto Prefix = PropertyName.substr(0, Start);
  if (Prefix.lower() != Prefix) {
    return false;
  }
  auto RegexExp =
      llvm::Regex(llvm::StringRef(validPropertyNameRegex(Acronyms, false)));
  return RegexExp.match(PropertyName.substr(Start + 1));
}
}  // namespace

PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
                                                   ClangTidyContext *Context)
    : ClangTidyCheck(Name, Context),
      SpecialAcronyms(
          utils::options::parseStringList(Options.get("Acronyms", ""))),
      IncludeDefaultAcronyms(Options.get("IncludeDefaultAcronyms", true)),
      EscapedAcronyms() {}

void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
  // this check should only be applied to ObjC sources.
  if (!getLangOpts().ObjC1 && !getLangOpts().ObjC2) {
    return;
  }
  if (IncludeDefaultAcronyms) {
    EscapedAcronyms.reserve(llvm::array_lengthof(DefaultSpecialAcronyms) +
                            SpecialAcronyms.size());
    // No need to regex-escape the default acronyms.
    EscapedAcronyms.insert(EscapedAcronyms.end(),
                           std::begin(DefaultSpecialAcronyms),
                           std::end(DefaultSpecialAcronyms));
  } else {
    EscapedAcronyms.reserve(SpecialAcronyms.size());
  }
  // In case someone defines a prefix which includes a regex
  // special character, regex-escape all the user-defined prefixes.
  std::transform(SpecialAcronyms.begin(), SpecialAcronyms.end(),
                 std::back_inserter(EscapedAcronyms),
                 [](const std::string &s) { return llvm::Regex::escape(s); });
  Finder->addMatcher(
      objcPropertyDecl(
          // the property name should be in Lower Camel Case like
          // 'lowerCamelCase'
          unless(matchesName(validPropertyNameRegex(EscapedAcronyms, true))))
          .bind("property"),
      this);
}

void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *MatchedDecl =
      Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
  assert(MatchedDecl->getName().size() > 0);
  auto *DeclContext = MatchedDecl->getDeclContext();
  auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);

  auto AcronymsRegex =
      llvm::Regex("^" + AcronymsGroupRegex(EscapedAcronyms) + "$");
  if (AcronymsRegex.match(MatchedDecl->getName())) {
    return;
  }
  if (CategoryDecl != nullptr &&
      hasCategoryPropertyPrefix(MatchedDecl->getName())) {
    if (!prefixedPropertyNameValid(MatchedDecl->getName(), EscapedAcronyms) ||
        CategoryDecl->IsClassExtension()) {
      NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
                                                           : CategoryProperty;
      diag(MatchedDecl->getLocation(),
           "property name '%0' not using lowerCamelCase style or not prefixed "
           "in a category, according to the Apple Coding Guidelines")
          << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);
    }
    return;
  }
  diag(MatchedDecl->getLocation(),
       "property name '%0' not using lowerCamelCase style or not prefixed in "
       "a category, according to the Apple Coding Guidelines")
      << MatchedDecl->getName()
      << generateFixItHint(MatchedDecl, StandardProperty);
}

void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  Options.store(Opts, "Acronyms",
                utils::options::serializeStringList(SpecialAcronyms));
  Options.store(Opts, "IncludeDefaultAcronyms", IncludeDefaultAcronyms);
}

}  // namespace objc
}  // namespace tidy
}  // namespace clang
<commit_msg>[clang-tidy/ObjC] Add SQL to list of acronyms<commit_after>//===--- PropertyDeclarationCheck.cpp - clang-tidy-------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "PropertyDeclarationCheck.h"
#include <algorithm>
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Regex.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace objc {

namespace {

// For StandardProperty the naming style is 'lowerCamelCase'.
// For CategoryProperty especially in categories of system class,
// to avoid naming conflict, the suggested naming style is
// 'abc_lowerCamelCase' (adding lowercase prefix followed by '_').
enum NamingStyle {
  StandardProperty = 1,
  CategoryProperty = 2,
};

/// The acronyms are aggregated from multiple sources including
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html#//apple_ref/doc/uid/20001285-BCIHCGAE
///
/// Keep this list sorted.
constexpr llvm::StringLiteral DefaultSpecialAcronyms[] = {
    "[2-9]G",
    "ACL",
    "API",
    "AR",
    "ARGB",
    "ASCII",
    "AV",
    "BGRA",
    "CA",
    "CF",
    "CG",
    "CI",
    "CRC",
    "CV",
    "CMYK",
    "DNS",
    "FPS",
    "FTP",
    "GIF",
    "GL",
    "GPS",
    "GUID",
    "HD",
    "HDR",
    "HMAC",
    "HTML",
    "HTTP",
    "HTTPS",
    "HUD",
    "ID",
    "JPG",
    "JS",
    "LAN",
    "LZW",
    "MAC",
    "MD",
    "MDNS",
    "MIDI",
    "NS",
    "OS",
    "PDF",
    "PIN",
    "PNG",
    "POI",
    "PSTN",
    "PTR",
    "QA",
    "QOS",
    "RGB",
    "RGBA",
    "RGBX",
    "RIPEMD",
    "ROM",
    "RPC",
    "RTF",
    "RTL",
    "SC",
    "SDK",
    "SHA",
    "SQL",
    "SSO",
    "TCP",
    "TIFF",
    "TTS",
    "UI",
    "URI",
    "URL",
    "UUID",
    "VC",
    "VOIP",
    "VPN",
    "VR",
    "W",
    "WAN",
    "X",
    "XML",
    "Y",
    "Z",
};

/// For now we will only fix 'CamelCase' or 'abc_CamelCase' property to
/// 'camelCase' or 'abc_camelCase'. For other cases the users need to
/// come up with a proper name by their own.
/// FIXME: provide fix for snake_case to snakeCase
FixItHint generateFixItHint(const ObjCPropertyDecl *Decl, NamingStyle Style) {
  auto Name = Decl->getName();
  auto NewName = Decl->getName().str();
  size_t Index = 0;
  if (Style == CategoryProperty) {
    Index = Name.find_first_of('_') + 1;
    NewName.replace(0, Index - 1, Name.substr(0, Index - 1).lower());
  }
  if (Index < Name.size()) {
    NewName[Index] = tolower(NewName[Index]);
    if (NewName != Name) {
      return FixItHint::CreateReplacement(
          CharSourceRange::getTokenRange(SourceRange(Decl->getLocation())),
          llvm::StringRef(NewName));
    }
  }
  return FixItHint();
}

std::string AcronymsGroupRegex(llvm::ArrayRef<std::string> EscapedAcronyms) {
  return "(" +
         llvm::join(EscapedAcronyms.begin(), EscapedAcronyms.end(), "s?|") +
         "s?)";
}

std::string validPropertyNameRegex(llvm::ArrayRef<std::string> EscapedAcronyms,
                                   bool UsedInMatcher) {
  // Allow any of these names:
  // foo
  // fooBar
  // url
  // urlString
  // URL
  // URLString
  // bundleID
  std::string StartMatcher = UsedInMatcher ? "::" : "^";
  std::string AcronymsMatcher = AcronymsGroupRegex(EscapedAcronyms);
  return StartMatcher + "(" + AcronymsMatcher + "[A-Z]?)?[a-z]+[a-z0-9]*(" +
         AcronymsMatcher + "|([A-Z][a-z0-9]+)|A|I)*$";
}

bool hasCategoryPropertyPrefix(llvm::StringRef PropertyName) {
  auto RegexExp = llvm::Regex("^[a-zA-Z]+_[a-zA-Z0-9][a-zA-Z0-9_]+$");
  return RegexExp.match(PropertyName);
}

bool prefixedPropertyNameValid(llvm::StringRef PropertyName,
                               llvm::ArrayRef<std::string> Acronyms) {
  size_t Start = PropertyName.find_first_of('_');
  assert(Start != llvm::StringRef::npos && Start + 1 < PropertyName.size());
  auto Prefix = PropertyName.substr(0, Start);
  if (Prefix.lower() != Prefix) {
    return false;
  }
  auto RegexExp =
      llvm::Regex(llvm::StringRef(validPropertyNameRegex(Acronyms, false)));
  return RegexExp.match(PropertyName.substr(Start + 1));
}
}  // namespace

PropertyDeclarationCheck::PropertyDeclarationCheck(StringRef Name,
                                                   ClangTidyContext *Context)
    : ClangTidyCheck(Name, Context),
      SpecialAcronyms(
          utils::options::parseStringList(Options.get("Acronyms", ""))),
      IncludeDefaultAcronyms(Options.get("IncludeDefaultAcronyms", true)),
      EscapedAcronyms() {}

void PropertyDeclarationCheck::registerMatchers(MatchFinder *Finder) {
  // this check should only be applied to ObjC sources.
  if (!getLangOpts().ObjC1 && !getLangOpts().ObjC2) {
    return;
  }
  if (IncludeDefaultAcronyms) {
    EscapedAcronyms.reserve(llvm::array_lengthof(DefaultSpecialAcronyms) +
                            SpecialAcronyms.size());
    // No need to regex-escape the default acronyms.
    EscapedAcronyms.insert(EscapedAcronyms.end(),
                           std::begin(DefaultSpecialAcronyms),
                           std::end(DefaultSpecialAcronyms));
  } else {
    EscapedAcronyms.reserve(SpecialAcronyms.size());
  }
  // In case someone defines a prefix which includes a regex
  // special character, regex-escape all the user-defined prefixes.
  std::transform(SpecialAcronyms.begin(), SpecialAcronyms.end(),
                 std::back_inserter(EscapedAcronyms),
                 [](const std::string &s) { return llvm::Regex::escape(s); });
  Finder->addMatcher(
      objcPropertyDecl(
          // the property name should be in Lower Camel Case like
          // 'lowerCamelCase'
          unless(matchesName(validPropertyNameRegex(EscapedAcronyms, true))))
          .bind("property"),
      this);
}

void PropertyDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *MatchedDecl =
      Result.Nodes.getNodeAs<ObjCPropertyDecl>("property");
  assert(MatchedDecl->getName().size() > 0);
  auto *DeclContext = MatchedDecl->getDeclContext();
  auto *CategoryDecl = llvm::dyn_cast<ObjCCategoryDecl>(DeclContext);

  auto AcronymsRegex =
      llvm::Regex("^" + AcronymsGroupRegex(EscapedAcronyms) + "$");
  if (AcronymsRegex.match(MatchedDecl->getName())) {
    return;
  }
  if (CategoryDecl != nullptr &&
      hasCategoryPropertyPrefix(MatchedDecl->getName())) {
    if (!prefixedPropertyNameValid(MatchedDecl->getName(), EscapedAcronyms) ||
        CategoryDecl->IsClassExtension()) {
      NamingStyle Style = CategoryDecl->IsClassExtension() ? StandardProperty
                                                           : CategoryProperty;
      diag(MatchedDecl->getLocation(),
           "property name '%0' not using lowerCamelCase style or not prefixed "
           "in a category, according to the Apple Coding Guidelines")
          << MatchedDecl->getName() << generateFixItHint(MatchedDecl, Style);
    }
    return;
  }
  diag(MatchedDecl->getLocation(),
       "property name '%0' not using lowerCamelCase style or not prefixed in "
       "a category, according to the Apple Coding Guidelines")
      << MatchedDecl->getName()
      << generateFixItHint(MatchedDecl, StandardProperty);
}

void PropertyDeclarationCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
  Options.store(Opts, "Acronyms",
                utils::options::serializeStringList(SpecialAcronyms));
  Options.store(Opts, "IncludeDefaultAcronyms", IncludeDefaultAcronyms);
}

}  // namespace objc
}  // namespace tidy
}  // namespace clang
<|endoftext|>


<commit_before>//
// Created by Danni on 09.06.15.
//

#include "TweeCompiler.h"
#include "ZAssemblyGenerator.h"
#include "exceptions.h"
#include <sstream>
#include <iostream>
#include <Passage/Body/Link.h>
#include <Passage/Body/Text.h>
#include <Passage/Body/Newline.h>
#include <Passage/Body/Expressions/Variable.h>
#include <Passage/Body/Macros/IfMacro.h>
#include <Passage/Body/Macros/ElseIfMacro.h>
#include <Passage/Body/Macros/ElseMacro.h>
#include <Passage/Body/Macros/EndIfMacro.h>
#include <plog/Log.h>

using namespace std;

static const string PASSAGE_GLOB = "PASSAGE_PTR",
        JUMP_TABLE_LABEL = "JUMP_TABLE_START",
        JUMP_TABLE_END_LABEL = "JUMP_TABLE_END",
        MAIN_ROUTINE = "main",
        USER_INPUT = "USER_INPUT",
        READ_BEGIN = "READ_BEGIN",
        IF_JUMP_LABEL = "IFJUMP",
        IF_JUMP_END_LABEL = "IFJUMPEND";

static const unsigned int ZSCII_NUM_OFFSET = 49;

//#define ZAS_DEBUG

void maskString(std::string& string) {
    std::replace( string.begin(), string.end(), ' ', '_');
}

string routineNameForPassageName(std::string passageName) {
    stringstream ss;
    maskString(passageName);
    ss << "R_" << passageName;
    return ss.str();
}

string routineNameForPassage(Passage& passage) {
    return routineNameForPassageName(passage.getHead().getName());
}

string labelForPassage(Passage& passage) {
    stringstream ss;
    string passageName = passage.getHead().getName();
    maskString(passageName);
    ss << "L_" << passageName;
    return ss.str();
}

void TweeCompiler::compile(TweeFile &tweeFile, std::ostream &out) {
    ZAssemblyGenerator assgen(out);
    vector<Passage> passages = tweeFile.getPassages();

    int ifDepth = -1;
    int ifJumpLabelID = 0;
    int ifEndJumpLabelID = 0;
    int possibleIfDepth = 255;
    std::array<std::string,255> nextJumpLabels;
    std::array<std::string,255> endJumpLabels;
    std::array<int,255> precedingIfMacros;
    for(auto precedingIfMacro = precedingIfMacros.begin(); precedingIfMacro != precedingIfMacros.end(); ++precedingIfMacro) {
        LOG_DEBUG << precedingIfMacro;
    }
    {
        int i = 0;
        for (auto passage = passages.begin(); passage != passages.end(); ++passage) {
            passageName2id[passage->getHead().getName()] = i;
            i++;
        }
    }

    // main routine
    {
        // globals
        assgen.addGlobal(PASSAGE_GLOB)
                .addGlobal(USER_INPUT);

        // call start routine first
        assgen.addRoutine(MAIN_ROUTINE)
                .markStart()
                .call(routineNameForPassageName("start"), PASSAGE_GLOB)
                .addLabel(JUMP_TABLE_LABEL);

        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            int passageId = passageName2id.at(passage->getHead().getName());

            assgen.jumpEquals(ZAssemblyGenerator::makeArgs({std::to_string(passageId), PASSAGE_GLOB}), labelForPassage(*passage));
        }

        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            assgen.addLabel(labelForPassage(*passage))
                    .call(routineNameForPassage(*passage), PASSAGE_GLOB)
                    .jump(JUMP_TABLE_LABEL);
        }

        assgen.addLabel(JUMP_TABLE_END_LABEL);

        assgen.quit();
    }

    // passage routines
    {
        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            const vector<unique_ptr<BodyPart>>& bodyParts = passage->getBody().getBodyParts();

            // declare passage routine
            assgen.addRoutine(routineNameForPassage(*passage));

            assgen.println(string("***** ") + passage->getHead().getName() + string(" *****"));

            //  print passage contents
            for(auto it = bodyParts.begin(); it != bodyParts.end(); it++) {
                BodyPart* bodyPart = it->get();
                if(Text* text = dynamic_cast<Text*>(bodyPart)) {
                    assgen.print(text->getContent());
                } else if(Newline* text = dynamic_cast<Newline*>(bodyPart)) {
                    assgen.newline();
                } else if (Variable * variable = dynamic_cast<Variable *>(bodyPart)) {
                    assgen.variable(variable->getName());
                } else if (Macro * macro = dynamic_cast<Macro *>(bodyPart)) {
                    LOG_DEBUG << "Found a Macro";
                    if (IfMacro * ifmacro = dynamic_cast<IfMacro *>(macro)) {
                        //depth++,
                        //set label for jump to after if block (else, else if, endif)
                        //evaluate expression
                        //make jump to set label if expression is true
                        ifDepth++;
                        nextJumpLabels[ifDepth] = IF_JUMP_LABEL + std::to_string(++ifJumpLabelID);
                        endJumpLabels[ifDepth] = IF_JUMP_END_LABEL + std::to_string(++ifEndJumpLabelID);
                        //TODO: evaluate expression IfMacro
                        std::string ifExprVal = ifmacro->getExpression()->to_string();
                        LOG_DEBUG << ifExprVal;
                        if(ifExprVal.compare("Const: 1")) {
                            assgen.push("1");
                        } else if (ifExprVal.compare("Const: 0")) {
                            assgen.push("0");
                        }
                        assgen.jumpNotEquals(ZAssemblyGenerator::makeArgs({"sp", "0"}) , nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 1;
                    } else if (ElseIfMacro * elseifmacro = dynamic_cast<ElseIfMacro *>(bodyPart)) {
                        //save label for jump to after if/else if block , in this case else
                        //make jump to set label if expression is true
                        //set label for jump to after if block (else, else if, endif)
                        //evaluate expression
                        //make jump to set label if expression is true
                        //else part
                        assgen.jump(endJumpLabels[ifDepth]);
                        assgen.addLabel(nextJumpLabels[ifDepth]);
                        nextJumpLabels[ifDepth] = IF_JUMP_LABEL + std::to_string(++ifJumpLabelID);
                        //TODO: evaluate expression ElseIfMacro
                        std::string ifExprVal = elseifmacro->getExpression()->to_string();
                        LOG_DEBUG << ifExprVal;
                        if(ifExprVal.compare("Const: 1")) {
                            assgen.push("1");
                        } else if (ifExprVal.compare("Const: 0")) {
                            assgen.push("0");
                        }
                        assgen.jumpNotEquals(ZAssemblyGenerator::makeArgs({"sp", "0"}) , nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 2;
                    } else if (ElseMacro * elsemacro = dynamic_cast<ElseMacro *>(bodyPart)) {
                        //save label for jump to after if/else if block , in this case else
                        //make jump to set label if expression is true
                        assgen.jump(endJumpLabels[ifDepth]);
                        assgen.addLabel(nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 3;
                    } else if (EndIfMacro * endifemacro = dynamic_cast<EndIfMacro *>(bodyPart)) {
                        //make jump to set label if expression is trueJumpLabels[ifDepth];
                        //decrease depth
                        assgen.addLabel(endJumpLabels[ifDepth]);
                        ifDepth--;
                        precedingIfMacros[ifDepth] = 0;
                    }
                }
            }

            assgen.newline();

            vector<Link*> links;
            // get links from passage
            for(auto it = bodyParts.begin(); it != bodyParts.end(); ++it)
            {
                BodyPart* bodyPart = it->get();
                if(Link* link = dynamic_cast<Link*>(bodyPart)) {
                    links.push_back(link);
                }
            }

            // present choices to user
            assgen.println("Select one of the following options");
            int i = 1;
            for (auto link = links.begin(); link != links.end(); link++) {
                assgen.println(string("    ") + to_string(i) + string(") ") + (*link)->getTarget() );
                i++;
            }

            assgen.addLabel(READ_BEGIN);

            // read user input
            assgen.read_char(USER_INPUT);

            // jump to according link selection
            i = 0;
            for (auto link = links.begin(); link != links.end(); link++) {
                string label = string("L") + to_string(i);
                assgen.jumpEquals(ZAssemblyGenerator::makeArgs({to_string(ZSCII_NUM_OFFSET + i), USER_INPUT}), label);

                i++;
            }

            // no proper selection was made
            assgen.jump(READ_BEGIN);

            i = 0;
            for (auto link = links.begin(); link != links.end(); link++) {
                string label = string("L") + to_string(i);
                try {
                    int targetPassageId = passageName2id.at((*link)->getTarget());
                    assgen.addLabel(label);
                    #ifdef ZAS_DEBUG
                    assgen.print(string("selected ") + to_string(targetPassageId) );
                    #endif
                    assgen.ret(to_string(targetPassageId));
                } catch (const out_of_range &err) {
                    cerr << "could not find passage for link target \"" << (*link)->getTarget() << "\"" << endl;
                    throw TweeDocumentException();
                }
                i++;
            }
        }
    }
}
<commit_msg>if macros error handling<commit_after>//
// Created by Danni on 09.06.15.
//

#include "TweeCompiler.h"
#include "ZAssemblyGenerator.h"
#include "exceptions.h"
#include <sstream>
#include <iostream>
#include <Passage/Body/Link.h>
#include <Passage/Body/Text.h>
#include <Passage/Body/Newline.h>
#include <Passage/Body/Expressions/Variable.h>
#include <Passage/Body/Macros/IfMacro.h>
#include <Passage/Body/Macros/ElseIfMacro.h>
#include <Passage/Body/Macros/ElseMacro.h>
#include <Passage/Body/Macros/EndIfMacro.h>
#include <plog/Log.h>

using namespace std;

static const string PASSAGE_GLOB = "PASSAGE_PTR",
        JUMP_TABLE_LABEL = "JUMP_TABLE_START",
        JUMP_TABLE_END_LABEL = "JUMP_TABLE_END",
        MAIN_ROUTINE = "main",
        USER_INPUT = "USER_INPUT",
        READ_BEGIN = "READ_BEGIN",
        IF_JUMP_LABEL = "IFJUMP",
        IF_JUMP_END_LABEL = "IFJUMPEND";

static const unsigned int ZSCII_NUM_OFFSET = 49;

//#define ZAS_DEBUG

void maskString(std::string& string) {
    std::replace( string.begin(), string.end(), ' ', '_');
}

string routineNameForPassageName(std::string passageName) {
    stringstream ss;
    maskString(passageName);
    ss << "R_" << passageName;
    return ss.str();
}

string routineNameForPassage(Passage& passage) {
    return routineNameForPassageName(passage.getHead().getName());
}

string labelForPassage(Passage& passage) {
    stringstream ss;
    string passageName = passage.getHead().getName();
    maskString(passageName);
    ss << "L_" << passageName;
    return ss.str();
}

void TweeCompiler::compile(TweeFile &tweeFile, std::ostream &out) {
    ZAssemblyGenerator assgen(out);
    vector<Passage> passages = tweeFile.getPassages();

    int ifDepth = -1;
    int ifJumpLabelID = 0;
    int ifEndJumpLabelID = 0;
    int possibleIfDepth = 255;
    std::array<std::string,255> nextJumpLabels;
    std::array<std::string,255> endJumpLabels;
    std::array<int,255> precedingIfMacros;
    for(auto precedingIfMacro = precedingIfMacros.begin(); precedingIfMacro != precedingIfMacros.end(); ++precedingIfMacro) {
        LOG_DEBUG << precedingIfMacro;
    }
    {
        int i = 0;
        for (auto passage = passages.begin(); passage != passages.end(); ++passage) {
            passageName2id[passage->getHead().getName()] = i;
            i++;
        }
    }

    // main routine
    {
        // globals
        assgen.addGlobal(PASSAGE_GLOB)
                .addGlobal(USER_INPUT);

        // call start routine first
        assgen.addRoutine(MAIN_ROUTINE)
                .markStart()
                .call(routineNameForPassageName("start"), PASSAGE_GLOB)
                .addLabel(JUMP_TABLE_LABEL);

        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            int passageId = passageName2id.at(passage->getHead().getName());

            assgen.jumpEquals(ZAssemblyGenerator::makeArgs({std::to_string(passageId), PASSAGE_GLOB}), labelForPassage(*passage));
        }

        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            assgen.addLabel(labelForPassage(*passage))
                    .call(routineNameForPassage(*passage), PASSAGE_GLOB)
                    .jump(JUMP_TABLE_LABEL);
        }

        assgen.addLabel(JUMP_TABLE_END_LABEL);

        assgen.quit();
    }

    // passage routines
    {
        for(auto passage = passages.begin(); passage != passages.end(); ++passage) {
            const vector<unique_ptr<BodyPart>>& bodyParts = passage->getBody().getBodyParts();

            // declare passage routine
            assgen.addRoutine(routineNameForPassage(*passage));

            assgen.println(string("***** ") + passage->getHead().getName() + string(" *****"));

            //  print passage contents
            for(auto it = bodyParts.begin(); it != bodyParts.end(); it++) {
                BodyPart* bodyPart = it->get();
                if(Text* text = dynamic_cast<Text*>(bodyPart)) {
                    assgen.print(text->getContent());
                } else if(Newline* text = dynamic_cast<Newline*>(bodyPart)) {
                    assgen.newline();
                } else if (Variable * variable = dynamic_cast<Variable *>(bodyPart)) {
                    assgen.variable(variable->getName());
                } else if (Macro * macro = dynamic_cast<Macro *>(bodyPart)) {
                    LOG_DEBUG << "Found a Macro";
                    if (IfMacro * ifmacro = dynamic_cast<IfMacro *>(macro)) {
                        //depth++,
                        //check preceding ifmacro
                        //set label for jump to after if block (else, else if, endif)
                        //evaluate expression
                        //make jump to set label if expression is true
                        ifDepth++;
                        if (precedingIfMacros[ifDepth] != 0) {
                            std::string errorMessage = "unexpected IfMacro after ";

                            switch(precedingIfMacros[ifDepth]) {
                                case 1:
                                    errorMessage += "another if IfMacro: ";
                                    errorMessage += "check your code for an <<[ ]*if.*>> not followed by an <<endif>> before another <<[ ]*if.*>>";
                                    break;
                                case 2:
                                    errorMessage += "an ElseIfMacro: ";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*if.*>> not followed by an <<endif>> before another <<[ ]*if.*>>";
                                    break;
                                case 3:
                                    errorMessage += "an ElseIfMacro: ";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*>> not followed by an <<endif>> before another <<[ ]*if.*>>";
                                    break;
                            }
                            cerr << errorMessage;
                            throw TweeDocumentException();
                        }
                        nextJumpLabels[ifDepth] = IF_JUMP_LABEL + std::to_string(++ifJumpLabelID);
                        endJumpLabels[ifDepth] = IF_JUMP_END_LABEL + std::to_string(++ifEndJumpLabelID);
                        //TODO: evaluate expression IfMacro
                        std::string ifExprVal = ifmacro->getExpression()->to_string();
                        LOG_DEBUG << ifExprVal;
                        if(ifExprVal.compare("Const: 1")) {
                            assgen.push("1");
                        } else if (ifExprVal.compare("Const: 0")) {
                            assgen.push("0");
                        }
                        assgen.jumpNotEquals(ZAssemblyGenerator::makeArgs({"sp", "0"}) , nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 1;
                    } else if (ElseIfMacro * elseifmacro = dynamic_cast<ElseIfMacro *>(bodyPart)) {
                        //check preceding ifmacro
                        //save label for jump to after if/else if block , in this case else
                        //make jump to set label if expression is true
                        //set label for jump to after if block (else, else if, endif)
                        //evaluate expression
                        //make jump to set label if expression is true
                        //else part
                        if (precedingIfMacros[ifDepth] != 1 || precedingIfMacros[ifDepth] != 2) {
                            std::string errorMessage = "unexpected ElseIfMacro ";

                            switch(precedingIfMacros[ifDepth]) {
                                case 0:
                                    errorMessage += "without a starting ifMacro: ";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*if.*>> without an associated <<if.*>>";
                                    break;
                                case 3:
                                    errorMessage += "after an ElseMacro";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*if.*>> after an <<[ ]*else[ ]*>>";
                                    break;
                            }
                            cerr << errorMessage;
                            throw TweeDocumentException();
                        }
                        assgen.jump(endJumpLabels[ifDepth]);
                        assgen.addLabel(nextJumpLabels[ifDepth]);
                        nextJumpLabels[ifDepth] = IF_JUMP_LABEL + std::to_string(++ifJumpLabelID);
                        //TODO: evaluate expression ElseIfMacro
                        std::string ifExprVal = elseifmacro->getExpression()->to_string();
                        LOG_DEBUG << ifExprVal;
                        if(ifExprVal.compare("Const: 1")) {
                            assgen.push("1");
                        } else if (ifExprVal.compare("Const: 0")) {
                            assgen.push("0");
                        }
                        assgen.jumpNotEquals(ZAssemblyGenerator::makeArgs({"sp", "0"}) , nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 2;
                    } else if (ElseMacro * elsemacro = dynamic_cast<ElseMacro *>(bodyPart)) {
                        //check preceding ifmacro
                        //save label for jump to after if/else if block , in this case else
                        //make jump to set label if expression is true
                        if (precedingIfMacros[ifDepth] != 1 || precedingIfMacros[ifDepth] != 2) {
                            std::string errorMessage = "unexpected ElseMacro ";

                            switch(precedingIfMacros[ifDepth]) {
                                case 0:
                                    errorMessage += "without a starting ifMacro: ";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*>> without an associated <<if.*>>";
                                    break;
                                case 3:
                                    errorMessage += "after an ElseMacro";
                                    errorMessage += "check your code for an <<[ ]*else[ ]*>> after an <<[ ]*else[ ]*>>";
                                    break;
                            }
                            cerr << errorMessage;
                            throw TweeDocumentException();
                        }
                        assgen.jump(endJumpLabels[ifDepth]);
                        assgen.addLabel(nextJumpLabels[ifDepth]);
                        precedingIfMacros[ifDepth] = 3;
                    } else if (EndIfMacro * endifemacro = dynamic_cast<EndIfMacro *>(bodyPart)) {
                        //check preceding ifmacro
                        //make jump to set label if expression is trueJumpLabels[ifDepth];
                        //decrease depth
                        if (precedingIfMacros[ifDepth] == 0) {
                            std::string errorMessage = "unexpected EndIfMacro without an if macro, look for an orphaned <<[ ]*endif[ ]*>> ";
                            cerr << errorMessage;
                            throw TweeDocumentException();
                        }
                        assgen.addLabel(endJumpLabels[ifDepth]);
                        ifDepth--;
                        precedingIfMacros[ifDepth] = 0;
                    }
                }
            }

            assgen.newline();

            vector<Link*> links;
            // get links from passage
            for(auto it = bodyParts.begin(); it != bodyParts.end(); ++it)
            {
                BodyPart* bodyPart = it->get();
                if(Link* link = dynamic_cast<Link*>(bodyPart)) {
                    links.push_back(link);
                }
            }

            // present choices to user
            assgen.println("Select one of the following options");
            int i = 1;
            for (auto link = links.begin(); link != links.end(); link++) {
                assgen.println(string("    ") + to_string(i) + string(") ") + (*link)->getTarget() );
                i++;
            }

            assgen.addLabel(READ_BEGIN);

            // read user input
            assgen.read_char(USER_INPUT);

            // jump to according link selection
            i = 0;
            for (auto link = links.begin(); link != links.end(); link++) {
                string label = string("L") + to_string(i);
                assgen.jumpEquals(ZAssemblyGenerator::makeArgs({to_string(ZSCII_NUM_OFFSET + i), USER_INPUT}), label);

                i++;
            }

            // no proper selection was made
            assgen.jump(READ_BEGIN);

            i = 0;
            for (auto link = links.begin(); link != links.end(); link++) {
                string label = string("L") + to_string(i);
                try {
                    int targetPassageId = passageName2id.at((*link)->getTarget());
                    assgen.addLabel(label);
                    #ifdef ZAS_DEBUG
                    assgen.print(string("selected ") + to_string(targetPassageId) );
                    #endif
                    assgen.ret(to_string(targetPassageId));
                } catch (const out_of_range &err) {
                    cerr << "could not find passage for link target \"" << (*link)->getTarget() << "\"" << endl;
                    throw TweeDocumentException();
                }
                i++;
            }
        }
    }
}
<|endoftext|>