headless_devtools_client_browsertest.cc 38.4 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 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 <memory>

7
#include "base/json/json_reader.h"
8
#include "base/run_loop.h"
9 10
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
11
#include "content/public/common/url_constants.h"
12
#include "content/public/test/browser_test.h"
13
#include "headless/lib/browser/headless_web_contents_impl.h"
14
#include "headless/public/devtools/domains/dom.h"
15
#include "headless/public/devtools/domains/emulation.h"
16
#include "headless/public/devtools/domains/inspector.h"
17 18 19 20
#include "headless/public/devtools/domains/network.h"
#include "headless/public/devtools/domains/page.h"
#include "headless/public/devtools/domains/runtime.h"
#include "headless/public/devtools/domains/target.h"
21 22
#include "headless/public/headless_browser.h"
#include "headless/public/headless_devtools_client.h"
23
#include "headless/public/headless_devtools_target.h"
24
#include "headless/test/headless_browser_test.h"
25
#include "headless/test/test_protocol_handler.h"
26
#include "testing/gmock/include/gmock/gmock.h"
27 28 29
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

30 31 32 33 34 35
#define EXPECT_SIZE_EQ(expected, actual)               \
  do {                                                 \
    EXPECT_EQ((expected).width(), (actual).width());   \
    EXPECT_EQ((expected).height(), (actual).height()); \
  } while (false)

36 37
using testing::ElementsAre;

38 39
namespace headless {

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
namespace {

std::vector<HeadlessWebContents*> GetAllWebContents(HeadlessBrowser* browser) {
  std::vector<HeadlessWebContents*> result;

  for (HeadlessBrowserContext* browser_context :
       browser->GetAllBrowserContexts()) {
    std::vector<HeadlessWebContents*> web_contents =
        browser_context->GetAllWebContents();
    result.insert(result.end(), web_contents.begin(), web_contents.end());
  }

  return result;
}

}  // namespace

57 58 59
class HeadlessDevToolsClientNavigationTest
    : public HeadlessAsyncDevTooledBrowserTest,
      page::ExperimentalObserver {
60
 public:
61
  void RunDevTooledTest() override {
62 63 64 65 66
    EXPECT_TRUE(embedded_test_server()->Start());
    std::unique_ptr<page::NavigateParams> params =
        page::NavigateParams::Builder()
            .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
            .Build();
67
    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
68 69 70 71 72
    base::RunLoop run_loop;
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
    run_loop.Run();
73 74 75
    devtools_client_->GetPage()->Navigate(std::move(params));
  }

76
  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
77
    devtools_client_->GetPage()->Disable();
78
    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
79 80
    FinishAsynchronousTest();
  }
81 82 83

  // Check that events with no parameters still get a parameters object.
  void OnFrameResized(const page::FrameResizedParams& params) override {}
84 85
};

86
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientNavigationTest);
87

88 89
class HeadlessDevToolsClientEvalTest
    : public HeadlessAsyncDevTooledBrowserTest {
90
 public:
91
  void RunDevTooledTest() override {
92 93 94
    std::unique_ptr<runtime::EvaluateParams> params =
        runtime::EvaluateParams::Builder().SetExpression("1 + 2").Build();
    devtools_client_->GetRuntime()->Evaluate(
95 96 97 98 99 100 101 102
        std::move(params),
        base::Bind(&HeadlessDevToolsClientEvalTest::OnFirstResult,
                   base::Unretained(this)));
    // Test the convenience overload which only takes the required command
    // parameters.
    devtools_client_->GetRuntime()->Evaluate(
        "24 * 7", base::Bind(&HeadlessDevToolsClientEvalTest::OnSecondResult,
                             base::Unretained(this)));
103 104
  }

105
  void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
106 107 108 109
    int value;
    EXPECT_TRUE(result->GetResult()->HasValue());
    EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
    EXPECT_EQ(3, value);
110 111 112 113 114 115 116
  }

  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
    int value;
    EXPECT_TRUE(result->GetResult()->HasValue());
    EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
    EXPECT_EQ(168, value);
117 118 119 120
    FinishAsynchronousTest();
  }
};

121
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientEvalTest);
122

123 124
class HeadlessDevToolsClientCallbackTest
    : public HeadlessAsyncDevTooledBrowserTest {
125 126 127
 public:
  HeadlessDevToolsClientCallbackTest() : first_result_received_(false) {}

128
  void RunDevTooledTest() override {
129
    // Null callback without parameters.
130
    devtools_client_->GetPage()->Enable();
131 132 133
    // Null callback with parameters.
    devtools_client_->GetRuntime()->Evaluate("true");
    // Non-null callback without parameters.
134
    devtools_client_->GetPage()->Disable(
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
        base::Bind(&HeadlessDevToolsClientCallbackTest::OnFirstResult,
                   base::Unretained(this)));
    // Non-null callback with parameters.
    devtools_client_->GetRuntime()->Evaluate(
        "true", base::Bind(&HeadlessDevToolsClientCallbackTest::OnSecondResult,
                           base::Unretained(this)));
  }

  void OnFirstResult() {
    EXPECT_FALSE(first_result_received_);
    first_result_received_ = true;
  }

  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
    EXPECT_TRUE(first_result_received_);
    FinishAsynchronousTest();
  }

 private:
  bool first_result_received_;
};

