diff --git a/net/http/http_cache_lookup_manager.cc b/net/http/http_cache_lookup_manager.cc
index d4d8a2e..ab80006 100644
--- a/net/http/http_cache_lookup_manager.cc
+++ b/net/http/http_cache_lookup_manager.cc
@@ -5,65 +5,95 @@
 #include "net/http/http_cache_lookup_manager.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/values.h"
 #include "net/base/load_flags.h"
 
 namespace net {
 
+// Returns parameters associated with the start of a server push lookup
+// transaction.
+std::unique_ptr<base::Value> NetLogPushLookupTransactionCallback(
+    const NetLogSource& net_log,
+    const ServerPushDelegate::ServerPushHelper* push_helper,
+    NetLogCaptureMode /* capture_mode */) {
+  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
+  net_log.AddToEventParameters(dict.get());
+  dict->SetString("push_url", push_helper->GetURL().possibly_invalid_spec());
+  return std::move(dict);
+}
+
 HttpCacheLookupManager::LookupTransaction::LookupTransaction(
-    std::unique_ptr<ServerPushHelper> server_push_helper)
+    std::unique_ptr<ServerPushHelper> server_push_helper,
+    NetLog* net_log)
     : push_helper_(std::move(server_push_helper)),
       request_(new HttpRequestInfo()),
-      transaction_(nullptr) {}
+      transaction_(nullptr),
+      net_log_(NetLogWithSource::Make(
+          net_log,
+          NetLogSourceType::SERVER_PUSH_LOOKUP_TRANSACTION)) {}
 
 HttpCacheLookupManager::LookupTransaction::~LookupTransaction() {}
 
 int HttpCacheLookupManager::LookupTransaction::StartLookup(
     HttpCache* cache,
     const CompletionCallback& callback,
-    const NetLogWithSource& net_log) {
+    const NetLogWithSource& session_net_log) {
+  net_log_.BeginEvent(NetLogEventType::SERVER_PUSH_LOOKUP_TRANSACTION,
+                      base::Bind(&NetLogPushLookupTransactionCallback,
+                                 session_net_log.source(), push_helper_.get()));
+
   request_->url = push_helper_->GetURL();
   request_->method = "GET";
   request_->load_flags = LOAD_ONLY_FROM_CACHE | LOAD_SKIP_CACHE_VALIDATION;
   cache->CreateTransaction(DEFAULT_PRIORITY, &transaction_);
-  return transaction_->Start(request_.get(), callback, net_log);
+  return transaction_->Start(request_.get(), callback, net_log_);
 }
 
-void HttpCacheLookupManager::LookupTransaction::CancelPush() {
-  DCHECK(push_helper_.get());
-  push_helper_->Cancel();
+void HttpCacheLookupManager::LookupTransaction::OnLookupComplete(int result) {
+  if (result == OK) {
+    DCHECK(push_helper_.get());
+    push_helper_->Cancel();
+  }
+  net_log_.EndEventWithNetErrorCode(
+      NetLogEventType::SERVER_PUSH_LOOKUP_TRANSACTION, result);
 }
 
-HttpCacheLookupManager::HttpCacheLookupManager(HttpCache* http_cache,
-                                               const NetLogWithSource& net_log)
-    : net_log_(net_log), http_cache_(http_cache), weak_factory_(this) {}
+HttpCacheLookupManager::HttpCacheLookupManager(HttpCache* http_cache)
+    : http_cache_(http_cache), weak_factory_(this) {}
 
 HttpCacheLookupManager::~HttpCacheLookupManager() {}
 
 void HttpCacheLookupManager::OnPush(
-    std::unique_ptr<ServerPushHelper> push_helper) {
+    std::unique_ptr<ServerPushHelper> push_helper,
+    const NetLogWithSource& session_net_log) {
   GURL pushed_url = push_helper->GetURL();
 
   // There's a pending lookup transaction sent over already.
   if (base::ContainsKey(lookup_transactions_, pushed_url))
     return;
 
-  auto lookup = base::MakeUnique<LookupTransaction>(std::move(push_helper));
+  auto lookup = base::MakeUnique<LookupTransaction>(std::move(push_helper),
+                                                    session_net_log.net_log());
+  // TODO(zhongyi): add events in session net log to log the creation of
+  // LookupTransaction.
 
   int rv = lookup->StartLookup(
       http_cache_, base::Bind(&HttpCacheLookupManager::OnLookupComplete,
                               weak_factory_.GetWeakPtr(), pushed_url),
-      net_log_);
+      session_net_log);
 
-  if (rv == ERR_IO_PENDING)
+  if (rv == ERR_IO_PENDING) {
     lookup_transactions_[pushed_url] = std::move(lookup);
+  } else {
+    lookup->OnLookupComplete(rv);
+  }
 }
 
 void HttpCacheLookupManager::OnLookupComplete(const GURL& url, int rv) {
   auto it = lookup_transactions_.find(url);
   DCHECK(it != lookup_transactions_.end());
 
-  if (rv == OK)
-    it->second->CancelPush();
+  it->second->OnLookupComplete(rv);
 
   lookup_transactions_.erase(it);
 }
diff --git a/net/http/http_cache_lookup_manager.h b/net/http/http_cache_lookup_manager.h
index 45cfa00..8a651bd 100644
--- a/net/http/http_cache_lookup_manager.h
+++ b/net/http/http_cache_lookup_manager.h
@@ -18,11 +18,12 @@
 class NET_EXPORT_PRIVATE HttpCacheLookupManager : public ServerPushDelegate {
  public:
   // |http_cache| MUST outlive the HttpCacheLookupManager.
-  HttpCacheLookupManager(HttpCache* http_cache,
-                         const NetLogWithSource& net_log);
+  explicit HttpCacheLookupManager(HttpCache* http_cache);
   ~HttpCacheLookupManager() override;
 
-  void OnPush(std::unique_ptr<ServerPushHelper> push_helper) override;
+  // ServerPushDelegate implementation.
+  void OnPush(std::unique_ptr<ServerPushHelper> push_helper,
+              const NetLogWithSource& session_net_log) override;
 
   // Invoked when the HttpCache::Transaction for |url| finishes to cancel the
   // server push if the response to the server push is found cached.
@@ -34,24 +35,25 @@
   // push.
   class LookupTransaction {
    public:
-    LookupTransaction(std::unique_ptr<ServerPushHelper> push_helper);
+    LookupTransaction(std::unique_ptr<ServerPushHelper> push_helper,
+                      NetLog* net_log);
     ~LookupTransaction();
 
     // Issues an HttpCache::Transaction to lookup whether the response is cached
     // without header validation.
     int StartLookup(HttpCache* cache,
                     const CompletionCallback& callback,
-                    const NetLogWithSource& net_log);
+                    const NetLogWithSource& session_net_log);
 
-    void CancelPush();
+    void OnLookupComplete(int result);
 
    private:
     std::unique_ptr<ServerPushHelper> push_helper_;
     std::unique_ptr<HttpRequestInfo> request_;
     std::unique_ptr<HttpTransaction> transaction_;
+    const NetLogWithSource net_log_;
   };
 
