<commit_before>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.time_left(clock.running_for());
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.time_left(clock.running_for());

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 2 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 0)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 1)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.time_left(clock.running_for());
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    if(still_on_principal_variation && principal_variation.size() > depth + 1)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        if(move_iter != all_legal_moves.end())
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
        }
    }
    else
    {
        still_on_principal_variation = false;
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), still_on_principal_variation ? 1 : 0);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.time_left(clock.running_for());
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.time_left(clock.running_for()));
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.time_left(clock.running_for()));

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 500)
        {
            recurse = false;
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            // Record immediate result without looking ahead further
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(better_than(result, best_result, perspective))
        {
            best_result = result;

            if(better_than(best_result, alpha, perspective))
            {
                alpha = best_result;
                if(better_than(alpha, beta, perspective) || alpha == beta)
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 1.0)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.time_left(clock.running_for());
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.time_left(clock.running_for()) < 0)
        {
            break;
        }

        if(!recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.time_left(clock.running_for());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.time_left(clock.running_for());
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.time_left(clock.running_for());
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                Game_Result move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, Game_Result move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<commit_msg>More thinking output<commit_after>#include "Players/Minimax_AI.h"

#include <iostream>

#include "Players/Game_Tree_Node_Result.h"
#include "Game/Board.h"
#include "Game/Clock.h"
#include "Utility.h"

const Move& Minimax_AI::choose_move(const Board& board, const Clock& clock) const
{
    // Erase data from previous board when starting new game
    if(board.game_record().size() <= 1)
    {
        principal_variation.clear();
        commentary.clear();
    }

    nodes_searched = 0;
    clock_start_time = clock.time_left(clock.running_for());
    maximum_depth = 0;

    nodes_evaluated = 0;
    total_evaluation_time = 0.0;
    time_at_last_output = clock.time_left(clock.running_for());

    const auto& legal_moves = board.legal_moves();
    if(legal_moves.size() == 1)
    {
        if(principal_variation.size() > 2 && principal_variation[1] == board.game_record().back())
        {
            // search_game_tree() assumes the principal variation starts
            // with the previous move of this player. If a move was forced,
            // then the principal variation needs to be updated to start with
            // the next move of this side after checking that the immediately
            // preceding move was the expected one.
            principal_variation.erase(principal_variation.begin(),
                                      principal_variation.begin() + 2);
        }
        else
        {
            principal_variation.clear();
        }

        commentary.push_back(principal_variation);

        return *legal_moves.front(); // If there's only one legal move, take it.
    }

    auto time_to_use = time_to_examine(board, clock);

    // alpha = highest score found that opponent will allow
    Game_Tree_Node_Result alpha_start = {Math::lose_score,
                                         board.whose_turn(),
                                         {}};

    // beta = score that will cause opponent to choose a different prior move
    Game_Tree_Node_Result beta_start = {Math::win_score,
                                        board.whose_turn(),
                                        {}};

    auto result = search_game_tree(board,
                                   time_to_use,
                                   clock,
                                   1,
                                   alpha_start,
                                   beta_start,
                                   ! principal_variation.empty());

    if(board.thinking_mode() == CECP)
    {
        output_thinking_cecp(result, clock, board.whose_turn());
    }

    if(result.depth() > 0)
    {
        commentary.push_back(result.variation);
    }
    else
    {
        commentary.push_back({});
    }

    if(result.depth() > 1)
    {
        principal_variation = result.variation;
    }
    else
    {
        principal_variation.clear();
    }

    if(nodes_evaluated > 0)
    {
        evaluation_speed = nodes_evaluated / total_evaluation_time;
    }

    return *result.variation.front();
}

Game_Tree_Node_Result Minimax_AI::search_game_tree(const Board& board,
                                                   const double time_to_examine,
                                                   const Clock& clock,
                                                   const size_t depth,
                                                   Game_Tree_Node_Result alpha,
                                                   const Game_Tree_Node_Result& beta,
                                                   bool still_on_principal_variation) const
{
    const auto time_start = clock.time_left(clock.running_for());
    maximum_depth = std::max(maximum_depth, depth);
    auto all_legal_moves = board.legal_moves();

    // The two items in the principal variation are the last two moves of
    // the non-hypothetical board. So, the first item in the principal variation to
    // consider is at index depth + 1 (since depth starts at 1).
    if(still_on_principal_variation && principal_variation.size() > depth + 1)
    {
        auto next_principal_variation_move = principal_variation[depth + 1];
        auto move_iter = std::find(all_legal_moves.begin(),
                                   all_legal_moves.end(),
                                   next_principal_variation_move);

        if(move_iter != all_legal_moves.end())
        {
            // Put principal variation move at start of list to allow
            // the most pruning later.
            std::iter_swap(all_legal_moves.begin(), move_iter);
        }
    }
    else
    {
        still_on_principal_variation = false;
    }

    // Consider capturing moves first after principal variation move
    auto partition_start = std::next(all_legal_moves.begin(), still_on_principal_variation ? 1 : 0);
    std::partition(partition_start, all_legal_moves.end(),
                   [&board](auto move){ return board.move_captures(*move); });

    const auto perspective = board.whose_turn();
    auto moves_left = all_legal_moves.size();

    Game_Tree_Node_Result best_result = {Math::lose_score,
                                         perspective,
                                         {all_legal_moves.front()}};

    for(const auto& move : all_legal_moves)
    {
        auto evaluate_start_time = clock.time_left(clock.running_for());
        ++nodes_searched;

        auto next_board = board;

        auto move_result = next_board.submit_move(*move);
        if(move_result.winner() != NONE)
        {
            // This move results in checkmate, no other move can be better.
            return create_result(next_board, perspective, move_result, depth);
        }

        if(alpha.depth() <= depth + 2 && alpha.is_winning_for(perspective))
        {
            // This move will take a longer path to victory
            // than one already found. Use "depth + 2" since,
            // if this move isn't winning (and it isn't, since
            // we're here), then the earliest move that can
            // win is the next one, which is two away (after
            // opponent's move).
            continue;
        }

        double time_left = time_to_examine - (time_start - clock.time_left(clock.running_for()));
        double time_allotted_for_this_move = (time_left / moves_left)*speculation_time_factor(next_board);
        time_allotted_for_this_move = std::min(time_allotted_for_this_move, clock.time_left(clock.running_for()));

        bool recurse;
        if(move_result.game_has_ended())
        {
            recurse = false;
        }
        else if(still_on_principal_variation)
        {
            recurse = true;
        }
        else if(depth > 500)
        {
            recurse = false;
        }
        else
        {
            auto minimum_time_to_recurse = next_board.legal_moves().size() / evaluation_speed;
            recurse = (time_allotted_for_this_move > minimum_time_to_recurse);
        }

        Game_Tree_Node_Result result;
        if(recurse)
        {
            result = search_game_tree(next_board,
                                      time_allotted_for_this_move,
                                      clock,
                                      depth + 1,
                                      beta,
                                      alpha,
                                      still_on_principal_variation);
        }
        else
        {
            // Record immediate result without looking ahead further
            result = create_result(next_board, perspective, move_result, depth);
        }

        if(better_than(result, best_result, perspective))
        {
            best_result = result;

            if(better_than(best_result, alpha, perspective))
            {
                alpha = best_result;
                if(better_than(alpha, beta, perspective) || alpha == beta)
                {
                    break;
                }
                else if(board.thinking_mode() == CECP && time_since_last_output(clock) > 0.1)
                {
                    output_thinking_cecp(alpha, clock,
                                         depth % 2 == 1 ? perspective : opposite(perspective));
                    time_at_last_output = clock.time_left(clock.running_for());
                }
            }
        }

        --moves_left;
        still_on_principal_variation = false; // only the first move is part of the principal variation

        if(clock.time_left(clock.running_for()) < 0)
        {
            break;
        }

        if(!recurse) // This move was scored by genome.evaluate().
        {
            ++nodes_evaluated;
            total_evaluation_time += evaluate_start_time - clock.time_left(clock.running_for());
        }
    }

    return best_result;
}

void Minimax_AI::output_thinking_cecp(const Game_Tree_Node_Result& thought,
                                      const Clock& clock,
                                      Color perspective) const
{
    auto score = thought.corrected_score(perspective) / centipawn_value();

    // Indicate "mate in N moves" where N == thought.depth
    if(thought.is_winning_for(perspective))
    {
        score = 10000.0 - thought.depth() + 1;
    }
    else if(thought.is_losing_for(perspective))
    {
        score = -(10000.0 - thought.depth() + 1);
    }

    auto time_so_far = clock_start_time - clock.time_left(clock.running_for());
    std::cout << thought.depth() // ply
        << " "
        << int(score) // score in what should be centipawns
        << " "
        << int(time_so_far * 100) // search time in centiseconds
        << " "
        << nodes_searched
        << " "
        << maximum_depth
        << " "
        << int(nodes_searched / time_so_far)
        << '\t';

    // Principal variation
    for(const auto& move : thought.variation)
    {
        std::cout << move->coordinate_move() << ' ';
    }

    std::cout << std::endl;
}

double Minimax_AI::time_since_last_output(const Clock& clock) const
{
    return time_at_last_output - clock.time_left(clock.running_for());
}

Game_Tree_Node_Result Minimax_AI::create_result(const Board& board,
                                                Color perspective,
                                                Game_Result move_result,
                                                size_t depth) const
{
    return {evaluate(board, move_result, perspective, depth),
            perspective,
            {board.game_record().end() - depth,
            board.game_record().end()}};
}

void Minimax_AI::calibrate_thinking_speed() const
{
    evaluation_speed = 100; // very conservative initial guess
    auto calibration_time = 1.0; // seconds
    Board board;
    Clock clock(calibration_time, 1, 0.0);
    clock.start();
    choose_move(board, clock);
    // choose_move() keeps track of the time it takes and the number of positions
    // it sees, so this practice move will update the positions_per_second to a
    // more reasonable value.
}

double Minimax_AI::evaluate(const Board & board, Game_Result move_result, Color perspective, size_t depth) const
{
    if(move_result.game_has_ended())
    {
        if(move_result.winner() == NONE) // stalemate
        {
            return 0;
        }
        else if(move_result.winner() == perspective) // checkmate win
        {
            return Math::win_score;
        }
        else // checkmate loss
        {
            return Math::lose_score;
        }
    }

    return internal_evaluate(board, perspective, depth);
}

double Minimax_AI::centipawn_value() const
{
    return value_of_centipawn;
}

void Minimax_AI::calculate_centipawn_value()
{
    auto board_with_pawns = Board("4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1");
    auto board_with_no_white_pawns = Board("4k3/pppppppp/8/8/8/8/8/4K3 w - - 0 1");
    value_of_centipawn = std::abs(evaluate(board_with_pawns, {}, WHITE, 0) - evaluate(board_with_no_white_pawns, {}, WHITE, 0)) / 800;
}

std::string Minimax_AI::commentary_for_move(size_t move_number) const
{
    std::string result;
    if(move_number < commentary.size() && !commentary.at(move_number).empty())
    {
        result = commentary.at(move_number).front()->coordinate_move();
        for(size_t i = 1; i < commentary.at(move_number).size(); ++i)
        {
            result += " " + commentary.at(move_number).at(i)->coordinate_move();
        }
    }

    return result;
}
<|endoftext|>


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

#include "chrome/browser/app_modal_dialog.h"

#include <gtk/gtk.h>

#include "app/l10n_util.h"
#include "app/message_box_flags.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/gtk_util.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

namespace {

// We stash pointers to widgets on the gtk_dialog so we can refer to them
// after dialog creation.
const char kPromptTextId[] = "chrome_prompt_text";
const char kSuppressCheckboxId[] = "chrome_suppress_checkbox";

// If there's a text entry in the dialog, get the text from the first one and
// return it.
std::wstring GetPromptText(GtkDialog* dialog) {
  GtkWidget* widget = static_cast<GtkWidget*>(
      g_object_get_data(G_OBJECT(dialog), kPromptTextId));
  if (widget)
    return UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(widget)));
  return std::wstring();
}

// If there's a toggle button in the dialog, return the toggled state.
// Otherwise, return false.
bool ShouldSuppressJSDialogs(GtkDialog* dialog) {
  GtkWidget* widget = static_cast<GtkWidget*>(
      g_object_get_data(G_OBJECT(dialog), kSuppressCheckboxId));
  if (widget)
    return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  return false;
}

void OnDialogResponse(GtkDialog* dialog, gint response_id,
                      AppModalDialog* app_modal_dialog) {
  switch (response_id) {
    case GTK_RESPONSE_OK:
      // The first arg is the prompt text and the second is true if we want to
      // suppress additional popups from the page.
      app_modal_dialog->OnAccept(GetPromptText(dialog),
                                 ShouldSuppressJSDialogs(dialog));
      break;

    case GTK_RESPONSE_CANCEL:
    case GTK_RESPONSE_DELETE_EVENT: // User hit the X on the dialog.
      app_modal_dialog->OnCancel();
      break;

    default:
      NOTREACHED();
  }
  gtk_widget_destroy(GTK_WIDGET(dialog));

  // Now that the dialog is gone, we can put all the  windows into separate
  // window groups so other dialogs are no longer app modal.
  for (BrowserList::const_iterator it = BrowserList::begin();
       it != BrowserList::end(); ++it) {
    GtkWindowGroup* window_group = gtk_window_group_new();
    gtk_window_group_add_window(window_group,
                                (*it)->window()->GetNativeHandle());
    g_object_unref(window_group);
  }

  delete app_modal_dialog;
}

}  // namespace

