blob: 3fdad4bc0489c90c9fdab2f67b8240962792bd4b [file] [log] [blame]
[email protected]73852b8f2010-05-14 00:38:121// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/printing/print_dialog_cloud.h"
6#include "chrome/browser/printing/print_dialog_cloud_internal.h"
7
8#include "app/l10n_util.h"
9#include "base/base64.h"
10#include "base/file_util.h"
11#include "base/json/json_reader.h"
[email protected]73852b8f2010-05-14 00:38:1212#include "base/values.h"
13#include "chrome/browser/browser_list.h"
[email protected]017a7a112010-10-12 16:38:2714#include "chrome/browser/browser_thread.h"
[email protected]37858e52010-08-26 00:22:0215#include "chrome/browser/debugger/devtools_manager.h"
[email protected]73852b8f2010-05-14 00:38:1216#include "chrome/browser/dom_ui/dom_ui.h"
17#include "chrome/browser/dom_ui/dom_ui_util.h"
18#include "chrome/browser/dom_ui/html_dialog_ui.h"
[email protected]2283eead2010-09-29 23:17:3019#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
[email protected]73852b8f2010-05-14 00:38:1220#include "chrome/browser/renderer_host/render_view_host.h"
[email protected]37858e52010-08-26 00:22:0221#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]73852b8f2010-05-14 00:38:1222#include "chrome/common/notification_observer.h"
23#include "chrome/common/notification_registrar.h"
24#include "chrome/common/notification_source.h"
[email protected]939856a2010-08-24 20:29:0225#include "chrome/common/notification_type.h"
26#include "chrome/common/render_messages_params.h"
[email protected]73852b8f2010-05-14 00:38:1227#include "chrome/common/url_constants.h"
[email protected]939856a2010-08-24 20:29:0228#include "webkit/glue/webpreferences.h"
[email protected]73852b8f2010-05-14 00:38:1229
30#include "grit/generated_resources.h"
31
32// This module implements the UI support in Chrome for cloud printing.
33// This means hosting a dialog containing HTML/JavaScript and using
34// the published cloud print user interface integration APIs to get
35// page setup settings from the dialog contents and provide the
36// generated print PDF to the dialog contents for uploading to the
37// cloud print service.
38
39// Currently, the flow between these classes is as follows:
40
41// PrintDialogCloud::CreatePrintDialogForPdf is called from
42// resource_message_filter_gtk.cc once the renderer has informed the
43// renderer host that PDF generation into the renderer host provided
44// temp file has been completed. That call is on the IO thread.
45// That, in turn, hops over to the UI thread to create an instance of
46// PrintDialogCloud.
47
48// The constructor for PrintDialogCloud creates a
49// CloudPrintHtmlDialogDelegate and asks the current active browser to
50// show an HTML dialog using that class as the delegate. That class
51// hands in the kCloudPrintResourcesURL as the URL to visit. That is
52// recognized by the GetDOMUIFactoryFunction as a signal to create an
53// ExternalHtmlDialogUI.
54
55// CloudPrintHtmlDialogDelegate also temporarily owns a
56// CloudPrintFlowHandler, a class which is responsible for the actual
57// interactions with the dialog contents, including handing in the PDF
58// print data and getting any page setup parameters that the dialog
59// contents provides. As part of bringing up the dialog,
60// HtmlDialogUI::RenderViewCreated is called (an override of
61// DOMUI::RenderViewCreated). That routine, in turn, calls the
62// delegate's GetDOMMessageHandlers routine, at which point the
63// ownership of the CloudPrintFlowHandler is handed over. A pointer
64// to the flow handler is kept to facilitate communication back and
65// forth between the two classes.
66
67// The DOMUI continues dialog bring-up, calling
68// CloudPrintFlowHandler::RegisterMessages. This is where the
69// additional object model capabilities are registered for the dialog
70// contents to use. It is also at this time that capabilities for the
71// dialog contents are adjusted to allow the dialog contents to close
72// the window. In addition, the pending URL is redirected to the
73// actual cloud print service URL. The flow controller also registers
74// for notification of when the dialog contents finish loading, which
75// is currently used to send the PDF data to the dialog contents.
76
77// In order to send the PDF data to the dialog contents, the flow
78// handler uses a CloudPrintDataSender. It creates one, letting it
79// know the name of the temporary file containing the PDF data, and
80// posts the task of reading the file
81// (CloudPrintDataSender::ReadPrintDataFile) to the file thread. That
82// routine reads in the file, and then hops over to the IO thread to
83// send that data to the dialog contents.
84
85// When the dialog contents are finished (by either being cancelled or
86// hitting the print button), the delegate is notified, and responds
87// that the dialog should be closed, at which point things are torn
88// down and released.
89
90// TODO(scottbyer):
91// http://code.google.com/p/chromium/issues/detail?id=44093 The
92// high-level flow (where the PDF data is generated before even
93// bringing up the dialog) isn't what we want.
94
95
96namespace internal_cloud_print_helpers {
97
[email protected]73852b8f2010-05-14 00:38:1298bool GetRealOrInt(const DictionaryValue& dictionary,
[email protected]a65175d2010-08-17 04:00:5799 const std::string& path,
[email protected]73852b8f2010-05-14 00:38:12100 double* out_value) {
101 if (!dictionary.GetReal(path, out_value)) {
102 int int_value = 0;
103 if (!dictionary.GetInteger(path, &int_value))
104 return false;
105 *out_value = int_value;
106 }
107 return true;
108}
109
110// From the JSON parsed value, get the entries for the page setup
111// parameters.
112bool GetPageSetupParameters(const std::string& json,
113 ViewMsg_Print_Params& parameters) {
114 scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
115 DLOG_IF(ERROR, (!parsed_value.get() ||
116 !parsed_value->IsType(Value::TYPE_DICTIONARY)))
117 << "PageSetup call didn't have expected contents";
118 if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
119 return false;
120
121 bool result = true;
122 DictionaryValue* params = static_cast<DictionaryValue*>(parsed_value.get());
[email protected]a65175d2010-08-17 04:00:57123 result &= GetRealOrInt(*params, "dpi", &parameters.dpi);
124 result &= GetRealOrInt(*params, "min_shrink", &parameters.min_shrink);
125 result &= GetRealOrInt(*params, "max_shrink", &parameters.max_shrink);
126 result &= params->GetBoolean("selection_only", &parameters.selection_only);
[email protected]73852b8f2010-05-14 00:38:12127 return result;
128}
129
130void CloudPrintDataSenderHelper::CallJavascriptFunction(
131 const std::wstring& function_name) {
132 dom_ui_->CallJavascriptFunction(function_name);
133}
134
135void CloudPrintDataSenderHelper::CallJavascriptFunction(
136 const std::wstring& function_name, const Value& arg) {
137 dom_ui_->CallJavascriptFunction(function_name, arg);
138}
139
140void CloudPrintDataSenderHelper::CallJavascriptFunction(
141 const std::wstring& function_name, const Value& arg1, const Value& arg2) {
142 dom_ui_->CallJavascriptFunction(function_name, arg1, arg2);
143}
144
145// Clears out the pointer we're using to communicate. Either routine is
146// potentially expensive enough that stopping whatever is in progress
147// is worth it.
148void CloudPrintDataSender::CancelPrintDataFile() {
149 AutoLock lock(lock_);
150 // We don't own helper, it was passed in to us, so no need to
151 // delete, just let it go.
152 helper_ = NULL;
153}
154
[email protected]38e08982010-10-22 17:28:43155CloudPrintDataSender::CloudPrintDataSender(CloudPrintDataSenderHelper* helper,
156 const string16& print_job_title)
157 : helper_(helper),
158 print_job_title_(print_job_title) {
159}
160
161CloudPrintDataSender::~CloudPrintDataSender() {}
162
[email protected]73852b8f2010-05-14 00:38:12163// Grab the raw PDF file contents and massage them into shape for
164// sending to the dialog contents (and up to the cloud print server)
165// by encoding it and prefixing it with the appropriate mime type.
166// Once that is done, kick off the next part of the task on the IO
167// thread.
168void CloudPrintDataSender::ReadPrintDataFile(const FilePath& path_to_pdf) {
[email protected]ba4f1132010-10-09 02:02:35169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
[email protected]73852b8f2010-05-14 00:38:12170 int64 file_size = 0;
171 if (file_util::GetFileSize(path_to_pdf, &file_size) && file_size != 0) {
172 std::string file_data;
173 if (file_size < kuint32max) {
174 file_data.reserve(static_cast<unsigned int>(file_size));
175 } else {
176 DLOG(WARNING) << " print data file too large to reserve space";
177 }
178 if (helper_ && file_util::ReadFileToString(path_to_pdf, &file_data)) {
179 std::string base64_data;
180 base::Base64Encode(file_data, &base64_data);
181 std::string header("data:application/pdf;base64,");
182 base64_data.insert(0, header);
183 scoped_ptr<StringValue> new_data(new StringValue(base64_data));
184 print_data_.swap(new_data);
[email protected]ba4f1132010-10-09 02:02:35185 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
186 NewRunnableMethod(
187 this,
188 &CloudPrintDataSender::SendPrintDataFile));
[email protected]73852b8f2010-05-14 00:38:12189 }
190 }
191}
192
193// We have the data in hand that needs to be pushed into the dialog
194// contents; do so from the IO thread.
195
196// TODO(scottbyer): If the print data ends up being larger than the
197// upload limit (currently 10MB), what we need to do is upload that
198// large data to google docs and set the URL in the printing
199// JavaScript to that location, and make sure it gets deleted when not
200// needed. - 4/1/2010
201void CloudPrintDataSender::SendPrintDataFile() {
[email protected]ba4f1132010-10-09 02:02:35202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]73852b8f2010-05-14 00:38:12203 AutoLock lock(lock_);
204 if (helper_ && print_data_.get()) {
[email protected]9848c7e2010-06-03 16:06:56205 StringValue title(print_job_title_);
[email protected]73852b8f2010-05-14 00:38:12206
207 // Send the print data to the dialog contents. The JavaScript
208 // function is a preliminary API for prototyping purposes and is
209 // subject to change.
210 const_cast<CloudPrintDataSenderHelper*>(helper_)->CallJavascriptFunction(
211 L"printApp._printDataUrl", *print_data_, title);
212 }
213}
214
215
[email protected]38e08982010-10-22 17:28:43216CloudPrintFlowHandler::CloudPrintFlowHandler(const FilePath& path_to_pdf,
217 const string16& print_job_title)
218 : path_to_pdf_(path_to_pdf),
219 print_job_title_(print_job_title) {
220}
221
222CloudPrintFlowHandler::~CloudPrintFlowHandler() {
223 // This will also cancel any task in flight.
224 CancelAnyRunningTask();
225}
226
227
[email protected]73852b8f2010-05-14 00:38:12228void CloudPrintFlowHandler::SetDialogDelegate(
229 CloudPrintHtmlDialogDelegate* delegate) {
230 // Even if setting a new dom_ui, it means any previous task needs
231 // to be cancelled, it's now invalid.
[email protected]ba4f1132010-10-09 02:02:35232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12233 CancelAnyRunningTask();
234 dialog_delegate_ = delegate;
235}
236
237// Cancels any print data sender we have in flight and removes our
238// reference to it, so when the task that is calling it finishes and
239// removes it's reference, it goes away.
240void CloudPrintFlowHandler::CancelAnyRunningTask() {
[email protected]ba4f1132010-10-09 02:02:35241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12242 if (print_data_sender_.get()) {
243 print_data_sender_->CancelPrintDataFile();
244 print_data_sender_ = NULL;
245 }
246}
247
248
249void CloudPrintFlowHandler::RegisterMessages() {
250 if (!dom_ui_)
251 return;
252
253 // TODO(scottbyer) - This is where we will register messages for the
254 // UI JS to use. Needed: Call to update page setup parameters.
255 dom_ui_->RegisterMessageCallback(
256 "ShowDebugger",
257 NewCallback(this, &CloudPrintFlowHandler::HandleShowDebugger));
258 dom_ui_->RegisterMessageCallback(
259 "SendPrintData",
260 NewCallback(this, &CloudPrintFlowHandler::HandleSendPrintData));
261 dom_ui_->RegisterMessageCallback(
262 "SetPageParameters",
263 NewCallback(this, &CloudPrintFlowHandler::HandleSetPageParameters));
264
265 if (dom_ui_->tab_contents()) {
266 // Also, take the opportunity to set some (minimal) additional
267 // script permissions required for the web UI.
268
269 // TODO(scottbyer): learn how to make sure we're talking to the
270 // right web site first.
271 RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
272 if (rvh && rvh->delegate()) {
273 WebPreferences webkit_prefs = rvh->delegate()->GetWebkitPrefs();
274 webkit_prefs.allow_scripts_to_close_windows = true;
275 rvh->UpdateWebPreferences(webkit_prefs);
276 }
277
278 // Register for appropriate notifications, and re-direct the URL
279 // to the real server URL, now that we've gotten an HTML dialog
280 // going.
281 NavigationController* controller = &dom_ui_->tab_contents()->controller();
282 NavigationEntry* pending_entry = controller->pending_entry();
283 if (pending_entry)
[email protected]2283eead2010-09-29 23:17:30284 pending_entry->set_url(CloudPrintURL(
[email protected]4baf1c42010-05-18 18:45:25285 dom_ui_->GetProfile()).GetCloudPrintServiceDialogURL());
[email protected]73852b8f2010-05-14 00:38:12286 registrar_.Add(this, NotificationType::LOAD_STOP,
287 Source<NavigationController>(controller));
288 }
289}
290
291void CloudPrintFlowHandler::Observe(NotificationType type,
292 const NotificationSource& source,
293 const NotificationDetails& details) {
294 if (type == NotificationType::LOAD_STOP) {
295 // Choose one or the other. If you need to debug, bring up the
296 // debugger. You can then use the various chrome.send()
297 // registrations above to kick of the various function calls,
298 // including chrome.send("SendPrintData") in the javaScript
299 // console and watch things happen with:
300 // HandleShowDebugger(NULL);
301 HandleSendPrintData(NULL);
302 }
303}
304
[email protected]88942a22010-08-19 20:34:43305void CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) {
[email protected]73852b8f2010-05-14 00:38:12306 ShowDebugger();
307}
308
309void CloudPrintFlowHandler::ShowDebugger() {
310 if (dom_ui_) {
311 RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
312 if (rvh)
313 DevToolsManager::GetInstance()->OpenDevToolsWindow(rvh);
314 }
315}
316
317scoped_refptr<CloudPrintDataSender>
318CloudPrintFlowHandler::CreateCloudPrintDataSender() {
319 DCHECK(dom_ui_);
320 print_data_helper_.reset(new CloudPrintDataSenderHelper(dom_ui_));
[email protected]9848c7e2010-06-03 16:06:56321 return new CloudPrintDataSender(print_data_helper_.get(), print_job_title_);
[email protected]73852b8f2010-05-14 00:38:12322}
323
[email protected]88942a22010-08-19 20:34:43324void CloudPrintFlowHandler::HandleSendPrintData(const ListValue* args) {
[email protected]ba4f1132010-10-09 02:02:35325 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12326 // This will cancel any ReadPrintDataFile() or SendPrintDataFile()
327 // requests in flight (this is anticipation of when setting page
328 // setup parameters becomes asynchronous and may be set while some
329 // data is in flight). Then we can clear out the print data.
330 CancelAnyRunningTask();
331 if (dom_ui_) {
332 print_data_sender_ = CreateCloudPrintDataSender();
[email protected]ba4f1132010-10-09 02:02:35333 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
334 NewRunnableMethod(
335 print_data_sender_.get(),
336 &CloudPrintDataSender::ReadPrintDataFile,
337 path_to_pdf_));
[email protected]73852b8f2010-05-14 00:38:12338 }
339}
340
[email protected]88942a22010-08-19 20:34:43341void CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) {
342 std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(args));
[email protected]73852b8f2010-05-14 00:38:12343 if (json.empty())
344 return;
345
346 // These are backstop default values - 72 dpi to match the screen,
347 // 8.5x11 inch paper with margins subtracted (1/4 inch top, left,
348 // right and 0.56 bottom), and the min page shrink and max page
349 // shrink values appear all over the place with no explanation.
350
351 // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings
352 // working so that we can get the default values from there. Fix up
353 // PrintWebViewHelper to do the same.
354 const int kDPI = 72;
355 const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI);
356 const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI);
357 const double kMinPageShrink = 1.25;
358 const double kMaxPageShrink = 2.0;
359
360 ViewMsg_Print_Params default_settings;
361 default_settings.printable_size = gfx::Size(kWidth, kHeight);
362 default_settings.dpi = kDPI;
363 default_settings.min_shrink = kMinPageShrink;
364 default_settings.max_shrink = kMaxPageShrink;
365 default_settings.desired_dpi = kDPI;
366 default_settings.document_cookie = 0;
367 default_settings.selection_only = false;
368
369 if (!GetPageSetupParameters(json, default_settings)) {
370 NOTREACHED();
371 return;
372 }
373
374 // TODO(scottbyer) - Here is where we would kick the originating
375 // renderer thread with these new parameters in order to get it to
376 // re-generate the PDF and hand it back to us. window.print() is
377 // currently synchronous, so there's a lot of work to do to get to
378 // that point.
379}
380
381CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
382 const FilePath& path_to_pdf,
383 int width, int height,
[email protected]9848c7e2010-06-03 16:06:56384 const std::string& json_arguments,
385 const string16& print_job_title)
386 : flow_handler_(new CloudPrintFlowHandler(path_to_pdf, print_job_title)),
[email protected]73852b8f2010-05-14 00:38:12387 owns_flow_handler_(true) {
388 Init(width, height, json_arguments);
389}
390
391CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
392 CloudPrintFlowHandler* flow_handler,
393 int width, int height,
394 const std::string& json_arguments)
395 : flow_handler_(flow_handler),
[email protected]18137e02010-05-25 21:10:35396 owns_flow_handler_(true) {
[email protected]73852b8f2010-05-14 00:38:12397 Init(width, height, json_arguments);
398}
399
400void CloudPrintHtmlDialogDelegate::Init(
[email protected]9848c7e2010-06-03 16:06:56401 int width, int height, const std::string& json_arguments) {
[email protected]73852b8f2010-05-14 00:38:12402 // This information is needed to show the dialog HTML content.
[email protected]ba4f1132010-10-09 02:02:35403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12404 std::string cloud_print_url(chrome::kCloudPrintResourcesURL);
405 params_.url = GURL(cloud_print_url);
406 params_.height = height;
407 params_.width = width;
408 params_.json_input = json_arguments;
409
410 flow_handler_->SetDialogDelegate(this);
411}
412
413CloudPrintHtmlDialogDelegate::~CloudPrintHtmlDialogDelegate() {
414 // If the flow_handler_ is about to outlive us because we don't own
415 // it anymore, we need to have it remove it's reference to us.
[email protected]ba4f1132010-10-09 02:02:35416 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12417 flow_handler_->SetDialogDelegate(NULL);
418 if (owns_flow_handler_) {
419 delete flow_handler_;
420 }
421}
422
423bool CloudPrintHtmlDialogDelegate::IsDialogModal() const {
424 return true;
425}
426
427std::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const {
428 return l10n_util::GetString(IDS_CLOUD_PRINT_TITLE);
429}
430
431GURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const {
432 return params_.url;
433}
434
435void CloudPrintHtmlDialogDelegate::GetDOMMessageHandlers(
436 std::vector<DOMMessageHandler*>* handlers) const {
437 handlers->push_back(flow_handler_);
438 // We don't own flow_handler_ anymore, but it sticks around until at
439 // least right after OnDialogClosed() is called (and this object is
440 // destroyed).
441 owns_flow_handler_ = false;
442}
443
444void CloudPrintHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const {
445 size->set_width(params_.width);
446 size->set_height(params_.height);
447}
448
449std::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const {
450 return params_.json_input;
451}
452
453void CloudPrintHtmlDialogDelegate::OnDialogClosed(
454 const std::string& json_retval) {
455 delete this;
456}
457
[email protected]18137e02010-05-25 21:10:35458void CloudPrintHtmlDialogDelegate::OnCloseContents(TabContents* source,
459 bool* out_close_dialog) {
460 if (out_close_dialog)
461 *out_close_dialog = true;
462}
463
[email protected]73852b8f2010-05-14 00:38:12464} // end of namespace internal_cloud_print_helpers
465
466// static, called on the IO thread. This is the main entry point into
467// creating the dialog.
468
469// TODO(scottbyer): The signature here will need to change as the
470// workflow through the printing code changes to allow for dynamically
471// changing page setup parameters while the dialog is active.
472void PrintDialogCloud::CreatePrintDialogForPdf(const FilePath& path_to_pdf) {
[email protected]ba4f1132010-10-09 02:02:35473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]73852b8f2010-05-14 00:38:12474
[email protected]ba4f1132010-10-09 02:02:35475 BrowserThread::PostTask(
476 BrowserThread::UI, FROM_HERE,
[email protected]73852b8f2010-05-14 00:38:12477 NewRunnableFunction(&PrintDialogCloud::CreateDialogImpl, path_to_pdf));
478}
479
480// static, called from the UI thread.
481void PrintDialogCloud::CreateDialogImpl(const FilePath& path_to_pdf) {
[email protected]ba4f1132010-10-09 02:02:35482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73852b8f2010-05-14 00:38:12483 new PrintDialogCloud(path_to_pdf);
484}
485
486// Initialize the print dialog. Called on the UI thread.
487PrintDialogCloud::PrintDialogCloud(const FilePath& path_to_pdf)
488 : browser_(BrowserList::GetLastActive()) {
489
490 // TODO(scottbyer): Verify GAIA login valid, execute GAIA login if not (should
491 // be distilled out of bookmark sync.)
[email protected]9848c7e2010-06-03 16:06:56492 string16 print_job_title;
493 if (browser_ && browser_->GetSelectedTabContents())
494 print_job_title = browser_->GetSelectedTabContents()->GetTitle();
[email protected]73852b8f2010-05-14 00:38:12495
496 // TODO(scottbyer): Get the dialog width, height from the dialog
497 // contents, and take the screen size into account.
498 HtmlDialogUIDelegate* dialog_delegate =
499 new internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate(
[email protected]9848c7e2010-06-03 16:06:56500 path_to_pdf, 500, 400, std::string(), print_job_title);
[email protected]73852b8f2010-05-14 00:38:12501 browser_->BrowserShowHtmlDialog(dialog_delegate, NULL);
502}
503
504PrintDialogCloud::~PrintDialogCloud() {
505}