Chromium Code Reviews| Index: mojo/shell/dynamic_application_loader.cc |
| diff --git a/mojo/shell/dynamic_application_loader.cc b/mojo/shell/dynamic_application_loader.cc |
| index 193582b8c3344156fd3bfba615e02a5a8c38cca5..2cac85013ec5fd37bd1e6113a7b8261e535f040f 100644 |
| --- a/mojo/shell/dynamic_application_loader.cc |
| +++ b/mojo/shell/dynamic_application_loader.cc |
| @@ -8,12 +8,17 @@ |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| +#include "base/format_macros.h" |
| +#include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| +#include "base/strings/string_util.h" |
| +#include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "mojo/common/common_type_converters.h" |
| #include "mojo/common/data_pipe_utils.h" |
| +#include "mojo/public/cpp/system/data_pipe.h" |
| #include "mojo/services/public/interfaces/network/url_loader.mojom.h" |
| #include "mojo/shell/context.h" |
| #include "mojo/shell/data_pipe_peek.h" |
| @@ -24,6 +29,167 @@ |
| namespace mojo { |
| namespace shell { |
| +namespace { |
| + |
| +void IgnoreResult(bool result) { |
| +} |
| + |
| +class LoaderResponse { |
| + public: |
| + virtual ~LoaderResponse() {} |
| + |
| + virtual URLResponsePtr AsURLResponse(base::TaskRunner* task_runner, |
| + uint32_t skip) = 0; |
| + |
| + virtual void AsPath( |
| + base::TaskRunner* task_runner, |
| + base::Callback<void(const base::FilePath&, bool)> callback) = 0; |
| + |
| + bool PeekContentHandler(std::string* mojo_shebang, |
| + GURL* mojo_content_handler_url) { |
| + std::string shebang; |
| + if (HasMojoMagic() && PeekFirstLine(&shebang)) { |
| + std::string url_string; |
| + base::TrimWhitespace( |
| + shebang.substr(2, std::string::npos), base::TRIM_ALL, &url_string); |
|
hansmuller
2014/11/04 16:36:57
Is this necessary? The URL parser is required to t
qsr
2014/11/04 17:30:57
Hum. Did that to try to correct another issue I ha
|
| + GURL url(url_string); |
| + if (url.is_valid()) { |
| + *mojo_shebang = shebang; |
| + *mojo_content_handler_url = url; |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + |
| + virtual std::string MimeType() = 0; |
| + |
| + protected: |
| + const char* kMojoMagic = "#!mojo:"; |
| + const size_t kMaxShebangLength = 2048; |
| + |
| + virtual bool HasMojoMagic() = 0; |
| + virtual bool PeekFirstLine(std::string* line) = 0; |
| +}; |
| + |
| +class NetworkLoaderResponse : public LoaderResponse { |
| + public: |
| + explicit NetworkLoaderResponse(URLResponsePtr response) |
| + : response_(response.Pass()) {} |
| + |
| + ~NetworkLoaderResponse() override { |
| + if (!path_.empty()) |
| + base::DeleteFile(path_, false); |
| + } |
| + |
| + URLResponsePtr AsURLResponse(base::TaskRunner* task_runner, |
| + uint32_t skip) override { |
| + if (skip) { |
| + DCHECK(ReadDataRaw(response_->body.get(), |
| + nullptr, |
| + &skip, |
| + MOJO_READ_DATA_FLAG_ALL_OR_NONE | |
| + MOJO_READ_DATA_FLAG_DISCARD) == MOJO_RESULT_OK); |
| + } |
| + return response_.Pass(); |
| + } |
| + |
| + void AsPath( |
| + base::TaskRunner* task_runner, |
| + base::Callback<void(const base::FilePath&, bool)> callback) override { |
| + if (!path_.empty() || !response_) { |
| + callback.Run(path_, base::PathExists(path_)); |
| + return; |
| + } |
| + base::CreateTemporaryFile(&path_); |
| + common::CopyToFile(response_->body.Pass(), |
| + path_, |
| + task_runner, |
| + base::Bind(callback, path_)); |
| + } |
| + |
| + std::string MimeType() override { |
| + DCHECK(response_); |
| + return response_->mime_type; |
| + } |
| + |
| + private: |
| + // TODO(hansmuller): Revisit this when a real peek operation is available. |
| + const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE; |
| + |
| + bool HasMojoMagic() override { |
| + std::string magic; |
| + return BlockingPeekNBytes(response_->body.get(), |
| + &magic, |
| + strlen(kMojoMagic), |
| + kPeekTimeout) && |
| + magic == kMojoMagic; |
| + } |
| + bool PeekFirstLine(std::string* line) override { |
| + return BlockingPeekLine( |
| + response_->body.get(), line, kMaxShebangLength, kPeekTimeout); |
| + } |
| + |
| + URLResponsePtr response_; |
| + base::FilePath path_; |
| +}; |
| + |
| +class LocalLoaderResponse : public LoaderResponse { |
| + public: |
| + LocalLoaderResponse(const GURL& url, base::FilePath path) |
| + : url_(url), path_(path) {} |
| + |
| + URLResponsePtr AsURLResponse(base::TaskRunner* task_runner, |
| + uint32_t skip) override { |
| + URLResponsePtr response(URLResponse::New()); |
| + response->url = String::From(url_); |
| + DataPipe data_pipe; |
| + response->body = data_pipe.consumer_handle.Pass(); |
| + int64 file_size; |
| + if (base::GetFileSize(path_, &file_size)) { |
| + response->headers = Array<String>(1); |
| + response->headers[0] = |
| + base::StringPrintf("Content-Length: %" PRId64, file_size); |
| + } |
| + common::CopyFromFile(path_, |
| + data_pipe.producer_handle.Pass(), |
| + skip, |
| + task_runner, |
| + base::Bind(&IgnoreResult)); |
| + return response.Pass(); |
| + } |
| + |
| + void AsPath( |
| + base::TaskRunner* task_runner, |
| + base::Callback<void(const base::FilePath&, bool)> callback) override { |
| + callback.Run(path_, base::PathExists(path_)); |
| + } |
| + |
| + std::string MimeType() override { return ""; } |
| + |
| + private: |
| + GURL url_; |
| + base::FilePath path_; |
| + |
| + bool HasMojoMagic() override { |
| + std::string magic; |
| + ReadFileToString(path_, &magic, strlen(kMojoMagic)); |
| + return magic == kMojoMagic; |
| + } |
| + |
| + bool PeekFirstLine(std::string* line) override { |
| + std::string start_of_file; |
| + ReadFileToString(path_, &start_of_file, kMaxShebangLength); |
| + size_t return_position = start_of_file.find('\n'); |
| + if (return_position == std::string::npos) |
| + return false; |
| + *line = start_of_file.substr(0, return_position + 1); |
| + return true; |
| + } |
| +}; |
| + |
| +} // namespace |
| + |
| // Encapsulates loading and running one individual application. |
| // |
| // Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must |
| @@ -36,12 +202,14 @@ namespace shell { |
| class DynamicApplicationLoader::Loader { |
| public: |
| Loader(Context* context, |
| + MimeTypeToURLMap* mime_type_to_url, |
| DynamicServiceRunnerFactory* runner_factory, |
| scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, |
| const LoaderCompleteCallback& loader_complete_callback) |
| : load_callbacks_(load_callbacks), |
| loader_complete_callback_(loader_complete_callback), |
| context_(context), |
| + mime_type_to_url_(mime_type_to_url), |
| runner_factory_(runner_factory), |
| weak_ptr_factory_(this) {} |
| @@ -72,9 +240,43 @@ class DynamicApplicationLoader::Loader { |
| void LoaderComplete() { loader_complete_callback_.Run(this); } |
| + void OnResponse(scoped_ptr<LoaderResponse> response) { |
| + // If the response begins with a #!mojo:<content-handler-url>, use it. |
| + { |
| + GURL url; |
| + std::string shebang; |
| + if (response->PeekContentHandler(&shebang, &url)) { |
| + load_callbacks_->LoadWithContentHandler( |
| + url, |
| + response->AsURLResponse(context_->task_runners()->blocking_pool(), |
| + shebang.size())); |
| + } |
| + } |
| + |
| + MimeTypeToURLMap::iterator iter = |
| + mime_type_to_url_->find(response->MimeType()); |
| + if (iter != mime_type_to_url_->end()) { |
| + load_callbacks_->LoadWithContentHandler( |
| + iter->second, |
| + response->AsURLResponse(context_->task_runners()->blocking_pool(), |
| + 0)); |
| + return; |
| + } |
| + |
| + // TODO(aa): Santify check that the thing we got looks vaguely like a mojo |
|
hansmuller
2014/11/04 16:36:57
Santify => Sanity
qsr
2014/11/04 17:30:57
Done.
|
| + // application. That could either mean looking for the platform-specific dll |
| + // header, or looking for some specific mojo signature prepended to the |
| + // library. |
| + |
| + response->AsPath( |
| + context_->task_runners()->blocking_pool(), |
| + base::Bind(&Loader::RunLibrary, weak_ptr_factory_.GetWeakPtr())); |
| + } |
| + |
| scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks_; |
| LoaderCompleteCallback loader_complete_callback_; |
| Context* context_; |
| + MimeTypeToURLMap* mime_type_to_url_; |
| private: |
| DynamicServiceRunnerFactory* runner_factory_; |
| @@ -87,14 +289,15 @@ class DynamicApplicationLoader::LocalLoader : public Loader { |
| public: |
| LocalLoader(const GURL& url, |
| Context* context, |
| + MimeTypeToURLMap* mime_type_to_url, |
| DynamicServiceRunnerFactory* runner_factory, |
| scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, |
| const LoaderCompleteCallback& loader_complete_callback) |
| : Loader(context, |
| + mime_type_to_url, |
| runner_factory, |
| load_callbacks, |
| - loader_complete_callback), |
| - weak_ptr_factory_(this) { |
| + loader_complete_callback) { |
| DCHECK(url.SchemeIsFile()); |
| url::RawCanonOutputW<1024> output; |
| url::DecodeURLEscapeSequences( |
| @@ -107,19 +310,10 @@ class DynamicApplicationLoader::LocalLoader : public Loader { |
| base::FilePath path(base::UTF16ToUTF8(decoded_path)); |
| #endif |
| - // Async for consistency with network case. |
| - base::MessageLoop::current()->PostTask( |
| - FROM_HERE, |
| - base::Bind(&LocalLoader::RunLibrary, |
| - weak_ptr_factory_.GetWeakPtr(), |
| - path, |
| - base::PathExists(path))); |
| + OnResponse(make_scoped_ptr(new LocalLoaderResponse(url, path))); |
| } |
| ~LocalLoader() override {} |
| - |
| - private: |
| - base::WeakPtrFactory<LocalLoader> weak_ptr_factory_; |
| }; |
| // A loader for network files. |
| @@ -133,10 +327,10 @@ class DynamicApplicationLoader::NetworkLoader : public Loader { |
| scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, |
| const LoaderCompleteCallback& loader_complete_callback) |
| : Loader(context, |
| + mime_type_to_url, |
| runner_factory, |
| load_callbacks, |
| loader_complete_callback), |
| - mime_type_to_url_(mime_type_to_url), |
| weak_ptr_factory_(this) { |
| URLRequestPtr request(URLRequest::New()); |
| request->url = String::From(url); |
| @@ -153,36 +347,7 @@ class DynamicApplicationLoader::NetworkLoader : public Loader { |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| - ~NetworkLoader() override { |
| - if (!file_.empty()) |
| - base::DeleteFile(file_, false); |
| - } |
| - |
| private: |
| - bool PeekContentHandler(DataPipeConsumerHandle source, |
| - std::string* mojo_shebang, |
| - GURL* mojo_content_handler_url) |
| - { |
| - const char* kMojoMagic = "#!mojo:"; |
| - // TODO(hansmuller): Revisit this when a real peek operation is available. |
| - const MojoDeadline kPeekTimeout = MOJO_DEADLINE_INDEFINITE; |
| - const size_t kMaxShebangLength = 2048; |
| - |
| - std::string magic; |
| - std::string shebang; |
| - if (BlockingPeekNBytes(source, &magic, strlen(kMojoMagic), kPeekTimeout) && |
| - magic == kMojoMagic && |
| - BlockingPeekLine(source, &shebang, kMaxShebangLength, kPeekTimeout)) { |
| - GURL url(shebang.substr(2, std::string::npos)); |
| - if (url.is_valid()) { |
| - *mojo_shebang = shebang; |
| - *mojo_content_handler_url = url; |
| - return true; |
| - } |
| - } |
| - return false; |
| - } |
| - |
| void OnLoadComplete(URLResponsePtr response) { |
| if (response->error) { |
| LOG(ERROR) << "Error (" << response->error->code << ": " |
| @@ -192,48 +357,10 @@ class DynamicApplicationLoader::NetworkLoader : public Loader { |
| return; |
| } |
| - // If the response begins with a #!mojo:<content-handler-url>, use it. |
| - { |
| - GURL url; |
| - std::string shebang; |
| - if (PeekContentHandler(response->body.get(), &shebang, &url)) { |
| - uint32_t num_skip_bytes = shebang.size(); |
| - if (ReadDataRaw(response->body.get(), |
| - nullptr, |
| - &num_skip_bytes, |
| - MOJO_READ_DATA_FLAG_ALL_OR_NONE | |
| - MOJO_READ_DATA_FLAG_DISCARD) == |
| - MOJO_RESULT_OK) { |
| - load_callbacks_->LoadWithContentHandler(url, response.Pass()); |
| - return; |
| - } |
| - } |
| - } |
| - |
| - MimeTypeToURLMap::iterator iter = |
| - mime_type_to_url_->find(response->mime_type); |
| - if (iter != mime_type_to_url_->end()) { |
| - load_callbacks_->LoadWithContentHandler(iter->second, response.Pass()); |
| - return; |
| - } |
| - |
| - // TODO(aa): Sanity check that the thing we got looks vaguely like a mojo |
| - // application. That could either mean looking for the platform-specific dll |
| - // header, or looking for some specific mojo signature prepended to the |
| - // library. |
| - |
| - base::CreateTemporaryFile(&file_); |
| - common::CopyToFile( |
| - response->body.Pass(), |
| - file_, |
| - context_->task_runners()->blocking_pool(), |
| - base::Bind( |
| - &NetworkLoader::RunLibrary, weak_ptr_factory_.GetWeakPtr(), file_)); |
| + OnResponse(make_scoped_ptr(new NetworkLoaderResponse(response.Pass()))); |
| } |
| - MimeTypeToURLMap* mime_type_to_url_; |
| URLLoaderPtr url_loader_; |
| - base::FilePath file_; |
| base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_; |
| }; |
| DynamicApplicationLoader::DynamicApplicationLoader( |
| @@ -272,6 +399,7 @@ void DynamicApplicationLoader::Load( |
| if (resolved_url.SchemeIsFile()) { |
| loaders_.push_back(new LocalLoader(resolved_url, |
| context_, |
| + &mime_type_to_url_, |
| runner_factory_.get(), |
| load_callbacks, |
| loader_complete_callback_)); |