Diff
Modified: trunk/LayoutTests/ChangeLog (222781 => 222782)
--- trunk/LayoutTests/ChangeLog 2017-10-03 16:05:30 UTC (rev 222781)
+++ trunk/LayoutTests/ChangeLog 2017-10-03 16:12:26 UTC (rev 222782)
@@ -1,3 +1,33 @@
+2017-10-03 Matt Baker <[email protected]>
+
+ Web Inspector: Add View layout tests, make views more testable
+ https://bugs.webkit.org/show_bug.cgi?id=161274
+ <rdar://problem/28038615>
+
+ Reviewed by Devin Rousso.
+
+ Add tests for creating views, adding and removing subviews, and layout
+ operations. These tests rely on a mock requestAnimationFrame, which is
+ enabled with FrontendTestHarness.redirectRequestAnimationFrame.
+
+ * inspector/view/asynchronous-layout-expected.txt: Added.
+ * inspector/view/asynchronous-layout.html: Added.
+ * inspector/view/basics-expected.txt: Added.
+ * inspector/view/basics.html: Added.
+ * inspector/view/synchronous-layout-expected.txt: Added.
+ * inspector/view/synchronous-layout.html: Added.
+
+ * inspector/view/resources/test-view.js: Added.
+ (TestPage.registerInitializer.WI.TestView):
+ (TestPage.registerInitializer.WI.TestView.prototype.get initialLayoutCount):
+ (TestPage.registerInitializer.WI.TestView.prototype.get layoutCount):
+ (TestPage.registerInitializer.WI.TestView.prototype.evaluateAfterLayout):
+ (TestPage.registerInitializer.WI.TestView.prototype.initialLayout):
+ (TestPage.registerInitializer.WI.TestView.prototype.layout):
+ (TestPage.registerInitializer):
+ Register an instrumentation subclass of View. TestView counts calls to
+ protected methods and accepts callbacks to execute when a layout completes.
+
2017-10-03 Ryan Haddad <[email protected]>
Update iOS TestExpectations for payment-request tests.
Added: trunk/LayoutTests/inspector/view/asynchronous-layout-expected.txt (0 => 222782)
--- trunk/LayoutTests/inspector/view/asynchronous-layout-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/view/asynchronous-layout-expected.txt 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,34 @@
+Testing asynchronous View layout operations: needsLayout, cancelLayout.
+
+
+== Running test suite: View.AsynchronousLayout
+-- Running test case: View.automaticLayout
+PASS: View should have a pending layout once it is attached.
+Layout complete.
+PASS: View should do an initial layout.
+PASS: View should update its layout.
+PASS: View should not have a pending layout.
+
+-- Running test case: View.automaticLayout.cancelled
+PASS: View should have a pending layout once it is attached.
+PASS: View should not have a pending layout once it is detached.
+
+-- Running test case: View.needsLayout
+Flush pending layouts, then schedule an update.
+PASS: View should have a pending layout.
+Layout complete.
+PASS: View should update its layout.
+PASS: View should not have a pending layout.
+
+-- Running test case: View.needsLayout.propogateToSubview
+Schedule parent view update.
+Layout complete.
+PASS: Chlid view should do an initial layout.
+PASS: Child view should update its layout.
+
+-- Running test case: View.cancelLayout
+Cancel automatic layout.
+PASS: View should not have a pending layout.
+Cancel scheduled layout.
+PASS: View should not have a pending layout.
+
Added: trunk/LayoutTests/inspector/view/asynchronous-layout.html (0 => 222782)
--- trunk/LayoutTests/inspector/view/asynchronous-layout.html (rev 0)
+++ trunk/LayoutTests/inspector/view/asynchronous-layout.html 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,108 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script src=""
+<script>
+function test()
+{
+ InspectorTest.redirectRequestAnimationFrame();
+
+ let suite = InspectorTest.createAsyncSuite("View.AsynchronousLayout");
+
+ suite.addTestCase({
+ name: "View.automaticLayout",
+ test(resolve, reject) {
+ let view = new WI.TestView;
+ WI.View.rootView().addSubview(view);
+ InspectorTest.expectThat(view.layoutPending, "View should have a pending layout once it is attached.");
+
+ view.evaluateAfterLayout(() => {
+ InspectorTest.log("Layout complete.");
+ InspectorTest.expectEqual(view.initialLayoutCount, 1, "View should do an initial layout.");
+ InspectorTest.expectEqual(view.layoutCount, 1, "View should update its layout.");
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout.");
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.automaticLayout.cancelled",
+ test(resolve, reject) {
+ let view = new WI.TestView;
+
+ WI.View.rootView().addSubview(view);
+ InspectorTest.expectThat(view.layoutPending, "View should have a pending layout once it is attached.");
+
+ WI.View.rootView().removeSubview(view);
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout once it is detached.");
+ resolve();
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.needsLayout",
+ test(resolve, reject) {
+ let view = new WI.TestView;
+ WI.View.rootView().addSubview(view);
+
+ InspectorTest.log("Flush pending layouts, then schedule an update.");
+ view.updateLayout();
+ view.needsLayout();
+ InspectorTest.expectThat(view.layoutPending, "View should have a pending layout.");
+
+ view.evaluateAfterLayout(() => {
+ InspectorTest.log("Layout complete.");
+ InspectorTest.expectEqual(view.layoutCount, 2, "View should update its layout.");
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout.");
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.needsLayout.propogateToSubview",
+ test(resolve, reject) {
+ let parent = new WI.TestView;
+ let child = new WI.TestView;
+ WI.View.rootView().addSubview(parent);
+ parent.addSubview(child);
+ InspectorTest.log("Schedule parent view update.");
+ parent.needsLayout();
+
+ child.evaluateAfterLayout(() => {
+ InspectorTest.log("Layout complete.");
+ InspectorTest.expectEqual(child.initialLayoutCount, 1, "Chlid view should do an initial layout.");
+ InspectorTest.expectEqual(child.layoutCount, 1, "Child view should update its layout.");
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.cancelLayout",
+ test(resolve, reject) {
+ let view = new WI.TestView;
+ WI.View.rootView().addSubview(view);
+
+ InspectorTest.log("Cancel automatic layout.");
+ view.cancelLayout();
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout.");
+
+ InspectorTest.log("Cancel scheduled layout.");
+ view.needsLayout();
+ view.cancelLayout();
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout.");
+ resolve();
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+ <p>Testing asynchronous View layout operations: needsLayout, cancelLayout.</p>
+</body>
+</html>
Added: trunk/LayoutTests/inspector/view/basics-expected.txt (0 => 222782)
--- trunk/LayoutTests/inspector/view/basics-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/view/basics-expected.txt 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,52 @@
+Testing basic View operations: root view access, view creation, and subview management.
+
+
+== Running test suite: View.Basics
+-- Running test case: View.rootView
+PASS: Root view should be attached by definition.
+PASS: Root view element should be document.body.
+PASS: Root view should not have a pending layout.
+PASS: Root view should not have subviews.
+PASS: View.rootView() should always return the same view.
+
+-- Running test case: View.constructor
+PASS: View should not be attached.
+PASS: View element should be a <div>.
+PASS: View element should not have child nodes.
+PASS: View should not have a pending layout.
+PASS: View should not have subviews.
+PASS: View should not have a parent.
+PASS: View should be created with passed in element.
+
+-- Running test case: View.addSubview
+PASS: Child should have the correct parent.
+PASS: Child should be a descendant of the parent.
+PASS: Child should be included in the parent's subviews.
+PASS: Adding a view multiple times should have no effect.
+PASS: Grandchild should be a descendant of it's grandparent.
+
+-- Running test case: View.removeSubview
+PASS: Removed view should not have a parent.
+PASS: Removed view should not be a descendant of the parent.
+PASS: Removed view should not be included in the parent's subviews.
+PASS: Removing a nonexistent view should have no effect.
+
+-- Running test case: View.insertSubviewBefore
+PASS: Inserting a view before `null` should append the view.
+PASS: child2 should be inserted before dhild1.
+PASS: child1 should be after child2.
+PASS: Inserting a view before a nonexistent view should have no effect.
+
+-- Running test case: View.replaceSubview
+PASS: Replaced view should not have a parent.
+PASS: New view should have the correct parent.
+PASS: Replacing a view with itself should have no effect.
+PASS: Replacing a nonexistent view should have no effect.
+
+-- Running test case: View.isAttached
+PASS: View added to the root should be attached.
+PASS: View removed from the root should not be attached.
+PASS: View added to a detached parent should not be attached.
+PASS: Attaching a view to the root causes descendent views to be attached.
+PASS: Detaching a view from the root causes descendent views to be detached.
+
Added: trunk/LayoutTests/inspector/view/basics.html (0 => 222782)
--- trunk/LayoutTests/inspector/view/basics.html (rev 0)
+++ trunk/LayoutTests/inspector/view/basics.html 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,158 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script>
+function test()
+{
+ let suite = InspectorTest.createSyncSuite("View.Basics");
+
+ suite.addTestCase({
+ name: "View.rootView",
+ test() {
+ let rootView = WI.View.rootView();
+ InspectorTest.expectThat(rootView.isAttached, "Root view should be attached by definition.");
+ InspectorTest.expectEqual(rootView.element, document.body, "Root view element should be document.body.");
+ InspectorTest.expectFalse(rootView.layoutPending, "Root view should not have a pending layout.");
+ InspectorTest.expectEqual(rootView.subviews.length, 0, "Root view should not have subviews.");
+ InspectorTest.expectEqual(WI.View.rootView(), WI.View.rootView(), "View.rootView() should always return the same view.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.constructor",
+ test() {
+ let view = new WI.View;
+ InspectorTest.expectFalse(view.isAttached, "View should not be attached.");
+ InspectorTest.expectEqual(view.element.tagName, "DIV", "View element should be a <div>.");
+ InspectorTest.expectEqual(view.element.childNodes.length, 0, "View element should not have child nodes.");
+ InspectorTest.expectFalse(view.layoutPending, "View should not have a pending layout.");
+ InspectorTest.expectEqual(view.subviews.length, 0, "View should not have subviews.");
+ InspectorTest.expectNull(view.parentView, "View should not have a parent.");
+
+ let existingElement = document.createElement("ol");
+ let customView = new WI.View(existingElement);
+ InspectorTest.expectEqual(customView.element, existingElement, "View should be created with passed in element.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.addSubview",
+ test() {
+ let parent = new WI.View;
+ let child = new WI.View;
+ parent.addSubview(child);
+ InspectorTest.expectEqual(child.parentView, parent, "Child should have the correct parent.");
+ InspectorTest.expectThat(child.isDescendantOf(parent), "Child should be a descendant of the parent.");
+ InspectorTest.expectThat(parent.subviews.includes(child), "Child should be included in the parent's subviews.");
+
+ let previousSubviews = parent.subviews.slice();
+ parent.addSubview(child);
+ InspectorTest.expectShallowEqual(previousSubviews, parent.subviews, "Adding a view multiple times should have no effect.");
+
+ let grandchild = new WI.View;
+ child.addSubview(grandchild);
+ InspectorTest.expectThat(grandchild.isDescendantOf(child.parentView), "Grandchild should be a descendant of it's grandparent.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.removeSubview",
+ test() {
+ let parent = new WI.View;
+ let child = new WI.View;
+ parent.addSubview(child);
+ parent.removeSubview(child);
+ InspectorTest.expectNull(child.parentView, "Removed view should not have a parent.");
+ InspectorTest.expectThat(!child.isDescendantOf(parent), "Removed view should not be a descendant of the parent.");
+ InspectorTest.expectThat(!parent.subviews.includes(child), "Removed view should not be included in the parent's subviews.");
+
+ let previousSubviews = parent.subviews.slice();
+ parent.removeSubview(new WI.View);
+ InspectorTest.expectShallowEqual(previousSubviews, parent.subviews, "Removing a nonexistent view should have no effect.")
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.insertSubviewBefore",
+ test() {
+ let parent = new WI.View;
+ let child1 = new WI.View;
+ let child2 = new WI.View;
+ parent.insertSubviewBefore(child1);
+ InspectorTest.expectEqual(parent.subviews[0], child1, "Inserting a view before `null` should append the view.");
+
+ parent.insertSubviewBefore(child2, child1);
+ InspectorTest.expectEqual(parent.subviews[0], child2, "child2 should be inserted before dhild1.");
+ InspectorTest.expectEqual(parent.subviews[1], child1, "child1 should be after child2.");
+
+ let previousSubviews = parent.subviews.slice();
+ parent.insertSubviewBefore(child2, new WI.View);
+ InspectorTest.expectShallowEqual(parent.subviews, previousSubviews, "Inserting a view before a nonexistent view should have no effect.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.replaceSubview",
+ test() {
+ let parent = new WI.View;
+ let child1 = new WI.View;
+ let child2 = new WI.View;
+ parent.addSubview(child1);
+ parent.replaceSubview(child1, child2);
+ InspectorTest.expectNull(child1.parentView, "Replaced view should not have a parent.");
+ InspectorTest.expectEqual(child2.parentView, parent, "New view should have the correct parent.");
+
+ let previousSubviews = parent.subviews.slice();
+ parent.replaceSubview(child2, child2);
+ InspectorTest.expectShallowEqual(parent.subviews, previousSubviews, "Replacing a view with itself should have no effect.");
+
+ previousSubviews = parent.subviews;
+ parent.replaceSubview(new WI.View, child1);
+ InspectorTest.expectShallowEqual(parent.subviews, previousSubviews, "Replacing a nonexistent view should have no effect.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.isAttached",
+ test() {
+ let view = new WI.View;
+ WI.View.rootView().addSubview(view);
+ InspectorTest.expectThat(view.isAttached, "View added to the root should be attached.");
+
+ WI.View.rootView().removeSubview(view);
+ InspectorTest.expectFalse(view.isAttached, "View removed from the root should not be attached.");
+
+ let parent = new WI.View;
+ let child = new WI.View;
+ parent.addSubview(child);
+ InspectorTest.expectFalse(child.isAttached, "View added to a detached parent should not be attached.");
+ WI.View.rootView().addSubview(parent);
+ InspectorTest.expectThat(child.isAttached, "Attaching a view to the root causes descendent views to be attached.");
+ WI.View.rootView().removeSubview(parent);
+ InspectorTest.expectFalse(child.isAttached, "Detaching a view from the root causes descendent views to be detached.");
+
+ return true;
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+ <p>Testing basic View operations: root view access, view creation, and subview management.</p>
+</body>
+</html>
Added: trunk/LayoutTests/inspector/view/resources/test-view.js (0 => 222782)
--- trunk/LayoutTests/inspector/view/resources/test-view.js (rev 0)
+++ trunk/LayoutTests/inspector/view/resources/test-view.js 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,39 @@
+TestPage.registerInitializer(() => {
+ WI.TestView = class TestView extends WI.View
+ {
+ constructor()
+ {
+ super();
+
+ this._layoutCallbacks = [];
+ this._initialLayoutCount = 0;
+ this._layoutCount = 0;
+ }
+
+ // Public
+
+ get initialLayoutCount() { return this._initialLayoutCount; }
+ get layoutCount() { return this._layoutCount; }
+
+ evaluateAfterLayout(callback)
+ {
+ this._layoutCallbacks.push(callback);
+ }
+
+ // Protected
+
+ initialLayout()
+ {
+ this._initialLayoutCount++;
+ }
+
+ layout()
+ {
+ this._layoutCount++;
+ let callbacks = this._layoutCallbacks;
+ this._layoutCallbacks = [];
+ for (let callback of callbacks)
+ callback();
+ }
+ };
+});
Added: trunk/LayoutTests/inspector/view/synchronous-layout-expected.txt (0 => 222782)
--- trunk/LayoutTests/inspector/view/synchronous-layout-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/view/synchronous-layout-expected.txt 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,24 @@
+Testing synchronous View layout operations: updateLayout, updateLayoutIfNeeded.
+
+
+== Running test suite: View.SynchronousLayout
+-- Running test case: View.updateLayout
+Update layout for attached view.
+PASS: View should do an initial layout.
+PASS: View should update its layout once.
+PASS: View should not have a pending layout.
+Update layout for detached view.
+PASS: View should do an initial layout.
+PASS: View should update its layout once.
+PASS: View should not have a pending layout.
+
+-- Running test case: View.updateLayout.propogateToSubviews
+Update parent view layout.
+PASS: Child view should do an initial layout.
+PASS: Child view should update its layout once.
+
+-- Running test case: View.updateLayoutIfNeeded
+PASS: View should update if an initial layout never happened.
+PASS: View should update if a layout is pending.
+PASS: View should not update if no layout is pending.
+
Added: trunk/LayoutTests/inspector/view/synchronous-layout.html (0 => 222782)
--- trunk/LayoutTests/inspector/view/synchronous-layout.html (rev 0)
+++ trunk/LayoutTests/inspector/view/synchronous-layout.html 2017-10-03 16:12:26 UTC (rev 222782)
@@ -0,0 +1,81 @@
+<!doctype html>
+<html>
+<head>
+<script src=""
+<script src=""
+<script>
+function test()
+{
+ InspectorTest.redirectRequestAnimationFrame();
+
+ let suite = InspectorTest.createSyncSuite("View.SynchronousLayout");
+
+ suite.addTestCase({
+ name: "View.updateLayout",
+ test() {
+ let view1 = new WI.TestView;
+ WI.View.rootView().addSubview(view1);
+ InspectorTest.log("Update layout for attached view.");
+ view1.updateLayout();
+ InspectorTest.expectEqual(view1.initialLayoutCount, 1, "View should do an initial layout.");
+ InspectorTest.expectEqual(view1.layoutCount, 1, "View should update its layout once.");
+ InspectorTest.expectFalse(view1.layoutPending, "View should not have a pending layout.");
+
+ let view2 = new WI.TestView;
+ InspectorTest.log("Update layout for detached view.");
+ view2.updateLayout();
+ InspectorTest.expectEqual(view2.initialLayoutCount, 1, "View should do an initial layout.");
+ InspectorTest.expectEqual(view2.layoutCount, 1, "View should update its layout once.");
+ InspectorTest.expectFalse(view2.layoutPending, "View should not have a pending layout.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.updateLayout.propogateToSubviews",
+ test() {
+ let parent = new WI.TestView;
+ let child = new WI.TestView;
+
+ WI.View.rootView().addSubview(parent);
+ parent.addSubview(child);
+
+ InspectorTest.log("Update parent view layout.");
+ parent.updateLayout();
+ InspectorTest.expectEqual(child.initialLayoutCount, 1, "Child view should do an initial layout.");
+ InspectorTest.expectEqual(child.layoutCount, 1, "Child view should update its layout once.");
+
+ return true;
+ }
+ });
+
+ suite.addTestCase({
+ name: "View.updateLayoutIfNeeded",
+ test() {
+ let view1 = new WI.TestView;
+ view1.updateLayoutIfNeeded();
+ InspectorTest.expectEqual(view1.layoutCount, 1, "View should update if an initial layout never happened.");
+
+ let view2 = new WI.TestView;
+ view2.needsLayout();
+ view2.updateLayoutIfNeeded();
+ InspectorTest.expectEqual(view2.layoutCount, 1, "View should update if a layout is pending.");
+
+ let view3 = new WI.TestView;
+ view3.updateLayout();
+ view3.updateLayoutIfNeeded();
+ InspectorTest.expectEqual(view3.layoutCount, 1, "View should not update if no layout is pending.");
+
+ return true;
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+ <p>Testing synchronous View layout operations: updateLayout, updateLayoutIfNeeded.</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (222781 => 222782)
--- trunk/Source/WebInspectorUI/ChangeLog 2017-10-03 16:05:30 UTC (rev 222781)
+++ trunk/Source/WebInspectorUI/ChangeLog 2017-10-03 16:12:26 UTC (rev 222782)
@@ -1,3 +1,28 @@
+2017-10-03 Matt Baker <[email protected]>
+
+ Web Inspector: Add View layout tests, make views more testable
+ https://bugs.webkit.org/show_bug.cgi?id=161274
+ <rdar://problem/28038615>
+
+ Reviewed by Devin Rousso.
+
+ This patch adds support for View testing. Since view layouts are scheduled
+ using requestAnimationFrame, FrontendTestHarness now provides a timer-based
+ polyfill, to allow nonintrusive testing of the frontend View hierarchy.
+
+ * UserInterface/Test.html:
+ Make WI.View available to tests.
+
+ * UserInterface/Test/FrontendTestHarness.js:
+ (FrontendTestHarness.prototype.redirectRequestAnimationFrame):
+
+ * UserInterface/Views/View.js:
+ (WI.View.rootView):
+ (WI.View.prototype.replaceSubview):
+ (WI.View.prototype._didMoveToWindow):
+ (WI.View._cancelScheduledLayoutForView):
+ Fixed issues caught while writing tests for the expected View behavior.
+
2017-10-02 Joseph Pecoraro <[email protected]>
Web Inspector: Escape more characters in posix string conversion
Modified: trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js (222781 => 222782)
--- trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js 2017-10-03 16:05:30 UTC (rev 222781)
+++ trunk/Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js 2017-10-03 16:12:26 UTC (rev 222782)
@@ -140,6 +140,46 @@
});
}
+ redirectRequestAnimationFrame()
+ {
+ console.assert(!this._originalRequestAnimationFrame);
+ if (this._originalRequestAnimationFrame)
+ return;
+
+ this._originalRequestAnimationFrame = window.requestAnimationFrame;
+ this._requestAnimationFrameCallbacks = new Map;
+ this._nextRequestIdentifier = 1;
+
+ window.requestAnimationFrame = (callback) => {
+ let requestIdentifier = this._nextRequestIdentifier++;
+ this._requestAnimationFrameCallbacks.set(requestIdentifier, callback);
+ if (this._requestAnimationFrameTimer)
+ return requestIdentifier;
+
+ let dispatchCallbacks = () => {
+ let callbacks = this._requestAnimationFrameCallbacks;
+ this._requestAnimationFrameCallbacks = new Map;
+ this._requestAnimationFrameTimer = undefined;
+ let timestamp = window.performance.now();
+ for (let callback of callbacks.values())
+ callback(timestamp);
+ };
+
+ this._requestAnimationFrameTimer = setTimeout(dispatchCallbacks, 0);
+ return requestIdentifier;
+ };
+
+ window.cancelAnimationFrame = (requestIdentifier) => {
+ if (!this._requestAnimationFrameCallbacks.delete(requestIdentifier))
+ return;
+
+ if (!this._requestAnimationFrameCallbacks.size) {
+ clearTimeout(this._requestAnimationFrameTimer);
+ this._requestAnimationFrameTimer = undefined;
+ }
+ };
+ }
+
redirectConsoleToTestOutput()
{
// We can't use arrow functions here because of 'arguments'. It might
Modified: trunk/Source/WebInspectorUI/UserInterface/Test.html (222781 => 222782)
--- trunk/Source/WebInspectorUI/UserInterface/Test.html 2017-10-03 16:05:30 UTC (rev 222781)
+++ trunk/Source/WebInspectorUI/UserInterface/Test.html 2017-10-03 16:12:26 UTC (rev 222782)
@@ -215,6 +215,7 @@
<script src=""
<script src=""
<script src=""
+ <script src=""
<script type="text/_javascript_">
WI.sharedApp = new WI.TestAppController;
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/View.js (222781 => 222782)
--- trunk/Source/WebInspectorUI/UserInterface/Views/View.js 2017-10-03 16:05:30 UTC (rev 222781)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/View.js 2017-10-03 16:12:26 UTC (rev 222782)
@@ -44,8 +44,12 @@
static rootView()
{
- if (!WI.View._rootView)
+ if (!WI.View._rootView) {
+ // Since the root view is attached by definition, it does not go through the
+ // normal view attachment process. Simply mark it as attached.
WI.View._rootView = new WI.View(document.body);
+ WI.View._rootView._isAttachedToRoot = true;
+ }
return WI.View._rootView;
}
@@ -129,6 +133,8 @@
replaceSubview(oldView, newView)
{
console.assert(oldView !== newView, "Cannot replace subview with itself.");
+ if (oldView === newView)
+ return;
this.insertSubviewBefore(newView, oldView);
this.removeSubview(oldView);
@@ -234,10 +240,13 @@
this._isAttachedToRoot = isAttachedToRoot;
if (this._isAttachedToRoot) {
+ WI.View._scheduleLayoutForView(this);
this.attached();
- WI.View._scheduleLayoutForView(this);
- } else
+ } else {
+ if (this._dirty)
+ this.cancelLayout();
this.detached();
+ }
for (let view of this._subviews)
view._didMoveToWindow(isAttachedToRoot);
@@ -328,6 +337,8 @@
parentView = parentView.parentView;
}
+ view._dirty = false;
+
if (!WI.View._scheduledLayoutUpdateIdentifier)
return;