157
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientCallbackTest);
158

159 160 161
class HeadlessDevToolsClientObserverTest
    : public HeadlessAsyncDevTooledBrowserTest,
      network::Observer {
162
 public:
163
  void RunDevTooledTest() override {
164
    EXPECT_TRUE(embedded_test_server()->Start());
165
    base::RunLoop run_loop;
166
    devtools_client_->GetNetwork()->AddObserver(this);
167 168 169 170 171
    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    devtools_client_->GetPage()->Navigate(
        embedded_test_server()->GetURL("/hello.html").spec());
  }

  void OnRequestWillBeSent(
      const network::RequestWillBeSentParams& params) override {
    EXPECT_EQ("GET", params.GetRequest()->GetMethod());
    EXPECT_EQ(embedded_test_server()->GetURL("/hello.html").spec(),
              params.GetRequest()->GetUrl());
  }

  void OnResponseReceived(
      const network::ResponseReceivedParams& params) override {
    EXPECT_EQ(200, params.GetResponse()->GetStatus());
    EXPECT_EQ("OK", params.GetResponse()->GetStatusText());
    std::string content_type;
    EXPECT_TRUE(params.GetResponse()->GetHeaders()->GetString("Content-Type",
                                                              &content_type));
    EXPECT_EQ("text/html", content_type);

192
    devtools_client_->GetNetwork()->Disable();
193 194 195 196 197
    devtools_client_->GetNetwork()->RemoveObserver(this);
    FinishAsynchronousTest();
  }
};

198
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientObserverTest);
199

200
class HeadlessDevToolsClientExperimentalTest
201
    : public HeadlessAsyncDevTooledBrowserTest,
202 203
      page::ExperimentalObserver {
 public:
204
  void RunDevTooledTest() override {
205
    EXPECT_TRUE(embedded_test_server()->Start());
206 207 208 209 210 211
    base::RunLoop run_loop;
    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();
212
    // Check that experimental commands require parameter objects.
213 214 215 216 217 218
    devtools_client_->GetRuntime()
        ->GetExperimental()
        ->SetCustomObjectFormatterEnabled(
            runtime::SetCustomObjectFormatterEnabledParams::Builder()
                .SetEnabled(false)
                .Build());
219

220 221 222 223 224
    // Check that a previously experimental command which takes no parameters
    // still works by giving it a parameter object.
    devtools_client_->GetRuntime()->GetExperimental()->RunIfWaitingForDebugger(
        runtime::RunIfWaitingForDebuggerParams::Builder().Build());

225 226 227 228 229 230
    devtools_client_->GetPage()->Navigate(
        embedded_test_server()->GetURL("/hello.html").spec());
  }

  void OnFrameStoppedLoading(
      const page::FrameStoppedLoadingParams& params) override {
231 232 233
    devtools_client_->GetPage()->Disable();
    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);

234 235 236 237 238 239
    // Check that a non-experimental command which has no return value can be
    // called with a void() callback.
    devtools_client_->GetPage()->Reload(
        page::ReloadParams::Builder().Build(),
        base::Bind(&HeadlessDevToolsClientExperimentalTest::OnReloadStarted,
                   base::Unretained(this)));
240
  }
241 242

  void OnReloadStarted() { FinishAsynchronousTest(); }
243 244
};

245
HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientExperimentalTest);
246

247
class TargetDomainCreateAndDeletePageTest
248 249 250 251
    : public HeadlessAsyncDevTooledBrowserTest {
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());

252
    EXPECT_EQ(1u, GetAllWebContents(browser()).size());
253

254 255
    devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
        target::CreateTargetParams::Builder()
256
            .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
257 258 259
            .SetWidth(1)
            .SetHeight(1)
            .Build(),
260
        base::Bind(&TargetDomainCreateAndDeletePageTest::OnCreateTargetResult,
261 262 263 264
                   base::Unretained(this)));
  }

  void OnCreateTargetResult(
265
      std::unique_ptr<target::CreateTargetResult> result) {
266
    EXPECT_EQ(2u, GetAllWebContents(browser()).size());
267

268 269 270 271 272 273 274
    HeadlessWebContentsImpl* contents = HeadlessWebContentsImpl::From(
        browser()->GetWebContentsForDevToolsAgentHostId(result->GetTargetId()));
    EXPECT_SIZE_EQ(gfx::Size(1, 1), contents->web_contents()
                                        ->GetRenderWidgetHostView()
                                        ->GetViewBounds()
                                        .size());

275 276
    devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
        target::CloseTargetParams::Builder()
277 278
            .SetTargetId(result->GetTargetId())
            .Build(),
279
        base::Bind(&TargetDomainCreateAndDeletePageTest::OnCloseTargetResult,
280 281 282
                   base::Unretained(this)));
  }

