Home | History | Annotate | Download | only in launcher
      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