diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/test/automation | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/automation')
23 files changed, 5398 insertions, 0 deletions
diff --git a/chrome/test/automation/SConscript b/chrome/test/automation/SConscript new file mode 100644 index 0000000..79286f2 --- /dev/null +++ b/chrome/test/automation/SConscript @@ -0,0 +1,58 @@ +# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ #'../../..',
+ #'$GTEST_DIR/include',
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/platform',
+ '#/..',
+ ],
+)
+
+input_files = [
+ 'autocomplete_edit_proxy.cc',
+ 'automation_handle_tracker.cc',
+ 'automation_proxy.cc',
+ 'browser_proxy.cc',
+ 'constrained_window_proxy.cc',
+ 'tab_proxy.cc',
+ 'window_proxy.cc',
+]
+
+lib = env.StaticLibrary('automation', input_files)
+
+i = env.Install('$TARGET_ROOT', lib)
+env.Alias('chrome', i)
diff --git a/chrome/test/automation/autocomplete_edit_proxy.cc b/chrome/test/automation/autocomplete_edit_proxy.cc new file mode 100644 index 0000000..33303ad --- /dev/null +++ b/chrome/test/automation/autocomplete_edit_proxy.cc @@ -0,0 +1,136 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/test/automation/autocomplete_edit_proxy.h" + +#include <vector> + +#include "chrome/test/automation/automation_constants.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" + +bool AutocompleteEditProxy::GetText(std::wstring* text) const { + if (!is_valid()) + return false; + if (!text) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + if (!sender_->SendAndWaitForResponse( + new AutomationMsg_AutocompleteEditGetTextRequest(0, handle_), &response, + AutomationMsg_AutocompleteEditGetTextResponse::ID)) + return false; + scoped_ptr<IPC::Message> response_deleter(response); + + Tuple2<bool, std::wstring> returned_result; + if (!AutomationMsg_AutocompleteEditGetTextResponse::Read(response, + &returned_result) || !returned_result.a) + return false; + + text->swap(returned_result.b); + return true; +} + +bool AutocompleteEditProxy::SetText(const std::wstring& text) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + if (!sender_->SendAndWaitForResponse( + new AutomationMsg_AutocompleteEditSetTextRequest(0, handle_, text), + &response, AutomationMsg_AutocompleteEditSetTextResponse::ID)) + return false; + + delete response; + return true; +} + +bool AutocompleteEditProxy::IsQueryInProgress(bool* query_in_progress) const { + if (!is_valid()) + return false; + if (!query_in_progress) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + if (!sender_->SendAndWaitForResponse( + new AutomationMsg_AutocompleteEditIsQueryInProgressRequest(0, handle_), + &response, AutomationMsg_AutocompleteEditIsQueryInProgressResponse::ID)) + return false; + scoped_ptr<IPC::Message> response_deleter(response); + + Tuple2<bool, bool> returned_result; + if (!AutomationMsg_AutocompleteEditIsQueryInProgressResponse::Read( + response, &returned_result) || !returned_result.a) + return false; + + *query_in_progress = returned_result.b; + return true; +} + + +bool AutocompleteEditProxy::WaitForQuery(int wait_timeout_ms) const { + const TimeTicks start = TimeTicks::Now(); + const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout_ms); + bool query_in_progress; + while (TimeTicks::Now() - start < timeout) { + if (IsQueryInProgress(&query_in_progress) && !query_in_progress) + return true; + Sleep(automation::kSleepTime); + } + // If we get here the query is still in progress. + return false; +} + +bool AutocompleteEditProxy::GetAutocompleteMatches(Matches* matches) const { + if (!is_valid()) + return false; + if (!matches) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + if (!sender_->SendAndWaitForResponse( + new AutomationMsg_AutocompleteEditGetMatchesRequest(0, handle_), + &response, AutomationMsg_AutocompleteEditGetMatchesResponse::ID)) + return false; + scoped_ptr<IPC::Message> response_deleter(response); + + Tuple2<bool, Matches> returned_result; + if (!AutomationMsg_AutocompleteEditGetMatchesResponse::Read( + response, &returned_result) || !returned_result.a) + return false; + + *matches = returned_result.b; + return true; +} diff --git a/chrome/test/automation/autocomplete_edit_proxy.h b/chrome/test/automation/autocomplete_edit_proxy.h new file mode 100644 index 0000000..608d1f1 --- /dev/null +++ b/chrome/test/automation/autocomplete_edit_proxy.h @@ -0,0 +1,169 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_AUTOCOMPLETE_EDIT_PROXY_H__ +#define CHROME_TEST_AUTOMATION_AUTOCOMPLETE_EDIT_PROXY_H__ + +#include <string> +#include <vector> + +#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/test/automation/automation_handle_tracker.h" + +// The purpose of this class is to act as a searializable version of +// AutocompleteMatch. The reason for this class is because we don't want to +// searialize all elements of AutocompleteMatch and we want some data from the +// autocomplete provider without the hassle of serializing it. +struct AutocompleteMatchData { + public: + AutocompleteMatchData() {} + explicit AutocompleteMatchData(const AutocompleteMatch& match) + : contents(match.contents), + deletable(match.deletable), + description(match.description), + destination_url(match.destination_url), + fill_into_edit(match.fill_into_edit), + inline_autocomplete_offset(match.inline_autocomplete_offset), + is_history_what_you_typed_match(match.is_history_what_you_typed_match), + provider_name(match.provider->name()), + relevance(match.relevance), + starred(match.starred) { + switch (match.type) { + case AutocompleteMatch::URL: + str_type = L"URL"; + break; + case AutocompleteMatch::KEYWORD: + str_type = L"KEYWORD"; + break; + case AutocompleteMatch::SEARCH: + str_type = L"SEARCH"; + break; + case AutocompleteMatch::HISTORY_SEARCH: + str_type = L"HISTORY"; + break; + default: + NOTREACHED(); + } + } + std::wstring contents; + bool deletable; + std::wstring description; + std::wstring destination_url; + std::wstring fill_into_edit; + size_t inline_autocomplete_offset; + bool is_history_what_you_typed_match; + std::string provider_name; + int relevance; + bool starred; + std::wstring str_type; +}; +typedef std::vector<AutocompleteMatchData> Matches; + +namespace IPC { + +template <> +struct ParamTraits<AutocompleteMatchData> { + typedef AutocompleteMatchData param_type; + static void Write(Message* m, const param_type& p) { + m->WriteWString(p.contents); + m->WriteBool(p.deletable); + m->WriteWString(p.description); + m->WriteWString(p.destination_url); + m->WriteWString(p.fill_into_edit); + m->WriteSize(p.inline_autocomplete_offset); + m->WriteBool(p.is_history_what_you_typed_match); + m->WriteString(p.provider_name); + m->WriteInt(p.relevance); + m->WriteBool(p.starred); + m->WriteWString(p.str_type); + } + + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadWString(iter, &r->contents) && + m->ReadBool(iter, &r->deletable) && + m->ReadWString(iter, &r->description) && + m->ReadWString(iter, &r->destination_url) && + m->ReadWString(iter, &r->fill_into_edit) && + m->ReadSize(iter, &r->inline_autocomplete_offset) && + m->ReadBool(iter, &r->is_history_what_you_typed_match) && + m->ReadString(iter, &r->provider_name) && + m->ReadInt(iter, &r->relevance) && + m->ReadBool(iter, &r->starred) && + m->ReadWString(iter, &r->str_type); + } + + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"[%S %s %S %S %S %d %s %s %d %s %S]", + p.contents, p.deletable ? "true" : "false", p.description, + p.destination_url, p.fill_into_edit, p.inline_autocomplete_offset, + p.is_history_what_you_typed_match ? "true" : "false", p.provider_name, + p.relevance, p.starred ? "true" : "false", p.str_type)); + } +}; +} // namespace IPC + +class AutocompleteEditProxy : public AutomationResourceProxy { + public: + AutocompleteEditProxy(AutomationMessageSender* sender, + AutomationHandleTracker* tracker, + int handle) + : AutomationResourceProxy(tracker, sender, handle) {} + virtual ~AutocompleteEditProxy() {} + + // All these functions return true if the autocomplete edit is valid and + // there are no IPC errors. + + // Gets the text visible in the omnibox. + bool GetText(std::wstring* text) const; + + // Sets the text visible in the omnibox. + bool SetText(const std::wstring& text); + + // Determines if a query to an autocomplete provider is still in progress. + // NOTE: No autocomplete queries will be made if the omnibox doesn't have + // focus. This can be achieved by sending a IDC_FOCUS_LOCATION accelerator + // to the browser. + bool IsQueryInProgress(bool* query_in_progress) const; + + // Gets a list of autocomplete matches that have been gathered so far. + bool GetAutocompleteMatches(Matches* matches) const; + + // Waits for all queries to autocomplete providers to complete. + // |wait_timeout_ms| gives the number of milliseconds to wait for the query + // to finish. Returns false if IPC call failed or if the function times out. + bool WaitForQuery(int wait_timeout_ms) const; + + private: + DISALLOW_EVIL_CONSTRUCTORS(AutocompleteEditProxy); +}; + +#endif // #define CHROME_TEST_AUTOMATION_AUTOCOMPLETE_EDIT_PROXY_H__ + diff --git a/chrome/test/automation/automation.vcproj b/chrome/test/automation/automation.vcproj new file mode 100644 index 0000000..38781fc --- /dev/null +++ b/chrome/test/automation/automation.vcproj @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="automation" + ProjectGUID="{1556EF78-C7E6-43C8-951F-F6B43AC0DD12}" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\automation.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\automation.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <File + RelativePath=".\autocomplete_edit_proxy.cc" + > + </File> + <File + RelativePath=".\autocomplete_edit_proxy.h" + > + </File> + <File + RelativePath=".\automation_constants.h" + > + </File> + <File + RelativePath=".\automation_handle_tracker.cc" + > + </File> + <File + RelativePath=".\automation_handle_tracker.h" + > + </File> + <File + RelativePath=".\automation_messages.h" + > + </File> + <File + RelativePath=".\automation_messages_internal.h" + > + </File> + <File + RelativePath=".\automation_proxy.cc" + > + </File> + <File + RelativePath=".\automation_proxy.h" + > + </File> + <File + RelativePath=".\browser_proxy.cc" + > + </File> + <File + RelativePath=".\browser_proxy.h" + > + </File> + <File + RelativePath=".\constrained_window_proxy.cc" + > + </File> + <File + RelativePath=".\constrained_window_proxy.h" + > + </File> + <File + RelativePath=".\tab_proxy.cc" + > + </File> + <File + RelativePath=".\tab_proxy.h" + > + </File> + <File + RelativePath=".\window_proxy.cc" + > + </File> + <File + RelativePath=".\window_proxy.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/test/automation/automation.vsprops b/chrome/test/automation/automation.vsprops new file mode 100644 index 0000000..4cc3b79 --- /dev/null +++ b/chrome/test/automation/automation.vsprops @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="automation" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\skia\using_skia.vsprops" + > + <Tool + Name="VCCLCompilerTool" + /> +</VisualStudioPropertySheet> diff --git a/chrome/test/automation/automation_constants.h b/chrome/test/automation/automation_constants.h new file mode 100644 index 0000000..994d233 --- /dev/null +++ b/chrome/test/automation/automation_constants.h @@ -0,0 +1,38 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_AUTOMATION_CONSTANTS_H__ +#define CHROME_TEST_AUTOMATION_AUTOMATION_CONSTANTS_H__ + +namespace automation { + // Amount of time to wait before querying the browser. + static const int kSleepTime = 250; +} + +#endif // CHROME_TEST_AUTOMATION_AUTOMATION_CONSTANTS_H__
\ No newline at end of file diff --git a/chrome/test/automation/automation_handle_tracker.cc b/chrome/test/automation/automation_handle_tracker.cc new file mode 100644 index 0000000..a31d125 --- /dev/null +++ b/chrome/test/automation/automation_handle_tracker.cc @@ -0,0 +1,97 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/test/automation/automation_handle_tracker.h" + +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" + +AutomationResourceProxy::AutomationResourceProxy( + AutomationHandleTracker* tracker, AutomationMessageSender* sender, + AutomationHandle handle) + : tracker_(tracker), + sender_(sender), + handle_(handle), + is_valid_(true) { + tracker_->Add(this); +} + +AutomationResourceProxy::~AutomationResourceProxy() { + if (tracker_) + tracker_->Remove(this); +} + +AutomationHandleTracker::~AutomationHandleTracker() { + // Tell any live objects that the tracker is going away so they don't try to + // call us when they are being destroyed. + for (HandleToObjectMap::iterator iter = handle_to_object_.begin(); + iter != handle_to_object_.end(); ++iter) { + iter->second->Invalidate(); + iter->second->TrackerGone(); + } +} + +void AutomationHandleTracker::Add(AutomationResourceProxy* proxy) { + handle_to_object_.insert(MapEntry(proxy->handle(), proxy)); +} + +void AutomationHandleTracker::Remove(AutomationResourceProxy* proxy) { + HandleToObjectMap::iterator iter = handle_to_object_.find(proxy->handle()); + if (iter == handle_to_object_.end()) + return; + + HandleToObjectMap::iterator end_of_matching_objects = + handle_to_object_.upper_bound(proxy->handle()); + + while(iter != end_of_matching_objects) { + if (iter->second == proxy) { + handle_to_object_.erase(iter); + + // If we have no more proxy objects using this handle, tell the + // app that it can clean up that handle. If the proxy isn't valid, + // that means that the app has already discarded this handle, and + // thus doesn't need to be notified that the handle is unused. + if (proxy->is_valid() && handle_to_object_.count(proxy->handle()) == 0) { + sender_->Send(new AutomationMsg_HandleUnused(0, proxy->handle())); + } + return; + } + ++iter; + } +} + +void AutomationHandleTracker::InvalidateHandle(AutomationHandle handle) { + HandleToObjectMap::iterator iter = handle_to_object_.lower_bound(handle); + HandleToObjectMap::const_iterator end_of_matching_objects = + handle_to_object_.upper_bound(handle); + + for (; iter != end_of_matching_objects; ++iter) { + iter->second->Invalidate(); + } +} diff --git a/chrome/test/automation/automation_handle_tracker.h b/chrome/test/automation/automation_handle_tracker.h new file mode 100644 index 0000000..8f064e3 --- /dev/null +++ b/chrome/test/automation/automation_handle_tracker.h @@ -0,0 +1,128 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file defines a mapping between Automation Proxy objects and +// their associated app-side handles. + +#ifndef CHROME_TEST_AUTOMATION_AUTOMATION_HANDLE_TRACKER_H__ +#define CHROME_TEST_AUTOMATION_AUTOMATION_HANDLE_TRACKER_H__ + +#include <map> + +#include "base/basictypes.h" + +// This represents a value that the app's AutomationProvider returns +// when asked for a resource (like a window or tab). +typedef int AutomationHandle; + +class AutomationHandleTracker; +class AutomationMessageSender; + +class AutomationResourceProxy { + public: + AutomationResourceProxy(AutomationHandleTracker* tracker, + AutomationMessageSender* sender, + AutomationHandle handle); + virtual ~AutomationResourceProxy(); + + // Marks this proxy object as no longer valid; this generally means + // that the corresponding resource on the app side is gone. + void Invalidate() { is_valid_ = false; } + bool is_valid() const { return is_valid_; } + + // Returns the handle that the app has generated to refer to this resource. + AutomationHandle handle() { return handle_; } + + protected: + friend class AutomationHandleTracker; + + AutomationHandle handle_; + + // Called by the tracker when it is being destroyed so we know not to call + // it back. + void TrackerGone() { + tracker_ = NULL; + } + + // Not owned by us, owned by the AutomationProxy object. May be NULL if the + // tracker has been destroyed (and hence the object is invalid). + AutomationHandleTracker* tracker_; + + // Not owned by us. + AutomationMessageSender* sender_; + + private: + // True if the resource that this object is a proxy for on the app side + // still exists. + bool is_valid_; + + DISALLOW_EVIL_CONSTRUCTORS(AutomationResourceProxy); +}; + +// This class keeps track of the mapping between AutomationHandles and +// AutomationResourceProxy objects. This is important because (1) multiple +// AutomationResourceProxy objects can be generated for the same handle +// (2) handles can be invalidated by the app, and all the associated +// proxy objects then need to be invalidated, and (3) when a handle is no +// longer being used on this end, we need to tell the app that it can +// discard the handle. +class AutomationHandleTracker { + public: + AutomationHandleTracker(AutomationMessageSender* sender) + : sender_(sender) {} + ~AutomationHandleTracker(); + + // Adds the specified proxy object to the tracker. + void Add(AutomationResourceProxy* proxy); + + // Removes a given proxy object from the mapping, and unregisters the + // handle on the app side if this was the last proxy object that was using + // that handle. This is a no-op if the proxy object is not currently + // in the tracker. + void Remove(AutomationResourceProxy* proxy); + + // Marks all proxy objects related to a given handle invalid. This is + // used when a resource (like a window) on the app side is closed, meaning + // that no further operations can be completed using the handle that + // identified that resource. + void InvalidateHandle(AutomationHandle handle); + + private: + typedef + std::multimap<AutomationHandle, AutomationResourceProxy*> HandleToObjectMap; + typedef std::pair<AutomationHandle, AutomationResourceProxy*> MapEntry; + + HandleToObjectMap handle_to_object_; + + AutomationMessageSender* sender_; + + DISALLOW_EVIL_CONSTRUCTORS(AutomationHandleTracker); +}; + +#endif // CHROME_TEST_AUTOMATION_AUTOMATION_HANDLE_TRACKER_H__
\ No newline at end of file diff --git a/chrome/test/automation/automation_messages.h b/chrome/test/automation/automation_messages.h new file mode 100644 index 0000000..a106e38 --- /dev/null +++ b/chrome/test/automation/automation_messages.h @@ -0,0 +1,65 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_AUTOMATION_MESSAGES_H__ +#define CHROME_TEST_AUTOMATION_AUTOMATION_MESSAGES_H__ + +#include <string> + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" + +enum AutomationMsg_NavigationResponseValues { + AUTOMATION_MSG_NAVIGATION_ERROR = 0, + AUTOMATION_MSG_NAVIGATION_SUCCESS, + AUTOMATION_MSG_NAVIGATION_AUTH_NEEDED, +}; + +// Two-pass include of render_messages_internal. Preprocessor magic allows +// us to use 1 header to define the enums and classes for our render messages. +#define IPC_MESSAGE_MACROS_ENUMS +#include "chrome/test/automation/automation_messages_internal.h" + +#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED +# undef IPC_MESSAGE_MACROS_LOG +# define IPC_MESSAGE_MACROS_CLASSES +#include "chrome/test/automation/automation_messages_internal.h" + +# undef IPC_MESSAGE_MACROS_CLASSES +# define IPC_MESSAGE_MACROS_LOG +#include "chrome/test/automation/automation_messages_internal.h" +#else +// No debug strings requested, just define the classes +# define IPC_MESSAGE_MACROS_CLASSES +#include "chrome/test/automation/automation_messages_internal.h" +#endif + + +#endif // CHROME_TEST_AUTOMATION_AUTOMATION_MESSAGES_H__ diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h new file mode 100644 index 0000000..64ee503 --- /dev/null +++ b/chrome/test/automation/automation_messages_internal.h @@ -0,0 +1,723 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Defines the IPC messages used by the automation interface. + +// This header is meant to be included in multiple passes, hence no traditional +// header guard. +// See ipc_message_macros.h for explanation of the macros and passes. + +#include <string> +#include <vector> + +#include "chrome/common/ipc_message_macros.h" +#include "chrome/common/navigation_types.h" +#include "chrome/test/automation/autocomplete_edit_proxy.h" + +// NOTE: All IPC messages have either a routing_id of 0 (for asynchronous +// messages), or one that's been assigned by the proxy (for calls +// which expect a response). The routing_id shouldn't be used for +// any other purpose in these message types. + +// NOTE: All the new IPC messages should go at the end (before IPC_END_MESSAGES) +// The IPC message IDs are part of an enum and hence the value +// assumed to be constant across the builds may change. +// The messages AutomationMsg_WindowHWND* in particular should not change +// since the PageCyclerReferenceTest depend on the correctness of the +// the message IDs across the builds. + +// By using a start value of 0 for automation messages, we keep backward +// compatability with old builds. +IPC_BEGIN_MESSAGES(Automation, 0) + + // This message is fired when the AutomationProvider is up and running + // in the app (the app is not fully up at this point). + IPC_MESSAGE_ROUTED0(AutomationMsg_Hello) + + // This message is fired when the initial tab(s) are finished loading. + IPC_MESSAGE_ROUTED0(AutomationMsg_InitialLoadsComplete) + + // This message notifies the AutomationProvider to append a new tab the window + // with the given handle. The response contains the index of the new tab, or + // -1 if the request failed. + // The second parameter is the url to be loaded in the new tab. + IPC_MESSAGE_ROUTED2(AutomationMsg_AppendTabRequest, int, GURL) + IPC_MESSAGE_ROUTED1(AutomationMsg_AppendTabResponse, int) + + // This message requests the (zero-based) index for the currently + // active tab in the window with the given handle. The response contains + // the index of the active tab, or -1 if the request failed. + IPC_MESSAGE_ROUTED1(AutomationMsg_ActiveTabIndexRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_ActiveTabIndexResponse, int) + + // This message notifies the AutomationProvider to active the tab. + // The first parameter is the handle to window resource. + // The second parameter is the (zero-based) index to be activated + IPC_MESSAGE_ROUTED2(AutomationMsg_ActivateTabRequest, int, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_ActivateTabResponse, int) + + // This message requests the cookie value for given url in the + // profile of the tab identified by the second parameter. The first + // parameter is the URL string. The response contains the length of the cookie + // value string. On failure, this length = -1. + IPC_MESSAGE_ROUTED2(AutomationMsg_GetCookiesRequest, GURL, int) + IPC_MESSAGE_ROUTED2(AutomationMsg_GetCookiesResponse, int, std::string) + + // This message notifies the AutomationProvider to set and broadcast a cookie + // with given name and value for the given url in the profile of the tab + // identified by the third parameter. The first parameter is the URL + // string, and the second parameter is the cookie name and value to be set. + // The response returns a non-negative value on success. + IPC_MESSAGE_ROUTED3(AutomationMsg_SetCookieRequest, GURL, std::string, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_SetCookieResponse, int) + + // This message notifies the AutomationProvider to navigate to a specified url + // in the tab with given handle. The first parameter is the handle to the tab + // resource. The second parameter is the target url. The response contains a + // status code which is nonnegative on success. + IPC_MESSAGE_ROUTED2(AutomationMsg_NavigateToURLRequest, int, GURL) + IPC_MESSAGE_ROUTED1(AutomationMsg_NavigateToURLResponse, + int) // see AutomationMsg_NavigationResponseValues + + // This message is used to implement the asynchronous version of + // NavigateToURL. + IPC_MESSAGE_ROUTED2(AutomationMsg_NavigationAsyncRequest, + int /* tab handle */, + GURL) + IPC_MESSAGE_ROUTED1(AutomationMsg_NavigationAsyncResponse, + bool /* error value */) + + // This message notifies the AutomationProvider to navigate back in session + // history in the tab with given handle. The first parameter is the handle + // to the tab resource. The response contains a status code which is + // nonnegative on success. + IPC_MESSAGE_ROUTED1(AutomationMsg_GoBackRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_GoBackResponse, + int) // see AutomationMsg_NavigationResponseValues + + // This message notifies the AutomationProvider to navigate forward in session + // history in the tab with given handle. The first parameter is the handle + // to the tab resource. The response contains a status code which is + // nonnegative on success. + IPC_MESSAGE_ROUTED1(AutomationMsg_GoForwardRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_GoForwardResponse, + int) // see AutomationMsg_NavigationResponseValues + + // This message requests the number of browser windows that the app currently + // has open. The parameter in the response is the number of windows. + IPC_MESSAGE_ROUTED0(AutomationMsg_BrowserWindowCountRequest) + IPC_MESSAGE_ROUTED1(AutomationMsg_BrowserWindowCountResponse, int) + + // This message requests the handle (int64 app-unique identifier) of the + // window with the given (zero-based) index. On error, the returned handle + // value is 0. + IPC_MESSAGE_ROUTED1(AutomationMsg_BrowserWindowRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_BrowserWindowResponse, int) + + // This message requests the number of tabs in the window with the given + // handle. The response contains the number of tabs, or -1 if the request + // failed. + IPC_MESSAGE_ROUTED1(AutomationMsg_TabCountRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_TabCountResponse, int) + + // This message requests the handle of the tab with the given (zero-based) + // index in the given app window. First parameter specifies the given window + // handle, second specifies the given tab_index. On error, the returned handle + // value is 0. + IPC_MESSAGE_ROUTED2(AutomationMsg_TabRequest, int, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_TabResponse, int) + + // This message requests the the title of the tab with the given handle. + // The response contains the size of the title string. On error, this value + // should be -1 and empty string. Note that the title can be empty in which + // case the size would be 0. + IPC_MESSAGE_ROUTED1(AutomationMsg_TabTitleRequest, int) + IPC_MESSAGE_ROUTED2(AutomationMsg_TabTitleResponse, int, std::wstring) + + // This message requests the the url of the tab with the given handle. + // The response contains a success flag and the URL string. The URL will + // be empty on failure, and it still may be empty on success. + IPC_MESSAGE_ROUTED1(AutomationMsg_TabURLRequest, + int /* tab handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_TabURLResponse, + bool /* success flag*/, + GURL) + + // This message requests the HWND of the top-level window that corresponds + // to the given automation handle. + // The response contains the HWND value, which is 0 if the call fails. + IPC_MESSAGE_ROUTED1(AutomationMsg_WindowHWNDRequest, + int /* automation handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_WindowHWNDResponse, + HWND /* Win32 handle */) + + // This message notifies the AutomationProxy that a handle that it has + // previously been given is now invalid. (For instance, if the handle + // represented a window which has now been closed.) The parameter + // value is the handle. + IPC_MESSAGE_ROUTED1(AutomationMsg_InvalidateHandle, int) + + // This message notifies the AutomationProvider that a handle is no + // longer being used, so it can stop paying attention to the + // associated resource. The parameter value is the handle. + IPC_MESSAGE_ROUTED1(AutomationMsg_HandleUnused, int) + + // This message requests the HWND of the tab that corresponds + // to the given automation handle. + // The response contains the HWND value, which is 0 if the call fails. + IPC_MESSAGE_ROUTED1(AutomationMsg_TabHWNDRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_TabHWNDResponse, + HWND /* win32 Window Handle*/) + + // This message tells the AutomationProvider to provide the given + // authentication data to the specified tab, in response to an HTTP/FTP + // authentication challenge. + // The response status will be negative on error. + IPC_MESSAGE_ROUTED3(AutomationMsg_SetAuthRequest, + int, // tab handle + std::wstring, // username + std::wstring) // password + IPC_MESSAGE_ROUTED1(AutomationMsg_SetAuthResponse, + int) // status + + // This message tells the AutomationProvider to cancel the login in the + // specified tab. + // The response status will be negative on error. + IPC_MESSAGE_ROUTED1(AutomationMsg_CancelAuthRequest, + int) // tab handle + IPC_MESSAGE_ROUTED1(AutomationMsg_CancelAuthResponse, + int) // status + + // Requests that the automation provider ask history for the most recent + // chain of redirects coming from the given URL. The response must be + // decoded by the caller manually; it contains an integer indicating the + // number of URLs, followed by that many wstrings indicating a chain of + // redirects. On failure, the count will be negative. + IPC_MESSAGE_ROUTED2(AutomationMsg_RedirectsFromRequest, + int, // tab handle + GURL) // source URL + IPC_MESSAGE_EMPTY(AutomationMsg_RedirectsFromResponse) + + // This message asks the AutomationProvider whether a tab is waiting for + // login info. + IPC_MESSAGE_ROUTED1(AutomationMsg_NeedsAuthRequest, + int) // tab handle + IPC_MESSAGE_ROUTED1(AutomationMsg_NeedsAuthResponse, + bool) // status + + // This message requests the AutomationProvider to apply a certain + // accelerator. It is completely asynchronous with the resulting accelerator + // action. + IPC_MESSAGE_ROUTED2(AutomationMsg_ApplyAcceleratorRequest, + int, // window handle + int) // accelerator id like (IDC_BACK, IDC_FORWARD ...) + // The list can be found at + // chrome/app/chrome_dll_resource.h + + // This message requests that the AutomationProvider executes a JavaScript, + // which is sent embedded in a 'javascript:' URL. + // The javascript is executed in context of child frame whose xpath + // is passed as parameter (context_frame). The execution results in + // a serialized JSON string response. + IPC_MESSAGE_ROUTED3(AutomationMsg_DomOperationRequest, + int, // tab handle + std::wstring, // context_frame + std::wstring) // the javascript to be executed + + // This message is used to communicate the values received by the + // callback binding the JS to Cpp. This message forms the second leg in + // the communication channel. The values are originally received in the + // renderer which are then sent to the app (wrapped as json) using + // corresponding message in render_messages_internal.h + // This message simply relays the json string. + IPC_MESSAGE_ROUTED1(AutomationMsg_DomOperationResponse, + std::string) // the serialized json string containing + // the result of a javascript execution + + // Is the Download Shelf visible for the specified tab? + IPC_MESSAGE_ROUTED1(AutomationMsg_ShelfVisibilityRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_ShelfVisibilityResponse, + bool /* is_visible */) + + // This message requests the number of constrained windows in the tab with + // the given handle. The response contains the number of constrained windows, + // or -1 if the request failed. + IPC_MESSAGE_ROUTED1(AutomationMsg_ConstrainedWindowCountRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_ConstrainedWindowCountResponse, + int /* constrained_window_count */) + + // This message requests the handle of the constrained window with the given + // (zero-based) index in the given tab. First parameter specifies the given + // tab handle, second specifies the given child_index. On error, the returned + // handle value is 0. + IPC_MESSAGE_ROUTED2(AutomationMsg_ConstrainedWindowRequest, + int, /* window_handle */ + int) /* child_index */ + + IPC_MESSAGE_ROUTED1(AutomationMsg_ConstrainedWindowResponse, + int) /* constrained_handle */ + + // This message requests the the title of the constrained window with the + // given handle. The response contains the size of the title string and title + // string. On error, this value should be -1 and empty string. Note that the + // title can be empty in which case the size would be 0. + IPC_MESSAGE_ROUTED1(AutomationMsg_ConstrainedTitleRequest, int) + IPC_MESSAGE_ROUTED2(AutomationMsg_ConstrainedTitleResponse, int, std::wstring) + + // This message requests the bounds of the specified View element in + // window coordinates. + // Request: + // int - the handle of the window in which the view appears + // int - the ID of the view, as specified in chrome/browser/view_ids.h + // bool - whether the bounds should be returned in the screen coordinates + // (if true) or in the browser coordinates (if false). + // Response: + // bool - true if the view was found + // gfx::Rect - the bounds of the view, in window coordinates + IPC_MESSAGE_ROUTED3(AutomationMsg_WindowViewBoundsRequest, int, int, bool) + IPC_MESSAGE_ROUTED2(AutomationMsg_WindowViewBoundsResponse, bool, gfx::Rect) + + // This message requests that a drag be performed in window coordinate space + // Request: + // int - the handle of the window that's the context for this drag + // std::vector<POINT> - the path of the drag in window coordinate space; + // it should have at least 2 points (start and end) + // int - the flags which identify the mouse button(s) for the drag, as + // defined in chrome/views/event.h + // Response: + // bool - true if the drag could be performed + IPC_MESSAGE_ROUTED3(AutomationMsg_WindowDragRequest, + int, std::vector<POINT>, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_WindowDragResponse, bool) + + // Similar to AutomationMsg_InitialLoadsComplete, this indicates that the + // new tab ui has completed the initial load of its data. + // Time is how many milliseconds the load took. + IPC_MESSAGE_ROUTED1(AutomationMsg_InitialNewTabUILoadComplete, + int /* time */) + + // This message starts a find within a tab corresponding to the supplied + // tab handle. The response contains the number of matches found on the page + // within the tab specified. The parameter 'search_string' specifies what + // string to search for, 'forward' specifies whether to search in forward + // direction (1=forward, 0=back), 'match_case' specifies case sensitivity + // (1=case sensitive, 0=case insensitive). If an error occurs, matches_found + // will be -1. + IPC_MESSAGE_ROUTED4(AutomationMsg_FindInPageRequest, + int, /* tab_handle */ + std::wstring, /* find_request */ + int, /* forward */ + int /* match_case */) + IPC_MESSAGE_ROUTED1(AutomationMsg_FindInPageResponse, + int /* matches_found */) + + // This message sends a inspect element request for a given tab. The response + // contains the number of resources loaded by the inspector controller. + IPC_MESSAGE_ROUTED3(AutomationMsg_InspectElementRequest, + int, /* tab_handle */ + int, /* x */ + int /* y */) + IPC_MESSAGE_ROUTED1(AutomationMsg_InspectElementResponse, int) + + // This message requests the process ID of the tab that corresponds + // to the given automation handle. + // The response has an integer corresponding to the PID of the tab's + // renderer, 0 if the tab currently has no renderer process, or -1 on error. + IPC_MESSAGE_ROUTED1(AutomationMsg_TabProcessIDRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_TabProcessIDResponse, + int /* process ID */) + + // This tells the browser to enable or disable the filtered network layer. + IPC_MESSAGE_ROUTED1(AutomationMsg_SetFilteredInet, + bool /* enabled */) + + // Gets the directory that downloads will occur in for the active profile. + IPC_MESSAGE_ROUTED1(AutomationMsg_DownloadDirectoryRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_DownloadDirectoryResponse, + std::wstring /* directory */) + + // This message requests the id of the view that has the focus in the + // specified window. If no view is focused, -1 is returned. Note that the + // window should either be a ViewWindow or a Browser. + IPC_MESSAGE_ROUTED1(AutomationMsg_GetFocusedViewIDRequest, + int /* view_handle */) + IPC_MESSAGE_ROUTED1(AutomationMsg_GetFocusedViewIDResponse, + int /* focused_view_id */) + + // This message shows/hides the window. + IPC_MESSAGE_ROUTED2(AutomationMsg_SetWindowVisibleRequest, + int /* view_handle */, bool /* visible */) + IPC_MESSAGE_ROUTED1(AutomationMsg_SetWindowVisibleResponse, + bool /* success */) + + // Gets the active status of a window. + IPC_MESSAGE_ROUTED1(AutomationMsg_IsWindowActiveRequest, + int /* view_handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_IsWindowActiveResponse, + bool /* success */, bool /* active */) + + // Makes the specified window the active window. + IPC_MESSAGE_ROUTED1(AutomationMsg_ActivateWindow, int /* view_handle */) + + // Opens a new browser window. + IPC_MESSAGE_ROUTED1(AutomationMsg_OpenNewBrowserWindow, + int /* show_command*/ ) + + // This message requests the handle (int64 app-unique identifier) of the + // current active top window. On error, the returned handle value is 0. + IPC_MESSAGE_ROUTED0(AutomationMsg_ActiveWindowRequest) + IPC_MESSAGE_ROUTED1(AutomationMsg_ActiveWindowResponse, int) + + // This message requests the browser associated with the specified window + // handle. + // The response contains a success flag and the handle of the browser. + IPC_MESSAGE_ROUTED1(AutomationMsg_BrowserForWindowRequest, + int /* window handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_BrowserForWindowResponse, + bool /* success flag */, + int /* browser handle */) + + // This message requests the window associated with the specified browser + // handle. + // The response contains a success flag and the handle of the window. + IPC_MESSAGE_ROUTED1(AutomationMsg_WindowForBrowserRequest, + int /* browser handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_WindowForBrowserResponse, + bool /* success flag */, + int /* window handle */) + + // This message requests the AutocompleteEdit associated with the specified + // browser handle. + // The response contains a success flag and the handle of the omnibox. + IPC_MESSAGE_ROUTED1(AutomationMsg_AutocompleteEditForBrowserRequest, + int /* browser handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_AutocompleteEditForBrowserResponse, + bool /* success flag */, + int /* AutocompleteEdit handle */) + + // This message requests that a mouse click be performed in window coordinate + // space. + // Request: + // int - the handle of the window that's the context for this click + // POINT - the point to click + // int - the flags which identify the mouse button(s) for the click, as + // defined in chrome/views/event.h + IPC_MESSAGE_ROUTED3(AutomationMsg_WindowClickRequest, int, POINT, int) + + // This message requests that a key press be performed. + // Request: + // int - the handle of the window that's the context for this click + // wchar_t - char of the key that was pressed. + // int - the flags which identify the modifiers (shift, ctrl, alt) + // associated for, as defined in chrome/views/event.h + IPC_MESSAGE_ROUTED3(AutomationMsg_WindowKeyPressRequest, int, wchar_t, int) + + // This message notifies the AutomationProvider to create a tab which is + // hosted by an external process. The response contains the HWND of the + // window that contains the external tab and the handle to the newly + // created tab + // The second parameter is the url to be loaded in the new tab. + IPC_MESSAGE_ROUTED0(AutomationMsg_CreateExternalTab) + IPC_MESSAGE_ROUTED2(AutomationMsg_CreateExternalTabResponse, HWND, int) + + // This message notifies the AutomationProvider to navigate to a specified + // url in the external tab with given handle. The first parameter is the + // handle to the tab resource. The second parameter is the target url. + // The response contains a status code which is nonnegative on success. + IPC_MESSAGE_ROUTED2(AutomationMsg_NavigateInExternalTabRequest, int, GURL) + IPC_MESSAGE_ROUTED1(AutomationMsg_NavigateInExternalTabResponse, + int) // see AutomationMsg_NavigationResponseValues + + // This message is an outgoing message from Chrome to an external host. + // It is a notification that the NavigationState was changed + // Request: + // -int: The flags specifying what changed + // (see TabContents::InvalidateTypes) + // Response: + // None expected + IPC_MESSAGE_ROUTED1(AutomationMsg_NavigationStateChanged, int) + + // This message is an outgoing message from Chrome to an external host. + // It is a notification that the target URL has changed (the target URL + // is the URL of the link that the user is hovering on) + // Request: + // -std::wstring: The new target URL + // Response: + // None expected + IPC_MESSAGE_ROUTED1(AutomationMsg_UpdateTargetUrl, std::wstring) + + // This message notifies the AutomationProvider to show the specified html + // text in an interstitial page in the tab with given handle. The first + // parameter is the handle to the tab resource. The second parameter is the + // html text to be displayed. + // The response contains a success flag. + IPC_MESSAGE_ROUTED2(AutomationMsg_ShowInterstitialPageRequest, + int, + std::string) + IPC_MESSAGE_ROUTED1(AutomationMsg_ShowInterstitialPageResponse, bool) + + // This message notifies the AutomationProvider to hide the current + // interstitial page in the tab with given handle. The parameter is the handle + // to the tab resource. + // The response contains a success flag. + IPC_MESSAGE_ROUTED1(AutomationMsg_HideInterstitialPageRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_HideInterstitialPageResponse, bool) + + // This message requests that a tab be closed. + // Request: + // - int: handle of the tab to close + // - bool: if true the proxy blocks until the tab has completely closed, + // otherwise the proxy only blocks until it initiates the close. + IPC_MESSAGE_ROUTED2(AutomationMsg_CloseTabRequest, int, bool) + IPC_MESSAGE_ROUTED1(AutomationMsg_CloseTabResponse, bool) + + // This message requests that the browser be closed. + // Request: + // - int: handle of the browser which contains the tab + // Response: + // - bool: whether the operation was successfull. + // - bool: whether the browser process will be terminated as a result (if + // this was the last closed browser window). + IPC_MESSAGE_ROUTED1(AutomationMsg_CloseBrowserRequest, int) + IPC_MESSAGE_ROUTED2(AutomationMsg_CloseBrowserResponse, bool, bool) + + // This message sets the keyboard accelarators to be used by an externally + // hosted tab. This call is not valid on a regular tab hosted within + // Chrome. + // Request: + // - int: handle of the tab + // - HACCEL: The accelerator table to be set + // - int: The number of entries in the accelerator table + // Response: + // -bool: whether the operation was successful. + IPC_MESSAGE_ROUTED3(AutomationMsg_SetAcceleratorsForTab, int, HACCEL, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_SetAcceleratorsForTabResponse, bool) + + // This message is an outgoing message from Chrome to an external host. + // It is a request to process a keyboard accelerator. + // Request: + // -MSG: The keyboard message + // Response: + // None expected + // TODO(sanjeevr): Ideally we need to add a response from the external + // host saying whether it processed the accelerator + IPC_MESSAGE_ROUTED1(AutomationMsg_HandleAccelerator, MSG) + + // This message is an outgoing message from Chrome to an external host. + // It is a request to open a url + // Request: + // -GURL: The URL to open + // -int: The WindowOpenDisposition that specifies where the URL should + // be opened (new tab, new window etc). + // Response: + // None expected + IPC_MESSAGE_ROUTED2(AutomationMsg_OpenURL, GURL, int) + + // This message is sent by the container of an externally hosted tab to + // reflect any accelerator keys that it did not process. This gives the + // tab a chance to handle the keys + // Request: + // - int: handle of the tab + // -MSG: The keyboard message that the container did not handle + // Response: + // None expected + IPC_MESSAGE_ROUTED2(AutomationMsg_ProcessUnhandledAccelerator, int, MSG) + + // This message requests the provider to wait until the specified tab has + // finished restoring after session restore. + // Request: + // - int: handle of the tab + // Response: + // - bool: whether the operation was successful. + IPC_MESSAGE_ROUTED1(AutomationMsg_WaitForTabToBeRestored, int) + + // Sent in response to AutomationMsg_WaitForTabToBeRestored once the tab has + // finished loading. + IPC_MESSAGE_ROUTED0(AutomationMsg_TabFinishedRestoring) + + // This message is an outgoing message from Chrome to an external host. + // It is a notification that a navigation happened + // Request: + // -int : Indicates the type of navigation (see the NavigationType enum) + // -int: If this was not a new navigation, then this value indicates the + // relative offset of the navigation. A positive offset means a + // forward navigation, a negative value means a backward navigation + // and 0 means this was a redirect + // Response: + // None expected + IPC_MESSAGE_ROUTED2(AutomationMsg_DidNavigate, int, int) + + // This message requests the different security states of the page displayed + // in the specified tab. + // Request: + // - int: handle of the tab + // Response: + // - bool: whether the operation was successful. + // - int: the security style of the tab (enum SecurityStyle see + // security_style.h)). + // - int: the status of the server's ssl cert (0 means no errors or no ssl + // was used). + // - int: the mixed content state, 0 means no mixed/unsafe contents. + IPC_MESSAGE_ROUTED1(AutomationMsg_GetSecurityState, int) + IPC_MESSAGE_ROUTED4(AutomationMsg_GetSecurityStateResponse, + bool, + int, + int, + int) + + // This message requests the page type of the page displayed in the specified + // tab (normal, error or interstitial). + // Request: + // - int: handle of the tab + // Response: + // - bool: whether the operation was successful. + // - int: the type of the page currently displayed (enum PageType see + // entry_navigation.h). + IPC_MESSAGE_ROUTED1(AutomationMsg_GetPageType, int) + IPC_MESSAGE_ROUTED2(AutomationMsg_GetPageTypeResponse, bool, int) + + // This message simulates the user action on the SSL blocking page showing in + // the specified tab. This message is only effective if an interstitial page + // is showing in the tab. + // Request: + // - int: handle of the tab + // - bool: whether to proceed or abort the navigation + // Response: + // - bool: whether the operation was successful. + IPC_MESSAGE_ROUTED2(AutomationMsg_ActionOnSSLBlockingPage, int, bool) + IPC_MESSAGE_ROUTED1(AutomationMsg_ActionOnSSLBlockingPageResponse, bool) + + // Message to request that a browser window is brought to the front and activated. + // Request: + // - int: handle of the browser window. + // Response: + // - bool: True if the browser is brought to the front. + IPC_MESSAGE_ROUTED1(AutomationMsg_BringBrowserToFront, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_BringBrowserToFrontResponse, bool) + + // Message to request whether a certain item is enabled of disabled in the "Page" + // menu in the browser window + // + // Request: + // - int: handle of the browser window. + // - int: IDC message identifier to query if enabled + // Response: + // - bool: True if the command is enabled on the Page menu + IPC_MESSAGE_ROUTED2(AutomationMsg_IsPageMenuCommandEnabled, int, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_IsPageMenuCommandEnabledResponse, bool) + + // This message notifies the AutomationProvider to print the tab with given + // handle. The first parameter is the handle to the tab resource. The + // response contains a bool which is true on success. + IPC_MESSAGE_ROUTED1(AutomationMsg_PrintNowRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_PrintNowResponse, bool) + + // This message notifies the AutomationProvider to reload the current page in + // the tab with given handle. The first parameter is the handle to the tab + // resource. The response contains a status code which is nonnegative on + // success. + IPC_MESSAGE_ROUTED1(AutomationMsg_ReloadRequest, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_ReloadResponse, + int) // see AutomationMsg_NavigationResponseValues + + // This message requests the handle (int64 app-unique identifier) of the + // last active browser window, or the browser at index 0 if there is no last + // active browser, or it no longer exists. Returns 0 if no browser windows + // exist. + IPC_MESSAGE_ROUTED0(AutomationMsg_LastActiveBrowserWindowRequest) + IPC_MESSAGE_ROUTED1(AutomationMsg_LastActiveBrowserWindowResponse, int) + + // This message requests the bounds of a constrained window (relative to its + // containing TabContents). On an internal error, the boolean in the result will + // be set to false. + IPC_MESSAGE_ROUTED1(AutomationMsg_ConstrainedWindowBoundsRequest, + int /* tab_handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_ConstrainedWindowBoundsResponse, + bool /* the requested window exists */, + gfx::Rect /* constrained_window_count */) + + // This message notifies the AutomationProvider to save the page with given + // handle. The first parameter is the handle to the tab resource. The second + // parameter is the main HTML file name. The third parameter is the directory + // for saving resources. The fourth parameter is the saving type: 0 for HTML + // only; 1 for complete web page. + // The response contains a bool which is true on success. + IPC_MESSAGE_ROUTED4(AutomationMsg_SavePageRequest, int, std::wstring, + std::wstring, int) + IPC_MESSAGE_ROUTED1(AutomationMsg_SavePageResponse, bool) + + + // This message requests the text currently being displayed in the + // AutocompleteEdit. The parameter is the handle to the AutocompleteEdit. + // The response is a string indicating the text in the AutocompleteEdit. + IPC_MESSAGE_ROUTED1(AutomationMsg_AutocompleteEditGetTextRequest, + int /* autocomplete edit handle */) + IPC_MESSAGE_ROUTED2(AutomationMsg_AutocompleteEditGetTextResponse, + bool /* the requested autocomplete edit exists */, + std::wstring /* omnibox text */) + + // This message sets the text being displayed in the AutocompleteEdit. The + // first parameter is the handle to the omnibox and the second parameter is + // the text to be displayed in the AutocompleteEdit. + // The response has no parameters and is returned when the operation has + // completed. + IPC_MESSAGE_ROUTED2(AutomationMsg_AutocompleteEditSetTextRequest, + int /* autocomplete edit handle */, + std::wstring /* text to set */) + IPC_MESSAGE_ROUTED1(AutomationMsg_AutocompleteEditSetTextResponse, + bool /* the requested autocomplete edit exists */) + + // This message requests if a query to a autocomplete provider is still in + // progress. The first parameter in the request is the handle to the + // autocomplete edit. + // The first parameter in the response indicates if the request succeeded. + // The second parameter in indicates if a query is still in progress. + IPC_MESSAGE_ROUTED1(AutomationMsg_AutocompleteEditIsQueryInProgressRequest, + int /* autocomplete edit handle*/) + IPC_MESSAGE_ROUTED2(AutomationMsg_AutocompleteEditIsQueryInProgressResponse, + bool /* the requested autocomplete edit exists */, + bool /* indicates if a query is in progress */) + + // This message requests a list of the autocomplete messages currently being + // displayed by the popup. The parameter in the request is a handle to the + // autocomplete edit. + // The first parameter in the response indicates if the request was + // successful while the second parameter is the actual list of matches. + IPC_MESSAGE_ROUTED1(AutomationMsg_AutocompleteEditGetMatchesRequest, + int /* autocomplete edit handle*/) + IPC_MESSAGE_ROUTED2(AutomationMsg_AutocompleteEditGetMatchesResponse, + bool /* the requested autocomplete edit exists */, + std::vector<AutocompleteMatchData> /* matches */) + +IPC_END_MESSAGES(Automation) diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc new file mode 100644 index 0000000..07e5387 --- /dev/null +++ b/chrome/test/automation/automation_proxy.cc @@ -0,0 +1,567 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <sstream> + +#include "chrome/test/automation/automation_proxy.h" + +#include "base/logging.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_message_macros.h" +#include "chrome/test/automation/autocomplete_edit_proxy.h" +#include "chrome/test/automation/automation_constants.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/automation/window_proxy.h" + +// This class exists to group together the data and functionality used for +// synchronous automation requests. +class AutomationRequest : + public base::RefCountedThreadSafe<AutomationRequest> { +public: + AutomationRequest() { + static int32 routing_id = 0; + routing_id_ = ++routing_id; + received_response_ = ::CreateEvent(NULL, TRUE, FALSE, NULL); + DCHECK(received_response_); + } + ~AutomationRequest() { + DCHECK(received_response_); + ::CloseHandle(received_response_); + } + + // This is called on the foreground thread to block while waiting for a + // response from the app. + // The function returns true if response is received, and returns false + // if there is a failure or timeout. + bool WaitForResponse(uint32 timeout_ms, bool* is_timeout) { + uint32 result = ::WaitForSingleObject(received_response_, timeout_ms); + if (is_timeout) + *is_timeout = (result == WAIT_TIMEOUT); + + return result != WAIT_FAILED && result != WAIT_TIMEOUT; + } + + // This is called on the background thread once the response has been + // received and the foreground thread can resume execution. + bool SignalResponseReady(const IPC::Message& response) { + response_.reset(new IPC::Message(response)); + + DCHECK(received_response_); + return !!::SetEvent(received_response_); + } + + // This can be used to take ownership of the response object that + // we've received, reducing the need to copy the message. + void GrabResponse(IPC::Message** response) { + DCHECK(response); + *response = response_.get(); + response_.release(); + } + + int32 routing_id() { return routing_id_; } + + const IPC::Message& response() { + DCHECK(response_.get()); + return *(response_.get()); + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(AutomationRequest); + + int32 routing_id_; + scoped_ptr<IPC::Message> response_; + HANDLE received_response_; +}; + +namespace { + +// This object allows messages received on the background thread to be +// properly triaged. +class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter { + public: + AutomationMessageFilter(AutomationProxy* server) : server_(server) {} + + // Return true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const IPC::Message& message) { + { + // Here we're checking to see if it matches the (because there should + // be at most one) synchronous request. If the received message is + // the response to the synchronous request, we clear the server's + // "current_request" pointer and signal to the request object that + // the response is ready for processing. We're clearing current_request + // here rather than on the foreground thread so that we can assert + // that both threads perceive it as cleared at the time that the + // foreground thread wakes up. + scoped_refptr<AutomationRequest> request = server_->current_request(); + if (request.get() && (message.routing_id() == request->routing_id())) { + server_->clear_current_request(); + request->SignalResponseReady(message); + return true; + } + if (message.routing_id() > 0) { + // The message is either the previous async response or arrived + // after timeout. + return true; + } + } + + bool handled = true; + + IPC_BEGIN_MESSAGE_MAP(AutomationMessageFilter, message) + IPC_MESSAGE_HANDLER_GENERIC( + AutomationMsg_Hello, server_->SignalAppLaunch()); + IPC_MESSAGE_HANDLER_GENERIC( + AutomationMsg_InitialLoadsComplete, server_->SignalInitialLoads()); + IPC_MESSAGE_HANDLER(AutomationMsg_InitialNewTabUILoadComplete, + NewTabLoaded); + IPC_MESSAGE_HANDLER_GENERIC( + AutomationMsg_InvalidateHandle, server_->InvalidateHandle(message)); + IPC_MESSAGE_UNHANDLED(handled = false); + IPC_END_MESSAGE_MAP() + + return handled; + } + + void NewTabLoaded(int load_time) { + server_->SignalNewTabUITab(load_time); + } + + private: + AutomationProxy* server_; +}; + +} // anonymous namespace + + +const int AutomationProxy::kMaxCommandExecutionTime = 30000; + +AutomationProxy::AutomationProxy() : current_request_(NULL) { + InitializeEvents(); + InitializeChannelID(); + InitializeThread(); + InitializeChannel(); + InitializeHandleTracker(); +} + +AutomationProxy::~AutomationProxy() { +} + +void AutomationProxy::InitializeEvents() { + app_launched_ = + CreateEvent(NULL, // Handle cannot be inherited by child processes. + TRUE, // No automatic reset after a waiting thread released. + FALSE, // Initially not signalled. + NULL); // No name. + DCHECK(app_launched_); + + // See the above call to CreateEvent to understand these parameters. + initial_loads_complete_ = CreateEvent(NULL, TRUE, FALSE, NULL); + DCHECK(initial_loads_complete_); + + // See the above call to CreateEvent to understand these parameters. + new_tab_ui_load_complete_ = CreateEvent(NULL, TRUE, FALSE, NULL); + DCHECK(new_tab_ui_load_complete_); +} + +void AutomationProxy::InitializeChannelID() { + // The channel counter keeps us out of trouble if we create and destroy + // several AutomationProxies sequentially over the course of a test run. + // (Creating the channel sometimes failed before when running a lot of + // tests in sequence, and our theory is that sometimes the channel ID + // wasn't getting freed up in time for the next test.) + static int channel_counter = 0; + + std::wostringstream buf; + buf << L"ChromeTestingInterface:" << GetCurrentProcessId() << + L"." << ++channel_counter; + channel_id_ = buf.str(); +} + +void AutomationProxy::InitializeThread() { + scoped_ptr<Thread> thread(new Thread("AutomationProxy_BackgroundThread")); + bool thread_result = thread->Start(); + DCHECK(thread_result); + thread_.swap(thread); +} + +void AutomationProxy::InitializeChannel() { + channel_.reset(new IPC::ChannelProxy( + channel_id_, + IPC::Channel::MODE_SERVER, + this, // we are the listener + new AutomationMessageFilter(this), + thread_->message_loop())); +} + +void AutomationProxy::InitializeHandleTracker() { + tracker_.reset(new AutomationHandleTracker(this)); +} + +bool AutomationProxy::WaitForAppLaunch() { + return ::WaitForSingleObject(app_launched_, + kMaxCommandExecutionTime) == WAIT_OBJECT_0; +} + +void AutomationProxy::SignalAppLaunch() { + ::SetEvent(app_launched_); +} + +bool AutomationProxy::WaitForInitialLoads() { + return ::WaitForSingleObject(initial_loads_complete_, + kMaxCommandExecutionTime) == WAIT_OBJECT_0; +} + +bool AutomationProxy::WaitForInitialNewTabUILoad(int* load_time) { + if (::WaitForSingleObject(new_tab_ui_load_complete_, + kMaxCommandExecutionTime) == WAIT_OBJECT_0) { + *load_time = new_tab_ui_load_time_; + ::ResetEvent(new_tab_ui_load_complete_); + return true; + } + return false; +} + +void AutomationProxy::SignalInitialLoads() { + ::SetEvent(initial_loads_complete_); +} + +void AutomationProxy::SignalNewTabUITab(int load_time) { + new_tab_ui_load_time_ = load_time; + ::SetEvent(new_tab_ui_load_complete_); +} + +bool AutomationProxy::GetBrowserWindowCount(int* num_windows) { + if (!num_windows) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool is_timeout = true; + bool succeeded = SendAndWaitForResponseWithTimeout( + new AutomationMsg_BrowserWindowCountRequest(0), &response, + AutomationMsg_BrowserWindowCountResponse::ID, + kMaxCommandExecutionTime, &is_timeout); + if (!succeeded) + return false; + + if (is_timeout) { + DLOG(ERROR) << "GetWindowCount did not complete in a timely fashion"; + return false; + } + + void* iter = NULL; + if (!response->ReadInt(&iter, num_windows)) { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool AutomationProxy::WaitForWindowCountToChange(int count, int* new_count, + int wait_timeout) { + const TimeTicks start = TimeTicks::Now(); + const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout); + while (TimeTicks::Now() - start < timeout) { + bool succeeded = GetBrowserWindowCount(new_count); + if (!succeeded) return false; + if (count != *new_count) return true; + Sleep(automation::kSleepTime); + } + // Window count never changed. + return false; +} + +bool AutomationProxy::WaitForWindowCountToBecome(int count, + int wait_timeout) { + const TimeTicks start = TimeTicks::Now(); + const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout); + while (TimeTicks::Now() - start < timeout) { + int new_count; + bool succeeded = GetBrowserWindowCount(&new_count); + if (!succeeded) { + // Try again next round, but log it. + DLOG(ERROR) << "GetBrowserWindowCount returned false"; + } else if (count == new_count) { + return true; + } + Sleep(automation::kSleepTime); + } + // Window count never reached the value we sought. + return false; +} + +bool AutomationProxy::SetFilteredInet(bool enabled) { + return Send(new AutomationMsg_SetFilteredInet(0, enabled)); +} + +void AutomationProxy::OnMessageReceived(const IPC::Message& msg) { + // This won't get called unless AutomationProxy is run from + // inside a message loop. + NOTREACHED(); +} + +void AutomationProxy::OnChannelError() { + DLOG(ERROR) << "Channel error in AutomationProxy."; +} + +WindowProxy* AutomationProxy::GetActiveWindow() { + IPC::Message* response = NULL; + bool is_timeout = true; + bool succeeded = SendAndWaitForResponseWithTimeout( + new AutomationMsg_ActiveWindowRequest(0), &response, + AutomationMsg_ActiveWindowResponse::ID, + kMaxCommandExecutionTime, &is_timeout); + if (!succeeded) + return NULL; + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit. + if (is_timeout) { + DLOG(ERROR) << "GetActiveWindow did not complete in a timely fashion"; + return NULL; + } + + void* iter = NULL; + int handle; + if (response->ReadInt(&iter, &handle) && (handle != 0)) + return new WindowProxy(this, tracker_.get(), handle); + + return NULL; +} + + +BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) { + IPC::Message* response; + bool is_timeout = true; + bool succeeded = SendAndWaitForResponseWithTimeout( + new AutomationMsg_BrowserWindowRequest(0, window_index), &response, + AutomationMsg_BrowserWindowResponse::ID, + kMaxCommandExecutionTime, &is_timeout); + if (!succeeded) + return NULL; + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit. + if (is_timeout) { + DLOG(ERROR) << "GetBrowserWindow did not complete in a timely fashion"; + return NULL; + } + + void* iter = NULL; + int handle; + if (!response->ReadInt(&iter, &handle) || (handle == 0)) { + DLOG(ERROR) << "Bad response from the window getter."; + return NULL; + } + return new BrowserProxy(this, tracker_.get(), handle); +} + +BrowserProxy* AutomationProxy::GetLastActiveBrowserWindow() { + IPC::Message* response; + bool is_timeout = true; + bool succeeded = SendAndWaitForResponseWithTimeout( + new AutomationMsg_LastActiveBrowserWindowRequest(0), + &response, AutomationMsg_LastActiveBrowserWindowResponse::ID, + kMaxCommandExecutionTime, &is_timeout); + if (!succeeded) + return NULL; + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit. + if (is_timeout) { + DLOG(ERROR) << "GetLastActiveBrowserWindow did not complete in a timely fashion"; + return NULL; + } + + void* iter = NULL; + int handle; + if (!response->ReadInt(&iter, &handle) || (handle == 0)) { + DLOG(ERROR) << "Bad response from the window getter."; + return NULL; + } + return new BrowserProxy(this, tracker_.get(), handle); +} + +BrowserProxy* AutomationProxy::GetBrowserForWindow(WindowProxy* window) { + return GetBrowserForWindowWithTimeout(window, INFINITE, NULL); +} + +BrowserProxy* AutomationProxy::GetBrowserForWindowWithTimeout( + WindowProxy* window, uint32 timeout_ms, bool* is_timeout) { + DCHECK(window); + if (!window->is_valid() || !window->handle()) + return false; + + IPC::Message* response = NULL; + bool succeeded = SendAndWaitForResponseWithTimeout( + new AutomationMsg_BrowserForWindowRequest(0, window->handle()), &response, + AutomationMsg_BrowserForWindowResponse::ID, timeout_ms, is_timeout); + if (!succeeded) + return NULL; + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit. + int browser_handle = 0; + void* iter = NULL; + bool handle_ok; + succeeded = response->ReadBool(&iter, &handle_ok); + if (succeeded) + succeeded = response->ReadInt(&iter, &browser_handle); + + if (succeeded) { + return new BrowserProxy(this, tracker_.get(), browser_handle); + } else { + return NULL; + } +} + +WindowProxy* AutomationProxy::GetWindowForBrowser(BrowserProxy* browser) { + if (!browser->is_valid() || !browser->handle()) + return false; + + IPC::Message* response = NULL; + bool succeeded = SendAndWaitForResponse( + new AutomationMsg_WindowForBrowserRequest(0, browser->handle()), &response, + AutomationMsg_WindowForBrowserResponse::ID); + if (!succeeded) + return NULL; + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit. + int window_handle; + void* iter = NULL; + bool handle_ok; + succeeded = response->ReadBool(&iter, &handle_ok); + if (succeeded) + succeeded = response->ReadInt(&iter, &window_handle); + + if (succeeded) { + return new WindowProxy(this, tracker_.get(), window_handle); + } else { + return NULL; + } +} + +AutocompleteEditProxy* AutomationProxy::GetAutocompleteEditForBrowser( + BrowserProxy* browser) { + if (!browser->is_valid() || !browser->handle()) + return NULL; + + IPC::Message* response = NULL; + if (!SendAndWaitForResponse( + new AutomationMsg_AutocompleteEditForBrowserRequest(0, browser->handle()), + &response, AutomationMsg_AutocompleteEditForBrowserResponse::ID)) + return NULL; + scoped_ptr<IPC::Message> response_deleter(response); + + int autocomplete_edit_handle; + void* iter = NULL; + bool handle_ok; + if (!response->ReadBool(&iter, &handle_ok) || + !response->ReadInt(&iter, &autocomplete_edit_handle)) + return NULL; + + return new AutocompleteEditProxy(this, tracker_.get(), + autocomplete_edit_handle); +} + +bool AutomationProxy::SendAndWaitForResponse(IPC::Message* request, + IPC::Message** response, + int response_type) { + return SendAndWaitForResponseWithTimeout(request, response, response_type, + INFINITE, NULL); +} + +bool AutomationProxy::SendAndWaitForResponseWithTimeout( + IPC::Message* request, + IPC::Message** response, + int response_type, + uint32 timeout_ms, + bool* is_timeout) { + + DCHECK(request); + DCHECK(response); + DCHECK(!current_request_) << + "Only one synchronous request should exist at any given time."; + + scoped_refptr<AutomationRequest> req = new AutomationRequest; + current_request_ = req; + + // Rewrite the message's routing ID so that we'll recognize the response + // to it when it comes back. + request->set_routing_id(req->routing_id()); + bool result = Send(request) && req->WaitForResponse(timeout_ms, is_timeout); + if (!result || req->response().type() != response_type) { + // If Send() or WaitForResponse() failed, current_request_ may not have + // gotten cleared by the background thread, so we'll clear it here. + current_request_ = NULL; + return false; + } + req->GrabResponse(response); + + return true; +} + +void AutomationProxy::InvalidateHandle(const IPC::Message& message) { + void* iter = NULL; + int handle; + + if (message.ReadInt(&iter, &handle)) { + tracker_->InvalidateHandle(handle); + } +} + +bool AutomationProxy::OpenNewBrowserWindow(int show_command) { + return Send(new AutomationMsg_OpenNewBrowserWindow(0, show_command)); +} + +TabProxy* AutomationProxy::CreateExternalTab(HWND* external_tab_container) { + IPC::Message* response = NULL; + bool succeeded = SendAndWaitForResponse( + new AutomationMsg_CreateExternalTab(0), &response, + AutomationMsg_CreateExternalTabResponse::ID); + if (!succeeded) { + return NULL; + } + void* iter = NULL; + int handle = 0; + TabProxy* tab_proxy = NULL; + if (IPC::ReadParam(response, &iter, external_tab_container) && + IsWindow(*external_tab_container)) { + if (response->ReadInt(&iter, &handle) && + (handle >= 0)) { + succeeded = true; + tab_proxy = new TabProxy(this, tracker_.get(), handle); + } + } else { + succeeded = false; + } + delete response; + return tab_proxy; +} diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h new file mode 100644 index 0000000..e8d63b5 --- /dev/null +++ b/chrome/test/automation/automation_proxy.h @@ -0,0 +1,240 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_H__ +#define CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/thread.h" +#include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_message.h" +#include "chrome/test/automation/automation_handle_tracker.h" +#include "chrome/test/automation/automation_messages.h" + +class AutomationRequest; +class BrowserProxy; +class WindowProxy; +class TabProxy; +class AutocompleteEditProxy; + +// This is an interface that AutomationProxy-related objects can use to +// access the message-sending abilities of the Proxy. +class AutomationMessageSender : public IPC::Message::Sender { + public: + // Sends a message synchronously (from the perspective of the caller's + // thread, at least); it doesn't return until a response has been received. + // This method takes ownership of the request object passed in. The caller + // is responsible for deleting the response object when they're done with it. + // response_type should be set to the message type of the expected response. + // A response object will only be available if the method returns true. + // NOTE: This method will overwrite any routing_id on the request message, + // since it uses this field to match the response up with the request. + virtual bool SendAndWaitForResponse(IPC::Message* request, + IPC::Message** response, + int response_type) = 0; + + // Sends a message synchronously; it doesn't return until a response has been + // received or a timeout has expired. + // The function returns true if a response is received, and returns false if + // there is a failure or timeout (in milliseconds). If return after timeout, + // is_timeout is set to true. + // See the comments in SendAndWaitForResponse for other details on usage. + // NOTE: When timeout occurs, the connection between proxy provider may be + // in transit state. Specifically, there might be pending IPC messages, + // and the proxy provider might be still working on the previous + // request. + virtual bool SendAndWaitForResponseWithTimeout(IPC::Message* request, + IPC::Message** response, + int response_type, + uint32 timeout_ms, + bool* is_timeout) = 0; +}; + +// This is the interface that external processes can use to interact with +// a running instance of the app. +class AutomationProxy : public IPC::Channel::Listener, + public AutomationMessageSender { + public: + AutomationProxy(); + virtual ~AutomationProxy(); + + // IPC callback + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelError(); + + // Waits for the app to launch and the automation provider to say hello + // (the app isn't fully done loading by this point). + // Returns true if the launch is successful + bool WaitForAppLaunch(); + + // Waits for any initial page loads to complete. + // NOTE: this only fires once for a run of the application. + // Returns true if the load is successful + bool WaitForInitialLoads(); + + // Waits for the initial destinations tab to report that it has finished + // querying. |load_time| is filled in with how long it took, in milliseconds. + // NOTE: this only fires once for a run of the application. + // Returns true if the load is successful. + bool WaitForInitialNewTabUILoad(int* load_time); + + // Open a new browser window, returning true on success. |show_command| + // identifies how the window should be shown. + // False likely indicates an IPC error. + bool OpenNewBrowserWindow(int show_command); + + // Fills the number of open browser windows into the given variable, returning + // true on success. False likely indicates an IPC error. + bool GetBrowserWindowCount(int* num_windows); + + // Block the thread until the window count changes. + // First parameter is the original window count. + // The second parameter is updated with the number of window tabs. + // The third parameter specifies the timeout length for the wait loop. + // Returns false if the window count does not change before time out. + // TODO(evanm): this function has a confusing name and semantics; it should + // be deprecated for WaitForWindowCountToBecome. + bool WaitForWindowCountToChange(int count, int* new_counter, + int wait_timeout); + + // Block the thread until the window count becomes the provided value. + // Returns true on success. + bool WaitForWindowCountToBecome(int target_count, int wait_timeout); + + // Returns the BrowserProxy for the browser window at the given index, + // transferring ownership of the pointer to the caller. + // On failure, returns NULL. + // + // Use GetBrowserWindowCount to see how many browser windows you can ask for. + // Window numbers are 0-based. + BrowserProxy* GetBrowserWindow(int window_index); + + // Returns the BrowserProxy for the browser window which was last active, + // transferring ownership of the pointer to the caller. + // If there was no last active browser window, or the last active browser + // window no longer exists (for example, if it was closed), returns + // GetBrowserWindow(0). + BrowserProxy* GetLastActiveBrowserWindow(); + + // Returns the WindowProxy for the currently active window, transferring + // ownership of the pointer to the caller. + // On failure, returns NULL. + WindowProxy* GetActiveWindow(); + + // Returns the browser this window corresponds to, or NULL if this window + // is not a browser. The caller owns the returned BrowserProxy. + BrowserProxy* GetBrowserForWindow(WindowProxy* window); + + // Same as GetBrowserForWindow except return NULL if response isn't received + // before the specified timeout. + BrowserProxy* GetBrowserForWindowWithTimeout(WindowProxy* window, + uint32 timeout_ms, + bool* is_timeout); + + // Returns the WindowProxy for this browser's window. It can be used to + // retreive view bounds, simulate clicks and key press events. The caller + // owns the returned WindowProxy. + // On failure, returns NULL. + WindowProxy* GetWindowForBrowser(BrowserProxy* browser); + + // Returns an AutocompleteEdit for this browser's window. It can be used to + // manipulate the omnibox. The caller owns the returned pointer. + // On failure, returns NULL. + AutocompleteEditProxy* GetAutocompleteEditForBrowser(BrowserProxy* browser); + + // Tells the browser to enable or disable network request filtering. Returns + // false if the message fails to send to the browser. + bool SetFilteredInet(bool enabled); + + // These methods are intended to be called by the background thread + // to signal that the given event has occurred, and that any corresponding + // Wait... function can return. + void SignalAppLaunch(); + void SignalInitialLoads(); + // load_time is how long, in ms, the tab contents took to load. + void SignalNewTabUITab(int load_time); + + // Returns the ID of the automation IPC channel, so that it can be + // passed to the app as a launch parameter. + const std::wstring& channel_id() const { return channel_id_; } + + // AutomationMessageSender implementations. + virtual bool Send(IPC::Message* message) { return channel_->Send(message); } + virtual bool SendAndWaitForResponse(IPC::Message* request, + IPC::Message** response, + int response_type); + virtual bool SendAndWaitForResponseWithTimeout(IPC::Message* request, + IPC::Message** response, + int response_type, + uint32 timeout_ms, + bool* is_timeout); + + // Returns the current AutomationRequest object. + AutomationRequest* current_request() { return current_request_; } + // Clears the current AutomationRequest object. + void clear_current_request() { current_request_ = NULL; } + + // Wrapper over AutomationHandleTracker::InvalidateHandle. Receives the message + // from AutomationProxy, unpacks the messages and routes that call to the + // tracker. + void InvalidateHandle(const IPC::Message& message); + + // Creates a tab that can hosted in an external process. The function + // returns a TabProxy representing the tab as well as a window handle + // that can be reparented in another process. + TabProxy* AutomationProxy::CreateExternalTab(HWND* external_tab_container); + private: + DISALLOW_EVIL_CONSTRUCTORS(AutomationProxy); + + void InitializeEvents(); + void InitializeChannelID(); + void InitializeThread(); + void InitializeChannel(); + void InitializeHandleTracker(); + + std::wstring channel_id_; + scoped_ptr<Thread> thread_; + scoped_ptr<IPC::ChannelProxy> channel_; + scoped_ptr<AutomationHandleTracker> tracker_; + + HANDLE app_launched_; + HANDLE initial_loads_complete_; + HANDLE new_tab_ui_load_complete_; + int new_tab_ui_load_time_; + + AutomationRequest* current_request_; + + static const int kMaxCommandExecutionTime; // Delay to let the browser + // execute the command.; +}; + +#endif // CHROME_TEST_AUTOMATION_AUTOMATION_PROXY_H__ diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc new file mode 100644 index 0000000..b639007 --- /dev/null +++ b/chrome/test/automation/automation_proxy_uitest.cc @@ -0,0 +1,831 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> + +#include "base/file_util.h" +#include "base/string_util.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/view_ids.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/test/automation/constrained_window_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "chrome/test/automation/window_proxy.h" +#include "chrome/test/ui/ui_test.h" +#include "chrome/views/event.h" +#include "net/base/net_util.h" + +typedef UITest AutomationProxyTest; + +class AutomationProxyVisibleTest : public UITest { + protected: + AutomationProxyVisibleTest() { + show_window_ = true; + } +}; + +TEST_F(AutomationProxyTest, GetBrowserWindowCount) { + int window_count = 0; + EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count)); + EXPECT_EQ(1, window_count); +#ifdef NDEBUG + ASSERT_FALSE(automation()->GetBrowserWindowCount(NULL)); +#endif +} + +TEST_F(AutomationProxyTest, GetBrowserWindow) { + { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + } + + { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(-1)); + ASSERT_FALSE(window.get()); + } + + { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(1)); + ASSERT_FALSE(window.get()); + } +}; + +TEST_F(AutomationProxyVisibleTest, WindowGetViewBounds) { + { + scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr<WindowProxy> window( + automation()->GetWindowForBrowser(browser.get())); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab1(browser->GetTab(0)); + ASSERT_TRUE(tab1.get()); + GURL tab1_url; + ASSERT_TRUE(tab1->GetCurrentURL(&tab1_url)); + + // Add another tab so we can simulate dragging. + ASSERT_TRUE(browser->AppendTab(GURL("about:"))); + + scoped_ptr<TabProxy> tab2(browser->GetTab(1)); + ASSERT_TRUE(tab2.get()); + GURL tab2_url; + ASSERT_TRUE(tab2->GetCurrentURL(&tab2_url)); + + EXPECT_NE(tab1_url.spec(), tab2_url.spec()); + + gfx::Rect bounds; + ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_0, &bounds, false)); + EXPECT_GT(bounds.x(), 0); + EXPECT_GT(bounds.width(), 0); + EXPECT_GT(bounds.height(), 0); + + gfx::Rect bounds2; + ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_LAST, &bounds2, false)); + EXPECT_GT(bounds2.width(), 0); + EXPECT_GT(bounds2.height(), 0); + EXPECT_GT(bounds2.x(), bounds.x()); + EXPECT_EQ(bounds2.y(), bounds.y()); + + gfx::Rect urlbar_bounds; + ASSERT_TRUE(window->GetViewBounds(VIEW_ID_LOCATION_BAR, &urlbar_bounds, + false)); + EXPECT_GT(urlbar_bounds.x(), 0); + EXPECT_GT(urlbar_bounds.y(), 0); + EXPECT_GT(urlbar_bounds.width(), 0); + EXPECT_GT(urlbar_bounds.height(), 0); + + /* + + TODO(beng): uncomment this section or move to interactive_ui_tests post + haste! + + // Now that we know where the tabs are, let's try dragging one. + POINT start; + POINT end; + start.x = bounds.x() + bounds.width() / 2; + start.y = bounds.y() + bounds.height() / 2; + end.x = start.x + 2 * bounds.width() / 3; + end.y = start.y; + ASSERT_TRUE(browser->SimulateDrag(start, end, + ChromeViews::Event::EF_LEFT_BUTTON_DOWN)); + + // Check to see that the drag event successfully swapped the two tabs. + tab1.reset(browser->GetTab(0)); + ASSERT_TRUE(tab1.get()); + GURL tab1_new_url; + ASSERT_TRUE(tab1->GetCurrentURL(&tab1_new_url)); + + tab2.reset(browser->GetTab(1)); + ASSERT_TRUE(tab2.get()); + GURL tab2_new_url; + ASSERT_TRUE(tab2->GetCurrentURL(&tab2_new_url)); + + EXPECT_EQ(tab1_url.spec(), tab2_new_url.spec()); + EXPECT_EQ(tab2_url.spec(), tab1_new_url.spec()); + + */ + } +} + +TEST_F(AutomationProxyTest, GetTabCount) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + int tab_count = 0; + ASSERT_TRUE(window->GetTabCount(&tab_count)); + ASSERT_EQ(1, tab_count); +} + +TEST_F(AutomationProxyTest, GetActiveTabIndex) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + int active_tab_index = -1; + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + ASSERT_EQ(0, active_tab_index); +} + +TEST_F(AutomationProxyVisibleTest, AppendTab) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + int original_tab_count; + ASSERT_TRUE(window->GetTabCount(&original_tab_count)); + ASSERT_EQ(1, original_tab_count); // By default there are 2 tabs opened. + + int original_active_tab_index; + ASSERT_TRUE(window->GetActiveTabIndex(&original_active_tab_index)); + ASSERT_EQ(0, original_active_tab_index); // By default 0-th tab is active + + ASSERT_TRUE(window->AppendTab(GURL("about:blank"))); + int tab_count; + ASSERT_TRUE(window->GetTabCount(&tab_count)); + ASSERT_EQ(original_tab_count + 1, tab_count); + + int active_tab_index = -1; + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + ASSERT_EQ(tab_count - 1, active_tab_index); + ASSERT_NE(original_active_tab_index, active_tab_index); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"title2.html"); + ASSERT_TRUE(window->AppendTab(net_util::FilePathToFileURL(filename))); + + int appended_tab_index; + // Append tab will also be active tab + ASSERT_TRUE(window->GetActiveTabIndex(&appended_tab_index)); + + scoped_ptr<TabProxy> tab(window->GetTab(appended_tab_index)); + ASSERT_TRUE(tab.get()); + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); +} + +TEST_F(AutomationProxyTest, ActivateTab) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + ASSERT_TRUE(window->AppendTab(GURL("about:blank"))); + + int at_index = 1; + ASSERT_TRUE(window->ActivateTab(at_index)); + int active_tab_index = -1; + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + ASSERT_EQ(at_index, active_tab_index); + + at_index = 0; + ASSERT_TRUE(window->ActivateTab(at_index)); + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + ASSERT_EQ(at_index, active_tab_index); +} + + +TEST_F(AutomationProxyTest, GetTab) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + { + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + // BUG [634097] : expected title should be "about:blank" + ASSERT_STREQ(L"", title.c_str()); + } + + { + ASSERT_FALSE(window->GetTab(-1)); + } + + { + scoped_ptr<TabProxy> tab; + tab.reset(window->GetTab(1)); + ASSERT_FALSE(tab.get()); + } +}; + +TEST_F(AutomationProxyTest, NavigateToURL) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + // BUG [634097] : expected title should be "about:blank" + ASSERT_STREQ(L"", title.c_str()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"title2.html"); + + tab->NavigateToURL(net_util::FilePathToFileURL(filename)); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); + + // TODO(vibhor) : Add a test using testserver. +} + +// This test is disabled. See bug 794412 +TEST_F(AutomationProxyTest, DISABLED_NavigateToURLWithTimeout1) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"title2.html"); + + bool is_timeout; + tab->NavigateToURLWithTimeout(net_util::FilePathToFileURL(filename), + 10000, &is_timeout); + ASSERT_FALSE(is_timeout); + + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); + + tab->NavigateToURLWithTimeout(net_util::FilePathToFileURL(filename), + 1, &is_timeout); + ASSERT_TRUE(is_timeout); + + Sleep(10); +} + +// This test is disabled. See bug 794412. +TEST_F(AutomationProxyTest, DISABLED_NavigateToURLWithTimeout2) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + tab.reset(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring filename1(test_data_directory_); + file_util::AppendToPath(&filename1, L"title1.html"); + + bool is_timeout; + tab->NavigateToURLWithTimeout(net_util::FilePathToFileURL(filename1), + 1, &is_timeout); + ASSERT_TRUE(is_timeout); + + std::wstring filename2(test_data_directory_); + file_util::AppendToPath(&filename2, L"title2.html"); + tab->NavigateToURLWithTimeout(net_util::FilePathToFileURL(filename2), + 10000, &is_timeout); + ASSERT_FALSE(is_timeout); + + Sleep(10); +} + +TEST_F(AutomationProxyTest, GoBackForward) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + // BUG [634097] : expected title should be "about:blank" + ASSERT_STREQ(L"", title.c_str()); + + ASSERT_FALSE(tab->GoBack()); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"", title.c_str()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"title2.html"); + ASSERT_TRUE(tab->NavigateToURL(net_util::FilePathToFileURL(filename))); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); + + ASSERT_TRUE(tab->GoBack()); + ASSERT_TRUE(tab->GetTabTitle(&title)); + // BUG [634097] : expected title should be "about:blank" + ASSERT_STREQ(L"", title.c_str()); + + ASSERT_TRUE(tab->GoForward()); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); + + ASSERT_FALSE(tab->GoForward()); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); +} + +TEST_F(AutomationProxyTest, GetCurrentURL) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + GURL url; + ASSERT_TRUE(tab->GetCurrentURL(&url)); + ASSERT_STREQ("about:blank", url.spec().c_str()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"cookie1.html"); + GURL newurl = net_util::FilePathToFileURL(filename); + ASSERT_TRUE(tab->NavigateToURL(newurl)); + ASSERT_TRUE(tab->GetCurrentURL(&url)); + // compare canonical urls... + ASSERT_STREQ(newurl.spec().c_str(), url.spec().c_str()); +} + +class AutomationProxyTest2 : public AutomationProxyVisibleTest { + protected: + AutomationProxyTest2() { + document1_ = test_data_directory_; + file_util::AppendToPath(&document1_, L"title1.html"); + + document2_ = test_data_directory_; + file_util::AppendToPath(&document2_, L"title2.html"); + launch_arguments_ = document1_ + L" " + document2_; + } + + std::wstring document1_; + std::wstring document2_; +}; + +TEST_F(AutomationProxyTest2, GetActiveTabIndex) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + int active_tab_index = -1; + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + int tab_count; + ASSERT_TRUE(window->GetTabCount(&tab_count)); + ASSERT_EQ(0, active_tab_index); + int at_index = 1; + ASSERT_TRUE(window->ActivateTab(at_index)); + ASSERT_TRUE(window->GetActiveTabIndex(&active_tab_index)); + ASSERT_EQ(at_index, active_tab_index); +} + +TEST_F(AutomationProxyTest2, GetTabTitle) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + std::wstring title; + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"title1.html", title.c_str()); + + tab.reset(window->GetTab(1)); + ASSERT_TRUE(tab.get()); + ASSERT_TRUE(tab->GetTabTitle(&title)); + ASSERT_STREQ(L"Title Of Awesomeness", title.c_str()); +} + +TEST_F(AutomationProxyTest, Cookies) { + GURL url(L"http://mojo.jojo.google.com"); + std::string value_result; + + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + // test setting the cookie: + ASSERT_TRUE(tab->SetCookie(url, "foo=baz")); + + ASSERT_TRUE(tab->GetCookieByName(url, "foo", &value_result)); + ASSERT_FALSE(value_result.empty()); + ASSERT_STREQ("baz", value_result.c_str()); + + // test clearing the cookie: + ASSERT_TRUE(tab->SetCookie(url, "foo=")); + + ASSERT_TRUE(tab->GetCookieByName(url, "foo", &value_result)); + ASSERT_TRUE(value_result.empty()); + + // now, test that we can get multiple cookies: + ASSERT_TRUE(tab->SetCookie(url, "foo1=baz1")); + ASSERT_TRUE(tab->SetCookie(url, "foo2=baz2")); + + ASSERT_TRUE(tab->GetCookies(url, &value_result)); + ASSERT_FALSE(value_result.empty()); + EXPECT_TRUE(value_result.find("foo1=baz1") != std::string::npos); + EXPECT_TRUE(value_result.find("foo2=baz2") != std::string::npos); +} + +TEST_F(AutomationProxyTest, GetHWND) { + scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr<WindowProxy> window( + automation()->GetWindowForBrowser(browser.get())); + ASSERT_TRUE(window.get()); + + HWND handle; + ASSERT_TRUE(window->GetHWND(&handle)); + ASSERT_TRUE(handle); +} + +TEST_F(AutomationProxyTest, NavigateToURLAsync) { + AutomationProxy* automation_object = automation(); + scoped_ptr<BrowserProxy> window(automation_object->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"cookie1.html"); + GURL newurl = net_util::FilePathToFileURL(filename); + + ASSERT_TRUE(tab->NavigateToURLAsync(newurl)); + std::string value = WaitUntilCookieNonEmpty(tab.get(), newurl, + "foo", 250, 5*1000); + ASSERT_STREQ("baz", value.c_str()); +} + +TEST_F(AutomationProxyTest, AcceleratorNewTab) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + + int old_tab_count = -1; + ASSERT_TRUE(window->GetTabCount(&old_tab_count)); + + ASSERT_TRUE(window->ApplyAccelerator(IDC_NEWTAB)); + int new_tab_count; + ASSERT_TRUE(window->WaitForTabCountToChange(old_tab_count, &new_tab_count, + 5000)); + if (new_tab_count == -1) + FAIL(); + ASSERT_EQ(old_tab_count + 1, new_tab_count); + scoped_ptr<TabProxy> tab(window->GetTab(new_tab_count - 1)); + ASSERT_TRUE(tab.get()); + + std::wstring title; + int i; + for (i = 0; i < 10; ++i) { + Sleep(kWaitForActionMaxMsec / 10); + ASSERT_TRUE(tab->GetTabTitle(&title)); + if (title == L"Destinations" || title == L"New Tab") + break; + } + // If we got to 10, the new tab failed to open. + ASSERT_NE(10, i); +} + +class AutomationProxyTest4 : public UITest { + protected: + AutomationProxyTest4() : UITest() { + dom_automation_enabled_ = true; + } +}; + +std::wstring SynthesizeJSURL(const std::wstring& value) { + std::wstring jscript; + SStringPrintf(&jscript, + L"javascript:void(window.domAutomationController.send(%s));", value.c_str()); + return jscript; +} + +TEST_F(AutomationProxyTest4, StringValueIsEchoedByDomAutomationController) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring expected(L"string"); + std::wstring jscript = SynthesizeJSURL(L"\"" + expected + L"\""); + std::wstring actual; + ASSERT_TRUE(tab->ExecuteAndExtractString(L"", jscript, &actual)); + ASSERT_STREQ(expected.c_str(), actual.c_str()); +} + +std::wstring BooleanToString(bool bool_value) { + Value* value = Value::CreateBooleanValue(bool_value); + std::string json_string; + JSONStringValueSerializer serializer(&json_string); + serializer.Serialize(*value); + return UTF8ToWide(json_string); +} + +TEST_F(AutomationProxyTest4, BooleanValueIsEchoedByDomAutomationController) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + bool expected = true; + std::wstring jscript = SynthesizeJSURL(BooleanToString(expected)); + bool actual = false; + ASSERT_TRUE(tab->ExecuteAndExtractBool(L"", jscript, &actual)); + ASSERT_EQ(expected, actual); +} + +TEST_F(AutomationProxyTest4, NumberValueIsEchoedByDomAutomationController) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + int expected = 1; + int actual = 0; + std::wstring expected_string; + SStringPrintf(&expected_string, L"%d", expected); + std::wstring jscript = SynthesizeJSURL(expected_string); + ASSERT_TRUE(tab->ExecuteAndExtractInt(L"", jscript, &actual)); + ASSERT_EQ(expected, actual); +} + +// TODO(vibhor): Add a test for ExecuteAndExtractValue() for JSON Dictionary +// type value + +class AutomationProxyTest3 : public UITest { + protected: + AutomationProxyTest3() : UITest() { + document1_ = test_data_directory_; + file_util::AppendToPath(&document1_, L"frame_dom_access"); + file_util::AppendToPath(&document1_, L"frame_dom_access.html"); + + dom_automation_enabled_ = true; + launch_arguments_ = document1_; + } + + std::wstring document1_; +}; + +std::wstring SynthesizeJSURLForDOMQuery(const std::wstring& id) { + std::wstring jscript; + SStringPrintf(&jscript, + L"javascript:void(window.domAutomationController)"); + StringAppendF(&jscript, L".send(document.getElementById('%s').nodeName);", + id.c_str()); + return jscript; +} + +TEST_F(AutomationProxyTest3, FrameDocumentCanBeAccessed) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring actual; + std::wstring xpath1 = L""; // top level frame + std::wstring jscript1 = SynthesizeJSURLForDOMQuery(L"myinput"); + ASSERT_TRUE(tab->ExecuteAndExtractString(xpath1, jscript1, &actual)); + ASSERT_EQ(L"INPUT", actual); + + std::wstring xpath2 = L"/html/body/iframe"; + std::wstring jscript2 = SynthesizeJSURLForDOMQuery(L"myspan"); + ASSERT_TRUE(tab->ExecuteAndExtractString(xpath2, jscript2, &actual)); + ASSERT_EQ(L"SPAN", actual); + + std::wstring xpath3 = L"/html/body/iframe\n/html/body/iframe"; + std::wstring jscript3 = SynthesizeJSURLForDOMQuery(L"mydiv"); + ASSERT_TRUE(tab->ExecuteAndExtractString(xpath3, jscript3, &actual)); + ASSERT_EQ(L"DIV", actual); + + // TODO(evanm): fix or remove this. + // This part of the test appears to verify that executing JS fails + // non-HTML pages, but the new tab is now HTML so this test isn't + // correct. +#if 0 + // Open a new Destinations tab to execute script inside. + window->ApplyAccelerator(IDC_NEWTAB); + tab.reset(window->GetTab(1)); + ASSERT_TRUE(tab.get()); + ASSERT_TRUE(window->ActivateTab(1)); + + std::wstring title; + int i; + for (i = 0; i < 10; ++i) { + Sleep(kWaitForActionMaxMsec / 10); + ASSERT_TRUE(tab->GetTabTitle(&title)); + if (title == L"Destinations") + break; + } + // If we got to 10, the new tab failed to open. + ASSERT_NE(10, i); + ASSERT_FALSE(tab->ExecuteAndExtractString(xpath1, jscript1, &actual)); +#endif +} + +TEST_F(AutomationProxyTest, ConstrainedWindowTest) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + tab.reset(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"constrained_files"); + file_util::AppendToPath(&filename, L"constrained_window.html"); + + ASSERT_TRUE(tab->NavigateToURL(net_util::FilePathToFileURL(filename))); + + int count; + ASSERT_TRUE(tab->WaitForChildWindowCountToChange(0, &count, 5000)); + + ASSERT_EQ(2, count); + + ConstrainedWindowProxy* cwindow = tab->GetConstrainedWindow(0); + ASSERT_TRUE(cwindow); + + std::wstring title; + ASSERT_TRUE(cwindow->GetTitle(&title)); + ASSERT_STREQ(L"Constrained Window 0 - Google Chrome", title.c_str()); + delete cwindow; + + cwindow = tab->GetConstrainedWindow(1); + ASSERT_TRUE(cwindow); + ASSERT_TRUE(cwindow->GetTitle(&title)); + ASSERT_STREQ(L"Constrained Window 1 - Google Chrome", title.c_str()); + delete cwindow; +} + +TEST_F(AutomationProxyTest, CantEscapeByOnloadMoveto) { + scoped_ptr<BrowserProxy> window(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(window.get()); + + scoped_ptr<TabProxy> tab(window->GetTab(0)); + tab.reset(window->GetTab(0)); + ASSERT_TRUE(tab.get()); + + std::wstring filename(test_data_directory_); + file_util::AppendToPath(&filename, L"constrained_files"); + file_util::AppendToPath(&filename, L"constrained_window_onload_moveto.html"); + + ASSERT_TRUE(tab->NavigateToURL(net_util::FilePathToFileURL(filename))); + + int count; + ASSERT_TRUE(tab->WaitForChildWindowCountToChange(0, &count, 5000)); + + ASSERT_EQ(1, count); + + ConstrainedWindowProxy* cwindow = tab->GetConstrainedWindow(0); + ASSERT_TRUE(cwindow); + + gfx::Rect rect; + bool is_timeout = false; + ASSERT_TRUE(cwindow->GetBoundsWithTimeout(&rect, 1000, &is_timeout)); + ASSERT_FALSE(is_timeout); + ASSERT_NE(20, rect.x()); + ASSERT_NE(20, rect.y()); +} + +bool ExternalTabHandler(HWND external_tab_window) { + static const wchar_t class_name[] = L"External_Tab_UI_Test_Class"; + static const wchar_t window_title[] = L"External Tab Tester"; + + WNDCLASSEX wnd_class = {0}; + wnd_class.cbSize = sizeof(wnd_class); + wnd_class.style = CS_HREDRAW | CS_VREDRAW; + wnd_class.lpfnWndProc = DefWindowProc; + wnd_class.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wnd_class.lpszClassName = class_name; + ATOM result = RegisterClassEx(&wnd_class); + if (0 == result) { + return false; + } + + unsigned long style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; + HWND external_tab_ui_parent = CreateWindow(class_name, window_title, + style, CW_USEDEFAULT, 0, + CW_USEDEFAULT, 0, NULL, NULL, + NULL, NULL); + style = GetWindowLong(external_tab_window, GWL_STYLE); + style |= WS_CHILD; + style &= ~WS_POPUP; + SetWindowLong(external_tab_window, GWL_STYLE, style); + SetParent(external_tab_window, external_tab_ui_parent); + RECT client_rect = {0}; + GetClientRect(external_tab_ui_parent, &client_rect); + SetWindowPos(external_tab_window, NULL, 0, 0, client_rect.right, + client_rect.bottom, SWP_NOZORDER); + ShowWindow(external_tab_window, SW_SHOW); + ShowWindow(external_tab_ui_parent, SW_SHOW); + + // Allow the renderers to connect. + Sleep(1000); + DestroyWindow(external_tab_ui_parent); + return true; +} + +TEST_F(AutomationProxyVisibleTest, CreateExternalTab) { + HWND external_tab_container = NULL; + scoped_ptr<TabProxy> tab(automation()->CreateExternalTab( + &external_tab_container)); + EXPECT_TRUE(tab != NULL); + EXPECT_NE(FALSE, ::IsWindow(external_tab_container)); + if (tab != NULL) { + tab->NavigateInExternalTab(GURL(L"http://www.google.com")); + EXPECT_EQ(true, ExternalTabHandler(external_tab_container)); + // Since the tab goes away lazily, wait a bit + Sleep(1000); + EXPECT_FALSE(tab->is_valid()); + } +} + +TEST_F(AutomationProxyTest, AutocompleteGetSetText) { + scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr<AutocompleteEditProxy> edit( + automation()->GetAutocompleteEditForBrowser(browser.get())); + ASSERT_TRUE(edit.get()); + EXPECT_TRUE(edit->is_valid()); + const std::wstring text_to_set = L"Lollerskates"; + std::wstring actual_text; + EXPECT_TRUE(edit->SetText(text_to_set)); + EXPECT_TRUE(edit->GetText(&actual_text)); + EXPECT_EQ(text_to_set, actual_text); + scoped_ptr<AutocompleteEditProxy> edit2( + automation()->GetAutocompleteEditForBrowser(browser.get())); + EXPECT_TRUE(edit2->GetText(&actual_text)); + EXPECT_EQ(text_to_set, actual_text); +} + +TEST_F(AutomationProxyTest, AutocompleteParallelProxy) +{ + scoped_ptr<BrowserProxy> browser1(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser1.get()); + scoped_ptr<AutocompleteEditProxy> edit1( + automation()->GetAutocompleteEditForBrowser(browser1.get())); + ASSERT_TRUE(edit1.get()); + EXPECT_TRUE(browser1->ApplyAccelerator(IDC_DUPLICATE)); + scoped_ptr<BrowserProxy> browser2(automation()->GetBrowserWindow(1)); + ASSERT_TRUE(browser2.get()); + scoped_ptr<AutocompleteEditProxy> edit2( + automation()->GetAutocompleteEditForBrowser(browser2.get())); + ASSERT_TRUE(edit2.get()); + EXPECT_TRUE(browser2->GetTab(0)->WaitForTabToBeRestored()); + const std::wstring text_to_set1 = L"Lollerskates"; + const std::wstring text_to_set2 = L"Roflcopter"; + std::wstring actual_text1, actual_text2; + EXPECT_TRUE(edit1->SetText(text_to_set1)); + EXPECT_TRUE(edit2->SetText(text_to_set2)); + EXPECT_TRUE(edit1->GetText(&actual_text1)); + EXPECT_TRUE(edit2->GetText(&actual_text2)); + EXPECT_EQ(text_to_set1, actual_text1); + EXPECT_EQ(text_to_set2, actual_text2); +} + +TEST_F(AutomationProxyVisibleTest, AutocompleteMatchesTest) { + scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr<AutocompleteEditProxy> edit( + automation()->GetAutocompleteEditForBrowser(browser.get())); + ASSERT_TRUE(edit.get()); + EXPECT_TRUE(browser->ApplyAccelerator(IDC_FOCUS_LOCATION)); + EXPECT_TRUE(edit->is_valid()); + EXPECT_TRUE(edit->SetText(L"Roflcopter")); + EXPECT_TRUE(edit->WaitForQuery(30000)); + bool query_in_progress; + EXPECT_TRUE(edit->IsQueryInProgress(&query_in_progress)); + EXPECT_FALSE(query_in_progress); + std::vector<AutocompleteMatchData> matches; + EXPECT_TRUE(edit->GetAutocompleteMatches(&matches)); + EXPECT_FALSE(matches.empty()); +} diff --git a/chrome/test/automation/browser_proxy.cc b/chrome/test/automation/browser_proxy.cc new file mode 100644 index 0000000..81b8900 --- /dev/null +++ b/chrome/test/automation/browser_proxy.cc @@ -0,0 +1,348 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/test/automation/browser_proxy.h" + +#include <vector> + +#include "base/logging.h" +#include "base/time.h" +#include "chrome/test/automation/automation_constants.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/tab_proxy.h" + +bool BrowserProxy::ActivateTab(int tab_index) { + return ActivateTabWithTimeout(tab_index, INFINITE, NULL); +} + +bool BrowserProxy::ActivateTabWithTimeout(int tab_index, uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_ActivateTabRequest(0, handle_, tab_index), &response, + AutomationMsg_ActivateTabResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + int activate_tab_response = -1; + if (response->ReadInt(&iter, &activate_tab_response) && + (activate_tab_response >= 0)) { + succeeded = true; + } else { + succeeded = false; + } + + return succeeded; +} + +bool BrowserProxy::BringToFront() { + return BringToFrontWithTimeout(INFINITE, NULL); +} + +bool BrowserProxy::BringToFrontWithTimeout(uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_BringBrowserToFront(0, handle_), &response, + AutomationMsg_BringBrowserToFrontResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + if (!response->ReadBool(&iter, &succeeded)) + succeeded = false; + + return succeeded; +} + +bool BrowserProxy::IsPageMenuCommandEnabledWithTimeout(int id, + uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_IsPageMenuCommandEnabled(0, handle_, id), &response, + AutomationMsg_IsPageMenuCommandEnabledResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + if (!response->ReadBool(&iter, &succeeded)) + succeeded = false; + + return succeeded; +} + +bool BrowserProxy::AppendTab(const GURL& tab_url) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_AppendTabRequest(0, handle_, tab_url), &response, + AutomationMsg_AppendTabResponse::ID); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + int append_tab_response = -1; + if (response->ReadInt(&iter, &append_tab_response) && + (append_tab_response >= 0)) { + succeeded = true; + } else { + succeeded = false; + } + + return succeeded; +} + +bool BrowserProxy::GetActiveTabIndex(int* active_tab_index) const { + return GetActiveTabIndexWithTimeout(active_tab_index, INFINITE, NULL); +} + +bool BrowserProxy::GetActiveTabIndexWithTimeout(int* active_tab_index, + uint32 timeout_ms, + bool* is_timeout) const { + if (!is_valid()) + return false; + + if (!active_tab_index) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_ActiveTabIndexRequest(0, handle_), &response, + AutomationMsg_ActiveTabIndexResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + int active_tab_index_response = -1; + if (response->ReadInt(&iter, &active_tab_index_response) && + (active_tab_index_response >= 0)) { + *active_tab_index = active_tab_index_response; + } else { + succeeded = false; + } + + return succeeded; +} + +TabProxy* BrowserProxy::GetTab(int tab_index) const { + if (!is_valid()) + return NULL; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_TabRequest(0, handle_, tab_index), &response, + AutomationMsg_TabResponse::ID); + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return NULL; + + void* iter = NULL; + int handle; + + if (!response->ReadInt(&iter, &handle) || (handle == 0)) + return NULL; + return new TabProxy(sender_, tracker_, handle); +} + +TabProxy* BrowserProxy::GetActiveTab() const { + return GetActiveTabWithTimeout(INFINITE, NULL); +} + +TabProxy* BrowserProxy::GetActiveTabWithTimeout(uint32 timeout_ms, + bool* is_timeout) const { + int active_tab_index; + if (!GetActiveTabIndexWithTimeout(&active_tab_index, timeout_ms, is_timeout)) + return NULL; + return GetTab(active_tab_index); +} + +bool BrowserProxy::GetTabCount(int* num_tabs) const { + return GetTabCountWithTimeout(num_tabs, INFINITE, NULL); +} + +bool BrowserProxy::GetTabCountWithTimeout(int* num_tabs, uint32 timeout_ms, + bool* is_timeout) const { + if (!is_valid()) + return false; + + if (!num_tabs) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_TabCountRequest(0, handle_), &response, + AutomationMsg_TabCountResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + int tab_count_response = -1; + if (response->ReadInt(&iter, &tab_count_response) && + (tab_count_response >= 0)) { + *num_tabs = tab_count_response; + } else { + succeeded = false; + } + + return succeeded; +} + +bool BrowserProxy::ApplyAccelerator(int id) { + if (!is_valid()) + return false; + + return sender_->Send( + new AutomationMsg_ApplyAcceleratorRequest(0, handle_, id)); +} + +bool BrowserProxy::SimulateDrag(const POINT& start, + const POINT& end, + int flags) { + return SimulateDragWithTimeout(start, end, flags, INFINITE, NULL); +} + +bool BrowserProxy::SimulateDragWithTimeout(const POINT& start, + const POINT& end, + int flags, + uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return false; + + std::vector<POINT> drag_path; + drag_path.push_back(start); + drag_path.push_back(end); + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_WindowDragRequest(0, handle_, drag_path, flags), + &response, AutomationMsg_WindowDragResponse::ID, timeout_ms, is_timeout); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + void* iter = NULL; + if (!response->ReadBool(&iter, &succeeded)) + succeeded = false; + + return succeeded; +} + +bool BrowserProxy::WaitForTabCountToChange(int count, int* new_count, + int wait_timeout) { + const TimeTicks start = TimeTicks::Now(); + const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout); + while (TimeTicks::Now() - start < timeout) { + Sleep(automation::kSleepTime); + bool is_timeout; + bool succeeded = GetTabCountWithTimeout(new_count, wait_timeout, + &is_timeout); + if (!succeeded) + return false; + if (count != *new_count) + return true; + } + // If we get here, the tab count hasn't changed. + return false; +} + +bool BrowserProxy::WaitForTabToBecomeActive(int tab, + int wait_timeout) { + const TimeTicks start = TimeTicks::Now(); + const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout); + while (TimeTicks::Now() - start < timeout) { + Sleep(automation::kSleepTime); + int active_tab; + if (GetActiveTabIndex(&active_tab) && active_tab == tab) + return true; + } + // If we get here, the active tab hasn't changed. + return false; +} + +bool BrowserProxy::GetHWND(HWND* handle) const { + if (!is_valid()) + return false; + + if (!handle) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_WindowHWNDRequest(0, handle_), &response, + AutomationMsg_WindowHWNDResponse::ID); + + scoped_ptr<IPC::Message> response_deleter(response); // Delete on return. + if (!succeeded) + return false; + + HWND hwnd_response; + if (AutomationMsg_WindowHWNDResponse::Read(response, &hwnd_response) && + hwnd_response) { + *handle = hwnd_response; + } else { + succeeded = false; + } + + return succeeded; +} diff --git a/chrome/test/automation/browser_proxy.h b/chrome/test/automation/browser_proxy.h new file mode 100644 index 0000000..469b377 --- /dev/null +++ b/chrome/test/automation/browser_proxy.h @@ -0,0 +1,164 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_BROWSER_PROXY_H__ +#define CHROME_TEST_AUTOMATION_BROWSER_PROXY_H__ + +#include <string> +#include <windows.h> +#include "chrome/test/automation/automation_handle_tracker.h" + +class GURL; +class TabProxy; + +namespace gfx { + class Rect; +} + +// This class presents the interface to actions that can be performed on +// a given browser window. Note that this object can be invalidated at any +// time if the corresponding browser window in the app is closed. In that case, +// any subsequent calls will return false immediately. +class BrowserProxy : public AutomationResourceProxy { + public: + BrowserProxy(AutomationMessageSender* sender, + AutomationHandleTracker* tracker, + int handle) + : AutomationResourceProxy(tracker, sender, handle) {} + virtual ~BrowserProxy() {} + + // Activates the tab corresponding to (zero-based) tab_index. Returns true if + // successful. + bool ActivateTab(int tab_index); + + // Like ActivateTab, but returns false if response is not received before + // the specified timeout. + bool ActivateTabWithTimeout(int tab_index, uint32 timeout_ms, + bool* is_timeout); + + // Bring the browser window to the front, activating it. Returns true on + // success. + bool BringToFront(); + + // Like BringToFront, but returns false if action is not completed before + // the specified timeout. + bool BringToFrontWithTimeout(uint32 timeout_ms, bool* is_timeout); + + // Checks to see if a navigation command is active or not. Can also + // return false if action is not completed before the specified + // timeout; is_timeout will be set in those cases. + bool IsPageMenuCommandEnabledWithTimeout(int id, uint32 timeout_ms, + bool* is_timeout); + + // Append a new tab to the TabStrip. The new tab is selected. + // The new tab navigates to the given tab_url. + // Returns true if successful. + // TODO(mpcomplete): If the navigation results in an auth challenge, the + // TabProxy we attach won't know about it. See bug 666730. + bool AppendTab(const GURL& tab_url); + + // Gets the (zero-based) index of the currently active tab. Returns true if + // successful. + bool GetActiveTabIndex(int* active_tab_index) const; + + // Like GetActiveTabIndex, but returns false if active tab is not received + // before the specified timeout. + bool GetActiveTabIndexWithTimeout(int* active_tab_index, uint32 timeout_ms, + bool* is_timeout) const; + + // Returns the number of tabs in the given window. Returns true if + // the call was successful. + bool GetTabCount(int* num_tabs) const; + + // Like GetTabCount, but returns false if tab count is not received within the + // before timeout. + bool GetTabCountWithTimeout(int* num_tabs, uint32 timeout_ms, + bool* is_timeout) const; + + // Returns the TabProxy for the tab at the given index, transferring + // ownership of the pointer to the caller. On failure, returns NULL. + // + // Use GetTabCount to see how many windows you can ask for. Tab numbers + // are 0-based. + TabProxy* GetTab(int tab_index) const; + + // Returns the TabProxy for the currently active tab, transferring + // ownership of the pointer to the caller. On failure, returns NULL. + TabProxy* GetActiveTab() const; + + // Like GetActiveTab, but returns NULL if no response is received before + // the specified timout. + TabProxy* GetActiveTabWithTimeout(uint32 timeout_ms, bool* is_timeout) const; + + // Apply the accelerator with given id (IDC_BACK, IDC_NEWTAB ...) + // Returns true if the call was successful. + // + // The alternate way to test the accelerators is to use the Windows messaging + // system to send the actual keyboard events (ui_controls.h) A precondition + // to using this system is that the target window should have the keyboard + // focus. This leads to a flaky test behavior in circumstances when the + // desktop screen is locked or the test is being executed over a remote + // desktop. + bool ApplyAccelerator(int id); + + // Performs a drag operation between the start and end points (both defined + // in window coordinates). |flags| specifies which buttons are pressed for + // the drag, as defined in chrome/views/event.h. + virtual bool SimulateDrag(const POINT& start, const POINT& end, int flags); + + // Like SimulateDrag, but returns false if response is not received before + // the specified timeout. + virtual bool SimulateDragWithTimeout(const POINT& start, const POINT& end, + int flags, uint32 timeout_ms, + bool* is_timeout); + + // Block the thread until the tab count changes. + // |count| is the original tab count. + // |new_count| is updated with the number of new tabs. + // |wait_timeout| is the timeout, in milliseconds, for waiting. + // Returns false if the tab count does not change. + bool WaitForTabCountToChange(int count, int* new_count, int wait_timeout); + + // Block the thread until the specified tab is the active tab. + // |wait_timeout| is the timeout, in milliseconds, for waiting. + // Returns false if the tab does not become active. + bool WaitForTabToBecomeActive(int tab, int wait_timeout); + + // Gets the outermost HWND that corresponds to the given browser. + // Returns true if the call was successful. + // Note that ideally this should go and the version of WindowProxy should be + // used instead. We have to keep it for start_up_tests that test against a + // reference build. + bool GetHWND(HWND* handle) const; + + private: + DISALLOW_EVIL_CONSTRUCTORS(BrowserProxy); +}; + +#endif // #define CHROME_TEST_AUTOMATION_BROWSER_PROXY_H__ diff --git a/chrome/test/automation/constrained_window_proxy.cc b/chrome/test/automation/constrained_window_proxy.cc new file mode 100644 index 0000000..03fae09 --- /dev/null +++ b/chrome/test/automation/constrained_window_proxy.cc @@ -0,0 +1,92 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "constrained_window_proxy.h" + +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" + +bool ConstrainedWindowProxy::GetTitle(std::wstring* title) const { + if (!is_valid()) + return false; + + if (!title) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_ConstrainedTitleRequest(0, handle_), &response, + AutomationMsg_ConstrainedTitleResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + int title_size_response = -1; + if (response->ReadInt(&iter, &title_size_response) && + (title_size_response >= 0)) { + response->ReadWString(&iter, title); + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool ConstrainedWindowProxy::GetBoundsWithTimeout(gfx::Rect* bounds, + uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return false; + + if (!bounds) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_ConstrainedWindowBoundsRequest(0, handle_), + &response, + AutomationMsg_ConstrainedWindowBoundsResponse::ID, + timeout_ms, + is_timeout); + scoped_ptr<IPC::Message> responseDeleter(response); + if (!succeeded) + return false; + + Tuple2<bool, gfx::Rect> result; + AutomationMsg_WindowViewBoundsResponse::Read(response, &result); + + *bounds = result.b; + return result.a; +} diff --git a/chrome/test/automation/constrained_window_proxy.h b/chrome/test/automation/constrained_window_proxy.h new file mode 100644 index 0000000..f3dcb6c --- /dev/null +++ b/chrome/test/automation/constrained_window_proxy.h @@ -0,0 +1,59 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_CONSTRAINED_WINDOW_PROXY_H__ +#define CHROME_TEST_AUTOMATION_CONSTRAINED_WINDOW_PROXY_H__ + +#include <string> + +#include "chrome/test/automation/automation_handle_tracker.h" + +namespace gfx { +class Rect; +} + +class ConstrainedWindowProxy : public AutomationResourceProxy { +public: + ConstrainedWindowProxy(AutomationMessageSender* sender, + AutomationHandleTracker* tracker, + int handle) + : AutomationResourceProxy(tracker, sender, handle) {} + + virtual ~ConstrainedWindowProxy() {} + + bool GetTitle(std::wstring* title) const; + bool GetBoundsWithTimeout(gfx::Rect* bounds, + uint32 timeout_ms, + bool* is_timeout); + +private: + DISALLOW_EVIL_CONSTRUCTORS(ConstrainedWindowProxy); +}; + +#endif // CHROME_TEST_AUTOMATION_CONSTRAINED_WINDOW_PROXY_H__
\ No newline at end of file diff --git a/chrome/test/automation/tab_proxy.cc b/chrome/test/automation/tab_proxy.cc new file mode 100644 index 0000000..4fb4432 --- /dev/null +++ b/chrome/test/automation/tab_proxy.cc @@ -0,0 +1,920 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/test/automation/tab_proxy.h" + +#include <algorithm> + +#include "base/logging.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/test/automation/automation_constants.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/constrained_window_proxy.h" +#include "googleurl/src/gurl.h" + +bool TabProxy::GetTabTitle(std::wstring* title) const { + if (!is_valid()) + return false; + + if (!title) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_TabTitleRequest(0, handle_), &response, + AutomationMsg_TabTitleResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + int tab_title_size_response = -1; + if (response->ReadInt(&iter, &tab_title_size_response) && + (tab_title_size_response >= 0)) { + response->ReadWString(&iter, title); + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool TabProxy::IsShelfVisible(bool* is_visible) { + if (!is_valid()) + return false; + + if (!is_visible) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_ShelfVisibilityRequest(0, handle_), + &response, + AutomationMsg_ShelfVisibilityResponse::ID); + if (!succeeded) + return false; + + void* iter = NULL; + response->ReadBool(&iter, is_visible); + delete response; + return true; +} + +int TabProxy::FindInPage(const std::wstring& search_string, + FindInPageDirection forward, + FindInPageCase match_case) { + if (!is_valid()) + return -1; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_FindInPageRequest(0, handle_, search_string, + forward, match_case), + &response, + AutomationMsg_FindInPageResponse::ID); + if (!succeeded) + return -1; + + void* iter = NULL; + int matches_found; + AutomationMsg_FindInPageResponse::Read(response, &matches_found); + + return matches_found; +} + +int TabProxy::NavigateToURL(const GURL& url) { + return NavigateToURLWithTimeout(url, INFINITE, NULL); +} + +int TabProxy::NavigateToURLWithTimeout(const GURL& url, + uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_NavigateToURLRequest(0, handle_, url), &response, + AutomationMsg_NavigateToURLResponse::ID, timeout_ms, is_timeout); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + int navigate_response = AUTOMATION_MSG_NAVIGATION_ERROR; + response->ReadInt(&iter, &navigate_response); + + delete response; + return navigate_response; +} + +int TabProxy::NavigateInExternalTab(const GURL& url) { + if (!is_valid()) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + IPC::Message* response = NULL; + bool is_timeout = false; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_NavigateInExternalTabRequest(0, handle_, url), &response, + AutomationMsg_NavigateInExternalTabResponse::ID, INFINITE, &is_timeout); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + int navigate_response = AUTOMATION_MSG_NAVIGATION_ERROR; + response->ReadInt(&iter, &navigate_response); + + delete response; + return navigate_response; +} + +bool TabProxy::SetAuth(const std::wstring& username, + const std::wstring& password) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_SetAuthRequest(0, handle_, username, password), &response, + AutomationMsg_SetAuthResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + int navigate_response = -1; + succeeded = (response->ReadInt(&iter, &navigate_response) && + navigate_response >= 0); + + delete response; + return succeeded; +} + +bool TabProxy::CancelAuth() { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_CancelAuthRequest(0, handle_), &response, + AutomationMsg_CancelAuthResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + int navigate_response = -1; + succeeded = (response->ReadInt(&iter, &navigate_response) && + navigate_response >= 0); + + delete response; + return succeeded; +} + +bool TabProxy::NeedsAuth() const { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_NeedsAuthRequest(0, handle_), &response, + AutomationMsg_NeedsAuthResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + bool needs_auth = false; + response->ReadBool(&iter, &needs_auth); + + delete response; + return needs_auth; +} + +int TabProxy::GoBack() { + if (!is_valid()) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_GoBackRequest(0, handle_), &response, + AutomationMsg_GoBackResponse::ID); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + int navigate_response = AUTOMATION_MSG_NAVIGATION_ERROR; + response->ReadInt(&iter, &navigate_response); + + delete response; + return navigate_response; +} + +int TabProxy::GoForward() { + if (!is_valid()) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_GoForwardRequest(0, handle_), &response, + AutomationMsg_GoForwardResponse::ID); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + int navigate_response = AUTOMATION_MSG_NAVIGATION_ERROR; + response->ReadInt(&iter, &navigate_response); + + delete response; + return navigate_response; +} + +int TabProxy::Reload() { + if (!is_valid()) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_ReloadRequest(0, handle_), &response, + AutomationMsg_ReloadResponse::ID); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + int navigate_response = AUTOMATION_MSG_NAVIGATION_ERROR; + response->ReadInt(&iter, &navigate_response); + + delete response; + return navigate_response; +} + +bool TabProxy::GetRedirectsFrom(const GURL& source_url, + std::vector<GURL>* redirects) { + std::vector<GURL> output; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_RedirectsFromRequest(0, handle_, source_url), &response, + AutomationMsg_RedirectsFromResponse::ID); + if (!succeeded) + return false; + scoped_ptr<IPC::Message> auto_deleter(response); + + void* iter = NULL; + int num_redirects; + if (!response->ReadInt(&iter, &num_redirects)) + return false; + if (num_redirects < 0) + return false; // Negative redirect counts indicate failure. + + for (int i = 0; i < num_redirects; i++) { + GURL cur; + if (!IPC::ParamTraits<GURL>::Read(response, &iter, &cur)) + return false; + output.push_back(cur); + } + redirects->swap(output); + return true; +} + +bool TabProxy::GetCurrentURL(GURL* url) const { + if (!is_valid()) + return false; + + if (!url) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_TabURLRequest(0, handle_), &response, + AutomationMsg_TabURLResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + bool tab_url_success = false; + if (response->ReadBool(&iter, &tab_url_success) && tab_url_success) { + if (!IPC::ParamTraits<GURL>::Read(response, &iter, url)) + succeeded = false; + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool TabProxy::NavigateToURLAsync(const GURL& url) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_NavigationAsyncRequest(0, handle_, url), &response, + AutomationMsg_NavigationAsyncResponse::ID); + + if (!succeeded) + return false; + + bool status; + if (AutomationMsg_NavigationAsyncResponse::Read(response, &status) && + status) { + succeeded = true; + } + + delete response; + return succeeded; +} + +bool TabProxy::GetHWND(HWND* hwnd) const { + if (!is_valid()) + return false; + if (!hwnd) { + NOTREACHED(); + return false; + } + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_TabHWNDRequest(0, handle_), &response, + AutomationMsg_TabHWNDResponse::ID); + if (!succeeded) + return false; + void* iter = NULL; + HWND tab_hwnd = NULL; + if (AutomationMsg_TabHWNDResponse::Read(response, &tab_hwnd) && tab_hwnd) { + *hwnd = tab_hwnd; + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool TabProxy::GetProcessID(int* process_id) const { + if (!is_valid()) + return false; + + if (!process_id) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_TabProcessIDRequest(0, handle_), &response, + AutomationMsg_TabProcessIDResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + int pid; + if (AutomationMsg_TabProcessIDResponse::Read(response, &pid) && (pid >= 0)) { + *process_id = pid; + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool TabProxy::ExecuteAndExtractString(const std::wstring& frame_xpath, + const std::wstring& jscript, + std::wstring* string_value) { + Value* root = NULL; + bool succeeded = ExecuteAndExtractValue(frame_xpath, jscript, &root); + if (!succeeded) + return false; + + std::wstring read_value; + DCHECK(root->IsType(Value::TYPE_LIST)); + Value* value = NULL; + succeeded = static_cast<ListValue*>(root)->Get(0, &value); + if (succeeded) { + succeeded = value->GetAsString(&read_value); + if (succeeded) { + string_value->swap(read_value); + } + } + + delete root; + return succeeded; +} + +bool TabProxy::ExecuteAndExtractBool(const std::wstring& frame_xpath, + const std::wstring& jscript, + bool* bool_value) { + Value* root = NULL; + bool succeeded = ExecuteAndExtractValue(frame_xpath, jscript, &root); + if (!succeeded) + return false; + + bool read_value = false; + DCHECK(root->IsType(Value::TYPE_LIST)); + Value* value = NULL; + succeeded = static_cast<ListValue*>(root)->Get(0, &value); + if (succeeded) { + succeeded = value->GetAsBoolean(&read_value); + if (succeeded) { + *bool_value = read_value; + } + } + + delete value; + return succeeded; +} + +bool TabProxy::ExecuteAndExtractInt(const std::wstring& frame_xpath, + const std::wstring& jscript, + int* int_value) { + Value* root = NULL; + bool succeeded = ExecuteAndExtractValue(frame_xpath, jscript, &root); + if (!succeeded) + return false; + + int read_value = 0; + DCHECK(root->IsType(Value::TYPE_LIST)); + Value* value = NULL; + succeeded = static_cast<ListValue*>(root)->Get(0, &value); + if (succeeded) { + succeeded = value->GetAsInteger(&read_value); + if (succeeded) { + *int_value = read_value; + } + } + + delete value; + return succeeded; +} + +bool TabProxy::ExecuteAndExtractValue(const std::wstring& frame_xpath, + const std::wstring& jscript, + Value** value) { + if (!is_valid()) + return false; + + if (!value) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_DomOperationRequest(0, handle_, frame_xpath, jscript), + &response, AutomationMsg_DomOperationResponse::ID); + + void* iter = NULL; + std::string json; + succeeded = response->ReadString(&iter, &json); + if (!succeeded) { + delete response; + return false; + } + // 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); + succeeded = deserializer.Deserialize(value); + + delete response; + return succeeded; +} + +bool TabProxy::GetConstrainedWindowCount(int* count) const { + if (!is_valid()) + return false; + + if (!count) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_ConstrainedWindowCountRequest(0, handle_), + &response, AutomationMsg_ConstrainedWindowCountResponse::ID); + + void* iter = NULL; + int count_response = -1; + if (response->ReadInt(&iter, &count_response) && + (count_response >= 0)) { + *count = count_response; + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +ConstrainedWindowProxy* TabProxy::GetConstrainedWindow( + int window_index) const { + if (!is_valid()) + return NULL; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_ConstrainedWindowRequest(0, handle_, window_index), + &response, AutomationMsg_ConstrainedWindowResponse::ID); + if (!succeeded) + return NULL; + + void* iter = NULL; + int handle; + + scoped_ptr<IPC::Message> response_deleter(response); // Ensure deleted. + if (response->ReadInt(&iter, &handle) && (handle != 0)) + return new ConstrainedWindowProxy(sender_, tracker_, handle); + return NULL; +} + +bool TabProxy::WaitForChildWindowCountToChange(int count, int* new_count, + int wait_timeout) { + int intervals = std::min(wait_timeout/automation::kSleepTime, 1); + for (int i = 0; i < intervals; ++i) { + Sleep(automation::kSleepTime); + bool succeeded = GetConstrainedWindowCount(new_count); + if (!succeeded) return false; + if (count != *new_count) return true; + } + // Constrained Window count did not change, return false. + return false; +} + +bool TabProxy::GetCookies(const GURL& url, std::string* cookies) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_GetCookiesRequest(0, url, handle_), &response, + AutomationMsg_GetCookiesResponse::ID); + + if (succeeded) { + void* iter = NULL; + int size; + std::string local_value; + + if (response->ReadInt(&iter, &size) && size >=0) { + if (!response->ReadString(&iter, cookies)) { + succeeded = false; + } + } else { + succeeded = false; + } + } + + delete response; + return succeeded; +} + +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::SetCookie(const GURL& url, const std::string& value) { + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_SetCookieRequest(0, url, value, handle_), &response, + AutomationMsg_SetCookieResponse::ID); + if (!succeeded) + return false; + + void* iter = NULL; + int response_value; + + if (!response->ReadInt(&iter, &response_value) || response_value < 0) { + succeeded = false; + } + + delete response; + return succeeded; +} + +int TabProxy::InspectElement(int x, int y) { + if (!is_valid()) + return -1; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_InspectElementRequest(0, handle_, x, y), + &response, AutomationMsg_InspectElementResponse::ID); + if (!succeeded) + return -1; + + int ret; + AutomationMsg_InspectElementResponse::Read(response, &ret); + return ret; +} + +bool TabProxy::GetDownloadDirectory(std::wstring* download_directory) { + DCHECK(download_directory); + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = + sender_->SendAndWaitForResponse( + new AutomationMsg_DownloadDirectoryRequest(0, handle_), + &response, + AutomationMsg_DownloadDirectoryResponse::ID); + if (!succeeded) + return false; + + void* iter = NULL; + response->ReadWString(&iter, download_directory); + delete response; + return true; +} + +bool TabProxy::ShowInterstitialPage(const std::string& html_text) { + if (!is_valid()) + return false; + + const int kTimeout = 2000; + bool is_timeout = false; + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_ShowInterstitialPageRequest(0, handle_, html_text), + &response, + AutomationMsg_ShowInterstitialPageResponse::ID, kTimeout, &is_timeout); + + if (!succeeded || !is_timeout) + return false; + + void* iter = NULL; + bool result = true; + response->ReadBool(&iter, &result); + + delete response; + return result; +} + +bool TabProxy::HideInterstitialPage() { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = + sender_->SendAndWaitForResponse( + new AutomationMsg_HideInterstitialPageRequest(0, handle_), + &response, + AutomationMsg_HideInterstitialPageResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + bool result = true; + response->ReadBool(&iter, &result); + + delete response; + return result; +} + +bool TabProxy::Close() { + return Close(false); +} + +bool TabProxy::Close(bool wait_until_closed) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = + sender_->SendAndWaitForResponse( + new AutomationMsg_CloseTabRequest(0, handle_, wait_until_closed), + &response, + AutomationMsg_CloseTabResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + bool result = true; + response->ReadBool(&iter, &result); + + delete response; + return result; +} + +bool TabProxy::SetAccelerators(HACCEL accel_table, + int accel_table_entry_count) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool is_timeout = false; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_SetAcceleratorsForTab(0, handle_, accel_table, + accel_table_entry_count), + &response, + AutomationMsg_SetAcceleratorsForTabResponse::ID, INFINITE, &is_timeout); + + if (!succeeded) + return AUTOMATION_MSG_NAVIGATION_ERROR; + + void* iter = NULL; + bool set_accel_response = false; + response->ReadBool(&iter, &set_accel_response); + + delete response; + return set_accel_response; +} + +bool TabProxy::ProcessUnhandledAccelerator(const MSG& msg) { + if (!is_valid()) + return false; + return sender_->Send( + new AutomationMsg_ProcessUnhandledAccelerator(0, handle_, msg)); + // This message expects no response +} + +bool TabProxy::WaitForTabToBeRestored() { + if (!is_valid()) + return false; + IPC::Message* response = NULL; + return sender_->SendAndWaitForResponse( + new AutomationMsg_WaitForTabToBeRestored(0, handle_), &response, + AutomationMsg_TabFinishedRestoring::ID); +} + +bool TabProxy::GetSecurityState(SecurityStyle* security_style, + int* ssl_cert_status, + int* mixed_content_state) { + DCHECK(security_style && ssl_cert_status && mixed_content_state); + + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool is_timeout = false; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_GetSecurityState(0, handle_), + &response, + AutomationMsg_GetSecurityStateResponse::ID, INFINITE, &is_timeout); + scoped_ptr<IPC::Message> auto_deleter(response); + + if (!succeeded) + return false; + + void* iter = NULL; + int value; + + response->ReadBool(&iter, &succeeded); + if (!succeeded) + return false; + response->ReadInt(&iter, &value); + *security_style = static_cast<SecurityStyle>(value); + response->ReadInt(&iter, ssl_cert_status); + response->ReadInt(&iter, mixed_content_state); + + return true; +} + +bool TabProxy::GetPageType(NavigationEntry::PageType* page_type) { + DCHECK(page_type); + + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool is_timeout = false; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_GetPageType(0, handle_), + &response, + AutomationMsg_GetPageTypeResponse::ID, INFINITE, &is_timeout); + scoped_ptr<IPC::Message> auto_deleter(response); + + if (!succeeded) + return false; + + void* iter = NULL; + int value; + response->ReadBool(&iter, &succeeded); + if (!succeeded) + return false; + response->ReadInt(&iter, &value); + *page_type = static_cast<NavigationEntry::PageType>(value); + return true; +} + +bool TabProxy::TakeActionOnSSLBlockingPage(bool proceed) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool is_timeout = false; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_ActionOnSSLBlockingPage(0, handle_, proceed), + &response, + AutomationMsg_ActionOnSSLBlockingPageResponse::ID, INFINITE, &is_timeout); + scoped_ptr<IPC::Message> auto_deleter(response); + + if (!succeeded) + return false; + + void* iter = NULL; + bool status = false; + response->ReadBool(&iter, &status); + + return status; +} + +bool TabProxy::PrintNow() { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_PrintNowRequest(0, handle_), &response, + AutomationMsg_PrintNowResponse::ID); + scoped_ptr<IPC::Message> auto_deleter(response); + if (!succeeded) + return false; + + void* iter = NULL; + succeeded = false; + return response->ReadBool(&iter, &succeeded) && succeeded; +} + +bool TabProxy::SavePage(const std::wstring& file_name, + const std::wstring& dir_path, + SavePackage::SavePackageType type) { + if (!is_valid()) + return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_SavePageRequest(0, handle_, file_name, + dir_path, static_cast<int>(type)), + &response, + AutomationMsg_SavePageResponse::ID); + + if (!succeeded) + return false; + + void* iter = NULL; + response->ReadBool(&iter, &succeeded); + delete response; + + return succeeded; +} diff --git a/chrome/test/automation/tab_proxy.h b/chrome/test/automation/tab_proxy.h new file mode 100644 index 0000000..712ab9c --- /dev/null +++ b/chrome/test/automation/tab_proxy.h @@ -0,0 +1,266 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_TAB_PROXY_H__ +#define CHROME_TEST_AUTOMATION_TAB_PROXY_H__ + +#include <wtypes.h> +#include <string> +#include <vector> + +#include "chrome/browser/security_style.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/save_package.h" +#include "chrome/test/automation/automation_handle_tracker.h" + +class ConstrainedWindowProxy; +class GURL; +class Value; + +typedef enum FindInPageDirection { BACK = 0, FWD = 1 }; +typedef enum FindInPageCase { IGNORE_CASE = 0, CASE_SENSITIVE = 1 }; + +class TabProxy : public AutomationResourceProxy { + public: + TabProxy(AutomationMessageSender* sender, + AutomationHandleTracker* tracker, + int handle) + : AutomationResourceProxy(tracker, sender, handle) {} + + virtual ~TabProxy() {} + + // Gets the current url of the tab. + bool GetCurrentURL(GURL* url) const; + + // Gets the title of the tab. + bool GetTabTitle(std::wstring* title) const; + + // Gets the number of constrained window for this tab. + bool GetConstrainedWindowCount(int* count) const; + + // Gets the proxy object for constrained window within this tab. Ownership + // for the returned object is transfered to the caller. Returns NULL on + // failure. + ConstrainedWindowProxy* GetConstrainedWindow(int window_index) const; + + // Execute a javascript in a frame's context whose xpath + // is provided as the first parameter and extract + // the values from the resulting json string. + // Example: + // jscript = "window.domAutomationController.send('string');" + // will result in value = "string" + // jscript = "window.domAutomationController.send(24);" + // will result in value = 24 + bool ExecuteAndExtractString(const std::wstring& frame_xpath, + const std::wstring& jscript, + std::wstring* value); + bool ExecuteAndExtractBool(const std::wstring& frame_xpath, + const std::wstring& jscript, + bool* value); + bool ExecuteAndExtractInt(const std::wstring& frame_xpath, + const std::wstring& jscript, + int* value); + bool ExecuteAndExtractValue(const std::wstring& frame_xpath, + const std::wstring& jscript, + Value** value); + + // Navigates to a url. This method accepts the same kinds of URL input that + // can be passed to Chrome on the command line. This is a synchronous call and + // hence blocks until the navigation completes. + // Returns a status from AutomationMsg_NavigationResponseValues. + int NavigateToURL(const GURL& url); + + // Navigates to a url. This is same as NavigateToURL with a timeout option. + // The function returns until the navigation completes or timeout (in + // milliseconds) occurs. If return after timeout, is_timeout is set to true. + int NavigateToURLWithTimeout(const GURL& url, uint32 timeout_ms, + bool* is_timeout); + + // Navigates to a url in an externally hosted tab. + // This method accepts the same kinds of URL input that + // can be passed to Chrome on the command line. This is a synchronous call and + // hence blocks until the navigation completes. + // Returns a status from AutomationMsg_NavigationResponseValues. + int NavigateInExternalTab(const GURL& url); + + // Navigates to a url. This is an asynchronous version of NavigateToURL. + // The function returns immediately after sending the LoadURL notification + // to the browser. + // TODO(vibhor): Add a callback if needed in future. + // TODO(mpcomplete): If the navigation results in an auth challenge, the + // TabProxy we attach won't know about it. See bug 666730. + bool NavigateToURLAsync(const GURL& url); + + // Replaces a vector contents with the redirect chain out of the given URL. + // Returns true on success. Failure may be due to being unable to send the + // message, parse the response, or a failure of the history system in the + // browser. + bool GetRedirectsFrom(const GURL& source_url, std::vector<GURL>* redirects); + + // Equivalent to hitting the Back button. This is a synchronous call and + // hence blocks until the navigation completes. + int GoBack(); + + // Equivalent to hitting the Forward button. This is a synchronous call and + // hence blocks until the navigation completes. + // Returns a status from AutomationMsg_NavigationResponseValues. + int GoForward(); + + // Equivalent to hitting the Reload button. This is a synchronous call and + // hence blocks until the navigation completes. + int Reload(); + + // Closes the tab. This is synchronous, but does NOT block until the tab has + // closed, rather it blocks until the browser has initiated the close. Use + // Close(true) if you need to block until tab completely closes. + // + // Note that this proxy is invalid after this call. + bool Close(); + + // Variant of close that allows you to specify whether you want to block + // until the tab has completely closed (wait_until_closed == true) or block + // until the browser has initiated the close (wait_until_closed = false). + // + // When a tab is closed the browser does additional work via invoke later + // and may wait for messages from the renderer. Supplying a value of true to + // this method waits until all processing is done. Be careful with this, + // when closing the last tab it is possible for the browser to shutdown BEFORE + // the tab has completely closed. In other words, this may NOT be sent for + // the last tab. + bool Close(bool wait_until_closed); + + // Gets the HWND that corresponds to the content area of this tab. + // Returns true if the call was successful. + // Returns a status from AutomationMsg_NavigationResponseValues. + bool GetHWND(HWND* hwnd) const; + + // Gets the process ID that corresponds to the content area of this tab. + // Returns true if the call was successful. If the specified tab has no + // separate process for rendering its content, the return value is true but + // the process_id is 0. + bool GetProcessID(int* process_id) const; + + // Supply or cancel authentication to a login prompt. These are synchronous + // calls and hence block until the load finishes (or another login prompt + // appears, in the case of invalid login info). + bool SetAuth(const std::wstring& username, const std::wstring& password); + bool CancelAuth(); + + // Checks if this tab has a login prompt waiting for auth. This will be + // true if a navigation results in a login prompt, and if an attempted login + // fails. + // Note that this is only valid if you've done a navigation on this same + // object; different TabProxy objects can refer to the same Tab. Calls + // that can set this are NavigateToURL, GoBack, and GoForward. + // TODO(mpcomplete): we have no way of knowing if auth is needed after either + // NavigateToURLAsync, or after appending a tab with an URL that triggers + // auth. + bool NeedsAuth() const; + + // Fills |*is_visible| with whether the tab's download shelf is currently + // visible. The return value indicates success. On failure, |*is_visible| is + // unchanged. + bool IsShelfVisible(bool* is_visible); + + // Starts a search within the current tab. The parameter 'search_string' + // specifies what string to search for, 'forward' specifies whether to search + // in forward direction, and 'match_case' specifies case sensitivity + // (true=case sensitive). A return value of -1 indicates failure. + int FindInPage(const std::wstring& search_string, FindInPageDirection forward, + FindInPageCase match_case); + + bool GetCookies(const GURL& url, std::string* cookies); + bool GetCookieByName(const GURL& url, + const std::string& name, + std::string* cookies); + bool SetCookie(const GURL& url, const std::string& value); + + // Sends a InspectElement message for the current tab. |x| and |y| are the + // coordinates that we want to simulate that the user is trying to inspect. + int InspectElement(int x, int y); + + // Block the thread until the constrained(child) window count changes. + // First parameter is the original child window count + // The second parameter is updated with the number of new child windows. + // The third parameter specifies the timeout length for the wait loop. + // Returns false if the count does not change. + bool WaitForChildWindowCountToChange(int count, int* new_count, + int wait_timeout); + + bool GetDownloadDirectory(std::wstring* download_directory); + + // Shows an interstitial page. Blocks until the interstitial page + // has been loaded. Return false if a failure happens.3 + bool ShowInterstitialPage(const std::string& html_text); + + // Hides the currently shown interstitial page. Blocks until the interstitial + // page has been hidden. Return false if a failure happens. + bool HideInterstitialPage(); + + // This sets the keyboard accelerators to be used by an externally + // hosted tab. This call is not valid on a regular tab hosted within + // Chrome. + bool SetAccelerators(HACCEL accel_table, int accel_table_entry_count); + + // The container of an externally hosted tab calls this to reflect any + // accelerator keys that it did not process. This gives the tab a chance + // to handle the keys + bool ProcessUnhandledAccelerator(const MSG& msg); + + bool WaitForTabToBeRestored(); + + // Retrieves the different security states for the current tab. + bool GetSecurityState(SecurityStyle* security_style, + int* ssl_cert_status, + int* mixed_content_state); + + // Returns the type of the page currently showing (normal, interstitial, + // error). + bool GetPageType(NavigationEntry::PageType* page_type); + + // Simulates the user action on the SSL blocking page. if |proceed| is true, + // this is equivalent to clicking the 'Proceed' button, if false to 'Take me + // out of there' button. + bool TakeActionOnSSLBlockingPage(bool proceed); + + // Prints the current page without user intervention. + bool PrintNow(); + + // Save the current web page. |file_name| is the HTML file name, and + // |dir_path| is the directory for saving resource files. |type| indicates + // which type we're saving as: HTML only or the complete web page. + bool SavePage(const std::wstring& file_name, + const std::wstring& dir_path, + SavePackage::SavePackageType type); + + private: + DISALLOW_EVIL_CONSTRUCTORS(TabProxy); +}; + +#endif // CHROME_TEST_AUTOMATION_TAB_PROXY_H__ diff --git a/chrome/test/automation/ui_controls.cc b/chrome/test/automation/ui_controls.cc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/automation/ui_controls.cc diff --git a/chrome/test/automation/ui_controls.h b/chrome/test/automation/ui_controls.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/automation/ui_controls.h diff --git a/chrome/test/automation/window_proxy.cc b/chrome/test/automation/window_proxy.cc new file mode 100644 index 0000000..5f647d2 --- /dev/null +++ b/chrome/test/automation/window_proxy.cc @@ -0,0 +1,187 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/test/automation/window_proxy.h" + +#include <vector> +#include <algorithm> + +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "chrome/test/automation/automation_constants.h" +#include "chrome/test/automation/automation_messages.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "googleurl/src/gurl.h" + +bool WindowProxy::GetHWND(HWND* handle) const { + if (!is_valid()) return false; + + if (!handle) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_WindowHWNDRequest(0, handle_), &response, + AutomationMsg_WindowHWNDResponse::ID); + if (!succeeded) + return false; + + HWND hwnd_response; + if (AutomationMsg_WindowHWNDResponse::Read(response, &hwnd_response) && + hwnd_response) { + *handle = hwnd_response; + } else { + succeeded = false; + } + + delete response; + return succeeded; +} + +bool WindowProxy::SimulateOSClick(const POINT& click, int flags) { + if (!is_valid()) return false; + + return sender_->Send( + new AutomationMsg_WindowClickRequest(0, handle_, click, flags)); +} + +bool WindowProxy::SimulateOSKeyPress(wchar_t key, int flags) { + if (!is_valid()) return false; + + return sender_->Send( + new AutomationMsg_WindowKeyPressRequest(0, handle_, key, flags)); +} + +bool WindowProxy::SetVisible(bool visible) { + if (!is_valid()) return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_SetWindowVisibleRequest(0, handle_, visible), + &response, AutomationMsg_SetWindowVisibleResponse::ID); + + scoped_ptr<IPC::Message> response_deleter(response); // Ensure deleted. + if (!succeeded) + return false; + + void* iter = NULL; + if (!response->ReadBool(&iter, &succeeded)) + succeeded = false; + + return succeeded; +} + +bool WindowProxy::IsActive(bool* active) { + if (!is_valid()) return false; + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_IsWindowActiveRequest(0, handle_), + &response, AutomationMsg_IsWindowActiveResponse::ID); + + scoped_ptr<IPC::Message> response_deleter(response); // Ensure deleted. + if (!succeeded) + return false; + + void* iter = NULL; + if (!response->ReadBool(&iter, &succeeded) || !succeeded) + return false; + + if (!response->ReadBool(&iter, active)) + return false; + + return true; +} + +bool WindowProxy::Activate() { + if (!is_valid()) return false; + + return sender_->Send(new AutomationMsg_ActivateWindow(0, handle_)); +} + +bool WindowProxy::GetViewBounds(int view_id, gfx::Rect* bounds, + bool screen_coordinates) { + return GetViewBoundsWithTimeout(view_id, bounds, screen_coordinates, + INFINITE, NULL); +} + +bool WindowProxy::GetViewBoundsWithTimeout(int view_id, gfx::Rect* bounds, + bool screen_coordinates, + uint32 timeout_ms, + bool* is_timeout) { + if (!is_valid()) return false; + + if (!bounds) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponseWithTimeout( + new AutomationMsg_WindowViewBoundsRequest(0, handle_, view_id, + screen_coordinates), + &response, + AutomationMsg_WindowViewBoundsResponse::ID, + timeout_ms, + is_timeout); + if (!succeeded) + return false; + + Tuple2<bool, gfx::Rect> result; + AutomationMsg_WindowViewBoundsResponse::Read(response, &result); + + *bounds = result.b; + return result.a; +} + +bool WindowProxy::GetFocusedViewID(int* view_id) { + if (!is_valid()) return false; + + if (!view_id) { + NOTREACHED(); + return false; + } + + IPC::Message* response = NULL; + bool succeeded = sender_->SendAndWaitForResponse( + new AutomationMsg_GetFocusedViewIDRequest(0, handle_), + &response, + AutomationMsg_GetFocusedViewIDResponse::ID); + + *view_id = -1; + if (succeeded && + AutomationMsg_GetFocusedViewIDResponse::Read(response, view_id)) + return true; + + return false; +}
\ No newline at end of file diff --git a/chrome/test/automation/window_proxy.h b/chrome/test/automation/window_proxy.h new file mode 100644 index 0000000..9fe7740 --- /dev/null +++ b/chrome/test/automation/window_proxy.h @@ -0,0 +1,106 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_TEST_AUTOMATION_WINDOW_PROXY_H__ +#define CHROME_TEST_AUTOMATION_WINDOW_PROXY_H__ + +#include <string> +#include "base/thread.h" +#include "chrome/test/automation/automation_handle_tracker.h" + +class GURL; +class BrowserProxy; +class TabProxy; +class WindowProxy; + +namespace gfx { + class Rect; +} + +// This class presents the interface to actions that can be performed on a given +// window. Note that this object can be invalidated at any time if the +// corresponding window in the app is closed. In that case, any subsequent +// calls will return false immediately. +class WindowProxy : public AutomationResourceProxy { + public: + WindowProxy(AutomationMessageSender* sender, + AutomationHandleTracker* tracker, + int handle) + : AutomationResourceProxy(tracker, sender, handle) {} + virtual ~WindowProxy() {} + + // Gets the outermost HWND that corresponds to the given window. + // Returns true if the call was successful. + bool GetHWND(HWND* handle) const; + + // Simulates a click at the OS level. |click| is in the window's coordinates + // and |flags| specifies which buttons are pressed (as defined in + // chrome/views/event.h). Note that this is equivalent to the user moving + // the mouse and pressing the button. So if there is a window on top of this + // window, the top window is clicked. + bool SimulateOSClick(const POINT& click, int flags); + + // Simulates a key press at the OS level. |key| is the key pressed and + // |flags| specifies which modifiers keys are also pressed (as defined in + // chrome/views/event.h). Note that this actually sends the event to the + // window that has focus. + bool SimulateOSKeyPress(wchar_t key, int flags); + + // Shows/hides the window and as a result makes it active/inactive. + // Returns true if the call was successful. + bool SetVisible(bool visible); + + // Sets |active| to true if this view is currently the active window. + // Returns true if the call was successful. + bool IsActive(bool* active); + + // Make this window the active window. + // Returns true if the call was successful. + bool Activate(); + + // Gets the bounds (in window coordinates) that correspond to the view with + // the given ID in this window. Returns true if bounds could be obtained. + // If |screen_coordinates| is true, the bounds are returned in the coordinates + // of the screen, if false in the coordinates of the browser. + bool GetViewBounds(int view_id, gfx::Rect* bounds, bool screen_coordinates); + + // Like GetViewBounds except returns false if timeout occurs before view + // bounds are obtained, and sets is_timeout accordingly. + bool GetViewBoundsWithTimeout(int view_id, gfx::Rect* bounds, + bool screen_coordinates, uint32 timeout_ms, + bool* is_timeout); + // Gets the id of the view that currently has focus. Returns true if the id + // was retrieved. + bool GetFocusedViewID(int* view_id); + + private: + DISALLOW_EVIL_CONSTRUCTORS(WindowProxy); +}; + +#endif // CHROME_TEST_AUTOMATION_WINDOW_PROXY_H__ |