<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/autocomplete/autocomplete_popup_model.h"

#include "base/string_util.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
#include "chrome/browser/autocomplete/autocomplete_popup_view.h"
#include "chrome/browser/net/dns_global.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/common/notification_service.h"
#include "third_party/icu38/public/common/unicode/ubidi.h"

AutocompletePopupModel::AutocompletePopupModel(
    AutocompletePopupView* popup_view,
    AutocompleteEditModel* edit_model,
    Profile* profile)
    : view_(popup_view),
      edit_model_(edit_model),
      controller_(new AutocompleteController(profile)),
      profile_(profile),
      hovered_line_(kNoMatch),
      selected_line_(kNoMatch),
      inside_synchronous_query_(false) {
  registrar_.Add(
      this,
      NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
      Source<AutocompleteController>(controller_.get()));
  registrar_.Add(
      this,
      NotificationType::AUTOCOMPLETE_CONTROLLER_SYNCHRONOUS_MATCHES_AVAILABLE,
      Source<AutocompleteController>(controller_.get()));
}

AutocompletePopupModel::~AutocompletePopupModel() {
  StopAutocomplete();
}

void AutocompletePopupModel::SetProfile(Profile* profile) {
  DCHECK(profile);
  profile_ = profile;
  controller_->SetProfile(profile);
}

void AutocompletePopupModel::StartAutocomplete(
    const std::wstring& text,
    const std::wstring& desired_tld,
    bool prevent_inline_autocomplete,
    bool prefer_keyword) {
  // The user is interacting with the edit, so stop tracking hover.
  SetHoveredLine(kNoMatch);

  manually_selected_match_.Clear();

  controller_->Start(text, desired_tld, prevent_inline_autocomplete,
                     prefer_keyword, false);
}

void AutocompletePopupModel::StopAutocomplete() {
  controller_->Stop(true);
  SetHoveredLine(kNoMatch);
  selected_line_ = kNoMatch;
  view_->UpdatePopupAppearance();
}

bool AutocompletePopupModel::IsOpen() const {
  return view_->IsOpen();
}

void AutocompletePopupModel::SetHoveredLine(size_t line) {
  const bool is_disabling = (line == kNoMatch);
  DCHECK(is_disabling || (line < controller_->result().size()));

  if (line == hovered_line_)
    return;  // Nothing to do

  // Make sure the old hovered line is redrawn.  No need to redraw the selected
  // line since selection overrides hover so the appearance won't change.
  const bool is_enabling = (hovered_line_ == kNoMatch);
  if (!is_enabling && (hovered_line_ != selected_line_))
    view_->InvalidateLine(hovered_line_);

  // Change the hover to the new line and make sure it's redrawn.
  hovered_line_ = line;
  if (!is_disabling && (hovered_line_ != selected_line_))
    view_->InvalidateLine(hovered_line_);

  if (is_enabling || is_disabling)
    view_->OnHoverEnabledOrDisabled(is_disabling);
}

void AutocompletePopupModel::SetSelectedLine(size_t line,
                                             bool reset_to_default) {
  const AutocompleteResult& result = controller_->result();
  CHECK(line < result.size());
  if (result.empty())
    return;

  // Cancel the query so the matches don't change on the user.
  controller_->Stop(false);

  const AutocompleteMatch& match = result.match_at(line);
  if (reset_to_default) {
    manually_selected_match_.Clear();
  } else {
    // Track the user's selection until they cancel it.
    manually_selected_match_.destination_url = match.destination_url;
    manually_selected_match_.provider_affinity = match.provider;
    manually_selected_match_.is_history_what_you_typed_match =
        match.is_history_what_you_typed_match;
  }

  if (line == selected_line_)
    return;  // Nothing else to do.

  // Update the edit with the new data for this match.
  std::wstring keyword;
  const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
  edit_model_->OnPopupDataChanged(
      reset_to_default ? std::wstring() : match.fill_into_edit,
      !reset_to_default, keyword, is_keyword_hint, match.type);

  // Repaint old and new selected lines immediately, so that the edit doesn't
  // appear to update [much] faster than the popup.  We must not update
  // |selected_line_| before calling OnPopupDataChanged() (since the edit may
  // call us back to get data about the old selection), and we must not call
  // UpdateWindow() before updating |selected_line_| (since the paint routine
  // relies on knowing the correct selected line).
  //
  // NOTE: We should never reach here with no selected line; the same code that
  // opened the popup and made it possible to get here should have also set a
  // selected line.
  CHECK(selected_line_ != kNoMatch);
  view_->InvalidateLine(selected_line_);
  selected_line_ = line;
  view_->InvalidateLine(selected_line_);
  view_->PaintUpdatesNow();
}

void AutocompletePopupModel::ResetToDefaultMatch() {
  const AutocompleteResult& result = controller_->result();
  CHECK(!result.empty());
  SetSelectedLine(result.default_match() - result.begin(), true);
}

GURL AutocompletePopupModel::URLsForCurrentSelection(
    PageTransition::Type* transition,
    bool* is_history_what_you_typed_match,
    GURL* alternate_nav_url) const {
  CHECK(IsOpen());
  const AutocompleteResult* result;
  AutocompleteResult::const_iterator match;
  if (!controller_->done()) {
    result = &controller_->latest_result();
    // It's technically possible for |result| to be empty if no provider returns
    // a synchronous result but the query has not completed synchronously;
    // pratically, however, that should never actually happen.
    if (result->empty())
      return GURL();
    // The user cannot have manually selected a match, or the query would have
    // stopped.  So the default match must be the desired selection.
    match = result->default_match();
  } else {
    // The query isn't running, so the standard result set can't possibly be out
    // of date.
    //
    // NOTE: In practice, it should actually be safe to use
    // controller_->latest_result() here too, since the controller keeps that
    // up-to-date.  However we generally try to avoid referring to that.
    result = &controller_->result();
    // If there are no results, the popup should be closed (so we should have
    // failed the CHECK above), and URLsForDefaultMatch() should have been
    // called instead.
    CHECK(!result->empty());
    CHECK(selected_line_ < result->size());
    match = result->begin() + selected_line_;
  }
  if (transition)
    *transition = match->transition;
  if (is_history_what_you_typed_match)
    *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
  if (alternate_nav_url && manually_selected_match_.empty())
    *alternate_nav_url = result->alternate_nav_url();
  return match->destination_url;
}

GURL AutocompletePopupModel::URLsForDefaultMatch(
    const std::wstring& text,
    const std::wstring& desired_tld,
    PageTransition::Type* transition,
    bool* is_history_what_you_typed_match,
    GURL* alternate_nav_url) {
  // We had better not already be doing anything, or this call will blow it
  // away.
  CHECK(!IsOpen());
  CHECK(controller_->done());

  // Run the new query and get only the synchronously available matches.
  inside_synchronous_query_ = true;  // Tell Observe() not to notify the edit or
                                     // update our appearance.
  controller_->Start(text, desired_tld, true, false, true);
  inside_synchronous_query_ = false;
  CHECK(controller_->done());

  const AutocompleteResult& result = controller_->result();
  GURL destination_url;
  if (!result.empty()) {
    // Get the URLs for the default match.
    const AutocompleteResult::const_iterator match = result.default_match();
    if (transition)
      *transition = match->transition;
    if (is_history_what_you_typed_match)
      *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
    if (alternate_nav_url)
      *alternate_nav_url = result.alternate_nav_url();
    destination_url = match->destination_url;
  }

  controller_->Stop(true);  // Keeps our state consistent.
  return destination_url;
}

bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match,
                                                std::wstring* keyword) const {
  // Assume we have no keyword until we find otherwise.
  keyword->clear();

  // If the current match is a keyword, return that as the selected keyword.
  if (TemplateURL::SupportsReplacement(match.template_url)) {
    keyword->assign(match.template_url->keyword());
    return false;
  }

  // See if the current match's fill_into_edit corresponds to a keyword.
  if (!profile_->GetTemplateURLModel())
    return false;
  profile_->GetTemplateURLModel()->Load();
  const std::wstring keyword_hint(
      TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit));
  if (keyword_hint.empty())
    return false;

  // Don't provide a hint if this keyword doesn't support replacement.
  const TemplateURL* const template_url =
      profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint);
  if (!TemplateURL::SupportsReplacement(template_url))
    return false;

  keyword->assign(keyword_hint);
  return true;
}

AutocompleteLog* AutocompletePopupModel::GetAutocompleteLog() {
  return new AutocompleteLog(controller_->input().text(),
      controller_->input().type(), selected_line_, 0, controller_->result());
}

void AutocompletePopupModel::Move(int count) {
  const AutocompleteResult& result = controller_->result();
  if (result.empty())
    return;

  // The user is using the keyboard to change the selection, so stop tracking
  // hover.
  SetHoveredLine(kNoMatch);

  // Clamp the new line to [0, result_.count() - 1].
  const size_t new_line = selected_line_ + count;
  SetSelectedLine((((count < 0) && (new_line >= selected_line_)) ?
      0 : std::min(new_line, result.size() - 1)), false);
}

void AutocompletePopupModel::TryDeletingCurrentItem() {
  // We could use URLsForCurrentSelection() here, but it seems better to try
  // and shift-delete the actual selection, rather than any "in progress, not
  // yet visible" one.
  if (selected_line_ == kNoMatch)
    return;
  const AutocompleteMatch& match =
      controller_->result().match_at(selected_line_);
  if (match.deletable) {
    const size_t selected_line = selected_line_;
    controller_->DeleteMatch(match);  // This will synchronously notify us that
                                      // the results have changed.
    const AutocompleteResult& result = controller_->result();
    if (!result.empty()) {
      // Move the selection to the next choice after the deleted one.
      // TODO(pkasting): Eventually the controller should take care of this
      // before notifying us, reducing flicker.  At that point the check for
      // deletability can move there too.
      SetSelectedLine(std::min(result.size() - 1, selected_line), false);
    }
  }
}

