blob: ff6f8862b7d630066fcec823da3f0e1e68734e80 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/resize_observer/resize_observer.h"
#include "third_party/blink/public/web/web_heap.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/core/exported/web_view_impl.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observation.h"
#include "third_party/blink/renderer/core/resize_observer/resize_observer_controller.h"
#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/platform/loader/fetch/access_control_status.h"
#include "third_party/blink/renderer/platform/loader/fetch/script_fetch_options.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/time.h"
namespace blink {
namespace {
class TestResizeObserverDelegate : public ResizeObserver::Delegate {
public:
TestResizeObserverDelegate(Document& document)
: document_(document), call_count_(0) {}
void OnResize(
const HeapVector<Member<ResizeObserverEntry>>& entries) override {
call_count_++;
}
ExecutionContext* GetExecutionContext() const { return document_; }
int CallCount() const { return call_count_; }
void Trace(blink::Visitor* visitor) override {
ResizeObserver::Delegate::Trace(visitor);
visitor->Trace(document_);
}
private:
Member<Document> document_;
int call_count_;
};
} // namespace
/* Testing:
* getTargetSize
* setTargetSize
* oubservationSizeOutOfSync == false
* modify target size
* oubservationSizeOutOfSync == true
*/
class ResizeObserverUnitTest : public SimTest {};
TEST_F(ResizeObserverUnitTest, ResizeObservationSize) {
SimRequest main_resource("https://example.com/", "text/html");
LoadURL("https://example.com/");
main_resource.Start();
main_resource.Write(R"HTML(
<div id='domTarget' style='width:100px;height:100px'>yo</div>
<svg height='200' width='200'>
<circle id='svgTarget' cx='100' cy='100' r='100'/>
</svg>
)HTML");
main_resource.Finish();
ResizeObserver::Delegate* delegate =
new TestResizeObserverDelegate(GetDocument());
ResizeObserver* observer = ResizeObserver::Create(GetDocument(), delegate);
Element* dom_target = GetDocument().getElementById("domTarget");
Element* svg_target = GetDocument().getElementById("svgTarget");
ResizeObservation* dom_observation =
new ResizeObservation(dom_target, observer);
ResizeObservation* svg_observation =
new ResizeObservation(svg_target, observer);
// Initial observation is out of sync
ASSERT_TRUE(dom_observation->ObservationSizeOutOfSync());
ASSERT_TRUE(svg_observation->ObservationSizeOutOfSync());
// Target size is correct
LayoutSize size = dom_observation->ComputeTargetSize();
ASSERT_EQ(size.Width(), 100);
ASSERT_EQ(size.Height(), 100);
dom_observation->SetObservationSize(size);
size = svg_observation->ComputeTargetSize();
ASSERT_EQ(size.Width(), 200);
ASSERT_EQ(size.Height(), 200);
svg_observation->SetObservationSize(size);
// Target size is in sync
ASSERT_FALSE(dom_observation->ObservationSizeOutOfSync());
// Target depths
ASSERT_EQ(svg_observation->TargetDepth() - dom_observation->TargetDepth(),
(size_t)1);
}
TEST_F(ResizeObserverUnitTest, TestMemoryLeaks) {
ResizeObserverController& controller =
GetDocument().EnsureResizeObserverController();
const HeapHashSet<WeakMember<ResizeObserver>>& observers =
controller.Observers();
ASSERT_EQ(observers.size(), 0U);
v8::HandleScope scope(v8::Isolate::GetCurrent());
ScriptController& script_controller =
GetDocument().ExecutingFrame()->GetScriptController();
//
// Test whether ResizeObserver is kept alive by direct JS reference
//
script_controller.ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode("var ro = new ResizeObserver( entries => {});"), KURL(),
kOpaqueResource, ScriptFetchOptions(),
ScriptController::kExecuteScriptWhenScriptsDisabled);
ASSERT_EQ(observers.size(), 1U);
script_controller.ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode("ro = undefined;"), KURL(), kOpaqueResource,
ScriptFetchOptions(),
ScriptController::kExecuteScriptWhenScriptsDisabled);
V8GCController::CollectAllGarbageForTesting(
v8::Isolate::GetCurrent(),
v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
WebHeap::CollectAllGarbageForTesting();
ASSERT_EQ(observers.IsEmpty(), true);
//
// Test whether ResizeObserver is kept alive by an Element
//
script_controller.ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode("var ro = new ResizeObserver( () => {});"
"var el = document.createElement('div');"
"ro.observe(el);"
"ro = undefined;"),
KURL(), kOpaqueResource, ScriptFetchOptions(),
ScriptController::kExecuteScriptWhenScriptsDisabled);
ASSERT_EQ(observers.size(), 1U);
V8GCController::CollectAllGarbageForTesting(
v8::Isolate::GetCurrent(),
v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
WebHeap::CollectAllGarbageForTesting();
ASSERT_EQ(observers.size(), 1U);
script_controller.ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode("el = undefined;"), KURL(), kOpaqueResource,
ScriptFetchOptions(),
ScriptController::kExecuteScriptWhenScriptsDisabled);
V8GCController::CollectAllGarbageForTesting(
v8::Isolate::GetCurrent(),
v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
WebHeap::CollectAllGarbageForTesting();
ASSERT_EQ(observers.IsEmpty(), true);
}
} // namespace blink