283
  void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) {
altimin's avatar
altimin committed
284
    EXPECT_TRUE(result->GetSuccess());
285
    EXPECT_EQ(1u, GetAllWebContents(browser()).size());
286 287 288 289
    FinishAsynchronousTest();
  }
};

290
HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeletePageTest);
291

292
class TargetDomainCreateAndDeleteBrowserContextTest
altimin's avatar
altimin committed
293 294 295 296
    : public HeadlessAsyncDevTooledBrowserTest {
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());

297
    EXPECT_EQ(1u, GetAllWebContents(browser()).size());
altimin's avatar
altimin committed
298

299 300 301
    devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
        target::CreateBrowserContextParams::Builder().Build(),
        base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest::
altimin's avatar
altimin committed
302 303 304 305 306
                       OnCreateContextResult,
                   base::Unretained(this)));
  }

  void OnCreateContextResult(
307
      std::unique_ptr<target::CreateBrowserContextResult> result) {
altimin's avatar
altimin committed
308 309
    browser_context_id_ = result->GetBrowserContextId();

310 311
    devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
        target::CreateTargetParams::Builder()
altimin's avatar
altimin committed
312 313 314 315 316
            .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
            .SetBrowserContextId(result->GetBrowserContextId())
            .SetWidth(1)
            .SetHeight(1)
            .Build(),
317
        base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest::
altimin's avatar
altimin committed
318 319 320 321 322
                       OnCreateTargetResult,
                   base::Unretained(this)));
  }

  void OnCreateTargetResult(
323
      std::unique_ptr<target::CreateTargetResult> result) {
324
    EXPECT_EQ(2u, GetAllWebContents(browser()).size());
altimin's avatar
altimin committed
325

326 327
    devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
        target::CloseTargetParams::Builder()
altimin's avatar
altimin committed
328 329
            .SetTargetId(result->GetTargetId())
            .Build(),
330
        base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest::
altimin's avatar
altimin committed
331 332 333 334
                       OnCloseTargetResult,
                   base::Unretained(this)));
  }

335
  void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) {
336
    EXPECT_EQ(1u, GetAllWebContents(browser()).size());
altimin's avatar
altimin committed
337 338
    EXPECT_TRUE(result->GetSuccess());

339 340
    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
        target::DisposeBrowserContextParams::Builder()
altimin's avatar
altimin committed
341 342
            .SetBrowserContextId(browser_context_id_)
            .Build(),
343
        base::Bind(&TargetDomainCreateAndDeleteBrowserContextTest::
altimin's avatar
altimin committed
344 345 346 347 348
                       OnDisposeBrowserContextResult,
                   base::Unretained(this)));
  }

  void OnDisposeBrowserContextResult(
349
      std::unique_ptr<target::DisposeBrowserContextResult> result) {
altimin's avatar
altimin committed
350 351 352 353 354 355 356 357
    EXPECT_TRUE(result->GetSuccess());
    FinishAsynchronousTest();
  }

 private:
  std::string browser_context_id_;
};

358
HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateAndDeleteBrowserContextTest);
altimin's avatar
altimin committed
359

360
class TargetDomainDisposeContextFailsIfInUse
361 362 363 364
    : public HeadlessAsyncDevTooledBrowserTest {
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());

365
    EXPECT_EQ(1u, GetAllWebContents(browser()).size());
366 367 368
    devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
        target::CreateBrowserContextParams::Builder().Build(),
        base::Bind(&TargetDomainDisposeContextFailsIfInUse::OnContextCreated,
369 370 371 372
                   base::Unretained(this)));
  }

  void OnContextCreated(
373
      std::unique_ptr<target::CreateBrowserContextResult> result) {
374 375
    context_id_ = result->GetBrowserContextId();

376 377
    devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
        target::CreateTargetParams::Builder()
378
            .SetUrl(embedded_test_server()->GetURL("/hello.html").spec())
379 380 381
            .SetBrowserContextId(context_id_)
            .Build(),
        base::Bind(
382
            &TargetDomainDisposeContextFailsIfInUse::OnCreateTargetResult,
383 384 385 386
            base::Unretained(this)));
  }

  void OnCreateTargetResult(
387
      std::unique_ptr<target::CreateTargetResult> result) {
388 389
    page_id_ = result->GetTargetId();

390 391
    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
        target::DisposeBrowserContextParams::Builder()
392 393
            .SetBrowserContextId(context_id_)
            .Build(),
394
        base::Bind(&TargetDomainDisposeContextFailsIfInUse::
395 396 397 398 399
                       OnDisposeBrowserContextResult,
                   base::Unretained(this)));
  }

  void OnDisposeBrowserContextResult(
400
      std::unique_ptr<target::DisposeBrowserContextResult> result) {
401 402 403
    EXPECT_FALSE(result->GetSuccess());

    // Close the page and try again.
404 405
    devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
        target::CloseTargetParams::Builder().SetTargetId(page_id_).Build(),
406
        base::Bind(
407
            &TargetDomainDisposeContextFailsIfInUse::OnCloseTargetResult,
408 409 410
            base::Unretained(this)));
  }