void AutocompletePopupModel::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  if (inside_synchronous_query_)
    return;

  const AutocompleteResult* result =
      Details<const AutocompleteResult>(details).ptr();
  switch (type.value) {
    case NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED: {
      selected_line_ = (result->default_match() == result->end()) ?
          kNoMatch : (result->default_match() - result->begin());
      // There had better not be a nonempty result set with no default match.
      CHECK((selected_line_ != kNoMatch) || result->empty());
      // If we're going to trim the window size to no longer include the hovered
      // line, turn hover off.  Practically, this shouldn't happen, but it
      // doesn't hurt to be defensive.
      if ((hovered_line_ != kNoMatch) && (result->size() <= hovered_line_))
        SetHoveredLine(kNoMatch);

      view_->UpdatePopupAppearance();
    }
    // FALL THROUGH

    case NotificationType::
             AUTOCOMPLETE_CONTROLLER_SYNCHRONOUS_MATCHES_AVAILABLE: {
      // Update the edit with the possibly new data for this match.
      // NOTE: This must be done after the code above, so that our internal
      // state will be consistent when the edit calls back to
      // URLsForCurrentSelection().
      std::wstring inline_autocomplete_text;
      std::wstring keyword;
      bool is_keyword_hint = false;
      AutocompleteMatch::Type type = AutocompleteMatch::SEARCH_WHAT_YOU_TYPED;
      const AutocompleteResult::const_iterator match(result->default_match());
      if (match != result->end()) {
        if ((match->inline_autocomplete_offset != std::wstring::npos) &&
            (match->inline_autocomplete_offset <
                match->fill_into_edit.length())) {
          inline_autocomplete_text =
              match->fill_into_edit.substr(match->inline_autocomplete_offset);
        }
        // Warm up DNS Prefetch Cache.
        chrome_browser_net::DnsPrefetchUrl(match->destination_url);
        // We could prefetch the alternate nav URL, if any, but because there
        // can be many of these as a user types an initial series of characters,
        // the OS DNS cache could suffer eviction problems for minimal gain.

        is_keyword_hint = GetKeywordForMatch(*match, &keyword);
        type = match->type;
      }
      edit_model_->OnPopupDataChanged(inline_autocomplete_text, false, keyword,
          is_keyword_hint, type);
      return;
    }

    default:
      NOTREACHED();
  }
}
<commit_msg>I was too aggressive when placing this check.  With rapid typing and deletion it's perfectly reasonable for the controller to be running a query when the popup is not open.<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/autocomplete/autocomplete_popup_model.h"

#include "base/string_util.h"
#include "chrome/browser/autocomplete/autocomplete_edit.h"
#include "chrome/browser/autocomplete/autocomplete_popup_view.h"
#include "chrome/browser/net/dns_global.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/common/notification_service.h"
#include "third_party/icu38/public/common/unicode/ubidi.h"

AutocompletePopupModel::AutocompletePopupModel(
    AutocompletePopupView* popup_view,
    AutocompleteEditModel* edit_model,
    Profile* profile)
    : view_(popup_view),
      edit_model_(edit_model),
      controller_(new AutocompleteController(profile)),
      profile_(profile),
      hovered_line_(kNoMatch),
      selected_line_(kNoMatch),
      inside_synchronous_query_(false) {
  registrar_.Add(
      this,
      NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
      Source<AutocompleteController>(controller_.get()));
  registrar_.Add(
      this,
      NotificationType::AUTOCOMPLETE_CONTROLLER_SYNCHRONOUS_MATCHES_AVAILABLE,
      Source<AutocompleteController>(controller_.get()));
}

AutocompletePopupModel::~AutocompletePopupModel() {
  StopAutocomplete();
}

void AutocompletePopupModel::SetProfile(Profile* profile) {
  DCHECK(profile);
  profile_ = profile;
  controller_->SetProfile(profile);
}

void AutocompletePopupModel::StartAutocomplete(
    const std::wstring& text,
    const std::wstring& desired_tld,
    bool prevent_inline_autocomplete,
    bool prefer_keyword) {
  // The user is interacting with the edit, so stop tracking hover.
  SetHoveredLine(kNoMatch);

  manually_selected_match_.Clear();

  controller_->Start(text, desired_tld, prevent_inline_autocomplete,
                     prefer_keyword, false);
}

void AutocompletePopupModel::StopAutocomplete() {
  controller_->Stop(true);
  SetHoveredLine(kNoMatch);
  selected_line_ = kNoMatch;
  view_->UpdatePopupAppearance();
}

bool AutocompletePopupModel::IsOpen() const {
  return view_->IsOpen();
}

void AutocompletePopupModel::SetHoveredLine(size_t line) {
  const bool is_disabling = (line == kNoMatch);
  DCHECK(is_disabling || (line < controller_->result().size()));

  if (line == hovered_line_)
    return;  // Nothing to do

  // Make sure the old hovered line is redrawn.  No need to redraw the selected
  // line since selection overrides hover so the appearance won't change.
  const bool is_enabling = (hovered_line_ == kNoMatch);
  if (!is_enabling && (hovered_line_ != selected_line_))
    view_->InvalidateLine(hovered_line_);

  // Change the hover to the new line and make sure it's redrawn.
  hovered_line_ = line;
  if (!is_disabling && (hovered_line_ != selected_line_))
    view_->InvalidateLine(hovered_line_);

  if (is_enabling || is_disabling)
    view_->OnHoverEnabledOrDisabled(is_disabling);
}

void AutocompletePopupModel::SetSelectedLine(size_t line,
                                             bool reset_to_default) {
  const AutocompleteResult& result = controller_->result();
  CHECK(line < result.size());
  if (result.empty())
    return;

  // Cancel the query so the matches don't change on the user.
  controller_->Stop(false);

  const AutocompleteMatch& match = result.match_at(line);
  if (reset_to_default) {
    manually_selected_match_.Clear();
  } else {
    // Track the user's selection until they cancel it.
    manually_selected_match_.destination_url = match.destination_url;
    manually_selected_match_.provider_affinity = match.provider;
    manually_selected_match_.is_history_what_you_typed_match =
        match.is_history_what_you_typed_match;
  }

  if (line == selected_line_)
    return;  // Nothing else to do.

  // Update the edit with the new data for this match.
  std::wstring keyword;
  const bool is_keyword_hint = GetKeywordForMatch(match, &keyword);
  edit_model_->OnPopupDataChanged(
      reset_to_default ? std::wstring() : match.fill_into_edit,
      !reset_to_default, keyword, is_keyword_hint, match.type);

  // Repaint old and new selected lines immediately, so that the edit doesn't
  // appear to update [much] faster than the popup.  We must not update
  // |selected_line_| before calling OnPopupDataChanged() (since the edit may
  // call us back to get data about the old selection), and we must not call
  // UpdateWindow() before updating |selected_line_| (since the paint routine
  // relies on knowing the correct selected line).
  //
  // NOTE: We should never reach here with no selected line; the same code that
  // opened the popup and made it possible to get here should have also set a
  // selected line.
  CHECK(selected_line_ != kNoMatch);
  view_->InvalidateLine(selected_line_);
  selected_line_ = line;
  view_->InvalidateLine(selected_line_);
  view_->PaintUpdatesNow();
}

void AutocompletePopupModel::ResetToDefaultMatch() {
  const AutocompleteResult& result = controller_->result();
  CHECK(!result.empty());
  SetSelectedLine(result.default_match() - result.begin(), true);
}

GURL AutocompletePopupModel::URLsForCurrentSelection(
    PageTransition::Type* transition,
    bool* is_history_what_you_typed_match,
    GURL* alternate_nav_url) const {
  const AutocompleteResult* result;
  AutocompleteResult::const_iterator match;
  if (!controller_->done()) {
    result = &controller_->latest_result();
    // It's technically possible for |result| to be empty if no provider returns
    // a synchronous result but the query has not completed synchronously;
    // pratically, however, that should never actually happen.
    if (result->empty())
      return GURL();
    // The user cannot have manually selected a match, or the query would have
    // stopped.  So the default match must be the desired selection.
    match = result->default_match();
  } else {
    CHECK(IsOpen());
    // The query isn't running, so the standard result set can't possibly be out
    // of date.
    //
    // NOTE: In practice, it should actually be safe to use
    // controller_->latest_result() here too, since the controller keeps that
    // up-to-date.  However we generally try to avoid referring to that.
    result = &controller_->result();
    // If there are no results, the popup should be closed (so we should have
    // failed the CHECK above), and URLsForDefaultMatch() should have been
    // called instead.
    CHECK(!result->empty());
    CHECK(selected_line_ < result->size());
    match = result->begin() + selected_line_;
  }
  if (transition)
    *transition = match->transition;
  if (is_history_what_you_typed_match)
    *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
  if (alternate_nav_url && manually_selected_match_.empty())
    *alternate_nav_url = result->alternate_nav_url();
  return match->destination_url;
}

GURL AutocompletePopupModel::URLsForDefaultMatch(
    const std::wstring& text,
    const std::wstring& desired_tld,
    PageTransition::Type* transition,
    bool* is_history_what_you_typed_match,
    GURL* alternate_nav_url) {
  // We had better not already be doing anything, or this call will blow it
  // away.
  CHECK(!IsOpen());
  CHECK(controller_->done());

  // Run the new query and get only the synchronously available matches.
  inside_synchronous_query_ = true;  // Tell Observe() not to notify the edit or
                                     // update our appearance.
  controller_->Start(text, desired_tld, true, false, true);
  inside_synchronous_query_ = false;
  CHECK(controller_->done());

  const AutocompleteResult& result = controller_->result();
  GURL destination_url;
  if (!result.empty()) {
    // Get the URLs for the default match.
    const AutocompleteResult::const_iterator match = result.default_match();
    if (transition)
      *transition = match->transition;
    if (is_history_what_you_typed_match)
      *is_history_what_you_typed_match = match->is_history_what_you_typed_match;
    if (alternate_nav_url)
      *alternate_nav_url = result.alternate_nav_url();
    destination_url = match->destination_url;
  }

  controller_->Stop(true);  // Keeps our state consistent.
  return destination_url;
}

bool AutocompletePopupModel::GetKeywordForMatch(const AutocompleteMatch& match,
                                                std::wstring* keyword) const {
  // Assume we have no keyword until we find otherwise.
  keyword->clear();

  // If the current match is a keyword, return that as the selected keyword.
  if (TemplateURL::SupportsReplacement(match.template_url)) {
    keyword->assign(match.template_url->keyword());
    return false;
  }

  // See if the current match's fill_into_edit corresponds to a keyword.
  if (!profile_->GetTemplateURLModel())
    return false;
  profile_->GetTemplateURLModel()->Load();
  const std::wstring keyword_hint(
      TemplateURLModel::CleanUserInputKeyword(match.fill_into_edit));
  if (keyword_hint.empty())
    return false;

  // Don't provide a hint if this keyword doesn't support replacement.
  const TemplateURL* const template_url =
      profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword_hint);
  if (!TemplateURL::SupportsReplacement(template_url))
    return false;

  keyword->assign(keyword_hint);
  return true;
}

AutocompleteLog* AutocompletePopupModel::GetAutocompleteLog() {
  return new AutocompleteLog(controller_->input().text(),
      controller_->input().type(), selected_line_, 0, controller_->result());
}

void AutocompletePopupModel::Move(int count) {
  const AutocompleteResult& result = controller_->result();
  if (result.empty())
    return;

  // The user is using the keyboard to change the selection, so stop tracking
  // hover.
  SetHoveredLine(kNoMatch);

  // Clamp the new line to [0, result_.count() - 1].
  const size_t new_line = selected_line_ + count;
  SetSelectedLine((((count < 0) && (new_line >= selected_line_)) ?
      0 : std::min(new_line, result.size() - 1)), false);
}