AppModalDialog::~AppModalDialog() {
}

void AppModalDialog::CreateAndShowDialog() {
  GtkButtonsType buttons = GTK_BUTTONS_NONE;
  GtkMessageType message_type = GTK_MESSAGE_OTHER;
  // We add in the OK button manually later because we want to focus it
  // explicitly.
  switch (dialog_flags_) {
    case MessageBoxFlags::kIsJavascriptAlert:
      buttons = GTK_BUTTONS_NONE;
      message_type = GTK_MESSAGE_WARNING;
      break;

    case MessageBoxFlags::kIsJavascriptConfirm:
      if (is_before_unload_dialog_) {
        // onbeforeunload also uses a confirm prompt, it just has custom
        // buttons.  We add the buttons using gtk_dialog_add_button below.
        buttons = GTK_BUTTONS_NONE;
      } else {
        buttons = GTK_BUTTONS_CANCEL;
      }
      message_type = GTK_MESSAGE_QUESTION;
      break;

    case MessageBoxFlags::kIsJavascriptPrompt:
      buttons = GTK_BUTTONS_CANCEL;
      message_type = GTK_MESSAGE_QUESTION;
      break;

    default:
      NOTREACHED();
  }

  // We want the alert to be app modal so put all the browser windows into the
  // same window group.
  GtkWindowGroup* window_group = gtk_window_group_new();
  for (BrowserList::const_iterator it = BrowserList::begin();
       it != BrowserList::end(); ++it) {
    gtk_window_group_add_window(window_group,
                                (*it)->window()->GetNativeHandle());
  }
  g_object_unref(window_group);

  gfx::NativeWindow window = client_->GetMessageBoxRootWindow();
  dialog_ = gtk_message_dialog_new(window, GTK_DIALOG_MODAL,
      message_type, buttons, "%s", WideToUTF8(message_text_).c_str());
  gtk_util::ApplyMessageDialogQuirks(dialog_);
  gtk_window_set_title(GTK_WINDOW(dialog_), WideToUTF8(title_).c_str());

  // Adjust content area as needed.  Set up the prompt text entry or
  // suppression check box.
  if (MessageBoxFlags::kIsJavascriptPrompt == dialog_flags_) {
    // TODO(tc): Replace with gtk_dialog_get_content_area() when using GTK 2.14+
    GtkWidget* contents_vbox = GTK_DIALOG(dialog_)->vbox;
    GtkWidget* text_box = gtk_entry_new();
    gtk_entry_set_text(GTK_ENTRY(text_box),
                       WideToUTF8(default_prompt_text_).c_str());
    gtk_box_pack_start(GTK_BOX(contents_vbox), text_box, TRUE, TRUE, 0);
    g_object_set_data(G_OBJECT(dialog_), kPromptTextId, text_box);
    gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE);
  }

  if (display_suppress_checkbox_) {
    GtkWidget* contents_vbox = GTK_DIALOG(dialog_)->vbox;
    GtkWidget* check_box = gtk_check_button_new_with_label(
        l10n_util::GetStringUTF8(
            IDS_JAVASCRIPT_MESSAGEBOX_SUPPRESS_OPTION).c_str());
    gtk_box_pack_start(GTK_BOX(contents_vbox), check_box, TRUE, TRUE, 0);
    g_object_set_data(G_OBJECT(dialog_), kSuppressCheckboxId, check_box);
  }

  // Adjust buttons/action area as needed.
  if (is_before_unload_dialog_) {
    std::string button_text = l10n_util::GetStringUTF8(
        IDS_BEFOREUNLOAD_MESSAGEBOX_OK_BUTTON_LABEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog_), button_text.c_str(),
                          GTK_RESPONSE_OK);

    button_text = l10n_util::GetStringUTF8(
        IDS_BEFOREUNLOAD_MESSAGEBOX_CANCEL_BUTTON_LABEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog_), button_text.c_str(),
                          GTK_RESPONSE_CANCEL);
  } else {
    // Add the OK button and focus it.
    GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(dialog_),
        GTK_STOCK_OK, GTK_RESPONSE_OK);
    if (MessageBoxFlags::kIsJavascriptPrompt != dialog_flags_)
      gtk_widget_grab_focus(ok_button);
  }

  gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_OK);
  g_signal_connect(dialog_, "response", G_CALLBACK(OnDialogResponse), this);

  // Suggest a minimum size.
  gtk_widget_realize(dialog_);
  gint width;
  GtkRequisition req;
  gtk_widget_size_request(dialog_, &req);
  gtk_util::GetWidgetSizeFromResources(dialog_, IDS_ALERT_DIALOG_WIDTH_CHARS, 0,
                                       &width, NULL);
  if (width > req.width)
    gtk_widget_set_size_request(dialog_, width, -1);

  gtk_widget_show_all(GTK_WIDGET(GTK_DIALOG(dialog_)));
}

void AppModalDialog::ActivateModalDialog() {
  gtk_window_present(GTK_WINDOW(dialog_));
}

void AppModalDialog::CloseModalDialog() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_DELETE_EVENT, this);
}

int AppModalDialog::GetDialogButtons() {
  switch (dialog_flags_) {
    case MessageBoxFlags::kIsJavascriptAlert:
      return MessageBoxFlags::DIALOGBUTTON_OK;

    case MessageBoxFlags::kIsJavascriptConfirm:
      return MessageBoxFlags::DIALOGBUTTON_OK |
             MessageBoxFlags::DIALOGBUTTON_CANCEL;

    case MessageBoxFlags::kIsJavascriptPrompt:
      return MessageBoxFlags::DIALOGBUTTON_OK;

    default:
      NOTREACHED();
      return 0;
  }
}

void AppModalDialog::AcceptWindow() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_OK, this);
}

void AppModalDialog::CancelWindow() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_CANCEL, this);
}
<commit_msg>GTK: fix sizing of js alert dialogs.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/app_modal_dialog.h"

#include <gtk/gtk.h>

#include "app/l10n_util.h"
#include "app/message_box_flags.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/gtk_util.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

