OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "mojo/shell/dynamic_service_loader.h" | 5 #include "mojo/shell/dynamic_service_loader.h" |
6 | 6 |
7 #include "base/location.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" |
| 9 #include "base/file_util.h" |
| 10 #include "base/files/file_path.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/message_loop/message_loop.h" |
| 13 #include "mojo/common/data_pipe_utils.h" |
| 14 #include "mojo/services/public/interfaces/network/url_loader.mojom.h" |
8 #include "mojo/shell/context.h" | 15 #include "mojo/shell/context.h" |
9 #include "mojo/shell/keep_alive.h" | 16 #include "mojo/shell/keep_alive.h" |
| 17 #include "mojo/shell/switches.h" |
| 18 #include "net/base/filename_util.h" |
10 | 19 |
11 namespace mojo { | 20 namespace mojo { |
12 namespace shell { | 21 namespace shell { |
13 | |
14 namespace { | 22 namespace { |
15 | 23 |
16 std::string MakeSharedLibraryName(const std::string& file_name) { | 24 class Loader { |
17 #if defined(OS_WIN) | 25 public: |
18 return file_name + ".dll"; | 26 explicit Loader(scoped_ptr<DynamicServiceRunner> runner) |
19 #elif defined(OS_LINUX) | 27 : runner_(runner.Pass()) { |
20 return "lib" + file_name + ".so"; | 28 } |
21 #elif defined(OS_MACOSX) | |
22 return "lib" + file_name + ".dylib"; | |
23 #else | |
24 NOTREACHED() << "dynamic loading of services not supported"; | |
25 return std::string(); | |
26 #endif | |
27 } | |
28 | 29 |
29 } // namespace | 30 virtual void Start(const GURL& url, |
| 31 ScopedMessagePipeHandle service_handle, |
| 32 Context* context) = 0; |
30 | 33 |
31 class DynamicServiceLoader::LoadContext : public mojo::shell::Loader::Delegate { | 34 void StartService(const base::FilePath& path, |
| 35 ScopedMessagePipeHandle service_handle, |
| 36 bool path_is_valid) { |
| 37 if (path_is_valid) { |
| 38 runner_->Start(path, service_handle.Pass(), |
| 39 base::Bind(&Loader::AppCompleted, base::Unretained(this))); |
| 40 } else { |
| 41 AppCompleted(); |
| 42 } |
| 43 } |
| 44 |
| 45 protected: |
| 46 virtual ~Loader() {} |
| 47 |
| 48 private: |
| 49 void AppCompleted() { |
| 50 delete this; |
| 51 } |
| 52 |
| 53 scoped_ptr<DynamicServiceRunner> runner_; |
| 54 }; |
| 55 |
| 56 // For loading services via file:// URLs. |
| 57 class LocalLoader : public Loader { |
32 public: | 58 public: |
33 LoadContext(DynamicServiceLoader* loader, | 59 explicit LocalLoader(scoped_ptr<DynamicServiceRunner> runner) |
34 const GURL& url, | 60 : Loader(runner.Pass()) { |
35 ScopedMessagePipeHandle service_handle, | 61 } |
36 scoped_ptr<DynamicServiceRunner> runner) | |
37 : loader_(loader), | |
38 url_(url), | |
39 service_handle_(service_handle.Pass()), | |
40 runner_(runner.Pass()), | |
41 keep_alive_(loader->context_) { | |
42 GURL url_to_load; | |
43 | 62 |
44 if (url.SchemeIs("mojo")) { | 63 virtual void Start(const GURL& url, |
45 std::string lib = MakeSharedLibraryName(url.ExtractFileName()); | 64 ScopedMessagePipeHandle service_handle, |
46 url_to_load = GURL(loader->context_->mojo_origin() + "/" + lib); | 65 Context* context) OVERRIDE { |
47 } else { | 66 base::FilePath path; |
48 url_to_load = url; | 67 net::FileURLToFilePath(url, &path); |
| 68 |
| 69 // TODO(darin): Check if the given file path exists. |
| 70 |
| 71 // Complete asynchronously for consistency with NetworkServiceLoader. |
| 72 base::MessageLoop::current()->PostTask( |
| 73 FROM_HERE, |
| 74 base::Bind(&Loader::StartService, |
| 75 base::Unretained(this), |
| 76 path, |
| 77 base::Passed(&service_handle), |
| 78 true)); |
| 79 } |
| 80 }; |
| 81 |
| 82 // For loading services via the network stack. |
| 83 class NetworkLoader : public Loader, public URLLoaderClient { |
| 84 public: |
| 85 explicit NetworkLoader(scoped_ptr<DynamicServiceRunner> runner, |
| 86 NetworkService* network_service) |
| 87 : Loader(runner.Pass()) { |
| 88 network_service->CreateURLLoader(Get(&url_loader_)); |
| 89 url_loader_.set_client(this); |
| 90 } |
| 91 |
| 92 virtual void Start(const GURL& url, |
| 93 ScopedMessagePipeHandle service_handle, |
| 94 Context* context) OVERRIDE { |
| 95 service_handle_ = service_handle.Pass(); |
| 96 |
| 97 URLRequestPtr request(URLRequest::New()); |
| 98 request->url = url.spec(); |
| 99 request->follow_redirects = true; |
| 100 |
| 101 if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 102 switches::kDisableCache)) { |
| 103 request->bypass_cache = true; |
49 } | 104 } |
50 | 105 |
51 request_ = loader_->context_->loader()->Load(url_to_load, this); | 106 DataPipe data_pipe; |
52 } | 107 url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass()); |
53 | 108 |
54 virtual ~LoadContext() { | 109 base::CreateTemporaryFile(&file_); |
| 110 common::CopyToFile(data_pipe.consumer_handle.Pass(), |
| 111 file_, |
| 112 context->task_runners()->blocking_pool(), |
| 113 base::Bind(&Loader::StartService, |
| 114 base::Unretained(this), |
| 115 file_, |
| 116 base::Passed(&service_handle_))); |
55 } | 117 } |
56 | 118 |
57 private: | 119 private: |
58 // |Loader::Delegate| method: | 120 // URLLoaderClient methods: |
59 virtual void DidCompleteLoad(const GURL& app_url, | 121 virtual void OnReceivedRedirect(URLResponsePtr response, |
60 const base::FilePath& app_path, | 122 const String& new_url, |
61 const std::string* mime_type) OVERRIDE { | 123 const String& new_method) OVERRIDE { |
62 DVLOG(2) << "Completed load of " << app_url << " (" << url_ << ") to " | 124 // TODO(darin): Handle redirects properly! |
63 << app_path.value(); | 125 } |
64 DCHECK(loader_->context_->task_runners()->ui_runner()-> | 126 virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE {} |
65 BelongsToCurrentThread()); | 127 virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE {} |
| 128 virtual void OnReceivedEndOfResponseBody() OVERRIDE {} |
66 | 129 |
67 runner_->Start( | 130 NetworkServicePtr network_service_; |
68 app_path, | 131 URLLoaderPtr url_loader_; |
69 service_handle_.Pass(), | 132 ScopedMessagePipeHandle service_handle_; |
70 base::Bind(&DynamicServiceLoader::AppCompleted, | 133 base::FilePath file_; |
71 base::Unretained(loader_), url_)); | 134 }; |
72 } | |
73 | 135 |
74 DynamicServiceLoader* const loader_; | 136 } // namespace |
75 const GURL url_; | |
76 scoped_ptr<mojo::shell::Loader::Job> request_; | |
77 ScopedMessagePipeHandle service_handle_; | |
78 scoped_ptr<DynamicServiceRunner> runner_; | |
79 KeepAlive keep_alive_; | |
80 | |
81 DISALLOW_COPY_AND_ASSIGN(LoadContext); | |
82 }; | |
83 | 137 |
84 DynamicServiceLoader::DynamicServiceLoader( | 138 DynamicServiceLoader::DynamicServiceLoader( |
85 Context* context, | 139 Context* context, |
86 scoped_ptr<DynamicServiceRunnerFactory> runner_factory) | 140 scoped_ptr<DynamicServiceRunnerFactory> runner_factory) |
87 : context_(context), | 141 : context_(context), |
88 runner_factory_(runner_factory.Pass()) { | 142 runner_factory_(runner_factory.Pass()) { |
89 } | 143 } |
90 | 144 |
91 DynamicServiceLoader::~DynamicServiceLoader() { | 145 DynamicServiceLoader::~DynamicServiceLoader() { |
92 DCHECK(url_to_load_context_.empty()); | |
93 } | 146 } |
94 | 147 |
95 void DynamicServiceLoader::LoadService(ServiceManager* manager, | 148 void DynamicServiceLoader::LoadService(ServiceManager* manager, |
96 const GURL& url, | 149 const GURL& url, |
97 ScopedMessagePipeHandle service_handle) { | 150 ScopedMessagePipeHandle service_handle) { |
98 DCHECK(url_to_load_context_.find(url) == url_to_load_context_.end()); | 151 scoped_ptr<DynamicServiceRunner> runner = runner_factory_->Create(context_); |
99 url_to_load_context_[url] = new LoadContext( | 152 |
100 this, url, service_handle.Pass(), runner_factory_->Create(context_)); | 153 GURL resolved_url; |
| 154 if (url.SchemeIs("mojo")) { |
| 155 resolved_url = context_->mojo_url_resolver()->Resolve(url); |
| 156 } else { |
| 157 resolved_url = url; |
| 158 } |
| 159 |
| 160 Loader* loader; |
| 161 if (resolved_url.SchemeIsFile()) { |
| 162 loader = new LocalLoader(runner.Pass()); |
| 163 } else { |
| 164 if (!network_service_.get()) { |
| 165 context_->service_manager()->ConnectTo(GURL("mojo:mojo_network_service"), |
| 166 &network_service_, |
| 167 GURL()); |
| 168 } |
| 169 loader = new NetworkLoader(runner.Pass(), network_service_.get()); |
| 170 } |
| 171 loader->Start(resolved_url, service_handle.Pass(), context_); |
101 } | 172 } |
102 | 173 |
103 void DynamicServiceLoader::OnServiceError(ServiceManager* manager, | 174 void DynamicServiceLoader::OnServiceError(ServiceManager* manager, |
104 const GURL& url) { | 175 const GURL& url) { |
105 } | 176 // TODO(darin): What should we do about service errors? This implies that |
106 | 177 // the app closed its handle to the service manager. Maybe we don't care? |
107 void DynamicServiceLoader::AppCompleted(const GURL& url) { | |
108 DCHECK(context_->task_runners()->ui_runner()->BelongsToCurrentThread()); | |
109 DVLOG(2) << "App completed (url: " << url << ")"; | |
110 | |
111 LoadContextMap::iterator it = url_to_load_context_.find(url); | |
112 DCHECK(it != url_to_load_context_.end()) << url; | |
113 | |
114 LoadContext* doomed = it->second; | |
115 url_to_load_context_.erase(it); | |
116 | |
117 delete doomed; | |
118 } | 178 } |
119 | 179 |
120 } // namespace shell | 180 } // namespace shell |
121 } // namespace mojo | 181 } // namespace mojo |
OLD | NEW |