| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/printing/print_dialog_cloud.h" |
| |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/file_util.h" |
| #include "base/json/json_reader.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/devtools/devtools_window.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/printing/print_dialog_cloud_internal.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_dialogs.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/print_messages.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/cloud_devices/common/cloud_devices_urls.h" |
| #include "components/google/core/browser/google_util.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/signin/core/common/profile_management_switches.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_entry.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/common/frame_navigate_params.h" |
| #include "content/public/common/web_preferences.h" |
| |
| #if defined(USE_AURA) |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_tree_host.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "ui/base/win/foreground_helper.h" |
| #endif |
| |
| // This module implements the UI support in Chrome for cloud printing. |
| // This means hosting a dialog containing HTML/JavaScript and using |
| // the published cloud print user interface integration APIs to get |
| // page setup settings from the dialog contents and provide the |
| // generated print data to the dialog contents for uploading to the |
| // cloud print service. |
| |
| // Currently, the flow between these classes is as follows: |
| |
| // PrintDialogCloud::CreatePrintDialogForFile is called from |
| // resource_message_filter_gtk.cc once the renderer has informed the |
| // renderer host that print data generation into the renderer host provided |
| // temp file has been completed. That call is on the FILE thread. |
| // That, in turn, hops over to the UI thread to create an instance of |
| // PrintDialogCloud. |
| |
| // The constructor for PrintDialogCloud creates a |
| // CloudPrintWebDialogDelegate and asks the current active browser to |
| // show an HTML dialog using that class as the delegate. That class |
| // hands in the kChromeUICloudPrintResourcesURL as the URL to visit. That is |
| // recognized by the GetWebUIFactoryFunction as a signal to create an |
| // ExternalWebDialogUI. |
| |
| // CloudPrintWebDialogDelegate also temporarily owns a |
| // CloudPrintFlowHandler, a class which is responsible for the actual |
| // interactions with the dialog contents, including handing in the |
| // print data and getting any page setup parameters that the dialog |
| // contents provides. As part of bringing up the dialog, |
| // WebDialogUI::RenderViewCreated is called (an override of |
| // WebUI::RenderViewCreated). That routine, in turn, calls the |
| // delegate's GetWebUIMessageHandlers routine, at which point the |
| // ownership of the CloudPrintFlowHandler is handed over. A pointer |
| // to the flow handler is kept to facilitate communication back and |
| // forth between the two classes. |
| |
| // The WebUI continues dialog bring-up, calling |
| // CloudPrintFlowHandler::RegisterMessages. This is where the |
| // additional object model capabilities are registered for the dialog |
| // contents to use. It is also at this time that capabilities for the |
| // dialog contents are adjusted to allow the dialog contents to close |
| // the window. In addition, the pending URL is redirected to the |
| // actual cloud print service URL. The flow controller also registers |
| // for notification of when the dialog contents finish loading, which |
| // is currently used to send the data to the dialog contents. |
| |
| // In order to send the data to the dialog contents, the flow |
| // handler uses a CloudPrintDataSender. It creates one, letting it |
| // know the name of the temporary file containing the data, and |
| // posts the task of reading the file |
| // (CloudPrintDataSender::ReadPrintDataFile) to the file thread. That |
| // routine reads in the file, and then hops over to the IO thread to |
| // send that data to the dialog contents. |
| |
| // When the dialog contents are finished (by either being cancelled or |
| // hitting the print button), the delegate is notified, and responds |
| // that the dialog should be closed, at which point things are torn |
| // down and released. |
| |
| using content::BrowserThread; |
| using content::NavigationController; |
| using content::NavigationEntry; |
| using content::RenderViewHost; |
| using content::WebContents; |
| using content::WebPreferences; |
| using content::WebUIMessageHandler; |
| using ui::WebDialogDelegate; |
| |
| namespace { |
| |
| const int kDefaultWidth = 912; |
| const int kDefaultHeight = 633; |
| |
| bool IsSimilarUrl(const GURL& url, const GURL& cloud_print_url) { |
| return url.host() == cloud_print_url.host() && |
| StartsWithASCII(url.path(), cloud_print_url.path(), false) && |
| url.scheme() == cloud_print_url.scheme(); |
| } |
| |
| class SignInObserver : public content::WebContentsObserver { |
| public: |
| SignInObserver(content::WebContents* web_contents, |
| GURL cloud_print_url, |
| const base::Closure& callback) |
| : WebContentsObserver(web_contents), |
| cloud_print_url_(cloud_print_url), |
| callback_(callback), |
| weak_ptr_factory_(this) { |
| } |
| |
| private: |
| // Overridden from content::WebContentsObserver: |
| virtual void DidNavigateMainFrame( |
| const content::LoadCommittedDetails& details, |
| const content::FrameNavigateParams& params) OVERRIDE { |
| if (IsSimilarUrl(params.url, cloud_print_url_)) { |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, |
| base::Bind(&SignInObserver::OnSignIn, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| virtual void WebContentsDestroyed() OVERRIDE { |
| delete this; |
| } |
| |
| void OnSignIn() { |
| callback_.Run(); |
| if (web_contents()) |
| web_contents()->Close(); |
| } |
| |
| GURL cloud_print_url_; |
| base::Closure callback_; |
| base::WeakPtrFactory<SignInObserver> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SignInObserver); |
| }; |
| |
| } // namespace |
| |
| namespace internal_cloud_print_helpers { |
| |
| // From the JSON parsed value, get the entries for the page setup |
| // parameters. |
| bool GetPageSetupParameters(const std::string& json, |
| PrintMsg_Print_Params& parameters) { |
| scoped_ptr<base::Value> parsed_value(base::JSONReader::Read(json)); |
| DLOG_IF(ERROR, (!parsed_value.get() || |
| !parsed_value->IsType(base::Value::TYPE_DICTIONARY))) |
| << "PageSetup call didn't have expected contents"; |
| if (!parsed_value.get() || |
| !parsed_value->IsType(base::Value::TYPE_DICTIONARY)) { |
| return false; |
| } |
| |
| bool result = true; |
| base::DictionaryValue* params = |
| static_cast<base::DictionaryValue*>(parsed_value.get()); |
| result &= params->GetDouble("dpi", ¶meters.dpi); |
| result &= params->GetDouble("min_shrink", ¶meters.min_shrink); |
| result &= params->GetDouble("max_shrink", ¶meters.max_shrink); |
| result &= params->GetBoolean("selection_only", ¶meters.selection_only); |
| return result; |
| } |
| |
| base::string16 GetSwitchValueString16(const CommandLine& command_line, |
| const char* switchName) { |
| #if defined(OS_WIN) |
| return command_line.GetSwitchValueNative(switchName); |
| #elif defined(OS_POSIX) |
| // POSIX Command line string types are different. |
| CommandLine::StringType native_switch_val; |
| native_switch_val = command_line.GetSwitchValueASCII(switchName); |
| // Convert the ASCII string to UTF16 to prepare to pass. |
| return base::ASCIIToUTF16(native_switch_val); |
| #endif |
| } |
| |
| void CloudPrintDataSenderHelper::CallJavascriptFunction( |
| const std::string& function_name, |
| const base::Value& arg1, |
| const base::Value& arg2) { |
| web_ui_->CallJavascriptFunction(function_name, arg1, arg2); |
| } |
| |
| // Clears out the pointer we're using to communicate. Either routine is |
| // potentially expensive enough that stopping whatever is in progress |
| // is worth it. |
| void CloudPrintDataSender::CancelPrintDataFile() { |
| base::AutoLock lock(lock_); |
| // We don't own helper, it was passed in to us, so no need to |
| // delete, just let it go. |
| helper_ = NULL; |
| } |
| |
| CloudPrintDataSender::CloudPrintDataSender( |
| CloudPrintDataSenderHelper* helper, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type, |
| const base::RefCountedMemory* data) |
| : helper_(helper), |
| print_job_title_(print_job_title), |
| print_ticket_(print_ticket), |
| file_type_(file_type), |
| data_(data) { |
| } |
| |
| CloudPrintDataSender::~CloudPrintDataSender() {} |
| |
| // We have the data in hand that needs to be pushed into the dialog |
| // contents; do so from the IO thread. |
| |
| // TODO(scottbyer): If the print data ends up being larger than the |
| // upload limit (currently 10MB), what we need to do is upload that |
| // large data to google docs and set the URL in the printing |
| // JavaScript to that location, and make sure it gets deleted when not |
| // needed. - 4/1/2010 |
| void CloudPrintDataSender::SendPrintData() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (!data_.get() || !data_->size()) |
| return; |
| |
| std::string base64_data; |
| base::Base64Encode( |
| base::StringPiece(data_->front_as<char>(), data_->size()), |
| &base64_data); |
| std::string header("data:"); |
| header.append(file_type_); |
| header.append(";base64,"); |
| base64_data.insert(0, header); |
| |
| base::AutoLock lock(lock_); |
| if (helper_) { |
| base::StringValue title(print_job_title_); |
| base::StringValue ticket(print_ticket_); |
| // TODO(abodenha): Change Javascript call to pass in print ticket |
| // after server side support is added. Add test for it. |
| |
| // Send the print data to the dialog contents. The JavaScript |
| // function is a preliminary API for prototyping purposes and is |
| // subject to change. |
| helper_->CallJavascriptFunction( |
| "printApp._printDataUrl", base::StringValue(base64_data), title); |
| } |
| } |
| |
| |
| CloudPrintFlowHandler::CloudPrintFlowHandler( |
| const base::RefCountedMemory* data, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) |
| : dialog_delegate_(NULL), |
| data_(data), |
| print_job_title_(print_job_title), |
| print_ticket_(print_ticket), |
| file_type_(file_type) { |
| } |
| |
| CloudPrintFlowHandler::~CloudPrintFlowHandler() { |
| // This will also cancel any task in flight. |
| CancelAnyRunningTask(); |
| } |
| |
| |
| void CloudPrintFlowHandler::SetDialogDelegate( |
| CloudPrintWebDialogDelegate* delegate) { |
| // Even if setting a new WebUI, it means any previous task needs |
| // to be canceled, its now invalid. |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| CancelAnyRunningTask(); |
| dialog_delegate_ = delegate; |
| } |
| |
| // Cancels any print data sender we have in flight and removes our |
| // reference to it, so when the task that is calling it finishes and |
| // removes its reference, it goes away. |
| void CloudPrintFlowHandler::CancelAnyRunningTask() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (print_data_sender_.get()) { |
| print_data_sender_->CancelPrintDataFile(); |
| print_data_sender_ = NULL; |
| } |
| } |
| |
| void CloudPrintFlowHandler::RegisterMessages() { |
| // TODO(scottbyer) - This is where we will register messages for the |
| // UI JS to use. Needed: Call to update page setup parameters. |
| web_ui()->RegisterMessageCallback("ShowDebugger", |
| base::Bind(&CloudPrintFlowHandler::HandleShowDebugger, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("SendPrintData", |
| base::Bind(&CloudPrintFlowHandler::HandleSendPrintData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("SetPageParameters", |
| base::Bind(&CloudPrintFlowHandler::HandleSetPageParameters, |
| base::Unretained(this))); |
| |
| // Register for appropriate notifications, and re-direct the URL |
| // to the real server URL, now that we've gotten an HTML dialog |
| // going. |
| NavigationController* controller = |
| &web_ui()->GetWebContents()->GetController(); |
| NavigationEntry* pending_entry = controller->GetPendingEntry(); |
| if (pending_entry) { |
| pending_entry->SetURL(google_util::AppendGoogleLocaleParam( |
| cloud_devices::GetCloudPrintRelativeURL("client/dialog.html"), |
| g_browser_process->GetApplicationLocale())); |
| } |
| registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, |
| content::Source<NavigationController>(controller)); |
| registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, |
| content::Source<NavigationController>(controller)); |
| } |
| |
| void CloudPrintFlowHandler::Observe( |
| int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| switch (type) { |
| case content::NOTIFICATION_LOAD_STOP: { |
| GURL url = web_ui()->GetWebContents()->GetURL(); |
| if (IsCloudPrintDialogUrl(url)) { |
| // Take the opportunity to set some (minimal) additional |
| // script permissions required for the web UI. |
| RenderViewHost* rvh = web_ui()->GetWebContents()->GetRenderViewHost(); |
| if (rvh) { |
| // TODO(chrishtr): this is wrong. allow_scripts_to_close_windows will |
| // be reset the next time a preference changes. |
| WebPreferences webkit_prefs = rvh->GetWebkitPreferences(); |
| webkit_prefs.allow_scripts_to_close_windows = true; |
| rvh->UpdateWebkitPreferences(webkit_prefs); |
| } else { |
| NOTREACHED(); |
| } |
| // Choose one or the other. If you need to debug, bring up the |
| // debugger. You can then use the various chrome.send() |
| // registrations above to kick of the various function calls, |
| // including chrome.send("SendPrintData") in the javaScript |
| // console and watch things happen with: |
| // HandleShowDebugger(NULL); |
| HandleSendPrintData(NULL); |
| } |
| break; |
| } |
| } |
| } |
| |
| void CloudPrintFlowHandler::HandleShowDebugger(const base::ListValue* args) { |
| ShowDebugger(); |
| } |
| |
| void CloudPrintFlowHandler::ShowDebugger() { |
| if (web_ui()) { |
| WebContents* web_contents = web_ui()->GetWebContents(); |
| if (web_contents) |
| DevToolsWindow::OpenDevToolsWindow(web_contents); |
| } |
| } |
| |
| scoped_refptr<CloudPrintDataSender> |
| CloudPrintFlowHandler::CreateCloudPrintDataSender() { |
| DCHECK(web_ui()); |
| print_data_helper_.reset(new CloudPrintDataSenderHelper(web_ui())); |
| scoped_refptr<CloudPrintDataSender> sender( |
| new CloudPrintDataSender(print_data_helper_.get(), |
| print_job_title_, |
| print_ticket_, |
| file_type_, |
| data_.get())); |
| return sender; |
| } |
| |
| void CloudPrintFlowHandler::HandleSendPrintData(const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| // This will cancel any ReadPrintDataFile() or SendPrintDataFile() |
| // requests in flight (this is anticipation of when setting page |
| // setup parameters becomes asynchronous and may be set while some |
| // data is in flight). Then we can clear out the print data. |
| CancelAnyRunningTask(); |
| if (web_ui()) { |
| print_data_sender_ = CreateCloudPrintDataSender(); |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&CloudPrintDataSender::SendPrintData, print_data_sender_)); |
| } |
| } |
| |
| void CloudPrintFlowHandler::HandleSetPageParameters( |
| const base::ListValue* args) { |
| std::string json; |
| bool ret = args->GetString(0, &json); |
| if (!ret || json.empty()) { |
| NOTREACHED() << "Empty json string"; |
| return; |
| } |
| |
| // These are backstop default values - 72 dpi to match the screen, |
| // 8.5x11 inch paper with margins subtracted (1/4 inch top, left, |
| // right and 0.56 bottom), and the min page shrink and max page |
| // shrink values appear all over the place with no explanation. |
| |
| // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings |
| // working so that we can get the default values from there. Fix up |
| // PrintWebViewHelper to do the same. |
| const int kDPI = 72; |
| const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI); |
| const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI); |
| const double kMinPageShrink = 1.25; |
| const double kMaxPageShrink = 2.0; |
| |
| PrintMsg_Print_Params default_settings; |
| default_settings.content_size = gfx::Size(kWidth, kHeight); |
| default_settings.printable_area = gfx::Rect(0, 0, kWidth, kHeight); |
| default_settings.dpi = kDPI; |
| default_settings.min_shrink = kMinPageShrink; |
| default_settings.max_shrink = kMaxPageShrink; |
| default_settings.desired_dpi = kDPI; |
| default_settings.document_cookie = 0; |
| default_settings.selection_only = false; |
| default_settings.preview_request_id = 0; |
| default_settings.is_first_request = true; |
| default_settings.print_to_pdf = false; |
| |
| if (!GetPageSetupParameters(json, default_settings)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| // TODO(scottbyer) - Here is where we would kick the originating |
| // renderer thread with these new parameters in order to get it to |
| // re-generate the PDF data and hand it back to us. window.print() is |
| // currently synchronous, so there's a lot of work to do to get to |
| // that point. |
| } |
| |
| void CloudPrintFlowHandler::StoreDialogClientSize() const { |
| if (web_ui() && web_ui()->GetWebContents()) { |
| gfx::Size size = web_ui()->GetWebContents()->GetContainerBounds().size(); |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogWidth, |
| size.width()); |
| profile->GetPrefs()->SetInteger(prefs::kCloudPrintDialogHeight, |
| size.height()); |
| } |
| } |
| |
| bool CloudPrintFlowHandler::IsCloudPrintDialogUrl(const GURL& url) { |
| GURL cloud_print_url = cloud_devices::GetCloudPrintURL(); |
| return IsSimilarUrl(url, cloud_print_url); |
| } |
| |
| CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate( |
| content::BrowserContext* browser_context, |
| gfx::NativeWindow modal_parent, |
| const base::RefCountedMemory* data, |
| const std::string& json_arguments, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) |
| : flow_handler_( |
| new CloudPrintFlowHandler(data, print_job_title, print_ticket, |
| file_type)), |
| modal_parent_(modal_parent), |
| owns_flow_handler_(true), |
| keep_alive_when_non_modal_(true) { |
| Init(browser_context, json_arguments); |
| } |
| |
| // For unit testing. |
| CloudPrintWebDialogDelegate::CloudPrintWebDialogDelegate( |
| CloudPrintFlowHandler* flow_handler, |
| const std::string& json_arguments) |
| : flow_handler_(flow_handler), |
| modal_parent_(NULL), |
| owns_flow_handler_(true), |
| keep_alive_when_non_modal_(false) { |
| Init(NULL, json_arguments); |
| } |
| |
| // Returns the persisted width/height for the print dialog. |
| void GetDialogWidthAndHeightFromPrefs(content::BrowserContext* browser_context, |
| int* width, |
| int* height) { |
| if (!browser_context) { |
| *width = kDefaultWidth; |
| *height = kDefaultHeight; |
| return; |
| } |
| |
| PrefService* prefs = Profile::FromBrowserContext(browser_context)->GetPrefs(); |
| *width = prefs->GetInteger(prefs::kCloudPrintDialogWidth); |
| *height = prefs->GetInteger(prefs::kCloudPrintDialogHeight); |
| } |
| |
| void CloudPrintWebDialogDelegate::Init(content::BrowserContext* browser_context, |
| const std::string& json_arguments) { |
| // This information is needed to show the dialog HTML content. |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| params_.url = GURL(chrome::kChromeUICloudPrintResourcesURL); |
| GetDialogWidthAndHeightFromPrefs(browser_context, |
| ¶ms_.width, |
| ¶ms_.height); |
| params_.json_input = json_arguments; |
| |
| flow_handler_->SetDialogDelegate(this); |
| // If we're not modal we can show the dialog with no browser. |
| // We need this to keep Chrome alive while our dialog is up. |
| if (!modal_parent_ && keep_alive_when_non_modal_) |
| chrome::IncrementKeepAliveCount(); |
| } |
| |
| CloudPrintWebDialogDelegate::~CloudPrintWebDialogDelegate() { |
| // If the flow_handler_ is about to outlive us because we don't own |
| // it anymore, we need to have it remove its reference to us. |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| flow_handler_->SetDialogDelegate(NULL); |
| if (owns_flow_handler_) { |
| delete flow_handler_; |
| } |
| } |
| |
| ui::ModalType CloudPrintWebDialogDelegate::GetDialogModalType() const { |
| return modal_parent_ ? ui::MODAL_TYPE_WINDOW : ui::MODAL_TYPE_NONE; |
| } |
| |
| base::string16 CloudPrintWebDialogDelegate::GetDialogTitle() const { |
| return base::string16(); |
| } |
| |
| GURL CloudPrintWebDialogDelegate::GetDialogContentURL() const { |
| return params_.url; |
| } |
| |
| void CloudPrintWebDialogDelegate::GetWebUIMessageHandlers( |
| std::vector<WebUIMessageHandler*>* handlers) const { |
| handlers->push_back(flow_handler_); |
| // We don't own flow_handler_ anymore, but it sticks around until at |
| // least right after OnDialogClosed() is called (and this object is |
| // destroyed). |
| owns_flow_handler_ = false; |
| } |
| |
| void CloudPrintWebDialogDelegate::GetDialogSize(gfx::Size* size) const { |
| size->set_width(params_.width); |
| size->set_height(params_.height); |
| } |
| |
| std::string CloudPrintWebDialogDelegate::GetDialogArgs() const { |
| return params_.json_input; |
| } |
| |
| void CloudPrintWebDialogDelegate::OnDialogClosed( |
| const std::string& json_retval) { |
| // Get the final dialog size and store it. |
| flow_handler_->StoreDialogClientSize(); |
| |
| // If we're modal we can show the dialog with no browser. |
| // End the keep-alive so that Chrome can exit. |
| if (!modal_parent_ && keep_alive_when_non_modal_) { |
| // Post to prevent recursive call tho this function. |
| base::MessageLoop::current()->PostTask( |
| FROM_HERE, base::Bind(&chrome::DecrementKeepAliveCount)); |
| } |
| delete this; |
| } |
| |
| void CloudPrintWebDialogDelegate::OnCloseContents(WebContents* source, |
| bool* out_close_dialog) { |
| if (out_close_dialog) |
| *out_close_dialog = true; |
| } |
| |
| bool CloudPrintWebDialogDelegate::ShouldShowDialogTitle() const { |
| return false; |
| } |
| |
| bool CloudPrintWebDialogDelegate::HandleContextMenu( |
| const content::ContextMenuParams& params) { |
| return true; |
| } |
| |
| // Called from the UI thread, starts up the dialog. |
| void CreateDialogImpl(content::BrowserContext* browser_context, |
| gfx::NativeWindow modal_parent, |
| const base::RefCountedMemory* data, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| WebDialogDelegate* dialog_delegate = |
| new internal_cloud_print_helpers::CloudPrintWebDialogDelegate( |
| browser_context, modal_parent, data, std::string(), print_job_title, |
| print_ticket, file_type); |
| #if defined(OS_WIN) |
| gfx::NativeWindow window = |
| #endif |
| chrome::ShowWebDialog(modal_parent, |
| Profile::FromBrowserContext(browser_context), |
| dialog_delegate); |
| #if defined(OS_WIN) |
| if (window) { |
| HWND dialog_handle; |
| #if defined(USE_AURA) |
| dialog_handle = window->GetHost()->GetAcceleratedWidget(); |
| #else |
| dialog_handle = window; |
| #endif |
| if (::GetForegroundWindow() != dialog_handle) { |
| ui::ForegroundHelper::SetForeground(dialog_handle); |
| } |
| } |
| #endif |
| } |
| |
| void CreateDialogForFileImpl(content::BrowserContext* browser_context, |
| gfx::NativeWindow modal_parent, |
| const base::FilePath& path_to_file, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) { |
| DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| scoped_refptr<base::RefCountedMemory> data; |
| int64 file_size = 0; |
| if (base::GetFileSize(path_to_file, &file_size) && file_size != 0) { |
| std::string file_data; |
| if (file_size < kuint32max) { |
| file_data.reserve(static_cast<unsigned int>(file_size)); |
| } else { |
| DLOG(WARNING) << " print data file too large to reserve space"; |
| } |
| if (base::ReadFileToString(path_to_file, &file_data)) { |
| data = base::RefCountedString::TakeString(&file_data); |
| } |
| } |
| // Proceed even for empty data to simplify testing. |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind(&print_dialog_cloud::CreatePrintDialogForBytes, |
| browser_context, modal_parent, data, print_job_title, |
| print_ticket, file_type)); |
| base::DeleteFile(path_to_file, false); |
| } |
| |
| } // namespace internal_cloud_print_helpers |
| |
| namespace print_dialog_cloud { |
| |
| void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterIntegerPref( |
| prefs::kCloudPrintDialogWidth, |
| kDefaultWidth, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kCloudPrintDialogHeight, |
| kDefaultHeight, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| } |
| |
| // Called on the FILE or UI thread. This is the main entry point into creating |
| // the dialog. |
| |
| void CreatePrintDialogForFile(content::BrowserContext* browser_context, |
| gfx::NativeWindow modal_parent, |
| const base::FilePath& path_to_file, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) || |
| BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| base::Bind(&internal_cloud_print_helpers::CreateDialogForFileImpl, |
| browser_context, modal_parent, path_to_file, print_job_title, |
| print_ticket, file_type)); |
| } |
| |
| void CreateCloudPrintSigninTab(Browser* browser, |
| bool add_account, |
| const base::Closure& callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (switches::IsEnableAccountConsistency() && |
| !browser->profile()->IsOffTheRecord()) { |
| browser->window()->ShowAvatarBubbleFromAvatarButton( |
| add_account ? BrowserWindow::AVATAR_BUBBLE_MODE_ADD_ACCOUNT |
| : BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN, |
| signin::ManageAccountsParams()); |
| } else { |
| GURL url = add_account ? cloud_devices::GetCloudPrintAddAccountURL() |
| : cloud_devices::GetCloudPrintSigninURL(); |
| content::WebContents* web_contents = |
| browser->OpenURL(content::OpenURLParams( |
| google_util::AppendGoogleLocaleParam( |
| url, g_browser_process->GetApplicationLocale()), |
| content::Referrer(), |
| NEW_FOREGROUND_TAB, |
| content::PAGE_TRANSITION_AUTO_BOOKMARK, |
| false)); |
| new SignInObserver(web_contents, cloud_devices::GetCloudPrintURL(), |
| callback); |
| } |
| } |
| |
| void CreatePrintDialogForBytes(content::BrowserContext* browser_context, |
| gfx::NativeWindow modal_parent, |
| const base::RefCountedMemory* data, |
| const base::string16& print_job_title, |
| const base::string16& print_ticket, |
| const std::string& file_type) { |
| internal_cloud_print_helpers::CreateDialogImpl(browser_context, modal_parent, |
| data, print_job_title, |
| print_ticket, file_type); |
| } |
| |
| bool CreatePrintDialogFromCommandLine(Profile* profile, |
| const CommandLine& command_line) { |
| DCHECK(command_line.HasSwitch(switches::kCloudPrintFile)); |
| if (!command_line.GetSwitchValuePath(switches::kCloudPrintFile).empty()) { |
| base::FilePath cloud_print_file; |
| cloud_print_file = |
| command_line.GetSwitchValuePath(switches::kCloudPrintFile); |
| if (!cloud_print_file.empty()) { |
| base::string16 print_job_title; |
| base::string16 print_job_print_ticket; |
| if (command_line.HasSwitch(switches::kCloudPrintJobTitle)) { |
| print_job_title = |
| internal_cloud_print_helpers::GetSwitchValueString16( |
| command_line, switches::kCloudPrintJobTitle); |
| } |
| if (command_line.HasSwitch(switches::kCloudPrintPrintTicket)) { |
| print_job_print_ticket = |
| internal_cloud_print_helpers::GetSwitchValueString16( |
| command_line, switches::kCloudPrintPrintTicket); |
| } |
| std::string file_type = "application/pdf"; |
| if (command_line.HasSwitch(switches::kCloudPrintFileType)) { |
| file_type = command_line.GetSwitchValueASCII( |
| switches::kCloudPrintFileType); |
| } |
| |
| print_dialog_cloud::CreatePrintDialogForFile(profile, NULL, |
| cloud_print_file, print_job_title, print_job_print_ticket, file_type); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace print_dialog_cloud |