namespace {

// We stash pointers to widgets on the gtk_dialog so we can refer to them
// after dialog creation.
const char kPromptTextId[] = "chrome_prompt_text";
const char kSuppressCheckboxId[] = "chrome_suppress_checkbox";

// If there's a text entry in the dialog, get the text from the first one and
// return it.
std::wstring GetPromptText(GtkDialog* dialog) {
  GtkWidget* widget = static_cast<GtkWidget*>(
      g_object_get_data(G_OBJECT(dialog), kPromptTextId));
  if (widget)
    return UTF8ToWide(gtk_entry_get_text(GTK_ENTRY(widget)));
  return std::wstring();
}

// If there's a toggle button in the dialog, return the toggled state.
// Otherwise, return false.
bool ShouldSuppressJSDialogs(GtkDialog* dialog) {
  GtkWidget* widget = static_cast<GtkWidget*>(
      g_object_get_data(G_OBJECT(dialog), kSuppressCheckboxId));
  if (widget)
    return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
  return false;
}

void OnDialogResponse(GtkDialog* dialog, gint response_id,
                      AppModalDialog* app_modal_dialog) {
  switch (response_id) {
    case GTK_RESPONSE_OK:
      // The first arg is the prompt text and the second is true if we want to
      // suppress additional popups from the page.
      app_modal_dialog->OnAccept(GetPromptText(dialog),
                                 ShouldSuppressJSDialogs(dialog));
      break;

    case GTK_RESPONSE_CANCEL:
    case GTK_RESPONSE_DELETE_EVENT: // User hit the X on the dialog.
      app_modal_dialog->OnCancel();
      break;

    default:
      NOTREACHED();
  }
  gtk_widget_destroy(GTK_WIDGET(dialog));

  // Now that the dialog is gone, we can put all the  windows into separate
  // window groups so other dialogs are no longer app modal.
  for (BrowserList::const_iterator it = BrowserList::begin();
       it != BrowserList::end(); ++it) {
    GtkWindowGroup* window_group = gtk_window_group_new();
    gtk_window_group_add_window(window_group,
                                (*it)->window()->GetNativeHandle());
    g_object_unref(window_group);
  }

  delete app_modal_dialog;
}

}  // namespace

AppModalDialog::~AppModalDialog() {
}

void AppModalDialog::CreateAndShowDialog() {
  GtkButtonsType buttons = GTK_BUTTONS_NONE;
  GtkMessageType message_type = GTK_MESSAGE_OTHER;
  // We add in the OK button manually later because we want to focus it
  // explicitly.
  switch (dialog_flags_) {
    case MessageBoxFlags::kIsJavascriptAlert:
      buttons = GTK_BUTTONS_NONE;
      message_type = GTK_MESSAGE_WARNING;
      break;

    case MessageBoxFlags::kIsJavascriptConfirm:
      if (is_before_unload_dialog_) {
        // onbeforeunload also uses a confirm prompt, it just has custom
        // buttons.  We add the buttons using gtk_dialog_add_button below.
        buttons = GTK_BUTTONS_NONE;
      } else {
        buttons = GTK_BUTTONS_CANCEL;
      }
      message_type = GTK_MESSAGE_QUESTION;
      break;

    case MessageBoxFlags::kIsJavascriptPrompt:
      buttons = GTK_BUTTONS_CANCEL;
      message_type = GTK_MESSAGE_QUESTION;
      break;

    default:
      NOTREACHED();
  }

  // We want the alert to be app modal so put all the browser windows into the
  // same window group.
  GtkWindowGroup* window_group = gtk_window_group_new();
  for (BrowserList::const_iterator it = BrowserList::begin();
       it != BrowserList::end(); ++it) {
    gtk_window_group_add_window(window_group,
                                (*it)->window()->GetNativeHandle());
  }
  g_object_unref(window_group);

  gfx::NativeWindow window = client_->GetMessageBoxRootWindow();
  dialog_ = gtk_message_dialog_new(window, GTK_DIALOG_MODAL,
      message_type, buttons, "%s", WideToUTF8(message_text_).c_str());
  gtk_util::ApplyMessageDialogQuirks(dialog_);
  gtk_window_set_title(GTK_WINDOW(dialog_), WideToUTF8(title_).c_str());

  // Adjust content area as needed.  Set up the prompt text entry or
  // suppression check box.
  if (MessageBoxFlags::kIsJavascriptPrompt == dialog_flags_) {
    // TODO(tc): Replace with gtk_dialog_get_content_area() when using GTK 2.14+
    GtkWidget* contents_vbox = GTK_DIALOG(dialog_)->vbox;
    GtkWidget* text_box = gtk_entry_new();
    gtk_entry_set_text(GTK_ENTRY(text_box),
                       WideToUTF8(default_prompt_text_).c_str());
    gtk_box_pack_start(GTK_BOX(contents_vbox), text_box, TRUE, TRUE, 0);
    g_object_set_data(G_OBJECT(dialog_), kPromptTextId, text_box);
    gtk_entry_set_activates_default(GTK_ENTRY(text_box), TRUE);
  }

  if (display_suppress_checkbox_) {
    GtkWidget* contents_vbox = GTK_DIALOG(dialog_)->vbox;
    GtkWidget* check_box = gtk_check_button_new_with_label(
        l10n_util::GetStringUTF8(
            IDS_JAVASCRIPT_MESSAGEBOX_SUPPRESS_OPTION).c_str());
    gtk_box_pack_start(GTK_BOX(contents_vbox), check_box, TRUE, TRUE, 0);
    g_object_set_data(G_OBJECT(dialog_), kSuppressCheckboxId, check_box);
  }

  // Adjust buttons/action area as needed.
  if (is_before_unload_dialog_) {
    std::string button_text = l10n_util::GetStringUTF8(
        IDS_BEFOREUNLOAD_MESSAGEBOX_OK_BUTTON_LABEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog_), button_text.c_str(),
                          GTK_RESPONSE_OK);

    button_text = l10n_util::GetStringUTF8(
        IDS_BEFOREUNLOAD_MESSAGEBOX_CANCEL_BUTTON_LABEL);
    gtk_dialog_add_button(GTK_DIALOG(dialog_), button_text.c_str(),
                          GTK_RESPONSE_CANCEL);
  } else {
    // Add the OK button and focus it.
    GtkWidget* ok_button = gtk_dialog_add_button(GTK_DIALOG(dialog_),
        GTK_STOCK_OK, GTK_RESPONSE_OK);
    if (MessageBoxFlags::kIsJavascriptPrompt != dialog_flags_)
      gtk_widget_grab_focus(ok_button);
  }

  gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_OK);
  g_signal_connect(dialog_, "response", G_CALLBACK(OnDialogResponse), this);

  gtk_widget_show_all(GTK_WIDGET(GTK_DIALOG(dialog_)));

  // Suggest a minimum size.
  gint width;
  GtkRequisition req;
  gtk_widget_size_request(dialog_, &req);
  gtk_util::GetWidgetSizeFromResources(dialog_, IDS_ALERT_DIALOG_WIDTH_CHARS, 0,
                                       &width, NULL);
  if (width > req.width)
    gtk_widget_set_size_request(dialog_, width, -1);
}

void AppModalDialog::ActivateModalDialog() {
  gtk_window_present(GTK_WINDOW(dialog_));
}

void AppModalDialog::CloseModalDialog() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_DELETE_EVENT, this);
}

int AppModalDialog::GetDialogButtons() {
  switch (dialog_flags_) {
    case MessageBoxFlags::kIsJavascriptAlert:
      return MessageBoxFlags::DIALOGBUTTON_OK;

    case MessageBoxFlags::kIsJavascriptConfirm:
      return MessageBoxFlags::DIALOGBUTTON_OK |
             MessageBoxFlags::DIALOGBUTTON_CANCEL;

    case MessageBoxFlags::kIsJavascriptPrompt:
      return MessageBoxFlags::DIALOGBUTTON_OK;

    default:
      NOTREACHED();
      return 0;
  }
}

void AppModalDialog::AcceptWindow() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_OK, this);
}

void AppModalDialog::CancelWindow() {
  OnDialogResponse(GTK_DIALOG(dialog_), GTK_RESPONSE_CANCEL, this);
}
<|endoftext|>


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

#include "chrome/browser/gtk/first_run_dialog.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/message_loop.h"
#include "chrome/app/breakpad_linux.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/common/gtk_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/google_update_settings.h"
#include "grit/generated_resources.h"
#include "grit/google_chrome_strings.h"
#include "grit/locale_settings.h"

// static
bool FirstRunDialog::Show(Profile* profile) {
  int response = -1;
  new FirstRunDialog(profile, response);
  return (response == GTK_RESPONSE_ACCEPT);
}

FirstRunDialog::FirstRunDialog(Profile* profile, int& response)
    : dialog_(NULL), report_crashes_(NULL), make_default_(NULL),
      import_data_(NULL), import_profile_(NULL), profile_(profile),
      response_(response), importer_host_(new ImporterHost()) {
  dialog_ = gtk_dialog_new_with_buttons(
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_TITLE).c_str(),
      NULL,  // No parent
      (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_OK).c_str(),
      GTK_RESPONSE_ACCEPT,
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_CANCEL).c_str(),
      GTK_RESPONSE_REJECT,
      NULL);
  gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE);
  int width, height;
  gtk_util::GetWidgetSizeFromResources(dialog_,
                                       IDS_FIRSTRUN_DIALOG_WIDTH_CHARS,
                                       IDS_FIRSTRUN_DIALOG_HEIGHT_LINES,
                                       &width, &height);
  gtk_window_set_default_size(GTK_WINDOW(dialog_), width, height);
  g_signal_connect(G_OBJECT(dialog_), "delete-event",
                   G_CALLBACK(gtk_widget_hide_on_delete), NULL);

  GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
  gtk_box_set_spacing(GTK_BOX(content_area), 18);

  GtkWidget* vbox = gtk_vbox_new(FALSE, 12);

