<commit_before>// Copyright 2009 Google Inc. All Rights Reserved.
// Author: lperron@google.com (Laurent Perron)

#include <stddef.h>
#include <vector>

#include "base/callback.h"
#include "base/logging.h"
#include "base/mutex.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "base/synchronization.h"
#include "constraint_solver/constraint_solver.h"
#include "constraint_solver/constraint_solveri.h"

DEFINE_int32(workers, 4, "Number of workers for tests");

namespace operations_research {
class UpVar : public BaseLNS {
 public:
  UpVar(const IntVar* const* vars, int size, int worker)
      : BaseLNS(vars, size),  worker_(worker) {}

  virtual ~UpVar() {}

  virtual bool NextFragment(vector<int>* fragment) {
    bool all_done = true;
    for (int i = 0; i < Size(); ++i) {
      if (Value(i) == 0) {
        all_done = false;
        break;
      }
    }
    if (all_done) {
      VLOG(1) << "worker " << worker_ << " thinks search should terminate";
      return false;
    }
    fragment->push_back(worker_);
    return true;
  }
 private:
  const int worker_;
};

void BuildModelWithSolution(int* const active,
                            Mutex* const mutex,
                            ParallelSolveSupport* const support,
                            bool master,
                            int worker) {
  Solver s(StringPrintf("Worker_%i", worker));
  IntVar* const x = s.MakeIntVar(0, 10, "x");
  IntVar* const y = s.MakeIntVar(0, 10, "y");
  Assignment* const solution = s.MakeAssignment();
  solution->Add(x);
  solution->Add(y);
  if (master) {
    VLOG(1) << "Master run";
    solution->SetValue(x, 2);
    solution->SetValue(y, 4);
    support->RegisterInitialSolution(solution);
    ThreadSafeIncrement(active, mutex, 2);
    VLOG(1) << "Master initial solution sent";
  } else {
    VLOG(1) << "Slave " << worker;
    CHECK(support->WaitForInitialSolution(solution, worker));
    VLOG(1) << "Worker solution received";
    CHECK_EQ(2, solution->Value(x));
    CHECK_EQ(4, solution->Value(y));
    ThreadSafeIncrement(active, mutex, 1);
  }
}

void BuildModelWithoutSolution(int* const active,
                               Mutex* const mutex,
                               ParallelSolveSupport* const support,
                               bool master,
                               int worker) {
  Solver s(StringPrintf("Worker_%i", worker));
  if (master) {
    support->RegisterNoInitialSolution();
    ThreadSafeIncrement(active, mutex, 2);
  } else {
    Assignment* const solution = s.MakeAssignment();
    CHECK(!support->WaitForInitialSolution(solution, worker));
    ThreadSafeIncrement(active, mutex, 1);
  }
}

void BuildModelWithSearch(int workers,
                          ParallelSolveSupport* const support,
                          bool master,
                          int worker) {
  Solver s(StringPrintf("Worker_%i", worker));
  VLOG(1) << "Worker " << worker  << " started";
  vector<IntVar*> vars;
  s.MakeBoolVarArray(workers, "vars", &vars);
  Assignment* const solution = s.MakeAssignment();
  solution->Add(vars);
  vector<int> coefficients;
  int obj_max = 0;
  for (int i = 0; i < workers; ++i) {
    const int value = (i + 1) * (i + 1);
    coefficients.push_back(value);
    obj_max += value;
  }
  IntVar* const objective = s.MakeScalProd(vars, coefficients)->Var();
  solution->AddObjective(objective);

  SolutionCollector* const collector = master ?
      s.MakeLastSolutionCollector(solution) :
      NULL;

  vector<SearchMonitor*> monitors;

  if (master) {
    monitors.push_back(collector);

    for (int i = 0; i < workers; ++i) {
      solution->SetValue(vars[i], 0);
    }
    solution->SetObjectiveValue(0);
    support->RegisterInitialSolution(solution);
  } else {
    CHECK(support->WaitForInitialSolution(solution, worker));
    CHECK_EQ(0, solution->ObjectiveValue());
  }

  monitors.push_back(s.MakeMaximize(objective, 1));

  UpVar* local_search_operator =
      s.RevAlloc(new UpVar(vars.data(), workers, worker));

  DecisionBuilder* db = s.MakePhase(vars,
                                    Solver::CHOOSE_FIRST_UNBOUND,
                                    Solver::ASSIGN_MAX_VALUE);

  SolutionPool* const pool = !master ?
      support->MakeSolutionPool(&s, worker) :
      s.MakeDefaultSolutionPool();

  LocalSearchPhaseParameters* parameters =
      s.MakeLocalSearchPhaseParameters(pool, local_search_operator, db);

  DecisionBuilder* const final_db = master ?
      support->MakeReplayDecisionBuilder(&s, solution) :
      s.MakeLocalSearchPhase(solution, parameters);

  monitors.push_back(
      support->MakeCommunicationMonitor(&s, solution, master, worker));

  s.Solve(final_db, monitors);
  if (master && collector->solution_count()) {
    CHECK_EQ(1, collector->solution_count());
    Assignment* const best_solution = collector->solution(0);
    CHECK_EQ(obj_max, best_solution->ObjectiveValue());
  }
}

void TestInitialSolution() {
  LOG(INFO) << "TestInitialSolution";
  int work_done = 0;
  Mutex mutex;
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         false,
                         NewPermanentCallback(
                             &BuildModelWithSolution,
                             &work_done,
                             &mutex)));

  support->Run();
  CHECK_EQ(FLAGS_workers + 2, work_done);
}

void TestNoInitialSolution() {
  LOG(INFO) << "TestNoInitialSolution";
  int work_done = 0;
  Mutex mutex;
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         false,
                         NewPermanentCallback(
                             &BuildModelWithoutSolution,
                             &work_done,
                             &mutex)));
  support->Run();
  CHECK_EQ(FLAGS_workers + 2, work_done);
}

void TestModelWithSearch() {
  LOG(INFO) << "TestModelWithSearch";
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         true,
                         NewPermanentCallback(
                             &BuildModelWithSearch,
                             FLAGS_workers)));
  support->Run();
}
}  // namespace  operations_research

int main(int argc, char **argv) {
  google::ParseCommandLineFlags(&argc, &argv, true);
  operations_research::TestInitialSolution();
  operations_research::TestNoInitialSolution();
  operations_research::TestModelWithSearch();
  return 0;
}
<commit_msg>document mtsearch_test<commit_after>// Copyright 2009 Google Inc. All Rights Reserved.
// Author: lperron@google.com (Laurent Perron)

#include <stddef.h>
#include <vector>

#include "base/callback.h"
#include "base/logging.h"
#include "base/mutex.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
#include "base/synchronization.h"
#include "constraint_solver/constraint_solver.h"
#include "constraint_solver/constraint_solveri.h"

DEFINE_int32(workers, 4, "Number of workers for tests");

namespace operations_research {
class UpVar : public BaseLNS {
 public:
  UpVar(const IntVar* const* vars, int size, int worker)
      : BaseLNS(vars, size),  worker_(worker) {}

  virtual ~UpVar() {}

  virtual bool NextFragment(vector<int>* fragment) {
    bool all_done = true;
    for (int i = 0; i < Size(); ++i) {
      if (Value(i) == 0) {
        all_done = false;
        break;
      }
    }
    if (all_done) {
      VLOG(1) << "worker " << worker_ << " thinks search should terminate";
      return false;
    }
    fragment->push_back(worker_);
    return true;
  }
 private:
  const int worker_;
};

void BuildModelWithSolution(int* const active,
                            Mutex* const mutex,
                            ParallelSolveSupport* const support,
                            bool master,
                            int worker) {
  Solver s(StringPrintf("Worker_%i", worker));
  IntVar* const x = s.MakeIntVar(0, 10, "x");
  IntVar* const y = s.MakeIntVar(0, 10, "y");
  Assignment* const solution = s.MakeAssignment();
  solution->Add(x);
  solution->Add(y);
  if (master) {
    VLOG(1) << "Master run";
    solution->SetValue(x, 2);
    solution->SetValue(y, 4);
    support->RegisterInitialSolution(solution);
    ThreadSafeIncrement(active, mutex, 2);
    VLOG(1) << "Master initial solution sent";
  } else {
    VLOG(1) << "Slave " << worker;
    CHECK(support->WaitForInitialSolution(solution, worker));
    VLOG(1) << "Worker solution received";
    CHECK_EQ(2, solution->Value(x));
    CHECK_EQ(4, solution->Value(y));
    ThreadSafeIncrement(active, mutex, 1);
  }
}

void BuildModelWithoutSolution(int* const active,
                               Mutex* const mutex,
                               ParallelSolveSupport* const support,
                               bool master,
                               int worker) {
  Solver s(StringPrintf("Worker_%i", worker));
  if (master) {
    support->RegisterNoInitialSolution();
    ThreadSafeIncrement(active, mutex, 2);
  } else {
    Assignment* const solution = s.MakeAssignment();
    CHECK(!support->WaitForInitialSolution(solution, worker));
    ThreadSafeIncrement(active, mutex, 1);
  }
}

void BuildModelWithSearch(int workers,
                          ParallelSolveSupport* const support,
                          bool master,
                          int worker) {
  // Standard model building.
  Solver s(StringPrintf("Worker_%i", worker));
  VLOG(1) << "Worker " << worker  << " started";
  vector<IntVar*> vars;
  s.MakeBoolVarArray(workers, "vars", &vars);
  Assignment* const solution = s.MakeAssignment();
  solution->Add(vars);
  vector<int> coefficients;
  int obj_max = 0;
  for (int i = 0; i < workers; ++i) {
    const int value = (i + 1) * (i + 1);
    coefficients.push_back(value);
    obj_max += value;
  }
  IntVar* const objective = s.MakeScalProd(vars, coefficients)->Var();
  solution->AddObjective(objective);

  SolutionCollector* const collector =
      master ? s.MakeLastSolutionCollector(solution) : NULL;

  vector<SearchMonitor*> monitors;

  // Create or wait for initial solution.
  if (master) {
    // Only the master needs to store solutions.
    monitors.push_back(collector);

    // Creates the initial solution.
    for (int i = 0; i < workers; ++i) {
      solution->SetValue(vars[i], 0);
    }
    solution->SetObjectiveValue(0);
    support->RegisterInitialSolution(solution);
  } else {
    // Workers waits for the initial solution.
    CHECK(support->WaitForInitialSolution(solution, worker));
    CHECK_EQ(0, solution->ObjectiveValue());
  }

  monitors.push_back(s.MakeMaximize(objective, 1));

  UpVar* const local_search_operator =
      s.RevAlloc(new UpVar(vars.data(), workers, worker));

  DecisionBuilder* db = s.MakePhase(vars,
                                    Solver::CHOOSE_FIRST_UNBOUND,
                                    Solver::ASSIGN_MAX_VALUE);

  // The master must run a dedicated decision builder to report
  // solutions found by the workers. The workers run a LNS operator
  // with a customized solution pool.
  DecisionBuilder* const final_db = master ?
      support->MakeReplayDecisionBuilder(&s, solution) :  // Master.
      s.MakeLocalSearchPhase(                             // Worker.
          solution,
          s.MakeLocalSearchPhaseParameters(
              support->MakeSolutionPool(&s, worker),
              local_search_operator,
              db));

  // Everybody needs this communication monitor.
  monitors.push_back(
      support->MakeCommunicationMonitor(&s, solution, master, worker));

  s.Solve(final_db, monitors);  // go!

  // The master will report solutions.
  if (master && collector->solution_count()) {
    CHECK_EQ(1, collector->solution_count());
    Assignment* const best_solution = collector->solution(0);
    CHECK_EQ(obj_max, best_solution->ObjectiveValue());
  }
}

void TestInitialSolution() {
  LOG(INFO) << "TestInitialSolution";
  int work_done = 0;
  Mutex mutex;
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         false,
                         NewPermanentCallback(
                             &BuildModelWithSolution,
                             &work_done,
                             &mutex)));

  support->Run();
  CHECK_EQ(FLAGS_workers + 2, work_done);
}

