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