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 100 namespace internal_cloud_print_helpers { 101 102 bool 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. 116 bool 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", ¶meters.dpi); 128 result &= GetDoubleOrInt(*params, "min_shrink", ¶meters.min_shrink); 129 result &= GetDoubleOrInt(*params, "max_shrink", ¶meters.max_shrink); 130 result &= params->GetBoolean("selection_only", ¶meters.selection_only); 131 return result; 132 } 133 134 void CloudPrintDataSenderHelper::CallJavascriptFunction( 135 const std::wstring& function_name) { 136 web_ui_->CallJavascriptFunction(WideToASCII(function_name)); 137 } 138 139 void CloudPrintDataSenderHelper::CallJavascriptFunction( 140 const std::wstring& function_name, const Value& arg) { 141 web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg); 142 } 143 144 void 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. 152 void 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 159 CloudPrintDataSender::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 167 CloudPrintDataSender::~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. 174 void 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 209 void 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 224 CloudPrintFlowHandler::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 232 CloudPrintFlowHandler::~CloudPrintFlowHandler() { 233 // This will also cancel any task in flight. 234 CancelAnyRunningTask(); 235 } 236 237 238 void 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. 250 void 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 258 void 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 300 void 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 314 void CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) { 315 ShowDebugger(); 316 } 317 318 void 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 326 scoped_refptr<CloudPrintDataSender> 327 CloudPrintFlowHandler::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 335 void 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 352 void 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 395 void 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 405 CloudPrintHtmlDialogDelegate::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. 421 CloudPrintHtmlDialogDelegate::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 432 void 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 448 CloudPrintHtmlDialogDelegate::~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 458 bool CloudPrintHtmlDialogDelegate::IsDialogModal() const { 459 return modal_; 460 } 461 462 std::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const { 463 return std::wstring(); 464 } 465 466 GURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const { 467 return params_.url; 468 } 469 470 void 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 479 void CloudPrintHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const { 480 size->set_width(params_.width); 481 size->set_height(params_.height); 482 } 483 484 std::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const { 485 return params_.json_input; 486 } 487 488 void 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 499 void CloudPrintHtmlDialogDelegate::OnCloseContents(TabContents* source, 500 bool* out_close_dialog) { 501 if (out_close_dialog) 502 *out_close_dialog = true; 503 } 504 505 bool CloudPrintHtmlDialogDelegate::ShouldShowDialogTitle() const { 506 return false; 507 } 508 509 bool CloudPrintHtmlDialogDelegate::HandleContextMenu( 510 const ContextMenuParams& params) { 511 return true; 512 } 513 514 // Called from the UI thread, starts up the dialog. 515 void 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 563 namespace 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. 571 void 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 587 bool 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 (at) 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