void TestNoInitialSolution() {
  LOG(INFO) << "TestNoInitialSolution";
  int work_done = 0;
  Mutex mutex;
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         false,
                         NewPermanentCallback(
                             &BuildModelWithoutSolution,
                             &work_done,
                             &mutex)));
  support->Run();
  CHECK_EQ(FLAGS_workers + 2, work_done);
}

void TestModelWithSearch() {
  LOG(INFO) << "TestModelWithSearch";
  scoped_ptr<ParallelSolveSupport> support(
      MakeMtSolveSupport(FLAGS_workers,
                         true,
                         NewPermanentCallback(
                             &BuildModelWithSearch,
                             FLAGS_workers)));
  support->Run();
}
}  // namespace  operations_research

int main(int argc, char **argv) {
  google::ParseCommandLineFlags(&argc, &argv, true);
  operations_research::TestInitialSolution();
  operations_research::TestNoInitialSolution();
  operations_research::TestModelWithSearch();
  return 0;
}
<|endoftext|>


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

#include "chrome/browser/debugger/devtools_http_protocol_handler.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/devtools_messages.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/listen_socket.h"
#include "net/server/http_server_request_info.h"
#include "net/url_request/url_request_context.h"

const int kBufferSize = 16 * 1024;

namespace {

// An internal implementation of DevToolsClientHost that delegates
// messages sent for DevToolsClient to a DebuggerShell instance.
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  explicit DevToolsClientHostImpl(HttpListenSocket* socket)
      : socket_(socket) {}
  ~DevToolsClientHostImpl() {}

  // DevToolsClientHost interface
  virtual void InspectedTabClosing() {
    socket_->Close();
  }

  virtual void SendMessageToClient(const IPC::Message& msg) {
    IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
      IPC_MESSAGE_HANDLER(DevToolsClientMsg_RpcMessage, OnRpcMessage);
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()
  }

  void NotifyCloseListener() {
    DevToolsClientHost::NotifyCloseListener();
  }
 private:
  // Message handling routines
  void OnRpcMessage(const DevToolsMessageData& data) {
    std::string message;
    message += "devtools$$dispatch(\"" + data.class_name + "\", \"" +
        data.method_name + "\"";
    for (std::vector<std::string>::const_iterator it = data.arguments.begin();
         it != data.arguments.end(); ++it) {
      std::string param = *it;
      if (!param.empty())
        message += ", " + param;
    }
    message += ")";
    socket_->SendOverWebSocket(message);
  }
  HttpListenSocket* socket_;
};

}

DevToolsHttpProtocolHandler::~DevToolsHttpProtocolHandler() {
  // Stop() must be called prior to this being called
  DCHECK(server_.get() == NULL);
}

void DevToolsHttpProtocolHandler::Start() {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
}

void DevToolsHttpProtocolHandler::Stop() {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
}

void DevToolsHttpProtocolHandler::OnHttpRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  if (info.path == "" || info.path == "/") {
    // Pages discovery request.
    ChromeThread::PostTask(
        ChromeThread::UI,
        FROM_HERE,
        NewRunnableMethod(this,
                          &DevToolsHttpProtocolHandler::OnHttpRequestUI,
                          socket,
                          info));
    return;
  }

  size_t pos = info.path.find("/devtools/");
  if (pos != 0) {
    socket->Send404();
    return;
  }

  // Proxy static files from chrome://devtools/*.
  URLRequest* request = new URLRequest(GURL("chrome:/" + info.path), this);
  Bind(request, socket);
  request->set_context(
      Profile::GetDefaultRequestContext()->GetURLRequestContext());
  request->Start();
}

void DevToolsHttpProtocolHandler::OnWebSocketRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
          socket,
          request));
}

void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket,
                                                     const std::string& data) {
  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
          socket,
          data));
}

void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) {
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it != socket_to_requests_io_.end()) {
    // Dispose delegating socket.
    for (std::set<URLRequest*>::iterator it2 = it->second.begin();
         it2 != it->second.end(); ++it2) {
      URLRequest* request = *it2;
      request->Cancel();
      request_to_socket_io_.erase(request);
      request_to_buffer_io_.erase(request);
      delete request;
    }
    socket_to_requests_io_.erase(socket);
  }

  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnCloseUI,
          socket));
}

void DevToolsHttpProtocolHandler::OnHttpRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  std::string response = "";
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      TabContents* tab_contents = model->GetTabContentsAt(i);
      NavigationController& controller = tab_contents->controller();
      NavigationEntry* entry = controller.GetActiveEntry();
      if (entry == NULL)
        continue;

      if (!entry->url().is_valid())
        continue;

      DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
          GetDevToolsClientHostFor(tab_contents->render_view_host());
      if (!client_host) {
        response += StringPrintf(
            "%s (%s)
",
            controller.session_id().id(),
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      } else {
        response += StringPrintf(
            "%s (%s)
",
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      }
    }
  }
  response += "";
  Send200(socket, response, "text/html");
}

void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  std::string prefix = "/devtools/page/";
  size_t pos = request.path.find(prefix);
  if (pos != 0) {
    Send404(socket);
    return;
  }
  std::string page_id = request.path.substr(prefix.length());
  int id = 0;
  if (!StringToInt(page_id, &id)) {
    Send500(socket, "Invalid page id: " + page_id);
    return;
  }

  TabContents* tab_contents = GetTabContents(id);
  if (tab_contents == NULL) {
    Send500(socket, "No such page id: " + page_id);
    return;
  }

  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
    Send500(socket, "Page with given id is being inspected: " + page_id);
    return;
  }

  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(socket);
  socket_to_client_host_ui_[socket] = client_host;

  manager->RegisterDevToolsClientHostFor(
      tab_contents->render_view_host(),
      client_host);
  AcceptWebSocket(socket, request);
}

void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
    HttpListenSocket* socket,
    const std::string& d) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;

  std::string data = d;
  // TODO(pfeldman): Replace with proper parsing / dispatching.
  DevToolsMessageData message_data;
  message_data.class_name = "ToolsAgent";
  message_data.method_name = "dispatchOnInspectorController";

  size_t pos = data.find(" ");
  message_data.arguments.push_back(data.substr(0, pos));
  data = data.substr(pos + 1);

  message_data.arguments.push_back(data);

  DevToolsManager* manager = DevToolsManager::GetInstance();
  manager->ForwardToDevToolsAgent(it->second,
      DevToolsAgentMsg_RpcMessage(DevToolsMessageData(message_data)));
}

void DevToolsHttpProtocolHandler::OnCloseUI(HttpListenSocket* socket) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;
  DevToolsClientHostImpl* client_host =
      static_cast<DevToolsClientHostImpl*>(it->second);
  client_host->NotifyCloseListener();
  delete client_host;
  socket_to_client_host_ui_.erase(socket);
}

void DevToolsHttpProtocolHandler::OnResponseStarted(URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  int expected_size = static_cast<int>(request->GetExpectedContentSize());

  std::string content_type;
  request->GetMimeType(&content_type);

  if (request->status().is_success()) {
    socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n"
                              "Content-Type:%s\r\n"
                              "Content-Length:%d\r\n"
                              "\r\n",
                              content_type.c_str(),
                              expected_size));
  } else {
    socket->Send404();
  }

  int bytes_read = 0;
  // Some servers may treat HEAD requests as GET requests.  To free up the
  // network connection as soon as possible, signal that the request has
  // completed immediately, without trying to read any data back (all we care
  // about is the response code and headers, which we already have).
  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  if (request->status().is_success())
    request->Read(buffer, kBufferSize, &bytes_read);
  OnReadCompleted(request, bytes_read);
}

