1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/printing/print_dialog_cloud.h"
6#include "chrome/browser/printing/print_dialog_cloud_internal.h"
7
8#include "base/base64.h"
9#include "base/command_line.h"
10#include "base/file_util.h"
11#include "base/json/json_reader.h"
12#include "base/values.h"
13#include "chrome/browser/debugger/devtools_manager.h"
14#include "chrome/browser/prefs/pref_service.h"
15#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/profiles/profile_manager.h"
18#include "chrome/browser/ui/browser_dialogs.h"
19#include "chrome/browser/ui/browser_list.h"
20#include "chrome/common/chrome_switches.h"
21#include "chrome/common/pref_names.h"
22#include "chrome/common/print_messages.h"
23#include "chrome/common/url_constants.h"
24#include "content/browser/browser_thread.h"
25#include "content/browser/renderer_host/render_view_host.h"
26#include "content/browser/tab_contents/tab_contents.h"
27#include "content/browser/tab_contents/tab_contents_view.h"
28#include "content/browser/webui/web_ui.h"
29#include "content/common/notification_registrar.h"
30#include "content/common/notification_source.h"
31#include "content/common/notification_type.h"
32#include "ui/base/l10n/l10n_util.h"
33#include "webkit/glue/webpreferences.h"
34
35#include "grit/generated_resources.h"
36
37// This module implements the UI support in Chrome for cloud printing.
38// This means hosting a dialog containing HTML/JavaScript and using
39// the published cloud print user interface integration APIs to get
40// page setup settings from the dialog contents and provide the
41// generated print data to the dialog contents for uploading to the
42// cloud print service.
43
44// Currently, the flow between these classes is as follows:
45
46// PrintDialogCloud::CreatePrintDialogForFile is called from
47// resource_message_filter_gtk.cc once the renderer has informed the
48// renderer host that print data generation into the renderer host provided
49// temp file has been completed.  That call is on the FILE thread.
50// That, in turn, hops over to the UI thread to create an instance of
51// PrintDialogCloud.
52
53// The constructor for PrintDialogCloud creates a
54// CloudPrintHtmlDialogDelegate and asks the current active browser to
55// show an HTML dialog using that class as the delegate. That class
56// hands in the kCloudPrintResourcesURL as the URL to visit.  That is
57// recognized by the GetWebUIFactoryFunction as a signal to create an
58// ExternalHtmlDialogUI.
59
60// CloudPrintHtmlDialogDelegate also temporarily owns a
61// CloudPrintFlowHandler, a class which is responsible for the actual
62// interactions with the dialog contents, including handing in the
63// print data and getting any page setup parameters that the dialog
64// contents provides.  As part of bringing up the dialog,
65// HtmlDialogUI::RenderViewCreated is called (an override of
66// WebUI::RenderViewCreated).  That routine, in turn, calls the
67// delegate's GetWebUIMessageHandlers routine, at which point the
68// ownership of the CloudPrintFlowHandler is handed over.  A pointer
69// to the flow handler is kept to facilitate communication back and
70// forth between the two classes.
71
72// The WebUI continues dialog bring-up, calling
73// CloudPrintFlowHandler::RegisterMessages.  This is where the
74// additional object model capabilities are registered for the dialog
75// contents to use.  It is also at this time that capabilities for the
76// dialog contents are adjusted to allow the dialog contents to close
77// the window.  In addition, the pending URL is redirected to the
78// actual cloud print service URL.  The flow controller also registers
79// for notification of when the dialog contents finish loading, which
80// is currently used to send the data to the dialog contents.
81
82// In order to send the data to the dialog contents, the flow
83// handler uses a CloudPrintDataSender.  It creates one, letting it
84// know the name of the temporary file containing the data, and
85// posts the task of reading the file
86// (CloudPrintDataSender::ReadPrintDataFile) to the file thread.  That
87// routine reads in the file, and then hops over to the IO thread to
88// send that data to the dialog contents.
89
90// When the dialog contents are finished (by either being cancelled or
91// hitting the print button), the delegate is notified, and responds
92// that the dialog should be closed, at which point things are torn
93// down and released.
94
95// TODO(scottbyer):
96// http://code.google.com/p/chromium/issues/detail?id=44093 The
97// high-level flow (where the data is generated before even
98// bringing up the dialog) isn't what we want.
99
100namespace internal_cloud_print_helpers {
101
102bool GetDoubleOrInt(const DictionaryValue& dictionary,
103                    const std::string& path,
104                    double* out_value) {
105  if (!dictionary.GetDouble(path, out_value)) {
106    int int_value = 0;
107    if (!dictionary.GetInteger(path, &int_value))
108      return false;
109    *out_value = int_value;
110  }
111  return true;
112}
113
114// From the JSON parsed value, get the entries for the page setup
115// parameters.
116bool GetPageSetupParameters(const std::string& json,
117                            PrintMsg_Print_Params& parameters) {
118  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
119  DLOG_IF(ERROR, (!parsed_value.get() ||
120                  !parsed_value->IsType(Value::TYPE_DICTIONARY)))
121      << "PageSetup call didn't have expected contents";
122  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
123    return false;
124
125  bool result = true;
126  DictionaryValue* params = static_cast<DictionaryValue*>(parsed_value.get());
127  result &= GetDoubleOrInt(*params, "dpi", &parameters.dpi);
128  result &= GetDoubleOrInt(*params, "min_shrink", &parameters.min_shrink);
129  result &= GetDoubleOrInt(*params, "max_shrink", &parameters.max_shrink);
130  result &= params->GetBoolean("selection_only", &parameters.selection_only);
131  return result;
132}
133
134void CloudPrintDataSenderHelper::CallJavascriptFunction(
135    const std::wstring& function_name) {
136  web_ui_->CallJavascriptFunction(WideToASCII(function_name));
137}
138
139void CloudPrintDataSenderHelper::CallJavascriptFunction(
140    const std::wstring& function_name, const Value& arg) {
141  web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg);
142}
143
144void CloudPrintDataSenderHelper::CallJavascriptFunction(
145    const std::wstring& function_name, const Value& arg1, const Value& arg2) {
146  web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg1, arg2);
147}
148
149// Clears out the pointer we're using to communicate.  Either routine is
150// potentially expensive enough that stopping whatever is in progress
151// is worth it.
152void CloudPrintDataSender::CancelPrintDataFile() {
153  base::AutoLock lock(lock_);
154  // We don't own helper, it was passed in to us, so no need to
155  // delete, just let it go.
156  helper_ = NULL;
157}
158
159CloudPrintDataSender::CloudPrintDataSender(CloudPrintDataSenderHelper* helper,
160                                           const string16& print_job_title,
161                                           const std::string& file_type)
162    : helper_(helper),
163      print_job_title_(print_job_title),
164      file_type_(file_type) {
165}
166
167CloudPrintDataSender::~CloudPrintDataSender() {}
168
169// Grab the raw file contents and massage them into shape for
170// sending to the dialog contents (and up to the cloud print server)
171// by encoding it and prefixing it with the appropriate mime type.
172// Once that is done, kick off the next part of the task on the IO
173// thread.
174void CloudPrintDataSender::ReadPrintDataFile(const FilePath& path_to_file) {
175  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
176  int64 file_size = 0;
177  if (file_util::GetFileSize(path_to_file, &file_size) && file_size != 0) {
178    std::string file_data;
179    if (file_size < kuint32max) {
180      file_data.reserve(static_cast<unsigned int>(file_size));
181    } else {
182      DLOG(WARNING) << " print data file too large to reserve space";
183    }
184    if (helper_ && file_util::ReadFileToString(path_to_file, &file_data)) {
185      std::string base64_data;
186      base::Base64Encode(file_data, &base64_data);
187      std::string header("data:");
188      header.append(file_type_);
189      header.append(";base64,");
190      base64_data.insert(0, header);
191      scoped_ptr<StringValue> new_data(new StringValue(base64_data));
192      print_data_.swap(new_data);
193      BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
194                              NewRunnableMethod(
195                                  this,
196                                  &CloudPrintDataSender::SendPrintDataFile));
197    }
198  }
199}
200
201// We have the data in hand that needs to be pushed into the dialog
202// contents; do so from the IO thread.
203
204// TODO(scottbyer): If the print data ends up being larger than the
205// upload limit (currently 10MB), what we need to do is upload that
206// large data to google docs and set the URL in the printing
207// JavaScript to that location, and make sure it gets deleted when not
208// needed. - 4/1/2010
209void CloudPrintDataSender::SendPrintDataFile() {
210  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211  base::AutoLock lock(lock_);
212  if (helper_ && print_data_.get()) {
213    StringValue title(print_job_title_);
214
215    // Send the print data to the dialog contents.  The JavaScript
216    // function is a preliminary API for prototyping purposes and is
217    // subject to change.
218    const_cast<CloudPrintDataSenderHelper*>(helper_)->CallJavascriptFunction(
219        L"printApp._printDataUrl", *print_data_, title);
220  }
221}
222
223
224CloudPrintFlowHandler::CloudPrintFlowHandler(const FilePath& path_to_file,
225                                             const string16& print_job_title,
226                                             const std::string& file_type)
227    : path_to_file_(path_to_file),
228      print_job_title_(print_job_title),
229      file_type_(file_type) {
230}
231
232CloudPrintFlowHandler::~CloudPrintFlowHandler() {
233  // This will also cancel any task in flight.
234  CancelAnyRunningTask();
235}
236
237
238void CloudPrintFlowHandler::SetDialogDelegate(
239    CloudPrintHtmlDialogDelegate* delegate) {
240  // Even if setting a new WebUI, it means any previous task needs
241  // to be cancelled, it's now invalid.
242  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243  CancelAnyRunningTask();
244  dialog_delegate_ = delegate;
245}
246
247// Cancels any print data sender we have in flight and removes our
248// reference to it, so when the task that is calling it finishes and
249// removes it's reference, it goes away.
250void CloudPrintFlowHandler::CancelAnyRunningTask() {
251  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252  if (print_data_sender_.get()) {
253    print_data_sender_->CancelPrintDataFile();
254    print_data_sender_ = NULL;
255  }
256}
257
258void CloudPrintFlowHandler::RegisterMessages() {
259  if (!web_ui_)
260    return;
261
262  // TODO(scottbyer) - This is where we will register messages for the
263  // UI JS to use.  Needed: Call to update page setup parameters.
264  web_ui_->RegisterMessageCallback(
265      "ShowDebugger",
266      NewCallback(this, &CloudPrintFlowHandler::HandleShowDebugger));
267  web_ui_->RegisterMessageCallback(
268      "SendPrintData",
269      NewCallback(this, &CloudPrintFlowHandler::HandleSendPrintData));
270  web_ui_->RegisterMessageCallback(
271      "SetPageParameters",
272      NewCallback(this, &CloudPrintFlowHandler::HandleSetPageParameters));
273
274  if (web_ui_->tab_contents()) {
275    // Also, take the opportunity to set some (minimal) additional
276    // script permissions required for the web UI.
277
278    // TODO(scottbyer): learn how to make sure we're talking to the
279    // right web site first.
280    RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
281    if (rvh && rvh->delegate()) {
282      WebPreferences webkit_prefs = rvh->delegate()->GetWebkitPrefs();
283      webkit_prefs.allow_scripts_to_close_windows = true;
284      rvh->UpdateWebPreferences(webkit_prefs);
285    }
286
287    // Register for appropriate notifications, and re-direct the URL
288    // to the real server URL, now that we've gotten an HTML dialog
289    // going.
290    NavigationController* controller = &web_ui_->tab_contents()->controller();
291    NavigationEntry* pending_entry = controller->pending_entry();
292    if (pending_entry)
293      pending_entry->set_url(CloudPrintURL(
294          web_ui_->GetProfile()).GetCloudPrintServiceDialogURL());
295    registrar_.Add(this, NotificationType::LOAD_STOP,
296                   Source<NavigationController>(controller));
297  }
298}
299
300void CloudPrintFlowHandler::Observe(NotificationType type,
301                                    const NotificationSource& source,
302                                    const NotificationDetails& details) {
303  if (type == NotificationType::LOAD_STOP) {
304    // Choose one or the other.  If you need to debug, bring up the
305    // debugger.  You can then use the various chrome.send()
306    // registrations above to kick of the various function calls,
307    // including chrome.send("SendPrintData") in the javaScript
308    // console and watch things happen with:
309    // HandleShowDebugger(NULL);
310    HandleSendPrintData(NULL);
311  }
312}
313
314void CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) {
315  ShowDebugger();
316}
317
318void CloudPrintFlowHandler::ShowDebugger() {
319  if (web_ui_) {
320    RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
321    if (rvh)
322      DevToolsManager::GetInstance()->OpenDevToolsWindow(rvh);
323  }
324}
325
326scoped_refptr<CloudPrintDataSender>
327CloudPrintFlowHandler::CreateCloudPrintDataSender() {
328  DCHECK(web_ui_);
329  print_data_helper_.reset(new CloudPrintDataSenderHelper(web_ui_));
330  return new CloudPrintDataSender(print_data_helper_.get(),
331                                  print_job_title_,
332                                  file_type_);
333}
334
335void CloudPrintFlowHandler::HandleSendPrintData(const ListValue* args) {
336  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337  // This will cancel any ReadPrintDataFile() or SendPrintDataFile()
338  // requests in flight (this is anticipation of when setting page
339  // setup parameters becomes asynchronous and may be set while some
340  // data is in flight).  Then we can clear out the print data.
341  CancelAnyRunningTask();
342  if (web_ui_) {
343    print_data_sender_ = CreateCloudPrintDataSender();
344    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
345                            NewRunnableMethod(
346                                print_data_sender_.get(),
347                                &CloudPrintDataSender::ReadPrintDataFile,
348                                path_to_file_));
349  }
350}
351
352void CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) {
353  std::string json;
354  args->GetString(0, &json);
355  if (json.empty()) {
356    NOTREACHED() << "Empty json string";
357    return;
358  }
359
360  // These are backstop default values - 72 dpi to match the screen,
361  // 8.5x11 inch paper with margins subtracted (1/4 inch top, left,
362  // right and 0.56 bottom), and the min page shrink and max page
363  // shrink values appear all over the place with no explanation.
364
365  // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings
366  // working so that we can get the default values from there.  Fix up
367  // PrintWebViewHelper to do the same.
368  const int kDPI = 72;
369  const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI);
370  const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI);
371  const double kMinPageShrink = 1.25;
372  const double kMaxPageShrink = 2.0;
373
374  PrintMsg_Print_Params default_settings;
375  default_settings.printable_size = gfx::Size(kWidth, kHeight);
376  default_settings.dpi = kDPI;
377  default_settings.min_shrink = kMinPageShrink;
378  default_settings.max_shrink = kMaxPageShrink;
379  default_settings.desired_dpi = kDPI;
380  default_settings.document_cookie = 0;
381  default_settings.selection_only = false;
382
383  if (!GetPageSetupParameters(json, default_settings)) {
384    NOTREACHED();
385    return;
386  }
387
388  // TODO(scottbyer) - Here is where we would kick the originating
389  // renderer thread with these new parameters in order to get it to
390  // re-generate the PDF data and hand it back to us.  window.print() is
391  // currently synchronous, so there's a lot of work to do to get to
392  // that point.
393}
394
395void CloudPrintFlowHandler::StoreDialogClientSize() const {
396  if (web_ui_ && web_ui_->tab_contents() && web_ui_->tab_contents()->view()) {
397    gfx::Size size = web_ui_->tab_contents()->view()->GetContainerSize();
398    web_ui_->GetProfile()->GetPrefs()->SetInteger(
399        prefs::kCloudPrintDialogWidth, size.width());
400    web_ui_->GetProfile()->GetPrefs()->SetInteger(
401        prefs::kCloudPrintDialogHeight, size.height());
402  }
403}
404
405CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
406    const FilePath& path_to_file,
407    int width, int height,
408    const std::string& json_arguments,
409    const string16& print_job_title,
410    const std::string& file_type,
411    bool modal)
412    : flow_handler_(new CloudPrintFlowHandler(path_to_file,
413                                              print_job_title,
414                                              file_type)),
415      modal_(modal),
416      owns_flow_handler_(true) {
417  Init(width, height, json_arguments);
418}
419
420// For unit testing.
421CloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate(
422    CloudPrintFlowHandler* flow_handler,
423    int width, int height,
424    const std::string& json_arguments,
425    bool modal)
426    : flow_handler_(flow_handler),
427      modal_(modal),
428      owns_flow_handler_(true) {
429  Init(width, height, json_arguments);
430}
431
432void CloudPrintHtmlDialogDelegate::Init(int width, int height,
433                                        const std::string& json_arguments) {
434  // This information is needed to show the dialog HTML content.
435  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
436  params_.url = GURL(chrome::kCloudPrintResourcesURL);
437  params_.height = height;
438  params_.width = width;
439  params_.json_input = json_arguments;
440
441  flow_handler_->SetDialogDelegate(this);
442  // If we're not modal we can show the dialog with no browser.
443  // We need this to keep Chrome alive while our dialog is up.
444  if (!modal_)
445    BrowserList::StartKeepAlive();
446}
447
448CloudPrintHtmlDialogDelegate::~CloudPrintHtmlDialogDelegate() {
449  // If the flow_handler_ is about to outlive us because we don't own
450  // it anymore, we need to have it remove it's reference to us.
451  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
452  flow_handler_->SetDialogDelegate(NULL);
453  if (owns_flow_handler_) {
454    delete flow_handler_;
455  }
456}
457
458bool CloudPrintHtmlDialogDelegate::IsDialogModal() const {
459    return modal_;
460}
461
462std::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const {
463  return std::wstring();
464}
465
466GURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const {
467  return params_.url;
468}
469
470void CloudPrintHtmlDialogDelegate::GetWebUIMessageHandlers(
471    std::vector<WebUIMessageHandler*>* handlers) const {
472  handlers->push_back(flow_handler_);
473  // We don't own flow_handler_ anymore, but it sticks around until at
474  // least right after OnDialogClosed() is called (and this object is
475  // destroyed).
476  owns_flow_handler_ = false;
477}
478
479void CloudPrintHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const {
480  size->set_width(params_.width);
481  size->set_height(params_.height);
482}
483
484std::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const {
485  return params_.json_input;
486}
487
488void CloudPrintHtmlDialogDelegate::OnDialogClosed(
489    const std::string& json_retval) {
490  // Get the final dialog size and store it.
491  flow_handler_->StoreDialogClientSize();
492  // If we're modal we can show the dialog with no browser.
493  // End the keep-alive so that Chrome can exit.
494  if (!modal_)
495    BrowserList::EndKeepAlive();
496  delete this;
497}
498
499void CloudPrintHtmlDialogDelegate::OnCloseContents(TabContents* source,
500                                                   bool* out_close_dialog) {
501  if (out_close_dialog)
502    *out_close_dialog = true;
503}
504
505bool CloudPrintHtmlDialogDelegate::ShouldShowDialogTitle() const {
506  return false;
507}
508
509bool CloudPrintHtmlDialogDelegate::HandleContextMenu(
510    const ContextMenuParams& params) {
511  return true;
512}
513
514// Called from the UI thread, starts up the dialog.
515void CreateDialogImpl(const FilePath& path_to_file,
516                      const string16& print_job_title,
517                      const std::string& file_type,
518                      bool modal) {
519  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
520  Browser* browser = BrowserList::GetLastActive();
521
522  const int kDefaultWidth = 497;
523  const int kDefaultHeight = 332;
524  string16 job_title = print_job_title;
525  Profile* profile = NULL;
526  if (modal) {
527    DCHECK(browser);
528    if (job_title.empty() && browser->GetSelectedTabContents())
529      job_title = browser->GetSelectedTabContents()->GetTitle();
530    profile = browser->GetProfile();
531  } else {
532    profile = ProfileManager::GetDefaultProfile();
533  }
534  DCHECK(profile);
535  PrefService* pref_service = profile->GetPrefs();
536  DCHECK(pref_service);
537  if (!pref_service->FindPreference(prefs::kCloudPrintDialogWidth)) {
538    pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogWidth,
539                                      kDefaultWidth);
540  }
541  if (!pref_service->FindPreference(prefs::kCloudPrintDialogHeight)) {
542    pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogHeight,
543                                      kDefaultHeight);
544  }
545
546  int width = pref_service->GetInteger(prefs::kCloudPrintDialogWidth);
547  int height = pref_service->GetInteger(prefs::kCloudPrintDialogHeight);
548
549  HtmlDialogUIDelegate* dialog_delegate =
550      new internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate(
551          path_to_file, width, height, std::string(), job_title, file_type,
552          modal);
553  if (modal) {
554    DCHECK(browser);
555    browser->BrowserShowHtmlDialog(dialog_delegate, NULL);
556  } else {
557    browser::ShowHtmlDialog(NULL, profile, dialog_delegate);
558  }
559}
560
561}  // namespace internal_cloud_print_helpers
562
563namespace print_dialog_cloud {
564
565// Called on the FILE or UI thread.  This is the main entry point into creating
566// the dialog.
567
568// TODO(scottbyer): The signature here will need to change as the
569// workflow through the printing code changes to allow for dynamically
570// changing page setup parameters while the dialog is active.
571void CreatePrintDialogForFile(const FilePath& path_to_file,
572                              const string16& print_job_title,
573                              const std::string& file_type,
574                              bool modal) {
575  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) ||
576         BrowserThread::CurrentlyOn(BrowserThread::UI));
577
578  BrowserThread::PostTask(
579      BrowserThread::UI, FROM_HERE,
580      NewRunnableFunction(&internal_cloud_print_helpers::CreateDialogImpl,
581                          path_to_file,
582                          print_job_title,
583                          file_type,
584                          modal));
585}
586
587bool CreatePrintDialogFromCommandLine(const CommandLine& command_line) {
588  if (!command_line.GetSwitchValuePath(switches::kCloudPrintFile).empty()) {
589    FilePath cloud_print_file;
590    cloud_print_file =
591        command_line.GetSwitchValuePath(switches::kCloudPrintFile);
592    if (!cloud_print_file.empty()) {
593      string16 print_job_title;
594      if (command_line.HasSwitch(switches::kCloudPrintJobTitle)) {
595#ifdef OS_WIN
596        CommandLine::StringType native_job_title;
597        native_job_title = command_line.GetSwitchValueNative(
598            switches::kCloudPrintJobTitle);
599        print_job_title = string16(native_job_title);
600#elif defined(OS_POSIX)
601        // TODO(abodenha@chromium.org) Implement this for OS_POSIX
602        // Command line string types are different
603#endif
604      }
605      std::string file_type = "application/pdf";
606      if (command_line.HasSwitch(switches::kCloudPrintFileType)) {
607        file_type = command_line.GetSwitchValueASCII(
608            switches::kCloudPrintFileType);
609      }
610      print_dialog_cloud::CreatePrintDialogForFile(cloud_print_file,
611                                                   print_job_title,
612                                                   file_type,
613                                                   false);
614      return true;
615    }
616  }
617  return false;
618}
619
620}  // end namespace
621