• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", &parameters.dpi);
134   result &= params->GetDouble("min_shrink", &parameters.min_shrink);
135   result &= params->GetDouble("max_shrink", &parameters.max_shrink);
136   result &= params->GetBoolean("selection_only", &parameters.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                                    &params_.width,
509                                    &params_.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