OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/printing/print_system_task_proxy.h" | |
6 | |
7 #include <ctype.h> | |
8 | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "base/string_split.h" | |
15 #include "base/string_util.h" | |
16 #include "base/values.h" | |
17 #include "printing/backend/print_backend.h" | |
18 #include "printing/print_job_constants.h" | |
19 #include "printing/print_settings.h" | |
20 | |
21 #if defined(USE_CUPS) | |
22 #include <cups/cups.h> | |
23 #include <cups/ppd.h> | |
24 | |
25 #include "base/file_util.h" | |
26 #endif | |
27 | |
28 #if defined(USE_CUPS) && !defined(OS_MACOSX) | |
29 namespace printing_internal { | |
30 | |
31 void parse_lpoptions(const FilePath& filepath, const std::string& printer_name, | |
32 int* num_options, cups_option_t** options) { | |
33 std::string content; | |
34 if (!file_util::ReadFileToString(filepath, &content)) | |
35 return; | |
36 | |
37 const char kDest[] = "dest"; | |
38 const char kDefault[] = "default"; | |
39 size_t kDestLen = sizeof(kDest) - 1; | |
40 size_t kDefaultLen = sizeof(kDefault) - 1; | |
41 std::vector <std::string> lines; | |
42 base::SplitString(content, '\n', &lines); | |
43 | |
44 for (size_t i = 0; i < lines.size(); ++i) { | |
45 std::string line = lines[i]; | |
46 if (line.empty()) | |
47 continue; | |
48 | |
49 if (base::strncasecmp (line.c_str(), kDefault, kDefaultLen) == 0 && | |
50 isspace(line[kDefaultLen])) { | |
51 line = line.substr(kDefaultLen); | |
52 } else if (base::strncasecmp (line.c_str(), kDest, kDestLen) == 0 && | |
53 isspace(line[kDestLen])) { | |
54 line = line.substr(kDestLen); | |
55 } else { | |
56 continue; | |
57 } | |
58 | |
59 TrimWhitespaceASCII(line, TRIM_ALL, &line); | |
60 if (line.empty()) | |
61 continue; | |
62 | |
63 size_t space_found = line.find(' '); | |
64 if (space_found == std::string::npos) | |
65 continue; | |
66 | |
67 std::string name = line.substr(0, space_found); | |
68 if (name.empty()) | |
69 continue; | |
70 | |
71 if (base::strncasecmp(printer_name.c_str(), name.c_str(), | |
72 name.length()) != 0) { | |
73 continue; // This is not the required printer. | |
74 } | |
75 | |
76 line = line.substr(space_found + 1); | |
77 TrimWhitespaceASCII(line, TRIM_ALL, &line); // Remove extra spaces. | |
78 if (line.empty()) | |
79 continue; | |
80 // Parse the selected printer custom options. | |
81 *num_options = cupsParseOptions(line.c_str(), 0, options); | |
82 } | |
83 } | |
84 | |
85 void mark_lpoptions(const std::string& printer_name, ppd_file_t** ppd) { | |
86 cups_option_t* options = NULL; | |
87 int num_options = 0; | |
88 ppdMarkDefaults(*ppd); | |
89 | |
90 const char kSystemLpOptionPath[] = "/etc/cups/lpoptions"; | |
91 const char kUserLpOptionPath[] = ".cups/lpoptions"; | |
92 | |
93 std::vector<FilePath> file_locations; | |
94 file_locations.push_back(FilePath(kSystemLpOptionPath)); | |
95 file_locations.push_back(FilePath( | |
96 file_util::GetHomeDir().Append(kUserLpOptionPath))); | |
97 | |
98 for (std::vector<FilePath>::const_iterator it = file_locations.begin(); | |
99 it != file_locations.end(); ++it) { | |
100 num_options = 0; | |
101 options = NULL; | |
102 parse_lpoptions(*it, printer_name, &num_options, &options); | |
103 if (num_options > 0 && options) { | |
104 cupsMarkOptions(*ppd, num_options, options); | |
105 cupsFreeOptions(num_options, options); | |
106 } | |
107 } | |
108 } | |
109 | |
110 } // printing_internal namespace | |
111 #endif | |
112 | |
113 namespace { | |
114 | |
115 const char kDisableColorOption[] = "disableColorOption"; | |
116 const char kSetColorAsDefault[] = "setColorAsDefault"; | |
117 const char kSetDuplexAsDefault[] = "setDuplexAsDefault"; | |
118 const char kPrinterColorModelForBlack[] = "printerColorModelForBlack"; | |
119 const char kPrinterColorModelForColor[] = "printerColorModelForColor"; | |
120 const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue"; | |
121 | |
122 #if defined(OS_WIN) | |
123 const char kPskColor[] = "psk:Color"; | |
124 const char kPskGray[] = "psk:Grayscale"; | |
125 const char kPskMonochrome[] = "psk:Monochrome"; | |
126 const char kPskDuplexFeature[] = "psk:JobDuplexAllDocumentsContiguously"; | |
127 const char kPskTwoSided[] = "psk:TwoSided"; | |
128 #elif defined(USE_CUPS) | |
129 const char kColorDevice[] = "ColorDevice"; | |
130 const char kColorModel[] = "ColorModel"; | |
131 const char kColorMode[] = "ColorMode"; | |
132 const char kProcessColorModel[] = "ProcessColorModel"; | |
133 const char kPrintoutMode[] = "PrintoutMode"; | |
134 const char kDraftGray[] = "Draft.Gray"; | |
135 const char kHighGray[] = "High.Gray"; | |
136 | |
137 const char kDuplex[] = "Duplex"; | |
138 const char kDuplexNone[] = "None"; | |
139 | |
140 bool getBasicColorModelSettings( | |
141 ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, | |
142 bool* color_is_default) { | |
143 ppd_option_t* color_model = ppdFindOption(ppd, kColorModel); | |
144 if (!color_model) | |
145 return false; | |
146 | |
147 if (ppdFindChoice(color_model, printing::kBlack)) | |
148 *color_model_for_black = printing::BLACK; | |
149 else if (ppdFindChoice(color_model, printing::kGray)) | |
150 *color_model_for_black = printing::GRAY; | |
151 | |
152 if (ppdFindChoice(color_model, printing::kColor)) | |
153 *color_model_for_color = printing::COLOR; | |
154 else if (ppdFindChoice(color_model, printing::kCMYK)) | |
155 *color_model_for_color = printing::CMYK; | |
156 else if (ppdFindChoice(color_model, printing::kRGB)) | |
157 *color_model_for_color = printing::RGB; | |
158 else if (ppdFindChoice(color_model, printing::kRGBA)) | |
159 *color_model_for_color = printing::RGBA; | |
160 else if (ppdFindChoice(color_model, printing::kRGB16)) | |
161 *color_model_for_color = printing::RGB16; | |
162 else if (ppdFindChoice(color_model, printing::kCMY)) | |
163 *color_model_for_color = printing::CMY; | |
164 else if (ppdFindChoice(color_model, printing::kKCMY)) | |
165 *color_model_for_color = printing::KCMY; | |
166 else if (ppdFindChoice(color_model, printing::kCMY_K)) | |
167 *color_model_for_color = printing::CMY_K; | |
168 | |
169 ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel); | |
170 if (!marked_choice) | |
171 marked_choice = ppdFindChoice(color_model, color_model->defchoice); | |
172 | |
173 if (marked_choice) { | |
174 *color_is_default = | |
175 (base::strcasecmp(marked_choice->choice, printing::kBlack) != 0) && | |
176 (base::strcasecmp(marked_choice->choice, printing::kGray) != 0); | |
177 } | |
178 return true; | |
179 } | |
180 | |
181 bool getPrintOutModeColorSettings( | |
182 ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, | |
183 bool* color_is_default) { | |
184 ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode); | |
185 if (!printout_mode) | |
186 return false; | |
187 | |
188 *color_model_for_color = printing::PRINTOUTMODE_NORMAL; | |
189 *color_model_for_black = printing::PRINTOUTMODE_NORMAL; | |
190 | |
191 // Check to see if NORMAL_GRAY value is supported by PrintoutMode. | |
192 // If NORMAL_GRAY is not supported, NORMAL value is used to | |
193 // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to | |
194 // represent color. | |
195 if (ppdFindChoice(printout_mode, printing::kNormalGray)) | |
196 *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY; | |
197 | |
198 // Get the default marked choice to identify the default color setting | |
199 // value. | |
200 ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode); | |
201 if (!printout_mode_choice) { | |
202 printout_mode_choice = ppdFindChoice(printout_mode, | |
203 printout_mode->defchoice); | |
204 } | |
205 if (printout_mode_choice) { | |
206 if ((base::strcasecmp(printout_mode_choice->choice, | |
207 printing::kNormalGray) == 0) || | |
208 (base::strcasecmp(printout_mode_choice->choice, kHighGray) == 0) || | |
209 (base::strcasecmp(printout_mode_choice->choice, kDraftGray) == 0)) { | |
210 *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY; | |
211 *color_is_default = false; | |
212 } | |
213 } | |
214 return true; | |
215 } | |
216 | |
217 bool getColorModeSettings( | |
218 ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, | |
219 bool* color_is_default) { | |
220 // Samsung printers use "ColorMode" attribute in their ppds. | |
221 ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode); | |
222 if (!color_mode_option) | |
223 return false; | |
224 | |
225 if (ppdFindChoice(color_mode_option, printing::kColor)) | |
226 *color_model_for_color = printing::COLORMODE_COLOR; | |
227 | |
228 if (ppdFindChoice(color_mode_option, printing::kMonochrome)) | |
229 *color_model_for_black = printing::COLORMODE_MONOCHROME; | |
230 | |
231 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode); | |
232 if (!mode_choice) { | |
233 mode_choice = ppdFindChoice(color_mode_option, | |
234 color_mode_option->defchoice); | |
235 } | |
236 | |
237 if (mode_choice) { | |
238 *color_is_default = | |
239 (base::strcasecmp(mode_choice->choice, printing::kColor) == 0); | |
240 } | |
241 return true; | |
242 } | |
243 | |
244 bool getHPColorSettings( | |
245 ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, | |
246 bool* color_is_default) { | |
247 // HP printers use "Color/Color Model" attribute in their ppds. | |
248 ppd_option_t* color_mode_option = ppdFindOption(ppd, printing::kColor); | |
249 if (!color_mode_option) | |
250 return false; | |
251 | |
252 if (ppdFindChoice(color_mode_option, printing::kColor)) | |
253 *color_model_for_color = printing::HP_COLOR_COLOR; | |
254 if (ppdFindChoice(color_mode_option, printing::kBlack)) | |
255 *color_model_for_black = printing::HP_COLOR_BLACK; | |
256 | |
257 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode); | |
258 if (!mode_choice) { | |
259 mode_choice = ppdFindChoice(color_mode_option, | |
260 color_mode_option->defchoice); | |
261 } | |
262 if (mode_choice) { | |
263 *color_is_default = | |
264 (base::strcasecmp(mode_choice->choice, printing::kColor) == 0); | |
265 } | |
266 return true; | |
267 } | |
268 | |
269 bool getProcessColorModelSettings( | |
270 ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, | |
271 bool* color_is_default) { | |
272 // Canon printers use "ProcessColorModel" attribute in their ppds. | |
273 ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel); | |
274 if (!color_mode_option) | |
275 return false; | |
276 | |
277 if (ppdFindChoice(color_mode_option, printing::kRGB)) | |
278 *color_model_for_color = printing::PROCESSCOLORMODEL_RGB; | |
279 else if (ppdFindChoice(color_mode_option, printing::kCMYK)) | |
280 *color_model_for_color = printing::PROCESSCOLORMODEL_CMYK; | |
281 | |
282 if (ppdFindChoice(color_mode_option, printing::kGreyscale)) | |
283 *color_model_for_black = printing::PROCESSCOLORMODEL_GREYSCALE; | |
284 | |
285 ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel); | |
286 if (!mode_choice) { | |
287 mode_choice = ppdFindChoice(color_mode_option, | |
288 color_mode_option->defchoice); | |
289 } | |
290 | |
291 if (mode_choice) { | |
292 *color_is_default = | |
293 (base::strcasecmp(mode_choice->choice, printing::kGreyscale) != 0); | |
294 } | |
295 return true; | |
296 } | |
297 #endif | |
298 | |
299 } // namespace | |
300 | |
301 PrintSystemTaskProxy::PrintSystemTaskProxy( | |
302 const base::WeakPtr<PrintPreviewHandler>& handler, | |
303 printing::PrintBackend* print_backend, | |
304 bool has_logged_printers_count) | |
305 : handler_(handler), | |
306 print_backend_(print_backend), | |
307 has_logged_printers_count_(has_logged_printers_count) { | |
308 } | |
309 | |
310 PrintSystemTaskProxy::~PrintSystemTaskProxy() { | |
311 } | |
312 | |
313 void PrintSystemTaskProxy::GetDefaultPrinter() { | |
314 VLOG(1) << "Get default printer start"; | |
315 StringValue* default_printer = NULL; | |
316 if (PrintPreviewHandler::last_used_printer_name_ == NULL) { | |
317 default_printer = new StringValue( | |
318 print_backend_->GetDefaultPrinterName()); | |
319 } else { | |
320 default_printer = new StringValue( | |
321 *PrintPreviewHandler::last_used_printer_name_); | |
322 } | |
323 std::string default_printer_string; | |
324 default_printer->GetAsString(&default_printer_string); | |
325 VLOG(1) << "Get default printer finished, found: " | |
326 << default_printer_string; | |
327 | |
328 StringValue* cloud_print_data = NULL; | |
329 if (PrintPreviewHandler::last_used_printer_cloud_print_data_ != NULL) { | |
330 cloud_print_data = new StringValue( | |
331 *PrintPreviewHandler::last_used_printer_cloud_print_data_); | |
332 } else { | |
333 cloud_print_data = new StringValue(""); | |
334 } | |
335 | |
336 BrowserThread::PostTask( | |
337 BrowserThread::UI, FROM_HERE, | |
338 base::Bind(&PrintSystemTaskProxy::SendDefaultPrinter, this, | |
339 default_printer, cloud_print_data)); | |
340 } | |
341 | |
342 void PrintSystemTaskProxy::SendDefaultPrinter( | |
343 const StringValue* default_printer, const StringValue* cloud_print_data) { | |
344 if (handler_) | |
345 handler_->SendDefaultPrinter(*default_printer, *cloud_print_data); | |
346 delete default_printer; | |
347 } | |
348 | |
349 void PrintSystemTaskProxy::EnumeratePrinters() { | |
350 VLOG(1) << "Enumerate printers start"; | |
351 ListValue* printers = new ListValue; | |
352 printing::PrinterList printer_list; | |
353 print_backend_->EnumeratePrinters(&printer_list); | |
354 | |
355 if (!has_logged_printers_count_) { | |
356 // Record the total number of printers. | |
357 UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters", | |
358 printer_list.size()); | |
359 } | |
360 int i = 0; | |
361 for (printing::PrinterList::iterator iter = printer_list.begin(); | |
362 iter != printer_list.end(); ++iter, ++i) { | |
363 DictionaryValue* printer_info = new DictionaryValue; | |
364 std::string printerName; | |
365 #if defined(OS_MACOSX) | |
366 // On Mac, |iter->printer_description| specifies the printer name and | |
367 // |iter->printer_name| specifies the device name / printer queue name. | |
368 printerName = iter->printer_description; | |
369 #else | |
370 printerName = iter->printer_name; | |
371 #endif | |
372 printer_info->SetString(printing::kSettingPrinterName, printerName); | |
373 printer_info->SetString(printing::kSettingDeviceName, iter->printer_name); | |
374 printers->Append(printer_info); | |
375 } | |
376 VLOG(1) << "Enumerate printers finished, found " << i << " printers"; | |
377 | |
378 BrowserThread::PostTask( | |
379 BrowserThread::UI, FROM_HERE, | |
380 base::Bind(&PrintSystemTaskProxy::SetupPrinterList, this, printers)); | |
381 } | |
382 | |
383 void PrintSystemTaskProxy::SetupPrinterList(ListValue* printers) { | |
384 if (handler_) | |
385 handler_->SetupPrinterList(*printers); | |
386 delete printers; | |
387 } | |
388 | |
389 void PrintSystemTaskProxy::GetPrinterCapabilities( | |
390 const std::string& printer_name) { | |
391 VLOG(1) << "Get printer capabilities start for " << printer_name; | |
392 printing::PrinterCapsAndDefaults printer_info; | |
393 | |
394 bool set_color_as_default = false; | |
395 bool disable_color_options = true; | |
396 bool set_duplex_as_default = false; | |
397 int printer_color_space_for_color = printing::UNKNOWN_COLOR_MODEL; | |
398 int printer_color_space_for_black = printing::UNKNOWN_COLOR_MODEL; | |
399 int default_duplex_setting_value = printing::UNKNOWN_DUPLEX_MODE; | |
400 if (!print_backend_->GetPrinterCapsAndDefaults(printer_name, | |
401 &printer_info)) { | |
402 return; | |
403 } | |
404 | |
405 #if defined(USE_CUPS) | |
406 FilePath ppd_file_path; | |
407 if (!file_util::CreateTemporaryFile(&ppd_file_path)) | |
408 return; | |
409 | |
410 int data_size = printer_info.printer_capabilities.length(); | |
411 if (data_size != file_util::WriteFile( | |
412 ppd_file_path, | |
413 printer_info.printer_capabilities.data(), | |
414 data_size)) { | |
415 file_util::Delete(ppd_file_path, false); | |
416 return; | |
417 } | |
418 | |
419 ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str()); | |
420 if (ppd) { | |
421 #if !defined(OS_MACOSX) | |
422 printing_internal::mark_lpoptions(printer_name, &ppd); | |
423 #endif | |
424 ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex); | |
425 if (duplex_choice) { | |
426 ppd_option_t* option = ppdFindOption(ppd, kDuplex); | |
427 if (option) | |
428 duplex_choice = ppdFindChoice(option, option->defchoice); | |
429 } | |
430 | |
431 if (duplex_choice) { | |
432 if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0) { | |
433 set_duplex_as_default = true; | |
434 default_duplex_setting_value = printing::LONG_EDGE; | |
435 } else { | |
436 default_duplex_setting_value = printing::SIMPLEX; | |
437 } | |
438 } | |
439 | |
440 bool is_color_device = false; | |
441 ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL); | |
442 if (attr && attr->value) | |
443 is_color_device = ppd->color_device; | |
444 disable_color_options = !is_color_device; | |
445 set_color_as_default = is_color_device; | |
446 | |
447 if (!((is_color_device && getBasicColorModelSettings( | |
448 ppd, &printer_color_space_for_black, | |
449 &printer_color_space_for_color, &set_color_as_default)) || | |
450 getPrintOutModeColorSettings( | |
451 ppd, &printer_color_space_for_black, | |
452 &printer_color_space_for_color, &set_color_as_default) || | |
453 getColorModeSettings( | |
454 ppd, &printer_color_space_for_black, | |
455 &printer_color_space_for_color, &set_color_as_default) || | |
456 getHPColorSettings( | |
457 ppd, &printer_color_space_for_black, | |
458 &printer_color_space_for_color, &set_color_as_default) || | |
459 getProcessColorModelSettings( | |
460 ppd, &printer_color_space_for_black, | |
461 &printer_color_space_for_color, &set_color_as_default))) { | |
462 VLOG(1) << "Unknown printer color model"; | |
463 } | |
464 ppdClose(ppd); | |
465 } | |
466 file_util::Delete(ppd_file_path, false); | |
467 #elif defined(OS_WIN) | |
468 // According to XPS 1.0 spec, only color printers have psk:Color. | |
469 // Therefore we don't need to parse the whole XML file, we just need to | |
470 // search the string. The spec can be found at: | |
471 // http://msdn.microsoft.com/en-us/windows/hardware/gg463431. | |
472 if (printer_info.printer_capabilities.find(kPskColor) != std::string::npos) | |
473 printer_color_space_for_color = printing::COLOR; | |
474 | |
475 if ((printer_info.printer_capabilities.find(kPskGray) != | |
476 std::string::npos) || | |
477 (printer_info.printer_capabilities.find(kPskMonochrome) != | |
478 std::string::npos)) { | |
479 printer_color_space_for_black = printing::GRAY; | |
480 } | |
481 set_color_as_default = | |
482 (printer_info.printer_defaults.find(kPskColor) != std::string::npos); | |
483 | |
484 set_duplex_as_default = | |
485 (printer_info.printer_defaults.find(kPskDuplexFeature) != | |
486 std::string::npos) && | |
487 (printer_info.printer_defaults.find(kPskTwoSided) != | |
488 std::string::npos); | |
489 | |
490 if (printer_info.printer_defaults.find(kPskDuplexFeature) != | |
491 std::string::npos) { | |
492 if (printer_info.printer_defaults.find(kPskTwoSided) != | |
493 std::string::npos) { | |
494 default_duplex_setting_value = printing::LONG_EDGE; | |
495 } else { | |
496 default_duplex_setting_value = printing::SIMPLEX; | |
497 } | |
498 } | |
499 #else | |
500 NOTIMPLEMENTED(); | |
501 #endif | |
502 disable_color_options = !printer_color_space_for_color || | |
503 !printer_color_space_for_black || | |
504 (printer_color_space_for_color == | |
505 printer_color_space_for_black); | |
506 | |
507 DictionaryValue settings_info; | |
508 settings_info.SetBoolean(kDisableColorOption, disable_color_options); | |
509 if (printer_color_space_for_color == printing::UNKNOWN_COLOR_MODEL) | |
510 printer_color_space_for_color = printing::COLOR; | |
511 | |
512 if (printer_color_space_for_black == printing::UNKNOWN_COLOR_MODEL) | |
513 printer_color_space_for_black = printing::GRAY; | |
514 | |
515 if (disable_color_options || | |
516 PrintPreviewHandler::last_used_color_model_ == | |
517 printing::UNKNOWN_COLOR_MODEL) { | |
518 settings_info.SetBoolean(kSetColorAsDefault, set_color_as_default); | |
519 } else { | |
520 settings_info.SetBoolean(kSetColorAsDefault, | |
521 printing::isColorModelSelected( | |
522 PrintPreviewHandler::last_used_color_model_)); | |
523 } | |
524 | |
525 settings_info.SetBoolean(kSetDuplexAsDefault, set_duplex_as_default); | |
526 settings_info.SetInteger(kPrinterColorModelForColor, | |
527 printer_color_space_for_color); | |
528 settings_info.SetInteger(kPrinterColorModelForBlack, | |
529 printer_color_space_for_black); | |
530 settings_info.SetInteger(kPrinterDefaultDuplexValue, | |
531 default_duplex_setting_value); | |
532 BrowserThread::PostTask( | |
533 BrowserThread::UI, FROM_HERE, | |
534 base::Bind(&PrintSystemTaskProxy::SendPrinterCapabilities, this, | |
535 settings_info.DeepCopy())); | |
536 } | |
537 | |
538 void PrintSystemTaskProxy::SendPrinterCapabilities( | |
539 DictionaryValue* settings_info) { | |
540 if (handler_) | |
541 handler_->SendPrinterCapabilities(*settings_info); | |
542 delete settings_info; | |
543 } | |
OLD | NEW |