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/service/cloud_print/cloud_print_connector.h" | |
6 | |
7 #include "base/md5.h" | |
8 #include "base/rand_util.h" | |
9 #include "base/string_number_conversions.h" | |
10 #include "base/string_split.h" | |
11 #include "base/stringprintf.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "base/values.h" | |
14 #include "chrome/service/cloud_print/cloud_print_consts.h" | |
15 #include "chrome/service/cloud_print/cloud_print_helpers.h" | |
16 #include "grit/generated_resources.h" | |
17 #include "ui/base/l10n/l10n_util.h" | |
18 | |
19 CloudPrintConnector::CloudPrintConnector( | |
20 Client* client, | |
21 const std::string& proxy_id, | |
22 const GURL& cloud_print_server_url, | |
23 const DictionaryValue* print_system_settings) | |
24 : client_(client), | |
25 proxy_id_(proxy_id), | |
26 cloud_print_server_url_(cloud_print_server_url), | |
27 next_response_handler_(NULL) { | |
28 if (print_system_settings) { | |
29 // It is possible to have no print settings specified. | |
30 print_system_settings_.reset(print_system_settings->DeepCopy()); | |
31 } | |
32 } | |
33 | |
34 CloudPrintConnector::~CloudPrintConnector() { | |
35 } | |
36 | |
37 bool CloudPrintConnector::Start() { | |
38 DCHECK(!print_system_.get()); | |
39 VLOG(1) << "CP_CONNECTOR: Starting connector, id: " << proxy_id_; | |
40 | |
41 pedning_tasks_.clear(); | |
Scott Byer
2011/10/27 22:22:50
spelling: pending_tasks
| |
42 | |
43 print_system_ = | |
44 cloud_print::PrintSystem::CreateInstance(print_system_settings_.get()); | |
45 if (!print_system_.get()) { | |
46 NOTREACHED(); | |
47 return false; // No print system available, fail initalization. | |
48 } | |
49 cloud_print::PrintSystem::PrintSystemResult result = print_system_->Init(); | |
50 if (!result.succeeded()) { | |
51 // We could not initialize the print system. We need to notify the server. | |
52 ReportUserMessage(kPrintSystemFailedMessageId, result.message()); | |
53 print_system_.release(); | |
54 return false; | |
55 } | |
56 | |
57 // Start watching for updates from the print system. | |
58 print_server_watcher_ = print_system_->CreatePrintServerWatcher(); | |
59 print_server_watcher_->StartWatching(this); | |
60 | |
61 // Get list of registered printers. | |
62 AddPendingAvailableTask(); | |
63 return true; | |
64 } | |
65 | |
66 void CloudPrintConnector::Stop() { | |
67 VLOG(1) << "CP_CONNECTOR: Stopping connector, id: " << proxy_id_; | |
68 DCHECK(print_system_.get()); | |
69 if (print_system_.get()) { | |
70 // Do uninitialization here. | |
71 pedning_tasks_.clear(); | |
72 print_server_watcher_.release(); | |
73 print_system_.release(); | |
74 } | |
75 } | |
76 | |
77 bool CloudPrintConnector::IsRunning() { | |
78 return print_system_.get() != NULL; | |
79 } | |
80 | |
81 void CloudPrintConnector::RegisterPrinters( | |
82 const printing::PrinterList& printers) { | |
83 if (!IsRunning()) | |
84 return; | |
85 printing::PrinterList::const_iterator it; | |
86 for (it = printers.begin(); it != printers.end(); ++it) { | |
87 AddPendingRegisterTask(*it); | |
88 } | |
89 } | |
90 | |
91 // Check for jobs for specific printer | |
92 void CloudPrintConnector::CheckForJobs(const std::string& reason, | |
93 const std::string& printer_id) { | |
94 if (!IsRunning()) | |
95 return; | |
96 if (!printer_id.empty()) { | |
97 JobHandlerMap::iterator index = job_handler_map_.find(printer_id); | |
98 if (index != job_handler_map_.end()) | |
99 index->second->CheckForJobs(reason); | |
100 } else { | |
101 for (JobHandlerMap::iterator index = job_handler_map_.begin(); | |
102 index != job_handler_map_.end(); index++) { | |
103 index->second->CheckForJobs(reason); | |
104 } | |
105 } | |
106 } | |
107 | |
108 void CloudPrintConnector::OnPrinterAdded() { | |
109 AddPendingAvailableTask(); | |
110 } | |
111 | |
112 void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) { | |
113 AddPendingDeleteTask(printer_id); | |
114 } | |
115 | |
116 void CloudPrintConnector::OnAuthError() { | |
117 client_->OnAuthFailed(); | |
118 } | |
119 | |
120 // CloudPrintURLFetcher::Delegate implementation. | |
121 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleRawData( | |
122 const content::URLFetcher* source, | |
123 const GURL& url, | |
124 const std::string& data) { | |
125 // If this notification came as a result of user message call, stop it. | |
126 // Otherwise proceed continue processing. | |
127 if (user_message_request_.get() && | |
128 user_message_request_->IsSameRequest(source)) | |
129 return CloudPrintURLFetcher::STOP_PROCESSING; | |
130 return CloudPrintURLFetcher::CONTINUE_PROCESSING; | |
131 } | |
132 | |
133 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleJSONData( | |
134 const content::URLFetcher* source, | |
135 const GURL& url, | |
136 DictionaryValue* json_data, | |
137 bool succeeded) { | |
138 if (!IsRunning()) // Orphant response. Connector has been stopped already. | |
139 return CloudPrintURLFetcher::STOP_PROCESSING; | |
140 | |
141 DCHECK(next_response_handler_); | |
142 return (this->*next_response_handler_)(source, url, json_data, succeeded); | |
143 } | |
144 | |
145 CloudPrintURLFetcher::ResponseAction | |
146 CloudPrintConnector::HandlePrinterListResponse( | |
147 const content::URLFetcher* source, | |
148 const GURL& url, | |
149 DictionaryValue* json_data, | |
150 bool succeeded) { | |
151 DCHECK(succeeded); | |
152 if (!succeeded) | |
153 return CloudPrintURLFetcher::RETRY_REQUEST; | |
154 | |
155 // Now we need to get the list of printers from the print system | |
156 // and split printers into 3 categories: | |
157 // - existing and registered printers | |
158 // - new printers | |
159 // - deleted printers | |
160 | |
161 // Get list of the printers from the print system. | |
162 printing::PrinterList local_printers; | |
163 cloud_print::PrintSystem::PrintSystemResult result = | |
164 print_system_->EnumeratePrinters(&local_printers); | |
165 bool full_list = result.succeeded(); | |
166 if (!result.succeeded()) { | |
167 std::string message = result.message(); | |
168 if (message.empty()) | |
169 message = l10n_util::GetStringUTF8(IDS_CLOUD_PRINT_ENUM_FAILED); | |
170 // There was a failure enumerating printers. Send a message to the server. | |
171 ReportUserMessage(kEnumPrintersFailedMessageId, message); | |
172 } | |
173 | |
174 // Go through the list of the cloud printers and init print job handlers. | |
175 ListValue* printer_list = NULL; | |
176 // There may be no "printers" value in the JSON | |
177 if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) { | |
178 for (size_t index = 0; index < printer_list->GetSize(); index++) { | |
179 DictionaryValue* printer_data = NULL; | |
180 if (printer_list->GetDictionary(index, &printer_data)) { | |
181 std::string printer_name; | |
182 printer_data->GetString(kNameValue, &printer_name); | |
183 if (RemovePrinterFromList(printer_name, &local_printers)) { | |
184 InitJobHandlerForPrinter(printer_data); | |
185 } else { | |
186 // Cloud printer is not found on the local system. | |
187 if (full_list) { // Delete only if we get the full list of printer. | |
188 std::string printer_id; | |
189 printer_data->GetString(kIdValue, &printer_id); | |
190 AddPendingDeleteTask(printer_id); | |
191 } | |
192 } | |
193 } else { | |
194 NOTREACHED(); | |
195 } | |
196 } | |
197 } | |
198 | |
199 request_ = NULL; | |
200 if (!local_printers.empty()) { | |
201 // Notify client that we have a list of printers available. | |
202 // Client will call us back to finish registration | |
203 client_->OnPrintersAvailable(local_printers); | |
204 } | |
205 ContinuePendingTaskProcessing(); // Continue processing background tasks. | |
206 return CloudPrintURLFetcher::STOP_PROCESSING; | |
207 } | |
208 | |
209 CloudPrintURLFetcher::ResponseAction | |
210 CloudPrintConnector::HandlePrinterDeleteResponse( | |
211 const content::URLFetcher* source, | |
212 const GURL& url, | |
213 DictionaryValue* json_data, | |
214 bool succeeded) { | |
215 VLOG(1) << "CP_CONNECTOR: Handler printer delete response, succeeded:" | |
216 << succeeded << " url: " << url; | |
217 ContinuePendingTaskProcessing(); // Continue processing background tasks. | |
218 return CloudPrintURLFetcher::STOP_PROCESSING; | |
219 } | |
220 | |
221 CloudPrintURLFetcher::ResponseAction | |
222 CloudPrintConnector::HandleRegisterPrinterResponse( | |
223 const content::URLFetcher* source, | |
224 const GURL& url, | |
225 DictionaryValue* json_data, | |
226 bool succeeded) { | |
227 VLOG(1) << "CP_CONNECTOR: Handler printer register response, succeeded:" | |
228 << succeeded << " url: " << url; | |
229 if (succeeded) { | |
230 ListValue* printer_list = NULL; | |
231 // There should be a "printers" value in the JSON | |
232 if (json_data->GetList(kPrinterListValue, &printer_list)) { | |
233 DictionaryValue* printer_data = NULL; | |
234 if (printer_list->GetDictionary(0, &printer_data)) | |
235 InitJobHandlerForPrinter(printer_data); | |
236 } | |
237 } | |
238 ContinuePendingTaskProcessing(); // Continue processing background tasks. | |
239 return CloudPrintURLFetcher::STOP_PROCESSING; | |
240 } | |
241 | |
242 | |
243 CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() { | |
244 OnAuthError(); | |
245 return CloudPrintURLFetcher::STOP_PROCESSING; | |
246 } | |
247 | |
248 std::string CloudPrintConnector::GetAuthHeader() { | |
249 return CloudPrintHelpers::GetCloudPrintAuthHeader(); | |
250 } | |
251 | |
252 void CloudPrintConnector::StartGetRequest(const GURL& url, | |
253 int max_retries, | |
254 ResponseHandler handler) { | |
255 next_response_handler_ = handler; | |
256 request_ = new CloudPrintURLFetcher; | |
257 request_->StartGetRequest(url, this, max_retries, std::string()); | |
258 } | |
259 | |
260 void CloudPrintConnector::StartPostRequest(const GURL& url, | |
261 int max_retries, | |
262 const std::string& mime_type, | |
263 const std::string& post_data, | |
264 ResponseHandler handler) { | |
265 next_response_handler_ = handler; | |
266 request_ = new CloudPrintURLFetcher; | |
267 request_->StartPostRequest( | |
268 url, this, max_retries, mime_type, post_data, std::string()); | |
269 } | |
270 | |
271 void CloudPrintConnector::ReportUserMessage(const std::string& message_id, | |
272 const std::string& failure_msg) { | |
273 // This is a fire and forget type of function. | |
274 // Result of this request will be ignored. | |
275 std::string mime_boundary; | |
276 CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); | |
277 GURL url = CloudPrintHelpers::GetUrlForUserMessage(cloud_print_server_url_, | |
278 message_id); | |
279 std::string post_data; | |
280 CloudPrintHelpers::AddMultipartValueForUpload(kMessageTextValue, | |
281 failure_msg, | |
282 mime_boundary, | |
283 std::string(), | |
284 &post_data); | |
285 // Terminate the request body | |
286 post_data.append("--" + mime_boundary + "--\r\n"); | |
287 std::string mime_type("multipart/form-data; boundary="); | |
288 mime_type += mime_boundary; | |
289 user_message_request_ = new CloudPrintURLFetcher; | |
290 user_message_request_->StartPostRequest(url, this, 1, mime_type, post_data, | |
291 std::string()); | |
292 } | |
293 | |
294 bool CloudPrintConnector::RemovePrinterFromList( | |
295 const std::string& printer_name, | |
296 printing::PrinterList* printer_list) { | |
297 for (printing::PrinterList::iterator index = printer_list->begin(); | |
298 index != printer_list->end(); index++) { | |
299 if (IsSamePrinter(index->printer_name, printer_name)) { | |
300 index = printer_list->erase(index); | |
301 return true; | |
302 } | |
303 } | |
304 return false; | |
305 } | |
306 | |
307 void CloudPrintConnector::InitJobHandlerForPrinter( | |
308 DictionaryValue* printer_data) { | |
309 DCHECK(printer_data); | |
310 PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud; | |
311 printer_data->GetString(kIdValue, &printer_info_cloud.printer_id); | |
312 DCHECK(!printer_info_cloud.printer_id.empty()); | |
313 VLOG(1) << "CP_CONNECTOR: Init job handler for printer id: " | |
314 << printer_info_cloud.printer_id; | |
315 JobHandlerMap::iterator index = job_handler_map_.find( | |
316 printer_info_cloud.printer_id); | |
317 if (index != job_handler_map_.end()) | |
318 return; // Nothing to do if we already have a job handler for this printer. | |
319 | |
320 printing::PrinterBasicInfo printer_info; | |
321 printer_data->GetString(kNameValue, &printer_info.printer_name); | |
322 DCHECK(!printer_info.printer_name.empty()); | |
323 printer_data->GetString(kPrinterDescValue, | |
324 &printer_info.printer_description); | |
325 // Printer status is a string value which actually contains an integer. | |
326 std::string printer_status; | |
327 if (printer_data->GetString(kPrinterStatusValue, &printer_status)) { | |
328 base::StringToInt(printer_status, &printer_info.printer_status); | |
329 } | |
330 printer_data->GetString(kPrinterCapsHashValue, | |
331 &printer_info_cloud.caps_hash); | |
332 ListValue* tags_list = NULL; | |
333 if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) { | |
334 for (size_t index = 0; index < tags_list->GetSize(); index++) { | |
335 std::string tag; | |
336 if (tags_list->GetString(index, &tag) && | |
337 StartsWithASCII(tag, kTagsHashTagName, false)) { | |
338 std::vector<std::string> tag_parts; | |
339 base::SplitStringDontTrim(tag, '=', &tag_parts); | |
340 DCHECK_EQ(tag_parts.size(), 2U); | |
341 if (tag_parts.size() == 2) | |
342 printer_info_cloud.tags_hash = tag_parts[1]; | |
343 } | |
344 } | |
345 } | |
346 scoped_refptr<PrinterJobHandler> job_handler; | |
347 job_handler = new PrinterJobHandler(printer_info, | |
348 printer_info_cloud, | |
349 cloud_print_server_url_, | |
350 print_system_.get(), | |
351 this); | |
352 job_handler_map_[printer_info_cloud.printer_id] = job_handler; | |
353 job_handler->Initialize(); | |
354 } | |
355 | |
356 void CloudPrintConnector::AddPendingAvailableTask() { | |
357 PendingTask task; | |
358 task.type = PEDNING_PRINTERS_AVAILABLE; | |
Scott Byer
2011/10/27 22:22:50
spelling -> PENDING
| |
359 AddPendingTask(task); | |
360 } | |
361 | |
362 void CloudPrintConnector::AddPendingDeleteTask(const std::string& id) { | |
363 PendingTask task; | |
364 task.type = PEDNING_PRINTER_DELETE; | |
365 task.printer_id = id; | |
366 AddPendingTask(task); | |
367 } | |
368 | |
369 void CloudPrintConnector::AddPendingRegisterTask( | |
370 const printing::PrinterBasicInfo& info) { | |
371 PendingTask task; | |
372 task.type = PEDNING_PRINTER_REGISTER; | |
373 task.printer_info = info; | |
374 AddPendingTask(task); | |
375 } | |
376 | |
377 void CloudPrintConnector::AddPendingTask(const PendingTask& task) { | |
378 pedning_tasks_.push_back(task); | |
379 // If this is the only pending task, we need to start the process. | |
380 if (pedning_tasks_.size() == 1) { | |
381 MessageLoop::current()->PostTask( | |
382 FROM_HERE, NewRunnableMethod( | |
383 this, &CloudPrintConnector::ProcessPendingTask)); | |
384 } | |
385 } | |
386 | |
387 void CloudPrintConnector::ProcessPendingTask() { | |
388 if (!IsRunning()) | |
389 return; // Orphant call. | |
390 if (pedning_tasks_.size() == 0) | |
391 return; // No peding tasks. | |
392 | |
393 PendingTask task = pedning_tasks_.front(); | |
394 | |
395 switch (task.type) { | |
396 case PEDNING_PRINTERS_AVAILABLE : | |
397 OnPrintersAvailable(); | |
398 break; | |
399 case PEDNING_PRINTER_REGISTER : | |
400 OnPrinterRegister(task.printer_info); | |
401 break; | |
402 case PEDNING_PRINTER_DELETE : | |
403 OnPrinterDelete(task.printer_id); | |
404 break; | |
405 default: | |
406 NOTREACHED(); | |
407 } | |
408 } | |
409 | |
410 void CloudPrintConnector::OnPrintersAvailable() { | |
411 GURL printer_list_url = | |
412 CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_, | |
413 proxy_id_); | |
414 StartGetRequest(printer_list_url, | |
415 kCloudPrintRegisterMaxRetryCount, | |
416 &CloudPrintConnector::HandlePrinterListResponse); | |
417 } | |
418 | |
419 void CloudPrintConnector::OnPrinterRegister( | |
420 const printing::PrinterBasicInfo& info) { | |
421 for (JobHandlerMap::iterator it = job_handler_map_.begin(); | |
422 it != job_handler_map_.end(); ++it) { | |
423 if (IsSamePrinter(it->second->GetPrinterName(), info.printer_name)) { | |
424 // Printer already registered, continue to the next task. | |
425 ContinuePendingTaskProcessing(); | |
426 return; | |
427 } | |
428 } | |
429 | |
430 cloud_print::PrintSystem::PrinterCapsAndDefaultsCallback* callback = | |
431 NewCallback(this, &CloudPrintConnector::OnReceivePrinterCaps); | |
432 // Asnchronously fetch the printer caps and defaults. The story will | |
433 // continue in OnReceivePrinterCaps. | |
434 print_system_->GetPrinterCapsAndDefaults( | |
435 info.printer_name.c_str(), callback); | |
436 } | |
437 | |
438 void CloudPrintConnector::OnPrinterDelete(const std::string& printer_id) { | |
439 // Remove corresponding printer job handler. | |
440 JobHandlerMap::iterator it = job_handler_map_.find(printer_id); | |
441 if (it != job_handler_map_.end()) { | |
442 it->second->Shutdown(); | |
443 job_handler_map_.erase(it); | |
444 } | |
445 | |
446 // TODO(gene): We probably should not try indefinitely here. Just once or | |
447 // twice should be enough. | |
448 // Bug: http://code.google.com/p/chromium/issues/detail?id=101850 | |
449 GURL url = CloudPrintHelpers::GetUrlForPrinterDelete(cloud_print_server_url_, | |
450 printer_id); | |
451 StartGetRequest(url, | |
452 kCloudPrintAPIMaxRetryCount, | |
453 &CloudPrintConnector::HandlePrinterDeleteResponse); | |
454 } | |
455 | |
456 void CloudPrintConnector::ContinuePendingTaskProcessing() { | |
457 if (pedning_tasks_.size() == 0) | |
458 return; // No peding tasks. | |
459 | |
460 // Delete current task and repost if we have more task avaialble. | |
461 pedning_tasks_.pop_front(); | |
462 if (pedning_tasks_.size() != 0) { | |
463 MessageLoop::current()->PostTask( | |
464 FROM_HERE, NewRunnableMethod( | |
465 this, &CloudPrintConnector::ProcessPendingTask)); | |
466 } | |
467 } | |
468 | |
469 void CloudPrintConnector::OnReceivePrinterCaps( | |
470 bool succeeded, | |
471 const std::string& printer_name, | |
472 const printing::PrinterCapsAndDefaults& caps_and_defaults) { | |
473 if (!IsRunning()) | |
474 return; // Orphant call. | |
475 DCHECK(pedning_tasks_.size() > 0 && | |
476 pedning_tasks_.front().type == PEDNING_PRINTER_REGISTER); | |
477 | |
478 if (!succeeded) { | |
479 LOG(ERROR) << "CP_CONNECTOR: Failed to get printer info for: " << | |
480 printer_name; | |
481 // This printer failed to register, notify the server of this failure. | |
482 string16 printer_name_utf16 = UTF8ToUTF16(printer_name); | |
483 std::string status_message = l10n_util::GetStringFUTF8( | |
484 IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED, | |
485 printer_name_utf16); | |
486 ReportUserMessage(kGetPrinterCapsFailedMessageId, status_message); | |
487 | |
488 ContinuePendingTaskProcessing(); // Skip this printer registration. | |
489 return; | |
490 } | |
491 | |
492 const printing::PrinterBasicInfo& info = pedning_tasks_.front().printer_info; | |
493 DCHECK(IsSamePrinter(info.printer_name, printer_name)); | |
494 | |
495 std::string mime_boundary; | |
496 CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); | |
497 std::string post_data; | |
498 | |
499 CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, | |
500 mime_boundary, | |
501 std::string(), &post_data); | |
502 CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, | |
503 info.printer_name, | |
504 mime_boundary, | |
505 std::string(), &post_data); | |
506 CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue, | |
507 info.printer_description, | |
508 mime_boundary, | |
509 std::string() , &post_data); | |
510 CloudPrintHelpers::AddMultipartValueForUpload( | |
511 kPrinterStatusValue, base::StringPrintf("%d", info.printer_status), | |
512 mime_boundary, std::string(), &post_data); | |
513 // Add printer options as tags. | |
514 CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(info.options, | |
515 mime_boundary, | |
516 &post_data); | |
517 | |
518 CloudPrintHelpers::AddMultipartValueForUpload( | |
519 kPrinterCapsValue, caps_and_defaults.printer_capabilities, | |
520 mime_boundary, caps_and_defaults.caps_mime_type, | |
521 &post_data); | |
522 CloudPrintHelpers::AddMultipartValueForUpload( | |
523 kPrinterDefaultsValue, caps_and_defaults.printer_defaults, | |
524 mime_boundary, caps_and_defaults.defaults_mime_type, | |
525 &post_data); | |
526 // Send a hash of the printer capabilities to the server. We will use this | |
527 // later to check if the capabilities have changed | |
528 CloudPrintHelpers::AddMultipartValueForUpload( | |
529 kPrinterCapsHashValue, | |
530 base::MD5String(caps_and_defaults.printer_capabilities), | |
531 mime_boundary, std::string(), &post_data); | |
532 | |
533 // Terminate the request body | |
534 post_data.append("--" + mime_boundary + "--\r\n"); | |
535 std::string mime_type("multipart/form-data; boundary="); | |
536 mime_type += mime_boundary; | |
537 | |
538 GURL post_url = CloudPrintHelpers::GetUrlForPrinterRegistration( | |
539 cloud_print_server_url_); | |
540 StartPostRequest(post_url, | |
541 kCloudPrintAPIMaxRetryCount, | |
542 mime_type, | |
543 post_data, | |
544 &CloudPrintConnector::HandleRegisterPrinterResponse); | |
545 } | |
546 | |
547 bool CloudPrintConnector::IsSamePrinter(const std::string& name1, | |
548 const std::string& name2) const { | |
549 return (0 == base::strcasecmp(name1.c_str(), name2.c_str())); | |
550 } | |
551 | |
OLD | NEW |