diff options
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 82 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 10 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.cc | 24 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.h | 21 | ||||
-rw-r--r-- | chrome/test/functional/PYAUTO_TESTS | 1 | ||||
-rw-r--r-- | chrome/test/functional/content.py | 92 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 35 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyautolib.i | 4 |
8 files changed, 259 insertions, 10 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 1630f14a..6692f09 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -9,6 +9,7 @@ #include "app/l10n_util.h" #include "app/message_box_flags.h" #include "base/callback.h" +#include "base/file_path.h" #include "base/file_version_info.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" @@ -1608,6 +1609,19 @@ void AutomationProvider::SetWindowDimensions(Browser* browser, Send(reply_message); } +std::string AutomationProvider::JSONErrorString(std::string err) { + std::string prefix = "{\"error\": \""; + std::string no_quote_err = err; + std::string suffix = "\"}"; + + // Don't allow input string to break JSON by embedding quotes. + // Try and make sure the input string won't break json quoting rules. + if (no_quote_err.find("\"") != std::string::npos) + no_quote_err = "unhappy about embedded quote in error string"; + + return prefix + no_quote_err + suffix; +} + // Sample json input: { "command": "GetBrowserInfo" } // Refer to GetBrowserInfo() in chrome/test/pyautolib/pyauto.py for // sample json output. @@ -1796,7 +1810,7 @@ void AutomationProvider::AddHistoryItem(Browser* browser, if (title.length()) hs->SetPageTitle(gurl, title); } else { - json_return = "{\"error\": \"bad args (no URL in dict?).\"}"; + json_return = JSONErrorString("bad args (no URL in dict?)"); reply_return = false; } @@ -1817,7 +1831,7 @@ void AutomationProvider::GetDownloadsInfo(Browser* browser, scoped_ptr<DictionaryValue> return_value(new DictionaryValue); if (!profile_->HasCreatedDownloadManager()) { - json_return = "{\"error\": \"no download manager\"}"; + json_return = JSONErrorString("no download manager"); reply_return = false; } else { // Use DownloadManager's GetDownloads() method and not GetCurrentDownloads() @@ -1883,7 +1897,7 @@ void AutomationProvider::WaitForDownloadsToComplete( // Look for a quick return. if (!profile_->HasCreatedDownloadManager()) { - json_return = "{\"error\": \"no download manager\"}"; + json_return = JSONErrorString("no download manager"); reply_return = false; } else { profile_->GetDownloadManager()->GetCurrentDownloads(&observer, @@ -1948,16 +1962,16 @@ void AutomationProvider::SetPrefs(Browser* browser, const PrefService::Preference* pref = pref_service->FindPreference(path.c_str()); if (!pref) { // Not a registered pref. - json_return = "{\"error\": \"pref not registered.\"}"; + json_return = JSONErrorString("pref not registered."); reply_return = false; } else if (pref->IsManaged()) { // Do not attempt to change a managed pref. - json_return = "{\"error\": \"pref is managed. cannot be changed.\"}"; + json_return = JSONErrorString("pref is managed. cannot be changed."); reply_return = false; } else { // Set the pref. pref_service->Set(path.c_str(), *val); } } else { - json_return = "{\"error\": \"no pref path or value given.\"}"; + json_return = JSONErrorString("no pref path or value given."); reply_return = false; } @@ -2019,7 +2033,7 @@ void AutomationProvider::SetOmniboxText(Browser* browser, std::wstring text; if (!args->GetString(L"text", &text)) { - json_return = "{\"error\": \"text missing\"}"; + json_return = JSONErrorString("text missing"); reply_return = false; } else { browser->FocusLocationBar(); @@ -2047,7 +2061,7 @@ void AutomationProvider::OmniboxMovePopupSelection( int count; if (!args->GetInteger(L"count", &count)) { - json_return = "{\"error\": \"count missing\"}"; + json_return = JSONErrorString("count missing"); reply_return = false; } else { LocationBar* loc_bar = browser->window()->GetLocationBar(); @@ -2153,7 +2167,7 @@ void AutomationProvider::EnablePlugin(Browser* browser, bool reply_return = true; FilePath::StringType path; if (!args->GetString(L"path", &path)) { - json_return = "{\"error\": \"path not specified.\"}"; + json_return = JSONErrorString("path not specified."); reply_return = false; } else if (!NPAPI::PluginList::Singleton()->EnablePlugin(FilePath(path))) { json_return = StringPrintf("{\"error\": \"Could not enable plugin" @@ -2176,7 +2190,7 @@ void AutomationProvider::DisablePlugin(Browser* browser, bool reply_return = true; FilePath::StringType path; if (!args->GetString(L"path", &path)) { - json_return = "{\"error\": \"path not specified.\"}"; + json_return = JSONErrorString("path not specified."); reply_return = false; } else if (!NPAPI::PluginList::Singleton()->DisablePlugin(FilePath(path))) { json_return = StringPrintf("{\"error\": \"Could not enable plugin" @@ -2189,6 +2203,52 @@ void AutomationProvider::DisablePlugin(Browser* browser, Send(reply_message); } +// Sample json input: +// { "command": "SaveTabContents", +// "tab_index": 0, +// "filename": <a full pathname> } +// Sample json output: +// {} +void AutomationProvider::SaveTabContents(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + std::string json_return; + int tab_index = 0; + FilePath::StringType filename; + FilePath::StringType parent_directory; + TabContents* tab_contents = NULL; + + if (!args->GetInteger(L"tab_index", &tab_index) || + !args->GetString(L"filename", &filename)) { + json_return = JSONErrorString("tab_index or filename param missing"); + } else { + tab_contents = browser->GetTabContentsAt(tab_index); + if (!tab_contents) { + json_return = JSONErrorString("no tab at tab_index"); + } + } + if (tab_contents) { + // We're doing a SAVE_AS_ONLY_HTML so the the directory path isn't + // used. Nevertheless, SavePackage requires it be valid. Sigh. + parent_directory = FilePath(filename).DirName().value(); + if (!tab_contents->SavePage(FilePath(filename), FilePath(parent_directory), + SavePackage::SAVE_AS_ONLY_HTML)) { + json_return = JSONErrorString("Could not initiate SavePage"); + } else { + // The observer will delete itself when done. + new SavePackageNotificationObserver(tab_contents->save_package(), + this, reply_message); + return; + } + } + + // if we get here, error. + DCHECK(!json_return.empty()); + AutomationMsg_SendJSONRequest::WriteReplyParams( + reply_message, json_return, false); + Send(reply_message); +} + void AutomationProvider::SendJSONRequest(int handle, std::string json_request, IPC::Message* reply_message) { @@ -2254,6 +2314,8 @@ void AutomationProvider::SendJSONRequest(int handle, handler_map["GetInitialLoadTimes"] = &AutomationProvider::GetInitialLoadTimes; + handler_map["SaveTabContents"] = &AutomationProvider::SaveTabContents; + if (error_string.empty()) { if (handler_map.find(std::string(command)) != handler_map.end()) { (this->*handler_map[command])(browser, dict_value, reply_message); diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index b644dd4..da32336 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -327,6 +327,10 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, int64 id, bool* success); + // Util for creating a JSON error return string (dict with key + // 'error' and error string value). No need to quote input. + std::string JSONErrorString(std::string err); + // Set window dimensions. // Uses the JSON interface for input/output. void SetWindowDimensions(Browser* browser, @@ -432,6 +436,12 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, DictionaryValue* args, IPC::Message* reply_message); + // Save the contents of a tab into a file. + // Uses the JSON interface for input/output. + void SaveTabContents(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + // Generic pattern for pyautolib // Uses the JSON interface for input/output. void SendJSONRequest(int handle, diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index a59e8d1..0b7f0af 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -11,6 +11,7 @@ #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/dom_operation_notification_details.h" +#include "chrome/browser/download/save_package.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_updater.h" @@ -1043,3 +1044,26 @@ void OmniboxAcceptNotificationObserver::Observe( } } +SavePackageNotificationObserver::SavePackageNotificationObserver( + SavePackage* save_package, + AutomationProvider* automation, + IPC::Message* reply_message) : automation_(automation), + reply_message_(reply_message) { + Source<SavePackage> source(save_package); + registrar_.Add(this, NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED, + source); +} + +void SavePackageNotificationObserver::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::SAVE_PACKAGE_SUCCESSFULLY_FINISHED) { + AutomationMsg_SendJSONRequest::WriteReplyParams( + reply_message_, std::string("{}"), true); + automation_->Send(reply_message_); + delete this; + } else { + NOTREACHED(); + } +} diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h index 6bf6a26..fbe8c15 100644 --- a/chrome/browser/automation/automation_provider_observers.h +++ b/chrome/browser/automation/automation_provider_observers.h @@ -21,6 +21,7 @@ class Browser; class Extension; class ExtensionProcessManager; class NavigationController; +class SavePackage; class TabContents; namespace IPC { @@ -632,4 +633,24 @@ class OmniboxAcceptNotificationObserver : public NotificationObserver { DISALLOW_COPY_AND_ASSIGN(OmniboxAcceptNotificationObserver); }; +// Allows the automation provider to wait for a save package notification. +class SavePackageNotificationObserver : public NotificationObserver { + public: + SavePackageNotificationObserver(SavePackage* save_package, + AutomationProvider* automation, + IPC::Message* reply_message); + virtual ~SavePackageNotificationObserver() {} + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + NotificationRegistrar registrar_; + AutomationProvider* automation_; + IPC::Message* reply_message_; + + DISALLOW_COPY_AND_ASSIGN(SavePackageNotificationObserver); +}; + #endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_ diff --git a/chrome/test/functional/PYAUTO_TESTS b/chrome/test/functional/PYAUTO_TESTS index 656f2b99..d512cd4 100644 --- a/chrome/test/functional/PYAUTO_TESTS +++ b/chrome/test/functional/PYAUTO_TESTS @@ -24,6 +24,7 @@ 'bookmark_bar', 'bookmarks', 'browser', + 'content', 'downloads', 'history', 'navigation', diff --git a/chrome/test/functional/content.py b/chrome/test/functional/content.py new file mode 100644 index 0000000..8765cb8 --- /dev/null +++ b/chrome/test/functional/content.py @@ -0,0 +1,92 @@ +#!/usr/bin/python +# Copyright (c) 2010 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import os + +import pyauto_functional # Must be imported before pyauto +import pyauto + + +class ContentTest(pyauto.PyUITest): + """TestCase for getting html content from the browser.""" + + def _DataDirURL(self, filename): + """Return a URL in the data dir for the given filename.""" + return self.GetFileURLForPath(os.path.join(self.DataDir(), filename)) + + def _StringContentCheck(self, content_string, have_list, nothave_list): + """Look for the presence or absence of strings in content. + + Confirm all strings in have_list are found in content_string. + Confirm all strings in nothave_list are NOT found in content_string. + """ + for s in have_list: + self.assertTrue(s in content_string) + for s in nothave_list: + self.assertTrue(s not in content_string) + + def _FileContentCheck(self, filename, have_list, nothave_list): + """String check in local file. + + For each local filename, tell the browser to load it as a file + UEL from the DataDir. Ask the browser for the loaded html. + Confirm all strings in have_list are found in it. Confirm all + strings in nothave_list are NOT found in it. Assumes only one + window/tab is open. + """ + self.NavigateToURL(self._DataDirURL(filename)) + self._StringContentCheck(self.GetTabContents(), have_list, nothave_list) + + def testLocalFileBasics(self): + """For a few local files, do some basic has / not has.""" + self._FileContentCheck('title1.html', + ['<html>', '</html>', 'page has no title'], + ['Title Of Awesomeness', '<b>']) + self._FileContentCheck('title2.html', + ['<html>', '</html>', 'Title Of Awesomeness'], + ['plastic flower', '<b>']) + self._FileContentCheck('title3.html', + ['<html>', '</html>', 'Title Of More Awesomeness'], + ['dinfidnfid', 'Title Of Awesomeness', '<b>']) + + def testTwoTabs(self): + """Test content when we have 2 tabs.""" + self.NavigateToURL(self._DataDirURL('title1.html')) + self.AppendTab(pyauto.GURL(self._DataDirURL('title2.html')), 0) + self._StringContentCheck(self.GetTabContents(0, 0), + ['page has no title'], + ['Awesomeness']) + self._StringContentCheck(self.GetTabContents(1, 0), + ['Awesomeness'], + ['page has no title']) + + def testThreeWindows(self): + """Test content when we have 3 windows.""" + self.NavigateToURL(self._DataDirURL('title1.html')) + for (window_index, url) in ((1, 'title2.html'), (2, 'title3.html')): + self.OpenNewBrowserWindow(True) + self.GetBrowserWindow(window_index).BringToFront() + self.NavigateToURL(self._DataDirURL(url), window_index, 0) + + self._StringContentCheck(self.GetTabContents(0, 0), + ['page has no title'], + ['Awesomeness']) + self._StringContentCheck(self.GetTabContents(0, 1), + ['Awesomeness'], + ['page has no title']) + self._StringContentCheck(self.GetTabContents(0, 2), + ['Title Of More Awesomeness'], + ['page has no title']) + + def testAboutVersion(self): + """Confirm about:version contains some expected content.""" + self.NavigateToURL('about:version') + self._StringContentCheck(self.GetTabContents(), + ['User Agent', 'Command Line'], + ['odmomfodfm disfnodugdzuoufgbn ifdnf fif']) + + +if __name__ == '__main__': + pyauto_functional.Main() diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 3d9c008..8f40bdc 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -29,7 +29,9 @@ import logging import optparse import os import re +import shutil import sys +import tempfile import time import types import unittest @@ -617,6 +619,39 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): if ret_dict.has_key('error'): raise JSONInterfaceError(ret_dict['error']) + def GetTabContents(self, tab_index=0, window_index=0): + """Get the html contents of a tab (a la "view source"). + + As an implementation detail, this saves the html in a file, reads + the file into a buffer, then deletes it. + + Args: + tab_index: tab index, defaults to 0. + window_index: window index, defaults to 0. + Returns: + html content of a page as a string. + """ + tempdir = tempfile.mkdtemp() + filename = os.path.join(tempdir, 'content.html') + cmd_dict = { # Prepare command for the json interface + 'command': 'SaveTabContents', + 'tab_index': tab_index, + 'filename': filename + } + ret_dict = json.loads(self._SendJSONRequest(window_index, + json.dumps(cmd_dict))) + if ret_dict.has_key('error'): + raise JSONInterfaceError(ret_dict['error']) + try: + f = open(filename) + all_data = f.read() + f.close() + return all_data + except IOError: + raise + finally: + shutil.rmtree(tempdir) + class PyUITestSuite(pyautolib.PyUITestSuiteBase, unittest.TestSuite): """Base TestSuite for PyAuto UI tests.""" diff --git a/chrome/test/pyautolib/pyautolib.i b/chrome/test/pyautolib/pyautolib.i index 5b3db11..1b011a2 100644 --- a/chrome/test/pyautolib/pyautolib.i +++ b/chrome/test/pyautolib/pyautolib.i @@ -93,6 +93,10 @@ class BrowserProxy { ActivateTab; bool ActivateTab(int tab_index); + %feature("docstring", "Activate the browser's window and bring it to front.") + BringToFront; + bool BringToFront(); + %feature("docstring", "Get proxy to the tab at the given zero-based index") GetTab; scoped_refptr<TabProxy> GetTab(int tab_index) const; |