OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/extensions/extensions_service.h" | 5 #include "chrome/browser/extensions/extensions_service.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/gfx/png_encoder.h" |
8 #include "base/scoped_handle.h" | 9 #include "base/scoped_handle.h" |
9 #include "base/scoped_temp_dir.h" | 10 #include "base/scoped_temp_dir.h" |
10 #include "base/string_util.h" | 11 #include "base/string_util.h" |
11 #include "base/third_party/nss/blapi.h" | 12 #include "base/third_party/nss/blapi.h" |
12 #include "base/third_party/nss/sha256.h" | 13 #include "base/third_party/nss/sha256.h" |
13 #include "base/thread.h" | 14 #include "base/thread.h" |
14 #include "base/values.h" | 15 #include "base/values.h" |
15 #include "net/base/file_stream.h" | 16 #include "net/base/file_stream.h" |
16 #include "chrome/browser/browser.h" | 17 #include "chrome/browser/browser.h" |
17 #include "chrome/browser/browser_list.h" | 18 #include "chrome/browser/browser_list.h" |
18 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
19 #include "chrome/browser/chrome_thread.h" | 20 #include "chrome/browser/chrome_thread.h" |
20 #include "chrome/browser/extensions/extension.h" | 21 #include "chrome/browser/extensions/extension.h" |
21 #include "chrome/browser/extensions/extension_browser_event_router.h" | 22 #include "chrome/browser/extensions/extension_browser_event_router.h" |
22 #include "chrome/browser/extensions/extension_error_reporter.h" | 23 #include "chrome/browser/extensions/extension_error_reporter.h" |
23 #include "chrome/browser/extensions/extension_process_manager.h" | 24 #include "chrome/browser/extensions/extension_process_manager.h" |
24 #include "chrome/browser/profile.h" | 25 #include "chrome/browser/profile.h" |
25 #include "chrome/browser/utility_process_host.h" | 26 #include "chrome/browser/utility_process_host.h" |
26 #include "chrome/common/extensions/extension_unpacker.h" | 27 #include "chrome/common/extensions/extension_unpacker.h" |
27 #include "chrome/common/json_value_serializer.h" | 28 #include "chrome/common/json_value_serializer.h" |
28 #include "chrome/common/notification_service.h" | 29 #include "chrome/common/notification_service.h" |
29 #include "chrome/common/pref_service.h" | 30 #include "chrome/common/pref_service.h" |
30 #include "chrome/common/unzip.h" | 31 #include "chrome/common/unzip.h" |
31 #include "chrome/common/url_constants.h" | 32 #include "chrome/common/url_constants.h" |
| 33 #include "third_party/skia/include/core/SkBitmap.h" |
32 | 34 |
33 #if defined(OS_WIN) | 35 #if defined(OS_WIN) |
34 #include "base/registry.h" | 36 #include "base/registry.h" |
35 #endif | 37 #endif |
36 | 38 |
37 // ExtensionsService | 39 // ExtensionsService |
38 | 40 |
39 const char* ExtensionsService::kInstallDirectoryName = "Extensions"; | 41 const char* ExtensionsService::kInstallDirectoryName = "Extensions"; |
40 const char* ExtensionsService::kCurrentVersionFileName = "Current Version"; | 42 const char* ExtensionsService::kCurrentVersionFileName = "Current Version"; |
41 const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; | 43 const char* ExtensionsServiceBackend::kTempExtensionName = "TEMP_INSTALL"; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 | 124 |
123 if (backend_->resource_dispatcher_host_) { | 125 if (backend_->resource_dispatcher_host_) { |
124 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, | 126 ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask(FROM_HERE, |
125 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, | 127 NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread, |
126 backend_->resource_dispatcher_host_, | 128 backend_->resource_dispatcher_host_, |
127 MessageLoop::current())); | 129 MessageLoop::current())); |
128 } else { | 130 } else { |
129 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're | 131 // Cheesy... but if we don't have a ResourceDispatcherHost, assume we're |
130 // in a unit test and run the unpacker directly in-process. | 132 // in a unit test and run the unpacker directly in-process. |
131 ExtensionUnpacker unpacker(temp_extension_path_); | 133 ExtensionUnpacker unpacker(temp_extension_path_); |
132 bool success = unpacker.Run(); | 134 if (unpacker.Run()) { |
133 OnUnpackExtensionReply(success, unpacker.error_message()); | 135 OnUnpackExtensionSucceeded(*unpacker.parsed_manifest(), |
| 136 unpacker.decoded_images()); |
| 137 } else { |
| 138 OnUnpackExtensionFailed(unpacker.error_message()); |
| 139 } |
134 } | 140 } |
135 } | 141 } |
136 | 142 |
137 private: | 143 private: |
138 // UtilityProcessHost::Client | 144 // UtilityProcessHost::Client |
139 virtual void OnProcessCrashed() { | 145 virtual void OnProcessCrashed() { |
140 OnUnpackExtensionReply(false, "Chrome crashed while trying to install"); | 146 OnUnpackExtensionFailed("Chrome crashed while trying to install"); |
141 } | 147 } |
142 | 148 |
143 virtual void OnUnpackExtensionReply(bool success, | 149 virtual void OnUnpackExtensionSucceeded( |
144 const std::string& error_message) { | 150 const DictionaryValue& manifest, |
145 if (success) { | 151 const std::vector< Tuple2<SkBitmap, FilePath> >& images) { |
146 // The extension was unpacked to the temp dir inside our unpacking dir. | 152 // The extension was unpacked to the temp dir inside our unpacking dir. |
147 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( | 153 FilePath extension_dir = temp_extension_path_.DirName().AppendASCII( |
148 ExtensionsServiceBackend::kTempExtensionName); | 154 ExtensionsServiceBackend::kTempExtensionName); |
149 backend_->OnExtensionUnpacked(extension_path_, extension_dir, | 155 backend_->OnExtensionUnpacked(extension_path_, extension_dir, |
150 expected_id_, from_external_); | 156 expected_id_, from_external_, |
151 } else { | 157 manifest, images); |
152 backend_->ReportExtensionInstallError(extension_path_, error_message); | |
153 } | |
154 Cleanup(); | 158 Cleanup(); |
155 Release(); // balanced in Run() | 159 } |
| 160 |
| 161 virtual void OnUnpackExtensionFailed(const std::string& error_message) { |
| 162 backend_->ReportExtensionInstallError(extension_path_, error_message); |
| 163 Cleanup(); |
156 } | 164 } |
157 | 165 |
158 // Cleans up our temp directory. | 166 // Cleans up our temp directory. |
159 void Cleanup() { | 167 void Cleanup() { |
160 file_util::Delete(temp_extension_path_.DirName(), true); | 168 file_util::Delete(temp_extension_path_.DirName(), true); |
| 169 Release(); // balanced in Run() |
161 } | 170 } |
162 | 171 |
163 // Starts the utility process that unpacks our extension. | 172 // Starts the utility process that unpacks our extension. |
164 void StartProcessOnIOThread(ResourceDispatcherHost* rdh, | 173 void StartProcessOnIOThread(ResourceDispatcherHost* rdh, |
165 MessageLoop* file_loop) { | 174 MessageLoop* file_loop) { |
166 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); | 175 UtilityProcessHost* host = new UtilityProcessHost(rdh, this, file_loop); |
167 host->StartExtensionUnpacker(temp_extension_path_); | 176 host->StartExtensionUnpacker(temp_extension_path_); |
168 } | 177 } |
169 | 178 |
170 scoped_refptr<ExtensionsServiceBackend> backend_; | 179 scoped_refptr<ExtensionsServiceBackend> backend_; |
(...skipping 634 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
805 bool from_external) { | 814 bool from_external) { |
806 UnpackerClient* client = | 815 UnpackerClient* client = |
807 new UnpackerClient(this, extension_path, expected_id, from_external); | 816 new UnpackerClient(this, extension_path, expected_id, from_external); |
808 client->Start(); | 817 client->Start(); |
809 } | 818 } |
810 | 819 |
811 void ExtensionsServiceBackend::OnExtensionUnpacked( | 820 void ExtensionsServiceBackend::OnExtensionUnpacked( |
812 const FilePath& extension_path, | 821 const FilePath& extension_path, |
813 const FilePath& temp_extension_dir, | 822 const FilePath& temp_extension_dir, |
814 const std::string expected_id, | 823 const std::string expected_id, |
815 bool from_external) { | 824 bool from_external, |
816 // TODO(mpcomplete): the utility process should pass up a parsed manifest that | 825 const DictionaryValue& manifest, |
817 // we rewrite in the browser. | 826 const std::vector< Tuple2<SkBitmap, FilePath> >& images) { |
818 // Bug http://code.google.com/p/chromium/issues/detail?id=11680 | |
819 scoped_ptr<DictionaryValue> manifest(ReadManifest(extension_path)); | |
820 if (!manifest.get()) { | |
821 // ReadManifest has already reported the extension error. | |
822 return; | |
823 } | |
824 | |
825 Extension extension; | 827 Extension extension; |
826 std::string error; | 828 std::string error; |
827 if (!extension.InitFromValue(*manifest, | 829 if (!extension.InitFromValue(manifest, |
828 true, // require ID | 830 true, // require ID |
829 &error)) { | 831 &error)) { |
830 ReportExtensionInstallError(extension_path, "Invalid extension manifest."); | 832 ReportExtensionInstallError(extension_path, "Invalid extension manifest."); |
831 return; | 833 return; |
832 } | 834 } |
833 | 835 |
834 // If an expected id was provided, make sure it matches. | 836 // If an expected id was provided, make sure it matches. |
835 if (!expected_id.empty() && expected_id != extension.id()) { | 837 if (!expected_id.empty() && expected_id != extension.id()) { |
836 ReportExtensionInstallError(extension_path, | 838 ReportExtensionInstallError(extension_path, |
837 "ID in new extension manifest does not match expected ID."); | 839 "ID in new extension manifest does not match expected ID."); |
838 return; | 840 return; |
839 } | 841 } |
840 | 842 |
841 // <profile>/Extensions/<id> | 843 // <profile>/Extensions/<id> |
842 FilePath dest_dir = install_directory_.AppendASCII(extension.id()); | 844 FilePath dest_dir = install_directory_.AppendASCII(extension.id()); |
843 std::string version = extension.VersionString(); | 845 std::string version = extension.VersionString(); |
844 std::string current_version; | 846 std::string current_version; |
845 bool was_update = false; | 847 bool was_update = false; |
846 if (ReadCurrentVersion(dest_dir, ¤t_version)) { | 848 if (ReadCurrentVersion(dest_dir, ¤t_version)) { |
847 if (!CheckCurrentVersion(version, current_version, dest_dir)) | 849 if (!CheckCurrentVersion(version, current_version, dest_dir)) |
848 return; | 850 return; |
849 was_update = true; | 851 was_update = true; |
850 } | 852 } |
851 | 853 |
| 854 // Write our parsed manifest back to disk, to ensure it doesn't contain an |
| 855 // exploitable bug that can be used to compromise the browser. |
| 856 std::string manifest_json; |
| 857 JSONStringValueSerializer serializer(&manifest_json); |
| 858 serializer.set_pretty_print(true); |
| 859 if (!serializer.Serialize(manifest)) { |
| 860 ReportExtensionInstallError(extension_path, |
| 861 "Error serializing manifest.json."); |
| 862 return; |
| 863 } |
| 864 |
| 865 FilePath manifest_path = |
| 866 temp_extension_dir.AppendASCII(Extension::kManifestFilename); |
| 867 if (!file_util::WriteFile(manifest_path, |
| 868 manifest_json.data(), manifest_json.size())) { |
| 869 ReportExtensionInstallError(extension_path, "Error saving manifest.json."); |
| 870 return; |
| 871 } |
| 872 |
| 873 // Write our parsed images back to disk as well. |
| 874 for (size_t i = 0; i < images.size(); ++i) { |
| 875 const SkBitmap& image = images[i].a; |
| 876 FilePath path = temp_extension_dir.Append(images[i].b); |
| 877 |
| 878 std::vector<unsigned char> image_data; |
| 879 // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even |
| 880 // though they may originally be .jpg, etc. Figure something out. |
| 881 // http://code.google.com/p/chromium/issues/detail?id=12459 |
| 882 if (!PNGEncoder::EncodeBGRASkBitmap(image, false, &image_data)) { |
| 883 ReportExtensionInstallError(extension_path, |
| 884 "Error re-encoding theme image."); |
| 885 return; |
| 886 } |
| 887 |
| 888 // Note: we're overwriting existing files that the utility process wrote, |
| 889 // so we can be sure the directory exists. |
| 890 const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]); |
| 891 if (!file_util::WriteFile(path, image_data_ptr, image_data.size())) { |
| 892 ReportExtensionInstallError(extension_path, "Error saving theme image."); |
| 893 return; |
| 894 } |
| 895 } |
| 896 |
852 // <profile>/Extensions/<dir_name>/<version> | 897 // <profile>/Extensions/<dir_name>/<version> |
853 FilePath version_dir = dest_dir.AppendASCII(version); | 898 FilePath version_dir = dest_dir.AppendASCII(version); |
| 899 |
| 900 // If anything fails after this, we want to delete the extension dir. |
| 901 ScopedTempDir scoped_version_dir; |
| 902 scoped_version_dir.Set(version_dir); |
| 903 |
854 if (!InstallDirSafely(temp_extension_dir, version_dir)) | 904 if (!InstallDirSafely(temp_extension_dir, version_dir)) |
855 return; | 905 return; |
856 | 906 |
857 if (!SetCurrentVersion(dest_dir, version)) { | 907 if (!SetCurrentVersion(dest_dir, version)) |
858 if (!file_util::Delete(version_dir, true)) | |
859 LOG(WARNING) << "Can't remove " << dest_dir.value(); | |
860 return; | 908 return; |
861 } | |
862 | 909 |
863 // To mark that this extension was installed from an external source, create a | 910 // To mark that this extension was installed from an external source, create a |
864 // zero-length file. At load time, this is used to indicate that the | 911 // zero-length file. At load time, this is used to indicate that the |
865 // extension should be uninstalled. | 912 // extension should be uninstalled. |
866 // TODO(erikkay): move this into per-extension config storage when it appears. | 913 // TODO(erikkay): move this into per-extension config storage when it appears. |
867 if (from_external) { | 914 if (from_external) { |
868 FilePath marker = version_dir.AppendASCII(kExternalInstallFile); | 915 FilePath marker = version_dir.AppendASCII(kExternalInstallFile); |
869 file_util::WriteFile(marker, NULL, 0); | 916 file_util::WriteFile(marker, NULL, 0); |
870 } | 917 } |
871 | 918 |
872 // Load the extension immediately and then report installation success. We | 919 // Load the extension immediately and then report installation success. We |
873 // don't load extensions for external installs because external installation | 920 // don't load extensions for external installs because external installation |
874 // occurs before the normal startup so we just let startup pick them up. We | 921 // occurs before the normal startup so we just let startup pick them up. We |
875 // don't notify installation because there is no UI or external install so | 922 // don't notify installation because there is no UI for external install so |
876 // there is nobody to notify. | 923 // there is nobody to notify. |
877 if (!from_external) { | 924 if (!from_external) { |
878 Extension* extension = LoadExtension(version_dir, true); // require id | 925 Extension* extension = LoadExtension(version_dir, true); // require id |
879 CHECK(extension); | 926 CHECK(extension); |
880 | 927 |
881 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( | 928 frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod( |
882 frontend_, &ExtensionsService::OnExtensionInstalled, extension, | 929 frontend_, &ExtensionsService::OnExtensionInstalled, extension, |
883 was_update)); | 930 was_update)); |
884 | 931 |
885 // Only one extension, but ReportExtensionsLoaded can handle multiple, | 932 // Only one extension, but ReportExtensionsLoaded can handle multiple, |
886 // so we need to construct a list. | 933 // so we need to construct a list. |
887 scoped_ptr<ExtensionList> extensions(new ExtensionList); | 934 scoped_ptr<ExtensionList> extensions(new ExtensionList); |
888 extensions->push_back(extension); | 935 extensions->push_back(extension); |
889 LOG(INFO) << "Done."; | 936 LOG(INFO) << "Done."; |
890 // Hand off ownership of the loaded extensions to the frontend. | 937 // Hand off ownership of the loaded extensions to the frontend. |
891 ReportExtensionsLoaded(extensions.release()); | 938 ReportExtensionsLoaded(extensions.release()); |
892 } | 939 } |
| 940 |
| 941 scoped_version_dir.Take(); |
893 } | 942 } |
894 | 943 |
895 void ExtensionsServiceBackend::ReportExtensionInstallError( | 944 void ExtensionsServiceBackend::ReportExtensionInstallError( |
896 const FilePath& extension_path, const std::string &error) { | 945 const FilePath& extension_path, const std::string &error) { |
897 | 946 |
898 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux. | 947 // TODO(erikkay): note that this isn't guaranteed to work properly on Linux. |
899 std::string path_str = WideToASCII(extension_path.ToWStringHack()); | 948 std::string path_str = WideToASCII(extension_path.ToWStringHack()); |
900 std::string message = | 949 std::string message = |
901 StringPrintf("Could not install extension from '%s'. %s", | 950 StringPrintf("Could not install extension from '%s'. %s", |
902 path_str.c_str(), error.c_str()); | 951 path_str.c_str(), error.c_str()); |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1030 | 1079 |
1031 bool ExtensionsServiceBackend::ShouldInstall(const std::string& id, | 1080 bool ExtensionsServiceBackend::ShouldInstall(const std::string& id, |
1032 const std::string& version) { | 1081 const std::string& version) { |
1033 FilePath dir(install_directory_.AppendASCII(id.c_str())); | 1082 FilePath dir(install_directory_.AppendASCII(id.c_str())); |
1034 std::string current_version; | 1083 std::string current_version; |
1035 if (ReadCurrentVersion(dir, ¤t_version)) { | 1084 if (ReadCurrentVersion(dir, ¤t_version)) { |
1036 return CheckCurrentVersion(version, current_version, dir); | 1085 return CheckCurrentVersion(version, current_version, dir); |
1037 } | 1086 } |
1038 return true; | 1087 return true; |
1039 } | 1088 } |
OLD | NEW |