summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/automation/automation_provider.cc82
-rw-r--r--chrome/browser/automation/automation_provider.h10
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc24
-rw-r--r--chrome/browser/automation/automation_provider_observers.h21
-rw-r--r--chrome/test/functional/PYAUTO_TESTS1
-rw-r--r--chrome/test/functional/content.py92
-rw-r--r--chrome/test/pyautolib/pyauto.py35
-rw-r--r--chrome/test/pyautolib/pyautolib.i4
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;