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 3 // found in the LICENSE file. 4 5 #include "base/compiler_specific.h" 6 #include "base/message_loop/message_loop.h" 7 #include "base/strings/string_tokenizer.h" 8 #include "mojo/public/cpp/application/application.h" 9 #include "mojo/services/public/cpp/view_manager/types.h" 10 #include "mojo/services/public/interfaces/launcher/launcher.mojom.h" 11 #include "mojo/services/public/interfaces/network/network_service.mojom.h" 12 #include "mojo/services/public/interfaces/network/url_loader.mojom.h" 13 #include "url/gurl.h" 14 15 namespace mojo { 16 namespace launcher { 17 18 class LauncherApp; 19 20 class LauncherConnection : public InterfaceImpl<Launcher> { 21 public: 22 explicit LauncherConnection(LauncherApp* app) : app_(app) {} 23 virtual ~LauncherConnection() {} 24 25 private: 26 // Overridden from Launcher: 27 virtual void Launch(const String& url) OVERRIDE; 28 29 LauncherApp* app_; 30 31 DISALLOW_COPY_AND_ASSIGN(LauncherConnection); 32 }; 33 34 class LaunchInstance : public URLLoaderClient { 35 public: 36 LaunchInstance(LauncherApp* app, 37 LauncherClient* client, 38 const String& url); 39 virtual ~LaunchInstance() {} 40 41 private: 42 // Overridden from URLLoaderClient: 43 virtual void OnReceivedRedirect(URLResponsePtr response, 44 const String& new_url, 45 const String& new_method) OVERRIDE { 46 } 47 virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE; 48 virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE { 49 ScheduleDestroy(); 50 } 51 virtual void OnReceivedEndOfResponseBody() OVERRIDE { 52 ScheduleDestroy(); 53 } 54 55 std::string GetContentType(const Array<String>& headers) { 56 for (size_t i = 0; i < headers.size(); ++i) { 57 base::StringTokenizer t(headers[i], ": ;="); 58 while (t.GetNext()) { 59 if (!t.token_is_delim() && t.token() == "Content-Type") { 60 while (t.GetNext()) { 61 if (!t.token_is_delim()) 62 return t.token(); 63 } 64 } 65 } 66 } 67 return ""; 68 } 69 70 void ScheduleDestroy() { 71 if (destroy_scheduled_) 72 return; 73 destroy_scheduled_ = true; 74 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 75 } 76 77 LauncherApp* app_; 78 bool destroy_scheduled_; 79 LauncherClient* client_; 80 URLLoaderPtr url_loader_; 81 ScopedDataPipeConsumerHandle response_body_stream_; 82 83 DISALLOW_COPY_AND_ASSIGN(LaunchInstance); 84 }; 85 86 class LauncherApp : public Application { 87 public: 88 LauncherApp() { 89 handler_map_["text/html"] = "mojo:mojo_html_viewer"; 90 handler_map_["image/png"] = "mojo:mojo_image_viewer"; 91 } 92 virtual ~LauncherApp() {} 93 94 URLLoaderPtr CreateURLLoader() { 95 URLLoaderPtr loader; 96 network_service_->CreateURLLoader(Get(&loader)); 97 return loader.Pass(); 98 } 99 100 std::string GetHandlerForContentType(const std::string& content_type) { 101 HandlerMap::const_iterator it = handler_map_.find(content_type); 102 return it != handler_map_.end() ? it->second : ""; 103 } 104 105 private: 106 typedef std::map<std::string, std::string> HandlerMap; 107 108 // Overridden from Application: 109 virtual void Initialize() OVERRIDE { 110 AddService<LauncherConnection>(this); 111 ConnectTo("mojo:mojo_network_service", &network_service_); 112 } 113 114 HandlerMap handler_map_; 115 116 NetworkServicePtr network_service_; 117 118 DISALLOW_COPY_AND_ASSIGN(LauncherApp); 119 }; 120 121 void LauncherConnection::Launch(const String& url_string) { 122 GURL url(url_string.To<std::string>()); 123 124 // For Mojo URLs, the handler can always be found at the origin. 125 // TODO(aa): Return error for invalid URL? 126 if (url.is_valid() && url.SchemeIs("mojo")) { 127 client()->OnLaunch(url_string, 128 url.GetOrigin().spec(), 129 navigation::ResponseDetailsPtr()); 130 return; 131 } 132 133 new LaunchInstance(app_, client(), url_string); 134 } 135 136 LaunchInstance::LaunchInstance(LauncherApp* app, 137 LauncherClient* client, 138 const String& url) 139 : app_(app), 140 destroy_scheduled_(false), 141 client_(client) { 142 url_loader_ = app_->CreateURLLoader(); 143 url_loader_.set_client(this); 144 145 URLRequestPtr request(URLRequest::New()); 146 request->url = url; 147 request->method = "GET"; 148 request->auto_follow_redirects = true; 149 150 DataPipe data_pipe; 151 response_body_stream_ = data_pipe.consumer_handle.Pass(); 152 153 url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass()); 154 } 155 156 void LaunchInstance::OnReceivedResponse(URLResponsePtr response) { 157 std::string content_type = GetContentType(response->headers); 158 std::string handler_url = app_->GetHandlerForContentType(content_type); 159 if (!handler_url.empty()) { 160 navigation::ResponseDetailsPtr nav_response( 161 navigation::ResponseDetails::New()); 162 nav_response->response = response.Pass(); 163 nav_response->response_body_stream = response_body_stream_.Pass(); 164 client_->OnLaunch(nav_response->response->url, handler_url, 165 nav_response.Pass()); 166 } 167 } 168 169 } // namespace launcher 170 171 // static 172 Application* Application::Create() { 173 return new launcher::LauncherApp; 174 } 175 176 } // namespace mojo 177