void AutocompletePopupModel::TryDeletingCurrentItem() {
  // We could use URLsForCurrentSelection() here, but it seems better to try
  // and shift-delete the actual selection, rather than any "in progress, not
  // yet visible" one.
  if (selected_line_ == kNoMatch)
    return;
  const AutocompleteMatch& match =
      controller_->result().match_at(selected_line_);
  if (match.deletable) {
    const size_t selected_line = selected_line_;
    controller_->DeleteMatch(match);  // This will synchronously notify us that
                                      // the results have changed.
    const AutocompleteResult& result = controller_->result();
    if (!result.empty()) {
      // Move the selection to the next choice after the deleted one.
      // TODO(pkasting): Eventually the controller should take care of this
      // before notifying us, reducing flicker.  At that point the check for
      // deletability can move there too.
      SetSelectedLine(std::min(result.size() - 1, selected_line), false);
    }
  }
}

void AutocompletePopupModel::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  if (inside_synchronous_query_)
    return;

  const AutocompleteResult* result =
      Details<const AutocompleteResult>(details).ptr();
  switch (type.value) {
    case NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED: {
      selected_line_ = (result->default_match() == result->end()) ?
          kNoMatch : (result->default_match() - result->begin());
      // There had better not be a nonempty result set with no default match.
      CHECK((selected_line_ != kNoMatch) || result->empty());
      // If we're going to trim the window size to no longer include the hovered
      // line, turn hover off.  Practically, this shouldn't happen, but it
      // doesn't hurt to be defensive.
      if ((hovered_line_ != kNoMatch) && (result->size() <= hovered_line_))
        SetHoveredLine(kNoMatch);

      view_->UpdatePopupAppearance();
    }
    // FALL THROUGH

    case NotificationType::
             AUTOCOMPLETE_CONTROLLER_SYNCHRONOUS_MATCHES_AVAILABLE: {
      // Update the edit with the possibly new data for this match.
      // NOTE: This must be done after the code above, so that our internal
      // state will be consistent when the edit calls back to
      // URLsForCurrentSelection().
      std::wstring inline_autocomplete_text;
      std::wstring keyword;
      bool is_keyword_hint = false;
      AutocompleteMatch::Type type = AutocompleteMatch::SEARCH_WHAT_YOU_TYPED;
      const AutocompleteResult::const_iterator match(result->default_match());
      if (match != result->end()) {
        if ((match->inline_autocomplete_offset != std::wstring::npos) &&
            (match->inline_autocomplete_offset <
                match->fill_into_edit.length())) {
          inline_autocomplete_text =
              match->fill_into_edit.substr(match->inline_autocomplete_offset);
        }
        // Warm up DNS Prefetch Cache.
        chrome_browser_net::DnsPrefetchUrl(match->destination_url);
        // We could prefetch the alternate nav URL, if any, but because there
        // can be many of these as a user types an initial series of characters,
        // the OS DNS cache could suffer eviction problems for minimal gain.

        is_keyword_hint = GetKeywordForMatch(*match, &keyword);
        type = match->type;
      }
      edit_model_->OnPopupDataChanged(inline_autocomplete_text, false, keyword,
          is_keyword_hint, type);
      return;
    }

    default:
      NOTREACHED();
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 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/profiles/profile_shortcut_manager.h"

#include <map>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/app_icon_win.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_info_cache_observer.h"
#include "chrome/browser/profiles/profile_info_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/auto_launch_util.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
#include "content/public/browser/browser_thread.h"
#include "grit/generated_resources.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/icon_util.h"

using content::BrowserThread;

namespace {

const char kProfileIconFileName[] = "Google Profile.ico";
const int kProfileAvatarShortcutBadgeWidth = 28;
const int kProfileAvatarShortcutBadgeHeight = 28;
const int kShortcutIconSize = 48;

// Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
// badging the browser distribution icon with the profile avatar.
// Returns a path to the shortcut icon file on disk, which is empty if this
// fails. Use index 0 when assigning the resulting file as the icon.
FilePath CreateChromeDesktopShortcutIconForProfile(
    const FilePath& profile_path,
    const SkBitmap& avatar_bitmap) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize);
  scoped_ptr<SkBitmap> app_icon_bitmap(
      IconUtil::CreateSkBitmapFromHICON(app_icon_handle));
  DestroyIcon(app_icon_handle);
  if (!app_icon_bitmap.get())
    return FilePath();

  // TODO(hallielaine): Share this chunk of code with
  // avatar_menu_button::DrawTaskBarDecoration.
  const SkBitmap* source_bitmap = NULL;
  SkBitmap squarer_bitmap;
  if ((avatar_bitmap.width() == profiles::kAvatarIconWidth) &&
      (avatar_bitmap.height() == profiles::kAvatarIconHeight)) {
    // Shave a couple of columns so the bitmap is more square. So when
    // resized to a square aspect ratio it looks pretty.
    int x = 2;
    avatar_bitmap.extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
        profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
    source_bitmap = &squarer_bitmap;
  } else {
    source_bitmap = &avatar_bitmap;
  }
  SkBitmap sk_icon = skia::ImageOperations::Resize(
      *source_bitmap,
      skia::ImageOperations::RESIZE_LANCZOS3,
      kProfileAvatarShortcutBadgeWidth,
      kProfileAvatarShortcutBadgeHeight);

  // Overlay the avatar on the icon, anchoring it to the bottom-right of the
  // icon.
  scoped_ptr<SkCanvas> offscreen_canvas(
      skia::CreateBitmapCanvas(app_icon_bitmap->width(),
                               app_icon_bitmap->height(),
                               false));
  DCHECK(offscreen_canvas.get());
  offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0);
  offscreen_canvas->drawBitmap(
      sk_icon,
      app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth,
      app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight);
  const SkBitmap& final_bitmap =
      offscreen_canvas->getDevice()->accessBitmap(false);

  // Finally, write the .ico file containing this new bitmap.
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path))
    return FilePath();

  return icon_path;
}

string16 CreateProfileShortcutFlags(const FilePath& profile_path) {
    return base::StringPrintf(L"--%ls=\"%ls\"",
      ASCIIToUTF16(switches::kProfileDirectory).c_str(),
      profile_path.BaseName().value().c_str());
}

// Wrap a ShellUtil/FileUtil function that returns a bool so it can be posted
// in a task to the FILE thread.
void CallBoolFunction(
    const base::Callback<bool(void)>& bool_function) {
  bool_function.Run();
}

// Renames an existing Chrome desktop profile shortcut. Must be called on the
// FILE thread.
void RenameChromeDesktopShortcutForProfile(
    const string16& old_shortcut_file,
    const string16& new_shortcut_file) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (ShellUtil::GetDesktopPath(false,  // User's directory instead of system.
                                &shortcut_path)) {
    FilePath old_shortcut_path = shortcut_path.Append(old_shortcut_file);
    FilePath new_shortcut_path = shortcut_path.Append(new_shortcut_file);
    if (!file_util::Move(old_shortcut_path, new_shortcut_path))
       LOG(ERROR) << "Could not rename Windows profile desktop shortcut.";
  }
}

// Create or update a profile desktop shortcut. Must be called on the FILE
// thread.
void CreateOrUpdateProfileDesktopShortcut(
    const FilePath& profile_path,
    const string16& profile_name,
    const SkBitmap& avatar_image,
    bool create) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_icon =
      CreateChromeDesktopShortcutIconForProfile(profile_path, avatar_image);

  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (!dist)
    return;
  string16 description(dist->GetAppDescription());

  uint32 options = create ? ShellUtil::SHORTCUT_CREATE_ALWAYS :
                            ShellUtil::SHORTCUT_NO_OPTIONS;
  ShellUtil::CreateChromeDesktopShortcut(
      dist,
      chrome_exe.value(),
      description,
      profile_name,
      CreateProfileShortcutFlags(profile_path),
      shortcut_icon.empty() ? chrome_exe.value() : shortcut_icon.value(),
      shortcut_icon.empty() ? dist->GetIconIndex() : 0,
      ShellUtil::CURRENT_USER,
      options);
}

}  // namespace

class ProfileShortcutManagerWin : public ProfileShortcutManager,
                                  public ProfileInfoCacheObserver {
 public:
  explicit ProfileShortcutManagerWin(ProfileManager* manager);
  virtual ~ProfileShortcutManagerWin();

  virtual void CreateProfileShortcut(const FilePath& profile_path) OVERRIDE;

  virtual void OnProfileAdded(const FilePath& profile_path) OVERRIDE;
  virtual void OnProfileWillBeRemoved(const FilePath& profile_path) OVERRIDE;
  virtual void OnProfileWasRemoved(const FilePath& profile_path,
                                   const string16& profile_name) OVERRIDE;
  virtual void OnProfileNameChanged(const FilePath& profile_path,
                                    const string16& old_profile_name) OVERRIDE;
  virtual void OnProfileAvatarChanged(const FilePath& profile_path) OVERRIDE;

 private:
  void StartProfileShortcutNameChange(const FilePath& profile_path,
                                      const string16& old_profile_name);
  // Gives the profile path of an alternate profile than |profile_path|.
  // Must only be called when the number profiles is 2.
  FilePath GetOtherProfilePath(const FilePath& profile_path);
  void UpdateShortcutForProfileAtPath(const FilePath& profile_path,
                                      bool create_always);
  ProfileManager* profile_manager_;
};

// static
bool ProfileShortcutManager::IsFeatureEnabled() {
  return true;
}

// static
ProfileShortcutManager* ProfileShortcutManager::Create(
    ProfileManager* manager) {
  return new ProfileShortcutManagerWin(manager);
}

ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager)
    : profile_manager_(manager) {
    profile_manager_->GetProfileInfoCache().AddObserver(this);
}

ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
    profile_manager_->GetProfileInfoCache().RemoveObserver(this);
}

void ProfileShortcutManagerWin::CreateProfileShortcut(
    const FilePath& profile_path) {
  UpdateShortcutForProfileAtPath(profile_path, true);
}

void ProfileShortcutManagerWin::OnProfileAdded(const FilePath& profile_path) {
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 2) {
    UpdateShortcutForProfileAtPath(
        GetOtherProfilePath(profile_path), false);
  }
}

void ProfileShortcutManagerWin::OnProfileWillBeRemoved(
    const FilePath& profile_path) {
}

void ProfileShortcutManagerWin::OnProfileWasRemoved(
    const FilePath& profile_path,
    const string16& profile_name) {
  // If there is only one profile remaining, remove the badging information
  // from an existing shortcut.
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) {
    UpdateShortcutForProfileAtPath(
        profile_manager_->GetProfileInfoCache().GetPathOfProfileAtIndex(0),
        false);
  }

  string16 profile_name_updated;
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() != 0)
    profile_name_updated = profile_name;

  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 shortcut;
  // If we can find the shortcut, delete it
  if (ShellUtil::GetChromeShortcutName(dist, false,
          profile_name_updated, &shortcut)) {
    std::vector<string16> appended_names(1, shortcut);
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
            base::Bind(&CallBoolFunction, base::Bind(
                &ShellUtil::RemoveChromeDesktopShortcutsWithAppendedNames,
                appended_names)));
  }
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
            base::Bind(&CallBoolFunction, base::Bind(
                &file_util::Delete, icon_path, false)));
}