411
  void OnCloseTargetResult(std::unique_ptr<target::CloseTargetResult> result) {
412 413
    EXPECT_TRUE(result->GetSuccess());

414 415
    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
        target::DisposeBrowserContextParams::Builder()
416 417
            .SetBrowserContextId(context_id_)
            .Build(),
418
        base::Bind(&TargetDomainDisposeContextFailsIfInUse::
419 420 421 422 423
                       OnDisposeBrowserContextResult2,
                   base::Unretained(this)));
  }

  void OnDisposeBrowserContextResult2(
424
      std::unique_ptr<target::DisposeBrowserContextResult> result) {
425 426 427 428 429 430 431 432 433
    EXPECT_TRUE(result->GetSuccess());
    FinishAsynchronousTest();
  }

 private:
  std::string context_id_;
  std::string page_id_;
};

434
HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainDisposeContextFailsIfInUse);
435

436
class TargetDomainCreateTwoContexts : public HeadlessAsyncDevTooledBrowserTest,
437 438
                                      public target::ExperimentalObserver,
                                      public page::Observer {
439 440 441 442
 public:
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());

443 444 445 446 447 448 449
    base::RunLoop run_loop;
    devtools_client_->GetPage()->AddObserver(this);
    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();

450 451 452 453
    devtools_client_->GetTarget()->GetExperimental()->AddObserver(this);
    devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
        target::CreateBrowserContextParams::Builder().Build(),
        base::Bind(&TargetDomainCreateTwoContexts::OnContextOneCreated,
454 455
                   base::Unretained(this)));

456 457 458
    devtools_client_->GetTarget()->GetExperimental()->CreateBrowserContext(
        target::CreateBrowserContextParams::Builder().Build(),
        base::Bind(&TargetDomainCreateTwoContexts::OnContextTwoCreated,
459 460 461 462
                   base::Unretained(this)));
  }

  void OnContextOneCreated(
463
      std::unique_ptr<target::CreateBrowserContextResult> result) {
464 465 466 467 468
    context_id_one_ = result->GetBrowserContextId();
    MaybeCreatePages();
  }

  void OnContextTwoCreated(
469
      std::unique_ptr<target::CreateBrowserContextResult> result) {
470 471 472 473 474 475 476 477
    context_id_two_ = result->GetBrowserContextId();
    MaybeCreatePages();
  }

  void MaybeCreatePages() {
    if (context_id_one_.empty() || context_id_two_.empty())
      return;

478 479
    devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
        target::CreateTargetParams::Builder()
480
            .SetUrl("about://blank")
481 482
            .SetBrowserContextId(context_id_one_)
            .Build(),
483
        base::Bind(&TargetDomainCreateTwoContexts::OnCreateTargetOneResult,
484 485
                   base::Unretained(this)));

486 487
    devtools_client_->GetTarget()->GetExperimental()->CreateTarget(
        target::CreateTargetParams::Builder()
488
            .SetUrl("about://blank")
489 490
            .SetBrowserContextId(context_id_two_)
            .Build(),
491
        base::Bind(&TargetDomainCreateTwoContexts::OnCreateTargetTwoResult,
492 493 494 495
                   base::Unretained(this)));
  }

  void OnCreateTargetOneResult(
496
      std::unique_ptr<target::CreateTargetResult> result) {
497 498 499 500 501
    page_id_one_ = result->GetTargetId();
    MaybeTestIsolation();
  }

  void OnCreateTargetTwoResult(
502
      std::unique_ptr<target::CreateTargetResult> result) {
503 504 505 506 507 508 509 510
    page_id_two_ = result->GetTargetId();
    MaybeTestIsolation();
  }

  void MaybeTestIsolation() {
    if (page_id_one_.empty() || page_id_two_.empty())
      return;

511 512 513 514 515
    devtools_client_->GetTarget()->GetExperimental()->AttachToTarget(
        target::AttachToTargetParams::Builder()
            .SetTargetId(page_id_one_)
            .Build(),
        base::Bind(&TargetDomainCreateTwoContexts::OnAttachedToTargetOne,
516 517
                   base::Unretained(this)));

518 519 520 521 522
    devtools_client_->GetTarget()->GetExperimental()->AttachToTarget(
        target::AttachToTargetParams::Builder()
            .SetTargetId(page_id_two_)
            .Build(),
        base::Bind(&TargetDomainCreateTwoContexts::OnAttachedToTargetTwo,
523 524 525
                   base::Unretained(this)));
  }

526 527
  void OnAttachedToTargetOne(
      std::unique_ptr<target::AttachToTargetResult> result) {
528 529 530 531 532 533 534 535 536 537 538
    StopNavigationOnTarget(101, page_id_one_);
  }

  void OnAttachedToTargetTwo(
      std::unique_ptr<target::AttachToTargetResult> result) {
    StopNavigationOnTarget(102, page_id_two_);
  }

  void StopNavigationOnTarget(int message_id, std::string target_id) {
    // Avoid triggering Page.loadEventFired for about://blank if loading hasn't
    // finished yet.
539 540
    devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget(
        target::SendMessageToTargetParams::Builder()
541 542 543
            .SetTargetId(target_id)
            .SetMessage("{\"id\":" + std::to_string(message_id) +
                        ", \"method\": \"Page.stopLoading\"}")
544 545 546
            .Build());
  }