void DevToolsHttpProtocolHandler::OnReadCompleted(URLRequest* request,
                                                  int bytes_read) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  do {
    if (!request->status().is_success() || bytes_read <= 0)
      break;
    socket->Send(buffer->data(), bytes_read);
  } while (request->Read(buffer, kBufferSize, &bytes_read));

  // See comments re: HEAD requests in OnResponseStarted().
  if (!request->status().is_io_pending())
    RequestCompleted(request);
}

DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port)
    : port_(port),
      server_(NULL) {
}

void DevToolsHttpProtocolHandler::Init() {
  server_ = HttpListenSocket::Listen("127.0.0.1", port_, this);
}

// Run on I/O thread
void DevToolsHttpProtocolHandler::Teardown() {
  server_ = NULL;
}

void DevToolsHttpProtocolHandler::Bind(URLRequest* request,
                                       HttpListenSocket* socket) {
  request_to_socket_io_[request] = socket;
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it == socket_to_requests_io_.end()) {
    std::pair<HttpListenSocket*, std::set<URLRequest*> > value(
        socket,
        std::set<URLRequest*>());
    it = socket_to_requests_io_.insert(value).first;
  }
  it->second.insert(request);
  request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
}

void DevToolsHttpProtocolHandler::RequestCompleted(URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;
  request_to_socket_io_.erase(request);
  SocketToRequestsMap::iterator it2 = socket_to_requests_io_.find(socket);
  it2->second.erase(request);
  request_to_buffer_io_.erase(request);
  delete request;
}

void DevToolsHttpProtocolHandler::Send200(HttpListenSocket* socket,
                                          const std::string& data,
                                          const std::string& mime_type) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send200,
                        data,
                        mime_type));
}

void DevToolsHttpProtocolHandler::Send404(HttpListenSocket* socket) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send404));
}

void DevToolsHttpProtocolHandler::Send500(HttpListenSocket* socket,
                                          const std::string& message) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send500,
                        message));
}

void DevToolsHttpProtocolHandler::AcceptWebSocket(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::AcceptWebSocket,
                        request));
}

TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      NavigationController& controller =
          model->GetTabContentsAt(i)->controller();
      if (controller.session_id().id() == session_id)
        return controller.tab_contents();
    }
  }
  return NULL;
}
<commit_msg>DevTools: close remote debugging connection on IO thread when inspected tab closes.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/debugger/devtools_http_protocol_handler.h"

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop_proxy.h"
#include "base/string_util.h"
#include "base/thread.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/debugger/devtools_client_host.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/devtools_messages.h"
#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/base/io_buffer.h"
#include "net/base/listen_socket.h"
#include "net/server/http_server_request_info.h"
#include "net/url_request/url_request_context.h"

const int kBufferSize = 16 * 1024;

namespace {

// An internal implementation of DevToolsClientHost that delegates
// messages sent for DevToolsClient to a DebuggerShell instance.
class DevToolsClientHostImpl : public DevToolsClientHost {
 public:
  explicit DevToolsClientHostImpl(HttpListenSocket* socket)
      : socket_(socket) {}
  ~DevToolsClientHostImpl() {}

  // DevToolsClientHost interface
  virtual void InspectedTabClosing() {
    ChromeThread::PostTask(
        ChromeThread::IO,
        FROM_HERE,
        NewRunnableMethod(socket_,
                          &HttpListenSocket::Close));
  }

  virtual void SendMessageToClient(const IPC::Message& msg) {
    IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
      IPC_MESSAGE_HANDLER(DevToolsClientMsg_RpcMessage, OnRpcMessage);
      IPC_MESSAGE_UNHANDLED_ERROR()
    IPC_END_MESSAGE_MAP()
  }

  void NotifyCloseListener() {
    DevToolsClientHost::NotifyCloseListener();
  }
 private:
  // Message handling routines
  void OnRpcMessage(const DevToolsMessageData& data) {
    std::string message;
    message += "devtools$$dispatch(\"" + data.class_name + "\", \"" +
        data.method_name + "\"";
    for (std::vector<std::string>::const_iterator it = data.arguments.begin();
         it != data.arguments.end(); ++it) {
      std::string param = *it;
      if (!param.empty())
        message += ", " + param;
    }
    message += ")";
    socket_->SendOverWebSocket(message);
  }
  HttpListenSocket* socket_;
};

}

DevToolsHttpProtocolHandler::~DevToolsHttpProtocolHandler() {
  // Stop() must be called prior to this being called
  DCHECK(server_.get() == NULL);
}

void DevToolsHttpProtocolHandler::Start() {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
}

void DevToolsHttpProtocolHandler::Stop() {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
}

void DevToolsHttpProtocolHandler::OnHttpRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  if (info.path == "" || info.path == "/") {
    // Pages discovery request.
    ChromeThread::PostTask(
        ChromeThread::UI,
        FROM_HERE,
        NewRunnableMethod(this,
                          &DevToolsHttpProtocolHandler::OnHttpRequestUI,
                          socket,
                          info));
    return;
  }

  size_t pos = info.path.find("/devtools/");
  if (pos != 0) {
    socket->Send404();
    return;
  }

  // Proxy static files from chrome://devtools/*.
  URLRequest* request = new URLRequest(GURL("chrome:/" + info.path), this);
  Bind(request, socket);
  request->set_context(
      Profile::GetDefaultRequestContext()->GetURLRequestContext());
  request->Start();
}

void DevToolsHttpProtocolHandler::OnWebSocketRequest(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
          socket,
          request));
}

void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket,
                                                     const std::string& data) {
  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
          socket,
          data));
}

void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) {
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it != socket_to_requests_io_.end()) {
    // Dispose delegating socket.
    for (std::set<URLRequest*>::iterator it2 = it->second.begin();
         it2 != it->second.end(); ++it2) {
      URLRequest* request = *it2;
      request->Cancel();
      request_to_socket_io_.erase(request);
      request_to_buffer_io_.erase(request);
      delete request;
    }
    socket_to_requests_io_.erase(socket);
  }

  ChromeThread::PostTask(
      ChromeThread::UI,
      FROM_HERE,
      NewRunnableMethod(
          this,
          &DevToolsHttpProtocolHandler::OnCloseUI,
          socket));
}

void DevToolsHttpProtocolHandler::OnHttpRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& info) {
  std::string response = "";
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      TabContents* tab_contents = model->GetTabContentsAt(i);
      NavigationController& controller = tab_contents->controller();
      NavigationEntry* entry = controller.GetActiveEntry();
      if (entry == NULL)
        continue;

      if (!entry->url().is_valid())
        continue;

      DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
          GetDevToolsClientHostFor(tab_contents->render_view_host());
      if (!client_host) {
        response += StringPrintf(
            "%s (%s)
",
            controller.session_id().id(),
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      } else {
        response += StringPrintf(
            "%s (%s)
",
            UTF16ToUTF8(entry->title()).c_str(),
            entry->url().spec().c_str());
      }
    }
  }
  response += "";
  Send200(socket, response, "text/html");
}

void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  std::string prefix = "/devtools/page/";
  size_t pos = request.path.find(prefix);
  if (pos != 0) {
    Send404(socket);
    return;
  }
  std::string page_id = request.path.substr(prefix.length());
  int id = 0;
  if (!StringToInt(page_id, &id)) {
    Send500(socket, "Invalid page id: " + page_id);
    return;
  }

  TabContents* tab_contents = GetTabContents(id);
  if (tab_contents == NULL) {
    Send500(socket, "No such page id: " + page_id);
    return;
  }

  DevToolsManager* manager = DevToolsManager::GetInstance();
  if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
    Send500(socket, "Page with given id is being inspected: " + page_id);
    return;
  }

  DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(socket);
  socket_to_client_host_ui_[socket] = client_host;

  manager->RegisterDevToolsClientHostFor(
      tab_contents->render_view_host(),
      client_host);
  AcceptWebSocket(socket, request);
}

void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
    HttpListenSocket* socket,
    const std::string& d) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;

  std::string data = d;
  // TODO(pfeldman): Replace with proper parsing / dispatching.
  DevToolsMessageData message_data;
  message_data.class_name = "ToolsAgent";
  message_data.method_name = "dispatchOnInspectorController";

  size_t pos = data.find(" ");
  message_data.arguments.push_back(data.substr(0, pos));
  data = data.substr(pos + 1);

  message_data.arguments.push_back(data);

  DevToolsManager* manager = DevToolsManager::GetInstance();
  manager->ForwardToDevToolsAgent(it->second,
      DevToolsAgentMsg_RpcMessage(DevToolsMessageData(message_data)));
}

void DevToolsHttpProtocolHandler::OnCloseUI(HttpListenSocket* socket) {
  SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
  if (it == socket_to_client_host_ui_.end())
    return;
  DevToolsClientHostImpl* client_host =
      static_cast<DevToolsClientHostImpl*>(it->second);
  client_host->NotifyCloseListener();
  delete client_host;
  socket_to_client_host_ui_.erase(socket);
}

void DevToolsHttpProtocolHandler::OnResponseStarted(URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  int expected_size = static_cast<int>(request->GetExpectedContentSize());

  std::string content_type;
  request->GetMimeType(&content_type);

  if (request->status().is_success()) {
    socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n"
                              "Content-Type:%s\r\n"
                              "Content-Length:%d\r\n"
                              "\r\n",
                              content_type.c_str(),
                              expected_size));
  } else {
    socket->Send404();
  }

  int bytes_read = 0;
  // Some servers may treat HEAD requests as GET requests.  To free up the
  // network connection as soon as possible, signal that the request has
  // completed immediately, without trying to read any data back (all we care
  // about is the response code and headers, which we already have).
  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  if (request->status().is_success())
    request->Read(buffer, kBufferSize, &bytes_read);
  OnReadCompleted(request, bytes_read);
}