void ProfileShortcutManagerWin::OnProfileNameChanged(
    const FilePath& profile_path,
    const string16& old_profile_name) {
  UpdateShortcutForProfileAtPath(profile_path, false);
}

void ProfileShortcutManagerWin::OnProfileAvatarChanged(
    const FilePath& profile_path) {
  UpdateShortcutForProfileAtPath(profile_path, false);
}

void ProfileShortcutManagerWin::StartProfileShortcutNameChange(
    const FilePath& profile_path,
    const string16& old_profile_name) {
  size_t profile_index = profile_manager_->GetProfileInfoCache().
      GetIndexOfProfileWithPath(profile_path);
  if (profile_index == std::string::npos)
      return;
  // If the shortcut will have an appended name, get the profile name.
  string16 new_profile_name =
      (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) ?
          string16() :
          profile_manager_->GetProfileInfoCache().
              GetNameOfProfileAtIndex(profile_index);

  string16 old_shortcut_file;
  string16 new_shortcut_file;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(
          dist, false, old_profile_name, &old_shortcut_file) &&
      ShellUtil::GetChromeShortcutName(
          dist, false, new_profile_name, &new_shortcut_file)) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&RenameChromeDesktopShortcutForProfile,
                   old_shortcut_file,
                   new_shortcut_file));
  }
}

// Gets the other profile path in the cache when there are two profiles.
FilePath ProfileShortcutManagerWin::GetOtherProfilePath(
    const FilePath& profile_path) {
  DCHECK_EQ(2U, profile_manager_->GetProfileInfoCache().GetNumberOfProfiles());
  // Get the index of the current profile, in order to find the index of the
  // other profile.
  size_t current_profile_index = profile_manager_->GetProfileInfoCache().
    GetIndexOfProfileWithPath(profile_path);
  size_t other_profile_index = (current_profile_index == 0) ? 1 : 0;
  return profile_manager_->GetProfileInfoCache().
      GetPathOfProfileAtIndex(other_profile_index);
}

void ProfileShortcutManagerWin::UpdateShortcutForProfileAtPath(
    const FilePath& profile_path,
    bool create_always) {
  size_t profile_index = profile_manager_->GetProfileInfoCache().
      GetIndexOfProfileWithPath(profile_path);
  if (profile_index == std::string::npos)
      return;
  bool remove_badging = profile_manager_->GetProfileInfoCache().
      GetNumberOfProfiles() == 1;

  string16 old_shortcut_appended_name =
      profile_manager_->GetProfileInfoCache().
          GetShortcutNameOfProfileAtIndex(profile_index);

  string16 new_shortcut_appended_name;
  if (!remove_badging) {
    new_shortcut_appended_name =
        profile_manager_->GetProfileInfoCache().
            GetNameOfProfileAtIndex(profile_index);
  }

  if (!create_always &&
      new_shortcut_appended_name != old_shortcut_appended_name)
    StartProfileShortcutNameChange(profile_path, old_shortcut_appended_name);

  SkBitmap profile_avatar_bitmap_copy;
  if (!remove_badging) {
    size_t profile_icon_index = profile_manager_->GetProfileInfoCache().
      GetAvatarIconIndexOfProfileAtIndex(profile_index);
    gfx::Image profile_avatar_image = ResourceBundle::GetSharedInstance().
        GetNativeImageNamed(
            profile_manager_->GetProfileInfoCache().
                GetDefaultAvatarIconResourceIDAtIndex(profile_icon_index));

    DCHECK(!profile_avatar_image.IsEmpty());
    const SkBitmap* profile_avatar_bitmap =
        profile_avatar_image.ToSkBitmap();
    // Make a copy of the SkBitmap to ensure that we can safely use the image
    // data on the FILE thread.
    DCHECK(profile_avatar_bitmap->deepCopyTo(
        &profile_avatar_bitmap_copy,
        profile_avatar_bitmap->getConfig()));
  }
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&CreateOrUpdateProfileDesktopShortcut,
                 profile_path, new_shortcut_appended_name,
                 profile_avatar_bitmap_copy, create_always));

  profile_manager_->GetProfileInfoCache().SetShortcutNameOfProfileAtIndex(
      profile_index, new_shortcut_appended_name);
}

<commit_msg>Fixed shortcut icons missing bug for canary.<commit_after>// Copyright (c) 2012 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/profiles/profile_shortcut_manager.h"

#include <map>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/app_icon_win.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile_info_cache_observer.h"
#include "chrome/browser/profiles/profile_info_util.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/auto_launch_util.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/shell_util.h"
#include "content/public/browser/browser_thread.h"
#include "grit/generated_resources.h"
#include "skia/ext/image_operations.h"
#include "skia/ext/platform_canvas.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/icon_util.h"

using content::BrowserThread;

namespace {

const char kProfileIconFileName[] = "Google Profile.ico";
const int kProfileAvatarShortcutBadgeWidth = 28;
const int kProfileAvatarShortcutBadgeHeight = 28;
const int kShortcutIconSize = 48;

// Creates a desktop shortcut icon file (.ico) on the disk for a given profile,
// badging the browser distribution icon with the profile avatar.
// Returns a path to the shortcut icon file on disk, which is empty if this
// fails. Use index 0 when assigning the resulting file as the icon.
FilePath CreateChromeDesktopShortcutIconForProfile(
    const FilePath& profile_path,
    const SkBitmap& avatar_bitmap) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  HICON app_icon_handle = GetAppIconForSize(kShortcutIconSize);
  scoped_ptr<SkBitmap> app_icon_bitmap(
      IconUtil::CreateSkBitmapFromHICON(app_icon_handle));
  DestroyIcon(app_icon_handle);
  if (!app_icon_bitmap.get())
    return FilePath();

  // TODO(hallielaine): Share this chunk of code with
  // avatar_menu_button::DrawTaskBarDecoration.
  const SkBitmap* source_bitmap = NULL;
  SkBitmap squarer_bitmap;
  if ((avatar_bitmap.width() == profiles::kAvatarIconWidth) &&
      (avatar_bitmap.height() == profiles::kAvatarIconHeight)) {
    // Shave a couple of columns so the bitmap is more square. So when
    // resized to a square aspect ratio it looks pretty.
    int x = 2;
    avatar_bitmap.extractSubset(&squarer_bitmap, SkIRect::MakeXYWH(x, 0,
        profiles::kAvatarIconWidth - x * 2, profiles::kAvatarIconHeight));
    source_bitmap = &squarer_bitmap;
  } else {
    source_bitmap = &avatar_bitmap;
  }
  SkBitmap sk_icon = skia::ImageOperations::Resize(
      *source_bitmap,
      skia::ImageOperations::RESIZE_LANCZOS3,
      kProfileAvatarShortcutBadgeWidth,
      kProfileAvatarShortcutBadgeHeight);

  // Overlay the avatar on the icon, anchoring it to the bottom-right of the
  // icon.
  scoped_ptr<SkCanvas> offscreen_canvas(
      skia::CreateBitmapCanvas(app_icon_bitmap->width(),
                               app_icon_bitmap->height(),
                               false));
  DCHECK(offscreen_canvas.get());
  offscreen_canvas->drawBitmap(*app_icon_bitmap, 0, 0);
  offscreen_canvas->drawBitmap(
      sk_icon,
      app_icon_bitmap->width() - kProfileAvatarShortcutBadgeWidth,
      app_icon_bitmap->height() - kProfileAvatarShortcutBadgeHeight);
  const SkBitmap& final_bitmap =
      offscreen_canvas->getDevice()->accessBitmap(false);

  // Finally, write the .ico file containing this new bitmap.
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  if (!IconUtil::CreateIconFileFromSkBitmap(final_bitmap, icon_path))
    return FilePath();

  return icon_path;
}

string16 CreateProfileShortcutFlags(const FilePath& profile_path) {
    return base::StringPrintf(L"--%ls=\"%ls\"",
      ASCIIToUTF16(switches::kProfileDirectory).c_str(),
      profile_path.BaseName().value().c_str());
}

// Wrap a ShellUtil/FileUtil function that returns a bool so it can be posted
// in a task to the FILE thread.
void CallBoolFunction(
    const base::Callback<bool(void)>& bool_function) {
  bool_function.Run();
}

// Renames an existing Chrome desktop profile shortcut. Must be called on the
// FILE thread.
void RenameChromeDesktopShortcutForProfile(
    const string16& old_shortcut_file,
    const string16& new_shortcut_file) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_path;
  if (ShellUtil::GetDesktopPath(false,  // User's directory instead of system.
                                &shortcut_path)) {
    FilePath old_shortcut_path = shortcut_path.Append(old_shortcut_file);
    FilePath new_shortcut_path = shortcut_path.Append(new_shortcut_file);
    if (!file_util::Move(old_shortcut_path, new_shortcut_path))
       LOG(ERROR) << "Could not rename Windows profile desktop shortcut.";
  }
}

// Create or update a profile desktop shortcut. Must be called on the FILE
// thread.
void CreateOrUpdateProfileDesktopShortcut(
    const FilePath& profile_path,
    const string16& profile_name,
    const SkBitmap& avatar_image,
    bool create) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  FilePath shortcut_icon =
      CreateChromeDesktopShortcutIconForProfile(profile_path, avatar_image);

  FilePath chrome_exe;
  if (!PathService::Get(base::FILE_EXE, &chrome_exe))
    return;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (!dist)
    return;
  string16 description(dist->GetAppDescription());

  uint32 options = create ? ShellUtil::SHORTCUT_CREATE_ALWAYS :
                            ShellUtil::SHORTCUT_NO_OPTIONS;
  ShellUtil::CreateChromeDesktopShortcut(
      dist,
      chrome_exe.value(),
      description,
      profile_name,
      CreateProfileShortcutFlags(profile_path),
      shortcut_icon.empty() ? chrome_exe.value() : shortcut_icon.value(),
      shortcut_icon.empty() ? dist->GetIconIndex() : 0,
      ShellUtil::CURRENT_USER,
      options);
}

}  // namespace