#if defined(GOOGLE_CHROME_BUILD)
  // TODO(port): remove this warning before beta release when we have all the
  // privacy features working.
  GtkWidget* privacy_label = gtk_label_new(
      "This version of Google Chrome for Linux is not appropriate for "
      "general consumer use.  Certain privacy features are unavailable "
      "at this time as described in our privacy policy at");
  gtk_misc_set_alignment(GTK_MISC(privacy_label), 0, 0);
  gtk_label_set_line_wrap(GTK_LABEL(privacy_label), TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), privacy_label, FALSE, FALSE, 0);

  GtkWidget* url_label = gtk_label_new(NULL);
  gtk_label_set_markup(GTK_LABEL(url_label),
      "<tt>http://www.google.com/chrome/intl/en/privacy_linux.html</tt>");
  // Set selectable to allow copy and paste.
  gtk_label_set_selectable(GTK_LABEL(url_label), TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), url_label, FALSE, FALSE, 0);

  report_crashes_ = gtk_check_button_new();
  GtkWidget* check_label = gtk_label_new(
      l10n_util::GetStringUTF8(IDS_OPTIONS_ENABLE_LOGGING).c_str());
  gtk_label_set_line_wrap(GTK_LABEL(check_label), TRUE);
  gtk_container_add(GTK_CONTAINER(report_crashes_), check_label);
  gtk_box_pack_start(GTK_BOX(vbox), report_crashes_, FALSE, FALSE, 0);
#endif

  make_default_ = gtk_check_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_DEFAULT_BROWSER).c_str());
  gtk_box_pack_start(GTK_BOX(vbox), make_default_, FALSE, FALSE, 0);

  GtkWidget* combo_hbox = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing);
  import_data_ = gtk_check_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_IMPORT).c_str());
  gtk_box_pack_start(GTK_BOX(combo_hbox), import_data_, FALSE, FALSE, 0);
  import_profile_ = gtk_combo_box_new_text();
  gtk_box_pack_start(GTK_BOX(combo_hbox), import_profile_, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), combo_hbox, FALSE, FALSE, 0);

  // Detect any supported browsers that we can import from and fill
  // up the combo box. If none found, disable import data checkbox.
  int profiles_count = importer_host_->GetAvailableProfileCount();
  if (profiles_count > 0) {
    for (int i = 0; i < profiles_count; i++) {
      std::wstring profile = importer_host_->GetSourceProfileNameAt(i);
      gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
                                WideToUTF8(profile).c_str());
    }
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(import_data_), TRUE);
    gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
  } else {
    gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
        l10n_util::GetStringUTF8(IDS_IMPORT_NO_PROFILE_FOUND).c_str());
    gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
    gtk_widget_set_sensitive(import_data_, FALSE);
    gtk_widget_set_sensitive(import_profile_, FALSE);
  }

  gtk_box_pack_start(GTK_BOX(content_area), vbox, FALSE, FALSE, 0);

  // TODO(port): it should be sufficient to just run the dialog:
  // int response = gtk_dialog_run(GTK_DIALOG(dialog));
  // but that spins a nested message loop and hoses us.  :(
  // http://code.google.com/p/chromium/issues/detail?id=12552
  // Instead, run a loop and extract the response manually.
  g_signal_connect(G_OBJECT(dialog_), "response",
                   G_CALLBACK(HandleOnResponseDialog), this);
  gtk_widget_show_all(dialog_);
  MessageLoop::current()->Run();
}

void FirstRunDialog::OnDialogResponse(GtkWidget* widget, int response) {
  gtk_widget_hide_all(dialog_);
  response_ = response;

  if (response == GTK_RESPONSE_ACCEPT) {
    // Mark that first run has ran.
    FirstRun::CreateSentinel();

    // Check if user has opted into reporting.
    if (report_crashes_ &&
        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(report_crashes_))) {
      if (GoogleUpdateSettings::SetCollectStatsConsent(true)) {
        PrefService* local_state = g_browser_process->local_state();
        if (local_state)
          local_state->SetBoolean(prefs::kMetricsReportingEnabled, true);
        else
          NOTREACHED();
        InitCrashReporter();
      }
    } else {
      GoogleUpdateSettings::SetCollectStatsConsent(false);
    }

    // If selected set as default browser.
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(make_default_)))
      ShellIntegration::SetAsDefaultBrowser();

    // Import data if selected.
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(import_data_))) {
      const ProfileInfo& source_profile =
          importer_host_->GetSourceProfileInfoAt(
          gtk_combo_box_get_active(GTK_COMBO_BOX(import_profile_)));
      int items = SEARCH_ENGINES + HISTORY + FAVORITES + HOME_PAGE + PASSWORDS;
      // TODO(port): Should we do the actual import in a new process like
      // Windows?
      StartImportingWithUI(GTK_WINDOW(dialog_), items, importer_host_.get(),
                           source_profile, profile_, this, true);
    } else {
      // We are done, so close Window.
      FirstRunDone();
    }
  }
}

void FirstRunDialog::FirstRunDone() {
  // Set preference to show first run bubble and welcome page.
  FirstRun::SetShowFirstRunBubblePref();
  FirstRun::SetShowWelcomePagePref();

  gtk_widget_destroy(dialog_);
  MessageLoop::current()->Quit();
}
<commit_msg>Need to cleanup first run dialog on cancellation too.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/first_run_dialog.h"

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/message_loop.h"
#include "chrome/app/breakpad_linux.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/common/gtk_util.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/google_update_settings.h"
#include "grit/generated_resources.h"
#include "grit/google_chrome_strings.h"
#include "grit/locale_settings.h"

// static
bool FirstRunDialog::Show(Profile* profile) {
  int response = -1;
  new FirstRunDialog(profile, response);
  return (response == GTK_RESPONSE_ACCEPT);
}

FirstRunDialog::FirstRunDialog(Profile* profile, int& response)
    : dialog_(NULL), report_crashes_(NULL), make_default_(NULL),
      import_data_(NULL), import_profile_(NULL), profile_(profile),
      response_(response), importer_host_(new ImporterHost()) {
  dialog_ = gtk_dialog_new_with_buttons(
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_TITLE).c_str(),
      NULL,  // No parent
      (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_OK).c_str(),
      GTK_RESPONSE_ACCEPT,
      l10n_util::GetStringUTF8(IDS_FIRSTRUN_DLG_CANCEL).c_str(),
      GTK_RESPONSE_REJECT,
      NULL);
  gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE);
  int width, height;
  gtk_util::GetWidgetSizeFromResources(dialog_,
                                       IDS_FIRSTRUN_DIALOG_WIDTH_CHARS,
                                       IDS_FIRSTRUN_DIALOG_HEIGHT_LINES,
                                       &width, &height);
  gtk_window_set_default_size(GTK_WINDOW(dialog_), width, height);
  g_signal_connect(G_OBJECT(dialog_), "delete-event",
                   G_CALLBACK(gtk_widget_hide_on_delete), NULL);

  GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
  gtk_box_set_spacing(GTK_BOX(content_area), 18);

  GtkWidget* vbox = gtk_vbox_new(FALSE, 12);

#if defined(GOOGLE_CHROME_BUILD)
  // TODO(port): remove this warning before beta release when we have all the
  // privacy features working.
  GtkWidget* privacy_label = gtk_label_new(
      "This version of Google Chrome for Linux is not appropriate for "
      "general consumer use.  Certain privacy features are unavailable "
      "at this time as described in our privacy policy at");
  gtk_misc_set_alignment(GTK_MISC(privacy_label), 0, 0);
  gtk_label_set_line_wrap(GTK_LABEL(privacy_label), TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), privacy_label, FALSE, FALSE, 0);

  GtkWidget* url_label = gtk_label_new(NULL);
  gtk_label_set_markup(GTK_LABEL(url_label),
      "<tt>http://www.google.com/chrome/intl/en/privacy_linux.html</tt>");
  // Set selectable to allow copy and paste.
  gtk_label_set_selectable(GTK_LABEL(url_label), TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), url_label, FALSE, FALSE, 0);

  report_crashes_ = gtk_check_button_new();
  GtkWidget* check_label = gtk_label_new(
      l10n_util::GetStringUTF8(IDS_OPTIONS_ENABLE_LOGGING).c_str());
  gtk_label_set_line_wrap(GTK_LABEL(check_label), TRUE);
  gtk_container_add(GTK_CONTAINER(report_crashes_), check_label);
  gtk_box_pack_start(GTK_BOX(vbox), report_crashes_, FALSE, FALSE, 0);
#endif

  make_default_ = gtk_check_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_DEFAULT_BROWSER).c_str());
  gtk_box_pack_start(GTK_BOX(vbox), make_default_, FALSE, FALSE, 0);

  GtkWidget* combo_hbox = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing);
  import_data_ = gtk_check_button_new_with_label(
      l10n_util::GetStringUTF8(IDS_FR_CUSTOMIZE_IMPORT).c_str());
  gtk_box_pack_start(GTK_BOX(combo_hbox), import_data_, FALSE, FALSE, 0);
  import_profile_ = gtk_combo_box_new_text();
  gtk_box_pack_start(GTK_BOX(combo_hbox), import_profile_, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), combo_hbox, FALSE, FALSE, 0);

  // Detect any supported browsers that we can import from and fill
  // up the combo box. If none found, disable import data checkbox.
  int profiles_count = importer_host_->GetAvailableProfileCount();
  if (profiles_count > 0) {
    for (int i = 0; i < profiles_count; i++) {
      std::wstring profile = importer_host_->GetSourceProfileNameAt(i);
      gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
                                WideToUTF8(profile).c_str());
    }
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(import_data_), TRUE);
    gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
  } else {
    gtk_combo_box_append_text(GTK_COMBO_BOX(import_profile_),
        l10n_util::GetStringUTF8(IDS_IMPORT_NO_PROFILE_FOUND).c_str());
    gtk_combo_box_set_active(GTK_COMBO_BOX(import_profile_), 0);
    gtk_widget_set_sensitive(import_data_, FALSE);
    gtk_widget_set_sensitive(import_profile_, FALSE);
  }

  gtk_box_pack_start(GTK_BOX(content_area), vbox, FALSE, FALSE, 0);

  // TODO(port): it should be sufficient to just run the dialog:
  // int response = gtk_dialog_run(GTK_DIALOG(dialog));
  // but that spins a nested message loop and hoses us.  :(
  // http://code.google.com/p/chromium/issues/detail?id=12552
  // Instead, run a loop and extract the response manually.
  g_signal_connect(G_OBJECT(dialog_), "response",
                   G_CALLBACK(HandleOnResponseDialog), this);
  gtk_widget_show_all(dialog_);
  MessageLoop::current()->Run();
}