void DevToolsHttpProtocolHandler::OnReadCompleted(URLRequest* request,
                                                  int bytes_read) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;

  net::IOBuffer* buffer = request_to_buffer_io_[request].get();
  do {
    if (!request->status().is_success() || bytes_read <= 0)
      break;
    socket->Send(buffer->data(), bytes_read);
  } while (request->Read(buffer, kBufferSize, &bytes_read));

  // See comments re: HEAD requests in OnResponseStarted().
  if (!request->status().is_io_pending())
    RequestCompleted(request);
}

DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port)
    : port_(port),
      server_(NULL) {
}

void DevToolsHttpProtocolHandler::Init() {
  server_ = HttpListenSocket::Listen("127.0.0.1", port_, this);
}

// Run on I/O thread
void DevToolsHttpProtocolHandler::Teardown() {
  server_ = NULL;
}

void DevToolsHttpProtocolHandler::Bind(URLRequest* request,
                                       HttpListenSocket* socket) {
  request_to_socket_io_[request] = socket;
  SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
  if (it == socket_to_requests_io_.end()) {
    std::pair<HttpListenSocket*, std::set<URLRequest*> > value(
        socket,
        std::set<URLRequest*>());
    it = socket_to_requests_io_.insert(value).first;
  }
  it->second.insert(request);
  request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
}

void DevToolsHttpProtocolHandler::RequestCompleted(URLRequest* request) {
  RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
  if (it == request_to_socket_io_.end())
    return;

  HttpListenSocket* socket = it->second;
  request_to_socket_io_.erase(request);
  SocketToRequestsMap::iterator it2 = socket_to_requests_io_.find(socket);
  it2->second.erase(request);
  request_to_buffer_io_.erase(request);
  delete request;
}

void DevToolsHttpProtocolHandler::Send200(HttpListenSocket* socket,
                                          const std::string& data,
                                          const std::string& mime_type) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send200,
                        data,
                        mime_type));
}

void DevToolsHttpProtocolHandler::Send404(HttpListenSocket* socket) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send404));
}

void DevToolsHttpProtocolHandler::Send500(HttpListenSocket* socket,
                                          const std::string& message) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::Send500,
                        message));
}

void DevToolsHttpProtocolHandler::AcceptWebSocket(
    HttpListenSocket* socket,
    const HttpServerRequestInfo& request) {
  ChromeThread::PostTask(
      ChromeThread::IO, FROM_HERE,
      NewRunnableMethod(socket,
                        &HttpListenSocket::AcceptWebSocket,
                        request));
}

TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
  for (BrowserList::const_iterator it = BrowserList::begin(),
       end = BrowserList::end(); it != end; ++it) {
    TabStripModel* model = (*it)->tabstrip_model();
    for (int i = 0, size = model->count(); i < size; ++i) {
      NavigationController& controller =
          model->GetTabContentsAt(i)->controller();
      if (controller.session_id().id() == session_id)
        return controller.tab_contents();
    }
  }
  return NULL;
}
<|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/extensions/extension_apitest.h"
#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"

using content::WebContents;

class KeybindingApiTest : public ExtensionApiTest {
 public:
  KeybindingApiTest() {
    CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableExperimentalExtensionApis);
  }
  virtual ~KeybindingApiTest() {}

 protected:
  BrowserActionTestUtil GetBrowserActionsBar() {
    return BrowserActionTestUtil(browser());
  }

  bool OpenPopup(int index) {
    ResultCatcher catcher;
    ui_test_utils::WindowedNotificationObserver popup_observer(
        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
        content::NotificationService::AllSources());
    GetBrowserActionsBar().Press(index);
    popup_observer.Wait();
    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    return GetBrowserActionsBar().HasPopup();
  }
};

#if defined(OS_WIN)  // Not implemented on other platforms yet.
IN_PROC_BROWSER_TEST_F(KeybindingApiTest, Basic) {
  ASSERT_TRUE(test_server()->Start());
  ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_;
  const Extension* extension = GetSingleLoadedExtension();
  ASSERT_TRUE(extension) << message_;

  // Test that there is a browser action in the toolbar.
  ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions());

  ui_test_utils::NavigateToURL(browser(),
      test_server()->GetURL("files/extensions/test_file.txt"));

  // Simulate the browser action being clicked (Ctrl+Shift+F).
  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
      browser(), ui::VKEY_F, true, true, false, false));

  // Verify the command worked.
  WebContents* tab = browser()->GetSelectedWebContents();
  bool result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      tab->GetRenderViewHost(), L"",
      L"setInterval(function(){"
      L"  if(document.body.bgColor == 'red'){"
      L"    window.domAutomationController.send(true)}}, 100)",
      &result));
  ASSERT_TRUE(result);

  // Simulate the event being sent (Ctrl+Shift+Y).
  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
      browser(), ui::VKEY_Y, true, true, false, false));

  result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      tab->GetRenderViewHost(), L"",
      L"setInterval(function(){"
      L"  if(document.body.bgColor == 'blue'){"
      L"    window.domAutomationController.send(true)}}, 100)",
      &result));
  ASSERT_TRUE(result);
}
#endif  // OS_WIN
<commit_msg>Enable the keybinding test for TOOLKIT_VIEWS.<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/extensions/extension_apitest.h"
#include "chrome/browser/extensions/browser_action_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"

using content::WebContents;

class KeybindingApiTest : public ExtensionApiTest {
 public:
  KeybindingApiTest() {
    CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableExperimentalExtensionApis);
  }
  virtual ~KeybindingApiTest() {}

 protected:
  BrowserActionTestUtil GetBrowserActionsBar() {
    return BrowserActionTestUtil(browser());
  }

  bool OpenPopup(int index) {
    ResultCatcher catcher;
    ui_test_utils::WindowedNotificationObserver popup_observer(
        content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
        content::NotificationService::AllSources());
    GetBrowserActionsBar().Press(index);
    popup_observer.Wait();
    EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    return GetBrowserActionsBar().HasPopup();
  }
};

#if defined(TOOLKIT_VIEWS)  // Not implemented on other platforms yet.
IN_PROC_BROWSER_TEST_F(KeybindingApiTest, Basic) {
  ASSERT_TRUE(test_server()->Start());
  ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_;
  const Extension* extension = GetSingleLoadedExtension();
  ASSERT_TRUE(extension) << message_;

  // Test that there is a browser action in the toolbar.
  ASSERT_EQ(1, GetBrowserActionsBar().NumberOfBrowserActions());

  ui_test_utils::NavigateToURL(browser(),
      test_server()->GetURL("files/extensions/test_file.txt"));

  // Simulate the browser action being clicked (Ctrl+Shift+F).
  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
      browser(), ui::VKEY_F, true, true, false, false));

  // Verify the command worked.
  WebContents* tab = browser()->GetSelectedWebContents();
  bool result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      tab->GetRenderViewHost(), L"",
      L"setInterval(function(){"
      L"  if(document.body.bgColor == 'red'){"
      L"    window.domAutomationController.send(true)}}, 100)",
      &result));
  ASSERT_TRUE(result);

  // Simulate the event being sent (Ctrl+Shift+Y).
  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
      browser(), ui::VKEY_Y, true, true, false, false));

  result = false;
  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
      tab->GetRenderViewHost(), L"",
      L"setInterval(function(){"
      L"  if(document.body.bgColor == 'blue'){"
      L"    window.domAutomationController.send(true)}}, 100)",
      &result));
  ASSERT_TRUE(result);
}
#endif  // TOOLKIT_VIEWS
<|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/renderer_host/render_message_filter.h"

#include <fcntl.h>
#include <map>

#include "app/x11_util.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "chrome/browser/browser_thread.h"
#if defined(TOOLKIT_GTK)
#include "chrome/browser/printing/print_dialog_gtk.h"
#else
#include "chrome/browser/printing/print_dialog_cloud.h"
#endif
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
#include "gfx/gtk_native_view_id_manager.h"
#include "grit/generated_resources.h"
#include "third_party/WebKit/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/WebKit/chromium/public/x11/WebScreenInfoFactory.h"
#include "ui/base/clipboard/clipboard.h"

using WebKit::WebScreenInfo;
using WebKit::WebScreenInfoFactory;

namespace {

typedef std::map<int, FilePath> SequenceToPathMap;

struct PrintingSequencePathMap {
  SequenceToPathMap map;
  int sequence;
};

// No locking, only access on the FILE thread.
static base::LazyInstance<PrintingSequencePathMap>
    g_printing_file_descriptor_map(base::LINKER_INITIALIZED);

}  // namespace

// We get null window_ids passed into the two functions below; please see
// http://crbug.com/9060 for more details.

// Called on the BACKGROUND_X11 thread.
void RenderMessageFilter::DoOnGetScreenInfo(gfx::NativeViewId view,
                                            IPC::Message* reply_msg) {
  Display* display = x11_util::GetSecondaryDisplay();
  int screen = x11_util::GetDefaultScreen(display);
  WebScreenInfo results = WebScreenInfoFactory::screenInfo(display, screen);
  ViewHostMsg_GetScreenInfo::WriteReplyParams(reply_msg, results);
  Send(reply_msg);
}

// Called on the BACKGROUND_X11 thread.
void RenderMessageFilter::DoOnGetWindowRect(gfx::NativeViewId view,
                                            IPC::Message* reply_msg) {
  // This is called to get the x, y offset (in screen coordinates) of the given
  // view and its width and height.
  gfx::Rect rect;
  XID window;

  AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
  if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
    if (window) {
      int x, y;
      unsigned width, height;
      if (x11_util::GetWindowGeometry(&x, &y, &width, &height, window))
        rect = gfx::Rect(x, y, width, height);
    }
  }

  ViewHostMsg_GetWindowRect::WriteReplyParams(reply_msg, rect);
  Send(reply_msg);
}

