diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js b/Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js new file mode 100644 index 000000000..7d9865d88 --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2013, 2015 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.ContentViewContainer = class ContentViewContainer extends WebInspector.View +{ + constructor() + { + super(); + + this.element.classList.add("content-view-container"); + + this._backForwardList = []; + this._currentIndex = -1; + } + + // Public + + get currentIndex() + { + return this._currentIndex; + } + + get backForwardList() + { + return this._backForwardList; + } + + get currentContentView() + { + if (this._currentIndex < 0 || this._currentIndex > this._backForwardList.length - 1) + return null; + return this._backForwardList[this._currentIndex].contentView; + } + + get currentBackForwardEntry() + { + if (this._currentIndex < 0 || this._currentIndex > this._backForwardList.length - 1) + return null; + return this._backForwardList[this._currentIndex]; + } + + contentViewForRepresentedObject(representedObject, onlyExisting, extraArguments) + { + return WebInspector.ContentView.contentViewForRepresentedObject(representedObject, onlyExisting, extraArguments); + } + + showContentViewForRepresentedObject(representedObject, extraArguments) + { + var contentView = this.contentViewForRepresentedObject(representedObject, false, extraArguments); + if (!contentView) + return null; + + this.showContentView(contentView); + + return contentView; + } + + showContentView(contentView, cookie) + { + console.assert(contentView instanceof WebInspector.ContentView); + if (!(contentView instanceof WebInspector.ContentView)) + return null; + + // ContentViews can be shared between containers. If this content view is + // not owned by us, it may need to be transferred to this container. + if (contentView.parentContainer !== this) + this._takeOwnershipOfContentView(contentView); + + let currentEntry = this.currentBackForwardEntry; + + // Try to find the last entry with the same content view so we can copy it + // to preserve the last scroll positions. The supplied cookie (if any) could + // still change the scroll position afterwards, but in most cases the cookie + // is undefined, so we want to show with a state last used. + let provisionalEntry = null; + for (let i = this._backForwardList.length - 1; i >= 0; --i) { + if (this._backForwardList[i].contentView === contentView) { + provisionalEntry = this._backForwardList[i].makeCopy(cookie); + break; + } + } + + if (!provisionalEntry) + provisionalEntry = new WebInspector.BackForwardEntry(contentView, cookie); + + // Don't do anything if we would have added an identical back/forward list entry. + if (provisionalEntry.isEqual(currentEntry)) { + const shouldCallShown = false; + currentEntry.prepareToShow(shouldCallShown); + return currentEntry.contentView; + } + + // Showing a content view will truncate the back/forward list after the current index and insert the content view + // at the end of the list. Finally, the current index will be updated to point to the end of the back/forward list. + + // Increment the current index to where we will insert the content view. + let newIndex = this._currentIndex + 1; + + // Insert the content view at the new index. This will remove any content views greater than or equal to the index. + let removedEntries = this._backForwardList.splice(newIndex, this._backForwardList.length - newIndex, provisionalEntry); + + console.assert(newIndex === this._backForwardList.length - 1); + console.assert(this._backForwardList[newIndex] === provisionalEntry); + + // Disassociate with the removed content views. + for (let i = 0; i < removedEntries.length; ++i) { + // Skip disassociation if this content view is still in the back/forward list. + let shouldDissociateContentView = !this._backForwardList.some((existingEntry) => existingEntry.contentView === removedEntries[i].contentView); + if (shouldDissociateContentView) + this._disassociateFromContentView(removedEntries[i].contentView, removedEntries[i].tombstone); + } + + // Associate with the new content view. + contentView._parentContainer = this; + + this.showBackForwardEntryForIndex(newIndex); + + return contentView; + } + + showBackForwardEntryForIndex(index) + { + console.assert(index >= 0 && index <= this._backForwardList.length - 1); + if (index < 0 || index > this._backForwardList.length - 1) + return; + + if (this._currentIndex === index) + return; + + var previousEntry = this.currentBackForwardEntry; + this._currentIndex = index; + var currentEntry = this.currentBackForwardEntry; + console.assert(currentEntry); + + var isNewContentView = !previousEntry || !currentEntry.contentView.visible; + if (isNewContentView) { + // Hide the currently visible content view. + if (previousEntry) + this._hideEntry(previousEntry); + this._showEntry(currentEntry, true); + } else + this._showEntry(currentEntry, false); + + this.dispatchEventToListeners(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange); + } + + replaceContentView(oldContentView, newContentView) + { + console.assert(oldContentView instanceof WebInspector.ContentView); + if (!(oldContentView instanceof WebInspector.ContentView)) + return; + + console.assert(newContentView instanceof WebInspector.ContentView); + if (!(newContentView instanceof WebInspector.ContentView)) + return; + + console.assert(oldContentView.parentContainer === this); + if (oldContentView.parentContainer !== this) + return; + + console.assert(!newContentView.parentContainer || newContentView.parentContainer === this); + if (newContentView.parentContainer && newContentView.parentContainer !== this) + return; + + var currentlyShowing = this.currentContentView === oldContentView; + if (currentlyShowing) + this._hideEntry(this.currentBackForwardEntry); + + // Disassociate with the old content view. + this._disassociateFromContentView(oldContentView, false); + + // Associate with the new content view. + newContentView._parentContainer = this; + + // Replace all occurrences of oldContentView with newContentView in the back/forward list. + for (var i = 0; i < this._backForwardList.length; ++i) { + if (this._backForwardList[i].contentView === oldContentView) { + console.assert(!this._backForwardList[i].tombstone); + let currentCookie = this._backForwardList[i].cookie; + this._backForwardList[i] = new WebInspector.BackForwardEntry(newContentView, currentCookie); + } + } + + this._removeIdenticalAdjacentBackForwardEntries(); + + // Re-show the current entry, because its content view instance was replaced. + if (currentlyShowing) { + this._showEntry(this.currentBackForwardEntry, true); + this.dispatchEventToListeners(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange); + } + } + + closeContentView(contentViewToClose) + { + if (!this._backForwardList.length) { + console.assert(this._currentIndex === -1); + return; + } + + // Do a check to see if all the content views are instances of this prototype. + // If they all are we can use the quicker closeAllContentViews method. + var allSameContentView = true; + for (var i = this._backForwardList.length - 1; i >= 0; --i) { + if (this._backForwardList[i].contentView !== contentViewToClose) { + allSameContentView = false; + break; + } + } + + if (allSameContentView) { + this.closeAllContentViews(); + return; + } + + var visibleContentView = this.currentContentView; + var backForwardListDidChange = false; + // Hide and disassociate with all the content views that are the same as contentViewToClose. + for (var i = this._backForwardList.length - 1; i >= 0; --i) { + var entry = this._backForwardList[i]; + if (entry.contentView !== contentViewToClose) + continue; + + if (entry.contentView === visibleContentView) + this._hideEntry(entry); + + if (this._currentIndex >= i) { + // Decrement the currentIndex since we will remove an item in the back/forward array + // that is the current index or comes before it. + --this._currentIndex; + } + + this._disassociateFromContentView(entry.contentView, entry.tombstone); + + // Remove the item from the back/forward list. + this._backForwardList.splice(i, 1); + backForwardListDidChange = true; + } + + if (backForwardListDidChange) + this._removeIdenticalAdjacentBackForwardEntries(); + + var currentEntry = this.currentBackForwardEntry; + console.assert(currentEntry || (!currentEntry && this._currentIndex === -1)); + + if (currentEntry && currentEntry.contentView !== visibleContentView || backForwardListDidChange) { + this._showEntry(currentEntry, true); + this.dispatchEventToListeners(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange); + } + } + + closeAllContentViews() + { + if (!this._backForwardList.length) { + console.assert(this._currentIndex === -1); + return; + } + + var visibleContentView = this.currentContentView; + + // Hide and disassociate with all the content views. + for (var i = 0; i < this._backForwardList.length; ++i) { + var entry = this._backForwardList[i]; + if (entry.contentView === visibleContentView) + this._hideEntry(entry); + this._disassociateFromContentView(entry.contentView, entry.tombstone); + } + + this._backForwardList = []; + this._currentIndex = -1; + + this.dispatchEventToListeners(WebInspector.ContentViewContainer.Event.CurrentContentViewDidChange); + } + + canGoBack() + { + return this._currentIndex > 0; + } + + canGoForward() + { + return this._currentIndex < this._backForwardList.length - 1; + } + + goBack() + { + if (!this.canGoBack()) + return; + this.showBackForwardEntryForIndex(this._currentIndex - 1); + } + + goForward() + { + if (!this.canGoForward()) + return; + this.showBackForwardEntryForIndex(this._currentIndex + 1); + } + + shown() + { + var currentEntry = this.currentBackForwardEntry; + if (!currentEntry) + return; + + this._showEntry(currentEntry, true); + } + + hidden() + { + var currentEntry = this.currentBackForwardEntry; + if (!currentEntry) + return; + + this._hideEntry(currentEntry); + } + + // Private + + _takeOwnershipOfContentView(contentView) + { + console.assert(contentView.parentContainer !== this, "We already have ownership of the ContentView"); + if (contentView.parentContainer === this) + return; + + if (contentView.parentContainer) + contentView.parentContainer._placeTombstonesForContentView(contentView); + + contentView._parentContainer = this; + + this._clearTombstonesForContentView(contentView); + } + + _placeTombstonesForContentView(contentView) + { + console.assert(contentView.parentContainer === this); + + // Ensure another ContentViewContainer doesn't close this ContentView while we still have it. + let tombstoneContentViewContainers = this._tombstoneContentViewContainersForContentView(contentView); + console.assert(!tombstoneContentViewContainers.includes(this)); + + let visibleContentView = this.currentContentView; + + for (let entry of this._backForwardList) { + if (entry.contentView !== contentView) + continue; + + if (entry.contentView === visibleContentView) { + this._hideEntry(entry); + visibleContentView = null; + } + + console.assert(!entry.tombstone); + entry.tombstone = true; + + tombstoneContentViewContainers.push(this); + } + } + + _clearTombstonesForContentView(contentView) + { + console.assert(contentView.parentContainer === this); + + let tombstoneContentViewContainers = this._tombstoneContentViewContainersForContentView(contentView); + const onlyFirst = false; + tombstoneContentViewContainers.remove(this, onlyFirst); + + for (let entry of this._backForwardList) { + if (entry.contentView !== contentView) + continue; + + console.assert(entry.tombstone); + entry.tombstone = false; + } + } + + _disassociateFromContentView(contentView, isTombstone) + { + // Just remove one of our tombstone back references. + // There may be other back/forward entries that need a reference. + if (isTombstone) { + let tombstoneContentViewContainers = this._tombstoneContentViewContainersForContentView(contentView); + const onlyFirst = true; + tombstoneContentViewContainers.remove(this, onlyFirst); + return; + } + + console.assert(!contentView.visible); + + if (!contentView._parentContainer) + return; + + contentView._parentContainer = null; + + // If another ContentViewContainer has tombstones for this, just transfer + // ownership to that ContentViewContainer and avoid closing the ContentView. + // We don't care who we transfer this to, so just use the first. + let tombstoneContentViewContainers = this._tombstoneContentViewContainersForContentView(contentView); + if (tombstoneContentViewContainers && tombstoneContentViewContainers.length) { + tombstoneContentViewContainers[0]._takeOwnershipOfContentView(contentView); + return; + } + + contentView.closed(); + + if (contentView.representedObject) + WebInspector.ContentView.closedContentViewForRepresentedObject(contentView.representedObject); + } + + _showEntry(entry, shouldCallShown) + { + console.assert(entry instanceof WebInspector.BackForwardEntry); + + // We may be showing a tombstone from a BackForward list or when re-showing a container + // that had previously had the content view transferred away from it. + // Take over the ContentView. + if (entry.tombstone) { + this._takeOwnershipOfContentView(entry.contentView); + console.assert(!entry.tombstone); + } + + if (!this.subviews.includes(entry.contentView)) + this.addSubview(entry.contentView); + + entry.prepareToShow(shouldCallShown); + } + + _hideEntry(entry) + { + console.assert(entry instanceof WebInspector.BackForwardEntry); + + // If this was a tombstone, the content view should already have been + // hidden when we placed the tombstone. + if (entry.tombstone) + return; + + entry.prepareToHide(); + if (this.subviews.includes(entry.contentView)) + this.removeSubview(entry.contentView); + } + + _tombstoneContentViewContainersForContentView(contentView) + { + let tombstoneContentViewContainers = contentView[WebInspector.ContentViewContainer.TombstoneContentViewContainersSymbol]; + if (!tombstoneContentViewContainers) + tombstoneContentViewContainers = contentView[WebInspector.ContentViewContainer.TombstoneContentViewContainersSymbol] = []; + return tombstoneContentViewContainers; + } + + _removeIdenticalAdjacentBackForwardEntries() + { + if (this._backForwardList.length < 2) + return; + + let previousEntry; + for (let i = this._backForwardList.length - 1; i >= 0; --i) { + let entry = this._backForwardList[i]; + if (!entry.isEqual(previousEntry)) { + previousEntry = entry; + continue; + } + + if (this._currentIndex >= i) { + // Decrement the currentIndex since we will remove an item in the back/forward array + // that is the current index or comes before it. + --this._currentIndex; + } + + this._backForwardList.splice(i, 1); + } + } +}; + +WebInspector.ContentViewContainer.Event = { + CurrentContentViewDidChange: "content-view-container-current-content-view-did-change" +}; + +WebInspector.ContentViewContainer.TombstoneContentViewContainersSymbol = Symbol("content-view-container-tombstone-content-view-containers"); |