1 // Copyright (c) 2012 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
7
8 #include "base/base64.h"
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/json/json_reader.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/devtools/devtools_window.h"
18 #include "chrome/browser/lifetime/application_lifetime.h"
19 #include "chrome/browser/printing/cloud_print/cloud_print_url.h"
20 #include "chrome/browser/printing/print_dialog_cloud_internal.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser_dialogs.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/print_messages.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/user_prefs/pref_registry_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/navigation_controller.h"
30 #include "content/public/browser/navigation_entry.h"
31 #include "content/public/browser/notification_registrar.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/notification_types.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/browser/web_contents_view.h"
37 #include "content/public/browser/web_ui.h"
38 #include "webkit/common/webpreferences.h"
39
40 #if defined(USE_AURA)
41 #include "ui/aura/root_window.h"
42 #include "ui/aura/window.h"
43 #endif
44
45 #if defined(OS_WIN)
46 #include "ui/base/win/foreground_helper.h"
47 #endif
48
49 // This module implements the UI support in Chrome for cloud printing.
50 // This means hosting a dialog containing HTML/JavaScript and using
51 // the published cloud print user interface integration APIs to get
52 // page setup settings from the dialog contents and provide the
53 // generated print data to the dialog contents for uploading to the
54 // cloud print service.
55
56 // Currently, the flow between these classes is as follows:
57
58 // PrintDialogCloud::CreatePrintDialogForFile is called from
59 // resource_message_filter_gtk.cc once the renderer has informed the
60 // renderer host that print data generation into the renderer host provided
61 // temp file has been completed. That call is on the FILE thread.
62 // That, in turn, hops over to the UI thread to create an instance of
63 // PrintDialogCloud.
64
65 // The constructor for PrintDialogCloud creates a
66 // CloudPrintWebDialogDelegate and asks the current active browser to
67 // show an HTML dialog using that class as the delegate. That class
68 // hands in the kChromeUICloudPrintResourcesURL as the URL to visit. That is
69 // recognized by the GetWebUIFactoryFunction as a signal to create an
70 // ExternalWebDialogUI.
71
72 // CloudPrintWebDialogDelegate also temporarily owns a
73 // CloudPrintFlowHandler, a class which is responsible for the actual
74 // interactions with the dialog contents, including handing in the
75 // print data and getting any page setup parameters that the dialog
76 // contents provides. As part of bringing up the dialog,
77 // WebDialogUI::RenderViewCreated is called (an override of
78 // WebUI::RenderViewCreated). That routine, in turn, calls the
79 // delegate's GetWebUIMessageHandlers routine, at which point the
80 // ownership of the CloudPrintFlowHandler is handed over. A pointer
81 // to the flow handler is kept to facilitate communication back and
82 // forth between the two classes.
83
84 // The WebUI continues dialog bring-up, calling
85 // CloudPrintFlowHandler::RegisterMessages. This is where the
86 // additional object model capabilities are registered for the dialog
87 // contents to use. It is also at this time that capabilities for the
88 // dialog contents are adjusted to allow the dialog contents to close
89 // the window. In addition, the pending URL is redirected to the
90 // actual cloud print service URL. The flow controller also registers
91 // for notification of when the dialog contents finish loading, which
92 // is currently used to send the data to the dialog contents.
93
94 // In order to send the data to the dialog contents, the flow
95 // handler uses a CloudPrintDataSender. It creates one, letting it
96 // know the name of the temporary file containing the data, and
97 // posts the task of reading the file
98 // (CloudPrintDataSender::ReadPrintDataFile) to the file thread. That
99 // routine reads in the file, and then hops over to the IO thread to
100 // send that data to the dialog contents.
101
102 // When the dialog contents are finished (by either being cancelled or
103 // hitting the print button), the delegate is notified, and responds
104 // that the dialog should be closed, at which point things are torn
105 // down and released.
106
107 using content::BrowserThread;
108 using content::NavigationController;
109 using content::NavigationEntry;
110 using content::RenderViewHost;
111 using content::WebContents;
112 using content::WebUIMessageHandler;
113 using ui::WebDialogDelegate;
114
115 const int kDefaultWidth = 912;
116 const int kDefaultHeight = 633;
117
118 namespace internal_cloud_print_helpers {
119
120 // From the JSON parsed value, get the entries for the page setup
121 // parameters.
GetPageSetupParameters(const std::string & json,PrintMsg_Print_Params & parameters)122 bool GetPageSetupParameters(const std::string& json,
123 PrintMsg_Print_Params& parameters) {
124 scoped_ptr<Value> parsed_value(base::JSONReader::Read(json));
125 DLOG_IF(ERROR, (!parsed_value.get() ||
126 !parsed_value->IsType(Value::TYPE_DICTIONARY)))
127 << "PageSetup call didn't have expected contents";
128 if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
129 return false;
130
131 bool result = true;
132 DictionaryValue* params = static_cast<DictionaryValue*>(parsed_value.get());
133 result &= params->GetDouble("dpi", ¶meters.dpi);
134 result &= params->GetDouble("min_shrink", ¶meters.min_shrink);
135 result &= params->GetDouble("max_shrink", ¶meters.max_shrink);
136 result &= params->GetBoolean("selection_only", ¶meters.selection_only);
137 return result;
138 }
139
GetSwitchValueString16(const CommandLine & command_line,const char * switchName)140 base::string16 GetSwitchValueString16(const CommandLine& command_line,
141 const char* switchName) {
142 #if defined(OS_WIN)
143 return command_line.GetSwitchValueNative(switchName);
144 #elif defined(OS_POSIX)
145 // POSIX Command line string types are different.
146 CommandLine::StringType native_switch_val;
147 native_switch_val = command_line.GetSwitchValueASCII(switchName);
148 // Convert the ASCII string to UTF16 to prepare to pass.
149 return base::ASCIIToUTF16(native_switch_val);
150 #endif
151 }
152
CallJavascriptFunction(const std::string & function_name,const Value & arg1,const Value & arg2)153 void CloudPrintDataSenderHelper::CallJavascriptFunction(
154 const std::string& function_name, const Value& arg1, const Value& arg2) {
155 web_ui_->CallJavascriptFunction(function_name, arg1, arg2);
156 }
157
158 // Clears out the pointer we're using to communicate. Either routine is
159 // potentially expensive enough that stopping whatever is in progress
160 // is worth it.
CancelPrintDataFile()161 void CloudPrintDataSender::CancelPrintDataFile() {
162 base::AutoLock lock(lock_);
163 // We don't own helper, it was passed in to us, so no need to
164 // delete, just let it go.
165 helper_ = NULL;
166 }
167
CloudPrintDataSender(CloudPrintDataSenderHelper * helper,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,const base::RefCountedMemory * data)168 CloudPrintDataSender::CloudPrintDataSender(
169 CloudPrintDataSenderHelper* helper,
170 const base::string16& print_job_title,
171 const base::string16& print_ticket,
172 const std::string& file_type,
173 const base::RefCountedMemory* data)
174 : helper_(helper),
175 print_job_title_(print_job_title),
176 print_ticket_(print_ticket),
177 file_type_(file_type),
178 data_(data) {
179 }
180
~CloudPrintDataSender()181 CloudPrintDataSender::~CloudPrintDataSender() {}
182
183 // We have the data in hand that needs to be pushed into the dialog
184 // contents; do so from the IO thread.
185
186 // TODO(scottbyer): If the print data ends up being larger than the
187 // upload limit (currently 10MB), what we need to do is upload that
188 // large data to google docs and set the URL in the printing
189 // JavaScript to that location, and make sure it gets deleted when not
190 // needed. - 4/1/2010
SendPrintData()191 void CloudPrintDataSender::SendPrintData() {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
193 if (!data_.get() || !data_->size())
194 return;
195
196 std::string base64_data;
197 base::Base64Encode(
198 base::StringPiece(reinterpret_cast<const char*>(data_->front()),
199 data_->size()),
200 &base64_data);
201 std::string header("data:");
202 header.append(file_type_);
203 header.append(";base64,");
204 base64_data.insert(0, header);
205
206 base::AutoLock lock(lock_);
207 if (helper_) {
208 base::StringValue title(print_job_title_);
209 base::StringValue ticket(print_ticket_);
210 // TODO(abodenha): Change Javascript call to pass in print ticket
211 // after server side support is added. Add test for it.
212
213 // Send the print data to the dialog contents. The JavaScript
214 // function is a preliminary API for prototyping purposes and is
215 // subject to change.
216 helper_->CallJavascriptFunction(
217 "printApp._printDataUrl", base::StringValue(base64_data), title);
218 }
219 }
220
221
CloudPrintFlowHandler(const base::RefCountedMemory * data,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,bool close_after_signin,const base::Closure & callback)222 CloudPrintFlowHandler::CloudPrintFlowHandler(
223 const base::RefCountedMemory* data,
224 const base::string16& print_job_title,
225 const base::string16& print_ticket,
226 const std::string& file_type,
227 bool close_after_signin,
228 const base::Closure& callback)
229 : dialog_delegate_(NULL),
230 data_(data),
231 print_job_title_(print_job_title),
232 print_ticket_(print_ticket),
233 file_type_(file_type),
234 close_after_signin_(close_after_signin),
235 callback_(callback) {
236 }
237
~CloudPrintFlowHandler()238 CloudPrintFlowHandler::~CloudPrintFlowHandler() {
239 // This will also cancel any task in flight.
240 CancelAnyRunningTask();
241 }
242
243
SetDialogDelegate(CloudPrintWebDialogDelegate * delegate)244 void CloudPrintFlowHandler::SetDialogDelegate(
245 CloudPrintWebDialogDelegate* delegate) {
246 // Even if setting a new WebUI, it means any previous task needs
247 // to be canceled, its now invalid.
248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
249 CancelAnyRunningTask();
250 dialog_delegate_ = delegate;
251 }
252
253 // Cancels any print data sender we have in flight and removes our
254 // reference to it, so when the task that is calling it finishes and
255 // removes its reference, it goes away.
CancelAnyRunningTask()256 void CloudPrintFlowHandler::CancelAnyRunningTask() {
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
258 if (print_data_sender_.get()) {
259 print_data_sender_->CancelPrintDataFile();
260 print_data_sender_ = NULL;
261 }
262 }
263
RegisterMessages()264 void CloudPrintFlowHandler::RegisterMessages() {
265 // TODO(scottbyer) - This is where we will register messages for the
266 // UI JS to use. Needed: Call to update page setup parameters.
267 web_ui()->RegisterMessageCallback("ShowDebugger",
268 base::Bind(&CloudPrintFlowHandler::HandleShowDebugger,
269 base::Unretained(this)));
270 web_ui()->RegisterMessageCallback("SendPrintData",
271 base::Bind(&CloudPrintFlowHandler::HandleSendPrintData,
272 base::Unretained(this)));
273 web_ui()->RegisterMessageCallback("SetPageParameters",
274 base::Bind(&CloudPrintFlowHandler::HandleSetPageParameters,
275 base::Unretained(this)));
276
277 // Register for appropriate notifications, and re-direct the URL
278 // to the real server URL, now that we've gotten an HTML dialog
279 // going.
280 NavigationController* controller =
281 &web_ui()->GetWebContents()->GetController();
282 NavigationEntry* pending_entry = controller->GetPendingEntry();
283 if (pending_entry) {
284 Profile* profile = Profile::FromWebUI(web_ui());
285 if (close_after_signin_) {
286 pending_entry->SetURL(
287 CloudPrintURL(profile).GetCloudPrintSigninURL());
288 } else {
289 pending_entry->SetURL(
290 CloudPrintURL(profile).GetCloudPrintServiceDialogURL());
291 }
292 }
293 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
294 content::Source<NavigationController>(controller));
295 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
296 content::Source<NavigationController>(controller));
297 }
298
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)299 void CloudPrintFlowHandler::Observe(
300 int type,
301 const content::NotificationSource& source,
302 const content::NotificationDetails& details) {
303 switch (type) {
304 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
305 NavigationEntry* entry =
306 web_ui()->GetWebContents()->GetController().GetActiveEntry();
307 if (entry)
308 NavigationToURLDidCloseDialog(entry->GetURL());
309 break;
310 }
311 case content::NOTIFICATION_LOAD_STOP: {
312 GURL url = web_ui()->GetWebContents()->GetURL();
313 if (IsCloudPrintDialogUrl(url)) {
314 // Take the opportunity to set some (minimal) additional
315 // script permissions required for the web UI.
316 RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
317 if (rvh) {
318 WebPreferences webkit_prefs = rvh->GetWebkitPreferences();
319 webkit_prefs.allow_scripts_to_close_windows = true;
320 rvh->UpdateWebkitPreferences(webkit_prefs);
321 } else {
322 NOTREACHED();
323 }
324 // Choose one or the other. If you need to debug, bring up the
325 // debugger. You can then use the various chrome.send()
326 // registrations above to kick of the various function calls,
327 // including chrome.send("SendPrintData") in the javaScript
328 // console and watch things happen with:
329 // HandleShowDebugger(NULL);
330 HandleSendPrintData(NULL);
331 }
332 break;
333 }
334 }
335 }
336
HandleShowDebugger(const ListValue * args)337 void CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) {
338 ShowDebugger();
339 }
340
ShowDebugger()341 void CloudPrintFlowHandler::ShowDebugger() {
342 if (web_ui()) {
343 RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost();
344 if (rvh)
345 DevToolsWindow::OpenDevToolsWindow(rvh);
346 }
347 }
348
349 scoped_refptr<CloudPrintDataSender>
CreateCloudPrintDataSender()350 CloudPrintFlowHandler::CreateCloudPrintDataSender() {
351 DCHECK(web_ui());
352 print_data_helper_.reset(new CloudPrintDataSenderHelper(web_ui()));
353 scoped_refptr<CloudPrintDataSender> sender(
354 new CloudPrintDataSender(print_data_helper_.get(),
355 print_job_title_,
356 print_ticket_,
357 file_type_,
358 data_.get()));
359 return sender;
360 }
361
HandleSendPrintData(const ListValue * args)362 void CloudPrintFlowHandler::HandleSendPrintData(const ListValue* args) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 // This will cancel any ReadPrintDataFile() or SendPrintDataFile()
365 // requests in flight (this is anticipation of when setting page
366 // setup parameters becomes asynchronous and may be set while some
367 // data is in flight). Then we can clear out the print data.
368 CancelAnyRunningTask();
369 if (web_ui()) {
370 print_data_sender_ = CreateCloudPrintDataSender();
371 BrowserThread::PostTask(
372 BrowserThread::IO, FROM_HERE,
373 base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender_));
374 }
375 }
376
HandleSetPageParameters(const ListValue * args)377 void CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) {
378 std::string json;
379 bool ret = args->GetString(0, &json);
380 if (!ret || json.empty()) {
381 NOTREACHED() << "Empty json string";
382 return;
383 }
384
385 // These are backstop default values - 72 dpi to match the screen,
386 // 8.5x11 inch paper with margins subtracted (1/4 inch top, left,
387 // right and 0.56 bottom), and the min page shrink and max page
388 // shrink values appear all over the place with no explanation.
389
390 // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings
391 // working so that we can get the default values from there. Fix up
392 // PrintWebViewHelper to do the same.
393 const int kDPI = 72;
394 const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI);
395 const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI);
396 const double kMinPageShrink = 1.25;
397 const double kMaxPageShrink = 2.0;
398
399 PrintMsg_Print_Params default_settings;
400 default_settings.content_size = gfx::Size(kWidth, kHeight);
401 default_settings.printable_area = gfx::Rect(0, 0, kWidth, kHeight);
402 default_settings.dpi = kDPI;
403 default_settings.min_shrink = kMinPageShrink;
404 default_settings.max_shrink = kMaxPageShrink;
405 default_settings.desired_dpi = kDPI;
406 default_settings.document_cookie = 0;
407 default_settings.selection_only = false;
408 default_settings.preview_request_id = 0;
409 default_settings.is_first_request = true;
410 default_settings.print_to_pdf = false;
411
412 if (!GetPageSetupParameters(json, default_settings)) {
413 NOTREACHED();
414 return;
415 }
416
417 // TODO(scottbyer) - Here is where we would kick the originating
418 // renderer thread with these new parameters in order to get it to
419 // re-generate the PDF data and hand it back to us. window.print() is
420 // currently synchronous, so there's a lot of work to do to get to
421 // that point.
422 }
423
StoreDialogClientSize() const424 void CloudPrintFlowHandler::StoreDialogClientSize() const {
425 if (web_ui() && web_ui()->GetWebContents() &&
426 web_ui()->GetWebContents()->GetView()) {
427 gfx::Size size = web_ui()->GetWebContents()->GetView()->GetContainerSize();
428 Profile* profile = Profile::FromWebUI(web_ui());
429 profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogWidth,
430 size.width());
431 profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogHeight,
432 size.height());
433 }
434 }
435
NavigationToURLDidCloseDialog(const GURL & url)436 bool CloudPrintFlowHandler::NavigationToURLDidCloseDialog(const GURL& url) {
437 if (close_after_signin_) {
438 if (IsCloudPrintDialogUrl(url)) {
439 StoreDialogClientSize();
440 web_ui()->GetWebContents()->GetRenderViewHost()->ClosePage();
441 callback_.Run();
442 return true;
443 }
444 }
445 return false;
446 }
447
IsCloudPrintDialogUrl(const GURL & url)448 bool CloudPrintFlowHandler::IsCloudPrintDialogUrl(const GURL& url) {
449 GURL cloud_print_url =
450 CloudPrintURL(Profile::FromWebUI(web_ui())).GetCloudPrintServiceURL();
451 return url.host() == cloud_print_url.host() &&
452 StartsWithASCII(url.path(), cloud_print_url.path(), false) &&
453 url.scheme() == cloud_print_url.scheme();
454 }
455
CloudPrintWebDialogDelegate(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::RefCountedMemory * data,const std::string & json_arguments,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,bool close_after_signin,const base::Closure & callback)456 CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate(
457 content::BrowserContext* browser_context,
458 gfx::NativeWindow modal_parent,
459 const base::RefCountedMemory* data,
460 const std::string& json_arguments,
461 const base::string16& print_job_title,
462 const base::string16& print_ticket,
463 const std::string& file_type,
464 bool close_after_signin,
465 const base::Closure& callback)
466 : flow_handler_(
467 new CloudPrintFlowHandler(data, print_job_title, print_ticket,
468 file_type, close_after_signin, callback)),
469 modal_parent_(modal_parent),
470 owns_flow_handler_(true),
471 keep_alive_when_non_modal_(true) {
472 Init(browser_context, json_arguments);
473 }
474
475 // For unit testing.
CloudPrintWebDialogDelegate(CloudPrintFlowHandler * flow_handler,const std::string & json_arguments)476 CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate(
477 CloudPrintFlowHandler* flow_handler,
478 const std::string& json_arguments)
479 : flow_handler_(flow_handler),
480 modal_parent_(NULL),
481 owns_flow_handler_(true),
482 keep_alive_when_non_modal_(false) {
483 Init(NULL, json_arguments);
484 }
485
486 // Returns the persisted width/height for the print dialog.
GetDialogWidthAndHeightFromPrefs(content::BrowserContext * browser_context,int * width,int * height)487 void GetDialogWidthAndHeightFromPrefs(content::BrowserContext* browser_context,
488 int* width,
489 int* height) {
490 if (!browser_context) {
491 *width = kDefaultWidth;
492 *height = kDefaultHeight;
493 return;
494 }
495
496 PrefService* prefs = Profile::FromBrowserContext(browser_context)->GetPrefs();
497 *width = prefs->GetInteger(prefs::kCloudPrintDialogWidth);
498 *height = prefs->GetInteger(prefs::kCloudPrintDialogHeight);
499 }
500
Init(content::BrowserContext * browser_context,const std::string & json_arguments)501 void CloudPrintWebDialogDelegate::Init(content::BrowserContext* browser_context,
502 const std::string& json_arguments) {
503 // This information is needed to show the dialog HTML content.
504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
505
506 params_.url = GURL(chrome::kChromeUICloudPrintResourcesURL);
507 GetDialogWidthAndHeightFromPrefs(browser_context,
508 ¶ms_.width,
509 ¶ms_.height);
510 params_.json_input = json_arguments;
511
512 flow_handler_->SetDialogDelegate(this);
513 // If we're not modal we can show the dialog with no browser.
514 // We need this to keep Chrome alive while our dialog is up.
515 if (!modal_parent_ && keep_alive_when_non_modal_)
516 chrome::StartKeepAlive();
517 }
518
~CloudPrintWebDialogDelegate()519 CloudPrintWebDialogDelegate::~CloudPrintWebDialogDelegate() {
520 // If the flow_handler_ is about to outlive us because we don't own
521 // it anymore, we need to have it remove its reference to us.
522 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
523 flow_handler_->SetDialogDelegate(NULL);
524 if (owns_flow_handler_) {
525 delete flow_handler_;
526 }
527 }
528
GetDialogModalType() const529 ui::ModalType CloudPrintWebDialogDelegate::GetDialogModalType() const {
530 return modal_parent_ ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE;
531 }
532
GetDialogTitle() const533 base::string16 CloudPrintWebDialogDelegate::GetDialogTitle() const {
534 return base::string16();
535 }
536
GetDialogContentURL() const537 GURL CloudPrintWebDialogDelegate::GetDialogContentURL() const {
538 return params_.url;
539 }
540
GetWebUIMessageHandlers(std::vector<WebUIMessageHandler * > * handlers) const541 void CloudPrintWebDialogDelegate::GetWebUIMessageHandlers(
542 std::vector<WebUIMessageHandler*>* handlers) const {
543 handlers->push_back(flow_handler_);
544 // We don't own flow_handler_ anymore, but it sticks around until at
545 // least right after OnDialogClosed() is called (and this object is
546 // destroyed).
547 owns_flow_handler_ = false;
548 }
549
GetDialogSize(gfx::Size * size) const550 void CloudPrintWebDialogDelegate::GetDialogSize(gfx::Size* size) const {
551 size->set_width(params_.width);
552 size->set_height(params_.height);
553 }
554
GetDialogArgs() const555 std::string CloudPrintWebDialogDelegate::GetDialogArgs() const {
556 return params_.json_input;
557 }
558
OnDialogClosed(const std::string & json_retval)559 void CloudPrintWebDialogDelegate::OnDialogClosed(
560 const std::string& json_retval) {
561 // Get the final dialog size and store it.
562 flow_handler_->StoreDialogClientSize();
563
564 // If we're modal we can show the dialog with no browser.
565 // End the keep-alive so that Chrome can exit.
566 if (!modal_parent_ && keep_alive_when_non_modal_) {
567 // Post to prevent recursive call tho this function.
568 base::MessageLoop::current()->PostTask(FROM_HERE,
569 base::Bind(&chrome::EndKeepAlive));
570 }
571 delete this;
572 }
573
OnCloseContents(WebContents * source,bool * out_close_dialog)574 void CloudPrintWebDialogDelegate::OnCloseContents(WebContents* source,
575 bool* out_close_dialog) {
576 if (out_close_dialog)
577 *out_close_dialog = true;
578 }
579
ShouldShowDialogTitle() const580 bool CloudPrintWebDialogDelegate::ShouldShowDialogTitle() const {
581 return false;
582 }
583
HandleContextMenu(const content::ContextMenuParams & params)584 bool CloudPrintWebDialogDelegate::HandleContextMenu(
585 const content::ContextMenuParams& params) {
586 return true;
587 }
588
HandleOpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params,content::WebContents ** out_new_contents)589 bool CloudPrintWebDialogDelegate::HandleOpenURLFromTab(
590 content::WebContents* source,
591 const content::OpenURLParams& params,
592 content::WebContents** out_new_contents) {
593 return flow_handler_->NavigationToURLDidCloseDialog(params.url);
594 }
595
596 // Called from the UI thread, starts up the dialog.
CreateDialogImpl(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::RefCountedMemory * data,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,bool close_after_signin,const base::Closure & callback)597 void CreateDialogImpl(content::BrowserContext* browser_context,
598 gfx::NativeWindow modal_parent,
599 const base::RefCountedMemory* data,
600 const base::string16& print_job_title,
601 const base::string16& print_ticket,
602 const std::string& file_type,
603 bool close_after_signin,
604 const base::Closure& callback) {
605 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
606 WebDialogDelegate* dialog_delegate =
607 new internal_cloud_print_helpers::CloudPrintWebDialogDelegate(
608 browser_context, modal_parent, data, std::string(), print_job_title,
609 print_ticket, file_type, close_after_signin, callback);
610 #if defined(OS_WIN)
611 gfx::NativeWindow window =
612 #endif
613 chrome::ShowWebDialog(modal_parent,
614 Profile::FromBrowserContext(browser_context),
615 dialog_delegate);
616 #if defined(OS_WIN)
617 if (window) {
618 HWND dialog_handle;
619 #if defined(USE_AURA)
620 dialog_handle = window->GetDispatcher()->host()->GetAcceleratedWidget();
621 #else
622 dialog_handle = window;
623 #endif
624 if (::GetForegroundWindow() != dialog_handle) {
625 ui::ForegroundHelper::SetForeground(dialog_handle);
626 }
627 }
628 #endif
629 }
630
CreateDialogSigninImpl(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::Closure & callback)631 void CreateDialogSigninImpl(content::BrowserContext* browser_context,
632 gfx::NativeWindow modal_parent,
633 const base::Closure& callback) {
634 CreateDialogImpl(browser_context, modal_parent, NULL, base::string16(),
635 base::string16(), std::string(), true, callback);
636 }
637
CreateDialogForFileImpl(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::FilePath & path_to_file,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,bool delete_on_close)638 void CreateDialogForFileImpl(content::BrowserContext* browser_context,
639 gfx::NativeWindow modal_parent,
640 const base::FilePath& path_to_file,
641 const base::string16& print_job_title,
642 const base::string16& print_ticket,
643 const std::string& file_type,
644 bool delete_on_close) {
645 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
646 scoped_refptr<base::RefCountedMemory> data;
647 int64 file_size = 0;
648 if (base::GetFileSize(path_to_file, &file_size) && file_size != 0) {
649 std::string file_data;
650 if (file_size < kuint32max) {
651 file_data.reserve(static_cast<unsigned int>(file_size));
652 } else {
653 DLOG(WARNING) << " print data file too large to reserve space";
654 }
655 if (base::ReadFileToString(path_to_file, &file_data)) {
656 data = base::RefCountedString::TakeString(&file_data);
657 }
658 }
659 // Proceed even for empty data to simplify testing.
660 BrowserThread::PostTask(
661 BrowserThread::UI, FROM_HERE,
662 base::Bind(&print_dialog_cloud::CreatePrintDialogForBytes,
663 browser_context, modal_parent, data, print_job_title,
664 print_ticket, file_type));
665 if (delete_on_close)
666 base::DeleteFile(path_to_file, false);
667 }
668
669 } // namespace internal_cloud_print_helpers
670
671 namespace print_dialog_cloud {
672
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)673 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
674 registry->RegisterIntegerPref(
675 prefs::kCloudPrintDialogWidth,
676 kDefaultWidth,
677 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
678 registry->RegisterIntegerPref(
679 prefs::kCloudPrintDialogHeight,
680 kDefaultHeight,
681 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
682 }
683
684 // Called on the FILE or UI thread. This is the main entry point into creating
685 // the dialog.
686
CreatePrintDialogForFile(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::FilePath & path_to_file,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type,bool delete_on_close)687 void CreatePrintDialogForFile(content::BrowserContext* browser_context,
688 gfx::NativeWindow modal_parent,
689 const base::FilePath& path_to_file,
690 const base::string16& print_job_title,
691 const base::string16& print_ticket,
692 const std::string& file_type,
693 bool delete_on_close) {
694 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) ||
695 BrowserThread::CurrentlyOn(BrowserThread::UI));
696 BrowserThread::PostTask(
697 BrowserThread::FILE, FROM_HERE,
698 base::Bind(&internal_cloud_print_helpers::CreateDialogForFileImpl,
699 browser_context, modal_parent, path_to_file, print_job_title,
700 print_ticket, file_type, delete_on_close));
701 }
702
CreateCloudPrintSigninDialog(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::Closure & callback)703 void CreateCloudPrintSigninDialog(content::BrowserContext* browser_context,
704 gfx::NativeWindow modal_parent,
705 const base::Closure& callback) {
706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
707
708 BrowserThread::PostTask(
709 BrowserThread::UI, FROM_HERE,
710 base::Bind(&internal_cloud_print_helpers::CreateDialogSigninImpl,
711 browser_context, modal_parent, callback));
712 }
713
CreatePrintDialogForBytes(content::BrowserContext * browser_context,gfx::NativeWindow modal_parent,const base::RefCountedMemory * data,const base::string16 & print_job_title,const base::string16 & print_ticket,const std::string & file_type)714 void CreatePrintDialogForBytes(content::BrowserContext* browser_context,
715 gfx::NativeWindow modal_parent,
716 const base::RefCountedMemory* data,
717 const base::string16& print_job_title,
718 const base::string16& print_ticket,
719 const std::string& file_type) {
720 internal_cloud_print_helpers::CreateDialogImpl(browser_context, modal_parent,
721 data, print_job_title,
722 print_ticket, file_type, false,
723 base::Closure());
724 }
725
CreatePrintDialogFromCommandLine(Profile * profile,const CommandLine & command_line)726 bool CreatePrintDialogFromCommandLine(Profile* profile,
727 const CommandLine& command_line) {
728 DCHECK(command_line.HasSwitch(switches::kCloudPrintFile));
729 if (!command_line.GetSwitchValuePath(switches::kCloudPrintFile).empty()) {
730 base::FilePath cloud_print_file;
731 cloud_print_file =
732 command_line.GetSwitchValuePath(switches::kCloudPrintFile);
733 if (!cloud_print_file.empty()) {
734 base::string16 print_job_title;
735 base::string16 print_job_print_ticket;
736 if (command_line.HasSwitch(switches::kCloudPrintJobTitle)) {
737 print_job_title =
738 internal_cloud_print_helpers::GetSwitchValueString16(
739 command_line, switches::kCloudPrintJobTitle);
740 }
741 if (command_line.HasSwitch(switches::kCloudPrintPrintTicket)) {
742 print_job_print_ticket =
743 internal_cloud_print_helpers::GetSwitchValueString16(
744 command_line, switches::kCloudPrintPrintTicket);
745 }
746 std::string file_type = "application/pdf";
747 if (command_line.HasSwitch(switches::kCloudPrintFileType)) {
748 file_type = command_line.GetSwitchValueASCII(
749 switches::kCloudPrintFileType);
750 }
751
752 bool delete_on_close = CommandLine::ForCurrentProcess()->HasSwitch(
753 switches::kCloudPrintDeleteFile);
754
755 print_dialog_cloud::CreatePrintDialogForFile(
756 profile,
757 NULL,
758 cloud_print_file,
759 print_job_title,
760 print_job_print_ticket,
761 file_type,
762 delete_on_close);
763 return true;
764 }
765 }
766 return false;
767 }
768
769 } // namespace print_dialog_cloud
770