| 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) {} |
| 65 BelongsToCurrentThread()); | 127 virtual void OnReceivedError(NetworkErrorPtr error) {} |
| 128 virtual void OnReceivedEndOfResponseBody() {} |
| 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 |