class ProfileShortcutManagerWin : public ProfileShortcutManager,
                                  public ProfileInfoCacheObserver {
 public:
  explicit ProfileShortcutManagerWin(ProfileManager* manager);
  virtual ~ProfileShortcutManagerWin();

  virtual void CreateProfileShortcut(const FilePath& profile_path) OVERRIDE;

  virtual void OnProfileAdded(const FilePath& profile_path) OVERRIDE;
  virtual void OnProfileWillBeRemoved(const FilePath& profile_path) OVERRIDE;
  virtual void OnProfileWasRemoved(const FilePath& profile_path,
                                   const string16& profile_name) OVERRIDE;
  virtual void OnProfileNameChanged(const FilePath& profile_path,
                                    const string16& old_profile_name) OVERRIDE;
  virtual void OnProfileAvatarChanged(const FilePath& profile_path) OVERRIDE;

 private:
  void StartProfileShortcutNameChange(const FilePath& profile_path,
                                      const string16& old_profile_name);
  // Gives the profile path of an alternate profile than |profile_path|.
  // Must only be called when the number profiles is 2.
  FilePath GetOtherProfilePath(const FilePath& profile_path);
  void UpdateShortcutForProfileAtPath(const FilePath& profile_path,
                                      bool create_always);
  ProfileManager* profile_manager_;
};

// static
bool ProfileShortcutManager::IsFeatureEnabled() {
  return true;
}

// static
ProfileShortcutManager* ProfileShortcutManager::Create(
    ProfileManager* manager) {
  return new ProfileShortcutManagerWin(manager);
}

ProfileShortcutManagerWin::ProfileShortcutManagerWin(ProfileManager* manager)
    : profile_manager_(manager) {
    profile_manager_->GetProfileInfoCache().AddObserver(this);
}

ProfileShortcutManagerWin::~ProfileShortcutManagerWin() {
    profile_manager_->GetProfileInfoCache().RemoveObserver(this);
}

void ProfileShortcutManagerWin::CreateProfileShortcut(
    const FilePath& profile_path) {
  UpdateShortcutForProfileAtPath(profile_path, true);
}

void ProfileShortcutManagerWin::OnProfileAdded(const FilePath& profile_path) {
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 2) {
    UpdateShortcutForProfileAtPath(
        GetOtherProfilePath(profile_path), false);
  }
}

void ProfileShortcutManagerWin::OnProfileWillBeRemoved(
    const FilePath& profile_path) {
}

void ProfileShortcutManagerWin::OnProfileWasRemoved(
    const FilePath& profile_path,
    const string16& profile_name) {
  // If there is only one profile remaining, remove the badging information
  // from an existing shortcut.
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) {
    UpdateShortcutForProfileAtPath(
        profile_manager_->GetProfileInfoCache().GetPathOfProfileAtIndex(0),
        false);
  }

  string16 profile_name_updated;
  if (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() != 0)
    profile_name_updated = profile_name;

  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  string16 shortcut;
  // If we can find the shortcut, delete it
  if (ShellUtil::GetChromeShortcutName(dist, false,
          profile_name_updated, &shortcut)) {
    std::vector<string16> appended_names(1, shortcut);
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
            base::Bind(&CallBoolFunction, base::Bind(
                &ShellUtil::RemoveChromeDesktopShortcutsWithAppendedNames,
                appended_names)));
  }
  FilePath icon_path = profile_path.AppendASCII(kProfileIconFileName);
  BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
            base::Bind(&CallBoolFunction, base::Bind(
                &file_util::Delete, icon_path, false)));
}

void ProfileShortcutManagerWin::OnProfileNameChanged(
    const FilePath& profile_path,
    const string16& old_profile_name) {
  UpdateShortcutForProfileAtPath(profile_path, false);
}

void ProfileShortcutManagerWin::OnProfileAvatarChanged(
    const FilePath& profile_path) {
  UpdateShortcutForProfileAtPath(profile_path, false);
}

void ProfileShortcutManagerWin::StartProfileShortcutNameChange(
    const FilePath& profile_path,
    const string16& old_profile_name) {
  size_t profile_index = profile_manager_->GetProfileInfoCache().
      GetIndexOfProfileWithPath(profile_path);
  if (profile_index == std::string::npos)
      return;
  // If the shortcut will have an appended name, get the profile name.
  string16 new_profile_name =
      (profile_manager_->GetProfileInfoCache().GetNumberOfProfiles() == 1) ?
          string16() :
          profile_manager_->GetProfileInfoCache().
              GetNameOfProfileAtIndex(profile_index);

  string16 old_shortcut_file;
  string16 new_shortcut_file;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (ShellUtil::GetChromeShortcutName(
          dist, false, old_profile_name, &old_shortcut_file) &&
      ShellUtil::GetChromeShortcutName(
          dist, false, new_profile_name, &new_shortcut_file)) {
    BrowserThread::PostTask(
        BrowserThread::FILE, FROM_HERE,
        base::Bind(&RenameChromeDesktopShortcutForProfile,
                   old_shortcut_file,
                   new_shortcut_file));
  }
}

// Gets the other profile path in the cache when there are two profiles.
FilePath ProfileShortcutManagerWin::GetOtherProfilePath(
    const FilePath& profile_path) {
  DCHECK_EQ(2U, profile_manager_->GetProfileInfoCache().GetNumberOfProfiles());
  // Get the index of the current profile, in order to find the index of the
  // other profile.
  size_t current_profile_index = profile_manager_->GetProfileInfoCache().
    GetIndexOfProfileWithPath(profile_path);
  size_t other_profile_index = (current_profile_index == 0) ? 1 : 0;
  return profile_manager_->GetProfileInfoCache().
      GetPathOfProfileAtIndex(other_profile_index);
}

void ProfileShortcutManagerWin::UpdateShortcutForProfileAtPath(
    const FilePath& profile_path,
    bool create_always) {
  size_t profile_index = profile_manager_->GetProfileInfoCache().
      GetIndexOfProfileWithPath(profile_path);
  if (profile_index == std::string::npos)
      return;
  bool remove_badging = profile_manager_->GetProfileInfoCache().
      GetNumberOfProfiles() == 1;

  string16 old_shortcut_appended_name =
      profile_manager_->GetProfileInfoCache().
          GetShortcutNameOfProfileAtIndex(profile_index);

  string16 new_shortcut_appended_name;
  if (!remove_badging) {
    new_shortcut_appended_name =
        profile_manager_->GetProfileInfoCache().
            GetNameOfProfileAtIndex(profile_index);
  }

  if (!create_always &&
      new_shortcut_appended_name != old_shortcut_appended_name)
    StartProfileShortcutNameChange(profile_path, old_shortcut_appended_name);

  SkBitmap profile_avatar_bitmap_copy;
  if (!remove_badging) {
    size_t profile_icon_index = profile_manager_->GetProfileInfoCache().
      GetAvatarIconIndexOfProfileAtIndex(profile_index);
    gfx::Image profile_avatar_image = ResourceBundle::GetSharedInstance().
        GetNativeImageNamed(
            profile_manager_->GetProfileInfoCache().
                GetDefaultAvatarIconResourceIDAtIndex(profile_icon_index));

    DCHECK(!profile_avatar_image.IsEmpty());
    const SkBitmap* profile_avatar_bitmap =
        profile_avatar_image.ToSkBitmap();
    // Make a copy of the SkBitmap to ensure that we can safely use the image
    // data on the FILE thread.
    profile_avatar_bitmap->deepCopyTo(
        &profile_avatar_bitmap_copy, profile_avatar_bitmap->getConfig());
  }
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      base::Bind(&CreateOrUpdateProfileDesktopShortcut,
                 profile_path, new_shortcut_appended_name,
                 profile_avatar_bitmap_copy, create_always));

  profile_manager_->GetProfileInfoCache().SetShortcutNameOfProfileAtIndex(
      profile_index, new_shortcut_appended_name);
}

<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <string>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <fnord/buffer.h>
#include <fnord/exception.h>
#include <fnord/io/inputstream.h>

