summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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>