-  NetLogWithSource net_log_;
   // HttpCache must outlive the HttpCacheLookupManager.
   HttpCache* http_cache_;
   std::map<GURL, std::unique_ptr<LookupTransaction>> lookup_transactions_;
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index 439aee7..2ceebeb6 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -25,7 +25,7 @@
  public:
   explicit MockServerPushHelper(const GURL& url) : request_url_(url) {}
 
-  const GURL& GetURL() override { return request_url_; }
+  const GURL& GetURL() const override { return request_url_; }
 
   MOCK_METHOD0(Cancel, void());
 
@@ -84,8 +84,7 @@
 
 TEST(HttpCacheLookupManagerTest, ServerPushMissCache) {
   MockHttpCache mock_cache;
-  HttpCacheLookupManager push_delegate(mock_cache.http_cache(),
-                                       NetLogWithSource());
+  HttpCacheLookupManager push_delegate(mock_cache.http_cache());
   GURL request_url("http://www.example.com/pushed.jpg");
 
   std::unique_ptr<MockServerPushHelper> push_helper =
@@ -94,7 +93,7 @@
 
   // Receive a server push and should not cancel the push.
   EXPECT_CALL(*push_helper_ptr, Cancel()).Times(0);
-  push_delegate.OnPush(std::move(push_helper));
+  push_delegate.OnPush(std::move(push_helper), NetLogWithSource());
   base::RunLoop().RunUntilIdle();
 
   // Make sure no network transaction is created.
@@ -105,8 +104,7 @@
 
 TEST(HttpCacheLookupManagerTest, ServerPushDoNotCreateCacheEntry) {
   MockHttpCache mock_cache;
-  HttpCacheLookupManager push_delegate(mock_cache.http_cache(),
-                                       NetLogWithSource());
+  HttpCacheLookupManager push_delegate(mock_cache.http_cache());
   GURL request_url("http://www.example.com/pushed.jpg");
 
   std::unique_ptr<MockServerPushHelper> push_helper =
@@ -115,7 +113,7 @@
 
   // Receive a server push and should not cancel the push.
   EXPECT_CALL(*push_helper_ptr, Cancel()).Times(0);
-  push_delegate.OnPush(std::move(push_helper));
+  push_delegate.OnPush(std::move(push_helper), NetLogWithSource());
   base::RunLoop().RunUntilIdle();
 
   // Receive another server push for the same url.
@@ -123,7 +121,7 @@
       base::MakeUnique<MockServerPushHelper>(request_url);
   MockServerPushHelper* push_helper_ptr2 = push_helper2.get();
   EXPECT_CALL(*push_helper_ptr2, Cancel()).Times(0);
-  push_delegate.OnPush(std::move(push_helper2));
+  push_delegate.OnPush(std::move(push_helper2), NetLogWithSource());
   base::RunLoop().RunUntilIdle();
 
   // Verify no network transaction is created.
@@ -135,8 +133,7 @@
 
 TEST(HttpCacheLookupManagerTest, ServerPushHitCache) {
   MockHttpCache mock_cache;
-  HttpCacheLookupManager push_delegate(mock_cache.http_cache(),
-                                       NetLogWithSource());
+  HttpCacheLookupManager push_delegate(mock_cache.http_cache());
   GURL request_url("http://www.example.com/pushed.jpg");
 
   // Populate the cache entry so that the cache lookup for server push hits.
@@ -157,7 +154,7 @@
 
   // Receive a server push and should cancel the push.
   EXPECT_CALL(*push_helper_ptr, Cancel()).Times(1);
-  push_delegate.OnPush(std::move(push_helper));
+  push_delegate.OnPush(std::move(push_helper), NetLogWithSource());
   base::RunLoop().RunUntilIdle();
 
   // Make sure no new net layer transaction is created.
@@ -173,8 +170,7 @@
 // send a new lookup transaction and should not be canceled.
 TEST(HttpCacheLookupManagerTest, ServerPushPendingLookup) {
   MockHttpCache mock_cache;
-  HttpCacheLookupManager push_delegate(mock_cache.http_cache(),
-                                       NetLogWithSource());
+  HttpCacheLookupManager push_delegate(mock_cache.http_cache());
   GURL request_url("http://www.example.com/pushed.jpg");
 
   // Populate the cache entry so that the cache lookup for server push hits.
@@ -195,7 +191,7 @@
 
   // Receive a server push and should cancel the push eventually.
   EXPECT_CALL(*push_helper_ptr, Cancel()).Times(1);
-  push_delegate.OnPush(std::move(push_helper));
+  push_delegate.OnPush(std::move(push_helper), NetLogWithSource());
 
   std::unique_ptr<MockServerPushHelper> push_helper2 =
       base::MakeUnique<MockServerPushHelper>(request_url);
@@ -203,7 +199,7 @@
 
   // Receive another server push and should not cancel the push.
   EXPECT_CALL(*push_helper_ptr2, Cancel()).Times(0);
-  push_delegate.OnPush(std::move(push_helper2));
+  push_delegate.OnPush(std::move(push_helper2), NetLogWithSource());
 
   base::RunLoop().RunUntilIdle();
 
@@ -218,8 +214,7 @@
 // Test the server push lookup is based on the full url.
 TEST(HttpCacheLookupManagerTest, ServerPushLookupOnUrl) {
   MockHttpCache mock_cache;
-  HttpCacheLookupManager push_delegate(mock_cache.http_cache(),
-                                       NetLogWithSource());
+  HttpCacheLookupManager push_delegate(mock_cache.http_cache());
   GURL request_url("http://www.example.com/pushed.jpg?u=0");
   GURL request_url2("http://www.example.com/pushed.jpg?u=1");
 
@@ -241,7 +236,7 @@
 
   // Receive a server push and should cancel the push eventually.
   EXPECT_CALL(*push_helper_ptr, Cancel()).Times(1);
-  push_delegate.OnPush(std::move(push_helper));
+  push_delegate.OnPush(std::move(push_helper), NetLogWithSource());
   // Run until the lookup transaction finishes for the first server push.
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_cache.network_layer()->transaction_count());
@@ -257,7 +252,7 @@
   MockServerPushHelper* push_helper_ptr2 = push_helper2.get();
 
   EXPECT_CALL(*push_helper_ptr2, Cancel()).Times(1);
-  push_delegate.OnPush(std::move(push_helper2));
+  push_delegate.OnPush(std::move(push_helper2), NetLogWithSource());
   // Run until the lookup transaction finishes for the second server push.
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, mock_cache.network_layer()->transaction_count());
@@ -275,7 +270,7 @@
   MockServerPushHelper* push_helper_ptr3 = push_helper3.get();
 
   EXPECT_CALL(*push_helper_ptr3, Cancel()).Times(0);
-  push_delegate.OnPush(std::move(push_helper3));
+  push_delegate.OnPush(std::move(push_helper3), NetLogWithSource());
 
   base::RunLoop().RunUntilIdle();
   // Make sure no new net layer transaction is created.
diff --git a/net/log/net_log_event_type_list.h b/net/log/net_log_event_type_list.h
index 893f16e0..5e0fd2d 100644
--- a/net/log/net_log_event_type_list.h
+++ b/net/log/net_log_event_type_list.h
@@ -1303,6 +1303,26 @@
 EVENT_TYPE(BIDIRECTIONAL_STREAM_FAILED)
 
 // ------------------------------------------------------------------------
+// SERVER_PUSH_LOOKUP_TRANSACTION
+// ------------------------------------------------------------------------
+
+// The start/end of a push lookup transaction for server push.
+//
+// The START event has the parameters:
+//   {
+//     "source_dependency": <Source identifier for the server push lookp.
+//                           It can be a QUIC_SESSION or a HTTP2_SESSION>,
+//     "pushed_url": <The url that has been pushed and looked up>,
+//   }
+//
+// If the transaction doesn't find the resource in cache, then the END phase
+// has these parameters:
+//   {
+//     "net_error": <Net error code integer>,
+//   }
+EVENT_TYPE(SERVER_PUSH_LOOKUP_TRANSACTION)
+
+// ------------------------------------------------------------------------
 // SpdySession
 // ------------------------------------------------------------------------
 
diff --git a/net/log/net_log_source_type_list.h b/net/log/net_log_source_type_list.h
index 878cda9..120245d 100644
--- a/net/log/net_log_source_type_list.h
+++ b/net/log/net_log_source_type_list.h
@@ -39,3 +39,4 @@
 SOURCE_TYPE(NETWORK_QUALITY_ESTIMATOR)
 SOURCE_TYPE(HTTP_STREAM_JOB_CONTROLLER)
 SOURCE_TYPE(CT_TREE_STATE_TRACKER)
+SOURCE_TYPE(SERVER_PUSH_LOOKUP_TRANSACTION)
diff --git a/net/quic/chromium/quic_chromium_client_session.cc b/net/quic/chromium/quic_chromium_client_session.cc
index 26989fe..e3f4cfe6 100644
--- a/net/quic/chromium/quic_chromium_client_session.cc
+++ b/net/quic/chromium/quic_chromium_client_session.cc
@@ -177,7 +177,7 @@
     }
   }
 
-  const GURL& GetURL() override { return request_url_; }
+  const GURL& GetURL() const override { return request_url_; }
 
  private:
   base::WeakPtr<QuicChromiumClientSession> session_;
@@ -1466,7 +1466,8 @@
     GURL pushed_url = GetUrlFromHeaderBlock(headers);
     if (push_delegate_) {
       push_delegate_->OnPush(base::MakeUnique<QuicServerPushHelper>(
-          weak_factory_.GetWeakPtr(), pushed_url));
+                                 weak_factory_.GetWeakPtr(), pushed_url),
+                             net_log_);
     }
   }
   net_log_.AddEvent(NetLogEventType::QUIC_SESSION_PUSH_PROMISE_RECEIVED,
diff --git a/net/spdy/server_push_delegate.h b/net/spdy/server_push_delegate.h
index 7e9c3db8..719ca8e 100644
--- a/net/spdy/server_push_delegate.h
+++ b/net/spdy/server_push_delegate.h
@@ -6,6 +6,7 @@
 #define NET_SPDY_SERVER_PUSH_DELEGATE_H_
 
 #include "net/base/net_export.h"
+#include "net/log/net_log_with_source.h"
 #include "url/gurl.h"
 
 namespace net {
@@ -23,13 +24,14 @@
     virtual void Cancel() = 0;
 
     // Gets the URL of the pushed request.
-    virtual const GURL& GetURL() = 0;
+    virtual const GURL& GetURL() const = 0;
   };
 
   virtual ~ServerPushDelegate() {}
 
   // Invoked by session when a push promise has been received.
-  virtual void OnPush(std::unique_ptr<ServerPushHelper> push_helper) = 0;
+  virtual void OnPush(std::unique_ptr<ServerPushHelper> push_helper,
+                      const NetLogWithSource& session_net_log) = 0;
 };
 
 }  // namespace net
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index aa8397a..4310226 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -381,7 +381,7 @@
       session_->CancelPush(request_url_);
   }
 
