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;