void FirstRunDialog::OnDialogResponse(GtkWidget* widget, int response) {
  bool import_started = false;
  gtk_widget_hide_all(dialog_);
  response_ = response;

  if (response == GTK_RESPONSE_ACCEPT) {
    // Mark that first run has ran.
    FirstRun::CreateSentinel();

    // Check if user has opted into reporting.
    if (report_crashes_ &&
        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(report_crashes_))) {
      if (GoogleUpdateSettings::SetCollectStatsConsent(true)) {
        PrefService* local_state = g_browser_process->local_state();
        if (local_state)
          local_state->SetBoolean(prefs::kMetricsReportingEnabled, true);
        else
          NOTREACHED();
        InitCrashReporter();
      }
    } else {
      GoogleUpdateSettings::SetCollectStatsConsent(false);
    }

    // If selected set as default browser.
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(make_default_)))
      ShellIntegration::SetAsDefaultBrowser();

    // Import data if selected.
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(import_data_))) {
      const ProfileInfo& source_profile =
          importer_host_->GetSourceProfileInfoAt(
          gtk_combo_box_get_active(GTK_COMBO_BOX(import_profile_)));
      int items = SEARCH_ENGINES + HISTORY + FAVORITES + HOME_PAGE + PASSWORDS;
      // TODO(port): Should we do the actual import in a new process like
      // Windows?
      StartImportingWithUI(GTK_WINDOW(dialog_), items, importer_host_.get(),
                           source_profile, profile_, this, true);
      import_started = true;
    }
  }
  if (!import_started)
    FirstRunDone();
}

void FirstRunDialog::FirstRunDone() {
  // Set preference to show first run bubble and welcome page.
  FirstRun::SetShowFirstRunBubblePref();
  FirstRun::SetShowWelcomePagePref();

  gtk_widget_destroy(dialog_);
  MessageLoop::current()->Quit();
}
<|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/ui/webui/devtools_ui.h"

#include <string>

#include "base/command_line.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/devtools/devtools_target_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
#include "ui/base/resource/resource_bundle.h"

using content::BrowserThread;
using content::WebContents;

namespace {

std::string PathWithoutParams(const std::string& path) {
  return GURL(std::string("chrome-devtools://devtools/") + path)
      .path().substr(1);
}

const char kRemoteFrontendDomain[] = "chrome-devtools-frontend.appspot.com";
const char kRemoteFrontendBase[] =
    "https://chrome-devtools-frontend.appspot.com/";
const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";

#if defined(DEBUG_DEVTOOLS)
// Local frontend url provided by InspectUI.
const char kFallbackFrontendURL[] =
    "chrome-devtools://devtools/bundled/inspector.html";
#else
// URL causing the DevTools window to display a plain text warning.
const char kFallbackFrontendURL[] =
    "data:text/plain,Cannot load DevTools frontend from an untrusted origin";
#endif  // defined(DEBUG_DEVTOOLS)

const char kRemoteOpenPrefix[] = "remote/open";

#if defined(DEBUG_DEVTOOLS)
const char kLocalSerial[] = "local";
#endif  // defined(DEBUG_DEVTOOLS)

// FetchRequest ---------------------------------------------------------------

class FetchRequest : public net::URLFetcherDelegate {
 public:
  FetchRequest(net::URLRequestContextGetter* request_context,
               const GURL& url,
               const content::URLDataSource::GotDataCallback& callback);

 private:
  ~FetchRequest() override {}
  void OnURLFetchComplete(const net::URLFetcher* source) override;
  scoped_ptr<net::URLFetcher> fetcher_;
  content::URLDataSource::GotDataCallback callback_;
};

FetchRequest::FetchRequest(
    net::URLRequestContextGetter* request_context,
    const GURL& url,
    const content::URLDataSource::GotDataCallback& callback)
    : callback_(callback) {
  if (!url.is_valid()) {
    OnURLFetchComplete(NULL);
    return;
  }

  fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::GET, this));
  fetcher_->SetRequestContext(request_context);
  fetcher_->Start();
}

void FetchRequest::OnURLFetchComplete(const net::URLFetcher* source) {
  std::string response;
  if (source)
    source->GetResponseAsString(&response);
  else
    response = kHttpNotFound;

  callback_.Run(base::RefCountedString::TakeString(&response));
  delete this;
}

// DevToolsDataSource ---------------------------------------------------------

std::string GetMimeTypeForPath(const std::string& path) {
  std::string filename = PathWithoutParams(path);
  if (EndsWith(filename, ".html", false)) {
    return "text/html";
  } else if (EndsWith(filename, ".css", false)) {
    return "text/css";
  } else if (EndsWith(filename, ".js", false)) {
    return "application/javascript";
  } else if (EndsWith(filename, ".png", false)) {
    return "image/png";
  } else if (EndsWith(filename, ".gif", false)) {
    return "image/gif";
  } else if (EndsWith(filename, ".manifest", false)) {
    return "text/cache-manifest";
  }
  return "text/html";
}

// An URLDataSource implementation that handles chrome-devtools://devtools/
// requests. Three types of requests could be handled based on the URL path:
// 1. /bundled/: bundled DevTools frontend is served.
// 2. /remote/: remote DevTools frontend is served from App Engine.
// 3. /remote/open/: query is URL which is opened on remote device.
class DevToolsDataSource : public content::URLDataSource {
 public:
  explicit DevToolsDataSource(net::URLRequestContextGetter* request_context);

  // content::URLDataSource implementation.
  std::string GetSource() const override;

  void StartDataRequest(
      const std::string& path,
      int render_process_id,
      int render_frame_id,
      const content::URLDataSource::GotDataCallback& callback) override;

 private:
  // content::URLDataSource overrides.
  std::string GetMimeType(const std::string& path) const override;
  bool ShouldAddContentSecurityPolicy() const override;
  bool ShouldDenyXFrameOptions() const override;
  bool ShouldServeMimeTypeAsContentTypeHeader() const override;

  // Serves bundled DevTools frontend from ResourceBundle.
  void StartBundledDataRequest(
      const std::string& path,
      int render_process_id,
      int render_frame_id,
      const content::URLDataSource::GotDataCallback& callback);

  // Serves remote DevTools frontend from hard-coded App Engine domain.
  void StartRemoteDataRequest(
      const std::string& path,
      int render_process_id,
      int render_frame_id,
      const content::URLDataSource::GotDataCallback& callback);

  ~DevToolsDataSource() override {}
  scoped_refptr<net::URLRequestContextGetter> request_context_;

  DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
};

DevToolsDataSource::DevToolsDataSource(
    net::URLRequestContextGetter* request_context)
    : request_context_(request_context) {
}

std::string DevToolsDataSource::GetSource() const {
  return chrome::kChromeUIDevToolsHost;
}

void DevToolsDataSource::StartDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  // Serve request from local bundle.
  std::string bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath);
  bundled_path_prefix += "/";
  if (StartsWithASCII(path, bundled_path_prefix, false)) {
    StartBundledDataRequest(path.substr(bundled_path_prefix.length()),
                            render_process_id, render_frame_id, callback);
    return;
  }

  // Serve static response while connecting to the remote device.
  if (StartsWithASCII(path, kRemoteOpenPrefix, false)) {
    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableDevToolsExperiments)) {
      callback.Run(NULL);
      return;
    }
    std::string response = "Connecting to the device...";
    callback.Run(base::RefCountedString::TakeString(&response));
    return;
  }

  // Serve request from remote location.
  std::string remote_path_prefix(chrome::kChromeUIDevToolsRemotePath);
  remote_path_prefix += "/";
  if (StartsWithASCII(path, remote_path_prefix, false)) {
    StartRemoteDataRequest(path.substr(remote_path_prefix.length()),
                           render_process_id, render_frame_id, callback);
    return;
  }

  callback.Run(NULL);
}

std::string DevToolsDataSource::GetMimeType(const std::string& path) const {
  return GetMimeTypeForPath(path);
}

bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const {
  return false;
}

bool DevToolsDataSource::ShouldDenyXFrameOptions() const {
  return false;
}

bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
  return true;
}

void DevToolsDataSource::StartBundledDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  std::string filename = PathWithoutParams(path);

  int resource_id =
      content::DevToolsHttpHandler::GetFrontendResourceId(filename);

  DLOG_IF(WARNING, resource_id == -1)
      << "Unable to find dev tool resource: " << filename
      << ". If you compiled with debug_devtools=1, try running with "
         "--debug-devtools.";
  const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  scoped_refptr<base::RefCountedStaticMemory> bytes(rb.LoadDataResourceBytes(
      resource_id));
  callback.Run(bytes.get());
}

void DevToolsDataSource::StartRemoteDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  GURL url = GURL(kRemoteFrontendBase + path);
  CHECK_EQ(url.host(), kRemoteFrontendDomain);
  new FetchRequest(request_context_.get(), url, callback);
}

// OpenRemotePageRequest ------------------------------------------------------

class OpenRemotePageRequest : public DevToolsAndroidBridge::DeviceListListener {
 public:
  OpenRemotePageRequest(
      Profile* profile,
      const std::string url,
      const DevToolsAndroidBridge::RemotePageCallback& callback);
  ~OpenRemotePageRequest() override {}

 private:
  // DevToolsAndroidBridge::Listener overrides.
  void DeviceListChanged(
      const DevToolsAndroidBridge::RemoteDevices& devices) override;

  bool OpenInBrowser(
      scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser);
  void RemotePageOpened(scoped_refptr<DevToolsAndroidBridge::RemotePage> page);