-  const GURL& GetURL() override { return request_url_; }
+  const GURL& GetURL() const override { return request_url_; }
 
  private:
   base::WeakPtr<SpdySession> session_;
@@ -1691,7 +1691,8 @@
   // Notify the push_delegate that a push promise has been received.
   if (push_delegate_) {
     push_delegate_->OnPush(base::MakeUnique<SpdyServerPushHelper>(
-        weak_factory_.GetWeakPtr(), gurl));
+                               weak_factory_.GetWeakPtr(), gurl),
+                           net_log_);
   }
 
   active_it->second->OnPushPromiseHeadersReceived(std::move(headers));
diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc
index 16d39db..6814025 100644
--- a/net/spdy/spdy_test_utils.cc
+++ b/net/spdy/spdy_test_utils.cc
@@ -168,7 +168,8 @@
 TestServerPushDelegate::~TestServerPushDelegate() {}
 
 void TestServerPushDelegate::OnPush(
-    std::unique_ptr<ServerPushHelper> push_helper) {
+    std::unique_ptr<ServerPushHelper> push_helper,
+    const NetLogWithSource& session_net_log) {
   push_helpers[push_helper->GetURL()] = std::move(push_helper);
 }
 
diff --git a/net/spdy/spdy_test_utils.h b/net/spdy/spdy_test_utils.h
index eb27d66f..6737381c 100644
--- a/net/spdy/spdy_test_utils.h
+++ b/net/spdy/spdy_test_utils.h
@@ -93,7 +93,8 @@
   explicit TestServerPushDelegate();
   ~TestServerPushDelegate() override;
 
-  void OnPush(std::unique_ptr<ServerPushHelper> push_helper) override;
+  void OnPush(std::unique_ptr<ServerPushHelper> push_helper,
+              const NetLogWithSource& session_net_log) override;
 
   bool CancelPush(GURL url);
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-label-element/label-attributes-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-label-element/label-attributes-expected.txt
deleted file mode 100644
index a3745238..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/semantics/forms/the-label-element/label-attributes-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a testharness.js-based test.
-PASS A label element with a 'for' attribute should only be associated with a labelable element. 
-FAIL A label element not in a document can not label any element in the document. assert_not_equals: A label element not in a document should not label an element in a document. got disallowed value Element node <input id="test1"></input>
-PASS The labeled control for a label element that has no 'for' attribute is the first labelable element which is a descendant of that label element. 
-PASS The 'for' attribute points to an inexistent id. 
-PASS A non-control follows by a control with same ID. 
-PASS The 'for' attribute is an empty string. 
-PASS A form control has multiple labels. 
-PASS A form control has an implicit label. 
-PASS A form control has no label 1. 
-PASS A form control has no label 2. 
-PASS A label in a form without a control 
-PASS A label outside a form with a control inside the form 
-PASS A label's htmlFor attribute must reflect the for content attribute 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
index 191e9c1..546d8a5c 100644
--- a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
@@ -69,6 +69,9 @@
     return nullptr;
   }
 
