Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(264)

Side by Side Diff: chrome/browser/chromeos/extensions/file_manager/event_router.cc

Issue 107813005: Files.app: Use types and constants generated by the IDL compiler in the event router. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix tests. Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/extensions/event_names.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/browser/chromeos/extensions/file_manager/event_router.h" 5 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h" 10 #include "base/prefs/pref_change_registrar.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
43 using chromeos::NetworkHandler; 43 using chromeos::NetworkHandler;
44 using content::BrowserThread; 44 using content::BrowserThread;
45 using drive::DriveIntegrationService; 45 using drive::DriveIntegrationService;
46 using drive::DriveIntegrationServiceFactory; 46 using drive::DriveIntegrationServiceFactory;
47 47
48 namespace file_browser_private = extensions::api::file_browser_private; 48 namespace file_browser_private = extensions::api::file_browser_private;
49 49
50 namespace file_manager { 50 namespace file_manager {
51 namespace { 51 namespace {
52 52
53 const char kPathChanged[] = "changed";
54 const char kPathWatchError[] = "error";
55
56 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path, 53 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
57 const base::Closure& success_callback, 54 const base::Closure& success_callback,
58 const base::Closure& failure_callback) { 55 const base::Closure& failure_callback) {
59 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 56 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
60 57
61 if (base::DirectoryExists(directory_path)) 58 if (base::DirectoryExists(directory_path))
62 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback); 59 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
63 else 60 else
64 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback); 61 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
65 }; 62 };
(...skipping 19 matching lines...) Expand all
85 82
86 // Frequency of sending onFileTransferUpdated. 83 // Frequency of sending onFileTransferUpdated.
87 const int64 kFileTransferEventFrequencyInMilliseconds = 1000; 84 const int64 kFileTransferEventFrequencyInMilliseconds = 1000;
88 85
89 // Utility function to check if |job_info| is a file uploading job. 86 // Utility function to check if |job_info| is a file uploading job.
90 bool IsUploadJob(drive::JobType type) { 87 bool IsUploadJob(drive::JobType type) {
91 return (type == drive::TYPE_UPLOAD_NEW_FILE || 88 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
92 type == drive::TYPE_UPLOAD_EXISTING_FILE); 89 type == drive::TYPE_UPLOAD_EXISTING_FILE);
93 } 90 }
94 91
95 // Converts the job info to its JSON (Value) form. 92 // Converts the job info to a IDL generated type.
96 scoped_ptr<base::DictionaryValue> JobInfoToDictionaryValue( 93 void JobInfoToTransferStatus(
97 const std::string& extension_id, 94 const std::string& extension_id,
98 const std::string& job_status, 95 const std::string& job_status,
99 const drive::JobInfo& job_info) { 96 const drive::JobInfo& job_info,
97 file_browser_private::FileTransferStatus* status) {
100 DCHECK(IsActiveFileTransferJobInfo(job_info)); 98 DCHECK(IsActiveFileTransferJobInfo(job_info));
101 99
102 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); 100 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
103 GURL url = util::ConvertRelativeFilePathToFileSystemUrl( 101 GURL url = util::ConvertRelativeFilePathToFileSystemUrl(
104 job_info.file_path, extension_id); 102 job_info.file_path, extension_id);
105 result->SetString("fileUrl", url.spec()); 103 status->file_url = url.spec();
106 result->SetString("transferState", job_status); 104 status->transfer_state = file_browser_private::ParseTransferState(job_status);
107 result->SetString("transferType", 105 status->transfer_type =
108 IsUploadJob(job_info.job_type) ? "upload" : "download"); 106 IsUploadJob(job_info.job_type) ?
107 file_browser_private::TRANSFER_TYPE_UPLOAD :
108 file_browser_private::TRANSFER_TYPE_DOWNLOAD;
109 // JavaScript does not have 64-bit integers. Instead we use double, which 109 // JavaScript does not have 64-bit integers. Instead we use double, which
110 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice 110 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
111 // in C++. Larger values are rounded. 111 // in C++. Larger values are rounded.
112 result->SetDouble("processed", 112 status->processed.reset(
113 static_cast<double>(job_info.num_completed_bytes)); 113 new double(static_cast<double>(job_info.num_completed_bytes)));
114 result->SetDouble("total", static_cast<double>(job_info.num_total_bytes)); 114 status->total.reset(
115 return result.Pass(); 115 new double(static_cast<double>(job_info.num_total_bytes)));
116 } 116 }
117 117
118 // Checks for availability of the Google+ Photos app. 118 // Checks for availability of the Google+ Photos app.
119 bool IsGooglePhotosInstalled(Profile *profile) { 119 bool IsGooglePhotosInstalled(Profile *profile) {
120 ExtensionService* service = 120 ExtensionService* service =
121 extensions::ExtensionSystem::Get(profile)->extension_service(); 121 extensions::ExtensionSystem::Get(profile)->extension_service();
122 if (!service) 122 if (!service)
123 return false; 123 return false;
124 124
125 // Google+ Photos uses several ids for different channels. Therefore, all of 125 // Google+ Photos uses several ids for different channels. Therefore, all of
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 profile, volume_info, &event.volume_metadata); 218 profile, volume_info, &event.volume_metadata);
219 219
220 if (!volume_info.mount_path.empty() && 220 if (!volume_info.mount_path.empty() &&
221 event.volume_metadata.mount_path.empty()) { 221 event.volume_metadata.mount_path.empty()) {
222 event.status = 222 event.status =
223 file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED; 223 file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
224 } 224 }
225 225
226 BroadcastEvent( 226 BroadcastEvent(
227 profile, 227 profile,
228 extensions::event_names::kOnFileBrowserMountCompleted, 228 file_browser_private::OnMountCompleted::kEventName,
229 file_browser_private::OnMountCompleted::Create(event)); 229 file_browser_private::OnMountCompleted::Create(event));
230 } 230 }
231 231
232 file_browser_private::CopyProgressStatusType 232 file_browser_private::CopyProgressStatusType
233 CopyProgressTypeToCopyProgressStatusType( 233 CopyProgressTypeToCopyProgressStatusType(
234 fileapi::FileSystemOperation::CopyProgressType type) { 234 fileapi::FileSystemOperation::CopyProgressType type) {
235 switch (type) { 235 switch (type) {
236 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY: 236 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
237 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY; 237 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
238 case fileapi::FileSystemOperation::END_COPY_ENTRY: 238 case fileapi::FileSystemOperation::END_COPY_ENTRY:
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 status.destination_url.reset(new std::string(destination_url.spec())); 421 status.destination_url.reset(new std::string(destination_url.spec()));
422 } else { 422 } else {
423 // Send error event. 423 // Send error event.
424 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR; 424 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
425 status.error.reset( 425 status.error.reset(
426 new int(fileapi::PlatformFileErrorToWebFileError(error))); 426 new int(fileapi::PlatformFileErrorToWebFileError(error)));
427 } 427 }
428 428
429 BroadcastEvent( 429 BroadcastEvent(
430 profile_, 430 profile_,
431 extensions::event_names::kOnFileBrowserCopyProgress, 431 file_browser_private::OnCopyProgress::kEventName,
432 file_browser_private::OnCopyProgress::Create(copy_id, status)); 432 file_browser_private::OnCopyProgress::Create(copy_id, status));
433 } 433 }
434 434
435 void EventRouter::OnCopyProgress( 435 void EventRouter::OnCopyProgress(
436 int copy_id, 436 int copy_id,
437 fileapi::FileSystemOperation::CopyProgressType type, 437 fileapi::FileSystemOperation::CopyProgressType type,
438 const GURL& source_url, 438 const GURL& source_url,
439 const GURL& destination_url, 439 const GURL& destination_url,
440 int64 size) { 440 int64 size) {
441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
442 442
443 file_browser_private::CopyProgressStatus status; 443 file_browser_private::CopyProgressStatus status;
444 status.type = CopyProgressTypeToCopyProgressStatusType(type); 444 status.type = CopyProgressTypeToCopyProgressStatusType(type);
445 status.source_url.reset(new std::string(source_url.spec())); 445 status.source_url.reset(new std::string(source_url.spec()));
446 if (type == fileapi::FileSystemOperation::END_COPY_ENTRY) 446 if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
447 status.destination_url.reset(new std::string(destination_url.spec())); 447 status.destination_url.reset(new std::string(destination_url.spec()));
448 if (type == fileapi::FileSystemOperation::PROGRESS) 448 if (type == fileapi::FileSystemOperation::PROGRESS)
449 status.size.reset(new double(size)); 449 status.size.reset(new double(size));
450 450
451 BroadcastEvent( 451 BroadcastEvent(
452 profile_, 452 profile_,
453 extensions::event_names::kOnFileBrowserCopyProgress, 453 file_browser_private::OnCopyProgress::kEventName,
454 file_browser_private::OnCopyProgress::Create(copy_id, status)); 454 file_browser_private::OnCopyProgress::Create(copy_id, status));
455 } 455 }
456 456
457 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) { 457 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
458 if (!profile_ || 458 if (!profile_ ||
459 !extensions::ExtensionSystem::Get(profile_)->event_router()) { 459 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
460 NOTREACHED(); 460 NOTREACHED();
461 return; 461 return;
462 } 462 }
463 463
464 BroadcastEvent( 464 BroadcastEvent(
465 profile_, 465 profile_,
466 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged, 466 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
467 make_scoped_ptr(new base::ListValue)); 467 file_browser_private::OnDriveConnectionStatusChanged::Create());
468 } 468 }
469 469
470 void EventRouter::OnFileManagerPrefsChanged() { 470 void EventRouter::OnFileManagerPrefsChanged() {
471 if (!profile_ || 471 if (!profile_ ||
472 !extensions::ExtensionSystem::Get(profile_)->event_router()) { 472 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
473 NOTREACHED(); 473 NOTREACHED();
474 return; 474 return;
475 } 475 }
476 476
477 BroadcastEvent( 477 BroadcastEvent(
478 profile_, 478 profile_,
479 extensions::event_names::kOnFileBrowserPreferencesChanged, 479 file_browser_private::OnPreferencesChanged::kEventName,
480 make_scoped_ptr(new base::ListValue)); 480 file_browser_private::OnPreferencesChanged::Create());
481 } 481 }
482 482
483 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) { 483 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
485 OnJobUpdated(job_info); 485 OnJobUpdated(job_info);
486 } 486 }
487 487
488 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) { 488 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490 if (!drive::IsActiveFileTransferJobInfo(job_info)) 490 if (!drive::IsActiveFileTransferJobInfo(job_info))
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 // amount of time passes after the previous one. This is to avoid 531 // amount of time passes after the previous one. This is to avoid
532 // flooding the IPC between extensions by many onFileTransferUpdated events. 532 // flooding the IPC between extensions by many onFileTransferUpdated events.
533 if (!always) { 533 if (!always) {
534 const int64 delta = (now - last_file_transfer_event_).InMilliseconds(); 534 const int64 delta = (now - last_file_transfer_event_).InMilliseconds();
535 // delta < 0 may rarely happen if system clock is synced and rewinded. 535 // delta < 0 may rarely happen if system clock is synced and rewinded.
536 // To be conservative, we don't skip in that case. 536 // To be conservative, we don't skip in that case.
537 if (0 <= delta && delta < kFileTransferEventFrequencyInMilliseconds) 537 if (0 <= delta && delta < kFileTransferEventFrequencyInMilliseconds)
538 return; 538 return;
539 } 539 }
540 540
541 // Convert the current |drive_jobs_| to a JSON value. 541 // Convert the current |drive_jobs_| to IDL type.
542 scoped_ptr<base::ListValue> event_list(new base::ListValue); 542 std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
543 status_list;
543 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator 544 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
544 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) { 545 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
545 546 linked_ptr<file_browser_private::FileTransferStatus> status(
546 scoped_ptr<base::DictionaryValue> job_info_dict( 547 new file_browser_private::FileTransferStatus());
547 JobInfoToDictionaryValue(kFileManagerAppId, 548 JobInfoToTransferStatus(kFileManagerAppId,
548 iter->second.status, 549 iter->second.status,
549 iter->second.job_info)); 550 iter->second.job_info,
550 event_list->Append(job_info_dict.release()); 551 status.get());
552 status_list.push_back(status);
551 } 553 }
552 554 BroadcastEvent(
553 scoped_ptr<base::ListValue> args(new base::ListValue()); 555 profile_,
554 args->Append(event_list.release()); 556 file_browser_private::OnFileTransfersUpdated::kEventName,
555 scoped_ptr<extensions::Event> event(new extensions::Event( 557 file_browser_private::OnFileTransfersUpdated::Create(status_list));
556 extensions::event_names::kOnFileTransfersUpdated, args.Pass()));
557 extensions::ExtensionSystem::Get(profile_)->event_router()->
558 DispatchEventToExtension(kFileManagerAppId, event.Pass());
559
560 last_file_transfer_event_ = now; 558 last_file_transfer_event_ = now;
561 } 559 }
562 560
563 void EventRouter::OnDirectoryChanged(const base::FilePath& directory_path) { 561 void EventRouter::OnDirectoryChanged(const base::FilePath& directory_path) {
564 HandleFileWatchNotification(directory_path, false); 562 HandleFileWatchNotification(directory_path, false);
565 } 563 }
566 564
567 void EventRouter::OnRefreshTokenInvalid() { 565 void EventRouter::OnRefreshTokenInvalid() {
568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 566 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
569 567
570 // Raise a DriveConnectionStatusChanged event to notify the status offline. 568 // Raise a DriveConnectionStatusChanged event to notify the status offline.
571 BroadcastEvent( 569 BroadcastEvent(
572 profile_, 570 profile_,
573 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged, 571 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
574 make_scoped_ptr(new base::ListValue)); 572 file_browser_private::OnDriveConnectionStatusChanged::Create());
575 } 573 }
576 574
577 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path, 575 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
578 bool got_error) { 576 bool got_error) {
579 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580 578
581 WatcherMap::const_iterator iter = file_watchers_.find(local_path); 579 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
582 if (iter == file_watchers_.end()) { 580 if (iter == file_watchers_.end()) {
583 return; 581 return;
584 } 582 }
585 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error, 583 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
586 iter->second->GetExtensionIds()); 584 iter->second->GetExtensionIds());
587 } 585 }
588 586
589 void EventRouter::DispatchDirectoryChangeEvent( 587 void EventRouter::DispatchDirectoryChangeEvent(
590 const base::FilePath& virtual_path, 588 const base::FilePath& virtual_path,
591 bool got_error, 589 bool got_error,
592 const std::vector<std::string>& extension_ids) { 590 const std::vector<std::string>& extension_ids) {
593 if (!profile_) { 591 if (!profile_) {
594 NOTREACHED(); 592 NOTREACHED();
595 return; 593 return;
596 } 594 }
597 595
598 for (size_t i = 0; i < extension_ids.size(); ++i) { 596 for (size_t i = 0; i < extension_ids.size(); ++i) {
599 const std::string& extension_id = extension_ids[i]; 597 const std::string& extension_id = extension_ids[i];
598 const GURL target_origin_url(
599 extensions::Extension::GetBaseURLFromExtensionId(extension_id));
600 // This will be replaced with a real Entry in custom bindings.
601 const fileapi::FileSystemInfo info =
602 fileapi::GetFileSystemInfoForChromeOS(target_origin_url.GetOrigin());
600 603
601 GURL target_origin_url(extensions::Extension::GetBaseURLFromExtensionId( 604 file_browser_private::FileWatchEvent event;
602 extension_id)); 605 event.event_type = got_error ?
603 scoped_ptr<base::ListValue> args(new base::ListValue()); 606 file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR :
604 base::DictionaryValue* watch_info = new base::DictionaryValue(); 607 file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
605 args->Append(watch_info); 608 event.entry.additional_properties.SetString("fileSystemName", info.name);
609 event.entry.additional_properties.SetString("fileSystemRoot",
610 info.root_url.spec());
611 event.entry.additional_properties.SetString("fileFullPath",
612 "/" + virtual_path.value());
613 event.entry.additional_properties.SetBoolean("fileIsDirectory", true);
606 614
607 // This will be replaced with a real Entry in custom bindings. 615 BroadcastEvent(
608 fileapi::FileSystemInfo info = 616 profile_,
609 fileapi::GetFileSystemInfoForChromeOS(target_origin_url.GetOrigin()); 617 file_browser_private::OnDirectoryChanged::kEventName,
610 base::DictionaryValue* entry = new base::DictionaryValue(); 618 file_browser_private::OnDirectoryChanged::Create(event));
611 entry->SetString("fileSystemName", info.name);
612 entry->SetString("fileSystemRoot", info.root_url.spec());
613 entry->SetString("fileFullPath", "/" + virtual_path.value());
614 entry->SetBoolean("fileIsDirectory", true);
615 watch_info->Set("entry", entry);
616 watch_info->SetString("eventType",
617 got_error ? kPathWatchError : kPathChanged);
618 scoped_ptr<extensions::Event> event(new extensions::Event(
619 extensions::event_names::kOnDirectoryChanged, args.Pass()));
620 extensions::ExtensionSystem::Get(profile_)->event_router()->
621 DispatchEventToExtension(extension_id, event.Pass());
622 } 619 }
623 } 620 }
624 621
625 void EventRouter::ShowRemovableDeviceInFileManager( 622 void EventRouter::ShowRemovableDeviceInFileManager(
626 const base::FilePath& mount_path) { 623 const base::FilePath& mount_path) {
627 // Do not attempt to open File Manager while the login is in progress or 624 // Do not attempt to open File Manager while the login is in progress or
628 // the screen is locked. 625 // the screen is locked.
629 if (chromeos::LoginDisplayHostImpl::default_host() || 626 if (chromeos::LoginDisplayHostImpl::default_host() ||
630 chromeos::ScreenLocker::default_screen_locker()) 627 chromeos::ScreenLocker::default_screen_locker())
631 return; 628 return;
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
756 void EventRouter::OnFormatCompleted(const std::string& device_path, 753 void EventRouter::OnFormatCompleted(const std::string& device_path,
757 bool success) { 754 bool success) {
758 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 755 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
759 DispatchDeviceEvent(success ? 756 DispatchDeviceEvent(success ?
760 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS : 757 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
761 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL, 758 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
762 device_path); 759 device_path);
763 } 760 }
764 761
765 } // namespace file_manager 762 } // namespace file_manager
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/event_names.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698