  std::string url_;
  DevToolsAndroidBridge::RemotePageCallback callback_;
  bool opening_;
  DevToolsAndroidBridge* android_bridge_;

  DISALLOW_COPY_AND_ASSIGN(OpenRemotePageRequest);
};

OpenRemotePageRequest::OpenRemotePageRequest(
    Profile* profile,
    const std::string url,
    const DevToolsAndroidBridge::RemotePageCallback& callback)
    : url_(url),
      callback_(callback),
      opening_(false),
      android_bridge_(
          DevToolsAndroidBridge::Factory::GetForProfile(profile)) {
  android_bridge_->AddDeviceListListener(this);
}

void OpenRemotePageRequest::DeviceListChanged(
    const DevToolsAndroidBridge::RemoteDevices& devices) {
  if (opening_)
    return;

  for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
      devices.begin(); dit != devices.end(); ++dit) {
    DevToolsAndroidBridge::RemoteDevice* device = dit->get();
    if (!device->is_connected())
      continue;

    DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
    for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
        browsers.begin(); bit != browsers.end(); ++bit) {
      if (OpenInBrowser(bit->get())) {
        opening_ = true;
        return;
      }
    }
  }
}

bool OpenRemotePageRequest::OpenInBrowser(
    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser) {
  if (!browser->IsChrome())
    return false;
#if defined(DEBUG_DEVTOOLS)
  if (browser->serial() == kLocalSerial)
    return false;
#endif  // defined(DEBUG_DEVTOOLS)
  android_bridge_->OpenRemotePage(
      browser,
      url_,
      base::Bind(&OpenRemotePageRequest::RemotePageOpened,
                 base::Unretained(this)));
  return true;
}

void OpenRemotePageRequest::RemotePageOpened(
    scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
  callback_.Run(page);
  android_bridge_->RemoveDeviceListListener(this);
  delete this;
}

}  // namespace

// DevToolsUI -----------------------------------------------------------------

// static
GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
  GURL url(frontend_url);
  if (!url.is_valid() || url.host() != kRemoteFrontendDomain)
    return GURL(kFallbackFrontendURL);
  return GURL(base::StringPrintf("%s://%s/%s/%s",
              content::kChromeDevToolsScheme,
              chrome::kChromeUIDevToolsHost,
              chrome::kChromeUIDevToolsRemotePath,
              url.path().substr(1).c_str()));
}

DevToolsUI::DevToolsUI(content::WebUI* web_ui)
    : WebUIController(web_ui),
      content::WebContentsObserver(web_ui->GetWebContents()),
      bindings_(web_ui->GetWebContents()),
      weak_factory_(this) {
  web_ui->SetBindings(0);
  Profile* profile = Profile::FromWebUI(web_ui);
  content::URLDataSource::Add(
      profile,
      new DevToolsDataSource(profile->GetRequestContext()));
}

DevToolsUI::~DevToolsUI() {
}

void DevToolsUI::NavigationEntryCommitted(
    const content::LoadCommittedDetails& load_details) {
  content::NavigationEntry* entry = load_details.entry;
  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableDevToolsExperiments)) {
    return;
  }

  if (entry->GetVirtualURL() == remote_frontend_loading_url_) {
    remote_frontend_loading_url_ = GURL();
    return;
  }

  std::string path = entry->GetVirtualURL().path().substr(1);
  if (!StartsWithASCII(path, kRemoteOpenPrefix, false))
    return;

  bindings_.Detach();
  remote_page_opening_url_ = entry->GetVirtualURL();
  new OpenRemotePageRequest(Profile::FromWebUI(web_ui()),
                            entry->GetVirtualURL().query(),
                            base::Bind(&DevToolsUI::RemotePageOpened,
                                       weak_factory_.GetWeakPtr(),
                                       entry->GetVirtualURL()));
}

void DevToolsUI::RemotePageOpened(
    const GURL& virtual_url,
    scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
  // Already navigated away while connecting to remote device.
  if (remote_page_opening_url_ != virtual_url)
    return;

  remote_page_opening_url_ = GURL();

  Profile* profile = Profile::FromWebUI(web_ui());
  GURL url = DevToolsUIBindings::ApplyThemeToURL(profile,
      DevToolsUI::GetProxyURL(page->frontend_url()));

  content::NavigationController& navigation_controller =
      web_ui()->GetWebContents()->GetController();
  content::NavigationController::LoadURLParams params(url);
  params.should_replace_current_entry = true;
  remote_frontend_loading_url_ = virtual_url;
  navigation_controller.LoadURLWithParams(params);
  navigation_controller.GetPendingEntry()->SetVirtualURL(virtual_url);

  DevToolsAndroidBridge* bridge =
      DevToolsAndroidBridge::Factory::GetForProfile(profile);
  scoped_ptr<DevToolsTargetImpl> target(bridge->CreatePageTarget(page));
  bindings_.AttachTo(target->GetAgentHost());
}
<commit_msg>[DevTools] Cancel FetchRequests on DevToolsDataSource destruction<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/ui/webui/devtools_ui.h"

#include <string>

#include "base/command_line.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/devtools/device/devtools_android_bridge.h"
#include "chrome/browser/devtools/devtools_target_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/devtools_http_handler.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_context_getter.h"
#include "ui/base/resource/resource_bundle.h"

using content::BrowserThread;
using content::WebContents;

namespace {

std::string PathWithoutParams(const std::string& path) {
  return GURL(std::string("chrome-devtools://devtools/") + path)
      .path().substr(1);
}

const char kRemoteFrontendDomain[] = "chrome-devtools-frontend.appspot.com";
const char kRemoteFrontendBase[] =
    "https://chrome-devtools-frontend.appspot.com/";
const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n";

#if defined(DEBUG_DEVTOOLS)
// Local frontend url provided by InspectUI.
const char kFallbackFrontendURL[] =
    "chrome-devtools://devtools/bundled/inspector.html";
#else
// URL causing the DevTools window to display a plain text warning.
const char kFallbackFrontendURL[] =
    "data:text/plain,Cannot load DevTools frontend from an untrusted origin";
#endif  // defined(DEBUG_DEVTOOLS)

const char kRemoteOpenPrefix[] = "remote/open";

#if defined(DEBUG_DEVTOOLS)
const char kLocalSerial[] = "local";
#endif  // defined(DEBUG_DEVTOOLS)

// DevToolsDataSource ---------------------------------------------------------

std::string GetMimeTypeForPath(const std::string& path) {
  std::string filename = PathWithoutParams(path);
  if (EndsWith(filename, ".html", false)) {
    return "text/html";
  } else if (EndsWith(filename, ".css", false)) {
    return "text/css";
  } else if (EndsWith(filename, ".js", false)) {
    return "application/javascript";
  } else if (EndsWith(filename, ".png", false)) {
    return "image/png";
  } else if (EndsWith(filename, ".gif", false)) {
    return "image/gif";
  } else if (EndsWith(filename, ".manifest", false)) {
    return "text/cache-manifest";
  }
  return "text/html";
}

// An URLDataSource implementation that handles chrome-devtools://devtools/
// requests. Three types of requests could be handled based on the URL path:
// 1. /bundled/: bundled DevTools frontend is served.
// 2. /remote/: remote DevTools frontend is served from App Engine.
// 3. /remote/open/: query is URL which is opened on remote device.
class DevToolsDataSource : public content::URLDataSource,
                           public net::URLFetcherDelegate {
 public:
  using GotDataCallback = content::URLDataSource::GotDataCallback;

  explicit DevToolsDataSource(net::URLRequestContextGetter* request_context);

  // content::URLDataSource implementation.
  std::string GetSource() const override;

  void StartDataRequest(const std::string& path,
                        int render_process_id,
                        int render_frame_id,
                        const GotDataCallback& callback) override;

 private:
  // content::URLDataSource overrides.
  std::string GetMimeType(const std::string& path) const override;
  bool ShouldAddContentSecurityPolicy() const override;
  bool ShouldDenyXFrameOptions() const override;
  bool ShouldServeMimeTypeAsContentTypeHeader() const override;

  // net::URLFetcherDelegate overrides.
  void OnURLFetchComplete(const net::URLFetcher* source) override;

  // Serves bundled DevTools frontend from ResourceBundle.
  void StartBundledDataRequest(const std::string& path,
                               int render_process_id,
                               int render_frame_id,
                               const GotDataCallback& callback);

  // Serves remote DevTools frontend from hard-coded App Engine domain.
  void StartRemoteDataRequest(const std::string& path,
                              int render_process_id,
                              int render_frame_id,
                              const GotDataCallback& callback);

  ~DevToolsDataSource() override;

  scoped_refptr<net::URLRequestContextGetter> request_context_;

  using PendingRequestsMap = std::map<const net::URLFetcher*, GotDataCallback>;
  PendingRequestsMap pending_;

  DISALLOW_COPY_AND_ASSIGN(DevToolsDataSource);
};

DevToolsDataSource::DevToolsDataSource(
    net::URLRequestContextGetter* request_context)
    : request_context_(request_context) {
}

DevToolsDataSource::~DevToolsDataSource() {
  for (const auto& pair : pending_) {
    delete pair.first;
    pair.second.Run(
        new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
  }
}

std::string DevToolsDataSource::GetSource() const {
  return chrome::kChromeUIDevToolsHost;
}

void DevToolsDataSource::StartDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  // Serve request from local bundle.
  std::string bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath);
  bundled_path_prefix += "/";
  if (StartsWithASCII(path, bundled_path_prefix, false)) {
    StartBundledDataRequest(path.substr(bundled_path_prefix.length()),
                            render_process_id, render_frame_id, callback);
    return;
  }