547
  void EnablePageOnTarget(int message_id, std::string target_id) {
548 549
    devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget(
        target::SendMessageToTargetParams::Builder()
550 551 552 553 554 555 556 557 558 559 560 561 562 563
            .SetTargetId(target_id)
            .SetMessage("{\"id\":" + std::to_string(message_id) +
                        ", \"method\": \"Page.enable\"}")
            .Build());
  }

  void NavigateTarget(int message_id, std::string target_id) {
    devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget(
        target::SendMessageToTargetParams::Builder()
            .SetTargetId(target_id)
            .SetMessage(
                "{\"id\":" + std::to_string(message_id) +
                ", \"method\": \"Page.navigate\", \"params\": {\"url\": \"" +
                embedded_test_server()->GetURL("/hello.html").spec() + "\"}}")
564 565 566 567 568 569 570
            .Build());
  }

  void MaybeSetCookieOnPageOne() {
    if (!page_one_loaded_ || !page_two_loaded_)
      return;

571 572
    devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget(
        target::SendMessageToTargetParams::Builder()
573
            .SetTargetId(page_id_one_)
574
            .SetMessage("{\"id\":401, \"method\": \"Runtime.evaluate\", "
575 576 577 578 579
                        "\"params\": {\"expression\": "
                        "\"document.cookie = 'foo=bar';\"}}")
            .Build());
  }

580 581
  void OnReceivedMessageFromTarget(
      const target::ReceivedMessageFromTargetParams& params) override {
582 583 584 585 586 587
    std::unique_ptr<base::Value> message =
        base::JSONReader::Read(params.GetMessage(), base::JSON_PARSE_RFC);
    const base::DictionaryValue* message_dict;
    if (!message || !message->GetAsDictionary(&message_dict)) {
      return;
    }
588

589 590 591 592 593 594 595 596 597 598 599
    std::string method;
    if (message_dict->GetString("method", &method) &&
        method == "Page.loadEventFired") {
      if (params.GetTargetId() == page_id_one_) {
        page_one_loaded_ = true;
      } else if (params.GetTargetId() == page_id_two_) {
        page_two_loaded_ = true;
      }
      MaybeSetCookieOnPageOne();
      return;
    }
600 601 602 603

    int message_id = 0;
    if (!message_dict->GetInteger("id", &message_id))
      return;
604 605
    const base::DictionaryValue* result_dict;
    if (message_dict->GetDictionary("result", &result_dict)) {
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      if (message_id == 101) {
        // 101: Page.stopNavigation on target one.
        EXPECT_EQ(page_id_one_, params.GetTargetId());
        EnablePageOnTarget(201, page_id_one_);
      } else if (message_id == 102) {
        // 102: Page.stopNavigation on target two.
        EXPECT_EQ(page_id_two_, params.GetTargetId());
        EnablePageOnTarget(202, page_id_two_);
      } else if (message_id == 201) {
        // 201: Page.enable on target one.
        EXPECT_EQ(page_id_one_, params.GetTargetId());
        NavigateTarget(301, page_id_one_);
      } else if (message_id == 202) {
        // 202: Page.enable on target two.
        EXPECT_EQ(page_id_two_, params.GetTargetId());
        NavigateTarget(302, page_id_two_);
      } else if (message_id == 401) {
        // 401: Runtime.evaluate on target one.
        EXPECT_EQ(page_id_one_, params.GetTargetId());

626 627 628 629
        // TODO(alexclarke): Make some better bindings
        // for Target.SendMessageToTarget.
        devtools_client_->GetTarget()->GetExperimental()->SendMessageToTarget(
            target::SendMessageToTargetParams::Builder()
630
                .SetTargetId(page_id_two_)
631
                .SetMessage("{\"id\":402, \"method\": \"Runtime.evaluate\", "
632 633 634
                            "\"params\": {\"expression\": "
                            "\"document.cookie;\"}}")
                .Build());
635 636 637 638 639 640 641 642 643
      } else if (message_id == 402) {
        // 402: Runtime.evaluate on target two.
        EXPECT_EQ(page_id_two_, params.GetTargetId());

        // There's a nested result. We want the inner one.
        EXPECT_TRUE(result_dict->GetDictionary("result", &result_dict));

        std::string value;
        EXPECT_TRUE(result_dict->GetString("value", &value));
644 645
        EXPECT_EQ("", value) << "Page 2 should not share cookies from page one";

646 647
        devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
            target::CloseTargetParams::Builder()
648 649
                .SetTargetId(page_id_one_)
                .Build(),
650
            base::Bind(&TargetDomainCreateTwoContexts::OnCloseTarget,
651 652
                       base::Unretained(this)));

653 654
        devtools_client_->GetTarget()->GetExperimental()->CloseTarget(
            target::CloseTargetParams::Builder()
655 656
                .SetTargetId(page_id_two_)
                .Build(),
657
            base::Bind(&TargetDomainCreateTwoContexts::OnCloseTarget,
658 659
                       base::Unretained(this)));

660
        devtools_client_->GetTarget()->GetExperimental()->RemoveObserver(this);
661 662 663 664
      }
    }
  }

