summaryrefslogtreecommitdiff
path: root/Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js')
-rw-r--r--Source/WebInspectorUI/UserInterface/Views/ContentViewContainer.js498
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");