Web Inspector: save and restore source positions in back/forward history
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / 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 = function(contentView, cookie, restoreCallback)
28 {
29     WebInspector.Object.call(this);
30     this._contentView = contentView;
31     // Cookies are compared with Object.shallowEqual, so should not store objects or arrays.
32     this._cookie = cookie || {};
33     this._scrollPositions = [];
34     this._restoreCallback = restoreCallback;
35 };
36
37 WebInspector.BackForwardEntry.prototype = {
38     constructor: WebInspector.BackForwardEntry,
39     __proto__: WebInspector.Object.prototype,
40
41     // Public
42
43     get contentView()
44     {
45         return this._contentView;
46     },
47
48     get cookie()
49     {
50         // Cookies are immutable; they represent a specific navigation action.
51         return Object.shallowCopy(this._cookie);
52     },
53
54     prepareToShow: function()
55     {
56         this._restoreFromCookie();
57
58         this.contentView.visible = true;
59         this.contentView.shown();
60         this.contentView.updateLayout();
61     },
62
63     prepareToHide: function()
64     {
65         this.contentView.visible = false;
66         this.contentView.hidden();
67
68         this._saveScrollPositions();
69     },
70
71     // Private
72
73     _restoreFromCookie: function()
74     {
75         this._restoreScrollPositions();
76
77         if (this._restoreCallback && typeof this._restoreCallback === "function")
78             this._restoreCallback.call(null, this.contentView, this.cookie);
79     },
80
81     _restoreScrollPositions: function()
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: function()
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 };