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/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/common/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 110 using content::BrowserThread; 111 using content::NavigationController; 112 using content::NavigationEntry; 113 using content::RenderViewHost; 114 using content::WebContents; 115 using content::WebUIMessageHandler; 116 using ui::WebDialogDelegate; 117 118 const int kDefaultWidth = 912; 119 const int kDefaultHeight = 633; 120 121 namespace internal_cloud_print_helpers { 122 123 // From the JSON parsed value, get the entries for the page setup 124 // parameters. 125 bool 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", ¶meters.dpi); 137 result &= params->GetDouble("min_shrink", ¶meters.min_shrink); 138 result &= params->GetDouble("max_shrink", ¶meters.max_shrink); 139 result &= params->GetBoolean("selection_only", ¶meters.selection_only); 140 return result; 141 } 142 143 string16 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 158 void CloudPrintDataSenderHelper::CallJavascriptFunction( 159 const std::wstring& function_name) { 160 web_ui_->CallJavascriptFunction(WideToASCII(function_name)); 161 } 162 163 void CloudPrintDataSenderHelper::CallJavascriptFunction( 164 const std::wstring& function_name, const Value& arg) { 165 web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg); 166 } 167 168 void 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 173 void 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. 185 void 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 192 CloudPrintDataSender::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 204 CloudPrintDataSender::~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 214 void CloudPrintDataSender::SendPrintData() { 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 216 if (!data_.get() || !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 std::string header("data:"); 225 header.append(file_type_); 226 header.append(";base64,"); 227 base64_data.insert(0, header); 228 229 base::AutoLock lock(lock_); 230 if (helper_) { 231 StringValue title(print_job_title_); 232 StringValue ticket(print_ticket_); 233 // TODO(abodenha): Change Javascript call to pass in print ticket 234 // after server side support is added. Add test for it. 235 236 // Send the print data to the dialog contents. The JavaScript 237 // function is a preliminary API for prototyping purposes and is 238 // subject to change. 239 const_cast<CloudPrintDataSenderHelper*>(helper_)->CallJavascriptFunction( 240 L"printApp._printDataUrl", StringValue(base64_data), title); 241 } 242 } 243 244 245 CloudPrintFlowHandler::CloudPrintFlowHandler(const base::RefCountedMemory* data, 246 const string16& print_job_title, 247 const string16& print_ticket, 248 const std::string& file_type, 249 bool close_after_signin, 250 const base::Closure& callback) 251 : dialog_delegate_(NULL), 252 data_(data), 253 print_job_title_(print_job_title), 254 print_ticket_(print_ticket), 255 file_type_(file_type), 256 close_after_signin_(close_after_signin), 257 callback_(callback) { 258 } 259 260 CloudPrintFlowHandler::~CloudPrintFlowHandler() { 261 // This will also cancel any task in flight. 262 CancelAnyRunningTask(); 263 } 264 265 266 void CloudPrintFlowHandler::SetDialogDelegate( 267 CloudPrintWebDialogDelegate* delegate) { 268 // Even if setting a new WebUI, it means any previous task needs 269 // to be canceled, its now invalid. 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 271 CancelAnyRunningTask(); 272 dialog_delegate_ = delegate; 273 } 274 275 // Cancels any print data sender we have in flight and removes our 276 // reference to it, so when the task that is calling it finishes and 277 // removes its reference, it goes away. 278 void CloudPrintFlowHandler::CancelAnyRunningTask() { 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 280 if (print_data_sender_.get()) { 281 print_data_sender_->CancelPrintDataFile(); 282 print_data_sender_ = NULL; 283 } 284 } 285 286 void CloudPrintFlowHandler::RegisterMessages() { 287 // TODO(scottbyer) - This is where we will register messages for the 288 // UI JS to use. Needed: Call to update page setup parameters. 289 web_ui()->RegisterMessageCallback("ShowDebugger", 290 base::Bind(&CloudPrintFlowHandler::HandleShowDebugger, 291 base::Unretained(this))); 292 web_ui()->RegisterMessageCallback("SendPrintData", 293 base::Bind(&CloudPrintFlowHandler::HandleSendPrintData, 294 base::Unretained(this))); 295 web_ui()->RegisterMessageCallback("SetPageParameters", 296 base::Bind(&CloudPrintFlowHandler::HandleSetPageParameters, 297 base::Unretained(this))); 298 299 // Register for appropriate notifications, and re-direct the URL 300 // to the real server URL, now that we've gotten an HTML dialog 301 // going. 302 NavigationController* controller = 303 &web_ui()->GetWebContents()->GetController(); 304 NavigationEntry* pending_entry = controller->GetPendingEntry(); 305 if (pending_entry) { 306 Profile* profile = Profile::FromWebUI(web_ui()); 307 if (close_after_signin_) { 308 pending_entry->SetURL( 309 CloudPrintURL(profile).GetCloudPrintSigninURL()); 310 } else { 311 pending_entry->SetURL( 312 CloudPrintURL(profile).GetCloudPrintServiceDialogURL()); 313 } 314 } 315 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, 316 content::Source<NavigationController>(controller)); 317 registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, 318 content::Source<NavigationController>(controller)); 319 } 320 321 void CloudPrintFlowHandler::Observe( 322 int type, 323 const content::NotificationSource& source, 324 const content::NotificationDetails& details) { 325 switch (type) { 326 case content::NOTIFICATION_NAV_ENTRY_COMMITTED: { 327 NavigationEntry* entry = 328 web_ui()->GetWebContents()->GetController().GetActiveEntry(); 329 if (entry) 330 NavigationToURLDidCloseDialog(entry->GetURL()); 331 break; 332 } 333 case content::NOTIFICATION_LOAD_STOP: { 334 GURL url = web_ui()->GetWebContents()->GetURL(); 335 if (IsCloudPrintDialogUrl(url)) { 336 // Take the opportunity to set some (minimal) additional 337 // script permissions required for the web UI. 338 RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost(); 339 if (rvh) { 340 WebPreferences webkit_prefs = rvh->GetWebkitPreferences(); 341 webkit_prefs.allow_scripts_to_close_windows = true; 342 rvh->UpdateWebkitPreferences(webkit_prefs); 343 } else { 344 NOTREACHED(); 345 } 346 // Choose one or the other. If you need to debug, bring up the 347 // debugger. You can then use the various chrome.send() 348 // registrations above to kick of the various function calls, 349 // including chrome.send("SendPrintData") in the javaScript 350 // console and watch things happen with: 351 // HandleShowDebugger(NULL); 352 HandleSendPrintData(NULL); 353 } 354 break; 355 } 356 } 357 } 358 359 void CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) { 360 ShowDebugger(); 361 } 362 363 void CloudPrintFlowHandler::ShowDebugger() { 364 if (web_ui()) { 365 RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost(); 366 if (rvh) 367 DevToolsWindow::OpenDevToolsWindow(rvh); 368 } 369 } 370 371 scoped_refptr<CloudPrintDataSender> 372 CloudPrintFlowHandler::CreateCloudPrintDataSender() { 373 DCHECK(web_ui()); 374 print_data_helper_.reset(new CloudPrintDataSenderHelper(web_ui())); 375 scoped_refptr<CloudPrintDataSender> sender( 376 new CloudPrintDataSender(print_data_helper_.get(), 377 print_job_title_, 378 print_ticket_, 379 file_type_, 380 data_.get())); 381 return sender; 382 } 383 384 void CloudPrintFlowHandler::HandleSendPrintData(const ListValue* args) { 385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 386 // This will cancel any ReadPrintDataFile() or SendPrintDataFile() 387 // requests in flight (this is anticipation of when setting page 388 // setup parameters becomes asynchronous and may be set while some 389 // data is in flight). Then we can clear out the print data. 390 CancelAnyRunningTask(); 391 if (web_ui()) { 392 print_data_sender_ = CreateCloudPrintDataSender(); 393 BrowserThread::PostTask( 394 BrowserThread::IO, FROM_HERE, 395 base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender_)); 396 } 397 } 398 399 void CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) { 400 std::string json; 401 bool ret = args->GetString(0, &json); 402 if (!ret || json.empty()) { 403 NOTREACHED() << "Empty json string"; 404 return; 405 } 406 407 // These are backstop default values - 72 dpi to match the screen, 408 // 8.5x11 inch paper with margins subtracted (1/4 inch top, left, 409 // right and 0.56 bottom), and the min page shrink and max page 410 // shrink values appear all over the place with no explanation. 411 412 // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings 413 // working so that we can get the default values from there. Fix up 414 // PrintWebViewHelper to do the same. 415 const int kDPI = 72; 416 const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI); 417 const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI); 418 const double kMinPageShrink = 1.25; 419 const double kMaxPageShrink = 2.0; 420 421 PrintMsg_Print_Params default_settings; 422 default_settings.content_size = gfx::Size(kWidth, kHeight); 423 default_settings.printable_area = gfx::Rect(0, 0, kWidth, kHeight); 424 default_settings.dpi = kDPI; 425 default_settings.min_shrink = kMinPageShrink; 426 default_settings.max_shrink = kMaxPageShrink; 427 default_settings.desired_dpi = kDPI; 428 default_settings.document_cookie = 0; 429 default_settings.selection_only = false; 430 default_settings.preview_request_id = 0; 431 default_settings.is_first_request = true; 432 default_settings.print_to_pdf = false; 433 434 if (!GetPageSetupParameters(json, default_settings)) { 435 NOTREACHED(); 436 return; 437 } 438 439 // TODO(scottbyer) - Here is where we would kick the originating 440 // renderer thread with these new parameters in order to get it to 441 // re-generate the PDF data and hand it back to us. window.print() is 442 // currently synchronous, so there's a lot of work to do to get to 443 // that point. 444 } 445 446 void CloudPrintFlowHandler::StoreDialogClientSize() const { 447 if (web_ui() && web_ui()->GetWebContents() && 448 web_ui()->GetWebContents()->GetView()) { 449 gfx::Size size = web_ui()->GetWebContents()->GetView()->GetContainerSize(); 450 Profile* profile = Profile::FromWebUI(web_ui()); 451 profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogWidth, 452 size.width()); 453 profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogHeight, 454 size.height()); 455 } 456 } 457 458 bool CloudPrintFlowHandler::NavigationToURLDidCloseDialog(const GURL& url) { 459 if (close_after_signin_) { 460 if (IsCloudPrintDialogUrl(url)) { 461 StoreDialogClientSize(); 462 web_ui()->GetWebContents()->GetRenderViewHost()->ClosePage(); 463 callback_.Run(); 464 return true; 465 } 466 } 467 return false; 468 } 469 470 bool CloudPrintFlowHandler::IsCloudPrintDialogUrl(const GURL& url) { 471 GURL cloud_print_url = 472 CloudPrintURL(Profile::FromWebUI(web_ui())).GetCloudPrintServiceURL(); 473 return url.host() == cloud_print_url.host() && 474 StartsWithASCII(url.path(), cloud_print_url.path(), false) && 475 url.scheme() == cloud_print_url.scheme(); 476 } 477 478 CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate( 479 content::BrowserContext* browser_context, 480 gfx::NativeWindow modal_parent, 481 const base::RefCountedMemory* data, 482 const std::string& json_arguments, 483 const string16& print_job_title, 484 const string16& print_ticket, 485 const std::string& file_type, 486 bool close_after_signin, 487 const base::Closure& callback) 488 : flow_handler_( 489 new CloudPrintFlowHandler(data, print_job_title, print_ticket, 490 file_type, close_after_signin, callback)), 491 modal_parent_(modal_parent), 492 owns_flow_handler_(true), 493 keep_alive_when_non_modal_(true) { 494 Init(browser_context, json_arguments); 495 } 496 497 // For unit testing. 498 CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate( 499 CloudPrintFlowHandler* flow_handler, 500 const std::string& json_arguments) 501 : flow_handler_(flow_handler), 502 modal_parent_(NULL), 503 owns_flow_handler_(true), 504 keep_alive_when_non_modal_(false) { 505 Init(NULL, json_arguments); 506 } 507 508 // Returns the persisted width/height for the print dialog. 509 void GetDialogWidthAndHeightFromPrefs(content::BrowserContext* browser_context, 510 int* width, 511 int* height) { 512 if (!browser_context) { 513 *width = kDefaultWidth; 514 *height = kDefaultHeight; 515 return; 516 } 517 518 PrefService* prefs = Profile::FromBrowserContext(browser_context)->GetPrefs(); 519 *width = prefs->GetInteger(prefs::kCloudPrintDialogWidth); 520 *height = prefs->GetInteger(prefs::kCloudPrintDialogHeight); 521 } 522 523 void CloudPrintWebDialogDelegate::Init(content::BrowserContext* browser_context, 524 const std::string& json_arguments) { 525 // This information is needed to show the dialog HTML content. 526 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 527 528 params_.url = GURL(chrome::kChromeUICloudPrintResourcesURL); 529 GetDialogWidthAndHeightFromPrefs(browser_context, 530 ¶ms_.width, 531 ¶ms_.height); 532 params_.json_input = json_arguments; 533 534 flow_handler_->SetDialogDelegate(this); 535 // If we're not modal we can show the dialog with no browser. 536 // We need this to keep Chrome alive while our dialog is up. 537 if (!modal_parent_ && keep_alive_when_non_modal_) 538 chrome::StartKeepAlive(); 539 } 540 541 CloudPrintWebDialogDelegate::~CloudPrintWebDialogDelegate() { 542 // If the flow_handler_ is about to outlive us because we don't own 543 // it anymore, we need to have it remove its reference to us. 544 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 545 flow_handler_->SetDialogDelegate(NULL); 546 if (owns_flow_handler_) { 547 delete flow_handler_; 548 } 549 } 550 551 ui::ModalType CloudPrintWebDialogDelegate::GetDialogModalType() const { 552 return modal_parent_ ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE; 553 } 554 555 string16 CloudPrintWebDialogDelegate::GetDialogTitle() const { 556 return string16(); 557 } 558 559 GURL CloudPrintWebDialogDelegate::GetDialogContentURL() const { 560 return params_.url; 561 } 562 563 void CloudPrintWebDialogDelegate::GetWebUIMessageHandlers( 564 std::vector<WebUIMessageHandler*>* handlers) const { 565 handlers->push_back(flow_handler_); 566 // We don't own flow_handler_ anymore, but it sticks around until at 567 // least right after OnDialogClosed() is called (and this object is 568 // destroyed). 569 owns_flow_handler_ = false; 570 } 571 572 void CloudPrintWebDialogDelegate::GetDialogSize(gfx::Size* size) const { 573 size->set_width(params_.width); 574 size->set_height(params_.height); 575 } 576 577 std::string CloudPrintWebDialogDelegate::GetDialogArgs() const { 578 return params_.json_input; 579 } 580 581 void CloudPrintWebDialogDelegate::OnDialogClosed( 582 const std::string& json_retval) { 583 // Get the final dialog size and store it. 584 flow_handler_->StoreDialogClientSize(); 585 586 // If we're modal we can show the dialog with no browser. 587 // End the keep-alive so that Chrome can exit. 588 if (!modal_parent_ && keep_alive_when_non_modal_) 589 chrome::EndKeepAlive(); 590 delete this; 591 } 592 593 void CloudPrintWebDialogDelegate::OnCloseContents(WebContents* source, 594 bool* out_close_dialog) { 595 if (out_close_dialog) 596 *out_close_dialog = true; 597 } 598 599 bool CloudPrintWebDialogDelegate::ShouldShowDialogTitle() const { 600 return false; 601 } 602 603 bool CloudPrintWebDialogDelegate::HandleContextMenu( 604 const content::ContextMenuParams& params) { 605 return true; 606 } 607 608 bool CloudPrintWebDialogDelegate::HandleOpenURLFromTab( 609 content::WebContents* source, 610 const content::OpenURLParams& params, 611 content::WebContents** out_new_contents) { 612 return flow_handler_->NavigationToURLDidCloseDialog(params.url); 613 } 614 615 // Called from the UI thread, starts up the dialog. 616 void CreateDialogImpl(content::BrowserContext* browser_context, 617 gfx::NativeWindow modal_parent, 618 const base::RefCountedMemory* data, 619 const string16& print_job_title, 620 const string16& print_ticket, 621 const std::string& file_type, 622 bool close_after_signin, 623 const base::Closure& callback) { 624 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 625 WebDialogDelegate* dialog_delegate = 626 new internal_cloud_print_helpers::CloudPrintWebDialogDelegate( 627 browser_context, modal_parent, data, std::string(), print_job_title, 628 print_ticket, file_type, close_after_signin, callback); 629 #if defined(OS_WIN) 630 gfx::NativeWindow window = 631 #endif 632 chrome::ShowWebDialog(modal_parent, 633 Profile::FromBrowserContext(browser_context), 634 dialog_delegate); 635 #if defined(OS_WIN) 636 if (window) { 637 HWND dialog_handle; 638 #if defined(USE_AURA) 639 dialog_handle = window->GetRootWindow()->GetAcceleratedWidget(); 640 #else 641 dialog_handle = window; 642 #endif 643 if (::GetForegroundWindow() != dialog_handle) { 644 ui::ForegroundHelper::SetForeground(dialog_handle); 645 } 646 } 647 #endif 648 } 649 650 void CreateDialogSigninImpl(content::BrowserContext* browser_context, 651 gfx::NativeWindow modal_parent, 652 const base::Closure& callback) { 653 CreateDialogImpl(browser_context, modal_parent, NULL, string16(), 654 string16(), std::string(), true, callback); 655 } 656 657 void CreateDialogForFileImpl(content::BrowserContext* browser_context, 658 gfx::NativeWindow modal_parent, 659 const base::FilePath& path_to_file, 660 const string16& print_job_title, 661 const string16& print_ticket, 662 const std::string& file_type, 663 bool delete_on_close) { 664 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 665 scoped_refptr<base::RefCountedMemory> data; 666 int64 file_size = 0; 667 if (file_util::GetFileSize(path_to_file, &file_size) && file_size != 0) { 668 std::string file_data; 669 if (file_size < kuint32max) { 670 file_data.reserve(static_cast<unsigned int>(file_size)); 671 } else { 672 DLOG(WARNING) << " print data file too large to reserve space"; 673 } 674 if (file_util::ReadFileToString(path_to_file, &file_data)) { 675 data = base::RefCountedString::TakeString(&file_data); 676 } 677 } 678 // Proceed even for empty data to simplify testing. 679 BrowserThread::PostTask( 680 BrowserThread::UI, FROM_HERE, 681 base::Bind(&print_dialog_cloud::CreatePrintDialogForBytes, 682 browser_context, modal_parent, data, print_job_title, 683 print_ticket, file_type)); 684 if (delete_on_close) 685 base::DeleteFile(path_to_file, false); 686 } 687 688 } // namespace internal_cloud_print_helpers 689 690 namespace print_dialog_cloud { 691 692 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { 693 registry->RegisterIntegerPref( 694 prefs::kCloudPrintDialogWidth, 695 kDefaultWidth, 696 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 697 registry->RegisterIntegerPref( 698 prefs::kCloudPrintDialogHeight, 699 kDefaultHeight, 700 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 701 } 702 703 // Called on the FILE or UI thread. This is the main entry point into creating 704 // the dialog. 705 706 void CreatePrintDialogForFile(content::BrowserContext* browser_context, 707 gfx::NativeWindow modal_parent, 708 const base::FilePath& path_to_file, 709 const string16& print_job_title, 710 const string16& print_ticket, 711 const std::string& file_type, 712 bool delete_on_close) { 713 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) || 714 BrowserThread::CurrentlyOn(BrowserThread::UI)); 715 BrowserThread::PostTask( 716 BrowserThread::FILE, FROM_HERE, 717 base::Bind(&internal_cloud_print_helpers::CreateDialogForFileImpl, 718 browser_context, modal_parent, path_to_file, print_job_title, 719 print_ticket, file_type, delete_on_close)); 720 } 721 722 void CreateCloudPrintSigninDialog(content::BrowserContext* browser_context, 723 gfx::NativeWindow modal_parent, 724 const base::Closure& callback) { 725 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 726 727 BrowserThread::PostTask( 728 BrowserThread::UI, FROM_HERE, 729 base::Bind(&internal_cloud_print_helpers::CreateDialogSigninImpl, 730 browser_context, modal_parent, callback)); 731 } 732 733 void CreatePrintDialogForBytes(content::BrowserContext* browser_context, 734 gfx::NativeWindow modal_parent, 735 const base::RefCountedMemory* data, 736 const string16& print_job_title, 737 const string16& print_ticket, 738 const std::string& file_type) { 739 internal_cloud_print_helpers::CreateDialogImpl(browser_context, modal_parent, 740 data, print_job_title, 741 print_ticket, file_type, false, 742 base::Closure()); 743 } 744 745 bool CreatePrintDialogFromCommandLine(const CommandLine& command_line) { 746 DCHECK(command_line.HasSwitch(switches::kCloudPrintFile)); 747 if (!command_line.GetSwitchValuePath(switches::kCloudPrintFile).empty()) { 748 base::FilePath cloud_print_file; 749 cloud_print_file = 750 command_line.GetSwitchValuePath(switches::kCloudPrintFile); 751 if (!cloud_print_file.empty()) { 752 string16 print_job_title; 753 string16 print_job_print_ticket; 754 if (command_line.HasSwitch(switches::kCloudPrintJobTitle)) { 755 print_job_title = 756 internal_cloud_print_helpers::GetSwitchValueString16( 757 command_line, switches::kCloudPrintJobTitle); 758 } 759 if (command_line.HasSwitch(switches::kCloudPrintPrintTicket)) { 760 print_job_print_ticket = 761 internal_cloud_print_helpers::GetSwitchValueString16( 762 command_line, switches::kCloudPrintPrintTicket); 763 } 764 std::string file_type = "application/pdf"; 765 if (command_line.HasSwitch(switches::kCloudPrintFileType)) { 766 file_type = command_line.GetSwitchValueASCII( 767 switches::kCloudPrintFileType); 768 } 769 770 bool delete_on_close = CommandLine::ForCurrentProcess()->HasSwitch( 771 switches::kCloudPrintDeleteFile); 772 773 print_dialog_cloud::CreatePrintDialogForFile( 774 ProfileManager::GetDefaultProfile(), 775 NULL, 776 cloud_print_file, 777 print_job_title, 778 print_job_print_ticket, 779 file_type, 780 delete_on_close); 781 return true; 782 } 783 } 784 return false; 785 } 786 787 } // end namespace 788