Web Inspector: save and restore source positions in back/forward history

https://bugs.webkit.org/show_bug.cgi?id=122062

Patch by Brian J. Burg <burg@cs.washington.edu> on 2013-10-02
Reviewed by Timothy Hatcher.

Previously, the back/forward entries comprised of only the content
views, but not their positions if navigated via hyperlink (i.e.,
handling script.js:42).  When multiple instances of the same
content view appeared in the back/forward list, the most recent
navigation was displayed rather than the linked position.

We now store context necessary to re-navigate such hyperlinks by
storing view- specific data inside a cookie object, and invoke a
supplied callback to take any position initialization actions,
such as calling TextEditor.revealPosition.  This state is
encapsulated into BackForwardEntry instances.

Functions that save and restore scroll positions inside content
views have been changed to store state in BackForwardEntry
instances, so multiple scroll positions can be saved for a content
view appearing in the navigation history more than once.

* UserInterface/BackForwardEntry.js: Added.
(WebInspector.BackForwardEntry):
(WebInspector.BackForwardEntry.prototype.get contentView):
(WebInspector.BackForwardEntry.prototype.get cookie):
(WebInspector.BackForwardEntry.prototype.prepareToShow):
(WebInspector.BackForwardEntry.prototype.prepareToHide):
(WebInspector.BackForwardEntry.prototype._restoreFromCookie):
(WebInspector.BackForwardEntry.prototype._restoreScrollPositions):
(WebInspector.BackForwardEntry.prototype._saveScrollPositions):
* UserInterface/ContentBrowser.js:
(WebInspector.ContentBrowser.prototype.showContentView):
(WebInspector.ContentBrowser.prototype._updateContentViewNavigationItems):
(WebInspector.ContentBrowser.prototype._updateFindBanner):
* UserInterface/ContentViewContainer.js:
(WebInspector.ContentViewContainer.prototype.get currentContentView):
(WebInspector.ContentViewContainer.prototype.get currentBackForwardEntry):
(WebInspector.ContentViewContainer.prototype.showContentView):
(WebInspector.ContentViewContainer.prototype.showBackForwardEntryForIndex):
(WebInspector.ContentViewContainer.prototype.replaceContentView):
(WebInspector.ContentViewContainer.prototype.closeAllContentViewsOfPrototype):
(WebInspector.ContentViewContainer.prototype.closeAllContentViews):
(WebInspector.ContentViewContainer.prototype.goBack):
(WebInspector.ContentViewContainer.prototype.goForward):
(WebInspector.ContentViewContainer.prototype.shown):
(WebInspector.ContentViewContainer.prototype.hidden):
(WebInspector.ContentViewContainer.prototype._showEntry):
(WebInspector.ContentViewContainer.prototype._hideEntry):
* UserInterface/Main.html:
* UserInterface/Main.js:
(WebInspector.openURL):
* UserInterface/ResourceSidebarPanel.js:
(WebInspector.ResourceSidebarPanel.prototype.restoreCallback):
(WebInspector.ResourceSidebarPanel.prototype.showSourceCode):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@156809 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 90884345
2013-10-02 Brian J. Burg <burg@cs.washington.edu>
Web Inspector: save and restore source positions in back/forward history
https://bugs.webkit.org/show_bug.cgi?id=122062
Reviewed by Timothy Hatcher.
Previously, the back/forward entries comprised of only the content
views, but not their positions if navigated via hyperlink (i.e.,
handling script.js:42). When multiple instances of the same
content view appeared in the back/forward list, the most recent
navigation was displayed rather than the linked position.
We now store context necessary to re-navigate such hyperlinks by
storing view- specific data inside a cookie object, and invoke a
supplied callback to take any position initialization actions,
such as calling TextEditor.revealPosition. This state is
encapsulated into BackForwardEntry instances.
Functions that save and restore scroll positions inside content
views have been changed to store state in BackForwardEntry
instances, so multiple scroll positions can be saved for a content
view appearing in the navigation history more than once.
* UserInterface/BackForwardEntry.js: Added.
(WebInspector.BackForwardEntry):
(WebInspector.BackForwardEntry.prototype.get contentView):
(WebInspector.BackForwardEntry.prototype.get cookie):
(WebInspector.BackForwardEntry.prototype.prepareToShow):
(WebInspector.BackForwardEntry.prototype.prepareToHide):
(WebInspector.BackForwardEntry.prototype._restoreFromCookie):
(WebInspector.BackForwardEntry.prototype._restoreScrollPositions):
(WebInspector.BackForwardEntry.prototype._saveScrollPositions):
* UserInterface/ContentBrowser.js:
(WebInspector.ContentBrowser.prototype.showContentView):
(WebInspector.ContentBrowser.prototype._updateContentViewNavigationItems):
(WebInspector.ContentBrowser.prototype._updateFindBanner):
* UserInterface/ContentViewContainer.js:
(WebInspector.ContentViewContainer.prototype.get currentContentView):
(WebInspector.ContentViewContainer.prototype.get currentBackForwardEntry):
(WebInspector.ContentViewContainer.prototype.showContentView):
(WebInspector.ContentViewContainer.prototype.showBackForwardEntryForIndex):
(WebInspector.ContentViewContainer.prototype.replaceContentView):
(WebInspector.ContentViewContainer.prototype.closeAllContentViewsOfPrototype):
(WebInspector.ContentViewContainer.prototype.closeAllContentViews):
(WebInspector.ContentViewContainer.prototype.goBack):
(WebInspector.ContentViewContainer.prototype.goForward):
(WebInspector.ContentViewContainer.prototype.shown):
(WebInspector.ContentViewContainer.prototype.hidden):
(WebInspector.ContentViewContainer.prototype._showEntry):
(WebInspector.ContentViewContainer.prototype._hideEntry):
* UserInterface/Main.html:
* UserInterface/Main.js:
(WebInspector.openURL):
* UserInterface/ResourceSidebarPanel.js:
(WebInspector.ResourceSidebarPanel.prototype.restoreCallback):
(WebInspector.ResourceSidebarPanel.prototype.showSourceCode):
2013-10-02 Antoine Quint <graouts@apple.com>
Web Inspector: highlight newly added console messages in the Activity Viewer
......
/*
* Copyright (C) 2013 University of Washington. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
WebInspector.BackForwardEntry = function(contentView, cookie, restoreCallback)
{
WebInspector.Object.call(this);
this._contentView = contentView;
// Cookies are compared with Object.shallowEqual, so should not store objects or arrays.
this._cookie = cookie || {};
this._scrollPositions = [];
this._restoreCallback = restoreCallback;
};
WebInspector.BackForwardEntry.prototype = {
constructor: WebInspector.BackForwardEntry,
__proto__: WebInspector.Object.prototype,
// Public
get contentView()
{
return this._contentView;
},
get cookie()
{
// Cookies are immutable; they represent a specific navigation action.
return Object.shallowCopy(this._cookie);
},
prepareToShow: function()
{
this._restoreFromCookie();
this.contentView.visible = true;
this.contentView.shown();
this.contentView.updateLayout();
},
prepareToHide: function()
{
this.contentView.visible = false;
this.contentView.hidden();
this._saveScrollPositions();
},
// Private
_restoreFromCookie: function()
{
this._restoreScrollPositions();
if (this._restoreCallback && typeof this._restoreCallback === "function")
this._restoreCallback.call(null, this.contentView, this.cookie);
},
_restoreScrollPositions: function()
{
// If no scroll positions are saved, do nothing.
if (!this._scrollPositions.length)
return;
var scrollableElements = this.contentView.scrollableElements || [];
console.assert(this._scrollPositions.length === scrollableElements.length);
for (var i = 0; i < scrollableElements.length; ++i) {
var position = this._scrollPositions[i];
var element = scrollableElements[i];
if (!element)
continue;
// Restore the top scroll position by either scrolling to the bottom or to the saved position.
element.scrollTop = position.isScrolledToBottom ? element.scrollHeight : position.scrollTop;
// Don't restore the left scroll position when scrolled to the bottom. This way the when content changes
// the user won't be left in a weird horizontal position.
element.scrollLeft = position.isScrolledToBottom ? 0 : position.scrollLeft;
}
},
_saveScrollPositions: function()
{
var scrollableElements = this.contentView.scrollableElements || [];
var scrollPositions = [];
for (var i = 0; i < scrollableElements.length; ++i) {
var element = scrollableElements[i];
if (!element)
continue;
var position = { scrollTop: element.scrollTop, scrollLeft: element.scrollLeft };
if (this.contentView.shouldKeepElementsScrolledToBottom)
position.isScrolledToBottom = element.isScrolledToBottom();
scrollPositions.push(position);
}
this._scrollPositions = scrollPositions;
}
};
......@@ -157,9 +157,9 @@ WebInspector.ContentBrowser.prototype = {
return this._contentViewContainer.showContentViewForRepresentedObject(representedObject);
},
showContentView: function(contentView)
showContentView: function(contentView, cookie, restoreCallback)
{
return this._contentViewContainer.showContentView(contentView);
return this._contentViewContainer.showContentView(contentView, cookie, restoreCallback);
},
contentViewForRepresentedObject: function(representedObject, onlyExisting)
......@@ -411,7 +411,7 @@ WebInspector.ContentBrowser.prototype = {
// Go through each of the items of the new content view and add a divider before them.
currentContentView.navigationItems.forEach(function(navigationItem, index) {
// Add dividers before items unless it's the first item and not a button.
// Add dividers before items unless it's the first item and not a button.
if (index !== 0 || navigationItem instanceof WebInspector.ButtonNavigationItem) {
var divider = new WebInspector.DividerNavigationItem;
navigationBar.insertNavigationItem(divider, insertionIndex++);
......@@ -433,7 +433,7 @@ WebInspector.ContentBrowser.prototype = {
this._findBanner.numberOfResults = null;
return;
}
this._findBanner.targetElement = currentContentView.element;
this._findBanner.numberOfResults = currentContentView.hasPerformedSearch ? currentContentView.numberOfSearchResults : null;
......
......@@ -239,6 +239,7 @@
<script src="TextEditor.js"></script>
<script src="EventHandler.js"></script>
<script src="SourceCodeTextEditor.js"></script>
<script src="BackForwardEntry.js"></script>
<script src="ContentViewContainer.js"></script>
<script src="ContentView.js"></script>
<script src="ClusterContentView.js"></script>
......
......@@ -511,7 +511,8 @@ WebInspector.openURL = function(url, frame, alwaysOpenExternally, lineNumber)
// WebInspector.Frame.resourceForURL does not check the main resource, only sub-resources. So check both.
var resource = frame.url === url ? frame.mainResource : frame.resourceForURL(url, searchChildFrames);
if (resource) {
this.resourceSidebarPanel.showSourceCode(resource, lineNumber);
var position = new WebInspector.SourceCodePosition(lineNumber, 0);
this.resourceSidebarPanel.showSourceCode(resource, position);
return;
}
......
......@@ -248,6 +248,7 @@ WebInspector.ResourceSidebarPanel.prototype = {
showSourceCode: function(sourceCode, positionToReveal, textRangeToSelect, forceUnformatted)
{
console.assert(!positionToReveal || positionToReveal instanceof WebInspector.SourceCodePosition, positionToReveal);
var representedObject = sourceCode;
if (representedObject instanceof WebInspector.Script) {
......@@ -259,16 +260,23 @@ WebInspector.ResourceSidebarPanel.prototype = {
if (representedObject instanceof WebInspector.Resource && representedObject.isMainResource())
representedObject = representedObject.parentFrame;
var contentView = WebInspector.contentBrowser.contentViewForRepresentedObject(representedObject);
var newContentView = WebInspector.contentBrowser.contentViewForRepresentedObject(representedObject);
var cookie = {lineNumber: positionToReveal.lineNumber, columnNumber: positionToReveal.columnNumber};
if (contentView instanceof WebInspector.FrameContentView)
contentView.showSourceCode(positionToReveal, textRangeToSelect, forceUnformatted);
else if (contentView instanceof WebInspector.ResourceClusterContentView)
contentView.showResponse(positionToReveal, textRangeToSelect, forceUnformatted);
else if (contentView instanceof WebInspector.ScriptContentView)
contentView.revealPosition(positionToReveal, textRangeToSelect, forceUnformatted);
var restoreCallback = function(contentView, savedCookie) {
var lineNumber = savedCookie.lineNumber;
var columnNumber = savedCookie.columnNumber;
var position = new WebInspector.SourceCodePosition(lineNumber, columnNumber);
WebInspector.contentBrowser.showContentView(contentView);
if (contentView instanceof WebInspector.FrameContentView)
contentView.showSourceCode(position)
else if (contentView instanceof WebInspector.ResourceClusterContentView)
contentView.showResponse(position)
else if (contentView instanceof WebInspector.ScriptContentView)
contentView.revealPosition(position)
};
WebInspector.contentBrowser.showContentView(newContentView, cookie, restoreCallback);
},
showSourceCodeLocation: function(sourceCodeLocation)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment