Web Inspector: Reduce synchronous view layouts
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / Models / BackForwardEntry.js
1 /*
2  *  Copyright (C) 2013 University of Washington. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
15  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 WebInspector.BackForwardEntry = class BackForwardEntry extends WebInspector.Object
28 {
29     constructor(contentView, cookie)
30     {
31         super();
32
33         this._contentView = contentView;
34
35         // Cookies are compared with Object.shallowEqual, so should not store objects or arrays.
36         this._cookie = cookie || {};
37         this._scrollPositions = [];
38
39         contentView.saveToCookie(this._cookie);
40     }
41
42     // Public
43
44     get contentView()
45     {
46         return this._contentView;
47     }
48
49     get cookie()
50     {
51         // Cookies are immutable; they represent a specific navigation action.
52         return Object.shallowCopy(this._cookie);
53     }
54
55     prepareToShow(shouldCallShown)
56     {
57         this._restoreFromCookie();
58
59         this.contentView.visible = true;
60         if (shouldCallShown)
61             this.contentView.shown();
62         this.contentView.needsLayout();
63     }
64
65     prepareToHide()
66     {
67         this.contentView.visible = false;
68         this.contentView.hidden();
69
70         this._saveScrollPositions();
71     }
72
73     // Private
74
75     _restoreFromCookie()
76     {
77         this._restoreScrollPositions();
78         this.contentView.restoreFromCookie(this.cookie);
79     }
80
81     _restoreScrollPositions()
82     {
83         // If no scroll positions are saved, do nothing.
84         if (!this._scrollPositions.length)
85             return;
86
87         var scrollableElements = this.contentView.scrollableElements || [];
88         console.assert(this._scrollPositions.length === scrollableElements.length);
89
90         for (var i = 0; i < scrollableElements.length; ++i) {
91             var position = this._scrollPositions[i];
92             var element = scrollableElements[i];
93             if (!element)
94                 continue;
95
96             // Restore the top scroll position by either scrolling to the bottom or to the saved position.
97             element.scrollTop = position.isScrolledToBottom ? element.scrollHeight : position.scrollTop;
98
99             // Don't restore the left scroll position when scrolled to the bottom. This way the when content changes
100             // the user won't be left in a weird horizontal position.
101             element.scrollLeft = position.isScrolledToBottom ? 0 : position.scrollLeft;
102         }
103     }
104
105     _saveScrollPositions()
106     {
107         var scrollableElements = this.contentView.scrollableElements || [];
108         var scrollPositions = [];
109         for (var i = 0; i < scrollableElements.length; ++i) {
110             var element = scrollableElements[i];
111             if (!element)
112                 continue;
113
114             var position = { scrollTop: element.scrollTop, scrollLeft: element.scrollLeft };
115             if (this.contentView.shouldKeepElementsScrolledToBottom)
116                 position.isScrolledToBottom = element.isScrolledToBottom();
117
118             scrollPositions.push(position);
119         }
120
121         this._scrollPositions = scrollPositions;
122     }
123 };