// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/compiler_specific.h" #include "base/message_loop/message_loop.h" #include "base/strings/string_tokenizer.h" #include "mojo/public/cpp/application/application.h" #include "mojo/services/public/cpp/view_manager/types.h" #include "mojo/services/public/interfaces/launcher/launcher.mojom.h" #include "mojo/services/public/interfaces/network/network_service.mojom.h" #include "mojo/services/public/interfaces/network/url_loader.mojom.h" #include "url/gurl.h" namespace mojo { namespace launcher { class LauncherApp; class LauncherConnection : public InterfaceImpl { public: explicit LauncherConnection(LauncherApp* app) : app_(app) {} virtual ~LauncherConnection() {} private: // Overridden from Launcher: virtual void Launch(const String& url) OVERRIDE; LauncherApp* app_; DISALLOW_COPY_AND_ASSIGN(LauncherConnection); }; class LaunchInstance : public URLLoaderClient { public: LaunchInstance(LauncherApp* app, LauncherClient* client, const String& url); virtual ~LaunchInstance() {} private: // Overridden from URLLoaderClient: virtual void OnReceivedRedirect(URLResponsePtr response, const String& new_url, const String& new_method) OVERRIDE { } virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE; virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE { ScheduleDestroy(); } virtual void OnReceivedEndOfResponseBody() OVERRIDE { ScheduleDestroy(); } std::string GetContentType(const Array& headers) { for (size_t i = 0; i < headers.size(); ++i) { base::StringTokenizer t(headers[i], ": ;="); while (t.GetNext()) { if (!t.token_is_delim() && t.token() == "Content-Type") { while (t.GetNext()) { if (!t.token_is_delim()) return t.token(); } } } } return ""; } void ScheduleDestroy() { if (destroy_scheduled_) return; destroy_scheduled_ = true; base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); } LauncherApp* app_; bool destroy_scheduled_; LauncherClient* client_; URLLoaderPtr url_loader_; ScopedDataPipeConsumerHandle response_body_stream_; DISALLOW_COPY_AND_ASSIGN(LaunchInstance); }; class LauncherApp : public Application { public: LauncherApp() { handler_map_["text/html"] = "mojo:mojo_html_viewer"; handler_map_["image/png"] = "mojo:mojo_image_viewer"; } virtual ~LauncherApp() {} URLLoaderPtr CreateURLLoader() { URLLoaderPtr loader; network_service_->CreateURLLoader(Get(&loader)); return loader.Pass(); } std::string GetHandlerForContentType(const std::string& content_type) { HandlerMap::const_iterator it = handler_map_.find(content_type); return it != handler_map_.end() ? it->second : ""; } private: typedef std::map HandlerMap; // Overridden from Application: virtual void Initialize() OVERRIDE { AddService(this); ConnectTo("mojo:mojo_network_service", &network_service_); } HandlerMap handler_map_; NetworkServicePtr network_service_; DISALLOW_COPY_AND_ASSIGN(LauncherApp); }; void LauncherConnection::Launch(const String& url_string) { GURL url(url_string.To()); // For Mojo URLs, the handler can always be found at the origin. // TODO(aa): Return error for invalid URL? if (url.is_valid() && url.SchemeIs("mojo")) { client()->OnLaunch(url_string, url.GetOrigin().spec(), navigation::ResponseDetailsPtr()); return; } new LaunchInstance(app_, client(), url_string); } LaunchInstance::LaunchInstance(LauncherApp* app, LauncherClient* client, const String& url) : app_(app), destroy_scheduled_(false), client_(client) { url_loader_ = app_->CreateURLLoader(); url_loader_.set_client(this); URLRequestPtr request(URLRequest::New()); request->url = url; request->method = "GET"; request->auto_follow_redirects = true; DataPipe data_pipe; response_body_stream_ = data_pipe.consumer_handle.Pass(); url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass()); } void LaunchInstance::OnReceivedResponse(URLResponsePtr response) { std::string content_type = GetContentType(response->headers); std::string handler_url = app_->GetHandlerForContentType(content_type); if (!handler_url.empty()) { navigation::ResponseDetailsPtr nav_response( navigation::ResponseDetails::New()); nav_response->response = response.Pass(); nav_response->response_body_stream = response_body_stream_.Pass(); client_->OnLaunch(nav_response->response->url, handler_url, nav_response.Pass()); } } } // namespace launcher // static Application* Application::Create() { return new launcher::LauncherApp; } } // namespace mojo