// Return the top-level parent of the given window. Called on the
// BACKGROUND_X11 thread.
static XID GetTopLevelWindow(XID window) {
  bool parent_is_root;
  XID parent_window;

  if (!x11_util::GetWindowParent(&parent_window, &parent_is_root, window))
    return 0;
  if (parent_is_root)
    return window;

  return GetTopLevelWindow(parent_window);
}

// Called on the BACKGROUND_X11 thread.
void RenderMessageFilter::DoOnGetRootWindowRect(gfx::NativeViewId view,
                                                IPC::Message* reply_msg) {
  // This is called to get the screen coordinates and size of the browser
  // window itself.
  gfx::Rect rect;
  XID window;

  AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
  if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
    if (window) {
      const XID toplevel = GetTopLevelWindow(window);
      if (toplevel) {
        int x, y;
        unsigned width, height;
        if (x11_util::GetWindowGeometry(&x, &y, &width, &height, toplevel))
          rect = gfx::Rect(x, y, width, height);
      }
    }
  }

  ViewHostMsg_GetRootWindowRect::WriteReplyParams(reply_msg, rect);
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardIsFormatAvailable(
    ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
    IPC::Message* reply_msg) {
  const bool result = GetClipboard()->IsFormatAvailable(format, buffer);

  ViewHostMsg_ClipboardIsFormatAvailable::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadText(ui::Clipboard::Buffer buffer,
                                                IPC::Message* reply_msg) {
  string16 result;
  GetClipboard()->ReadText(buffer, &result);

  ViewHostMsg_ClipboardReadText::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadAsciiText(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  std::string result;
  GetClipboard()->ReadAsciiText(buffer, &result);

  ViewHostMsg_ClipboardReadAsciiText::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadHTML(ui::Clipboard::Buffer buffer,
                                                IPC::Message* reply_msg) {
  std::string src_url_str;
  string16 markup;
  GetClipboard()->ReadHTML(buffer, &markup, &src_url_str);
  const GURL src_url = GURL(src_url_str);

  ViewHostMsg_ClipboardReadHTML::WriteReplyParams(reply_msg, markup, src_url);
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadAvailableTypes(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  Send(reply_msg);
}

// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadData(ui::Clipboard::Buffer buffer,
                                                const string16& type,
                                                IPC::Message* reply_msg) {
  Send(reply_msg);
}
// Called on the UI thread.
void RenderMessageFilter::DoOnClipboardReadFilenames(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  Send(reply_msg);
}

void RenderMessageFilter::DoOnAllocateTempFileForPrinting(
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FileDescriptor temp_file_fd(-1, false);
  bool allow_print =
#if defined(TOOLKIT_GTK)
    !PrintDialogGtk::DialogShowing();
#else
    true;
#endif
  FilePath path;
  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
  const int sequence_number = g_printing_file_descriptor_map.Get().sequence++;

  if (allow_print &&
      file_util::CreateTemporaryFile(&path)) {
    int fd = open(path.value().c_str(), O_WRONLY);
    if (fd >= 0) {
      SequenceToPathMap::iterator it = map->find(sequence_number);
      if (it != map->end()) {
        NOTREACHED() << "Sequence number already in use. seq=" <<
            sequence_number;
      } else {
        (*map)[sequence_number] = path;
        temp_file_fd.fd = fd;
        temp_file_fd.auto_close = true;
      }
    }
  }

  ViewHostMsg_AllocateTempFileForPrinting::WriteReplyParams(
      reply_msg, temp_file_fd, sequence_number);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnTempFileForPrintingWritten(int sequence_number) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
  SequenceToPathMap::iterator it = map->find(sequence_number);
  if (it == map->end()) {
    NOTREACHED() << "Got a sequence that we didn't pass to the "
                    "renderer: " << sequence_number;
    return;
  }

#if defined(TOOLKIT_GTK)
  PrintDialogGtk::CreatePrintDialogForPdf(it->second);
#else
  if (cloud_print_enabled_)
    PrintDialogCloud::CreatePrintDialogForPdf(it->second);
  else
    NOTIMPLEMENTED();
#endif

  // Erase the entry in the map.
  map->erase(it);
}

// Called on the IO thread.
void RenderMessageFilter::OnGetScreenInfo(gfx::NativeViewId view,
                                          IPC::Message* reply_msg) {
   BrowserThread::PostTask(
      BrowserThread::BACKGROUND_X11, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnGetScreenInfo, view, reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnGetWindowRect(gfx::NativeViewId view,
                                          IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::BACKGROUND_X11, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnGetWindowRect, view, reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnGetRootWindowRect(gfx::NativeViewId view,
                                              IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::BACKGROUND_X11, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnGetRootWindowRect, view, reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardIsFormatAvailable(
    ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
    IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardIsFormatAvailable, format,
          buffer, reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadText(ui::Clipboard::Buffer buffer,
                                              IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadText, buffer,
          reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
                                                   IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadAsciiText, buffer,
          reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadHTML(ui::Clipboard::Buffer buffer,
                                              IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadHTML, buffer,
          reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadAvailableTypes(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadAvailableTypes, buffer,
          reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadData(ui::Clipboard::Buffer buffer,
                                              const string16& type,
                                              IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadData, buffer, type,
          reply_msg));
}

// Called on the IO thread.
void RenderMessageFilter::OnClipboardReadFilenames(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadFilenames, buffer,
          reply_msg));
}

void RenderMessageFilter::OnAllocateTempFileForPrinting(
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnAllocateTempFileForPrinting,
          reply_msg));
}

void RenderMessageFilter::OnTempFileForPrintingWritten(int sequence_number) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnTempFileForPrintingWritten,
          sequence_number));
}
<commit_msg>Convert comments to CurrentlyOn checks.<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/renderer_host/render_message_filter.h"

#include <fcntl.h>
#include <map>

#include "app/x11_util.h"
#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "chrome/browser/browser_thread.h"
#if defined(TOOLKIT_GTK)
#include "chrome/browser/printing/print_dialog_gtk.h"
#else
#include "chrome/browser/printing/print_dialog_cloud.h"
#endif
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
#include "gfx/gtk_native_view_id_manager.h"
#include "grit/generated_resources.h"
#include "third_party/WebKit/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/WebKit/chromium/public/x11/WebScreenInfoFactory.h"
#include "ui/base/clipboard/clipboard.h"

using WebKit::WebScreenInfo;
using WebKit::WebScreenInfoFactory;

namespace {

typedef std::map<int, FilePath> SequenceToPathMap;

struct PrintingSequencePathMap {
  SequenceToPathMap map;
  int sequence;
};

// No locking, only access on the FILE thread.
static base::LazyInstance<PrintingSequencePathMap>
    g_printing_file_descriptor_map(base::LINKER_INITIALIZED);

}  // namespace

// We get null window_ids passed into the two functions below; please see
// http://crbug.com/9060 for more details.

void RenderMessageFilter::DoOnGetScreenInfo(gfx::NativeViewId view,
                                            IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
  Display* display = x11_util::GetSecondaryDisplay();
  int screen = x11_util::GetDefaultScreen(display);
  WebScreenInfo results = WebScreenInfoFactory::screenInfo(display, screen);
  ViewHostMsg_GetScreenInfo::WriteReplyParams(reply_msg, results);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnGetWindowRect(gfx::NativeViewId view,
                                            IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
  // This is called to get the x, y offset (in screen coordinates) of the given
  // view and its width and height.
  gfx::Rect rect;
  XID window;

  AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
  if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
    if (window) {
      int x, y;
      unsigned width, height;
      if (x11_util::GetWindowGeometry(&x, &y, &width, &height, window))
        rect = gfx::Rect(x, y, width, height);
    }
  }

  ViewHostMsg_GetWindowRect::WriteReplyParams(reply_msg, rect);
  Send(reply_msg);
}

// Return the top-level parent of the given window. Called on the
// BACKGROUND_X11 thread.
static XID GetTopLevelWindow(XID window) {
  bool parent_is_root;
  XID parent_window;

  if (!x11_util::GetWindowParent(&parent_window, &parent_is_root, window))
    return 0;
  if (parent_is_root)
    return window;

  return GetTopLevelWindow(parent_window);
}

void RenderMessageFilter::DoOnGetRootWindowRect(gfx::NativeViewId view,
                                                IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
  // This is called to get the screen coordinates and size of the browser
  // window itself.
  gfx::Rect rect;
  XID window;

  AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
  if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
    if (window) {
      const XID toplevel = GetTopLevelWindow(window);
      if (toplevel) {
        int x, y;
        unsigned width, height;
        if (x11_util::GetWindowGeometry(&x, &y, &width, &height, toplevel))
          rect = gfx::Rect(x, y, width, height);
      }
    }
  }

  ViewHostMsg_GetRootWindowRect::WriteReplyParams(reply_msg, rect);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardIsFormatAvailable(
    ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  const bool result = GetClipboard()->IsFormatAvailable(format, buffer);

  ViewHostMsg_ClipboardIsFormatAvailable::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardReadText(ui::Clipboard::Buffer buffer,
                                                IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  string16 result;
  GetClipboard()->ReadText(buffer, &result);

  ViewHostMsg_ClipboardReadText::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardReadAsciiText(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  std::string result;
  GetClipboard()->ReadAsciiText(buffer, &result);

  ViewHostMsg_ClipboardReadAsciiText::WriteReplyParams(reply_msg, result);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardReadHTML(ui::Clipboard::Buffer buffer,
                                                IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  std::string src_url_str;
  string16 markup;
  GetClipboard()->ReadHTML(buffer, &markup, &src_url_str);
  const GURL src_url = GURL(src_url_str);

  ViewHostMsg_ClipboardReadHTML::WriteReplyParams(reply_msg, markup, src_url);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardReadAvailableTypes(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  Send(reply_msg);
}

void RenderMessageFilter::DoOnClipboardReadData(ui::Clipboard::Buffer buffer,
                                                const string16& type,
                                                IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  Send(reply_msg);
}
void RenderMessageFilter::DoOnClipboardReadFilenames(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  Send(reply_msg);
}

void RenderMessageFilter::DoOnAllocateTempFileForPrinting(
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  base::FileDescriptor temp_file_fd(-1, false);
  bool allow_print =
#if defined(TOOLKIT_GTK)
    !PrintDialogGtk::DialogShowing();
#else
    true;
#endif
  FilePath path;
  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
  const int sequence_number = g_printing_file_descriptor_map.Get().sequence++;

  if (allow_print &&
      file_util::CreateTemporaryFile(&path)) {
    int fd = open(path.value().c_str(), O_WRONLY);
    if (fd >= 0) {
      SequenceToPathMap::iterator it = map->find(sequence_number);
      if (it != map->end()) {
        NOTREACHED() << "Sequence number already in use. seq=" <<
            sequence_number;
      } else {
        (*map)[sequence_number] = path;
        temp_file_fd.fd = fd;
        temp_file_fd.auto_close = true;
      }
    }
  }

  ViewHostMsg_AllocateTempFileForPrinting::WriteReplyParams(
      reply_msg, temp_file_fd, sequence_number);
  Send(reply_msg);
}

void RenderMessageFilter::DoOnTempFileForPrintingWritten(int sequence_number) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
  SequenceToPathMap::iterator it = map->find(sequence_number);
  if (it == map->end()) {
    NOTREACHED() << "Got a sequence that we didn't pass to the "
                    "renderer: " << sequence_number;
    return;
  }

#if defined(TOOLKIT_GTK)
  PrintDialogGtk::CreatePrintDialogForPdf(it->second);
#else
  if (cloud_print_enabled_)
    PrintDialogCloud::CreatePrintDialogForPdf(it->second);
  else
    NOTIMPLEMENTED();
#endif

  // Erase the entry in the map.
  map->erase(it);
}

void RenderMessageFilter::OnGetScreenInfo(gfx::NativeViewId view,
                                          IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
     BrowserThread::BACKGROUND_X11, FROM_HERE,
     NewRunnableMethod(
         this, &RenderMessageFilter::DoOnGetScreenInfo, view, reply_msg));
}

void RenderMessageFilter::OnGetWindowRect(gfx::NativeViewId view,
                                          IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::BACKGROUND_X11, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnGetWindowRect, view, reply_msg));
}

void RenderMessageFilter::OnGetRootWindowRect(gfx::NativeViewId view,
                                              IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::BACKGROUND_X11, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnGetRootWindowRect, view, reply_msg));
}