namespace fnord {

std::unique_ptr<InputStream> InputStream::getStdin() {
  auto stdin_stream = new FileInputStream(0, false);
  return std::unique_ptr<InputStream>(stdin_stream);
}

InputStream::InputStream(
    const std::string& filename /* = "<anonymous input stream>" */) :
    filename_(filename) {}

const std::string& InputStream::getFileName() const {
  return filename_;
}

void InputStream::setFileName(const std::string& filename) {
  filename_ = filename;
}

// FIXPAUL: optimize?
size_t InputStream::readNextBytes(std::string* target, size_t n_bytes) {
  char byte;
  size_t length;

  for (length = 0; length < n_bytes && readNextByte(&byte); ++length) {
    *target += byte;
  }

  return length;
}

// FIXPAUL: optimize?
size_t InputStream::readNextBytes(Buffer* target, size_t n_bytes) {
  char byte;
  size_t length;

  target->reserve(n_bytes);

  for (length = 0; length < n_bytes && readNextByte(&byte); ++length) {
    target->append(&byte, sizeof(byte));
  }

  return length;
}

size_t InputStream::readNextBytes(void* target, size_t n_bytes) {
  size_t length = 0;

  while (length < n_bytes && readNextByte(((char*) target) + length)) {
    ++length;
  }

  return length;
}

// FIXPAUL: optimize?
size_t InputStream::readUntilEOF(std::string* target) {
  char byte;
  size_t length;

  for (length = 0; readNextByte(&byte); ++length) {
    *target += byte;
  }

  return length;
}

uint8_t InputStream::readUInt8() {
  uint8_t val;
  if (readNextBytes(&val, sizeof(uint8_t)) != sizeof(uint8_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint16_t InputStream::readUInt16() {
  uint16_t val;
  if (readNextBytes(&val, sizeof(uint16_t)) != sizeof(uint16_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint32_t InputStream::readUInt32() {
  uint32_t val;
  if (readNextBytes(&val, sizeof(uint32_t)) != sizeof(uint32_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint64_t InputStream::readUInt64() {
  uint64_t val;
  if (readNextBytes(&val, sizeof(uint64_t)) != sizeof(uint64_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

std::unique_ptr<FileInputStream> FileInputStream::openFile(
    const std::string& file_path) {
  auto fp = file_path.c_str();
  int fd = open(fp, O_RDONLY);

  if (fd < 1) {
    RAISE_ERRNO(kIOError, "error opening file '%s'", fp);
  }

  std::unique_ptr<FileInputStream> stream(new FileInputStream(fd, true));
  stream->readNextChunk();
  stream->setFileName(file_path);
  return stream;
}

std::unique_ptr<FileInputStream> FileInputStream::fromFileDescriptor(
    int fd,
    bool close_on_destroy /* = false */) {
  std::unique_ptr<FileInputStream> stream(
      new FileInputStream(fd, close_on_destroy));

  stream->setFileName(StringUtil::format("<fd:$0>", fd));
  return stream;
}

std::unique_ptr<FileInputStream> FileInputStream::fromFile(File&& file) {
  auto fd = file.fd();

  std::unique_ptr<FileInputStream> stream(new FileInputStream(std::move(file)));
  stream->setFileName(StringUtil::format("<fd:$0>", fd));
  return stream;
}

FileInputStream::FileInputStream(
    int fd,
    bool close_on_destroy /* = false */) :
    fd_(fd),
    close_on_destroy_(close_on_destroy),
    buf_len_(0),
    buf_pos_(0) {}

FileInputStream::FileInputStream(
    File&& file) :
    FileInputStream(file.releaseFD(), true) {}

FileInputStream::~FileInputStream() {
  if (fd_ >= 0 && close_on_destroy_) {
    close(fd_);
  }
}

bool FileInputStream::readNextByte(char* target) {
  if (buf_pos_ >= buf_len_) {
    readNextChunk();
  }

  if (buf_pos_ < buf_len_) {
    *target = buf_[buf_pos_++];
    return true;
  } else {
    return false;
  }
}

FileInputStream::kByteOrderMark FileInputStream::readByteOrderMark() {
  static char kByteOrderMarkUTF8[] = "\xEF\xBB\xBF";
  if (buf_pos_ + 2 < buf_len_ &&
      strncmp(buf_ + buf_pos_, kByteOrderMarkUTF8, 3) == 0) {
    buf_pos_ += 3;
    return BOM_UTF8;
  }

  return BOM_UNKNOWN;
}

void FileInputStream::readNextChunk() {
  int bytes_read = read(fd_, buf_, sizeof(buf_));

  if (bytes_read < 0) {
    RAISE_ERRNO(kIOError, "read(%s) failed", getFileName().c_str());
  }

  buf_pos_ = 0;
  buf_len_ = bytes_read;
}

void FileInputStream::rewind() {
  buf_pos_ = 0;
  buf_len_ = 0;

  if (lseek(fd_, 0, SEEK_SET) < 0) {
    RAISE_ERRNO(kIOError, "lseek(%s) failed", getFileName().c_str());
  }
}

std::unique_ptr<StringInputStream> StringInputStream::fromString(
    const std::string& string) {
  return std::unique_ptr<StringInputStream>(new StringInputStream(string));
}

StringInputStream::StringInputStream(
    const std::string& string) :
    str_(string),
    cur_(0) {
}

bool StringInputStream::readNextByte(char* target) {
  if (cur_ < str_.size()) {
    *target = str_[cur_++];
    return true;
  } else {
    return false;
  }
}

void StringInputStream::rewind() {
  cur_ = 0;
}

std::unique_ptr<BufferInputStream> BufferInputStream::fromBuffer(
    const Buffer* buf) {
  return std::unique_ptr<BufferInputStream>(new BufferInputStream(buf));
}

BufferInputStream::BufferInputStream(
    const Buffer* buf) :
    buf_(buf),
    cur_(0) {
}

bool BufferInputStream::readNextByte(char* target) {
  if (cur_ < buf_->size()) {
    *target = buf_->charAt(cur_++);
    return true;
  } else {
    return false;
  }
}

void BufferInputStream::rewind() {
  cur_ = 0;
}

} // fnord

<commit_msg>InputStream::readString impl<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <string>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <fnord/buffer.h>
#include <fnord/exception.h>
#include <fnord/io/inputstream.h>

namespace fnord {

std::unique_ptr<InputStream> InputStream::getStdin() {
  auto stdin_stream = new FileInputStream(0, false);
  return std::unique_ptr<InputStream>(stdin_stream);
}

InputStream::InputStream(
    const std::string& filename /* = "<anonymous input stream>" */) :
    filename_(filename) {}

const std::string& InputStream::getFileName() const {
  return filename_;
}

void InputStream::setFileName(const std::string& filename) {
  filename_ = filename;
}

// FIXPAUL: optimize?
size_t InputStream::readNextBytes(std::string* target, size_t n_bytes) {
  char byte;
  size_t length;

  for (length = 0; length < n_bytes && readNextByte(&byte); ++length) {
    *target += byte;
  }

  return length;
}

// FIXPAUL: optimize?
size_t InputStream::readNextBytes(Buffer* target, size_t n_bytes) {
  char byte;
  size_t length;

  target->reserve(n_bytes);

  for (length = 0; length < n_bytes && readNextByte(&byte); ++length) {
    target->append(&byte, sizeof(byte));
  }

  return length;
}

size_t InputStream::readNextBytes(void* target, size_t n_bytes) {
  size_t length = 0;

  while (length < n_bytes && readNextByte(((char*) target) + length)) {
    ++length;
  }

  return length;
}

// FIXPAUL: optimize?
size_t InputStream::readUntilEOF(std::string* target) {
  char byte;
  size_t length;

  for (length = 0; readNextByte(&byte); ++length) {
    *target += byte;
  }

  return length;
}

uint8_t InputStream::readUInt8() {
  uint8_t val;
  if (readNextBytes(&val, sizeof(uint8_t)) != sizeof(uint8_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint16_t InputStream::readUInt16() {
  uint16_t val;
  if (readNextBytes(&val, sizeof(uint16_t)) != sizeof(uint16_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint32_t InputStream::readUInt32() {
  uint32_t val;
  if (readNextBytes(&val, sizeof(uint32_t)) != sizeof(uint32_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

uint64_t InputStream::readUInt64() {
  uint64_t val;
  if (readNextBytes(&val, sizeof(uint64_t)) != sizeof(uint64_t)) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

String InputStream::readString(size_t size) {
  String val;
  if (readNextBytes(&val, size) != size) {
    RAISE(kRuntimeError, "unexpected end of stream");
  }

  return val;
}

std::unique_ptr<FileInputStream> FileInputStream::openFile(
    const std::string& file_path) {
  auto fp = file_path.c_str();
  int fd = open(fp, O_RDONLY);

  if (fd < 1) {
    RAISE_ERRNO(kIOError, "error opening file '%s'", fp);
  }

  std::unique_ptr<FileInputStream> stream(new FileInputStream(fd, true));
  stream->readNextChunk();
  stream->setFileName(file_path);
  return stream;
}

std::unique_ptr<FileInputStream> FileInputStream::fromFileDescriptor(
    int fd,
    bool close_on_destroy /* = false */) {
  std::unique_ptr<FileInputStream> stream(
      new FileInputStream(fd, close_on_destroy));

  stream->setFileName(StringUtil::format("<fd:$0>", fd));
  return stream;
}

std::unique_ptr<FileInputStream> FileInputStream::fromFile(File&& file) {
  auto fd = file.fd();

  std::unique_ptr<FileInputStream> stream(new FileInputStream(std::move(file)));
  stream->setFileName(StringUtil::format("<fd:$0>", fd));
  return stream;
}

FileInputStream::FileInputStream(
    int fd,
    bool close_on_destroy /* = false */) :
    fd_(fd),
    close_on_destroy_(close_on_destroy),
    buf_len_(0),
    buf_pos_(0) {}

FileInputStream::FileInputStream(
    File&& file) :
    FileInputStream(file.releaseFD(), true) {}

FileInputStream::~FileInputStream() {
  if (fd_ >= 0 && close_on_destroy_) {
    close(fd_);
  }
}

bool FileInputStream::readNextByte(char* target) {
  if (buf_pos_ >= buf_len_) {
    readNextChunk();
  }

  if (buf_pos_ < buf_len_) {
    *target = buf_[buf_pos_++];
    return true;
  } else {
    return false;
  }
}

FileInputStream::kByteOrderMark FileInputStream::readByteOrderMark() {
  static char kByteOrderMarkUTF8[] = "\xEF\xBB\xBF";
  if (buf_pos_ + 2 < buf_len_ &&
      strncmp(buf_ + buf_pos_, kByteOrderMarkUTF8, 3) == 0) {
    buf_pos_ += 3;
    return BOM_UTF8;
  }

  return BOM_UNKNOWN;
}

void FileInputStream::readNextChunk() {
  int bytes_read = read(fd_, buf_, sizeof(buf_));

  if (bytes_read < 0) {
    RAISE_ERRNO(kIOError, "read(%s) failed", getFileName().c_str());
  }

  buf_pos_ = 0;
  buf_len_ = bytes_read;
}

void FileInputStream::rewind() {
  buf_pos_ = 0;
  buf_len_ = 0;

  if (lseek(fd_, 0, SEEK_SET) < 0) {
    RAISE_ERRNO(kIOError, "lseek(%s) failed", getFileName().c_str());
  }
}

std::unique_ptr<StringInputStream> StringInputStream::fromString(
    const std::string& string) {
  return std::unique_ptr<StringInputStream>(new StringInputStream(string));
}

StringInputStream::StringInputStream(
    const std::string& string) :
    str_(string),
    cur_(0) {
}

bool StringInputStream::readNextByte(char* target) {
  if (cur_ < str_.size()) {
    *target = str_[cur_++];
    return true;
  } else {
    return false;
  }
}

void StringInputStream::rewind() {
  cur_ = 0;
}

std::unique_ptr<BufferInputStream> BufferInputStream::fromBuffer(
    const Buffer* buf) {
  return std::unique_ptr<BufferInputStream>(new BufferInputStream(buf));
}

BufferInputStream::BufferInputStream(
    const Buffer* buf) :
    buf_(buf),
    cur_(0) {
}

bool BufferInputStream::readNextByte(char* target) {
  if (cur_ < buf_->size()) {
    *target = buf_->charAt(cur_++);
    return true;
  } else {
    return false;
  }
}

void BufferInputStream::rewind() {
  cur_ = 0;
}

} // fnord

<|endoftext|>


<commit_before><commit_msg>Revert 20927 - Experiment with opening links opened via context menu in the foreground.<commit_after><|endoftext|>


<commit_before>#pragma once

namespace df
{
  void InitRenderThread() {}
}
<commit_msg>Clean up.<commit_after><|endoftext|>


<commit_before>#include "tinfra/subprocess.h"

#include "tinfra/io/stream.h"
#include "tinfra/fmt.h"
#include "tinfra/win32.h"
#include "tinfra/holder.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

namespace tinfra {

template<>
void holder<HANDLE>::release() {
    if( value_ != 0 ) {
        ::CloseHandle(value_);
        value_ = 0;
    }
}

namespace win32 {

using tinfra::io::stream;
using tinfra::io::io_exception;
using tinfra::io::open_native;

static HANDLE open_null_for_subprocess(std::ios::openmode mode)
{
    stream* stream = tinfra::io::open_file("NUL", mode);
    HANDLE result = reinterpret_cast<HANDLE>(stream->native());
    stream->release();
    delete stream;
    SetHandleInformation(result, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    return result;
}

//
// pipe support for win32
//

struct win32_subprocess: public subprocess {
    stream* stdin_;
    stream* stdout_;
    stream* stderr_;

    HANDLE process_handle;

    int exit_code;

    win32_subprocess()
        : process_handle(0), exit_code(-1)
    {
    }

    ~win32_subprocess() {
        delete stdin_;
        delete stdout_;
        if( stdout_ != stderr_ )
            delete stderr_;

        get_exit_code();

        if( !::CloseHandle(process_handle) ) {
            // TODO: silent win32 error
        }
    }

    virtual void     wait() {
        if( WaitForSingleObject(process_handle, INFINITE) != WAIT_OBJECT_0 ) {
            throw_system_error("WaitForSingleObject on subprocess failed");
        }
    }

    virtual int      get_exit_code() {
        if( process_handle != NULL ) {
            DWORD dwExitCode;
            if( !::GetExitCodeProcess(process_handle,&dwExitCode) ) {
                throw_system_error("GetExitCodeProcess failed");
            }
            if( dwExitCode == STILL_ACTIVE )
                return -1;
            exit_code = dwExitCode;
        }
        return exit_code;
    }

    virtual void     terminate() {
        // unfortunately there is no way to grcefully kill
        // win32 process
        kill();
    }
    virtual void     kill() {
        if( !::TerminateProcess(process_handle, 10) ) {
            throw_system_error("TerminateProcess failed");
        }
    }

    virtual stream*  get_stdin()  { return stdin_;  }
    virtual stream*  get_stdout() { return stdout_; }
    virtual stream*  get_stderr() { return stderr_; }

    virtual intptr_t get_native_handle() { return reinterpret_cast<intptr_t>(process_handle); }

    void start(std::vector<std::string> const& args) {
        std::ostringstream cmd;
        for(std::vector<std::string>::const_iterator i = args.begin(); i != args.end(); ++i ) {
            if( i != args.begin() )
                cmd << " ";
            if( i->find_first_of(" \t") )
                cmd << "\"" << *i << "\"";
            else
                cmd << *i;
        }
        start(cmd.str().c_str());
    }

    void start(const char* command) {
        HANDLE out_here(0);     // writing HERE -> child
        HANDLE out_remote(0);   // reading CHILD <- here

        HANDLE in_here(0);      // reading HERE <- child
        HANDLE in_remote(0);    // writing CHILD -> here

        HANDLE err_here(0);     // reading HERE <- child (diagnostic)
        HANDLE err_remote(0);   // writing CHILD -> here (diagnostic)

        const bool fread =  (stdout_mode == REDIRECT);
        const bool fwrite = (stdin_mode  == REDIRECT);
        const bool ferr  =  (stderr_mode == REDIRECT && !redirect_stderr);

        stdin_ = stderr_ = stdout_ = 0;

        {
            SECURITY_ATTRIBUTES saAttr;

            saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
            saAttr.bInheritHandle = TRUE;
            saAttr.lpSecurityDescriptor = NULL;

            if( fwrite ) {
                if ( CreatePipe(&out_remote, &out_here, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(out_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(out_here,   HANDLE_FLAG_INHERIT, 0);
            }
            if( fread ) {
                if ( CreatePipe(&in_here, &in_remote, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(in_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(in_here,   HANDLE_FLAG_INHERIT, 0);
            }
            if( ferr ) {
                if ( CreatePipe(&err_here, &    err_remote, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(err_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(err_here,   HANDLE_FLAG_INHERIT, 0);
            }
        }
        {	    /* spawn process */
            STARTUPINFO si;

            PROCESS_INFORMATION processi;
            DWORD creationFlags = 0;

            memset(&processi,0,sizeof(processi));

            memset(&si,0,sizeof(si));
            si.cb = sizeof(si);
            /* GetStartupInfo(&si);*/

            if( fwrite ) {
                stdin_ = open_native(reinterpret_cast<intptr_t>((HANDLE)out_here));
                out_here = 0;
                si.hStdInput = out_remote;
            } else if( stdin_mode == NONE) {
                si.hStdInput = out_remote = open_null_for_subprocess(std::ios::in);
            } else {
                si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
            }

            if( fread ) {
                stdout_ = open_native(reinterpret_cast<intptr_t>((HANDLE)in_here));
                in_here = 0;
                si.hStdOutput = in_remote;
            } else if( stdout_mode == NONE) {
                si.hStdOutput = in_remote = open_null_for_subprocess(std::ios::out);
            } else {
                si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
            }

            if( redirect_stderr && fread ) {
                stderr_ = stdout_;
                si.hStdError = in_remote;
            } else if( ferr ) {
                stderr_ = open_native(reinterpret_cast<intptr_t>((HANDLE)err_here));
                err_here = 0;
                si.hStdError = err_remote;
            } else if( stderr_mode == NONE ) {
                si.hStdError = err_remote = open_null_for_subprocess(std::ios::out);
            } else{
                stderr_ = NULL;
                si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
            }

            si.dwFlags |= STARTF_USESTDHANDLES;

            creationFlags |= DETACHED_PROCESS;
            si.dwFlags |= STARTF_USESHOWWINDOW;
            si.wShowWindow = SW_HIDE;

            if( CreateProcess(
                NULL,
                (char*)command,
                NULL,
                NULL,
                TRUE,
                creationFlags,
                NULL,
                NULL,
                &si,
                &processi) == 0 )
            {
                throw_system_error("CreateProcess failed");
            }
            process_handle = processi.hProcess;
            if( in_remote )
                ::CloseHandle(in_remote);
            if( out_remote )
                ::CloseHandle(out_remote);
            if( err_remote )
                ::CloseHandle(err_remote);
        }
    }
};

} // end namespace win32

//
// tinfra global stub for win32
//

subprocess* create_subprocess() {
    return new win32::win32_subprocess();
}

} // end namespace tinfra::win32
<commit_msg>w32_subprocess: general tweaking<commit_after>#include "tinfra/subprocess.h"

#include "tinfra/io/stream.h"
#include "tinfra/fmt.h"
#include "tinfra/win32.h"
#include "tinfra/holder.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

namespace tinfra {

template<>
void holder<HANDLE>::release() {
    if( value_ != 0 ) {
        ::CloseHandle(value_);
        value_ = 0;
    }
}

namespace win32 {

using tinfra::io::stream;
using tinfra::io::io_exception;
using tinfra::io::open_native;

static HANDLE open_null_for_subprocess(std::ios::openmode mode)
{
    stream* stream = tinfra::io::open_file("NUL", mode);
    HANDLE result = reinterpret_cast<HANDLE>(stream->native());
    stream->release();
    delete stream;
    SetHandleInformation(result, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
    return result;
}

//
// pipe support for win32
//

struct win32_subprocess: public subprocess {
    stream* stdin_;
    stream* stdout_;
    stream* stderr_;

    HANDLE process_handle;

    int exit_code;

    win32_subprocess()
        : process_handle(0), exit_code(-1)
    {
    }

    ~win32_subprocess() {
        delete stdin_;
        delete stdout_;
        if( stdout_ != stderr_ )
            delete stderr_;
        try {
            get_exit_code();
        } catch(std::exception& e) {
            // TODO: silent win32 error
        }
        if( process_handle ) {
            if( !::CloseHandle(process_handle) ) {
                // TODO: silent win32 error
            }
        }
    }

    virtual void     wait() {
        if( WaitForSingleObject(process_handle, INFINITE) != WAIT_OBJECT_0 ) {
            throw_system_error("WaitForSingleObject on subprocess failed");
        }
    }

    virtual int      get_exit_code() {
        if( exit_code == -1 && process_handle != NULL ) {
            DWORD dwExitCode;
            if( !::GetExitCodeProcess(process_handle,&dwExitCode) ) {
                throw_system_error("GetExitCodeProcess failed");
            }
            if( dwExitCode == STILL_ACTIVE )
                return -1;
            exit_code = dwExitCode;
        }
        return exit_code;
    }

    virtual void     terminate() {
        // unfortunately there is no way to grcefully kill
        // win32 process
        kill();
    }
    virtual void     kill() {
        if( !::TerminateProcess(process_handle, 10) ) {
            throw_system_error("TerminateProcess failed");
        }
    }

    virtual stream*  get_stdin()  { return stdin_;  }
    virtual stream*  get_stdout() { return stdout_; }
    virtual stream*  get_stderr() { return stderr_; }

    virtual intptr_t get_native_handle() { return reinterpret_cast<intptr_t>(process_handle); }

    void start(std::vector<std::string> const& args) {
        std::ostringstream cmd;
        for(std::vector<std::string>::const_iterator i = args.begin(); i != args.end(); ++i ) {
            if( i != args.begin() )
                cmd << " ";
            if( i->find_first_of(" \t") )
                cmd << "\"" << *i << "\"";
            else
                cmd << *i;
        }
        start(cmd.str().c_str());
    }

    void start(const char* command) {
        HANDLE out_here(0);     // writing HERE -> child
        HANDLE out_remote(0);   // reading CHILD <- here

        HANDLE in_here(0);      // reading HERE <- child
        HANDLE in_remote(0);    // writing CHILD -> here

        HANDLE err_here(0);     // reading HERE <- child (diagnostic)
        HANDLE err_remote(0);   // writing CHILD -> here (diagnostic)

        const bool fread =  (stdout_mode == REDIRECT);
        const bool fwrite = (stdin_mode  == REDIRECT);
        const bool ferr  =  (stderr_mode == REDIRECT && !redirect_stderr);

        stdin_ = stderr_ = stdout_ = 0;

        {
            SECURITY_ATTRIBUTES saAttr;

            saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
            saAttr.bInheritHandle = TRUE;
            saAttr.lpSecurityDescriptor = NULL;

            if( fwrite ) {
                if ( CreatePipe(&out_remote, &out_here, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(out_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(out_here,   HANDLE_FLAG_INHERIT, 0);
            }
            if( fread ) {
                if ( CreatePipe(&in_here, &in_remote, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(in_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(in_here,   HANDLE_FLAG_INHERIT, 0);
            }
            if( ferr ) {
                if ( CreatePipe(&err_here, &    err_remote, &saAttr, 0) == 0 ) {
                    throw_system_error("CreatePipe failed");
                }
                SetHandleInformation(err_remote, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
                SetHandleInformation(err_here,   HANDLE_FLAG_INHERIT, 0);
            }
        }
        {	    /* spawn process */
            STARTUPINFO si;

            PROCESS_INFORMATION processi;
            DWORD creationFlags = 0;

            memset(&processi,0,sizeof(processi));

            memset(&si,0,sizeof(si));
            si.cb = sizeof(si);
            /* GetStartupInfo(&si);*/

            if( fwrite ) {
                stdin_ = open_native(reinterpret_cast<intptr_t>((HANDLE)out_here));
                out_here = 0;
                si.hStdInput = out_remote;
            } else if( stdin_mode == NONE) {
                si.hStdInput = out_remote = open_null_for_subprocess(std::ios::in);
            } else {
                si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
            }

            if( fread ) {
                stdout_ = open_native(reinterpret_cast<intptr_t>((HANDLE)in_here));
                in_here = 0;
                si.hStdOutput = in_remote;
            } else if( stdout_mode == NONE) {
                si.hStdOutput = in_remote = open_null_for_subprocess(std::ios::out);
            } else {
                si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
            }

            if( redirect_stderr && fread ) {
                stderr_ = stdout_;
                si.hStdError = in_remote;
            } else if( ferr ) {
                stderr_ = open_native(reinterpret_cast<intptr_t>((HANDLE)err_here));
                err_here = 0;
                si.hStdError = err_remote;
            } else if( stderr_mode == NONE ) {
                si.hStdError = err_remote = open_null_for_subprocess(std::ios::out);
            } else{
                stderr_ = NULL;
                si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
            }

            si.dwFlags |= STARTF_USESTDHANDLES;

            creationFlags |= DETACHED_PROCESS;
            si.dwFlags |= STARTF_USESHOWWINDOW;
            si.wShowWindow = SW_HIDE;

            if( CreateProcess(
                NULL,
                (char*)command,
                NULL,
                NULL,
                TRUE,
                creationFlags,
                NULL,
                NULL,
                &si,
                &processi) == 0 )
            {
                throw_system_error("CreateProcess failed");
            }
            process_handle = processi.hProcess;
            if( in_remote )
                ::CloseHandle(in_remote);
            if( out_remote )
                ::CloseHandle(out_remote);
            if( err_remote )
                ::CloseHandle(err_remote);
        }
    }
};

} // end namespace win32

//
// tinfra global stub for win32
//

subprocess* create_subprocess() {
    return new win32::win32_subprocess();
}

} // end namespace tinfra::win32
<|endoftext|>


<commit_before>// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_CXX_REQUIRES_HPP
#define IOX_HOOFS_CXX_REQUIRES_HPP

namespace iox
{
namespace cxx
{
namespace internal
{
void Require(
    const bool condition, const char* file, const int line, const char* function, const char* conditionString) noexcept;
} // namespace internal

// implementing C++ Core Guideline, I.6. Prefer Expects
// see:
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-expects
#define Expects(condition) internal::Require(condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, #condition)

// implementing C++ Core Guideline, I.8. Prefer Ensures
// see:
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-ensures
#define Ensures(condition) internal::Require(condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, #condition)

} // namespace cxx
} // namespace iox

#endif
<commit_msg>iox-#996 Add platform correction to requires<commit_after>// Copyright (c) 2022 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_CXX_REQUIRES_HPP
#define IOX_HOOFS_CXX_REQUIRES_HPP

#include "iceoryx_hoofs/platform/platform_correction.hpp"

namespace iox
{
namespace cxx
{
namespace internal
{
void Require(
    const bool condition, const char* file, const int line, const char* function, const char* conditionString) noexcept;
} // namespace internal

// implementing C++ Core Guideline, I.6. Prefer Expects
// see:
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-expects
#define Expects(condition) internal::Require(condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, #condition)

// implementing C++ Core Guideline, I.8. Prefer Ensures
// see:
// https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-ensures
#define Ensures(condition) internal::Require(condition, __FILE__, __LINE__, __PRETTY_FUNCTION__, #condition)

} // namespace cxx
} // namespace iox

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "iceoryx_posh/internal/capro/capro_message.hpp"
#include "iceoryx_posh/internal/roudi/introspection/port_introspection.hpp"
#include "test.hpp"

using namespace ::testing;
// using ::testing::Return;
using namespace iox::capro;

class CaproMessage_test : public Test
{
  public:
    void SetUp(){};
    void TearDown(){};
};

TEST_F(CaproMessage_test, CtorNoParams)
{
    CaproMessage testObj = CaproMessage();
    EXPECT_EQ(testObj.m_requestPort, nullptr);
    EXPECT_EQ(testObj.m_type, CaproMessageType::NOTYPE);
    EXPECT_EQ(testObj.m_subType, CaproMessageSubType::NOSUBTYPE);
}

TEST_F(CaproMessage_test, CtorParams)
{
    uint16_t testServiceID = 1;
    uint16_t testEventID = 2;
    uint16_t testInstanceID = 3;
    ServiceDescription sd = ServiceDescription(testServiceID, testEventID, testInstanceID);
    iox::popo::ReceiverPortData recData;

    CaproMessage testObj = CaproMessage(CaproMessageType::OFFER, sd, CaproMessageSubType::SERVICE, &recData);
    EXPECT_EQ(testObj.m_requestPort, &recData);
    EXPECT_EQ(testObj.m_type, CaproMessageType::OFFER);
    EXPECT_EQ(testObj.m_subType, CaproMessageSubType::SERVICE);
}<commit_msg>iox-#168: added unit test cases for capro message<commit_after>// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "iceoryx_posh/internal/capro/capro_message.hpp"
#include "iceoryx_posh/internal/roudi/introspection/port_introspection.hpp"
#include "test.hpp"

using namespace ::testing;
// using ::testing::Return;
using namespace iox::capro;

class CaproMessage_test : public Test
{
  public:
    void SetUp(){};
    void TearDown(){};
};

TEST_F(CaproMessage_test, CtorNoParams)
{
    CaproMessage testObj = CaproMessage();
    EXPECT_EQ(testObj.m_requestPort, nullptr);
    EXPECT_EQ(testObj.m_type, CaproMessageType::NOTYPE);
    EXPECT_EQ(testObj.m_subType, CaproMessageSubType::NOSUBTYPE);
}

TEST_F(CaproMessage_test, CtorParams)
{
    uint16_t testServiceID = 1;
    uint16_t testEventID = 2;
    uint16_t testInstanceID = 3;
    ServiceDescription sd = ServiceDescription(testServiceID, testEventID, testInstanceID);
    iox::popo::ReceiverPortData recData;

    CaproMessage testObj = CaproMessage(CaproMessageType::OFFER, sd, CaproMessageSubType::SERVICE, &recData);
    EXPECT_EQ(testObj.m_requestPort, &recData);
    EXPECT_EQ(testObj.m_type, CaproMessageType::OFFER);
    EXPECT_EQ(testObj.m_subType, CaproMessageSubType::SERVICE);
}

TEST_F(CaproMessage_test, CtorParameterized)
{
    uint16_t testServiceID = 1;
    uint16_t testEventID = 2;
    uint16_t testInstanceID = 3;
    ServiceDescription sd = ServiceDescription(testServiceID, testEventID, testInstanceID);
    iox::popo::ReceiverPortData recData;

    CaproMessage testObj = CaproMessage(CaproMessageType::OFFER, sd);
    EXPECT_EQ(testObj.m_requestPort, nullptr);
    EXPECT_EQ(testObj.m_subType, CaproMessageSubType::NOSUBTYPE);
}<|endoftext|>


<commit_before>// Copyright 2013 The Flutter 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 "linear_gradient_contents.h"

#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/tessellator/tessellator.h"

namespace impeller {

LinearGradientContents::LinearGradientContents() = default;

LinearGradientContents::~LinearGradientContents() = default;

void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) {
  start_point_ = start_point;
  end_point_ = end_point;
}

void LinearGradientContents::SetColors(std::vector<Color> colors) {
  colors_ = std::move(colors);
  if (colors_.empty()) {
    colors_.push_back(Color::Black());
    colors_.push_back(Color::Black());
  } else if (colors_.size() < 2u) {
    colors_.push_back(colors_.back());
  }
}

const std::vector<Color>& LinearGradientContents::GetColors() const {
  return colors_;
}

bool LinearGradientContents::Render(const ContentContext& renderer,
                                    const Entity& entity,
                                    RenderPass& pass) const {
  using VS = GradientFillPipeline::VertexShader;
  using FS = GradientFillPipeline::FragmentShader;

  auto vertices_builder = VertexBufferBuilder<VS::PerVertexData>();
  {
    auto result = Tessellator{}.Tessellate(entity.GetPath().GetFillType(),
                                           entity.GetPath().CreatePolyline(),
                                           [&vertices_builder](Point point) {
                                             VS::PerVertexData vtx;
                                             vtx.vertices = point;
                                             vertices_builder.AppendVertex(vtx);
                                           });
    if (!result) {
      return false;
    }
  }

  VS::FrameInfo frame_info;
  frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
                   entity.GetTransformation();

  FS::GradientInfo gradient_info;
  gradient_info.start_point = start_point_;
  gradient_info.end_point = end_point_;
  gradient_info.start_color = colors_[0];
  gradient_info.end_color = colors_[1];

  Command cmd;
  cmd.label = "LinearGradientFill";
  cmd.pipeline =
      renderer.GetGradientFillPipeline(OptionsFromPassAndEntity(pass, entity));
  cmd.stencil_reference = entity.GetStencilDepth();
  cmd.BindVertices(
      vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
  cmd.primitive_type = PrimitiveType::kTriangle;
  FS::BindGradientInfo(
      cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info));
  VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info));
  return pass.AddCommand(std::move(cmd));
}

}  // namespace impeller
<commit_msg>Premultiply gradient colors (#106)<commit_after>// Copyright 2013 The Flutter 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 "linear_gradient_contents.h"

#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/tessellator/tessellator.h"

namespace impeller {

LinearGradientContents::LinearGradientContents() = default;

LinearGradientContents::~LinearGradientContents() = default;

void LinearGradientContents::SetEndPoints(Point start_point, Point end_point) {
  start_point_ = start_point;
  end_point_ = end_point;
}

void LinearGradientContents::SetColors(std::vector<Color> colors) {
  colors_ = std::move(colors);
  if (colors_.empty()) {
    colors_.push_back(Color::Black());
    colors_.push_back(Color::Black());
  } else if (colors_.size() < 2u) {
    colors_.push_back(colors_.back());
  }
}

const std::vector<Color>& LinearGradientContents::GetColors() const {
  return colors_;
}

bool LinearGradientContents::Render(const ContentContext& renderer,
                                    const Entity& entity,
                                    RenderPass& pass) const {
  using VS = GradientFillPipeline::VertexShader;
  using FS = GradientFillPipeline::FragmentShader;

  auto vertices_builder = VertexBufferBuilder<VS::PerVertexData>();
  {
    auto result = Tessellator{}.Tessellate(entity.GetPath().GetFillType(),
                                           entity.GetPath().CreatePolyline(),
                                           [&vertices_builder](Point point) {
                                             VS::PerVertexData vtx;
                                             vtx.vertices = point;
                                             vertices_builder.AppendVertex(vtx);
                                           });
    if (!result) {
      return false;
    }
  }

  VS::FrameInfo frame_info;
  frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
                   entity.GetTransformation();

  FS::GradientInfo gradient_info;
  gradient_info.start_point = start_point_;
  gradient_info.end_point = end_point_;
  gradient_info.start_color = colors_[0].Premultiply();
  gradient_info.end_color = colors_[1].Premultiply();

  Command cmd;
  cmd.label = "LinearGradientFill";
  cmd.pipeline =
      renderer.GetGradientFillPipeline(OptionsFromPassAndEntity(pass, entity));
  cmd.stencil_reference = entity.GetStencilDepth();
  cmd.BindVertices(
      vertices_builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
  cmd.primitive_type = PrimitiveType::kTriangle;
  FS::BindGradientInfo(
      cmd, pass.GetTransientsBuffer().EmplaceUniform(gradient_info));
  VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info));
  return pass.AddCommand(std::move(cmd));
}

}  // namespace impeller
<|endoftext|>