  // Serve static response while connecting to the remote device.
  if (StartsWithASCII(path, kRemoteOpenPrefix, false)) {
    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableDevToolsExperiments)) {
      callback.Run(NULL);
      return;
    }
    std::string response = "Connecting to the device...";
    callback.Run(base::RefCountedString::TakeString(&response));
    return;
  }

  // Serve request from remote location.
  std::string remote_path_prefix(chrome::kChromeUIDevToolsRemotePath);
  remote_path_prefix += "/";
  if (StartsWithASCII(path, remote_path_prefix, false)) {
    StartRemoteDataRequest(path.substr(remote_path_prefix.length()),
                           render_process_id, render_frame_id, callback);
    return;
  }

  callback.Run(NULL);
}

std::string DevToolsDataSource::GetMimeType(const std::string& path) const {
  return GetMimeTypeForPath(path);
}

bool DevToolsDataSource::ShouldAddContentSecurityPolicy() const {
  return false;
}

bool DevToolsDataSource::ShouldDenyXFrameOptions() const {
  return false;
}

bool DevToolsDataSource::ShouldServeMimeTypeAsContentTypeHeader() const {
  return true;
}

void DevToolsDataSource::StartBundledDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  std::string filename = PathWithoutParams(path);

  int resource_id =
      content::DevToolsHttpHandler::GetFrontendResourceId(filename);

  DLOG_IF(WARNING, resource_id == -1)
      << "Unable to find dev tool resource: " << filename
      << ". If you compiled with debug_devtools=1, try running with "
         "--debug-devtools.";
  const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  scoped_refptr<base::RefCountedStaticMemory> bytes(rb.LoadDataResourceBytes(
      resource_id));
  callback.Run(bytes.get());
}

void DevToolsDataSource::StartRemoteDataRequest(
    const std::string& path,
    int render_process_id,
    int render_frame_id,
    const content::URLDataSource::GotDataCallback& callback) {
  GURL url = GURL(kRemoteFrontendBase + path);
  CHECK_EQ(url.host(), kRemoteFrontendDomain);
  if (!url.is_valid()) {
    callback.Run(
        new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound)));
    return;
  }
  net::URLFetcher* fetcher =
      net::URLFetcher::Create(url, net::URLFetcher::GET, this);
  pending_[fetcher] = callback;
  fetcher->SetRequestContext(request_context_.get());
  fetcher->Start();
}

void DevToolsDataSource::OnURLFetchComplete(const net::URLFetcher* source) {
  DCHECK(source);
  PendingRequestsMap::iterator it = pending_.find(source);
  DCHECK(it != pending_.end());
  std::string response;
  source->GetResponseAsString(&response);
  delete source;
  it->second.Run(base::RefCountedString::TakeString(&response));
  pending_.erase(it);
}

// OpenRemotePageRequest ------------------------------------------------------

class OpenRemotePageRequest : public DevToolsAndroidBridge::DeviceListListener {
 public:
  OpenRemotePageRequest(
      Profile* profile,
      const std::string url,
      const DevToolsAndroidBridge::RemotePageCallback& callback);
  ~OpenRemotePageRequest() override {}

 private:
  // DevToolsAndroidBridge::Listener overrides.
  void DeviceListChanged(
      const DevToolsAndroidBridge::RemoteDevices& devices) override;

  bool OpenInBrowser(
      scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser);
  void RemotePageOpened(scoped_refptr<DevToolsAndroidBridge::RemotePage> page);

  std::string url_;
  DevToolsAndroidBridge::RemotePageCallback callback_;
  bool opening_;
  DevToolsAndroidBridge* android_bridge_;

  DISALLOW_COPY_AND_ASSIGN(OpenRemotePageRequest);
};

OpenRemotePageRequest::OpenRemotePageRequest(
    Profile* profile,
    const std::string url,
    const DevToolsAndroidBridge::RemotePageCallback& callback)
    : url_(url),
      callback_(callback),
      opening_(false),
      android_bridge_(
          DevToolsAndroidBridge::Factory::GetForProfile(profile)) {
  android_bridge_->AddDeviceListListener(this);
}

void OpenRemotePageRequest::DeviceListChanged(
    const DevToolsAndroidBridge::RemoteDevices& devices) {
  if (opening_)
    return;

  for (DevToolsAndroidBridge::RemoteDevices::const_iterator dit =
      devices.begin(); dit != devices.end(); ++dit) {
    DevToolsAndroidBridge::RemoteDevice* device = dit->get();
    if (!device->is_connected())
      continue;

    DevToolsAndroidBridge::RemoteBrowsers& browsers = device->browsers();
    for (DevToolsAndroidBridge::RemoteBrowsers::iterator bit =
        browsers.begin(); bit != browsers.end(); ++bit) {
      if (OpenInBrowser(bit->get())) {
        opening_ = true;
        return;
      }
    }
  }
}

bool OpenRemotePageRequest::OpenInBrowser(
    scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser) {
  if (!browser->IsChrome())
    return false;
#if defined(DEBUG_DEVTOOLS)
  if (browser->serial() == kLocalSerial)
    return false;
#endif  // defined(DEBUG_DEVTOOLS)
  android_bridge_->OpenRemotePage(
      browser,
      url_,
      base::Bind(&OpenRemotePageRequest::RemotePageOpened,
                 base::Unretained(this)));
  return true;
}

void OpenRemotePageRequest::RemotePageOpened(
    scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
  callback_.Run(page);
  android_bridge_->RemoveDeviceListListener(this);
  delete this;
}

}  // namespace

// DevToolsUI -----------------------------------------------------------------

// static
GURL DevToolsUI::GetProxyURL(const std::string& frontend_url) {
  GURL url(frontend_url);
  if (!url.is_valid() || url.host() != kRemoteFrontendDomain)
    return GURL(kFallbackFrontendURL);
  return GURL(base::StringPrintf("%s://%s/%s/%s",
              content::kChromeDevToolsScheme,
              chrome::kChromeUIDevToolsHost,
              chrome::kChromeUIDevToolsRemotePath,
              url.path().substr(1).c_str()));
}

DevToolsUI::DevToolsUI(content::WebUI* web_ui)
    : WebUIController(web_ui),
      content::WebContentsObserver(web_ui->GetWebContents()),
      bindings_(web_ui->GetWebContents()),
      weak_factory_(this) {
  web_ui->SetBindings(0);
  Profile* profile = Profile::FromWebUI(web_ui);
  content::URLDataSource::Add(
      profile,
      new DevToolsDataSource(profile->GetRequestContext()));
}

DevToolsUI::~DevToolsUI() {
}

void DevToolsUI::NavigationEntryCommitted(
    const content::LoadCommittedDetails& load_details) {
  content::NavigationEntry* entry = load_details.entry;
  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kEnableDevToolsExperiments)) {
    return;
  }

  if (entry->GetVirtualURL() == remote_frontend_loading_url_) {
    remote_frontend_loading_url_ = GURL();
    return;
  }

  std::string path = entry->GetVirtualURL().path().substr(1);
  if (!StartsWithASCII(path, kRemoteOpenPrefix, false))
    return;

  bindings_.Detach();
  remote_page_opening_url_ = entry->GetVirtualURL();
  new OpenRemotePageRequest(Profile::FromWebUI(web_ui()),
                            entry->GetVirtualURL().query(),
                            base::Bind(&DevToolsUI::RemotePageOpened,
                                       weak_factory_.GetWeakPtr(),
                                       entry->GetVirtualURL()));
}

void DevToolsUI::RemotePageOpened(
    const GURL& virtual_url,
    scoped_refptr<DevToolsAndroidBridge::RemotePage> page) {
  // Already navigated away while connecting to remote device.
  if (remote_page_opening_url_ != virtual_url)
    return;

  remote_page_opening_url_ = GURL();

  Profile* profile = Profile::FromWebUI(web_ui());
  GURL url = DevToolsUIBindings::ApplyThemeToURL(profile,
      DevToolsUI::GetProxyURL(page->frontend_url()));

  content::NavigationController& navigation_controller =
      web_ui()->GetWebContents()->GetController();
  content::NavigationController::LoadURLParams params(url);
  params.should_replace_current_entry = true;
  remote_frontend_loading_url_ = virtual_url;
  navigation_controller.LoadURLWithParams(params);
  navigation_controller.GetPendingEntry()->SetVirtualURL(virtual_url);

  DevToolsAndroidBridge* bridge =
      DevToolsAndroidBridge::Factory::GetForProfile(profile);
  scoped_ptr<DevToolsTargetImpl> target(bridge->CreatePageTarget(page));
  bindings_.AttachTo(target->GetAgentHost());
}
<|endoftext|>


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

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#endif

#include <string>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/env_var.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/env_vars.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class ChromeLoggingTest : public testing::Test {
 public:
  // Stores the current value of the log file name environment
  // variable and sets the variable to new_value.
  void SaveEnvironmentVariable(std::string new_value) {
    scoped_ptr<base::EnvVarGetter> env(base::EnvVarGetter::Create());
    if (!env->GetEnv(env_vars::kLogFileName, &environment_filename_))
      environment_filename_ = "";

    // TODO(port) Add base::SetEnv() and get rid of the ifdefs.
#if defined(OS_WIN)
    SetEnvironmentVariable(ASCIIToWide(env_vars::kLogFileName).c_str(),
                           ASCIIToWide(new_value).c_str());
#else
    setenv(env_vars::kLogFileName, new_value.c_str(), 1);
#endif
  }

  // Restores the value of the log file nave environment variable
  // previously saved by SaveEnvironmentVariable().
  void RestoreEnvironmentVariable() {
#if defined(OS_WIN)
    SetEnvironmentVariable(ASCIIToWide(env_vars::kLogFileName).c_str(),
                           ASCIIToWide(environment_filename_).c_str());
#else
    setenv(env_vars::kLogFileName, environment_filename_.c_str(), 1);
#endif
  }

 private:
  std::string environment_filename_;  // Saves real environment value.
};
};

// Tests the log file name getter without an environment variable.
TEST_F(ChromeLoggingTest, LogFileName) {
  SaveEnvironmentVariable("");

  FilePath filename = logging::GetLogFileName();
  ASSERT_NE(FilePath::StringType::npos,
            filename.value().find(FILE_PATH_LITERAL("chrome_debug.log")));

  RestoreEnvironmentVariable();
}