665
  void OnCloseTarget(std::unique_ptr<target::CloseTargetResult> result) {
666 667 668 669 670
    page_close_count_++;

    if (page_close_count_ < 2)
      return;

671 672
    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
        target::DisposeBrowserContextParams::Builder()
673 674
            .SetBrowserContextId(context_id_one_)
            .Build(),
675
        base::Bind(&TargetDomainCreateTwoContexts::OnCloseContext,
676 677
                   base::Unretained(this)));

678 679
    devtools_client_->GetTarget()->GetExperimental()->DisposeBrowserContext(
        target::DisposeBrowserContextParams::Builder()
680 681
            .SetBrowserContextId(context_id_two_)
            .Build(),
682
        base::Bind(&TargetDomainCreateTwoContexts::OnCloseContext,
683 684 685 686
                   base::Unretained(this)));
  }

  void OnCloseContext(
687
      std::unique_ptr<target::DisposeBrowserContextResult> result) {
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
    EXPECT_TRUE(result->GetSuccess());
    if (++context_closed_count_ < 2)
      return;

    FinishAsynchronousTest();
  }

 private:
  std::string context_id_one_;
  std::string context_id_two_;
  std::string page_id_one_;
  std::string page_id_two_;
  bool page_one_loaded_ = false;
  bool page_two_loaded_ = false;
  int page_close_count_ = 0;
  int context_closed_count_ = 0;
};

706
HEADLESS_ASYNC_DEVTOOLED_TEST_F(TargetDomainCreateTwoContexts);
707

708 709 710 711 712 713
class HeadlessDevToolsNavigationControlTest
    : public HeadlessAsyncDevTooledBrowserTest,
      page::ExperimentalObserver {
 public:
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());
714
    base::RunLoop run_loop;
715
    devtools_client_->GetPage()->GetExperimental()->AddObserver(this);
716 717 718 719
    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    devtools_client_->GetPage()->GetExperimental()->SetControlNavigations(
        headless::page::SetControlNavigationsParams::Builder()
            .SetEnabled(true)
            .Build());
    devtools_client_->GetPage()->Navigate(
        embedded_test_server()->GetURL("/hello.html").spec());
  }

  void OnNavigationRequested(
      const headless::page::NavigationRequestedParams& params) override {
    navigation_requested_ = true;
    // Allow the navigation to proceed.
    devtools_client_->GetPage()->GetExperimental()->ProcessNavigation(
        headless::page::ProcessNavigationParams::Builder()
            .SetNavigationId(params.GetNavigationId())
            .SetResponse(headless::page::NavigationResponse::PROCEED)
            .Build());
  }

  void OnFrameStoppedLoading(
      const page::FrameStoppedLoadingParams& params) override {
    EXPECT_TRUE(navigation_requested_);
    FinishAsynchronousTest();
  }

 private:
  bool navigation_requested_ = false;
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNavigationControlTest);