void RenderMessageFilter::OnClipboardIsFormatAvailable(
    ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardIsFormatAvailable, format,
          buffer, reply_msg));
}

void RenderMessageFilter::OnClipboardReadText(ui::Clipboard::Buffer buffer,
                                              IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadText, buffer,
          reply_msg));
}

void RenderMessageFilter::OnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
                                                   IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadAsciiText, buffer,
          reply_msg));
}

void RenderMessageFilter::OnClipboardReadHTML(ui::Clipboard::Buffer buffer,
                                              IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadHTML, buffer,
          reply_msg));
}

void RenderMessageFilter::OnClipboardReadAvailableTypes(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadAvailableTypes, buffer,
          reply_msg));
}

void RenderMessageFilter::OnClipboardReadData(ui::Clipboard::Buffer buffer,
                                              const string16& type,
                                              IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadData, buffer, type,
          reply_msg));
}

void RenderMessageFilter::OnClipboardReadFilenames(
    ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnClipboardReadFilenames, buffer,
          reply_msg));
}

void RenderMessageFilter::OnAllocateTempFileForPrinting(
    IPC::Message* reply_msg) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnAllocateTempFileForPrinting,
          reply_msg));
}

void RenderMessageFilter::OnTempFileForPrintingWritten(int sequence_number) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
  BrowserThread::PostTask(
      BrowserThread::FILE, FROM_HERE,
      NewRunnableMethod(
          this, &RenderMessageFilter::DoOnTempFileForPrintingWritten,
          sequence_number));
}
<|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 <vector>

#include "base/command_line.h"
#include "base/lock.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/safe_browsing/protocol_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/host_resolver.h"
#include "net/base/load_flags.h"
#include "net/base/net_log.h"
#include "net/socket/tcp_pinger.h"
#include "testing/gtest/include/gtest/gtest.h"

const char kHost[] = "127.0.0.1";
const char kPort[] = "40101";
const char kDataFile[] = "datafile.dat";
const char kUrlVerifyPath[] = "/safebrowsing/verify_urls";
const char kTestCompletePath[] = "/test_complete";

class SafeBrowsingTestServer {
 public:
  SafeBrowsingTestServer(const std::string& hostname,
                         const std::string& port,
                         const std::string& datafile)
      : hostname_(hostname),
        port_(port),
        datafile_(datafile),
        server_handle_(base::kNullProcessHandle) {
  }
  ~SafeBrowsingTestServer() {
    EXPECT_EQ(server_handle_, base::kNullProcessHandle);
  }

  // Start the python server test suite.
  bool Start() {
    // Get path to python server script
    FilePath testserver_path;
    if (!PathService::Get(base::DIR_SOURCE_ROOT, &testserver_path))
      return false;
    testserver_path = testserver_path
        .Append(FILE_PATH_LITERAL("chrome"))
        .Append(FILE_PATH_LITERAL("browser"))
        .Append(FILE_PATH_LITERAL("safe_browsing"))
        .Append(FILE_PATH_LITERAL("testserver"));
    AppendToPythonPath(testserver_path);

    FilePath testserver = testserver_path.Append(
        FILE_PATH_LITERAL("safebrowsing_test_server.py"));

#if defined(OS_WIN)
    FilePath datafile = testserver_path.Append(ASCIIToWide(datafile_));
    FilePath python_runtime;
    // Get path to python interpreter
    if (!PathService::Get(base::DIR_SOURCE_ROOT, &python_runtime))
      return false;
    python_runtime = python_runtime
        .Append(FILE_PATH_LITERAL("third_party"))
        .Append(FILE_PATH_LITERAL("python_24"))
        .Append(FILE_PATH_LITERAL("python.exe"));

    std::wstring command_line =
        L"\"" + python_runtime.ToWStringHack() + L"\" " +
        L"\"" + testserver.ToWStringHack() +
        L"\" --port=" + UTF8ToWide(port_) +
        L"\" --datafile=" + datafile.ToWStringHack() + L"\"";

    if (!base::LaunchApp(command_line, false, true, &server_handle_)) {
      LOG(ERROR) << "Failed to launch " << command_line;
      return false;
    }
#elif defined(OS_POSIX)
    FilePath datafile = testserver_path.Append(datafile_);
    std::vector<std::string> command_line;
    command_line.push_back("python");
    command_line.push_back(testserver.value());
    command_line.push_back("--port=" + port_);
    command_line.push_back("--datafile=" + datafile.value());

    base::file_handle_mapping_vector no_mappings;
    LOG(INFO) << "Trying to launch " << command_line[0] << " ...";
    if (!base::LaunchApp(command_line, no_mappings, false, &server_handle_)) {
      LOG(ERROR) << "Failed to launch " << command_line[0] << " ...";
      return false;
    }
#endif

    // Let the server start, then verify that it's up.
    // Our server is Python, and takes about 500ms to start
    // up the first time, and about 200ms after that.
    if (!WaitServerToStart(hostname_, StringToInt(port_))) {
      LOG(ERROR) << "Failed to connect to server";
      Stop();
      return false;
    }

    LOG(INFO) << "Started on port " << port_;
    return true;
  }

  // Stop the python server test suite.
  bool Stop() {
    if (!server_handle_) {
      return true;
    }

    // First check if the process has already terminated.
    bool ret = base::WaitForSingleProcess(server_handle_, 0);
    if (!ret) {
      ret = base::KillProcess(server_handle_, 1, true);
    }

    if (ret) {
      base::CloseProcessHandle(server_handle_);
      server_handle_ = base::kNullProcessHandle;
      LOG(INFO) << "Stopped.";
    } else {
      LOG(INFO) << "Kill failed?";
      return false;
    }
    return true;
  }

 private:
  // Make sure the test server is actually started and ready to serve.
  bool WaitServerToStart(const std::string& host_name, int port) {
    net::AddressList addr;
    scoped_refptr<net::HostResolver> resolver(net::CreateSystemHostResolver(
        net::HostResolver::kDefaultParallelism));
    net::HostResolver::RequestInfo info(host_name, port);
    int rv = resolver->Resolve(info, &addr, NULL, NULL, net::BoundNetLog());
    if (rv != net::OK)
      return false;

    net::TCPPinger pinger(addr);
    rv = pinger.Ping(base::TimeDelta::FromSeconds(kConnectionTimeoutSec),
                     kRetryAttempts);
    return rv == net::OK;
  }

  void AppendToPythonPath(const FilePath& dir) {
#if defined(OS_WIN)
    const wchar_t kPythonPath[] = L"PYTHONPATH";
    wchar_t oldpath[8192];
    if (GetEnvironmentVariable(kPythonPath, oldpath, arraysize(oldpath)) == 0) {
      SetEnvironmentVariableW(kPythonPath, dir.value().c_str());
    } else if (!wcsstr(oldpath, dir.value().c_str())) {
      std::wstring newpath(oldpath);
      newpath.append(L";");
      newpath.append(dir.value());
      SetEnvironmentVariableW(kPythonPath, newpath.c_str());
    }
#elif defined(OS_POSIX)
    const char kPythonPath[] = "PYTHONPATH";
    const char* oldpath = getenv(kPythonPath);
    // setenv() leaks memory intentionally on Mac
    if (!oldpath) {
      setenv(kPythonPath, dir.value().c_str(), 1);
    } else if (!strstr(oldpath, dir.value().c_str())) {
      std::string newpath(oldpath);
      newpath.append(":");
      newpath.append(dir.value());
      setenv(kPythonPath, newpath.c_str(), 1);
    }
#endif
  }

