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