751 752
class HeadlessCrashObserverTest : public HeadlessAsyncDevTooledBrowserTest,
                                  inspector::ExperimentalObserver {
753 754
 public:
  void RunDevTooledTest() override {
755 756 757
    devtools_client_->GetInspector()->GetExperimental()->AddObserver(this);
    devtools_client_->GetInspector()->GetExperimental()->Enable(
        headless::inspector::EnableParams::Builder().Build());
758 759 760 761
    devtools_client_->GetPage()->Enable();
    devtools_client_->GetPage()->Navigate(content::kChromeUICrashURL);
  }

762 763 764 765 766 767
  void OnTargetCrashed(const inspector::TargetCrashedParams& params) override {
    FinishAsynchronousTest();
    render_process_exited_ = true;
  }

  // Make sure we don't fail because the renderer crashed!
768 769
  void RenderProcessExited(base::TerminationStatus status,
                           int exit_code) override {
dvallet's avatar
dvallet committed
770 771 772
#if defined(OS_WIN) || defined(OS_MACOSX)
    EXPECT_EQ(base::TERMINATION_STATUS_PROCESS_CRASHED, status);
#else
773
    EXPECT_EQ(base::TERMINATION_STATUS_ABNORMAL_TERMINATION, status);
dvallet's avatar
dvallet committed
774
#endif  // defined(OS_WIN) || defined(OS_MACOSX)
775 776 777 778 779
  }
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessCrashObserverTest);

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
class HeadlessDevToolsClientAttachTest
    : public HeadlessAsyncDevTooledBrowserTest {
 public:
  void RunDevTooledTest() override {
    other_devtools_client_ = HeadlessDevToolsClient::Create();
    HeadlessDevToolsTarget* devtools_target =
        web_contents_->GetDevToolsTarget();

    // Try attaching: there's already a client attached.
    EXPECT_FALSE(devtools_target->AttachClient(other_devtools_client_.get()));
    EXPECT_TRUE(devtools_target->IsAttached());
    // Detach the existing client, attach the other client.
    devtools_target->DetachClient(devtools_client_.get());
    EXPECT_FALSE(devtools_target->IsAttached());
    EXPECT_TRUE(devtools_target->AttachClient(other_devtools_client_.get()));
    EXPECT_TRUE(devtools_target->IsAttached());

    // Now, let's make sure this devtools client works.
    other_devtools_client_->GetRuntime()->Evaluate(
        "24 * 7", base::Bind(&HeadlessDevToolsClientAttachTest::OnFirstResult,
                             base::Unretained(this)));
  }

  void OnFirstResult(std::unique_ptr<runtime::EvaluateResult> result) {
    int value;
    EXPECT_TRUE(result->GetResult()->HasValue());
    EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
    EXPECT_EQ(24 * 7, value);

    HeadlessDevToolsTarget* devtools_target =
        web_contents_->GetDevToolsTarget();

    // Try attach, then force-attach the original client.
    EXPECT_FALSE(devtools_target->AttachClient(devtools_client_.get()));
    devtools_target->ForceAttachClient(devtools_client_.get());
    EXPECT_TRUE(devtools_target->IsAttached());

    devtools_client_->GetRuntime()->Evaluate(
        "27 * 4", base::Bind(&HeadlessDevToolsClientAttachTest::OnSecondResult,
                             base::Unretained(this)));
  }

  void OnSecondResult(std::unique_ptr<runtime::EvaluateResult> result) {
    int value;
    EXPECT_TRUE(result->GetResult()->HasValue());
    EXPECT_TRUE(result->GetResult()->GetValue()->GetAsInteger(&value));
    EXPECT_EQ(27 * 4, value);

    // If everything worked, this call will not crash, since it
    // detaches devtools_client_.
    FinishAsynchronousTest();
  }

 protected:
  std::unique_ptr<HeadlessDevToolsClient> other_devtools_client_;
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsClientAttachTest);

839 840 841 842 843 844
class HeadlessDevToolsMethodCallErrorTest
    : public HeadlessAsyncDevTooledBrowserTest,
      public page::Observer {
 public:
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());
845
    base::RunLoop run_loop;
846
    devtools_client_->GetPage()->AddObserver(this);
847 848 849 850
    devtools_client_->GetPage()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
    devtools_client_->GetPage()->Navigate(
        embedded_test_server()->GetURL("/hello.html").spec());
  }

  void OnLoadEventFired(const page::LoadEventFiredParams& params) override {
    devtools_client_->GetPage()->GetExperimental()->RemoveObserver(this);
    devtools_client_->GetDOM()->GetDocument(
        base::Bind(&HeadlessDevToolsMethodCallErrorTest::OnGetDocument,
                   base::Unretained(this)));
  }

  void OnGetDocument(std::unique_ptr<dom::GetDocumentResult> result) {
    devtools_client_->GetDOM()->QuerySelector(
        dom::QuerySelectorParams::Builder()
            .SetNodeId(result->GetRoot()->GetNodeId())
            .SetSelector("<o_O>")
            .Build(),
        base::Bind(&HeadlessDevToolsMethodCallErrorTest::OnQuerySelector,
                   base::Unretained(this)));
  }

  void OnQuerySelector(std::unique_ptr<dom::QuerySelectorResult> result) {
    EXPECT_EQ(nullptr, result);
    FinishAsynchronousTest();
  }
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsMethodCallErrorTest);

880 881 882 883 884 885 886
class HeadlessDevToolsNetworkBlockedUrlTest
    : public HeadlessAsyncDevTooledBrowserTest,
      public page::Observer,
      public network::Observer {
 public:
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());
887
    base::RunLoop run_loop;
888 889 890
    devtools_client_->GetPage()->AddObserver(this);
    devtools_client_->GetPage()->Enable();
    devtools_client_->GetNetwork()->AddObserver(this);
891 892 893 894
    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();
895 896 897 898
    std::vector<std::string> blockedUrls;
    blockedUrls.push_back("dom_tree_test.css");
    devtools_client_->GetNetwork()->GetExperimental()->SetBlockedURLs(
        network::SetBlockedURLsParams::Builder().SetUrls(blockedUrls).Build());
899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
    devtools_client_->GetPage()->Navigate(
        embedded_test_server()->GetURL("/dom_tree_test.html").spec());
  }

  std::string GetUrlPath(const std::string& url) const {
    GURL gurl(url);
    return gurl.path();
  }

  void OnRequestWillBeSent(
      const network::RequestWillBeSentParams& params) override {
    std::string path = GetUrlPath(params.GetRequest()->GetUrl());
    requests_to_be_sent_.push_back(path);
    request_id_to_path_[params.GetRequestId()] = path;
  }

  void OnResponseReceived(
      const network::ResponseReceivedParams& params) override {
    responses_received_.push_back(GetUrlPath(params.GetResponse()->GetUrl()));
  }

  void OnLoadingFailed(const network::LoadingFailedParams& failed) override {
    failures_.push_back(request_id_to_path_[failed.GetRequestId()]);
    EXPECT_EQ(network::BlockedReason::INSPECTOR, failed.GetBlockedReason());
  }

  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
    EXPECT_THAT(requests_to_be_sent_,
                ElementsAre("/dom_tree_test.html", "/dom_tree_test.css",
                            "/iframe.html"));
    EXPECT_THAT(responses_received_,
                ElementsAre("/dom_tree_test.html", "/iframe.html"));
    EXPECT_THAT(failures_, ElementsAre("/dom_tree_test.css"));
    FinishAsynchronousTest();
  }

  std::map<std::string, std::string> request_id_to_path_;
  std::vector<std::string> requests_to_be_sent_;
  std::vector<std::string> responses_received_;
  std::vector<std::string> failures_;
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(HeadlessDevToolsNetworkBlockedUrlTest);