  std::string hostname_;
  std::string port_;
  std::string datafile_;
  static const int kConnectionTimeoutSec = 5;
  static const int kRetryAttempts = 3;
  base::ProcessHandle server_handle_;
};

typedef struct {
  std::string url;
  std::string list_name;
  bool is_phishing;
} PhishingUrl;

// Parses server response for verify_urls. The expected format is:
//
// first.random.url.com/   internal-test-shavar   yes
// second.random.url.com/  internal-test-shavar   yes
// ...
static bool ParsePhishingUrls(const std::string& data,
                              std::vector<PhishingUrl>* phishing_urls) {
  std::vector<std::string> urls;
  SplitString(data, '\n', &urls);
  for (size_t i = 0; i < urls.size(); ++i) {
    PhishingUrl phishing_url;
    std::vector<std::string> url_parts;
    SplitStringAlongWhitespace(urls[i], &url_parts);
    if (url_parts.size() < 3) {
      CHECK(false) << "Unexpected URL format in phishing URL list: "
                   << urls[i];
      return false;
    }
    phishing_url.url = url_parts[0];
    phishing_url.list_name = url_parts[1];
    if (url_parts[2] == "yes") {
      phishing_url.is_phishing = true;
    } else {
      CHECK_EQ("no", url_parts[2]);
      phishing_url.is_phishing = false;
    }
    phishing_urls->push_back(phishing_url);
  }
  return true;
}

// This starts the browser and keeps status of states related to SafeBrowsing.
class SafeBrowsingServiceTest : public InProcessBrowserTest {
 public:
  SafeBrowsingServiceTest()
    : safe_browsing_service_(NULL),
      is_update_in_progress_(false),
      is_initial_request_(false),
      is_update_scheduled_(false),
      is_checked_url_in_db_(false),
      is_checked_url_safe_(false) {
  }

  void UpdateSafeBrowsingStatus() {
    CHECK(safe_browsing_service_);
    AutoLock lock(update_status_mutex_);
    is_update_in_progress_ = safe_browsing_service_->IsUpdateInProgress();
    is_initial_request_ =
        safe_browsing_service_->protocol_manager_->is_initial_request();
    last_update_ = safe_browsing_service_->protocol_manager_->last_update();
    is_update_scheduled_ =
        safe_browsing_service_->protocol_manager_->update_timer_.IsRunning();
  }

  void ForceUpdate() {
    CHECK(safe_browsing_service_);
    safe_browsing_service_->protocol_manager_->ForceScheduleNextUpdate(0);
  }

  void CheckUrl(SafeBrowsingService::Client* helper, const GURL& url) {
    CHECK(safe_browsing_service_);
    AutoLock lock(update_status_mutex_);
    if (safe_browsing_service_->CheckUrl(url, helper)) {
      is_checked_url_in_db_ = false;
      return;
    }
    is_checked_url_in_db_ = true;
  }

  bool is_checked_url_in_db() {
    AutoLock l(update_status_mutex_);
    return is_checked_url_in_db_;
  }

  void set_is_checked_url_safe(bool safe) {
    AutoLock l(update_status_mutex_);
    is_checked_url_safe_ = safe;
  }

  bool is_checked_url_safe() {
    AutoLock l(update_status_mutex_);
    return is_checked_url_safe_;
  }

  bool is_update_in_progress() {
    AutoLock l(update_status_mutex_);
    return is_update_in_progress_;
  }

  bool is_initial_request() {
    AutoLock l(update_status_mutex_);
    return is_initial_request_;
  }

  base::Time last_update() {
    AutoLock l(update_status_mutex_);
    return last_update_;
  }

  bool is_update_scheduled() {
    AutoLock l(update_status_mutex_);
    return is_update_scheduled_;
  }

 protected:
  void InitSafeBrowsingService() {
    safe_browsing_service_ =
        g_browser_process->resource_dispatcher_host()->safe_browsing_service();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    // Makes sure the auto update is not triggered. This test will force the
    // update when needed.
    command_line->AppendSwitch(switches::kSbDisableAutoUpdate);
    command_line->AppendSwitchWithValue(
        switches::kSbInfoURLPrefix, "http://localhost:40101/safebrowsing");
    command_line->AppendSwitchWithValue(
        switches::kSbMacKeyURLPrefix, "http://localhost:40101/safebrowsing");
  }

  void SetTestStep(int step) {
    std::string test_step = StringPrintf("&test_step=%d", step);
    safe_browsing_service_->protocol_manager_->set_additional_query(test_step);
  }

 private:
  SafeBrowsingService* safe_browsing_service_;

  // Protects all variables below since they are updated on IO thread but
  // read on UI thread.
  Lock update_status_mutex_;
  // States associated with safebrowsing service updates.
  bool is_update_in_progress_;
  bool is_initial_request_;
  base::Time last_update_;
  bool is_update_scheduled_;
  // Indicates if there is a match between a URL's prefix and safebrowsing
  // database (thus potentially it is a phishing url).
  bool is_checked_url_in_db_;
  // True if last verified URL is not a phishing URL and thus it is safe.
  bool is_checked_url_safe_;
  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTest);
};

// A ref counted helper class that handles callbacks between IO thread and UI
// thread.
class SafeBrowsingServiceTestHelper
    : public base::RefCountedThreadSafe<SafeBrowsingServiceTestHelper>,
      public SafeBrowsingService::Client,
      public URLFetcher::Delegate {
 public:
  explicit SafeBrowsingServiceTestHelper(
      SafeBrowsingServiceTest* safe_browsing_test)
      : safe_browsing_test_(safe_browsing_test) {
  }

  // Callbacks for SafeBrowsingService::Client.
  virtual void OnUrlCheckResult(const GURL& url,
                                SafeBrowsingService::UrlCheckResult result) {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    CHECK(safe_browsing_test_->is_checked_url_in_db());
    safe_browsing_test_->set_is_checked_url_safe(
        result == SafeBrowsingService::URL_SAFE);
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::OnCheckUrlOnIOThreadDone));
  }
  virtual void OnBlockingPageComplete(bool proceed) {
    NOTREACHED() << "Not implemented.";
  }

  // Functions and callbacks to start the safebrowsing database update.
  void ForceUpdate() {
    ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::ForceUpdateInIOThread));
  }
  void ForceUpdateInIOThread() {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    safe_browsing_test_->ForceUpdate();
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::OnForceUpdateDone));
  }
  void OnForceUpdateDone() {
    StopUILoop();
  }

  // Functions and callbacks related to CheckUrl. These are used to verify
  // phishing URLs.
  void CheckUrl(const GURL& url) {
    ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::CheckUrlOnIOThread, url));
  }
  void CheckUrlOnIOThread(const GURL& url) {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    safe_browsing_test_->CheckUrl(this, url);
    if (!safe_browsing_test_->is_checked_url_in_db()) {
      // Ends the checking since this url's prefix is not in database.
      ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(
          this, &SafeBrowsingServiceTestHelper::OnCheckUrlOnIOThreadDone));
    }
    // Otherwise, OnCheckUrlOnIOThreadDone is called in OnUrlCheckResult since
    // safebrowsing service further fetches hashes from safebrowsing server.
  }
  void OnCheckUrlOnIOThreadDone() {
    StopUILoop();
  }

  // Functions and callbacks related to safebrowsing server status.
  void CheckStatusAfterDelay(int64 wait_time_sec) {
    ChromeThread::PostDelayedTask(
        ChromeThread::IO,
        FROM_HERE,
        NewRunnableMethod(this,
            &SafeBrowsingServiceTestHelper::CheckStatusOnIOThread),
        wait_time_sec * 1000);
  }
  void CheckStatusOnIOThread() {
    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    safe_browsing_test_->UpdateSafeBrowsingStatus();
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::OnCheckStatusOnIOThreadDone));
  }
  void OnCheckStatusOnIOThreadDone() {
    StopUILoop();
  }

  // Calls test server to fetch urls for verification.
  void FetchUrlsToVerify(const std::string& host,
                         const std::string& port,
                         int test_step) {
    GURL url(StringPrintf("http://%s:%s/%s?"
                          "client=chromium&appver=1.0&pver=2.2&test_step=%d",
                          host.c_str(), port.c_str(), kUrlVerifyPath,
                          test_step));
    url_fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this));
    url_fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE);
    url_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
    url_fetcher_->Start();
  }

  // Calls test server to check if test data is done.
  void VerifyTestComplete(const std::string& host,
                          const std::string& port,
                          int test_step) {
    GURL url(StringPrintf("http://%s:%s/%s?test_step=%d",
                          host.c_str(), port.c_str(), kTestCompletePath,
                          test_step));
    url_fetcher_.reset(new URLFetcher(url, URLFetcher::GET, this));
    url_fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE);
    url_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
    url_fetcher_->Start();
  }

  // Callback for URLFetcher.
  virtual void OnURLFetchComplete(const URLFetcher* source,
                                  const GURL& url,
                                  const URLRequestStatus& status,
                                  int response_code,
                                  const ResponseCookies& cookies,
                                  const std::string& data) {
    response_data_ = data;
    StopUILoop();
  }

  const std::string& response_data() {
    return response_data_;
  }

 private:
  // Stops UI loop after desired status is updated.
  void StopUILoop() {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
    MessageLoopForUI::current()->Quit();
  }

  base::OneShotTimer<SafeBrowsingServiceTestHelper> check_update_timer_;
  SafeBrowsingServiceTest* safe_browsing_test_;
  scoped_ptr<URLFetcher> url_fetcher_;
  std::string response_data_;
  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTestHelper);
};

IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, SafeBrowsingSystemTest) {
  InitSafeBrowsingService();
  scoped_refptr<SafeBrowsingServiceTestHelper> safe_browsing_helper =
      new SafeBrowsingServiceTestHelper(this);

  // Waits for 1 sec and makes sure safebrowsing update is not happening.
  // MessageLoop will stop once OnCheckStatusOnIOThreadDone in
  // safe_browsing_helper is called and status from safe_browsing_service_
  // is checked.
  safe_browsing_helper->CheckStatusAfterDelay(1);
  ui_test_utils::RunMessageLoop();
  EXPECT_FALSE(is_update_in_progress());
  EXPECT_TRUE(is_initial_request());
  EXPECT_FALSE(is_update_scheduled());
  EXPECT_TRUE(last_update().is_null());

  // Verify with a test phishing URL. Loop will stop once
  // OnCheckUrlOnIOThreadDone in safe_browsing_helper is called and URL check
  // is done.
  const char test_url[] = "http://ianfette.org";
  safe_browsing_helper->CheckUrl(GURL(test_url));
  ui_test_utils::RunMessageLoop();
  EXPECT_TRUE(is_checked_url_in_db());

  SafeBrowsingTestServer test_server(kHost, kPort, kDataFile);
  test_server.Start();
  // Limits max test steps so the test won't run wild.
  const int kMaxSteps = 10;
  int last_step = 0;
  // Starts from test step 1.
  for (int step = 1; step < kMaxSteps; step++) {
    // Every step should be a fresh start.
    EXPECT_FALSE(is_update_in_progress());
    EXPECT_FALSE(is_update_scheduled());

    // Starts safebrowsing update on IO thread. Waits till scheduled
    // update finishes. Stops waiting after kMaxWaitSec if the update
    // could not finish.
    base::Time now = base::Time::Now();
    SetTestStep(step);
    safe_browsing_helper->ForceUpdate();
    ui_test_utils::RunMessageLoop();
    int wait_sec = 0;
    const int kMaxWaitSec = 10;
    while ((is_update_scheduled() || is_update_in_progress()) &&
           wait_sec++ < kMaxWaitSec) {
      safe_browsing_helper->CheckStatusAfterDelay(1);
      ui_test_utils::RunMessageLoop();
    }
    EXPECT_LT(wait_sec, kMaxWaitSec)
        << "Can't finish update in required time limit!";
    if (last_update() < now) {
      // This means no data available anymore.
      break;
    }

    // Fetches urls to verify and waits till server responses with data.
    safe_browsing_helper->FetchUrlsToVerify(kHost, kPort, step);
    ui_test_utils::RunMessageLoop();
    LOG(INFO) << safe_browsing_helper->response_data();

    std::vector<PhishingUrl> phishing_urls;
    EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper->response_data(),
                                  &phishing_urls));
    for (size_t j = 0; j < phishing_urls.size(); ++j) {
      // Verifes with server if a URL is a phishing URL and waits till server
      // responses.
      safe_browsing_helper->CheckUrl(GURL(phishing_urls[j].url));
      ui_test_utils::RunMessageLoop();
      if (phishing_urls[j].is_phishing) {
        EXPECT_TRUE(is_checked_url_in_db());
        EXPECT_FALSE(is_checked_url_safe());
      } else {
        EXPECT_TRUE(is_checked_url_in_db());
      }
    }
    last_step = step;
  }

  // Verifies with server if test is done and waits till server responses.
  safe_browsing_helper->VerifyTestComplete(kHost, kPort, last_step);
  ui_test_utils::RunMessageLoop();
  EXPECT_EQ("yes", safe_browsing_helper->response_data());
  test_server.Stop();
}
<commit_msg>Revert 55138 - Full end to end test using safebrowsing test server suite.<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 "base/command_line.h"
#include "base/lock.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/safe_browsing/protocol_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

// This starts the browser and keeps status of states related to SafeBrowsing.
class SafeBrowsingServiceTest : public InProcessBrowserTest {
 public:
  SafeBrowsingServiceTest()
    : safe_browsing_service_(NULL),
      is_update_in_progress_(false),
      is_initial_request_(false),
      is_update_scheduled_(false),
      is_url_match_in_db_(false) {
  }

  void UpdateSafeBrowsingStatus() {
    CHECK(safe_browsing_service_);
    AutoLock lock(update_status_mutex_);
    is_update_in_progress_ = safe_browsing_service_->IsUpdateInProgress();
    is_initial_request_ =
        safe_browsing_service_->protocol_manager_->is_initial_request();
    last_update_ = safe_browsing_service_->protocol_manager_->last_update();
    is_update_scheduled_ =
        safe_browsing_service_->protocol_manager_->update_timer_.IsRunning();
  }

  void CheckUrl(SafeBrowsingService::Client* helper, const GURL& url) {
    CHECK(safe_browsing_service_);
    AutoLock lock(update_status_mutex_);
    if (!safe_browsing_service_->CheckUrl(url, helper)) {
      safe_browsing_service_->CancelCheck(helper);
      is_url_match_in_db_ = false;
    }
    is_url_match_in_db_ = true;
  }

  bool is_url_match_in_db() {
    AutoLock l(update_status_mutex_);
    return is_url_match_in_db_;
  }

  bool is_update_in_progress() {
    AutoLock l(update_status_mutex_);
    return is_update_in_progress_;
  }

  bool is_initial_request() {
    AutoLock l(update_status_mutex_);
    return is_initial_request_;
  }

  base::Time last_update() {
    AutoLock l(update_status_mutex_);
    return last_update_;
  }

  bool is_update_scheduled() {
    AutoLock l(update_status_mutex_);
    return is_update_scheduled_;
  }

 protected:
  void InitSafeBrowsingService() {
    safe_browsing_service_ =
        g_browser_process->resource_dispatcher_host()->safe_browsing_service();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    // Makes sure the auto update is not triggered. This test will force the
    // update when needed.
    command_line->AppendSwitch(switches::kSbDisableAutoUpdate);
  }

 private:
  SafeBrowsingService* safe_browsing_service_;

  // Protects all variables below since they are updated on IO thread but
  // read on UI thread.
  Lock update_status_mutex_;
  // States associated with safebrowsing service updates.
  bool is_update_in_progress_;
  bool is_initial_request_;
  base::Time last_update_;
  bool is_update_scheduled_;
  // Indicates if there is a match between a URL's prefix and safebrowsing
  // database (thus potentially it is a phishing url).
  bool is_url_match_in_db_;
  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTest);
};

// A ref counted helper class that handles callbacks between IO thread and UI
// thread.
class SafeBrowsingServiceTestHelper
    : public base::RefCountedThreadSafe<SafeBrowsingServiceTestHelper>,
      public SafeBrowsingService::Client {
 public:
  explicit SafeBrowsingServiceTestHelper(
      SafeBrowsingServiceTest* safe_browsing_test)
      : safe_browsing_test_(safe_browsing_test) {
  }

  // Callbacks for SafeBrowsingService::Client. Not implemented yet.
  virtual void OnUrlCheckResult(const GURL& url,
                                SafeBrowsingService::UrlCheckResult result) {
    NOTREACHED() << "Not implemented.";
  }
  virtual void OnBlockingPageComplete(bool proceed) {
    NOTREACHED() << "Not implemented.";
  }

  // Functions and callbacks related to CheckUrl. These are used to verify if
  // a URL is a phishing URL.
  void CheckUrl(const GURL& url) {
    ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::CheckUrlOnIOThread, url));
  }
  void CheckUrlOnIOThread(const GURL& url) {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    safe_browsing_test_->CheckUrl(this, url);
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::OnCheckUrlOnIOThreadDone));
  }
  void OnCheckUrlOnIOThreadDone() {
    StopUILoop();
  }

  // Functions and callbacks related to safebrowsing server status.
  void CheckStatusOnIOThread() {
    DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
    safe_browsing_test_->UpdateSafeBrowsingStatus();
    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE, NewRunnableMethod(this,
        &SafeBrowsingServiceTestHelper::OnCheckStatusOnIOThreadDone));
  }
  void OnCheckStatusOnIOThreadDone() {
    StopUILoop();
  }
  void CheckStatusAfterDelay(int64 wait_time_sec) {
    ChromeThread::PostDelayedTask(
        ChromeThread::IO,
        FROM_HERE,
        NewRunnableMethod(this,
            &SafeBrowsingServiceTestHelper::CheckStatusOnIOThread),
        wait_time_sec * 1000);
  }

 private:
  // Stops UI loop after desired status is updated.
  void StopUILoop() {
    CHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
    MessageLoopForUI::current()->Quit();
  }

  base::OneShotTimer<SafeBrowsingServiceTestHelper> check_update_timer_;
  SafeBrowsingServiceTest* safe_browsing_test_;
  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTestHelper);
};

IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, SafeBrowsingSystemTest) {
  InitSafeBrowsingService();
  scoped_refptr<SafeBrowsingServiceTestHelper> safe_browsing_helper =
      new SafeBrowsingServiceTestHelper(this);

  // Waits for 1 sec and makes sure safebrowsing update is not happening.
  safe_browsing_helper->CheckStatusAfterDelay(1);
  // Loop will stop once OnCheckStatusOnIOThreadDone in safe_browsing_helper
  // is called and status from safe_browsing_service_ is checked.
  ui_test_utils::RunMessageLoop();
  EXPECT_FALSE(is_update_in_progress());
  EXPECT_TRUE(is_initial_request());
  EXPECT_FALSE(is_update_scheduled());
  EXPECT_TRUE(last_update().is_null());

  // Verify URL.
  const char test_url[] = "http://ianfette.org";
  safe_browsing_helper->CheckUrl(GURL(test_url));
  // Loop will stop once OnCheckUrlOnIOThreadDone in safe_browsing_helper
  // is called and url check is done.
  ui_test_utils::RunMessageLoop();
  EXPECT_TRUE(is_url_match_in_db());

  // TODO(lzheng): Add tests to launch a testing safebrowsing server
  // and issue requests repeatedly:
  // http://code.google.com/p/google-safe-browsing/wiki/ProtocolTesting
}
<|endoftext|>