Home | History | Annotate | Download | only in webui
      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 <limits>
      6 
      7 #include "base/command_line.h"
      8 #include "base/file_util.h"
      9 #include "base/files/file_path.h"
     10 #include "base/path_service.h"
     11 #include "base/run_loop.h"
     12 #include "base/strings/string_util.h"
     13 #include "content/browser/webui/web_ui_controller_factory_registry.h"
     14 #include "content/public/browser/browser_context.h"
     15 #include "content/public/browser/render_view_host.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "content/public/browser/web_ui_controller.h"
     18 #include "content/public/browser/web_ui_data_source.h"
     19 #include "content/public/common/content_paths.h"
     20 #include "content/public/common/content_switches.h"
     21 #include "content/public/common/url_utils.h"
     22 #include "content/public/test/content_browser_test.h"
     23 #include "content/public/test/content_browser_test_utils.h"
     24 #include "content/test/data/web_ui_test_mojo_bindings.mojom.h"
     25 #include "grit/content_resources.h"
     26 #include "mojo/common/test/test_utils.h"
     27 #include "mojo/public/js/bindings/constants.h"
     28 
     29 namespace content {
     30 namespace {
     31 
     32 bool got_message = false;
     33 
     34 // The bindings for the page are generated from a .mojom file. This code looks
     35 // up the generated file from disk and returns it.
     36 bool GetResource(const std::string& id,
     37                  const WebUIDataSource::GotDataCallback& callback) {
     38   // These are handled by the WebUIDataSource that AddMojoDataSource() creates.
     39   if (id == mojo::kCodecModuleName ||
     40       id == mojo::kConnectionModuleName ||
     41       id == mojo::kConnectorModuleName ||
     42       id == mojo::kUnicodeModuleName ||
     43       id == mojo::kRouterModuleName)
     44     return false;
     45 
     46   std::string contents;
     47   CHECK(base::ReadFileToString(mojo::test::GetFilePathForJSResource(id),
     48                                &contents,
     49                                std::string::npos)) << id;
     50   base::RefCountedString* ref_contents = new base::RefCountedString;
     51   ref_contents->data() = contents;
     52   callback.Run(ref_contents);
     53   return true;
     54 }
     55 
     56 class BrowserTargetImpl : public BrowserTarget {
     57  public:
     58   BrowserTargetImpl(mojo::ScopedMessagePipeHandle handle,
     59                     base::RunLoop* run_loop)
     60       : run_loop_(run_loop) {
     61     renderer_.Bind(handle.Pass());
     62     renderer_.set_client(this);
     63   }
     64 
     65   virtual ~BrowserTargetImpl() {}
     66 
     67   // BrowserTarget overrides:
     68   virtual void PingResponse() OVERRIDE {
     69     NOTREACHED();
     70   }
     71 
     72  protected:
     73   RendererTargetPtr renderer_;
     74   base::RunLoop* run_loop_;
     75 
     76  private:
     77   DISALLOW_COPY_AND_ASSIGN(BrowserTargetImpl);
     78 };
     79 
     80 class PingBrowserTargetImpl : public BrowserTargetImpl {
     81  public:
     82   PingBrowserTargetImpl(mojo::ScopedMessagePipeHandle handle,
     83                         base::RunLoop* run_loop)
     84       : BrowserTargetImpl(handle.Pass(), run_loop) {
     85     renderer_->Ping();
     86   }
     87 
     88   virtual ~PingBrowserTargetImpl() {}
     89 
     90   // BrowserTarget overrides:
     91   // Quit the RunLoop when called.
     92   virtual void PingResponse() OVERRIDE {
     93     got_message = true;
     94     run_loop_->Quit();
     95   }
     96 
     97  private:
     98   DISALLOW_COPY_AND_ASSIGN(PingBrowserTargetImpl);
     99 };
    100 
    101 // WebUIController that sets up mojo bindings.
    102 class TestWebUIController : public WebUIController {
    103  public:
    104    TestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
    105       : WebUIController(web_ui),
    106         run_loop_(run_loop) {
    107     content::WebUIDataSource* data_source =
    108         WebUIDataSource::AddMojoDataSource(
    109             web_ui->GetWebContents()->GetBrowserContext());
    110     data_source->SetRequestFilter(base::Bind(&GetResource));
    111   }
    112 
    113  protected:
    114   base::RunLoop* run_loop_;
    115   scoped_ptr<BrowserTargetImpl> browser_target_;
    116 
    117  private:
    118   DISALLOW_COPY_AND_ASSIGN(TestWebUIController);
    119 };
    120 
    121 // TestWebUIController that additionally creates the ping test BrowserTarget
    122 // implementation at the right time.
    123 class PingTestWebUIController : public TestWebUIController {
    124  public:
    125    PingTestWebUIController(WebUI* web_ui, base::RunLoop* run_loop)
    126        : TestWebUIController(web_ui, run_loop) {
    127    }
    128 
    129   // WebUIController overrides:
    130   virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE {
    131     mojo::MessagePipe pipe;
    132     browser_target_.reset(
    133         new PingBrowserTargetImpl(pipe.handle0.Pass(), run_loop_));
    134     render_view_host->SetWebUIHandle(pipe.handle1.Pass());
    135   }
    136 
    137  private:
    138   DISALLOW_COPY_AND_ASSIGN(PingTestWebUIController);
    139 };
    140 
    141 // WebUIControllerFactory that creates TestWebUIController.
    142 class TestWebUIControllerFactory : public WebUIControllerFactory {
    143  public:
    144   TestWebUIControllerFactory() : run_loop_(NULL) {}
    145 
    146   void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
    147 
    148   virtual WebUIController* CreateWebUIControllerForURL(
    149       WebUI* web_ui, const GURL& url) const OVERRIDE {
    150     if (url.query() == "ping")
    151       return new PingTestWebUIController(web_ui, run_loop_);
    152     return NULL;
    153   }
    154   virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
    155       const GURL& url) const OVERRIDE {
    156     return reinterpret_cast<WebUI::TypeID>(1);
    157   }
    158   virtual bool UseWebUIForURL(BrowserContext* browser_context,
    159                               const GURL& url) const OVERRIDE {
    160     return true;
    161   }
    162   virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
    163                                       const GURL& url) const OVERRIDE {
    164     return true;
    165   }
    166 
    167  private:
    168   base::RunLoop* run_loop_;
    169 
    170   DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory);
    171 };
    172 
    173 class WebUIMojoTest : public ContentBrowserTest {
    174  public:
    175   WebUIMojoTest() {
    176     WebUIControllerFactory::RegisterFactory(&factory_);
    177   }
    178 
    179   virtual ~WebUIMojoTest() {
    180     WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
    181   }
    182 
    183   TestWebUIControllerFactory* factory() { return &factory_; }
    184 
    185  private:
    186   TestWebUIControllerFactory factory_;
    187 
    188   DISALLOW_COPY_AND_ASSIGN(WebUIMojoTest);
    189 };
    190 
    191 // Loads a webui page that contains mojo bindings and verifies a message makes
    192 // it from the browser to the page and back.
    193 IN_PROC_BROWSER_TEST_F(WebUIMojoTest, EndToEndPing) {
    194   // Currently there is no way to have a generated file included in the isolate
    195   // files. If the bindings file doesn't exist assume we're on such a bot and
    196   // pass.
    197   // TODO(sky): remove this conditional when isolates support copying from gen.
    198   const base::FilePath test_file_path(
    199       mojo::test::GetFilePathForJSResource(
    200           "content/test/data/web_ui_test_mojo_bindings.mojom"));
    201   if (!base::PathExists(test_file_path)) {
    202     LOG(WARNING) << " mojom binding file doesn't exist, assuming on isolate";
    203     return;
    204   }
    205 
    206   got_message = false;
    207   ASSERT_TRUE(test_server()->Start());
    208   base::RunLoop run_loop;
    209   factory()->set_run_loop(&run_loop);
    210   GURL test_url(test_server()->GetURL("files/web_ui_mojo.html?ping"));
    211   NavigateToURL(shell(), test_url);
    212   // RunLoop is quit when message received from page.
    213   run_loop.Run();
    214   EXPECT_TRUE(got_message);
    215 }
    216 
    217 }  // namespace
    218 }  // namespace content
    219