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:
LauncherConnection(LauncherApp * app)22 explicit LauncherConnection(LauncherApp* app) : app_(app) {}
~LauncherConnection()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);
~LaunchInstance()39 virtual ~LaunchInstance() {}
40
41 private:
42 // Overridden from URLLoaderClient:
OnReceivedRedirect(URLResponsePtr response,const String & new_url,const String & new_method)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;
OnReceivedError(NetworkErrorPtr error)48 virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE {
49 ScheduleDestroy();
50 }
OnReceivedEndOfResponseBody()51 virtual void OnReceivedEndOfResponseBody() OVERRIDE {
52 ScheduleDestroy();
53 }
54
GetContentType(const Array<String> & headers)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
ScheduleDestroy()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:
LauncherApp()88 LauncherApp() {
89 handler_map_["text/html"] = "mojo:mojo_html_viewer";
90 handler_map_["image/png"] = "mojo:mojo_image_viewer";
91 }
~LauncherApp()92 virtual ~LauncherApp() {}
93
CreateURLLoader()94 URLLoaderPtr CreateURLLoader() {
95 URLLoaderPtr loader;
96 network_service_->CreateURLLoader(Get(&loader));
97 return loader.Pass();
98 }
99
GetHandlerForContentType(const std::string & content_type)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:
Initialize()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
Launch(const String & url_string)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
LaunchInstance(LauncherApp * app,LauncherClient * client,const String & url)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
OnReceivedResponse(URLResponsePtr response)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
Create()172 Application* Application::Create() {
173 return new launcher::LauncherApp;
174 }
175
176 } // namespace mojo
177