172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/printing/print_dialog_cloud.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/printing/print_dialog_cloud_internal.h" 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/base64.h" 9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/command_line.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/json/json_reader.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/values.h" 133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/debugger/devtools_manager.h" 14513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/browser/prefs/pref_service.h" 153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/printing/cloud_print/cloud_print_url.h" 1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/profiles/profile_manager.h" 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/browser_dialogs.h" 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h" 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/chrome_switches.h" 21513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "chrome/common/pref_names.h" 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/print_messages.h" 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h" 24dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/browser_thread.h" 25dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_view_host.h" 26dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h" 27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents_view.h" 28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/webui/web_ui.h" 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_registrar.h" 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h" 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_type.h" 3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "webkit/glue/webpreferences.h" 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h" 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This module implements the UI support in Chrome for cloud printing. 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// This means hosting a dialog containing HTML/JavaScript and using 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the published cloud print user interface integration APIs to get 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// page setup settings from the dialog contents and provide the 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// generated print data to the dialog contents for uploading to the 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// cloud print service. 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Currently, the flow between these classes is as follows: 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// PrintDialogCloud::CreatePrintDialogForFile is called from 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// resource_message_filter_gtk.cc once the renderer has informed the 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// renderer host that print data generation into the renderer host provided 4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// temp file has been completed. That call is on the FILE thread. 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// That, in turn, hops over to the UI thread to create an instance of 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// PrintDialogCloud. 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The constructor for PrintDialogCloud creates a 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CloudPrintHtmlDialogDelegate and asks the current active browser to 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// show an HTML dialog using that class as the delegate. That class 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// hands in the kCloudPrintResourcesURL as the URL to visit. That is 5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// recognized by the GetWebUIFactoryFunction as a signal to create an 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// ExternalHtmlDialogUI. 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CloudPrintHtmlDialogDelegate also temporarily owns a 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CloudPrintFlowHandler, a class which is responsible for the actual 62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// interactions with the dialog contents, including handing in the 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// print data and getting any page setup parameters that the dialog 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// contents provides. As part of bringing up the dialog, 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// HtmlDialogUI::RenderViewCreated is called (an override of 6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// WebUI::RenderViewCreated). That routine, in turn, calls the 6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// delegate's GetWebUIMessageHandlers routine, at which point the 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// ownership of the CloudPrintFlowHandler is handed over. A pointer 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// to the flow handler is kept to facilitate communication back and 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// forth between the two classes. 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// The WebUI continues dialog bring-up, calling 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CloudPrintFlowHandler::RegisterMessages. This is where the 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// additional object model capabilities are registered for the dialog 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// contents to use. It is also at this time that capabilities for the 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// dialog contents are adjusted to allow the dialog contents to close 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the window. In addition, the pending URL is redirected to the 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// actual cloud print service URL. The flow controller also registers 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// for notification of when the dialog contents finish loading, which 80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// is currently used to send the data to the dialog contents. 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// In order to send the data to the dialog contents, the flow 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// handler uses a CloudPrintDataSender. It creates one, letting it 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// know the name of the temporary file containing the data, and 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// posts the task of reading the file 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// (CloudPrintDataSender::ReadPrintDataFile) to the file thread. That 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// routine reads in the file, and then hops over to the IO thread to 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// send that data to the dialog contents. 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// When the dialog contents are finished (by either being cancelled or 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// hitting the print button), the delegate is notified, and responds 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// that the dialog should be closed, at which point things are torn 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// down and released. 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(scottbyer): 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// http://code.google.com/p/chromium/issues/detail?id=44093 The 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// high-level flow (where the data is generated before even 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// bringing up the dialog) isn't what we want. 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace internal_cloud_print_helpers { 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool GetDoubleOrInt(const DictionaryValue& dictionary, 10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& path, 10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen double* out_value) { 10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!dictionary.GetDouble(path, out_value)) { 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int int_value = 0; 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!dictionary.GetInteger(path, &int_value)) 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *out_value = int_value; 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// From the JSON parsed value, get the entries for the page setup 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// parameters. 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool GetPageSetupParameters(const std::string& json, 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PrintMsg_Print_Params& parameters) { 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false)); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DLOG_IF(ERROR, (!parsed_value.get() || 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !parsed_value->IsType(Value::TYPE_DICTIONARY))) 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << "PageSetup call didn't have expected contents"; 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY)) 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool result = true; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DictionaryValue* params = static_cast<DictionaryValue*>(parsed_value.get()); 12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen result &= GetDoubleOrInt(*params, "dpi", ¶meters.dpi); 12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen result &= GetDoubleOrInt(*params, "min_shrink", ¶meters.min_shrink); 12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen result &= GetDoubleOrInt(*params, "max_shrink", ¶meters.max_shrink); 1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick result &= params->GetBoolean("selection_only", ¶meters.selection_only); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintDataSenderHelper::CallJavascriptFunction( 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& function_name) { 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen web_ui_->CallJavascriptFunction(WideToASCII(function_name)); 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintDataSenderHelper::CallJavascriptFunction( 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& function_name, const Value& arg) { 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg); 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintDataSenderHelper::CallJavascriptFunction( 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::wstring& function_name, const Value& arg1, const Value& arg2) { 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen web_ui_->CallJavascriptFunction(WideToASCII(function_name), arg1, arg2); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Clears out the pointer we're using to communicate. Either routine is 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// potentially expensive enough that stopping whatever is in progress 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// is worth it. 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintDataSender::CancelPrintDataFile() { 15372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(lock_); 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We don't own helper, it was passed in to us, so no need to 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // delete, just let it go. 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch helper_ = NULL; 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 159513209b27ff55e2841eac0e4120199c23acce758Ben MurdochCloudPrintDataSender::CloudPrintDataSender(CloudPrintDataSenderHelper* helper, 160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const string16& print_job_title, 161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& file_type) 162513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch : helper_(helper), 163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title_(print_job_title), 164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type_(file_type) { 165513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 166513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 167513209b27ff55e2841eac0e4120199c23acce758Ben MurdochCloudPrintDataSender::~CloudPrintDataSender() {} 168513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Grab the raw file contents and massage them into shape for 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// sending to the dialog contents (and up to the cloud print server) 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// by encoding it and prefixing it with the appropriate mime type. 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Once that is done, kick off the next part of the task on the IO 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// thread. 174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CloudPrintDataSender::ReadPrintDataFile(const FilePath& path_to_file) { 175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int64 file_size = 0; 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (file_util::GetFileSize(path_to_file, &file_size) && file_size != 0) { 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string file_data; 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (file_size < kuint32max) { 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch file_data.reserve(static_cast<unsigned int>(file_size)); 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DLOG(WARNING) << " print data file too large to reserve space"; 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (helper_ && file_util::ReadFileToString(path_to_file, &file_data)) { 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string base64_data; 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base::Base64Encode(file_data, &base64_data); 187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::string header("data:"); 188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen header.append(file_type_); 189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen header.append(";base64,"); 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch base64_data.insert(0, header); 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<StringValue> new_data(new StringValue(base64_data)); 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch print_data_.swap(new_data); 193731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 194731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick NewRunnableMethod( 195731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick this, 196731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &CloudPrintDataSender::SendPrintDataFile)); 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// We have the data in hand that needs to be pushed into the dialog 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// contents; do so from the IO thread. 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(scottbyer): If the print data ends up being larger than the 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// upload limit (currently 10MB), what we need to do is upload that 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// large data to google docs and set the URL in the printing 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// JavaScript to that location, and make sure it gets deleted when not 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// needed. - 4/1/2010 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintDataSender::SendPrintDataFile() { 210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 21172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen base::AutoLock lock(lock_); 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (helper_ && print_data_.get()) { 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StringValue title(print_job_title_); 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Send the print data to the dialog contents. The JavaScript 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // function is a preliminary API for prototyping purposes and is 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // subject to change. 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const_cast<CloudPrintDataSenderHelper*>(helper_)->CallJavascriptFunction( 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch L"printApp._printDataUrl", *print_data_, title); 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenCloudPrintFlowHandler::CloudPrintFlowHandler(const FilePath& path_to_file, 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const string16& print_job_title, 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& file_type) 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen : path_to_file_(path_to_file), 228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title_(print_job_title), 229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type_(file_type) { 230513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 231513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 232513209b27ff55e2841eac0e4120199c23acce758Ben MurdochCloudPrintFlowHandler::~CloudPrintFlowHandler() { 233513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // This will also cancel any task in flight. 234513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch CancelAnyRunningTask(); 235513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 236513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 237513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintFlowHandler::SetDialogDelegate( 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CloudPrintHtmlDialogDelegate* delegate) { 24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // Even if setting a new WebUI, it means any previous task needs 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to be cancelled, it's now invalid. 242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CancelAnyRunningTask(); 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch dialog_delegate_ = delegate; 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Cancels any print data sender we have in flight and removes our 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// reference to it, so when the task that is calling it finishes and 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// removes it's reference, it goes away. 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintFlowHandler::CancelAnyRunningTask() { 251731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (print_data_sender_.get()) { 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch print_data_sender_->CancelPrintDataFile(); 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch print_data_sender_ = NULL; 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintFlowHandler::RegisterMessages() { 25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!web_ui_) 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(scottbyer) - This is where we will register messages for the 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // UI JS to use. Needed: Call to update page setup parameters. 26472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->RegisterMessageCallback( 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "ShowDebugger", 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewCallback(this, &CloudPrintFlowHandler::HandleShowDebugger)); 26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->RegisterMessageCallback( 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SendPrintData", 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewCallback(this, &CloudPrintFlowHandler::HandleSendPrintData)); 27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->RegisterMessageCallback( 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "SetPageParameters", 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NewCallback(this, &CloudPrintFlowHandler::HandleSetPageParameters)); 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (web_ui_->tab_contents()) { 275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Also, take the opportunity to set some (minimal) additional 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // script permissions required for the web UI. 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(scottbyer): learn how to make sure we're talking to the 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // right web site first. 28072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host(); 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (rvh && rvh->delegate()) { 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch WebPreferences webkit_prefs = rvh->delegate()->GetWebkitPrefs(); 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch webkit_prefs.allow_scripts_to_close_windows = true; 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rvh->UpdateWebPreferences(webkit_prefs); 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Register for appropriate notifications, and re-direct the URL 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // to the real server URL, now that we've gotten an HTML dialog 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // going. 29072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen NavigationController* controller = &web_ui_->tab_contents()->controller(); 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NavigationEntry* pending_entry = controller->pending_entry(); 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (pending_entry) 2933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick pending_entry->set_url(CloudPrintURL( 29472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->GetProfile()).GetCloudPrintServiceDialogURL()); 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch registrar_.Add(this, NotificationType::LOAD_STOP, 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Source<NavigationController>(controller)); 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintFlowHandler::Observe(NotificationType type, 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationSource& source, 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const NotificationDetails& details) { 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (type == NotificationType::LOAD_STOP) { 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Choose one or the other. If you need to debug, bring up the 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // debugger. You can then use the various chrome.send() 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // registrations above to kick of the various function calls, 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // including chrome.send("SendPrintData") in the javaScript 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // console and watch things happen with: 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // HandleShowDebugger(NULL); 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HandleSendPrintData(NULL); 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CloudPrintFlowHandler::HandleShowDebugger(const ListValue* args) { 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ShowDebugger(); 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintFlowHandler::ShowDebugger() { 31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (web_ui_) { 32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host(); 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (rvh) 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DevToolsManager::GetInstance()->OpenDevToolsWindow(rvh); 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochscoped_refptr<CloudPrintDataSender> 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCloudPrintFlowHandler::CreateCloudPrintDataSender() { 32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(web_ui_); 32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen print_data_helper_.reset(new CloudPrintDataSenderHelper(web_ui_)); 330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return new CloudPrintDataSender(print_data_helper_.get(), 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title_, 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type_); 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CloudPrintFlowHandler::HandleSendPrintData(const ListValue* args) { 336731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This will cancel any ReadPrintDataFile() or SendPrintDataFile() 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // requests in flight (this is anticipation of when setting page 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // setup parameters becomes asynchronous and may be set while some 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // data is in flight). Then we can clear out the print data. 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CancelAnyRunningTask(); 34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (web_ui_) { 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch print_data_sender_ = CreateCloudPrintDataSender(); 344731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 345731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick NewRunnableMethod( 346731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick print_data_sender_.get(), 347731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &CloudPrintDataSender::ReadPrintDataFile, 348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen path_to_file_)); 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CloudPrintFlowHandler::HandleSetPageParameters(const ListValue* args) { 353dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen std::string json; 354dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen args->GetString(0, &json); 355dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen if (json.empty()) { 356dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen NOTREACHED() << "Empty json string"; 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 358dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen } 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // These are backstop default values - 72 dpi to match the screen, 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 8.5x11 inch paper with margins subtracted (1/4 inch top, left, 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // right and 0.56 bottom), and the min page shrink and max page 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // shrink values appear all over the place with no explanation. 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(scottbyer): Get a Linux/ChromeOS edge for PrintSettings 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // working so that we can get the default values from there. Fix up 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // PrintWebViewHelper to do the same. 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int kDPI = 72; 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int kWidth = static_cast<int>((8.5-0.25-0.25)*kDPI); 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int kHeight = static_cast<int>((11-0.25-0.56)*kDPI); 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const double kMinPageShrink = 1.25; 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const double kMaxPageShrink = 2.0; 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen PrintMsg_Print_Params default_settings; 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.printable_size = gfx::Size(kWidth, kHeight); 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.dpi = kDPI; 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.min_shrink = kMinPageShrink; 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.max_shrink = kMaxPageShrink; 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.desired_dpi = kDPI; 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.document_cookie = 0; 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch default_settings.selection_only = false; 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!GetPageSetupParameters(json, default_settings)) { 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(scottbyer) - Here is where we would kick the originating 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // renderer thread with these new parameters in order to get it to 390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // re-generate the PDF data and hand it back to us. window.print() is 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // currently synchronous, so there's a lot of work to do to get to 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // that point. 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 395513209b27ff55e2841eac0e4120199c23acce758Ben Murdochvoid CloudPrintFlowHandler::StoreDialogClientSize() const { 39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (web_ui_ && web_ui_->tab_contents() && web_ui_->tab_contents()->view()) { 39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen gfx::Size size = web_ui_->tab_contents()->view()->GetContainerSize(); 39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->GetProfile()->GetPrefs()->SetInteger( 399513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch prefs::kCloudPrintDialogWidth, size.width()); 40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen web_ui_->GetProfile()->GetPrefs()->SetInteger( 401513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch prefs::kCloudPrintDialogHeight, size.height()); 402513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 403513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 404513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate( 406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const FilePath& path_to_file, 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int width, int height, 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& json_arguments, 40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const string16& print_job_title, 410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& file_type, 41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool modal) 412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen : flow_handler_(new CloudPrintFlowHandler(path_to_file, 413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title, 414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type)), 41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen modal_(modal), 416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch owns_flow_handler_(true) { 417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Init(width, height, json_arguments); 418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// For unit testing. 421c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCloudPrintHtmlDialogDelegate::CloudPrintHtmlDialogDelegate( 422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CloudPrintFlowHandler* flow_handler, 423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int width, int height, 42472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& json_arguments, 42572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen bool modal) 426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : flow_handler_(flow_handler), 42772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen modal_(modal), 428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch owns_flow_handler_(true) { 429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Init(width, height, json_arguments); 430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 43272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid CloudPrintHtmlDialogDelegate::Init(int width, int height, 43372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen const std::string& json_arguments) { 434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This information is needed to show the dialog HTML content. 435731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 43672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen params_.url = GURL(chrome::kCloudPrintResourcesURL); 437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_.height = height; 438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_.width = width; 439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch params_.json_input = json_arguments; 440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch flow_handler_->SetDialogDelegate(this); 44272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If we're not modal we can show the dialog with no browser. 44372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // We need this to keep Chrome alive while our dialog is up. 44472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!modal_) 44572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen BrowserList::StartKeepAlive(); 446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 448c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCloudPrintHtmlDialogDelegate::~CloudPrintHtmlDialogDelegate() { 449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the flow_handler_ is about to outlive us because we don't own 450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // it anymore, we need to have it remove it's reference to us. 451731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch flow_handler_->SetDialogDelegate(NULL); 453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (owns_flow_handler_) { 454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete flow_handler_; 455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool CloudPrintHtmlDialogDelegate::IsDialogModal() const { 45972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return modal_; 460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring CloudPrintHtmlDialogDelegate::GetDialogTitle() const { 463513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return std::wstring(); 464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 466c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGURL CloudPrintHtmlDialogDelegate::GetDialogContentURL() const { 467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return params_.url; 468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 47072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid CloudPrintHtmlDialogDelegate::GetWebUIMessageHandlers( 47172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen std::vector<WebUIMessageHandler*>* handlers) const { 472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch handlers->push_back(flow_handler_); 473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We don't own flow_handler_ anymore, but it sticks around until at 474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // least right after OnDialogClosed() is called (and this object is 475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // destroyed). 476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch owns_flow_handler_ = false; 477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintHtmlDialogDelegate::GetDialogSize(gfx::Size* size) const { 480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size->set_width(params_.width); 481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size->set_height(params_.height); 482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::string CloudPrintHtmlDialogDelegate::GetDialogArgs() const { 485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return params_.json_input; 486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintHtmlDialogDelegate::OnDialogClosed( 489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& json_retval) { 490513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // Get the final dialog size and store it. 491513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch flow_handler_->StoreDialogClientSize(); 49272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // If we're modal we can show the dialog with no browser. 49372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen // End the keep-alive so that Chrome can exit. 49472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!modal_) 49572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen BrowserList::EndKeepAlive(); 496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch delete this; 497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CloudPrintHtmlDialogDelegate::OnCloseContents(TabContents* source, 500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool* out_close_dialog) { 501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (out_close_dialog) 502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *out_close_dialog = true; 503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 505513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool CloudPrintHtmlDialogDelegate::ShouldShowDialogTitle() const { 506513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return false; 507513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch} 508513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool CloudPrintHtmlDialogDelegate::HandleContextMenu( 510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const ContextMenuParams& params) { 511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return true; 512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Called from the UI thread, starts up the dialog. 515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CreateDialogImpl(const FilePath& path_to_file, 516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const string16& print_job_title, 517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& file_type, 518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool modal) { 519731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Browser* browser = BrowserList::GetLastActive(); 52172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 522513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const int kDefaultWidth = 497; 523513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const int kDefaultHeight = 332; 52472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen string16 job_title = print_job_title; 52572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen Profile* profile = NULL; 52672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (modal) { 527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(browser); 528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (job_title.empty() && browser->GetSelectedTabContents()) 529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen job_title = browser->GetSelectedTabContents()->GetTitle(); 530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen profile = browser->GetProfile(); 53172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 53272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen profile = ProfileManager::GetDefaultProfile(); 53372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 53472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen DCHECK(profile); 53572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen PrefService* pref_service = profile->GetPrefs(); 536513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch DCHECK(pref_service); 537513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!pref_service->FindPreference(prefs::kCloudPrintDialogWidth)) { 538513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogWidth, 539513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch kDefaultWidth); 540513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 541513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!pref_service->FindPreference(prefs::kCloudPrintDialogHeight)) { 542513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch pref_service->RegisterIntegerPref(prefs::kCloudPrintDialogHeight, 543513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch kDefaultHeight); 544513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 545513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch 546513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch int width = pref_service->GetInteger(prefs::kCloudPrintDialogWidth); 547513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch int height = pref_service->GetInteger(prefs::kCloudPrintDialogHeight); 54872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen 549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch HtmlDialogUIDelegate* dialog_delegate = 550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new internal_cloud_print_helpers::CloudPrintHtmlDialogDelegate( 551ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen path_to_file, width, height, std::string(), job_title, file_type, 552ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen modal); 55372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (modal) { 554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(browser); 555ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen browser->BrowserShowHtmlDialog(dialog_delegate, NULL); 55672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } else { 55772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen browser::ShowHtmlDialog(NULL, profile, dialog_delegate); 55872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} // namespace internal_cloud_print_helpers 562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace print_dialog_cloud { 564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Called on the FILE or UI thread. This is the main entry point into creating 566ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// the dialog. 567ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 568ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(scottbyer): The signature here will need to change as the 569ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// workflow through the printing code changes to allow for dynamically 570ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// changing page setup parameters while the dialog is active. 571ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CreatePrintDialogForFile(const FilePath& path_to_file, 572ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const string16& print_job_title, 573ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const std::string& file_type, 574ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool modal) { 575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE) || 576ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::CurrentlyOn(BrowserThread::UI)); 577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 578ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::PostTask( 579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen BrowserThread::UI, FROM_HERE, 580ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen NewRunnableFunction(&internal_cloud_print_helpers::CreateDialogImpl, 581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen path_to_file, 582ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title, 583ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type, 584ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen modal)); 585ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 586ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 587ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool CreatePrintDialogFromCommandLine(const CommandLine& command_line) { 588ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!command_line.GetSwitchValuePath(switches::kCloudPrintFile).empty()) { 589ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen FilePath cloud_print_file; 590ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cloud_print_file = 591ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen command_line.GetSwitchValuePath(switches::kCloudPrintFile); 592ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!cloud_print_file.empty()) { 593ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen string16 print_job_title; 594ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (command_line.HasSwitch(switches::kCloudPrintJobTitle)) { 595ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#ifdef OS_WIN 596ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CommandLine::StringType native_job_title; 597ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen native_job_title = command_line.GetSwitchValueNative( 598ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen switches::kCloudPrintJobTitle); 599ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title = string16(native_job_title); 600ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#elif defined(OS_POSIX) 601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // TODO(abodenha@chromium.org) Implement this for OS_POSIX 602ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Command line string types are different 603ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif 604ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 605ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen std::string file_type = "application/pdf"; 606ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (command_line.HasSwitch(switches::kCloudPrintFileType)) { 607ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type = command_line.GetSwitchValueASCII( 608ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen switches::kCloudPrintFileType); 609ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 610ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_dialog_cloud::CreatePrintDialogForFile(cloud_print_file, 611ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen print_job_title, 612ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen file_type, 613ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen false); 614ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return true; 615ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 616ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 617ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return false; 618ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} 619ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 620ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen} // end namespace 621