// Tests the log file name getter with an environment variable.
TEST_F(ChromeLoggingTest, EnvironmentLogFileName) {
  SaveEnvironmentVariable("test value");

  FilePath filename = logging::GetLogFileName();
  ASSERT_EQ(FilePath(FILE_PATH_LITERAL("test value")).value(),
            filename.value());

  RestoreEnvironmentVariable();
}

#if defined(OS_LINUX) && (!defined(NDEBUG) || !defined(USE_LINUX_BREAKPAD))
// On Linux in Debug mode, Chrome generates a SIGTRAP.
// we do not catch SIGTRAPs, thus no crash dump.
// This also does not work if Breakpad is disabled.
#define EXPECTED_ASSERT_CRASHES 0
#else
#define EXPECTED_ASSERT_CRASHES 1
#endif

#if !defined(NDEBUG)  // We don't have assertions in release builds.
// Tests whether we correctly fail on browser assertions during tests.
class AssertionTest : public UITest {
 protected:
  AssertionTest() : UITest() {
    // Initial loads will never complete due to assertion.
    wait_for_initial_loads_ = false;

    // We're testing the renderer rather than the browser assertion here,
    // because the browser assertion would flunk the test during SetUp()
    // (since TAU wouldn't be able to find the browser window).
    launch_arguments_.AppendSwitch(switches::kRendererAssertTest);
  }
};

// Launch the app in assertion test mode, then close the app.
#if defined(OS_WIN)
// http://crbug.com/26715
#define Assertion DISABLED_Assertion
#endif
TEST_F(AssertionTest, Assertion) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_errors_ = 0;
    expected_crashes_ = 0;
  } else {
    expected_errors_ = 1;
    expected_crashes_ = EXPECTED_ASSERT_CRASHES;
  }
}
#endif  // !defined(NDEBUG)

#if !defined(OFFICIAL_BUILD)
// Only works on Linux in Release mode with CHROME_HEADLESS=1
class CheckFalseTest : public UITest {
 protected:
  CheckFalseTest() : UITest() {
    // Initial loads will never complete due to assertion.
    wait_for_initial_loads_ = false;

    // We're testing the renderer rather than the browser assertion here,
    // because the browser assertion would flunk the test during SetUp()
    // (since TAU wouldn't be able to find the browser window).
    launch_arguments_.AppendSwitch(switches::kRendererCheckFalseTest);
  }
};

#if defined(OS_WIN)
// http://crbug.com/38497
#define CheckFails FLAKY_CheckFails
#endif
// Launch the app in assertion test mode, then close the app.
TEST_F(CheckFalseTest, CheckFails) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_errors_ = 0;
    expected_crashes_ = 0;
  } else {
    expected_errors_ = 1;
    expected_crashes_ = EXPECTED_ASSERT_CRASHES;
  }
}
#endif  // !defined(OFFICIAL_BUILD)

// Tests whether we correctly fail on browser crashes during UI Tests.
class RendererCrashTest : public UITest {
 protected:
  RendererCrashTest() : UITest() {
    // Initial loads will never complete due to crash.
    wait_for_initial_loads_ = false;

    launch_arguments_.AppendSwitch(switches::kRendererCrashTest);
  }
};

#if defined(OS_LINUX) && !defined(USE_LINUX_BREAKPAD)
// On Linux, do not expect a crash dump if Breakpad is disabled.
#define EXPECTED_CRASH_CRASHES 0
#else
#define EXPECTED_CRASH_CRASHES 1
#endif

#if defined(OS_WIN)
// http://crbug.com/32048
#define Crash FLAKY_Crash
#endif
// Launch the app in renderer crash test mode, then close the app.
TEST_F(RendererCrashTest, Crash) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_crashes_ = 0;
  } else {
    scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(browser.get());
    ASSERT_TRUE(browser->WaitForTabCountToBecome(1, action_max_timeout_ms()));
    expected_crashes_ = EXPECTED_CRASH_CRASHES;
  }
}
<commit_msg>CrOS: Disable failing RendererCrashTest.Crash test.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#endif

#include <string>

#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/env_var.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/env_vars.h"
#include "chrome/common/logging_chrome.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class ChromeLoggingTest : public testing::Test {
 public:
  // Stores the current value of the log file name environment
  // variable and sets the variable to new_value.
  void SaveEnvironmentVariable(std::string new_value) {
    scoped_ptr<base::EnvVarGetter> env(base::EnvVarGetter::Create());
    if (!env->GetEnv(env_vars::kLogFileName, &environment_filename_))
      environment_filename_ = "";

    // TODO(port) Add base::SetEnv() and get rid of the ifdefs.
#if defined(OS_WIN)
    SetEnvironmentVariable(ASCIIToWide(env_vars::kLogFileName).c_str(),
                           ASCIIToWide(new_value).c_str());
#else
    setenv(env_vars::kLogFileName, new_value.c_str(), 1);
#endif
  }

  // Restores the value of the log file nave environment variable
  // previously saved by SaveEnvironmentVariable().
  void RestoreEnvironmentVariable() {
#if defined(OS_WIN)
    SetEnvironmentVariable(ASCIIToWide(env_vars::kLogFileName).c_str(),
                           ASCIIToWide(environment_filename_).c_str());
#else
    setenv(env_vars::kLogFileName, environment_filename_.c_str(), 1);
#endif
  }

 private:
  std::string environment_filename_;  // Saves real environment value.
};
};

// Tests the log file name getter without an environment variable.
TEST_F(ChromeLoggingTest, LogFileName) {
  SaveEnvironmentVariable("");

  FilePath filename = logging::GetLogFileName();
  ASSERT_NE(FilePath::StringType::npos,
            filename.value().find(FILE_PATH_LITERAL("chrome_debug.log")));

  RestoreEnvironmentVariable();
}

// Tests the log file name getter with an environment variable.
TEST_F(ChromeLoggingTest, EnvironmentLogFileName) {
  SaveEnvironmentVariable("test value");

  FilePath filename = logging::GetLogFileName();
  ASSERT_EQ(FilePath(FILE_PATH_LITERAL("test value")).value(),
            filename.value());

  RestoreEnvironmentVariable();
}

#if defined(OS_LINUX) && (!defined(NDEBUG) || !defined(USE_LINUX_BREAKPAD))
// On Linux in Debug mode, Chrome generates a SIGTRAP.
// we do not catch SIGTRAPs, thus no crash dump.
// This also does not work if Breakpad is disabled.
#define EXPECTED_ASSERT_CRASHES 0
#else
#define EXPECTED_ASSERT_CRASHES 1
#endif

#if !defined(NDEBUG)  // We don't have assertions in release builds.
// Tests whether we correctly fail on browser assertions during tests.
class AssertionTest : public UITest {
 protected:
  AssertionTest() : UITest() {
    // Initial loads will never complete due to assertion.
    wait_for_initial_loads_ = false;

    // We're testing the renderer rather than the browser assertion here,
    // because the browser assertion would flunk the test during SetUp()
    // (since TAU wouldn't be able to find the browser window).
    launch_arguments_.AppendSwitch(switches::kRendererAssertTest);
  }
};

// Launch the app in assertion test mode, then close the app.
#if defined(OS_WIN)
// http://crbug.com/26715
#define Assertion DISABLED_Assertion
#endif
TEST_F(AssertionTest, Assertion) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_errors_ = 0;
    expected_crashes_ = 0;
  } else {
    expected_errors_ = 1;
    expected_crashes_ = EXPECTED_ASSERT_CRASHES;
  }
}
#endif  // !defined(NDEBUG)

#if !defined(OFFICIAL_BUILD)
// Only works on Linux in Release mode with CHROME_HEADLESS=1
class CheckFalseTest : public UITest {
 protected:
  CheckFalseTest() : UITest() {
    // Initial loads will never complete due to assertion.
    wait_for_initial_loads_ = false;

    // We're testing the renderer rather than the browser assertion here,
    // because the browser assertion would flunk the test during SetUp()
    // (since TAU wouldn't be able to find the browser window).
    launch_arguments_.AppendSwitch(switches::kRendererCheckFalseTest);
  }
};

#if defined(OS_WIN)
// http://crbug.com/38497
#define CheckFails FLAKY_CheckFails
#endif
// Launch the app in assertion test mode, then close the app.
TEST_F(CheckFalseTest, CheckFails) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_errors_ = 0;
    expected_crashes_ = 0;
  } else {
    expected_errors_ = 1;
    expected_crashes_ = EXPECTED_ASSERT_CRASHES;
  }
}
#endif  // !defined(OFFICIAL_BUILD)

// Tests whether we correctly fail on browser crashes during UI Tests.
class RendererCrashTest : public UITest {
 protected:
  RendererCrashTest() : UITest() {
    // Initial loads will never complete due to crash.
    wait_for_initial_loads_ = false;

    launch_arguments_.AppendSwitch(switches::kRendererCrashTest);
  }
};

#if defined(OS_LINUX) && !defined(USE_LINUX_BREAKPAD)
// On Linux, do not expect a crash dump if Breakpad is disabled.
#define EXPECTED_CRASH_CRASHES 0
#else
#define EXPECTED_CRASH_CRASHES 1
#endif

#if defined(OS_WIN)
// http://crbug.com/32048
#define Crash FLAKY_Crash
#elif defined(OS_CHROMEOS)
// http://crbug.com/43115
#define Crash DISABLED_Crash
#endif
// Launch the app in renderer crash test mode, then close the app.
TEST_F(RendererCrashTest, Crash) {
  if (UITest::in_process_renderer()) {
    // in process mode doesn't do the crashing.
    expected_crashes_ = 0;
  } else {
    scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
    ASSERT_TRUE(browser.get());
    ASSERT_TRUE(browser->WaitForTabCountToBecome(1, action_max_timeout_ms()));
    expected_crashes_ = EXPECTED_CRASH_CRASHES;
  }
}
<|endoftext|>