943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
namespace {
// Keep in sync with X_DevTools_Emulate_Network_Conditions_Client_Id defined in
// HTTPNames.json5.
const char kDevToolsEmulateNetworkConditionsClientId[] =
    "X-DevTools-Emulate-Network-Conditions-Client-Id";
}  // namespace

class DevToolsHeaderStrippingTest : public HeadlessAsyncDevTooledBrowserTest,
                                    public page::Observer,
                                    public network::Observer {
  void RunDevTooledTest() override {
    EXPECT_TRUE(embedded_test_server()->Start());
    base::RunLoop run_loop;
    devtools_client_->GetPage()->AddObserver(this);
    devtools_client_->GetPage()->Enable();
    // Enable network domain in order to get DevTools to add the header.
    devtools_client_->GetNetwork()->AddObserver(this);
    devtools_client_->GetNetwork()->Enable(run_loop.QuitClosure());
    base::MessageLoop::ScopedNestableTaskAllower nest_loop(
        base::MessageLoop::current());
    run_loop.Run();
    devtools_client_->GetPage()->Navigate(
        "http://not-an-actual-domain.tld/hello.html");
  }

  ProtocolHandlerMap GetProtocolHandlers() override {
    const std::string kResponseBody = "<p>HTTP response body</p>";
    ProtocolHandlerMap protocol_handlers;
    protocol_handlers[url::kHttpScheme] =
        base::MakeUnique<TestProtocolHandler>(kResponseBody);
    test_handler_ = static_cast<TestProtocolHandler*>(
        protocol_handlers[url::kHttpScheme].get());
    return protocol_handlers;
  }

  void OnLoadEventFired(const page::LoadEventFiredParams&) override {
    EXPECT_FALSE(test_handler_->last_http_request_headers().IsEmpty());
    EXPECT_FALSE(test_handler_->last_http_request_headers().HasHeader(
        kDevToolsEmulateNetworkConditionsClientId));
    FinishAsynchronousTest();
  }

  TestProtocolHandler* test_handler_;  // NOT OWNED
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsHeaderStrippingTest);

990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
class RawDevtoolsProtocolTest
    : public HeadlessAsyncDevTooledBrowserTest,
      public HeadlessDevToolsClient::RawProtocolListener {
 public:
  void RunDevTooledTest() override {
    devtools_client_->SetRawProtocolListener(this);

    base::DictionaryValue message;
    message.SetInteger("id", devtools_client_->GetNextRawDevToolsMessageId());
    message.SetString("method", "Runtime.evaluate");
    std::unique_ptr<base::DictionaryValue> params(new base::DictionaryValue());
    params->SetString("expression", "1+1");
    message.Set("params", std::move(params));
    devtools_client_->SendRawDevToolsMessage(message);
  }

  bool OnProtocolMessage(const std::string& devtools_agent_host_id,
                         const std::string& json_message,
                         const base::DictionaryValue& parsed_message) override {
    EXPECT_EQ(
        "{\"id\":1,\"result\":{\"result\":{\"type\":\"number\","
        "\"value\":2,\"description\":\"2\"}}}",
        json_message);

    int frame_tree_node_id = 0;
    EXPECT_TRUE(web_contents_->GetFrameTreeNodeIdForDevToolsAgentHostId(
        devtools_agent_host_id, &frame_tree_node_id));
    EXPECT_NE(0, frame_tree_node_id);
    FinishAsynchronousTest();
    return true;
  }
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(RawDevtoolsProtocolTest);

1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
class DevToolsAttachAndDetachNotifications
    : public HeadlessAsyncDevTooledBrowserTest {
 public:
  void DevToolsClientAttached() override { dev_tools_client_attached_ = true; }

  void RunDevTooledTest() override {
    EXPECT_TRUE(dev_tools_client_attached_);
    FinishAsynchronousTest();
  }

  void DevToolsClientDetached() override { dev_tools_client_detached_ = true; }

  void TearDownOnMainThread() override {
    EXPECT_TRUE(dev_tools_client_detached_);
  }

 private:
  bool dev_tools_client_attached_ = false;
  bool dev_tools_client_detached_ = false;
};

HEADLESS_ASYNC_DEVTOOLED_TEST_F(DevToolsAttachAndDetachNotifications);

1048
}  // namespace headless