text: // 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/test/automation/tab_proxy.h"
  
  #include <algorithm>
  
  #include "base/json/json_string_value_serializer.h"
  #include "base/logging.h"
  #include "base/string16.h"
  #include "base/threading/platform_thread.h"
  #include "base/utf_string_conversions.h"
  #include "chrome/common/automation_constants.h"
  #include "chrome/common/automation_messages.h"
  #include "chrome/test/automation/automation_json_requests.h"
  #include "chrome/test/automation/automation_proxy.h"
  #include "chrome/test/automation/browser_proxy.h"
  #include "googleurl/src/gurl.h"
  
  TabProxy::TabProxy(AutomationMessageSender* sender,
                     AutomationHandleTracker* tracker,
                     int handle)
      : AutomationResourceProxy(tracker, sender, handle) {
  }
  
  bool TabProxy::GetTabTitle(std::wstring* title) const {
    if (!is_valid())
      return false;
  
    if (!title) {
      NOTREACHED();
      return false;
    }
  
    int tab_title_size_response = 0;
  
    bool succeeded = sender_->Send(
        new AutomationMsg_TabTitle(handle_, &tab_title_size_response, title));
    return succeeded;
  }
  
  bool TabProxy::GetTabIndex(int* index) const {
    if (!is_valid())
      return false;
  
    if (!index) {
      NOTREACHED();
      return false;
    }
  
    return sender_->Send(new AutomationMsg_TabIndex(handle_, index));
  }
  
  int TabProxy::FindInPage(const std::wstring& search_string,
                           FindInPageDirection forward,
                           FindInPageCase match_case,
                           bool find_next,
                           int* ordinal) {
    if (!is_valid())
      return -1;
  
    AutomationMsg_Find_Params params;
    params.search_string = WideToUTF16Hack(search_string);
    params.find_next = find_next;
    params.match_case = (match_case == CASE_SENSITIVE);
    params.forward = (forward == FWD);
  
    int matches = 0;
    int ordinal2 = 0;
    bool succeeded = sender_->Send(new AutomationMsg_Find(handle_,
                                                          params,
                                                          &ordinal2,
                                                          &matches));
    if (!succeeded)
      return -1;
    if (ordinal)
      *ordinal = ordinal2;
    return matches;
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::NavigateToURL(
      const GURL& url) {
    return NavigateToURLBlockUntilNavigationsComplete(url, 1);
  }
  
  AutomationMsg_NavigationResponseValues
      TabProxy::NavigateToURLBlockUntilNavigationsComplete(
          const GURL& url, int number_of_navigations) {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues navigate_response =
        AUTOMATION_MSG_NAVIGATION_ERROR;
  
   sender_->Send(new AutomationMsg_NavigateToURLBlockUntilNavigationsComplete(
        handle_, url, number_of_navigations, &navigate_response));
  
    return navigate_response;
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::GoBack() {
    return GoBackBlockUntilNavigationsComplete(1);
  }
  
  AutomationMsg_NavigationResponseValues
      TabProxy::GoBackBlockUntilNavigationsComplete(int number_of_navigations) {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues navigate_response =
        AUTOMATION_MSG_NAVIGATION_ERROR;
    sender_->Send(new AutomationMsg_GoBackBlockUntilNavigationsComplete(
        handle_, number_of_navigations, &navigate_response));
    return navigate_response;
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::GoForward() {
    return GoForwardBlockUntilNavigationsComplete(1);
  }
  
  AutomationMsg_NavigationResponseValues
      TabProxy::GoForwardBlockUntilNavigationsComplete(
          int number_of_navigations) {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues navigate_response =
        AUTOMATION_MSG_NAVIGATION_ERROR;
    sender_->Send(new AutomationMsg_GoForwardBlockUntilNavigationsComplete(
        handle_, number_of_navigations, &navigate_response));
    return navigate_response;
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::Reload() {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues navigate_response =
        AUTOMATION_MSG_NAVIGATION_ERROR;
    sender_->Send(new AutomationMsg_Reload(handle_, &navigate_response));
    return navigate_response;
  }
  
  bool TabProxy::GetCurrentURL(GURL* url) const {
    if (!is_valid())
      return false;
  
    if (!url) {
      NOTREACHED();
      return false;
    }
  
    bool succeeded = false;
    sender_->Send(new AutomationMsg_TabURL(handle_, &succeeded, url));
    return succeeded;
  }
  
  bool TabProxy::NavigateToURLAsync(const GURL& url) {
    if (!is_valid())
      return false;
  
    bool status = false;
    sender_->Send(new AutomationMsg_NavigationAsync(handle_,
                                                    url,
                                                    &status));
    return status;
  }
  
  bool TabProxy::ExecuteAndExtractString(const std::wstring& frame_xpath,
                                         const std::wstring& jscript,
                                         std::wstring* string_value) {
    scoped_ptr<Value> root(ExecuteAndExtractValue(frame_xpath, jscript));
    if (root == NULL)
      return false;
  
    DCHECK(root->IsType(Value::TYPE_LIST));
    Value* value = NULL;
    bool succeeded = static_cast<ListValue*>(root.get())->Get(0, &value);
    if (succeeded) {
      string16 read_value;
      succeeded = value->GetAsString(&read_value);
      if (succeeded) {
        // TODO(viettrungluu): remove conversion. (But should |jscript| be UTF-8?)
        *string_value = UTF16ToWideHack(read_value);
      }
    }
    return succeeded;
  }
  
  bool TabProxy::ExecuteAndExtractBool(const std::wstring& frame_xpath,
                                       const std::wstring& jscript,
                                       bool* bool_value) {
    scoped_ptr<Value> root(ExecuteAndExtractValue(frame_xpath, jscript));
    if (root == NULL)
      return false;
  
    bool read_value = false;
    DCHECK(root->IsType(Value::TYPE_LIST));
    Value* value = NULL;
    bool succeeded = static_cast<ListValue*>(root.get())->Get(0, &value);
    if (succeeded) {
      succeeded = value->GetAsBoolean(&read_value);
      if (succeeded) {
        *bool_value = read_value;
      }
    }
    return succeeded;
  }
  
  bool TabProxy::ExecuteAndExtractInt(const std::wstring& frame_xpath,
                                      const std::wstring& jscript,
                                      int* int_value) {
    scoped_ptr<Value> root(ExecuteAndExtractValue(frame_xpath, jscript));
    if (root == NULL)
      return false;
  
    int read_value = 0;
    DCHECK(root->IsType(Value::TYPE_LIST));
    Value* value = NULL;
    bool succeeded = static_cast<ListValue*>(root.get())->Get(0, &value);
    if (succeeded) {
      succeeded = value->GetAsInteger(&read_value);
      if (succeeded) {
        *int_value = read_value;
      }
    }
    return succeeded;
  }
  
  Value* TabProxy::ExecuteAndExtractValue(const std::wstring& frame_xpath,
                                          const std::wstring& jscript) {
    if (!is_valid())
      return NULL;
  
    std::string json;
    if (!sender_->Send(new AutomationMsg_DomOperation(handle_, frame_xpath,
                                                      jscript, &json))) {
      return NULL;
    }
    // Wrap |json| in an array before deserializing because valid JSON has an
    // array or an object as the root.
    json.insert(0, "[");
    json.append("]");
  
    JSONStringValueSerializer deserializer(json);
    return deserializer.Deserialize(NULL, NULL);
  }
  
  bool TabProxy::GetCookies(const GURL& url, std::string* cookies) {
    if (!is_valid())
      return false;
  
    int size = 0;
    return sender_->Send(new AutomationMsg_GetCookies(url, handle_, &size,
                                                      cookies));
  }
  
  bool TabProxy::GetCookieByName(const GURL& url,
                                 const std::string& name,
                                 std::string* cookie) {
    std::string cookies;
    if (!GetCookies(url, &cookies))
      return false;
  
    std::string namestr = name + "=";
    std::string::size_type idx = cookies.find(namestr);
    if (idx != std::string::npos) {
      cookies.erase(0, idx + namestr.length());
      *cookie = cookies.substr(0, cookies.find(";"));
    } else {
      cookie->clear();
    }
  
    return true;
  }
  
  bool TabProxy::Close() {
    return Close(false);
  }
  
  bool TabProxy::Close(bool wait_until_closed) {
    if (!is_valid())
      return false;
  
    bool succeeded = false;
    sender_->Send(new AutomationMsg_CloseTab(handle_, wait_until_closed,
                                             &succeeded));
    return succeeded;
  }
  
  #if defined(OS_WIN)
  bool TabProxy::ProcessUnhandledAccelerator(const MSG& msg) {
    if (!is_valid())
      return false;
  
    return sender_->Send(
        new AutomationMsg_ProcessUnhandledAccelerator(handle_, msg));
    // This message expects no response
  }
  
  bool TabProxy::SetInitialFocus(bool reverse, bool restore_focus_to_view) {
    if (!is_valid())
      return false;
  
    return sender_->Send(
        new AutomationMsg_SetInitialFocus(handle_, reverse,
                                          restore_focus_to_view));
    // This message expects no response
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::NavigateInExternalTab(
      const GURL& url, const GURL& referrer) {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues rv = AUTOMATION_MSG_NAVIGATION_ERROR;
    sender_->Send(new AutomationMsg_NavigateInExternalTab(handle_, url,
                                                          referrer, &rv));
    return rv;
  }
  
  AutomationMsg_NavigationResponseValues TabProxy::NavigateExternalTabAtIndex(
      int index) {
    if (!is_valid())
      return AUTOMATION_MSG_NAVIGATION_ERROR;
  
    AutomationMsg_NavigationResponseValues rv = AUTOMATION_MSG_NAVIGATION_ERROR;
    sender_->Send(new AutomationMsg_NavigateExternalTabAtIndex(handle_, index,
                                                               &rv));
    return rv;
  }
  
  void TabProxy::HandleMessageFromExternalHost(const std::string& message,
                                               const std::string& origin,
                                               const std::string& target) {
    if (!is_valid())
      return;
  
    sender_->Send(
        new AutomationMsg_HandleMessageFromExternalHost(
            handle_, message, origin, target));
  }
  #endif  // defined(OS_WIN)
  
  bool TabProxy::PrintAsync() {
    if (!is_valid())
      return false;
  
    return sender_->Send(new AutomationMsg_PrintAsync(handle_));
  }
  
  bool TabProxy::WaitForInfoBarCount(size_t target_count) {
    if (!is_valid())
      return false;
  
    bool success = false;
    return sender_->Send(new AutomationMsg_WaitForInfoBarCount(
        handle_, target_count, &success)) && success;
  }
  
  bool TabProxy::OverrideEncoding(const std::string& encoding) {
    if (!is_valid())
      return false;
  
    bool succeeded = false;
    sender_->Send(new AutomationMsg_OverrideEncoding(handle_, encoding,
                                                     &succeeded));
    return succeeded;
  }
  
  bool TabProxy::CaptureEntirePageAsPNG(const base::FilePath& path) {
    if (!is_valid())
      return false;
  
    int browser_index, tab_index;
    automation::Error error;
    if (!SendGetIndicesFromTabHandleJSONRequest(
           sender_, handle_, &browser_index, &tab_index, &error)) {
      return false;
    }
  
    return SendCaptureEntirePageJSONRequest(
        sender_, WebViewLocator::ForIndexPair(browser_index, tab_index),
        path, &error);
  }
  
  #if defined(OS_WIN)
  void TabProxy::Reposition(HWND window, HWND window_insert_after, int left,
                            int top, int width, int height, int flags,
                            HWND parent_window) {
    Reposition_Params params;
    params.window = window;
    params.window_insert_after = window_insert_after;
    params.left = left;
    params.top = top;
    params.width = width;
    params.height = height;
    params.flags = flags;
    params.set_parent = (::IsWindow(parent_window) ? true : false);
    params.parent_window = parent_window;
    sender_->Send(new AutomationMsg_TabReposition(handle_, params));
  }
  
  void TabProxy::SendContextMenuCommand(int selected_command) {
    sender_->Send(new AutomationMsg_ForwardContextMenuCommandToChrome(
        handle_, selected_command));
  }
  
  void TabProxy::OnHostMoved() {
    sender_->Send(new AutomationMsg_BrowserMove(handle_));
  }
  #endif  // defined(OS_WIN)
  
  void TabProxy::SelectAll() {
    sender_->Send(new AutomationMsg_SelectAll(handle_));
  }
  
  void TabProxy::Cut() {
    sender_->Send(new AutomationMsg_Cut(handle_));
  }
  
  void TabProxy::Copy() {
    sender_->Send(new AutomationMsg_Copy(handle_));
  }
  
  void TabProxy::Paste() {
    sender_->Send(new AutomationMsg_Paste(handle_));
  }
  
  void TabProxy::SimulateKeyPress(ui::KeyboardCode key) {
    sender_->Send(new AutomationMsg_KeyPress(handle_, key));
  }
  
  void TabProxy::ReloadAsync() {
    sender_->Send(new AutomationMsg_ReloadAsync(handle_));
  }
  
  void TabProxy::StopAsync() {
    sender_->Send(new AutomationMsg_StopAsync(handle_));
  }
  
  void TabProxy::SaveAsAsync() {
    sender_->Send(new AutomationMsg_SaveAsAsync(handle_));
  }
  
  void TabProxy::JavaScriptStressTestControl(int cmd, int param) {
    if (!is_valid())
      return;
  
    sender_->Send(new AutomationMsg_JavaScriptStressTestControl(
        handle_, cmd, param));
  }
  
  void TabProxy::AddObserver(TabProxyDelegate* observer) {
    base::AutoLock lock(list_lock_);
    observers_list_.AddObserver(observer);
  }
  
  void TabProxy::RemoveObserver(TabProxyDelegate* observer) {
    base::AutoLock lock(list_lock_);
    observers_list_.RemoveObserver(observer);
  }
  
  // Called on Channel background thread, if TabMessages filter is installed.
  bool TabProxy::OnMessageReceived(const IPC::Message& message) {
    base::AutoLock lock(list_lock_);
    FOR_EACH_OBSERVER(TabProxyDelegate, observers_list_,
                      OnMessageReceived(this, message));
    return true;
  }
  
  void TabProxy::OnChannelError() {
    base::AutoLock lock(list_lock_);
    FOR_EACH_OBSERVER(TabProxyDelegate, observers_list_, OnChannelError(this));
  }
  
  TabProxy::~TabProxy() {}
  
  void TabProxy::FirstObjectAdded() {
    AddRef();
  }
  
  void TabProxy::LastObjectRemoved() {
    Release();
  }
  