summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-14 00:38:12 +0000
committermaruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-14 00:38:12 +0000
commit73852b8f9c03c6b7a27436f828ed888f71232257 (patch)
treeda07fd53317f13964f93383f9591fcdfdfa61ea7
parent564551a2ece790b22fd2a70aeb8591805fe943be (diff)
downloadchromium_src-73852b8f9c03c6b7a27436f828ed888f71232257.zip
chromium_src-73852b8f9c03c6b7a27436f828ed888f71232257.tar.gz
chromium_src-73852b8f9c03c6b7a27436f828ed888f71232257.tar.bz2
[Large; Chromium OS] Work to host the cloud print dialog when built
for Chromium OS. Currently disabled by default behind a command line switch, and containing a non-real URL for now, this code is at prototype level. It works (when enabled and pointed at a functioning cloud print service URL), has the beginnings of some unit tests, and has the beginnings of deeper communication with the dialog contents, and it shuts off the DOM UI access from the dialog contents. Patch contributed by Scott Byer Review URL: http://codereview.chromium.org/1769006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47228 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/generated_resources.grd5
-rw-r--r--chrome/browser/browser.cc3
-rw-r--r--chrome/browser/chrome_plugin_host.cc1
-rw-r--r--chrome/browser/cocoa/html_dialog_window_controller.mm2
-rw-r--r--chrome/browser/cocoa/html_dialog_window_controller_unittest.mm2
-rw-r--r--chrome/browser/dom_ui/dom_ui_factory.cc7
-rw-r--r--chrome/browser/dom_ui/dom_ui_util.cc37
-rw-r--r--chrome/browser/dom_ui/dom_ui_util.h34
-rw-r--r--chrome/browser/dom_ui/html_dialog_ui.cc42
-rw-r--r--chrome/browser/dom_ui/html_dialog_ui.h17
-rw-r--r--chrome/browser/gtk/html_dialog_gtk.h1
-rw-r--r--chrome/browser/modal_html_dialog_delegate.h1
-rw-r--r--chrome/browser/printing/print_dialog_cloud.cc480
-rw-r--r--chrome/browser/printing/print_dialog_cloud.h38
-rw-r--r--chrome/browser/printing/print_dialog_cloud_internal.h172
-rw-r--r--chrome/browser/printing/print_dialog_cloud_uitest.cc176
-rw-r--r--chrome/browser/printing/print_dialog_cloud_unittest.cc189
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc2
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.h2
-rw-r--r--chrome/browser/renderer_host/resource_message_filter_gtk.cc7
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc25
-rw-r--r--chrome/browser/sync/sync_setup_flow.h3
-rw-r--r--chrome/browser/views/html_dialog_view.cc13
-rw-r--r--chrome/browser/views/html_dialog_view.h4
-rw-r--r--chrome/browser/views/select_file_dialog.cc2
-rw-r--r--chrome/chrome_browser.gypi8
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--chrome/common/url_constants.cc3
-rw-r--r--chrome/common/url_constants.h4
-rw-r--r--chrome/test/data/printing/cloud_print_emptytest.pdf0
-rw-r--r--chrome/test/data/printing/cloud_print_uitest.html78
32 files changed, 1310 insertions, 51 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b12c05e..a465f19 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4524,6 +4524,11 @@ Keep your key file in a safe place. You will need it to create new versions of y
Something went wrong when trying to print. Please check your printer and try again.
</message>
+ <!-- Cloud Print dialog messages -->
+ <message name="IDS_CLOUD_PRINT_TITLE" desc="Title for the print dialog">
+ Print
+ </message>
+
<!-- Load State -->
<message name="IDS_LOAD_STATE_IDLE"></message>
<message name="IDS_LOAD_STATE_WAITING_FOR_CACHE">
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index aee74e5..f375ce0 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -3046,7 +3046,8 @@ void Browser::InitCommandState() {
#if defined(OS_CHROMEOS)
command_updater_.UpdateCommandEnabled(IDC_COMPACT_NAVBAR, true);
- command_updater_.UpdateCommandEnabled(IDC_PRINT, false);
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableCloudPrint))
+ command_updater_.UpdateCommandEnabled(IDC_PRINT, false);
#endif
ExtensionsService* extensions_service = profile()->GetExtensionsService();
bool enable_extensions =
diff --git a/chrome/browser/chrome_plugin_host.cc b/chrome/browser/chrome_plugin_host.cc
index c9b0428..30e1fa4 100644
--- a/chrome/browser/chrome_plugin_host.cc
+++ b/chrome/browser/chrome_plugin_host.cc
@@ -326,6 +326,7 @@ class ModelessHtmlDialogDelegate : public HtmlDialogUIDelegate {
io_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &ModelessHtmlDialogDelegate::ReportResults, json_retval));
}
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
private:
// Actually shows the dialog on the UI thread.
diff --git a/chrome/browser/cocoa/html_dialog_window_controller.mm b/chrome/browser/cocoa/html_dialog_window_controller.mm
index 32b2096..75c6b6e 100644
--- a/chrome/browser/cocoa/html_dialog_window_controller.mm
+++ b/chrome/browser/cocoa/html_dialog_window_controller.mm
@@ -42,6 +42,7 @@ public:
virtual void GetDialogSize(gfx::Size* size) const;
virtual std::string GetDialogArgs() const;
virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
// HtmlDialogTabContentsDelegate declarations.
virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
@@ -288,4 +289,3 @@ void HtmlDialogWindowDelegateBridge::HandleKeyboardEvent(
}
@end
-
diff --git a/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm b/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm
index e5f1ea7..8b64443 100644
--- a/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm
+++ b/chrome/browser/cocoa/html_dialog_window_controller_unittest.mm
@@ -32,6 +32,8 @@ public:
MOCK_CONST_METHOD1(GetDialogSize, void(gfx::Size*));
MOCK_CONST_METHOD0(GetDialogArgs, std::string());
MOCK_METHOD1(OnDialogClosed, void(const std::string& json_retval));
+ MOCK_METHOD2(OnCloseContents,
+ void(TabContents* source, bool* out_close_dialog));
};
class HtmlDialogWindowControllerTest : public BrowserWithTestWindowTest {
diff --git a/chrome/browser/dom_ui/dom_ui_factory.cc b/chrome/browser/dom_ui/dom_ui_factory.cc
index bdbfa9a..b9af9ee 100644
--- a/chrome/browser/dom_ui/dom_ui_factory.cc
+++ b/chrome/browser/dom_ui/dom_ui_factory.cc
@@ -19,6 +19,7 @@
#include "chrome/browser/extensions/extension_dom_ui.h"
#include "chrome/browser/extensions/extensions_service.h"
#include "chrome/browser/extensions/extensions_ui.h"
+#include "chrome/browser/printing/print_dialog_cloud.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/url_constants.h"
@@ -69,6 +70,12 @@ static DOMUIFactoryFunction GetDOMUIFactoryFunction(const GURL& url) {
return &NewDOMUI<PrintUI>;
#endif
+ // All platform builds of Chrome will need to have a cloud printing
+ // dialog as backup. It's just that on Chrome OS, it's the only
+ // print dialog.
+ if (url.host() == chrome::kCloudPrintResourcesHost)
+ return &NewDOMUI<ExternalHtmlDialogUI>;
+
// This will get called a lot to check all URLs, so do a quick check of other
// schemes (gears was handled above) to filter out most URLs.
if (!url.SchemeIs(chrome::kChromeInternalScheme) &&
diff --git a/chrome/browser/dom_ui/dom_ui_util.cc b/chrome/browser/dom_ui/dom_ui_util.cc
new file mode 100644
index 0000000..e247655
--- /dev/null
+++ b/chrome/browser/dom_ui/dom_ui_util.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/dom_ui/dom_ui_util.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+
+namespace dom_ui_util {
+
+std::string GetJsonResponseFromFirstArgumentInList(const Value* content) {
+ return GetJsonResponseFromArgumentList(content, 0);
+}
+
+std::string GetJsonResponseFromArgumentList(const Value* content,
+ size_t list_index) {
+ std::string result;
+
+ if (!content || !content->IsType(Value::TYPE_LIST)) {
+ NOTREACHED();
+ return result;
+ }
+ const ListValue* args = static_cast<const ListValue*>(content);
+ if (args->GetSize() <= list_index) {
+ NOTREACHED();
+ return result;
+ }
+
+ Value* value = NULL;
+ if (args->Get(list_index, &value))
+ value->GetAsString(&result);
+
+ return result;
+}
+
+} // end of namespace dom_ui_util
diff --git a/chrome/browser/dom_ui/dom_ui_util.h b/chrome/browser/dom_ui/dom_ui_util.h
new file mode 100644
index 0000000..48c68a1
--- /dev/null
+++ b/chrome/browser/dom_ui/dom_ui_util.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef CHROME_BROWSER_DOM_UI_DOM_UI_UTIL_H_
+#define CHROME_BROWSER_DOM_UI_DOM_UI_UTIL_H_
+
+#include <string>
+
+class Value;
+
+namespace dom_ui_util {
+
+// Convenience routine to get the response string from an argument
+// list. Typically used when supporting a DOMUI and getting calls
+// from the hosted code. Content must be a ListValue with at least
+// one entry in it, and that first entry must be a string, which is
+// returned. The parameter is a Value for convenience. Returns an
+// empty string on error or if the parameter is not a ListValue.
+std::string GetJsonResponseFromFirstArgumentInList(const Value* content);
+
+// Convenience routine to get one of the response strings from an
+// argument list. content must be a ListValue, with at least
+// (list_index+1) entries in it. list_index is the 0-based index of
+// the entry to pull from that list, and that entry must be a string,
+// which is returned. The parameter is a Value for convenience.
+// Returns an empty string on error or if the parameter is not a
+// ListValue.
+std::string GetJsonResponseFromArgumentList(const Value* content,
+ size_t list_index);
+
+} // end of namespace
+
+#endif // CHROME_BROWSER_DOM_UI_DOM_UI_UTIL_H_
diff --git a/chrome/browser/dom_ui/html_dialog_ui.cc b/chrome/browser/dom_ui/html_dialog_ui.cc
index 739a4c4..f93e9ca 100644
--- a/chrome/browser/dom_ui/html_dialog_ui.cc
+++ b/chrome/browser/dom_ui/html_dialog_ui.cc
@@ -7,8 +7,10 @@
#include "base/callback.h"
#include "base/singleton.h"
#include "base/values.h"
+#include "chrome/browser/dom_ui/dom_ui_util.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/common/bindings_policy.h"
HtmlDialogUI::HtmlDialogUI(TabContents* tab_contents) : DOMUI(tab_contents) {
}
@@ -32,28 +34,6 @@ PropertyAccessor<HtmlDialogUIDelegate*>& HtmlDialogUI::GetPropertyAccessor() {
////////////////////////////////////////////////////////////////////////////////
// Private:
-// Helper function to read the JSON string from the Value parameter.
-static std::string GetJsonResponse(const Value* content) {
- if (!content || !content->IsType(Value::TYPE_LIST)) {
- NOTREACHED();
- return std::string();
- }
- const ListValue* args = static_cast<const ListValue*>(content);
- if (args->GetSize() != 1) {
- NOTREACHED();
- return std::string();
- }
-
- std::string result;
- Value* value = NULL;
- if (!args->Get(0, &value) || !value->GetAsString(&result)) {
- NOTREACHED();
- return std::string();
- }
-
- return result;
-}
-
void HtmlDialogUI::RenderViewCreated(RenderViewHost* render_view_host) {
// Hook up the javascript function calls, also known as chrome.send("foo")
// calls in the HTML, to the actual C++ functions.
@@ -70,7 +50,8 @@ void HtmlDialogUI::RenderViewCreated(RenderViewHost* render_view_host) {
(*delegate)->GetDOMMessageHandlers(&handlers);
}
- render_view_host->SetDOMUIProperty("dialogArguments", dialog_args);
+ if (0 != (bindings_ & BindingsPolicy::DOM_UI))
+ render_view_host->SetDOMUIProperty("dialogArguments", dialog_args);
for (std::vector<DOMMessageHandler*>::iterator it = handlers.begin();
it != handlers.end(); ++it) {
(*it)->Attach(this);
@@ -82,5 +63,18 @@ void HtmlDialogUI::OnDialogClosed(const Value* content) {
HtmlDialogUIDelegate** delegate = GetPropertyAccessor().GetProperty(
tab_contents()->property_bag());
if (delegate)
- (*delegate)->OnDialogClosed(GetJsonResponse(content));
+ (*delegate)->OnDialogClosed(
+ dom_ui_util::GetJsonResponseFromFirstArgumentInList(content));
+}
+
+ExternalHtmlDialogUI::ExternalHtmlDialogUI(TabContents* tab_contents)
+ : HtmlDialogUI(tab_contents) {
+ // Non-file based UI needs to not have access to the DOM UI bindings
+ // for security reasons. The code hosting the dialog should provide
+ // dialog specific functionality through other bindings and methods
+ // that are scoped in duration to the dialogs existence.
+ bindings_ &= ~BindingsPolicy::DOM_UI;
+}
+
+ExternalHtmlDialogUI::~ExternalHtmlDialogUI() {
}
diff --git a/chrome/browser/dom_ui/html_dialog_ui.h b/chrome/browser/dom_ui/html_dialog_ui.h
index ffc5196..be2df7f 100644
--- a/chrome/browser/dom_ui/html_dialog_ui.h
+++ b/chrome/browser/dom_ui/html_dialog_ui.h
@@ -44,6 +44,12 @@ class HtmlDialogUIDelegate {
// A callback to notify the delegate that the dialog closed.
virtual void OnDialogClosed(const std::string& json_retval) = 0;
+ // A callback to notify the delegate that the contents have gone
+ // away. Only relevant if your dialog hosts code that calls
+ // windows.close() and you've allowed that. If the output parameter
+ // is set to true, then the dialog is closed. The default is false.
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) = 0;
+
protected:
~HtmlDialogUIDelegate() {}
};
@@ -89,4 +95,15 @@ class HtmlDialogUI : public DOMUI {
DISALLOW_COPY_AND_ASSIGN(HtmlDialogUI);
};
+// Displays external URL contents inside a modal HTML dialog.
+//
+// Intended to be the place to collect the settings and lockdowns
+// necessary for running external UI conponents securely (e.g., the
+// cloud print dialog).
+class ExternalHtmlDialogUI : public HtmlDialogUI {
+ public:
+ explicit ExternalHtmlDialogUI(TabContents* tab_contents);
+ virtual ~ExternalHtmlDialogUI();
+};
+
#endif // CHROME_BROWSER_DOM_UI_HTML_DIALOG_UI_H_
diff --git a/chrome/browser/gtk/html_dialog_gtk.h b/chrome/browser/gtk/html_dialog_gtk.h
index d292ffb..250f1a0 100644
--- a/chrome/browser/gtk/html_dialog_gtk.h
+++ b/chrome/browser/gtk/html_dialog_gtk.h
@@ -43,6 +43,7 @@ class HtmlDialogGtk : public HtmlDialogTabContentsDelegate,
virtual void GetDialogSize(gfx::Size* size) const;
virtual std::string GetDialogArgs() const;
virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
// Overridden from TabContentsDelegate:
virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
diff --git a/chrome/browser/modal_html_dialog_delegate.h b/chrome/browser/modal_html_dialog_delegate.h
index 80f83ca..85415ea 100644
--- a/chrome/browser/modal_html_dialog_delegate.h
+++ b/chrome/browser/modal_html_dialog_delegate.h
@@ -38,6 +38,7 @@ class ModalHtmlDialogDelegate
virtual void GetDialogSize(gfx::Size* size) const;
virtual std::string GetDialogArgs() const;
virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
private:
NotificationRegistrar registrar_;
diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc
new file mode 100644
index 0000000..02385d2
--- /dev/null
+++ b/chrome/browser/printing/print_dialog_cloud.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#include "chrome/browser/printing/print_dialog_cloud_internal.h"
+
+#include "app/l10n_util.h"
+#include "base/base64.h"
+#include "base/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/dom_ui/dom_ui.h"
+#include "chrome/browser/dom_ui/dom_ui_util.h"
+#include "chrome/browser/dom_ui/html_dialog_ui.h"
+#include "chrome/browser/debugger/devtools_manager.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/url_constants.h"
+
+#include "grit/generated_resources.h"
+
+// This module implements the UI support in Chrome for cloud printing.
+// This means hosting a dialog containing HTML/JavaScript and using
+// the published cloud print user interface integration APIs to get
+// page setup settings from the dialog contents and provide the
+// generated print PDF to the dialog contents for uploading to the
+// cloud print service.
+
+// Currently, the flow between these classes is as follows:
+
+// PrintDialogCloud::CreatePrintDialogForPdf is called from
+// resource_message_filter_gtk.cc once the renderer has informed the
+// renderer host that PDF generation into the renderer host provided
+// temp file has been completed. That call is on the IO thread.
+// That, in turn, hops over to the UI thread to create an instance of
+// PrintDialogCloud.
+
+// The constructor for PrintDialogCloud creates a
+// CloudPrintHtmlDialogDelegate and asks the current active browser to
+// show an HTML dialog using that class as the delegate. That class
+// hands in the kCloudPrintResourcesURL as the URL to visit. That is
+// recognized by the GetDOMUIFactoryFunction as a signal to create an
+// ExternalHtmlDialogUI.
+
+// CloudPrintHtmlDialogDelegate also temporarily owns a
+// CloudPrintFlowHandler, a class which is responsible for the actual
+// interactions with the dialog contents, including handing in the PDF
+// print data and getting any page setup parameters that the dialog
+// contents provides. As part of bringing up the dialog,
+// HtmlDialogUI::RenderViewCreated is called (an override of
+// DOMUI::RenderViewCreated). That routine, in turn, calls the
+// delegate's GetDOMMessageHandlers routine, at which point the
+// ownership of the CloudPrintFlowHandler is handed over. A pointer
+// to the flow handler is kept to facilitate communication back and
+// forth between the two classes.
+
+// The DOMUI continues dialog bring-up, calling
+// CloudPrintFlowHandler::RegisterMessages. This is where the
+// additional object model capabilities are registered for the dialog
+// contents to use. It is also at this time that capabilities for the
+// dialog contents are adjusted to allow the dialog contents to close
+// the window. In addition, the pending URL is redirected to the
+// actual cloud print service URL. The flow controller also registers
+// for notification of when the dialog contents finish loading, which
+// is currently used to send the PDF data to the dialog contents.
+
+// In order to send the PDF data to the dialog contents, the flow
+// handler uses a CloudPrintDataSender. It creates one, letting it
+// know the name of the temporary file containing the PDF data, and
+// posts the task of reading the file
+// (CloudPrintDataSender::ReadPrintDataFile) to the file thread. That
+// routine reads in the file, and then hops over to the IO thread to
+// send that data to the dialog contents.
+
+// When the dialog contents are finished (by either being cancelled or
+// hitting the print button), the delegate is notified, and responds
+// that the dialog should be closed, at which point things are torn
+// down and released.
+
+// TODO(scottbyer):
+// http://code.google.com/p/chromium/issues/detail?id=44093 The
+// high-level flow (where the PDF data is generated before even
+// bringing up the dialog) isn't what we want.
+
+
+namespace internal_cloud_print_helpers {
+
+// TODO(scottbyer): Replace with the real public URL when we have one.
+// That, and get it into the profile instead of as a hardwired
+// constant.
+const char* const kCloudPrintDialogUrl =
+ "http://placeholderurl.ned/printing/client/dialog.html";
+
+bool GetRealOrInt(const DictionaryValue& dictionary,
+ const std::wstring& path,
+ double* out_value) {
+ if (!dictionary.GetReal(path, out_value)) {
+ int int_value = 0;
+ if (!dictionary.GetInteger(path, &int_value))
+ return false;
+ *out_value = int_value;
+ }
+ return true;
+}
+
+// From the JSON parsed value, get the entries for the page setup
+// parameters.
+bool GetPageSetupParameters(const std::string& json,
+ ViewMsg_Print_Params& parameters) {
+ scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
+ DLOG_IF(ERROR, (!parsed_value.get() ||
+ !parsed_value->IsType(Value::TYPE_DICTIONARY)))
+ << "PageSetup call didn't have expected contents";
+ if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ bool result = true;
+ DictionaryValue* params = static_cast<DictionaryValue*>(parsed_value.get());
+ result &= GetRealOrInt(*params, L"dpi", &parameters.dpi);
+ result &= GetRealOrInt(*params, L"min_shrink", &parameters.min_shrink);
+ result &= GetRealOrInt(*params, L"max_shrink", &parameters.max_shrink);
+ result &= params->GetBoolean(L"selection_only", &parameters.selection_only);
+ return result;
+}
+
+void CloudPrintDataSenderHelper::CallJavascriptFunction(
+ const std::wstring& function_name) {
+ dom_ui_->CallJavascriptFunction(function_name);
+}
+
+void CloudPrintDataSenderHelper::CallJavascriptFunction(
+ const std::wstring& function_name, const Value& arg) {
+ dom_ui_->CallJavascriptFunction(function_name, arg);
+}
+
+void CloudPrintDataSenderHelper::CallJavascriptFunction(
+ const std::wstring& function_name, const Value& arg1, const Value& arg2) {
+ dom_ui_->CallJavascriptFunction(function_name, arg1, arg2);
+}
+
+// Clears out the pointer we're using to communicate. Either routine is
+// potentially expensive enough that stopping whatever is in progress
+// is worth it.
+void CloudPrintDataSender::CancelPrintDataFile() {
+ AutoLock lock(lock_);
+ // We don't own helper, it was passed in to us, so no need to
+ // delete, just let it go.
+ helper_ = NULL;
+}
+
+// Grab the raw PDF file contents and massage them into shape for
+// sending to the dialog contents (and up to the cloud print server)
+// by encoding it and prefixing it with the appropriate mime type.
+// Once that is done, kick off the next part of the task on the IO
+// thread.
+void CloudPrintDataSender::ReadPrintDataFile(const FilePath& path_to_pdf) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ int64 file_size = 0;
+ if (file_util::GetFileSize(path_to_pdf, &file_size) && file_size != 0) {
+ std::string file_data;
+ if (file_size < kuint32max) {
+ file_data.reserve(static_cast<unsigned int>(file_size));
+ } else {
+ DLOG(WARNING) << " print data file too large to reserve space";
+ }
+ if (helper_ && file_util::ReadFileToString(path_to_pdf, &file_data)) {
+ std::string base64_data;
+ base::Base64Encode(file_data, &base64_data);
+ std::string header("data:application/pdf;base64,");
+ base64_data.insert(0, header);
+ scoped_ptr<StringValue> new_data(new StringValue(base64_data));
+ print_data_.swap(new_data);
+ ChromeThread::PostTask(ChromeThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &CloudPrintDataSender::SendPrintDataFile));
+ }
+ }
+}
+
+// We have the data in hand that needs to be pushed into the dialog
+// contents; do so from the IO thread.
+
+// TODO(scottbyer): If the print data ends up being larger than the
+// upload limit (currently 10MB), what we need to do is upload that
+// large data to google docs and set the URL in the printing
+// JavaScript to that location, and make sure it gets deleted when not
+// needed. - 4/1/2010
+void CloudPrintDataSender::SendPrintDataFile() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+ AutoLock lock(lock_);
+ if (helper_ && print_data_.get()) {
+ // TODO(scottbyer) - fill this in with the title or URL of the
+ // original page.
+ StringValue title("Chrome Print Test");
+
+ // Send the print data to the dialog contents. The JavaScript
+ // function is a preliminary API for prototyping purposes and is
+ // subject to change.
+ const_cast<CloudPrintDataSenderHelper*>(helper_)->CallJavascriptFunction(
+ L"printApp._printDataUrl", *print_data_, title);
+ }
+}
+
+
+void CloudPrintFlowHandler::SetDialogDelegate(
+ CloudPrintHtmlDialogDelegate* delegate) {
+ // Even if setting a new dom_ui, it means any previous task needs
+ // to be cancelled, it's now invalid.
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ CancelAnyRunningTask();
+ dialog_delegate_ = delegate;
+}
+
+// Cancels any print data sender we have in flight and removes our
+// reference to it, so when the task that is calling it finishes and
+// removes it's reference, it goes away.
+void CloudPrintFlowHandler::CancelAnyRunningTask() {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ if (print_data_sender_.get()) {
+ print_data_sender_->CancelPrintDataFile();
+ print_data_sender_ = NULL;
+ }
+}
+
+
+void CloudPrintFlowHandler::RegisterMessages() {
+ if (!dom_ui_)
+ return;
+
+ // TODO(scottbyer) - This is where we will register messages for the
+ // UI JS to use. Needed: Call to update page setup parameters.
+ dom_ui_->RegisterMessageCallback(
+ "ShowDebugger",
+ NewCallback(this, &CloudPrintFlowHandler::HandleShowDebugger));
+ dom_ui_->RegisterMessageCallback(
+ "SendPrintData",
+ NewCallback(this, &CloudPrintFlowHandler::HandleSendPrintData));
+ dom_ui_->RegisterMessageCallback(
+ "SetPageParameters",
+ NewCallback(this, &CloudPrintFlowHandler::HandleSetPageParameters));
+
+ if (dom_ui_->tab_contents()) {
+ // Also, take the opportunity to set some (minimal) additional
+ // script permissions required for the web UI.
+
+ // TODO(scottbyer): learn how to make sure we're talking to the
+ // right web site first.
+ RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
+ if (rvh && rvh->delegate()) {
+ WebPreferences webkit_prefs = rvh->delegate()->GetWebkitPrefs();
+ webkit_prefs.allow_scripts_to_close_windows = true;
+ rvh->UpdateWebPreferences(webkit_prefs);
+ }
+
+ // Register for appropriate notifications, and re-direct the URL
+ // to the real server URL, now that we've gotten an HTML dialog
+ // going.
+ NavigationController* controller = &dom_ui_->tab_contents()->controller();
+ NavigationEntry* pending_entry = controller->pending_entry();
+ if (pending_entry)
+ pending_entry->set_url(GURL(kCloudPrintDialogUrl));
+ registrar_.Add(this, NotificationType::LOAD_STOP,
+ Source<NavigationController>(controller));
+ }
+}
+
+void CloudPrintFlowHandler::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::LOAD_STOP) {
+ // Choose one or the other. If you need to debug, bring up the
+ // debugger. You can then use the various chrome.send()
+ // registrations above to kick of the various function calls,
+ // including chrome.send("SendPrintData") in the javaScript
+ // console and watch things happen with:
+ // HandleShowDebugger(NULL);
+ HandleSendPrintData(NULL);
+ }
+}
+
+void CloudPrintFlowHandler::HandleShowDebugger(const Value* value) {
+ ShowDebugger();
+}
+
+void CloudPrintFlowHandler::ShowDebugger() {
+ if (dom_ui_) {
+ RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
+ if (rvh)
+ DevToolsManager::GetInstance()->OpenDevToolsWindow(rvh);
+ }
+}
+
+scoped_refptr<CloudPrintDataSender>
+CloudPrintFlowHandler::CreateCloudPrintDataSender() {
+ DCHECK(dom_ui_);
+ print_data_helper_.reset(new CloudPrintDataSenderHelper(dom_ui_));
+ return new CloudPrintDataSender(print_data_helper_.get());
+}
+
+void CloudPrintFlowHandler::HandleSendPrintData(const Value* value) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ // This will cancel any ReadPrintDataFile() or SendPrintDataFile()
+ // requests in flight (this is anticipation of when setting page
+ // setup parameters becomes asynchronous and may be set while some
+ // data is in flight). Then we can clear out the print data.
+ CancelAnyRunningTask();
+ if (dom_ui_) {
+ print_data_sender_ = CreateCloudPrintDataSender();
+ ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ print_data_sender_.get(),
+ &CloudPrintDataSender::ReadPrintDataFile,
+ path_to_pdf_));
+ }
+}
+
+void CloudPrintFlowHandler::HandleSetPageParameters(const Value* value) {
+ std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(value));
+ if (json.empty())
+ return;
+
+ // These are backstop default values - 72 dpi to match the screen,
+ // 8.5x11 inch paper with margins subtracted (1/4 inch top, left,
+ // right and 0.56 bottom), and the min page shrink and max page
+ // shrink values appear all over the place with no explanation.
+
+ // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings
+ // working so that we can get the default values from there. Fix up
+ // PrintWebViewHelper to do the same.
+ const int kDPI = 72;
+ const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI);
+ const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI);
+ const double kMinPageShrink = 1.25;
+ const double kMaxPageShrink = 2.0;
+
+ ViewMsg_Print_Params default_settings;
+ default_settings.printable_size = gfx::Size(kWidth, kHeight);
+ default_settings.dpi = kDPI;
+ default_settings.min_shrink = kMinPageShrink;
+ default_settings.max_shrink = kMaxPageShrink;
+ default_settings.desired_dpi = kDPI;
+ default_settings.document_cookie = 0;
+ default_settings.selection_only = false;
+
+ if (!GetPageSetupParameters(json, default_settings)) {
+ NOTREACHED();
+ return;
+ }
+
+ // TODO(scottbyer) - Here is where we would kick the originating
+ // renderer thread with these new parameters in order to get it to
+ // re-generate the PDF and hand it back to us. window.print() is
+ // currently synchronous, so there's a lot of work to do to get to
+ // that point.
+}
+
+CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
+ const FilePath& path_to_pdf,
+ int width, int height,
+ const std::string& json_arguments)
+ : flow_handler_(new CloudPrintFlowHandler(path_to_pdf)),
+ owns_flow_handler_(true) {
+ Init(width, height, json_arguments);
+}
+
+CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
+ CloudPrintFlowHandler* flow_handler,
+ int width, int height,
+ const std::string& json_arguments)
+ : flow_handler_(flow_handler),
+ owns_flow_handler_(false) {
+ Init(width, height, json_arguments);
+}
+
+void CloudPrintHtmlDialogDelegate::Init(
+ int width, int height, const std::string& json_arguments) {
+ // This information is needed to show the dialog HTML content.
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ std::string cloud_print_url(chrome::kCloudPrintResourcesURL);
+ params_.url = GURL(cloud_print_url);
+ params_.height = height;
+ params_.width = width;
+ params_.json_input = json_arguments;
+
+ flow_handler_->SetDialogDelegate(this);
+}
+
+CloudPrintHtmlDialogDelegate::~CloudPrintHtmlDialogDelegate() {
+ // If the flow_handler_ is about to outlive us because we don't own
+ // it anymore, we need to have it remove it's reference to us.
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ flow_handler_->SetDialogDelegate(NULL);
+ if (owns_flow_handler_) {
+ delete flow_handler_;
+ }
+}
+
+bool CloudPrintHtmlDialogDelegate::IsDialogModal() const {
+ return true;
+}
+
+std::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const {
+ return l10n_util::GetString(IDS_CLOUD_PRINT_TITLE);
+}
+
+GURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const {
+ return params_.url;
+}
+
+void CloudPrintHtmlDialogDelegate::GetDOMMessageHandlers(
+ std::vector<DOMMessageHandler*>* handlers) const {
+ handlers->push_back(flow_handler_);
+ // We don't own flow_handler_ anymore, but it sticks around until at
+ // least right after OnDialogClosed() is called (and this object is
+ // destroyed).
+ owns_flow_handler_ = false;
+}
+
+void CloudPrintHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const {
+ size->set_width(params_.width);
+ size->set_height(params_.height);
+}
+
+std::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const {
+ return params_.json_input;
+}
+
+void CloudPrintHtmlDialogDelegate::OnDialogClosed(
+ const std::string& json_retval) {
+ delete this;
+}
+
+} // end of namespace internal_cloud_print_helpers
+
+// static, called on the IO thread. This is the main entry point into
+// creating the dialog.
+
+// TODO(scottbyer): The signature here will need to change as the
+// workflow through the printing code changes to allow for dynamically
+// changing page setup parameters while the dialog is active.
+void PrintDialogCloud::CreatePrintDialogForPdf(const FilePath& path_to_pdf) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
+
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableFunction(&PrintDialogCloud::CreateDialogImpl, path_to_pdf));
+}
+
+// static, called from the UI thread.
+void PrintDialogCloud::CreateDialogImpl(const FilePath& path_to_pdf) {
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+ new PrintDialogCloud(path_to_pdf);
+}
+
+// Initialize the print dialog. Called on the UI thread.
+PrintDialogCloud::PrintDialogCloud(const FilePath& path_to_pdf)
+ : browser_(BrowserList::GetLastActive()) {
+
+ // TODO(scottbyer): Verify GAIA login valid, execute GAIA login if not (should
+ // be distilled out of bookmark sync.)
+
+ // TODO(scottbyer): Get the dialog width, height from the dialog
+ // contents, and take the screen size into account.
+ HtmlDialogUIDelegate* dialog_delegate =
+ new internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate(
+ path_to_pdf, 500, 400, std::string());
+ browser_->BrowserShowHtmlDialog(dialog_delegate, NULL);
+}
+
+PrintDialogCloud::~PrintDialogCloud() {
+}
diff --git a/chrome/browser/printing/print_dialog_cloud.h b/chrome/browser/printing/print_dialog_cloud.h
new file mode 100644
index 0000000..7e8515b
--- /dev/null
+++ b/chrome/browser/printing/print_dialog_cloud.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_H_
+#define CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_H_
+
+#include "base/basictypes.h"
+
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class Browser;
+class FilePath;
+namespace IPC {
+class Message;
+}
+
+class PrintDialogCloud {
+ public:
+ // Called on the IO thread.
+ static void CreatePrintDialogForPdf(const FilePath& path_to_pdf);
+
+ private:
+ FRIEND_TEST(PrintDialogCloudTest, HandlersRegistered);
+
+ explicit PrintDialogCloud(const FilePath& path_to_pdf);
+ ~PrintDialogCloud();
+
+ // Called as a task from the UI thread, creates an object instance
+ // to run the HTML/JS based print dialog for printing through the cloud.
+ static void CreateDialogImpl(const FilePath& path_to_pdf);
+
+ Browser* browser_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintDialogCloud);
+};
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_H_
diff --git a/chrome/browser/printing/print_dialog_cloud_internal.h b/chrome/browser/printing/print_dialog_cloud_internal.h
new file mode 100644
index 0000000..1bde98e
--- /dev/null
+++ b/chrome/browser/printing/print_dialog_cloud_internal.h
@@ -0,0 +1,172 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_INTERNAL_H_
+#define CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_INTERNAL_H_
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/dom_ui/dom_ui.h"
+#include "chrome/browser/dom_ui/html_dialog_ui.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class StringValue;
+
+namespace internal_cloud_print_helpers {
+
+// TODO(scottbyer): this should really be fetched from a profile
+// preference. 4/8/10
+extern const char* const kCloudPrintDialogUrl;
+
+// Small class to virtualize a few functions to aid with unit testing.
+class CloudPrintDataSenderHelper {
+ public:
+ explicit CloudPrintDataSenderHelper(DOMUI* dom_ui) : dom_ui_(dom_ui) {}
+ virtual ~CloudPrintDataSenderHelper() {}
+
+ // Virtualize the overrides of these three functions from DOMUI to
+ // facilitate unit testing.
+ virtual void CallJavascriptFunction(const std::wstring& function_name);
+ virtual void CallJavascriptFunction(const std::wstring& function_name,
+ const Value& arg);
+ virtual void CallJavascriptFunction(const std::wstring& function_name,
+ const Value& arg1,
+ const Value& arg2);
+
+ private:
+ DOMUI* dom_ui_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintDataSenderHelper);
+};
+
+// Small helper class to get the print data loaded in from the PDF
+// file (on the FILE thread) and send it to the print dialog contents
+// (on the IO thread), allowing for cancellation.
+class CloudPrintDataSender
+ : public base::RefCountedThreadSafe<CloudPrintDataSender> {
+ public:
+ // The owner of this object is also expected to own and control the
+ // lifetime of the helper.
+ explicit CloudPrintDataSender(CloudPrintDataSenderHelper* helper)
+ : helper_(helper) {}
+
+ // Calls to read in the PDF file (on the FILE thread) then send that
+ // information to the dialog renderer (on the IO thread). We know
+ // that the dom_ui pointer lifetime will outlast us, so we should be
+ // good.
+ void ReadPrintDataFile(const FilePath& path_to_pdf);
+ void SendPrintDataFile();
+
+ // Cancels any ramining part of the task by clearing out the dom_ui
+ // helper_ ptr.
+ void CancelPrintDataFile();
+
+ private:
+ friend class base::RefCountedThreadSafe<CloudPrintDataSender>;
+ ~CloudPrintDataSender() {}
+
+ Lock lock_;
+ CloudPrintDataSenderHelper* volatile helper_;
+ scoped_ptr<StringValue> print_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintDataSender);
+};
+
+class CloudPrintHtmlDialogDelegate;
+
+// The CloudPrintFlowHandler connects the state machine (the UI delegate)
+// to the dialog backing HTML and JS by providing DOMMessageHandler
+// functions for the JS to use. This include refreshing the page
+// setup parameters (which will cause a re-generation of the PDF in
+// the renderer process - do we want a progress throbber shown?
+// Probably..), and packing up the PDF and job parameters and sending
+// them to the cloud.
+class CloudPrintFlowHandler : public DOMMessageHandler,
+ public NotificationObserver {
+ public:
+ explicit CloudPrintFlowHandler(const FilePath& path_to_pdf)
+ : path_to_pdf_(path_to_pdf) {}
+ virtual ~CloudPrintFlowHandler() {
+ // This will also cancel any task in flight.
+ CancelAnyRunningTask();
+ }
+
+ // DOMMessageHandler implementation.
+ virtual void RegisterMessages();
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Callbacks from the page.
+ void HandleShowDebugger(const Value* value);
+ void HandleSendPrintData(const Value* value);
+ void HandleSetPageParameters(const Value* value);
+
+ // Call to get the debugger loaded on our hosted dialog page
+ // specifically. Since we're not in an official browser tab, only
+ // way to get the debugger going.
+ void ShowDebugger();
+
+ void SetDialogDelegate(CloudPrintHtmlDialogDelegate *delegate);
+ void CancelAnyRunningTask();
+
+ private:
+ // For unit testing.
+ virtual scoped_refptr<CloudPrintDataSender> CreateCloudPrintDataSender();
+
+ CloudPrintHtmlDialogDelegate* dialog_delegate_;
+ NotificationRegistrar registrar_;
+ FilePath path_to_pdf_;
+ scoped_refptr<CloudPrintDataSender> print_data_sender_;
+ scoped_ptr<CloudPrintDataSenderHelper> print_data_helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintFlowHandler);
+};
+
+// State machine used to run the printing dialog. This class is used
+// to open and run the html dialog and deletes itself when the dialog
+// is closed.
+class CloudPrintHtmlDialogDelegate : public HtmlDialogUIDelegate {
+ public:
+ CloudPrintHtmlDialogDelegate(const FilePath& path_to_pdf,
+ int width, int height,
+ const std::string& json_arguments);
+ ~CloudPrintHtmlDialogDelegate();
+
+ // HTMLDialogUIDelegate implementation:
+ virtual bool IsDialogModal() const;
+ virtual std::wstring GetDialogTitle() const;
+ virtual GURL GetDialogContentURL() const;
+ virtual void GetDOMMessageHandlers(
+ std::vector<DOMMessageHandler*>* handlers) const;
+ virtual void GetDialogSize(gfx::Size* size) const;
+ virtual std::string GetDialogArgs() const;
+ virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
+
+ private:
+ CloudPrintHtmlDialogDelegate(CloudPrintFlowHandler* flow_handler,
+ int width, int height,
+ const std::string& json_arguments);
+ void Init(int width, int height, const std::string& json_arguments);
+
+ CloudPrintFlowHandler* flow_handler_;
+ mutable bool owns_flow_handler_;
+
+ // The parameters needed to display a modal HTML dialog.
+ HtmlDialogUI::HtmlDialogParams params_;
+
+ DISALLOW_COPY_AND_ASSIGN(CloudPrintHtmlDialogDelegate);
+};
+
+} // namespace internal_cloud_print_helpers
+
+#endif // CHROME_BROWSER_PRINTING_PRINT_DIALOG_CLOUD_INTERNAL_H_
diff --git a/chrome/browser/printing/print_dialog_cloud_uitest.cc b/chrome/browser/printing/print_dialog_cloud_uitest.cc
new file mode 100644
index 0000000..753f5ae
--- /dev/null
+++ b/chrome/browser/printing/print_dialog_cloud_uitest.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#include "chrome/browser/printing/print_dialog_cloud_internal.h"
+
+#include <functional>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/singleton.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "net/url_request/url_request_filter.h"
+#include "net/url_request/url_request_test_job.h"
+
+namespace {
+
+class TestData {
+ public:
+ TestData() {}
+
+ char* GetTestData() {
+ if (test_data_.empty()) {
+ FilePath test_data_directory;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
+ FilePath test_file =
+ test_data_directory.AppendASCII("printing/cloud_print_uitest.html");
+ file_util::ReadFileToString(test_file, &test_data_);
+ }
+ return &test_data_[0];
+ }
+ private:
+ std::string test_data_;
+};
+
+// A simple test URLRequestJob. We don't care what it does, only that
+// whether it starts and finishes.
+class SimpleTestJob : public URLRequestTestJob {
+ public:
+ explicit SimpleTestJob(URLRequest* request)
+ : URLRequestTestJob(request, test_headers(),
+ Singleton<TestData>()->GetTestData(), true) {}
+ private:
+ ~SimpleTestJob() {}
+};
+
+class TestResult {
+ public:
+ TestResult() : result_(false) {}
+ void SetResult(bool value) {
+ result_ = value;
+ }
+ bool GetResult() {
+ return result_;
+ }
+ private:
+ bool result_;
+};
+
+} // namespace
+
+class PrintDialogCloudTest : public InProcessBrowserTest,
+ public NotificationObserver {
+ public:
+ PrintDialogCloudTest() : handler_added_(false) {
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory_);
+ GURL cloud_print_url(internal_cloud_print_helpers::kCloudPrintDialogUrl);
+ host_name_ = cloud_print_url.host();
+ }
+
+ // Must be static for handing into AddHostnameHandler.
+ static URLRequest::ProtocolFactory Factory;
+
+ virtual void SetUp() {
+ Singleton<TestResult>()->SetResult(false);
+ InProcessBrowserTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ if (handler_added_) {
+ URLRequestFilter* filter = URLRequestFilter::GetInstance();
+ filter->RemoveHostnameHandler("http", host_name_);
+ handler_added_ = false;
+ }
+ InProcessBrowserTest::TearDown();
+ }
+
+ // Normally this is something I would expect could go into SetUp(),
+ // but there seems to be some timing or ordering related issue with
+ // the test harness that made that flaky. Calling this from the
+ // individual test functions seems to fix that.
+ void AddTestHandlers() {
+ if (!handler_added_) {
+ URLRequestFilter* filter = URLRequestFilter::GetInstance();
+ filter->AddHostnameHandler("http", host_name_,
+ &PrintDialogCloudTest::Factory);
+ handler_added_ = true;
+
+ registrar_.Add(this, NotificationType::LOAD_STOP,
+ NotificationService::AllSources());
+ }
+ }
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::LOAD_STOP) {
+ MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ registrar_.Remove(this, NotificationType::LOAD_STOP,
+ NotificationService::AllSources());
+ }
+ }
+
+ bool handler_added_;
+ std::string host_name_;
+ std::string test_data_;
+ FilePath test_data_directory_;
+ NotificationRegistrar registrar_;
+};
+
+URLRequestJob* PrintDialogCloudTest::Factory(URLRequest* request,
+ const std::string& scheme) {
+ if (request && (request->url() ==
+ GURL(internal_cloud_print_helpers::kCloudPrintDialogUrl)))
+ Singleton<TestResult>()->SetResult(true);
+ return new SimpleTestJob(request);
+}
+
+IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, HandlersRegistered) {
+ BrowserList::SetLastActive(browser());
+ ASSERT_TRUE(BrowserList::GetLastActive());
+
+ AddTestHandlers();
+
+ FilePath pdf_file =
+ test_data_directory_.AppendASCII("printing/cloud_print_uitest.pdf");
+ new PrintDialogCloud(pdf_file);
+
+ ui_test_utils::RunMessageLoop();
+
+ ASSERT_TRUE(Singleton<TestResult>()->GetResult());
+}
+
+#if defined(OS_CHROMEOS)
+// Disabled until the extern URL is live so that the Print menu item
+// can be enabled for Chromium OS.
+IN_PROC_BROWSER_TEST_F(PrintDialogCloudTest, DISABLED_DialogGrabbed) {
+ BrowserList::SetLastActive(browser());
+ ASSERT_TRUE(BrowserList::GetLastActive());
+
+ AddTestHandlers();
+
+ // This goes back one step further for the Chrome OS case, to making
+ // sure 'window.print()' gets to the right place.
+ ASSERT_TRUE(browser()->GetSelectedTabContents());
+ ASSERT_TRUE(browser()->GetSelectedTabContents()->render_view_host());
+
+ std::wstring window_print(L"window.print()");
+ browser()->GetSelectedTabContents()->render_view_host()->
+ ExecuteJavascriptInWebFrame(std::wstring(), window_print);
+
+ ui_test_utils::RunMessageLoop();
+
+ ASSERT_TRUE(Singleton<TestResult>()->GetResult());
+}
+#endif
diff --git a/chrome/browser/printing/print_dialog_cloud_unittest.cc b/chrome/browser/printing/print_dialog_cloud_unittest.cc
new file mode 100644
index 0000000..85a3ce3
--- /dev/null
+++ b/chrome/browser/printing/print_dialog_cloud_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#include "chrome/browser/printing/print_dialog_cloud_internal.h"
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::AtLeast;
+using testing::Return;
+using testing::_;
+using testing::A;
+
+static const char* const kPDFTestFile = "printing/cloud_print_unittest.pdf";
+static const char* const kEmptyPDFTestFile =
+ "printing/cloud_print_emptytest.pdf";
+
+FilePath GetTestDataFileName() {
+ FilePath test_data_directory;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
+ FilePath test_file = test_data_directory.AppendASCII(kPDFTestFile);
+ return test_file;
+}
+
+FilePath GetEmptyDataFileName() {
+ FilePath test_data_directory;
+ PathService::Get(chrome::DIR_TEST_DATA, &test_data_directory);
+ FilePath test_file = test_data_directory.AppendASCII(kEmptyPDFTestFile);
+ return test_file;
+}
+
+char* GetTestData() {
+ static std::string sTestFileData;
+ if (sTestFileData.empty()) {
+ FilePath test_file = GetTestDataFileName();
+ file_util::ReadFileToString(test_file, &sTestFileData);
+ }
+ return &sTestFileData[0];
+}
+
+namespace internal_cloud_print_helpers {
+
+class MockCloudPrintFlowHandler : public CloudPrintFlowHandler {
+ public:
+ MOCK_METHOD0(RegisterMessages,
+ void());
+ MOCK_METHOD3(Observe,
+ void(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details));
+ MOCK_METHOD0(CreateCloudPrintDataSender,
+ scoped_refptr<CloudPrintDataSender>());
+};
+
+class MockCloudPrintHtmlDialogDelegate : public CloudPrintHtmlDialogDelegate {
+ public:
+ MOCK_CONST_METHOD0(IsDialogModal,
+ bool());
+ MOCK_CONST_METHOD0(GetDialogTitle,
+ std::wstring());
+ MOCK_CONST_METHOD0(GetDialogContentURL,
+ GURL());
+ MOCK_CONST_METHOD1(GetDOMMessageHandlers,
+ void(std::vector<DOMMessageHandler*>* handlers));
+ MOCK_CONST_METHOD1(GetDialogSize,
+ void(gfx::Size* size));
+ MOCK_CONST_METHOD0(GetDialogArgs,
+ std::string());
+ MOCK_METHOD1(OnDialogClosed,
+ void(const std::string& json_retval));
+ MOCK_METHOD2(OnCloseContents,
+ void(TabContents* source, bool *out_close_dialog));
+};
+
+} // namespace internal_cloud_print_helpers
+
+using internal_cloud_print_helpers::CloudPrintDataSenderHelper;
+using internal_cloud_print_helpers::CloudPrintDataSender;
+
+class MockExternalHtmlDialogUI : public ExternalHtmlDialogUI {
+ public:
+ MOCK_METHOD1(RenderViewCreated,
+ void(RenderViewHost* render_view_host));
+};
+
+class MockCloudPrintDataSenderHelper : public CloudPrintDataSenderHelper {
+ public:
+ // TODO(scottbyer): At some point this probably wants to use a
+ // MockTabContents instead of NULL, and to pre-load it with a bunch
+ // of expects/results.
+ MockCloudPrintDataSenderHelper() : CloudPrintDataSenderHelper(NULL) {}
+ MOCK_METHOD1(CallJavascriptFunction, void(const std::wstring&));
+ MOCK_METHOD2(CallJavascriptFunction, void(const std::wstring&,
+ const Value& arg1));
+ MOCK_METHOD3(CallJavascriptFunction, void(const std::wstring&,
+ const Value& arg1,
+ const Value& arg2));
+};
+
+// Testing for CloudPrintDataSender needs a mock DOMUI.
+class CloudPrintDataSenderTest : public testing::Test {
+ public:
+ CloudPrintDataSenderTest()
+ : file_thread_(ChromeThread::FILE, &message_loop_),
+ io_thread_(ChromeThread::IO, &message_loop_) {}
+
+ protected:
+ virtual void SetUp() {
+ mock_helper_.reset(new MockCloudPrintDataSenderHelper);
+ print_data_sender_ =
+ new CloudPrintDataSender(mock_helper_.get());
+ }
+
+ scoped_refptr<CloudPrintDataSender> print_data_sender_;
+ scoped_ptr<MockCloudPrintDataSenderHelper> mock_helper_;
+
+ MessageLoop message_loop_;
+ ChromeThread file_thread_;
+ ChromeThread io_thread_;
+};
+
+// TODO(scottbyer): DISABLED until the binary test file can get
+// checked in separate from the patch.
+TEST_F(CloudPrintDataSenderTest, DISABLED_CanSend) {
+ EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).
+ WillOnce(Return());
+
+ FilePath test_data_file_name = GetTestDataFileName();
+ ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ print_data_sender_.get(),
+ &CloudPrintDataSender::ReadPrintDataFile,
+ test_data_file_name));
+ MessageLoop::current()->RunAllPending();
+}
+
+TEST_F(CloudPrintDataSenderTest, BadFile) {
+ EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
+
+#if defined(OS_WIN)
+ FilePath bad_data_file_name(L"/some/file/that/isnot/there");
+#else
+ FilePath bad_data_file_name("/some/file/that/isnot/there");
+#endif
+ ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ print_data_sender_.get(),
+ &CloudPrintDataSender::ReadPrintDataFile,
+ bad_data_file_name));
+ MessageLoop::current()->RunAllPending();
+}
+
+TEST_F(CloudPrintDataSenderTest, EmptyFile) {
+ EXPECT_CALL(*mock_helper_, CallJavascriptFunction(_, _, _)).Times(0);
+
+ FilePath empty_data_file_name = GetEmptyDataFileName();
+ ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ print_data_sender_.get(),
+ &CloudPrintDataSender::ReadPrintDataFile,
+ empty_data_file_name));
+ MessageLoop::current()->RunAllPending();
+}
+
+// Testing for CloudPrintFlowHandler needs a mock
+// CloudPrintHtmlDialogDelegate, mock CloudPrintDataSender, and a mock
+// DOMUI.
+
+// Testing for CloudPrintHtmlDialogDelegate needs a mock
+// CloudPrintFlowHandler.
+
+// Testing for ExternalHtmlDialogUI needs a mock TabContents, mock
+// CloudPrintHtmlDialogDelegate (provided through the mock
+// tab_contents)
+
+// Testing for PrintDialogCloud needs a mock Browser.
diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc
index f27ee9b..6cb3362 100644
--- a/chrome/browser/renderer_host/resource_message_filter.cc
+++ b/chrome/browser/renderer_host/resource_message_filter.cc
@@ -329,6 +329,8 @@ ResourceMessageFilter::ResourceMessageFilter(
DCHECK(dom_storage_dispatcher_host_.get());
render_widget_helper_->Init(id(), resource_dispatcher_host_);
+ cloud_print_enabled_ = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableCloudPrint);
}
ResourceMessageFilter::~ResourceMessageFilter() {
diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h
index 5eef048..88642cd 100644
--- a/chrome/browser/renderer_host/resource_message_filter.h
+++ b/chrome/browser/renderer_host/resource_message_filter.h
@@ -416,6 +416,8 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter,
// Whether this process is used for off the record tabs.
bool off_the_record_;
+ bool cloud_print_enabled_;
+
// A callback to create a routing id for the associated renderer process.
scoped_ptr<CallbackWithReturnValue<int>::Type> next_route_id_callback_;
diff --git a/chrome/browser/renderer_host/resource_message_filter_gtk.cc b/chrome/browser/renderer_host/resource_message_filter_gtk.cc
index 3a64462..c4b6530 100644
--- a/chrome/browser/renderer_host/resource_message_filter_gtk.cc
+++ b/chrome/browser/renderer_host/resource_message_filter_gtk.cc
@@ -16,6 +16,8 @@
#include "chrome/browser/chrome_thread.h"
#if defined(TOOLKIT_GTK)
#include "chrome/browser/printing/print_dialog_gtk.h"
+#else
+#include "chrome/browser/printing/print_dialog_cloud.h"
#endif
#include "chrome/common/chrome_paths.h"
#include "chrome/common/render_messages.h"
@@ -308,7 +310,10 @@ void ResourceMessageFilter::OnTempFileForPrintingWritten(int fd_in_browser) {
#if defined(TOOLKIT_GTK)
PrintDialogGtk::CreatePrintDialogForPdf(it->second);
#else
- NOTIMPLEMENTED();
+ if (cloud_print_enabled_)
+ PrintDialogCloud::CreatePrintDialogForPdf(it->second);
+ else
+ NOTIMPLEMENTED();
#endif
// Erase the entry in the map.
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index 1ed639d..b1b9fa1 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -17,6 +17,7 @@
#if defined(OS_MACOSX)
#include "chrome/browser/cocoa/html_dialog_window_controller_cppsafe.h"
#endif
+#include "chrome/browser/dom_ui/dom_ui_util.h"
#include "chrome/browser/google_service_auth_error.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
@@ -32,28 +33,6 @@
static const wchar_t* kLoginIFrameXPath = L"//iframe[@id='login']";
static const wchar_t* kDoneIframeXPath = L"//iframe[@id='done']";
-// Helper function to read the JSON string from the Value parameter.
-static std::string GetJsonResponse(const Value* content) {
- if (!content || !content->IsType(Value::TYPE_LIST)) {
- NOTREACHED();
- return std::string();
- }
- const ListValue* args = static_cast<const ListValue*>(content);
- if (args->GetSize() != 1) {
- NOTREACHED();
- return std::string();
- }
-
- std::string result;
- Value* value = NULL;
- if (!args->Get(0, &value) || !value->GetAsString(&result)) {
- NOTREACHED();
- return std::string();
- }
-
- return result;
-}
-
void FlowHandler::RegisterMessages() {
dom_ui_->RegisterMessageCallback("ShowCustomize",
NewCallback(this, &FlowHandler::HandleUserClickedCustomize));
@@ -102,7 +81,7 @@ void FlowHandler::ClickCustomizeCancel(const Value* value) {
void FlowHandler::HandleSubmitAuth(const Value* value) {
- std::string json(GetJsonResponse(value));
+ std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(value));
std::string username, password, captcha;
if (json.empty())
return;
diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h
index 4e52ee3..db9de27 100644
--- a/chrome/browser/sync/sync_setup_flow.h
+++ b/chrome/browser/sync/sync_setup_flow.h
@@ -75,6 +75,9 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
virtual void OnDialogClosed(const std::string& json_retval);
// HtmlDialogUIDelegate implementation.
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { }
+
+ // HtmlDialogUIDelegate implementation.
virtual std::wstring GetDialogTitle() const {
return l10n_util::GetString(IDS_SYNC_MY_BOOKMARKS_LABEL);
}
diff --git a/chrome/browser/views/html_dialog_view.cc b/chrome/browser/views/html_dialog_view.cc
index 68a2c80..0da1cf9 100644
--- a/chrome/browser/views/html_dialog_view.cc
+++ b/chrome/browser/views/html_dialog_view.cc
@@ -136,6 +136,12 @@ void HtmlDialogView::OnDialogClosed(const std::string& json_retval) {
window()->Close();
}
+void HtmlDialogView::OnCloseContents(TabContents* source,
+ bool* out_close_dialog) {
+ if (delegate_)
+ delegate_->OnCloseContents(source, out_close_dialog);
+}
+
////////////////////////////////////////////////////////////////////////////////
// TabContentsDelegate implementation:
@@ -162,6 +168,13 @@ void HtmlDialogView::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
#endif
}
+void HtmlDialogView::CloseContents(TabContents* source) {
+ bool close_dialog = false;
+ OnCloseContents(source, &close_dialog);
+ if (close_dialog)
+ OnDialogClosed(std::string());
+}
+
////////////////////////////////////////////////////////////////////////////////
// HtmlDialogView:
diff --git a/chrome/browser/views/html_dialog_view.h b/chrome/browser/views/html_dialog_view.h
index 6ba6f87..e7d0275 100644
--- a/chrome/browser/views/html_dialog_view.h
+++ b/chrome/browser/views/html_dialog_view.h
@@ -55,7 +55,7 @@ class HtmlDialogView
virtual views::View* GetContentsView();
virtual views::View* GetInitiallyFocusedView();
- // Overridden from HtmlDialogUI::Delegate:
+ // Overridden from HtmlDialogUIDelegate:
virtual bool IsDialogModal() const;
virtual std::wstring GetDialogTitle() const;
virtual GURL GetDialogContentURL() const;
@@ -64,11 +64,13 @@ class HtmlDialogView
virtual void GetDialogSize(gfx::Size* size) const;
virtual std::string GetDialogArgs() const;
virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog);
// Overridden from TabContentsDelegate:
virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
virtual void ToolbarSizeChanged(TabContents* source, bool is_animating);
virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
+ virtual void CloseContents(TabContents* source);
private:
// This view is a delegate to the HTML content since it needs to get notified
diff --git a/chrome/browser/views/select_file_dialog.cc b/chrome/browser/views/select_file_dialog.cc
index c8cd839..0d523a8 100644
--- a/chrome/browser/views/select_file_dialog.cc
+++ b/chrome/browser/views/select_file_dialog.cc
@@ -114,6 +114,8 @@ class SelectFileDialogImpl : public SelectFileDialog {
virtual void GetDialogSize(gfx::Size* size) const;
virtual std::string GetDialogArgs() const;
virtual void OnDialogClosed(const std::string& json_retval);
+ virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {
+ }
DISALLOW_COPY_AND_ASSIGN(FileBrowseDelegate);
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b192909..1382d5c 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -907,6 +907,8 @@
'browser/dom_ui/dom_ui_theme_source.h',
'browser/dom_ui/dom_ui_thumbnail_source.cc',
'browser/dom_ui/dom_ui_thumbnail_source.h',
+ 'browser/dom_ui/dom_ui_util.cc',
+ 'browser/dom_ui/dom_ui_util.h',
'browser/dom_ui/downloads_dom_handler.cc',
'browser/dom_ui/downloads_dom_handler.h',
'browser/dom_ui/downloads_ui.cc',
@@ -1755,6 +1757,8 @@
'browser/pref_store.h',
'browser/printing/print_dialog_gtk.cc',
'browser/printing/print_dialog_gtk.h',
+ 'browser/printing/print_dialog_cloud.cc',
+ 'browser/printing/print_dialog_cloud.h',
'browser/printing/print_job.cc',
'browser/printing/print_job.h',
'browser/printing/print_job_manager.cc',
@@ -2842,6 +2846,8 @@
['include', '^browser/dock_info.cc'],
['include', '^browser/dock_info.h'],
['include', '^browser/extensions/'],
+ ['include', 'browser/printing/print_dialog_cloud.cc'],
+ ['include', 'browser/printing/print_dialog_cloud.h'],
['include', '^browser/views/about_chrome_view.cc'],
['include', '^browser/views/about_chrome_view.h'],
['include', '^browser/views/accelerator_table_gtk.cc'],
@@ -2951,6 +2957,8 @@
['include', '^browser/views/infobars/*'],
['include', '^browser/views/info_bubble.cc'],
['include', '^browser/views/info_bubble.h'],
+ ['include', '^browser/views/html_dialog_view.cc'],
+ ['include', '^browser/views/html_dialog_view.h'],
['include', '^browser/views/location_bar/click_handler.cc'],
['include', '^browser/views/location_bar/click_handler.h'],
['include', '^browser/views/location_bar/content_setting_image_view.cc'],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index f18f78b..f6ce069 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -867,6 +867,7 @@
'browser/password_manager/password_store_win_unittest.cc',
'browser/pref_member_unittest.cc',
'browser/pref_service_unittest.cc',
+ 'browser/printing/print_dialog_cloud_unittest.cc',
'browser/printing/print_job_unittest.cc',
'browser/privacy_blacklist/blacklist_interceptor_unittest.cc',
'browser/privacy_blacklist/blacklist_unittest.cc',
@@ -1357,6 +1358,7 @@
'browser/geolocation/geolocation_browsertest.cc',
'browser/net/cookie_policy_browsertest.cc',
'browser/net/ftp_browsertest.cc',
+ 'browser/printing/print_dialog_cloud_uitest.cc',
'browser/sessions/session_restore_browsertest.cc',
'browser/ssl/ssl_browser_tests.cc',
'browser/task_manager_browsertest.cc',
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index c22fc24..ab8c758 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -89,6 +89,7 @@ extern const char kEnableApps[];
extern const char kEnableAuthNegotiatePort[];
extern const char kEnableBenchmarking[];
extern const char kEnableCloudPrintProxy[];
+extern const char kEnableCloudPrint[];
extern const char kEnableExperimentalExtensionApis[];
extern const char kEnableExperimentalWebGL[];
extern const char kEnableExtensionTimelineApi[];
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 879b2f7..6542fd1 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -93,6 +93,9 @@ const char kSyncSetupDonePath[] = "setupdone";
const char kAppCacheViewInternalsURL[] = "chrome://appcache-internals/";
+const char kCloudPrintResourcesURL[] = "chrome://cloudprintresources/";
+const char kCloudPrintResourcesHost[] = "cloudprintresources";
+
const char kNetworkViewInternalsURL[] = "chrome://net-internals/";
const char kNetworkViewCacheURL[] = "chrome://view-http-cache/";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index 230cc67..e3cf3d9 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -93,6 +93,10 @@ extern const char kSyncSetupDonePath[];
// AppCache related URL.
extern const char kAppCacheViewInternalsURL[];
+// Cloud Print dialog URL components.
+extern const char kCloudPrintResourcesURL[];
+extern const char kCloudPrintResourcesHost[];
+
// Network related URLs.
extern const char kNetworkViewCacheURL[];
extern const char kNetworkViewInternalsURL[];
diff --git a/chrome/test/data/printing/cloud_print_emptytest.pdf b/chrome/test/data/printing/cloud_print_emptytest.pdf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/chrome/test/data/printing/cloud_print_emptytest.pdf
diff --git a/chrome/test/data/printing/cloud_print_uitest.html b/chrome/test/data/printing/cloud_print_uitest.html
new file mode 100644
index 0000000..2b79b4a
--- /dev/null
+++ b/chrome/test/data/printing/cloud_print_uitest.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Cloud Print Dialog unittest</title>
+<script language="javascript">
+ window.onload = onLoad;
+
+ function onLoad() {
+ }
+
+ function testPageSetup() {
+ var result = JSON.stringify({'dpi': 300,
+ 'min_shrink': 1.25,
+ 'max_shrink': 2.0,
+ 'selection_only': false});
+ chrome.send('SetPageParameters', [result]);
+ }
+
+ function testWindowPrint() {
+ window.print();
+ }
+
+ function testWindowClose() {
+ window.close();
+ }
+
+ function testWindowSizeLarger() {
+ window.resizeBy(0, 50);
+ }
+
+ function testWindowSizeSmaller() {
+ window.resizeBy(0, -50);
+ }
+
+ function showDebugger() {
+ onLoad();
+ chrome.send('ShowDebugger', ['']);
+ }
+
+ function testSendPrintData() {
+ chrome.send('SendPrintData', ['']);
+ }
+</script>
+</head>
+<body style='margin:0; border:0;'>
+ <hr>
+ <table border='0' width='100%'>
+ <tr>
+ <td align='right'>
+ <input type='button' value='Open Debug Window' id='debug'
+ onclick='showDebugger();'/>
+ <input type='button' value='Test Send Data' id='data'
+ onclick='testSendPrintData();'/>
+ <input type='button' name='test' value='Test window.print()'
+ onclick='testWindowPrint();'/>
+ </td>
+ </tr>
+ <tr>
+ <td align='right'>
+ <input type='button' name='larger' value='Make Window Larger'
+ onclick='makeWindowLarger();'/>
+ <input type='button' name='smaller' value='Make Window Smaller'
+ onclick='makeWindowSmaller();'/>
+ <input type='button' name='test' value='Test Page Setup'
+ onclick='testPageSetup();'/>
+ </td>
+ </tr>
+ <tr>
+ <td align='right'>
+ <input type='button' value='Cancel' id='cancel'
+ onclick='testWindowClose();'/>
+ </td>
+ </tr>
+ </table>
+ <div id='message' style='font-weight:bolder;'>&nbsp;</div>
+ <div id='advanced' class='advanced' style='display:none;'></div>
+</body>
+</html>