OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/service/cloud_print/printer_info.h" | 5 #include "chrome/service/cloud_print/print_system.h" |
6 | 6 |
7 #include <cups/cups.h> | 7 #include <cups/cups.h> |
8 #include <list> | 8 #include <list> |
9 #include <map> | 9 #include <map> |
10 | 10 |
11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
12 #include "base/file_path.h" | 12 #include "base/file_path.h" |
13 #include "base/file_util.h" | 13 #include "base/file_util.h" |
14 #include "base/logging.h" | 14 #include "base/logging.h" |
15 #include "base/lock.h" | 15 #include "base/lock.h" |
16 #include "base/message_loop.h" | 16 #include "base/message_loop.h" |
17 #include "base/rand_util.h" | 17 #include "base/rand_util.h" |
18 #include "base/string_util.h" | 18 #include "base/string_util.h" |
19 #include "base/task.h" | 19 #include "base/task.h" |
20 #include "base/values.h" | 20 #include "base/values.h" |
21 #include "base/utf_string_conversions.h" | 21 #include "base/utf_string_conversions.h" |
22 | 22 |
23 namespace cloud_print { | 23 namespace cloud_print { |
24 | 24 |
25 static const char kCUPSPrinterInfoOpt[] = "printer-info"; | 25 static const char kCUPSPrinterInfoOpt[] = "printer-info"; |
26 static const char kCUPSPrinterStateOpt[] = "printer-state"; | 26 static const char kCUPSPrinterStateOpt[] = "printer-state"; |
27 | 27 |
28 void EnumeratePrinters(PrinterList* printer_list) { | 28 class PrintSystemCUPS : public PrintSystem { |
| 29 public: |
| 30 virtual void EnumeratePrinters(PrinterList* printer_list); |
| 31 |
| 32 virtual bool GetPrinterCapsAndDefaults(const std::string& printer_name, |
| 33 PrinterCapsAndDefaults* printer_info); |
| 34 |
| 35 virtual bool ValidatePrintTicket(const std::string& printer_name, |
| 36 const std::string& print_ticket_data); |
| 37 |
| 38 virtual bool SpoolPrintJob(const std::string& print_ticket, |
| 39 const FilePath& print_data_file_path, |
| 40 const std::string& print_data_mime_type, |
| 41 const std::string& printer_name, |
| 42 const std::string& job_title, |
| 43 PlatformJobId* job_id_ret); |
| 44 |
| 45 virtual bool GetJobDetails(const std::string& printer_name, |
| 46 PlatformJobId job_id, |
| 47 PrintJobDetails *job_details); |
| 48 |
| 49 virtual bool IsValidPrinter(const std::string& printer_name); |
| 50 |
| 51 // TODO(gene): Add implementation for CUPS print server watcher. |
| 52 class PrintServerWatcherCUPS |
| 53 : public PrintSystem::PrintServerWatcher { |
| 54 public: |
| 55 PrintServerWatcherCUPS() {} |
| 56 |
| 57 // PrintSystem::PrintServerWatcher interface |
| 58 virtual bool StartWatching( |
| 59 PrintSystem::PrintServerWatcher::Delegate* delegate) { |
| 60 NOTIMPLEMENTED(); |
| 61 return true; |
| 62 } |
| 63 virtual bool StopWatching() { |
| 64 NOTIMPLEMENTED(); |
| 65 return true; |
| 66 } |
| 67 }; |
| 68 |
| 69 class PrinterWatcherCUPS |
| 70 : public PrintSystem::PrinterWatcher { |
| 71 public: |
| 72 explicit PrinterWatcherCUPS(PrintSystemCUPS* print_system, |
| 73 const std::string& printer_name) |
| 74 : printer_name_(printer_name), print_system_(print_system) { |
| 75 } |
| 76 |
| 77 // PrintSystem::PrinterWatcher interface |
| 78 virtual bool StartWatching( |
| 79 PrintSystem::PrinterWatcher::Delegate* delegate) { |
| 80 if (delegate_ != NULL) |
| 81 StopWatching(); |
| 82 delegate_ = delegate; |
| 83 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 84 NewRunnableMethod(this, |
| 85 &PrintSystemCUPS::PrinterWatcherCUPS::Update), 5000); |
| 86 return true; |
| 87 } |
| 88 virtual bool StopWatching() { |
| 89 delegate_ = NULL; |
| 90 return true; |
| 91 } |
| 92 bool GetCurrentPrinterInfo(PrinterBasicInfo* printer_info) { |
| 93 DCHECK(printer_info); |
| 94 return print_system_->GetPrinterInfo(printer_name_, printer_info); |
| 95 } |
| 96 |
| 97 void Update() { |
| 98 if (delegate_ == NULL) |
| 99 return; // Orphan call. We have been stopped already. |
| 100 // For CUPS proxy, we are going to fire OnJobChanged notification |
| 101 // periodically. Higher level will check if there are any outstanding |
| 102 // jobs for this printer and check their status. If printer has no |
| 103 // outstanding jobs, OnJobChanged() will do nothing. |
| 104 delegate_->OnJobChanged(); |
| 105 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 106 NewRunnableMethod(this, |
| 107 &PrintSystemCUPS::PrinterWatcherCUPS::Update), |
| 108 kNotificationTimeout); |
| 109 } |
| 110 private: |
| 111 static const int kNotificationTimeout = 5000; // in ms |
| 112 std::string printer_name_; |
| 113 PrintSystem::PrinterWatcher::Delegate* delegate_; |
| 114 scoped_refptr<PrintSystemCUPS> print_system_; |
| 115 DISALLOW_COPY_AND_ASSIGN(PrinterWatcherCUPS); |
| 116 }; |
| 117 |
| 118 virtual PrintSystem::PrintServerWatcher* CreatePrintServerWatcher(); |
| 119 virtual PrintSystem::PrinterWatcher* CreatePrinterWatcher( |
| 120 const std::string& printer_name); |
| 121 |
| 122 // Helper functions. |
| 123 bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info); |
| 124 bool ParsePrintTicket(const std::string& print_ticket, |
| 125 std::map<std::string, std::string>* options); |
| 126 }; |
| 127 |
| 128 void PrintSystemCUPS::EnumeratePrinters(PrinterList* printer_list) { |
29 DCHECK(printer_list); | 129 DCHECK(printer_list); |
30 printer_list->clear(); | 130 printer_list->clear(); |
31 | 131 |
32 cups_dest_t* destinations = NULL; | 132 cups_dest_t* destinations = NULL; |
33 int num_dests = cupsGetDests(&destinations); | 133 int num_dests = cupsGetDests(&destinations); |
34 | 134 |
35 for (int printer_index = 0; printer_index < num_dests; printer_index++) { | 135 for (int printer_index = 0; printer_index < num_dests; printer_index++) { |
36 const cups_dest_t& printer = destinations[printer_index]; | 136 const cups_dest_t& printer = destinations[printer_index]; |
37 | 137 |
38 PrinterBasicInfo printer_info; | 138 PrinterBasicInfo printer_info; |
(...skipping 13 matching lines...) Expand all Loading... |
52 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) { | 152 for (int opt_index = 0; opt_index < printer.num_options; opt_index++) { |
53 printer_info.options[printer.options[opt_index].name] = | 153 printer_info.options[printer.options[opt_index].name] = |
54 printer.options[opt_index].value; | 154 printer.options[opt_index].value; |
55 } | 155 } |
56 | 156 |
57 printer_list->push_back(printer_info); | 157 printer_list->push_back(printer_info); |
58 } | 158 } |
59 | 159 |
60 cupsFreeDests(num_dests, destinations); | 160 cupsFreeDests(num_dests, destinations); |
61 | 161 |
62 DLOG(INFO) << "Enumerated " << printer_list->size() << " printers."; | 162 DLOG(INFO) << "CP_CUPS: Enumerated " << printer_list->size() << " printers."; |
63 } | 163 } |
64 | 164 |
65 bool GetPrinterCapsAndDefaults(const std::string& printer_name, | 165 bool PrintSystemCUPS::GetPrinterCapsAndDefaults(const std::string& printer_name, |
66 PrinterCapsAndDefaults* printer_info) { | 166 PrinterCapsAndDefaults* printer_info) { |
67 DCHECK(printer_info); | 167 DCHECK(printer_info); |
68 | 168 |
69 DLOG(INFO) << "Getting Caps and Defaults for: " << printer_name; | 169 DLOG(INFO) << "CP_CUPS: Getting Caps and Defaults for: " << printer_name; |
70 | 170 |
71 static Lock ppd_lock; | 171 static Lock ppd_lock; |
72 // cupsGetPPD returns a filename stored in a static buffer in CUPS. | 172 // cupsGetPPD returns a filename stored in a static buffer in CUPS. |
73 // Protect this code with lock. | 173 // Protect this code with lock. |
74 ppd_lock.Acquire(); | 174 ppd_lock.Acquire(); |
75 FilePath ppd_path(cupsGetPPD(printer_name.c_str())); | 175 FilePath ppd_path; |
| 176 const char* ppd_file_path = cupsGetPPD(printer_name.c_str()); |
| 177 if (ppd_file_path) |
| 178 ppd_path = FilePath(ppd_file_path); |
76 ppd_lock.Release(); | 179 ppd_lock.Release(); |
77 | 180 |
| 181 // In some cases CUPS failed to get ppd file. |
| 182 if (ppd_path.empty()) { |
| 183 DLOG(ERROR) << "CP_CUPS: Failed to get PPD for: " << printer_name; |
| 184 return false; |
| 185 } |
| 186 |
78 std::string content; | 187 std::string content; |
79 bool res = file_util::ReadFileToString(ppd_path, &content); | 188 bool res = file_util::ReadFileToString(ppd_path, &content); |
80 | 189 |
81 file_util::Delete(ppd_path, false); | 190 file_util::Delete(ppd_path, false); |
82 | 191 |
83 if (res) { | 192 if (res) { |
84 printer_info->printer_capabilities.swap(content); | 193 printer_info->printer_capabilities.swap(content); |
85 printer_info->caps_mime_type = "application/pagemaker"; | 194 printer_info->caps_mime_type = "application/pagemaker"; |
86 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here. | 195 // In CUPS, printer defaults is a part of PPD file. Nothing to upload here. |
87 printer_info->printer_defaults.clear(); | 196 printer_info->printer_defaults.clear(); |
88 printer_info->defaults_mime_type.clear(); | 197 printer_info->defaults_mime_type.clear(); |
89 } | 198 } |
90 | 199 |
91 return res; | 200 return res; |
92 } | 201 } |
93 | 202 |
94 bool ValidatePrintTicket(const std::string& printer_name, | 203 bool PrintSystemCUPS::ValidatePrintTicket(const std::string& printer_name, |
95 const std::string& print_ticket_data) { | 204 const std::string& print_ticket_data) { |
96 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data, | 205 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket_data, |
97 false)); | 206 false)); |
98 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); | 207 return ticket_value != NULL && ticket_value->IsType(Value::TYPE_DICTIONARY); |
99 } | 208 } |
100 | 209 |
101 std::string GenerateProxyId() { | |
102 // TODO(gene): This code should generate a unique id for proxy. ID should be | |
103 // unique for this user. Rand may return the same number. We'll need to change | |
104 // this in the future. | |
105 std::string id("CP_PROXY_"); | |
106 id += Uint64ToString(base::RandUint64()); | |
107 return id; | |
108 } | |
109 | |
110 // Print ticket on linux is a JSON string containing only one dictionary. | 210 // Print ticket on linux is a JSON string containing only one dictionary. |
111 bool ParsePrintTicket(const std::string& print_ticket, | 211 bool PrintSystemCUPS::ParsePrintTicket(const std::string& print_ticket, |
112 std::map<std::string, std::string>* options) { | 212 std::map<std::string, std::string>* options) { |
113 DCHECK(options); | 213 DCHECK(options); |
114 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false)); | 214 scoped_ptr<Value> ticket_value(base::JSONReader::Read(print_ticket, false)); |
115 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) | 215 if (ticket_value == NULL || !ticket_value->IsType(Value::TYPE_DICTIONARY)) |
116 return false; | 216 return false; |
117 | 217 |
118 options->clear(); | 218 options->clear(); |
119 DictionaryValue* ticket_dict = | 219 DictionaryValue* ticket_dict = |
120 static_cast<DictionaryValue*>(ticket_value.get()); | 220 static_cast<DictionaryValue*>(ticket_value.get()); |
121 DictionaryValue::key_iterator it(ticket_dict->begin_keys()); | 221 DictionaryValue::key_iterator it(ticket_dict->begin_keys()); |
122 for (; it != ticket_dict->end_keys(); ++it) { | 222 for (; it != ticket_dict->end_keys(); ++it) { |
123 std::wstring key = *it; | 223 std::wstring key = *it; |
124 std::string value; | 224 std::string value; |
125 if (ticket_dict->GetString(key, &value)) { | 225 if (ticket_dict->GetString(key, &value)) { |
126 (*options)[WideToUTF8(key.c_str())] = value; | 226 (*options)[WideToUTF8(key.c_str())] = value; |
127 } | 227 } |
128 } | 228 } |
129 | 229 |
130 return true; | 230 return true; |
131 } | 231 } |
132 | 232 |
133 bool SpoolPrintJob(const std::string& print_ticket, | 233 bool PrintSystemCUPS::SpoolPrintJob(const std::string& print_ticket, |
134 const FilePath& print_data_file_path, | 234 const FilePath& print_data_file_path, |
135 const std::string& print_data_mime_type, | 235 const std::string& print_data_mime_type, |
136 const std::string& printer_name, | 236 const std::string& printer_name, |
137 const std::string& job_title, | 237 const std::string& job_title, |
138 PlatformJobId* job_id_ret) { | 238 PlatformJobId* job_id_ret) { |
139 DCHECK(job_id_ret); | 239 DCHECK(job_id_ret); |
140 | 240 |
141 DLOG(INFO) << "Spooling print job for: " << printer_name; | 241 DLOG(INFO) << "CP_CUPS: Spooling print job for: " << printer_name; |
142 | 242 |
143 // We need to store options as char* string for the duration of the | 243 // We need to store options as char* string for the duration of the |
144 // cupsPrintFile call. We'll use map here to store options, since | 244 // cupsPrintFile call. We'll use map here to store options, since |
145 // Dictionary value from JSON parser returns wchat_t. | 245 // Dictionary value from JSON parser returns wchat_t. |
146 std::map<std::string, std::string> options; | 246 std::map<std::string, std::string> options; |
147 bool res = ParsePrintTicket(print_ticket, &options); | 247 bool res = ParsePrintTicket(print_ticket, &options); |
148 DCHECK(res); // If print ticket is invalid we still print using defaults. | 248 DCHECK(res); // If print ticket is invalid we still print using defaults. |
149 | 249 |
150 std::vector<cups_option_t> cups_options; | 250 std::vector<cups_option_t> cups_options; |
151 std::map<std::string, std::string>::iterator it; | 251 std::map<std::string, std::string>::iterator it; |
152 for (it = options.begin(); it != options.end(); ++it) { | 252 for (it = options.begin(); it != options.end(); ++it) { |
153 cups_option_t opt; | 253 cups_option_t opt; |
154 opt.name = const_cast<char*>(it->first.c_str()); | 254 opt.name = const_cast<char*>(it->first.c_str()); |
155 opt.value = const_cast<char*>(it->second.c_str()); | 255 opt.value = const_cast<char*>(it->second.c_str()); |
156 cups_options.push_back(opt); | 256 cups_options.push_back(opt); |
157 } | 257 } |
158 | 258 |
159 int job_id = cupsPrintFile(printer_name.c_str(), | 259 int job_id = cupsPrintFile(printer_name.c_str(), |
160 print_data_file_path.value().c_str(), | 260 print_data_file_path.value().c_str(), |
161 job_title.c_str(), | 261 job_title.c_str(), |
162 cups_options.size(), | 262 cups_options.size(), |
163 &(cups_options[0])); | 263 &(cups_options[0])); |
164 | 264 |
| 265 DLOG(INFO) << "CP_CUPS: Job spooled, id: " << job_id; |
| 266 |
165 if (job_id == 0) | 267 if (job_id == 0) |
166 return false; | 268 return false; |
167 | 269 |
168 *job_id_ret = job_id; | 270 *job_id_ret = job_id; |
169 return true; | 271 return true; |
170 } | 272 } |
171 | 273 |
172 bool GetJobDetails(const std::string& printer_name, | 274 bool PrintSystemCUPS::GetJobDetails(const std::string& printer_name, |
173 PlatformJobId job_id, | 275 PlatformJobId job_id, |
174 PrintJobDetails *job_details) { | 276 PrintJobDetails *job_details) { |
175 DCHECK(job_details); | 277 DCHECK(job_details); |
176 | 278 |
177 DLOG(INFO) << "Getting job details for: " << printer_name << | 279 DLOG(INFO) << "CP_CUPS: Getting job details for: " << printer_name << |
178 " job_id: " << job_id; | 280 " job_id: " << job_id; |
179 | 281 |
180 cups_job_t* jobs = NULL; | 282 cups_job_t* jobs = NULL; |
181 int num_jobs = cupsGetJobs(&jobs, printer_name.c_str(), 1, -1); | 283 int num_jobs = cupsGetJobs(&jobs, printer_name.c_str(), 1, -1); |
182 | 284 |
183 bool found = false; | 285 bool found = false; |
184 for (int i = 0; i < num_jobs; i++) { | 286 for (int i = 0; i < num_jobs; i++) { |
185 if (jobs[i].id == job_id) { | 287 if (jobs[i].id == job_id) { |
186 found = true; | 288 found = true; |
187 switch (jobs[i].state) { | 289 switch (jobs[i].state) { |
(...skipping 17 matching lines...) Expand all Loading... |
205 | 307 |
206 // We don't have any details on the number of processed pages here. | 308 // We don't have any details on the number of processed pages here. |
207 break; | 309 break; |
208 } | 310 } |
209 } | 311 } |
210 | 312 |
211 cupsFreeJobs(num_jobs, jobs); | 313 cupsFreeJobs(num_jobs, jobs); |
212 return found; | 314 return found; |
213 } | 315 } |
214 | 316 |
215 bool GetPrinterInfo(const std::string& printer_name, PrinterBasicInfo* info) { | 317 bool PrintSystemCUPS::GetPrinterInfo(const std::string& printer_name, |
| 318 PrinterBasicInfo* info) { |
216 DCHECK(info); | 319 DCHECK(info); |
217 | 320 |
218 DLOG(INFO) << "Getting printer info for: " << printer_name; | 321 DLOG(INFO) << "CP_CUPS: Getting printer info for: " << printer_name; |
219 | 322 |
220 // This is not very efficient way to get specific printer info. CUPS 1.4 | 323 // This is not very efficient way to get specific printer info. CUPS 1.4 |
221 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available | 324 // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available |
222 // everywhere (for example, it supported from Mac OS 10.6 only). | 325 // everywhere (for example, it supported from Mac OS 10.6 only). |
223 PrinterList printer_list; | 326 PrinterList printer_list; |
224 EnumeratePrinters(&printer_list); | 327 EnumeratePrinters(&printer_list); |
225 | 328 |
226 PrinterList::iterator it; | 329 PrinterList::iterator it; |
227 for (it = printer_list.begin(); it != printer_list.end(); ++it) { | 330 for (it = printer_list.begin(); it != printer_list.end(); ++it) { |
228 if (it->printer_name == printer_name) { | 331 if (it->printer_name == printer_name) { |
229 *info = *it; | 332 *info = *it; |
230 return true; | 333 return true; |
231 } | 334 } |
232 } | 335 } |
233 return false; | 336 return false; |
234 } | 337 } |
235 | 338 |
236 bool IsValidPrinter(const std::string& printer_name) { | 339 bool PrintSystemCUPS::IsValidPrinter(const std::string& printer_name) { |
237 PrinterBasicInfo info; | 340 PrinterBasicInfo info; |
238 return GetPrinterInfo(printer_name, &info); | 341 return GetPrinterInfo(printer_name, &info); |
239 } | 342 } |
240 | 343 |
241 class PrinterChangeNotifier::NotificationState | 344 PrintSystem::PrintServerWatcher* |
242 : public base::RefCountedThreadSafe< | 345 PrintSystemCUPS::CreatePrintServerWatcher() { |
243 PrinterChangeNotifier::NotificationState> { | 346 return new PrintServerWatcherCUPS(); |
244 public: | |
245 NotificationState() : delegate_(NULL) {} | |
246 bool Start(const std::string& printer_name, | |
247 PrinterChangeNotifier::Delegate* delegate) { | |
248 if (delegate_ != NULL) | |
249 Stop(); | |
250 | |
251 printer_name_ = printer_name; | |
252 delegate_ = delegate; | |
253 | |
254 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
255 NewRunnableMethod(this, | |
256 &PrinterChangeNotifier::NotificationState::Update), 5000); | |
257 return true; | |
258 } | |
259 bool Stop() { | |
260 delegate_ = NULL; | |
261 return true; | |
262 } | |
263 void Update() { | |
264 if (delegate_ == NULL) | |
265 return; // Orphan call. We have been stopped already. | |
266 // For CUPS proxy, we are going to fire OnJobChanged notification | |
267 // periodically. Higher level will check if there are any outstanding | |
268 // jobs for this printer and check their status. If printer has no | |
269 // outstanding jobs, OnJobChanged() will do nothing. | |
270 delegate_->OnJobChanged(); | |
271 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
272 NewRunnableMethod(this, | |
273 &PrinterChangeNotifier::NotificationState::Update), | |
274 kNotificationTimeout); | |
275 } | |
276 std::string printer_name() const { | |
277 return printer_name_; | |
278 } | |
279 private: | |
280 friend class base::RefCountedThreadSafe< | |
281 PrinterChangeNotifier::NotificationState>; | |
282 ~NotificationState() { | |
283 Stop(); | |
284 } | |
285 static const int kNotificationTimeout = 5000; // in ms | |
286 std::string printer_name_; // The printer being watched | |
287 PrinterChangeNotifier::Delegate* delegate_; // Delegate to notify | |
288 }; | |
289 | |
290 PrinterChangeNotifier::PrinterChangeNotifier() : state_(NULL) { | |
291 } | 347 } |
292 | 348 |
293 PrinterChangeNotifier::~PrinterChangeNotifier() { | 349 PrintSystem::PrinterWatcher* PrintSystemCUPS::CreatePrinterWatcher( |
294 StopWatching(); | 350 const std::string& printer_name) { |
| 351 DCHECK(!printer_name.empty()); |
| 352 return new PrinterWatcherCUPS(this, printer_name); |
295 } | 353 } |
296 | 354 |
297 bool PrinterChangeNotifier::StartWatching(const std::string& printer_name, | 355 std::string PrintSystem::GenerateProxyId() { |
298 Delegate* delegate) { | 356 // TODO(gene): This code should generate a unique id for proxy. ID should be |
299 if (state_) { | 357 // unique for this user. Rand may return the same number. We'll need to change |
300 NOTREACHED(); | 358 // this in the future. |
301 return false; | 359 std::string id("CP_PROXY_"); |
302 } | 360 id += Uint64ToString(base::RandUint64()); |
303 state_ = new NotificationState; | 361 return id; |
304 state_->AddRef(); | |
305 if (!state_->Start(printer_name, delegate)) { | |
306 StopWatching(); | |
307 return false; | |
308 } | |
309 return true; | |
310 } | 362 } |
311 | 363 |
312 bool PrinterChangeNotifier::StopWatching() { | 364 scoped_refptr<PrintSystem> PrintSystem::CreateInstance() { |
313 if (!state_) { | 365 return new PrintSystemCUPS; |
314 return false; | |
315 } | |
316 state_->Stop(); | |
317 state_->Release(); | |
318 state_ = NULL; | |
319 return true; | |
320 } | 366 } |
321 | 367 |
322 bool PrinterChangeNotifier::GetCurrentPrinterInfo( | |
323 PrinterBasicInfo* printer_info) { | |
324 if (!state_) { | |
325 return false; | |
326 } | |
327 DCHECK(printer_info); | |
328 return GetPrinterInfo(state_->printer_name(), printer_info); | |
329 } | |
330 } // namespace cloud_print | 368 } // namespace cloud_print |
331 | 369 |
OLD | NEW |