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", ¶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 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 ¶ms_.width, 532 ¶ms_.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