+  if (!isInTreeScope())
+    return nullptr;
+
   if (Element* element = treeScope().getElementById(controlId)) {
     if (isLabelableElement(*element) &&
         toLabelableElement(*element).supportLabels()) {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
index 124b4fe..dfbad21 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -179,8 +179,9 @@
 
 RefPtr<NGPhysicalTextFragment> NGFragmentBuilder::ToTextFragment(
     NGInlineNode* node,
-    unsigned start_index,
-    unsigned end_index) {
+    unsigned index,
+    unsigned start_offset,
+    unsigned end_offset) {
   DCHECK_EQ(type_, NGPhysicalFragment::kFragmentText);
   DCHECK(children_.isEmpty());
   DCHECK(offsets_.isEmpty());
@@ -189,7 +190,7 @@
   Vector<Persistent<NGFloatingObject>> empty_positioned_floats;
 
   return adoptRef(new NGPhysicalTextFragment(
-      layout_object_, node, start_index, end_index,
+      layout_object_, node, index, start_offset, end_offset,
       size_.ConvertToPhysical(writing_mode_),
       overflow_.ConvertToPhysical(writing_mode_), out_of_flow_descendants_,
       out_of_flow_positions_, empty_unpositioned_floats,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
index 7d0b7b1..5bffa4f 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -92,8 +92,9 @@
   // Creates the fragment. Can only be called once.
   RefPtr<NGPhysicalBoxFragment> ToBoxFragment();
   RefPtr<NGPhysicalTextFragment> ToTextFragment(NGInlineNode*,
-                                                unsigned start_index,
-                                                unsigned end_index);
+                                                unsigned index,
+                                                unsigned start_offset,
+                                                unsigned end_offset);
 
   // Mutable list of floats that need to be positioned.
   Vector<Persistent<NGFloatingObject>>& MutableUnpositionedFloats() {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
index 85bf0aa..406a740 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.cc
@@ -11,15 +11,17 @@
 #include "core/layout/ng/ng_box_fragment.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
 #include "core/layout/ng/ng_fragment_builder.h"
-#include "core/layout/ng/ng_line_builder.h"
 #include "core/layout/ng/ng_layout_inline_items_builder.h"
+#include "core/layout/ng/ng_line_builder.h"
 #include "core/layout/ng/ng_physical_box_fragment.h"
 #include "core/layout/ng/ng_physical_text_fragment.h"
 #include "core/layout/ng/ng_text_fragment.h"
 #include "core/layout/ng/ng_text_layout_algorithm.h"
 #include "core/style/ComputedStyle.h"
+#include "platform/fonts/CharacterRange.h"
 #include "platform/fonts/shaping/CachingWordShapeIterator.h"
 #include "platform/fonts/shaping/CachingWordShaper.h"
+#include "platform/fonts/shaping/ShapeResultBuffer.h"
 #include "wtf/text/CharacterNames.h"
 
 namespace blink {
@@ -188,10 +190,32 @@
 }
 
 LayoutUnit NGLayoutInlineItem::InlineSize() const {
-  LayoutUnit inline_size;
+  return InlineSize(start_offset_, end_offset_);
+}
+
+LayoutUnit NGLayoutInlineItem::InlineSize(unsigned start, unsigned end) const {
+  DCHECK(start >= StartOffset() && start <= end && end <= EndOffset());
+
+  if (start == end)
+    return LayoutUnit();
+
+  if (!style_) {
+    // Bidi controls do not have widths.
+    // TODO(kojii): Atomic inline not supported yet.
+    return LayoutUnit();
+  }
+
+  float total_width = 0;
   for (const auto& result : shape_results_)
-    inline_size += result->width();
-  return inline_size;
+    total_width += result->width();
+
+  if (start == start_offset_ && end == end_offset_)
+    return LayoutUnit(total_width);
+
+  return LayoutUnit(ShapeResultBuffer::getCharacterRange(
+                        shape_results_, Direction(), total_width,
+                        start - StartOffset(), end - StartOffset())
+                        .width());
 }
 
 void NGInlineNode::ShapeText() {
@@ -207,11 +231,12 @@
     ShapeCache* shape_cache = item_font.shapeCache();
 
     TextRun item_run(item_text);
+    item_run.setDirection(item.Direction());
     CachingWordShapeIterator iterator(shape_cache, item_run, &item_font);
     RefPtr<const ShapeResult> word_result;
     while (iterator.next(&word_result)) {
-      item.shape_results_.push_back(word_result.get());
-    };
+      item.shape_results_.push_back(std::move(word_result));
+    }
   }
 }
 
@@ -224,6 +249,9 @@
                                 NGLineBuilder* line_builder) {
   PrepareLayout();
 
+  if (text_content_.isEmpty())
+    return;
+
   NGTextLayoutAlgorithm(this, constraint_space).LayoutInline(line_builder);
 }
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
index 4ac2468f..85914af 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node.h
@@ -12,6 +12,7 @@
 #include "platform/heap/Handle.h"
 #include "platform/text/TextDirection.h"
 #include "wtf/text/WTFString.h"
+
 #include <unicode/ubidi.h>
 #include <unicode/uscript.h>
 
@@ -43,6 +44,7 @@
   // calling the Layout method.
   void PrepareLayout();
 
+  const String& Text() const { return text_content_; }
   String Text(unsigned start_offset, unsigned end_offset) const {
     return text_content_.substring(start_offset, end_offset);
   }
@@ -55,6 +57,9 @@
 
   bool IsBidiEnabled() const { return is_bidi_enabled_; }
 
+  void AssertOffset(unsigned index, unsigned offset) const;
+  void AssertEndOffset(unsigned index, unsigned offset) const;
+
   DECLARE_VIRTUAL_TRACE();
 
  protected:
@@ -117,6 +122,7 @@
   void SetEndOffset(unsigned);
 
   LayoutUnit InlineSize() const;
+  LayoutUnit InlineSize(unsigned start, unsigned end) const;
 
   static void Split(Vector<NGLayoutInlineItem>&,
                     unsigned index,
@@ -126,6 +132,9 @@
                                unsigned end_offset,
                                UBiDiLevel);
 
+  void AssertOffset(unsigned offset) const;
+  void AssertEndOffset(unsigned offset) const;
+
  private:
   unsigned start_offset_;
   unsigned end_offset_;
@@ -134,12 +143,29 @@
   FontFallbackPriority fallback_priority_;
   bool rotate_sideways_;
   const ComputedStyle* style_;
-  Vector<RefPtr<const ShapeResult>> shape_results_;
+  Vector<RefPtr<const ShapeResult>, 64> shape_results_;
   LayoutObject* layout_object_;
 
   friend class NGInlineNode;
 };
 
+inline void NGLayoutInlineItem::AssertOffset(unsigned offset) const {
+  DCHECK(offset >= start_offset_ && offset < end_offset_);
+}
+
+inline void NGLayoutInlineItem::AssertEndOffset(unsigned offset) const {
+  DCHECK(offset >= start_offset_ && offset <= end_offset_);
+}
+
+inline void NGInlineNode::AssertOffset(unsigned index, unsigned offset) const {
+  items_[index].AssertOffset(offset);
+}
+
+inline void NGInlineNode::AssertEndOffset(unsigned index,
+                                          unsigned offset) const {
+  items_[index].AssertEndOffset(offset);
+}
+
 DEFINE_TYPE_CASTS(NGInlineNode,
                   NGLayoutInputNode,
                   node,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
index e744d94..9ff5381 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_inline_node_test.cc
@@ -178,22 +178,24 @@
   TEST_ITEM_OFFSET_DIR(items[8], 22u, 28u, TextDirection::kLtr);
 }
 
-#define TEST_TEXT_FRAGMENT(fragment, node, start_index, end_index, dir) \
-  EXPECT_EQ(node, fragment->Node());                                    \
-  EXPECT_EQ(start_index, fragment->StartIndex());                       \
-  EXPECT_EQ(end_index, fragment->EndIndex());                           \
-  EXPECT_EQ(dir, node->Items()[fragment->StartIndex()].Direction())
+#define TEST_TEXT_FRAGMENT(fragment, node, index, start_offset, end_offset, \
+                           dir)                                             \
+  EXPECT_EQ(node, fragment->Node());                                        \
+  EXPECT_EQ(index, fragment->ItemIndex());                                  \
+  EXPECT_EQ(start_offset, fragment->StartOffset());                         \
+  EXPECT_EQ(end_offset, fragment->EndOffset());                             \
+  EXPECT_EQ(dir, node->Items()[fragment->ItemIndex()].Direction())
 
 TEST_F(NGInlineNodeTest, CreateLineBidiIsolate) {
   NGInlineNodeForTest* node = CreateBidiIsolateNode(style_.get());
   Vector<RefPtr<const NGPhysicalTextFragment>> fragments;
   CreateLine(node, &fragments);
   ASSERT_EQ(5u, fragments.size());
-  TEST_TEXT_FRAGMENT(fragments[0], node, 0u, 1u, TextDirection::kLtr);
-  TEST_TEXT_FRAGMENT(fragments[1], node, 6u, 7u, TextDirection::kRtl);
-  TEST_TEXT_FRAGMENT(fragments[2], node, 4u, 5u, TextDirection::kLtr);
-  TEST_TEXT_FRAGMENT(fragments[3], node, 2u, 3u, TextDirection::kRtl);
-  TEST_TEXT_FRAGMENT(fragments[4], node, 8u, 9u, TextDirection::kLtr);
+  TEST_TEXT_FRAGMENT(fragments[0], node, 0u, 0u, 6u, TextDirection::kLtr);
+  TEST_TEXT_FRAGMENT(fragments[1], node, 6u, 16u, 21u, TextDirection::kRtl);
+  TEST_TEXT_FRAGMENT(fragments[2], node, 4u, 14u, 15u, TextDirection::kLtr);
+  TEST_TEXT_FRAGMENT(fragments[3], node, 2u, 7u, 13u, TextDirection::kRtl);
+  TEST_TEXT_FRAGMENT(fragments[4], node, 8u, 22u, 28u, TextDirection::kLtr);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
index 8fd02813..d59bbb2 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.cc
@@ -12,6 +12,7 @@
 #include "core/layout/ng/ng_constraint_space.h"
 #include "core/layout/ng/ng_fragment_builder.h"
 #include "core/layout/ng/ng_inline_node.h"
+#include "core/layout/ng/ng_length_utils.h"
 #include "core/layout/ng/ng_text_fragment.h"
 #include "core/layout/ng/ng_units.h"
 #include "core/style/ComputedStyle.h"
@@ -30,32 +31,118 @@
 {
 }
 
-void NGLineBuilder::Add(unsigned start_index,
-                        unsigned end_index,
-                        LayoutUnit inline_size) {
-  DCHECK_LT(start_index, end_index);
-  line_item_chunks_.push_back(
-      LineItemChunk{start_index, end_index, inline_size});
+bool NGLineBuilder::CanFitOnLine() const {
+  LayoutUnit available_size = constraint_space_->AvailableSize().inline_size;
+  if (available_size == NGSizeIndefinite)
+    return true;
+  return end_position_ <= available_size;
+}
+
+bool NGLineBuilder::HasItems() const {
+  return start_offset_ != end_offset_;
+}
+
+bool NGLineBuilder::HasBreakOpportunity() const {
+  return start_offset_ != last_break_opportunity_offset_;
+}
+
+bool NGLineBuilder::HasItemsAfterLastBreakOpportunity() const {
+  return last_break_opportunity_offset_ != end_offset_;
+}
+
+void NGLineBuilder::SetStart(unsigned index, unsigned offset) {
+  inline_box_->AssertOffset(index, offset);
+
+  start_index_ = last_index_ = last_break_opportunity_index_ = index;
+  start_offset_ = end_offset_ = last_break_opportunity_offset_ = offset;
+  end_position_ = last_break_opportunity_position_ = LayoutUnit();
+}
+
+void NGLineBuilder::SetEnd(unsigned end_offset) {
+  const Vector<NGLayoutInlineItem>& items = inline_box_->Items();
+  DCHECK(end_offset > end_offset_ && end_offset <= items.back().EndOffset());
+
+  // Find the item index for |end_offset|, while accumulating inline-size.
+  unsigned last_index = last_index_;
+  const NGLayoutInlineItem* item = &items[last_index];
+  LayoutUnit inline_size_since_current_end;
+  if (end_offset <= item->EndOffset()) {
+    inline_size_since_current_end = item->InlineSize(end_offset_, end_offset);
+  } else {
+    inline_size_since_current_end =
+        item->InlineSize(end_offset_, item->EndOffset());
+    item = &items[++last_index];
+    for (; end_offset > item->EndOffset(); item = &items[++last_index])
+      inline_size_since_current_end += item->InlineSize();
+    inline_size_since_current_end +=
+        item->InlineSize(item->StartOffset(), end_offset);
+  }
+
+  SetEnd(last_index, end_offset, inline_size_since_current_end);
+}
+
+void NGLineBuilder::SetEnd(unsigned last_index,
+                           unsigned end_offset,
+                           LayoutUnit inline_size_since_current_end) {
+  inline_box_->AssertEndOffset(last_index, end_offset);
+  DCHECK_GE(last_index, last_index_);
+  DCHECK_GT(end_offset, end_offset_);
+
+  end_position_ += inline_size_since_current_end;
+  last_index_ = last_index;
+  end_offset_ = end_offset;
+}
+
+void NGLineBuilder::SetBreakOpportunity() {
+  last_break_opportunity_index_ = last_index_;
+  last_break_opportunity_offset_ = end_offset_;
+  last_break_opportunity_position_ = end_position_;
+}
+
+void NGLineBuilder::SetStartOfHangables(unsigned offset) {
+  // TODO(kojii): Implement.
 }
 
 void NGLineBuilder::CreateLine() {
+  if (HasItemsAfterLastBreakOpportunity())
+    SetBreakOpportunity();
+  CreateLineUpToLastBreakOpportunity();
+}
+
+void NGLineBuilder::CreateLineUpToLastBreakOpportunity() {
+  const Vector<NGLayoutInlineItem>& items = inline_box_->Items();
+
+  // Create a list of LineItemChunk from |start| and |last_break_opportunity|.
+  // TODO(kojii): Consider refactoring LineItemChunk once NGLineBuilder's public
+  // API is more finalized. It does not fit well with the current API.
+  Vector<LineItemChunk, 32> line_item_chunks;
+  unsigned start_offset = start_offset_;
+  for (unsigned i = start_index_; i <= last_break_opportunity_index_; i++) {
+    const NGLayoutInlineItem& item = items[i];
+    unsigned end_offset =
+        std::min(item.EndOffset(), last_break_opportunity_offset_);
+    line_item_chunks.push_back(
+        LineItemChunk{i, start_offset, end_offset,
+                      item.InlineSize(start_offset, end_offset)});
+    start_offset = end_offset;
+  }
+
   if (inline_box_->IsBidiEnabled())
-    BidiReorder();
+    BidiReorder(&line_item_chunks);
 
   NGFragmentBuilder text_builder(NGPhysicalFragment::kFragmentText,
                                  inline_box_->GetLayoutObject());
   text_builder.SetWritingMode(constraint_space_->WritingMode());
   LayoutUnit inline_offset;
-  const Vector<NGLayoutInlineItem>& items = inline_box_->Items();
-  for (const auto& line_item_chunk : line_item_chunks_) {
-    const NGLayoutInlineItem& start_item = items[line_item_chunk.start_index];
+  for (const auto& line_item_chunk : line_item_chunks) {
+    const NGLayoutInlineItem& item = items[line_item_chunk.index];
     // Skip bidi controls.
-    if (!start_item.GetLayoutObject())
+    if (!item.GetLayoutObject())
       continue;
-    const ComputedStyle* style = start_item.Style();
+    const ComputedStyle* style = item.Style();
     // TODO(kojii): Handling atomic inline needs more thoughts.
     if (!style)
-      style = start_item.GetLayoutObject()->style();
+      style = item.GetLayoutObject()->style();
 
     // TODO(kojii): The block size for a text fragment isn't clear, revisit when
     // we implement line box layout.
@@ -67,7 +154,8 @@
     TextDirection css_direction = style->direction();
     text_builder.SetDirection(css_direction);
     RefPtr<NGPhysicalTextFragment> text_fragment = text_builder.ToTextFragment(
-        inline_box_, line_item_chunk.start_index, line_item_chunk.end_index);
+        inline_box_, line_item_chunk.index, line_item_chunk.start_offset,
+        line_item_chunk.end_offset);
 
     fragments_.push_back(std::move(text_fragment));
     offsets_.push_back(NGLogicalOffset(inline_offset, content_size_));
@@ -83,16 +171,23 @@
 
     max_inline_size_ = std::max(max_inline_size_, inline_offset);
     // TODO(kojii): Implement block size when we support baseline alignment.
-    content_size_ += LayoutUnit(100);
+    content_size_ += LayoutUnit(20);
   }
 
-  line_item_chunks_.clear();
+  // Prepare for the next line.
+  // Move |start| to |last_break_opportunity|, keeping items after
+  // |last_break_opportunity|.
+  start_index_ = last_break_opportunity_index_;
+  start_offset_ = last_break_opportunity_offset_;
+  DCHECK_GE(end_position_, last_break_opportunity_position_);
+  end_position_ -= last_break_opportunity_position_;
+  last_break_opportunity_position_ = LayoutUnit();
 #if DCHECK_IS_ON()
   is_bidi_reordered_ = false;
 #endif
 }
 
-void NGLineBuilder::BidiReorder() {
+void NGLineBuilder::BidiReorder(Vector<LineItemChunk, 32>* line_item_chunks) {
 #if DCHECK_IS_ON()
   DCHECK(!is_bidi_reordered_);
   is_bidi_reordered_ = true;
@@ -108,26 +203,26 @@
   // handle the direction of each run, we use |ubidi_reorderVisual()| to reorder
   // runs instead of characters.
   Vector<UBiDiLevel, 32> levels;
-  levels.reserveInitialCapacity(line_item_chunks_.size());
-  for (const auto& chunk : line_item_chunks_)
-    levels.push_back(inline_box_->Items()[chunk.start_index].BidiLevel());
-  Vector<int32_t, 32> indicies_in_visual_order(line_item_chunks_.size());
+  levels.reserveInitialCapacity(line_item_chunks->size());
+  for (const auto& chunk : *line_item_chunks)
+    levels.push_back(inline_box_->Items()[chunk.index].BidiLevel());
+  Vector<int32_t, 32> indicies_in_visual_order(line_item_chunks->size());
   NGBidiParagraph::IndiciesInVisualOrder(levels, &indicies_in_visual_order);
 
-  // Reorder |line_item_chunks_| in visual order.
+  // Reorder |line_item_chunks| in visual order.
   Vector<LineItemChunk, 32> line_item_chunks_in_visual_order(
-      line_item_chunks_.size());
+      line_item_chunks->size());
   for (unsigned visual_index = 0;
        visual_index < indicies_in_visual_order.size(); visual_index++) {
     unsigned logical_index = indicies_in_visual_order[visual_index];
     line_item_chunks_in_visual_order[visual_index] =
-        line_item_chunks_[logical_index];
+        (*line_item_chunks)[logical_index];
   }
-  line_item_chunks_.swap(line_item_chunks_in_visual_order);
+  line_item_chunks->swap(line_item_chunks_in_visual_order);
 }
 
 void NGLineBuilder::CreateFragments(NGFragmentBuilder* container_builder) {
-  DCHECK(line_item_chunks_.isEmpty()) << "Must call CreateLine().";
+  DCHECK(!HasItems()) << "Must call CreateLine()";
   DCHECK_EQ(fragments_.size(), offsets_.size());
 
   for (unsigned i = 0; i < fragments_.size(); i++) {
@@ -162,19 +257,16 @@
     for (; fragment_index < line_box_data.fragment_end; fragment_index++) {
       NGPhysicalTextFragment* text_fragment =
           toNGPhysicalTextFragment(fragments_[fragment_index].get());
-      // TODO(kojii): needs to reverse for RTL?
-      for (unsigned item_index = text_fragment->StartIndex();
-           item_index < text_fragment->EndIndex(); item_index++) {
-        const NGLayoutInlineItem& item = items[item_index];
-        LayoutObject* layout_object = item.GetLayoutObject();
-        if (!layout_object)  // Skip bidi controls.
-          continue;
-        BidiRun* run;
-        if (layout_object->isText()) {
-          unsigned text_offset = text_offsets[item_index];
-          run = new BidiRun(item.StartOffset() - text_offset,
-                            item.EndOffset() - text_offset, item.BidiLevel(),
-                            LineLayoutItem(layout_object));
+      const NGLayoutInlineItem& item = items[text_fragment->ItemIndex()];
+      LayoutObject* layout_object = item.GetLayoutObject();
+      if (!layout_object)  // Skip bidi controls.
+        continue;
+      BidiRun* run;
+      if (layout_object->isText()) {
+        unsigned text_offset = text_offsets[text_fragment->ItemIndex()];
+        run = new BidiRun(text_fragment->StartOffset() - text_offset,
+                          text_fragment->EndOffset() - text_offset,
+                          item.BidiLevel(), LineLayoutItem(layout_object));
         } else {
           DCHECK(layout_object->isAtomicInlineLevel());
           run = new BidiRun(0, 1, item.BidiLevel(),
@@ -182,7 +274,6 @@
         }
         bidi_runs.addRun(run);
         fragments_for_bidi_runs.push_back(text_fragment);
-      }
     }
     // TODO(kojii): bidi needs to find the logical last run.
     bidi_runs.setLogicallyLastRun(bidi_runs.lastRun());
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
index 552a19b..9a0ac33 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_line_builder.h
@@ -18,18 +18,61 @@
 class NGInlineNode;
 
 // NGLineBuilder creates the fragment tree for a line.
+// NGLineBuilder manages the current line as a range, |start| and |end|.
+// |end| can be extended multiple times before creating a line, usually until
+// |!CanFitOnLine()|.
+// |SetBreakOpportunity| can mark the last confirmed offset that can fit.
 class CORE_EXPORT NGLineBuilder final
     : public GarbageCollectedFinalized<NGLineBuilder> {
  public:
   NGLineBuilder(NGInlineNode*, const NGConstraintSpace*);
 
-  // Add a range of NGLayoutInlineItem to the line in the logical order.
-  void Add(unsigned start_index, unsigned end_index, LayoutUnit inline_size);
+  const NGConstraintSpace& ConstraintSpace() const {
+    return *constraint_space_;
+  }
 
-  // Create a line from items added by |Add()|.
+  // Returns if the current items fit on a line.
+  bool CanFitOnLine() const;
+
+  // Returns if there were any items.
+  bool HasItems() const;
+
+  // Set the start offset.
+  // Set the end as well, and therefore empties the current line.
+  void SetStart(unsigned index, unsigned offset);
+
+  // Set the end offset.
+  void SetEnd(unsigned end_offset);
+
+  // Set the end offset, if caller knows the index and inline size since the
+  // current end offset.
+  void SetEnd(unsigned last_index,
+              unsigned end_offset,
+              LayoutUnit inline_size_since_current_end);
+
+  // Create a line up to the end offset.
+  // Then set the start to the end offset, and thus empty the current line.
   void CreateLine();
 
-  // Create fragments for lines created by |CreateLine()|.
+  // Returns if a break opportunity was set on the current line.
+  bool HasBreakOpportunity() const;
+
+  // Returns if there were items after the last break opportunity.
+  bool HasItemsAfterLastBreakOpportunity() const;
+
+  // Set the break opportunity at the current end offset.
+  void SetBreakOpportunity();
+
+  // Create a line up to the last break opportunity.
+  // Items after that are sent to the next line.
+  void CreateLineUpToLastBreakOpportunity();
+
+  // Set the start offset of hangables; e.g., spaces or hanging punctuations.
+  // Hangable characters can go beyond the right margin, and are ignored for
+  // center/right alignment.
+  void SetStartOfHangables(unsigned offset);
+
+  // Create fragments for all lines created so far.
   void CreateFragments(NGFragmentBuilder*);
 
   // Copy fragment data of all lines created by this NGLineBuilder to
@@ -41,14 +84,15 @@
   DECLARE_VIRTUAL_TRACE();
 
  private:
-  void BidiReorder();
-
   struct LineItemChunk {
-    unsigned start_index;
-    unsigned end_index;
+    unsigned index;
+    unsigned start_offset;
+    unsigned end_offset;
     LayoutUnit inline_size;
   };
 
+  void BidiReorder(Vector<LineItemChunk, 32>*);
+
   // LineBoxData is a set of data for a line box that are computed in early
   // phases, such as in |CreateLine()|, and will be used in later phases.
   // TODO(kojii): Not sure if all these data are needed in fragment tree. If
@@ -63,8 +107,15 @@
   Member<const NGConstraintSpace> constraint_space_;
   Vector<RefPtr<NGPhysicalFragment>, 32> fragments_;
   Vector<NGLogicalOffset, 32> offsets_;
-  Vector<LineItemChunk, 32> line_item_chunks_;
   Vector<LineBoxData, 32> line_box_data_list_;
+  unsigned start_index_ = 0;
+  unsigned start_offset_ = 0;
+  unsigned last_index_ = 0;
+  unsigned end_offset_ = 0;
+  unsigned last_break_opportunity_index_ = 0;
+  unsigned last_break_opportunity_offset_ = 0;
+  LayoutUnit end_position_;
+  LayoutUnit last_break_opportunity_position_;
   LayoutUnit content_size_;
   LayoutUnit max_inline_size_;
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h
index c6b3bce..bb33b0f1 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_physical_text_fragment.h
@@ -19,8 +19,9 @@
   NGPhysicalTextFragment(
       LayoutObject* layout_object,
       const NGInlineNode* node,
-      unsigned start_index,
-      unsigned end_index,
+      unsigned item_index,
+      unsigned start_offset,
+      unsigned end_offset,
       NGPhysicalSize size,
       NGPhysicalSize overflow,
       PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>>&
@@ -37,23 +38,24 @@
                            unpositioned_floats,
                            positioned_floats),
         node_(node),
-        start_index_(start_index),
-        end_index_(end_index) {}
+        item_index_(item_index),
+        start_offset_(start_offset),
+        end_offset_(end_offset) {}
 
   const NGInlineNode* Node() const { return node_; }
 
   // The range of NGLayoutInlineItem.
-  // |StartIndex| shows the lower logical index, so the visual order iteration
-  // for RTL should be done from |EndIndex - 1| to |StartIndex|.
-  unsigned StartIndex() const { return start_index_; }
-  unsigned EndIndex() const { return end_index_; }
+  unsigned ItemIndex() const { return item_index_; }
+  unsigned StartOffset() const { return start_offset_; }
+  unsigned EndOffset() const { return end_offset_; }
 
  private:
   // TODO(kojii): NGInlineNode is to access text content and NGLayoutInlineItem.
   // Review if it's better to point them.
   Persistent<const NGInlineNode> node_;
-  unsigned start_index_;
-  unsigned end_index_;
+  unsigned item_index_;
+  unsigned start_offset_;
+  unsigned end_offset_;
 };
 
 DEFINE_TYPE_CASTS(NGPhysicalTextFragment,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
index 9371806..1f1e593 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_text_layout_algorithm.cc
@@ -12,6 +12,7 @@
 #include "core/layout/ng/ng_line_builder.h"
 #include "core/layout/ng/ng_text_fragment.h"
 #include "core/style/ComputedStyle.h"
+#include "platform/text/TextBreakIterator.h"
 
 namespace blink {
 
@@ -30,41 +31,83 @@
   return nullptr;
 }
 
+static bool IsHangable(UChar ch) {
+  return ch == ' ';
+}
+
 void NGTextLayoutAlgorithm::LayoutInline(NGLineBuilder* line_builder) {
   // TODO(kojii): Make this tickable. Each line is easy. Needs more thoughts
   // for each fragment in a line. Bidi reordering is probably atomic.
   // TODO(kojii): oof is not well-thought yet. The bottom static position may be
   // in the next line, https://github.com/w3c/csswg-drafts/issues/609
-  const Vector<NGLayoutInlineItem>& items = inline_box_->Items();
-  for (unsigned start_index = 0; start_index < items.size();) {
-    const NGLayoutInlineItem& start_item = items[start_index];
-    // Make a bidi control a chunk by itself.
-    // TODO(kojii): atomic inline is not well-thought yet.
-    if (!start_item.Style()) {
-      line_builder->Add(start_index, start_index + 1, LayoutUnit());
-      start_index++;
+  const String& text_content = inline_box_->Text();
+  DCHECK(!text_content.isEmpty());
+  // TODO(kojii): Give the locale to LazyLineBreakIterator.
+  LazyLineBreakIterator line_break_iterator(text_content);
+  unsigned current_offset = 0;
+  line_builder->SetStart(0, current_offset);
+  const unsigned end_offset = text_content.length();
+  while (current_offset < end_offset) {
+    // Find the next break opportunity.
+    int tmp_next_breakable_offset = -1;
+    line_break_iterator.isBreakable(current_offset + 1,
+                                    tmp_next_breakable_offset);
+    current_offset =
+        tmp_next_breakable_offset >= 0 ? tmp_next_breakable_offset : end_offset;
+    DCHECK_LE(current_offset, end_offset);
+
+    // Advance the break opportunity to the end of hangable characters; e.g.,
+    // spaces.
+    // Unlike the ICU line breaker, LazyLineBreakIterator breaks before
+    // breakable spaces, and expect the line breaker to handle spaces
+    // differently. This logic computes in the ICU way; break after spaces, and
+    // handle spaces as hangable characters.
+    unsigned start_of_hangables = current_offset;
+    while (current_offset < end_offset &&
+           IsHangable(text_content[current_offset]))
+      current_offset++;
+
+    // Set the end to the next break opportunity.
+    line_builder->SetEnd(current_offset);
+
+    // If there are more available spaces, mark the break opportunity and fetch
+    // more text.
+    if (line_builder->CanFitOnLine()) {
+      line_builder->SetBreakOpportunity();
       continue;
     }
 
-    // TODO(kojii): Implement the line breaker.
-
-    LayoutUnit inline_size = start_item.InlineSize();
-    unsigned i = start_index + 1;
-    for (; i < items.size(); i++) {
-      const NGLayoutInlineItem& item = items[i];
-      // Split chunks before bidi controls, or at bidi level boundaries.
-      // Also split at LayoutObject boundaries to generate InlineBox in
-      // |CopyFragmentDataToLayoutBlockFlow()|.
-      if (item.GetLayoutObject() != start_item.GetLayoutObject() ||
-          !item.Style() || item.BidiLevel() != start_item.BidiLevel()) {
-        break;
-      }
-      inline_size += item.InlineSize();
+    // Compute hangable characters if exists.
+    if (current_offset != start_of_hangables) {
+      line_builder->SetStartOfHangables(start_of_hangables);
+      // If text before hangables can fit, include it in the current line.
+      if (line_builder->CanFitOnLine())
+        line_builder->SetBreakOpportunity();
     }
-    line_builder->Add(start_index, i, inline_size);
-    start_index = i;
+
+    if (!line_builder->HasBreakOpportunity()) {
+      // The first word (break opportunity) did not fit on the line.
+      // Create a line including items that don't fit, allowing them to
+      // overflow.
+      line_builder->CreateLine();
+    } else {
+      line_builder->CreateLineUpToLastBreakOpportunity();
+
+      // Items after the last break opportunity were sent to the next line.
+      // Set the break opportunity, or create a line if the word doesn't fit.
+      if (line_builder->HasItems()) {
+        if (!line_builder->CanFitOnLine())
+          line_builder->CreateLine();
+        else
+          line_builder->SetBreakOpportunity();
+      }
+    }
   }
-  line_builder->CreateLine();
+
+  // If inline children ended with items left in the line builder, create a line
+  // for them.
+  if (line_builder->HasItems())
+    line_builder->CreateLine();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
index 7ae5401e..c8c8fec 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.cpp
@@ -314,10 +314,12 @@
   return advance;
 }
 
-CharacterRange ShapeResultBuffer::getCharacterRange(TextDirection direction,
-                                                    float totalWidth,
-                                                    unsigned absoluteFrom,
-                                                    unsigned absoluteTo) const {
+CharacterRange ShapeResultBuffer::getCharacterRange(
+    const Vector<RefPtr<const ShapeResult>, 64>& results,
+    TextDirection direction,
+    float totalWidth,
+    unsigned absoluteFrom,
+    unsigned absoluteTo) {
   float currentX = 0;
   float fromX = 0;
   float toX = 0;
@@ -334,8 +336,8 @@
   int to = absoluteTo;
 
   unsigned totalNumCharacters = 0;
-  for (unsigned j = 0; j < m_results.size(); j++) {
-    const RefPtr<const ShapeResult> result = m_results[j];
+  for (unsigned j = 0; j < results.size(); j++) {
+    const RefPtr<const ShapeResult> result = results[j];
     if (direction == TextDirection::kRtl) {
       // Convert logical offsets to visual offsets, because results are in
       // logical order while runs are in visual order.
@@ -400,6 +402,13 @@
   return CharacterRange(toX, fromX);
 }
 
+CharacterRange ShapeResultBuffer::getCharacterRange(TextDirection direction,
+                                                    float totalWidth,
+                                                    unsigned from,
+                                                    unsigned to) const {
+  return getCharacterRange(m_results, direction, totalWidth, from, to);
+}
+
 void ShapeResultBuffer::addRunInfoRanges(const ShapeResult::RunInfo& runInfo,
                                          float offset,
                                          Vector<CharacterRange>& ranges) {
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
index 97870b81..6dcb22d 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
+++ b/third_party/WebKit/Source/platform/fonts/shaping/ShapeResultBuffer.h
@@ -5,6 +5,7 @@
 #ifndef ShapeResultBuffer_h
 #define ShapeResultBuffer_h
 
+#include "platform/PlatformExport.h"
 #include "platform/fonts/shaping/ShapeResult.h"
 #include "wtf/Allocator.h"
 #include "wtf/RefPtr.h"
@@ -17,7 +18,7 @@
 struct GlyphData;
 class TextRun;
 
-class ShapeResultBuffer {
+class PLATFORM_EXPORT ShapeResultBuffer {
   WTF_MAKE_NONCOPYABLE(ShapeResultBuffer);
   STACK_ALLOCATED();
 
@@ -50,6 +51,13 @@
   Vector<CharacterRange> individualCharacterRanges(TextDirection,
                                                    float totalWidth) const;
 
+  static CharacterRange getCharacterRange(
+      const Vector<RefPtr<const ShapeResult>, 64>&,
+      TextDirection,
+      float totalWidth,
+      unsigned from,
+      unsigned to);
+
  private:
   float fillFastHorizontalGlyphBuffer(GlyphBuffer*, const TextRun&) const;