Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/ui/View.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/ui/View.js b/third_party/WebKit/Source/devtools/front_end/ui/View.js |
| index 5863780fc30b4062abbea84c4aae1430e46358fc..1f5f639beb23a8ed280de260849909a388a9a2c7 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/ui/View.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/ui/View.js |
| @@ -3,12 +3,52 @@ |
| // found in the LICENSE file. |
| /** |
| + * @interface |
| + */ |
| +WebInspector.View = function() |
| +{ |
| +} |
| + |
| +WebInspector.View.prototype = { |
| + /** |
| + * @return {string} |
| + */ |
| + viewId: function() { }, |
| + |
| + /** |
| + * @return {string} |
| + */ |
| + title: function() { }, |
| + |
| + /** |
| + * @return {boolean} |
| + */ |
| + isCloseable: function() { }, |
| + |
| + /** |
| + * @return {boolean} |
| + */ |
| + isTransient: function() { }, |
| + |
| + /** |
| + * @return {!Promise<!Array<!WebInspector.ToolbarItem>>} |
| + */ |
| + toolbarItems: function() { }, |
| + |
| + /** |
| + * @return {!Promise<!WebInspector.Widget>} |
| + */ |
| + widget: function() { } |
| +} |
| + |
| +/** |
| * @constructor |
| * @extends {WebInspector.VBox} |
| + * @implements {WebInspector.View} |
| * @param {string} title |
| * @param {boolean=} isWebComponent |
| */ |
| -WebInspector.View = function(title, isWebComponent) |
| +WebInspector.SimpleView = function(title, isWebComponent) |
| { |
| WebInspector.VBox.call(this, isWebComponent); |
| this._title = title; |
| @@ -16,8 +56,18 @@ WebInspector.View = function(title, isWebComponent) |
| this._toolbarItems = []; |
| } |
| -WebInspector.View.prototype = { |
| +WebInspector.SimpleView.prototype = { |
| + /** |
| + * @override |
| + * @return {string} |
| + */ |
| + viewId: function() |
| + { |
| + return this._title; |
| + }, |
| + |
| /** |
| + * @override |
| * @return {string} |
| */ |
| title: function() |
| @@ -26,6 +76,50 @@ WebInspector.View.prototype = { |
| }, |
| /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isCloseable: function() |
| + { |
| + return false; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isTransient: function() |
| + { |
| + return false; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {!Promise<!Array<!WebInspector.ToolbarItem>>} |
| + */ |
| + toolbarItems: function() |
| + { |
| + return Promise.resolve(this._toolbarItems); |
|
dgozman
2016/08/05 01:32:09
this.syncToolbarItems()
|
| + }, |
| + |
| + /** |
| + * @return {!Array<!WebInspector.ToolbarItem>} |
| + */ |
| + syncToolbarItems: function() |
| + { |
| + return this._toolbarItems; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {!Promise<!WebInspector.Widget>} |
| + */ |
| + widget: function() |
| + { |
| + return /** @type {!Promise<!WebInspector.Widget>} */ (Promise.resolve(this)); |
| + }, |
| + |
| + /** |
| * @param {!WebInspector.ToolbarItem} item |
| */ |
| addToolbarItem: function(item) |
| @@ -33,15 +127,73 @@ WebInspector.View.prototype = { |
| this._toolbarItems.push(item); |
| }, |
| + __proto__: WebInspector.VBox.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.View} |
| + * @param {!Runtime.Extension} extension |
| + */ |
| +WebInspector.ProvidedView = function(extension) |
| +{ |
| + this._extension = extension; |
| +} |
| + |
| +WebInspector.ProvidedView.prototype = { |
| /** |
| - * @return {!Array<!WebInspector.ToolbarItem>} |
| + * @override |
| + * @return {string} |
| + */ |
| + viewId: function() |
| + { |
| + return this._extension.descriptor()["id"]; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {string} |
| + */ |
| + title: function() |
| + { |
| + return this._extension.title(); |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isCloseable: function() |
| + { |
| + return this._extension.descriptor()["persistence"] === "closeable"; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + isTransient: function() |
| + { |
| + return this._extension.descriptor()["persistence"] === "transient"; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @return {!Promise<!Array<!WebInspector.ToolbarItem>>} |
| */ |
| toolbarItems: function() |
| { |
| - return this._toolbarItems; |
| + return Promise.resolve([]); |
| }, |
| - __proto__: WebInspector.VBox.prototype |
| + /** |
| + * @override |
| + * @return {!Promise<!WebInspector.Widget>} |
| + */ |
| + widget: function() |
| + { |
| + return /** @type {!Promise<!WebInspector.Widget>} */ (this._extension.instance()); |
| + } |
| } |
| /** |
| @@ -51,9 +203,14 @@ WebInspector.ViewLocation = function() { } |
| WebInspector.ViewLocation.prototype = { |
| /** |
| - * @param {string} viewId |
| + * @param {!WebInspector.View} view |
| + */ |
| + appendView: function(view) { }, |
| + |
| + /** |
| + * @param {!WebInspector.View} view |
| */ |
| - showView: function(viewId) { }, |
| + showView: function(view) { }, |
| /** |
| * @return {!WebInspector.Widget} |
| @@ -92,32 +249,47 @@ WebInspector.ViewLocationResolver.prototype = { |
| */ |
| WebInspector.ViewManager = function() |
| { |
| + /** @type {!Map<string, !WebInspector.View>} */ |
| + this._views = new Map(); |
| + /** @type {!Map<string, string>} */ |
| + this._locations = new Map(); |
|
dgozman
2016/08/05 01:32:09
_locationIdByViewId
|
| + |
| + for (var extension of self.runtime.extensions("view")) { |
| + var descriptor = extension.descriptor(); |
| + this._views.set(descriptor["id"], new WebInspector.ProvidedView(extension)); |
| + this._locations.set(descriptor["id"], descriptor["location"]); |
| + } |
| } |
| WebInspector.ViewManager.prototype = { |
| + |
|
dgozman
2016/08/05 01:32:09
nit: extra blank line
|
| /** |
| * @param {string} viewId |
| */ |
| showView: function(viewId) |
| { |
| - var extensions = self.runtime.extensions("view").filter(extension => extension.descriptor()["id"] === viewId); |
| - if (!extensions.length) { |
| + var view = this._views.get(viewId); |
| + if (!view) { |
| console.error("Could not find view for id: '" + viewId + "'"); |
| return; |
| } |
| - var extension = extensions[0]; |
| - var location = extensions[0].descriptor()["location"]; |
| + var location = this._locations.get(viewId); |
| + if (!location) { |
|
dgozman
2016/08/05 01:32:09
Can just assert since _locations and _views share
|
| + console.error("Could not find location for id: '" + viewId + "'"); |
| + return; |
| + } |
| if (location === "drawer-view") |
| WebInspector.userMetrics.drawerShown(viewId); |
| + |
| var resolverExtensions = self.runtime.extensions(WebInspector.ViewLocationResolver).filter(extension => extension.descriptor()["name"] === location); |
| if (!resolverExtensions.length) |
| return; |
| var resolverExtension = resolverExtensions[0]; |
| - resolverExtension.instance().then(this._revealLocation.bind(this, viewId, location)); |
| + resolverExtension.instance().then(this._revealLocation.bind(this, view, location)); |
| }, |
| /** |
| - * @param {string} location |
| + * @param {string=} location |
| * @param {boolean=} restoreSelection |
| * @param {boolean=} enableMoreTabsButton |
| * @return {!WebInspector.TabbedViewLocation} |
| @@ -128,32 +300,164 @@ WebInspector.ViewManager.prototype = { |
| }, |
| /** |
| - * @param {string} viewId |
| + * @param {string=} location |
| + * @return {!WebInspector.ViewLocation} |
| + */ |
| + createStackLocation: function(location) |
| + { |
| + return new WebInspector.ViewManager._StackLocation(this, location); |
| + }, |
| + |
| + /** |
| + * @param {!WebInspector.View} view |
| * @param {string} location |
| * @param {!WebInspector.ViewLocationResolver} resolver |
| */ |
| - _revealLocation: function(viewId, location, resolver) |
| + _revealLocation: function(view, location, resolver) |
| { |
| var viewLocation = resolver.revealLocation(location); |
| if (viewLocation) |
| - viewLocation.showView(viewId); |
| + viewLocation.showView(view); |
| }, |
| /** |
| * @param {string} location |
| - * @return {!Array<!Runtime.Extension>} |
| + * @return {!Array<!WebInspector.View>} |
| */ |
| _viewsForLocation: function(location) |
| { |
| - return self.runtime.extensions("view").filter(extension => extension.descriptor()["location"] === location); |
| + var result = []; |
| + for (var id of this._views.keys()) { |
| + if (this._locations.get(id) === location) |
| + result.push(this._views.get(id)); |
| + } |
| + return result; |
| } |
| } |
| /** |
| * @constructor |
| + * @extends {WebInspector.VBox} |
| + * @param {!WebInspector.View} view |
| + */ |
| +WebInspector.ViewManager._ContainerWidget = function(view) |
| +{ |
| + WebInspector.VBox.call(this); |
| + this.element.classList.add("flex-auto", "view-container", "overflow-auto"); |
| + this._view = view; |
| +} |
| + |
| +WebInspector.ViewManager._ContainerWidget.prototype = { |
| + /** |
| + * @return {!Promise<!WebInspector.Widget>} |
| + */ |
| + _materialize: function() |
| + { |
| + if (this._materializePromise) |
| + return this._materializePromise; |
| + var promises = []; |
| + promises.push(this._view.toolbarItems().then(toolbarItems => { |
|
dgozman
2016/08/05 01:32:09
style: named function please
|
| + if (toolbarItems.length) { |
| + var toolbar = new WebInspector.Toolbar("", this.element); |
| + for (var item of toolbarItems) |
| + toolbar.appendToolbarItem(item); |
| + } |
| + })); |
| + promises.push(this._view.widget().then(widget => widget.show(this.element))); |
| + this._materializePromise = Promise.all(promises).then(() => this); |
| + return this._materializePromise; |
| + }, |
| + |
| + __proto__: WebInspector.VBox.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| + * @extends {WebInspector.VBox} |
| + * @param {!WebInspector.View} view |
| + */ |
| +WebInspector.ViewManager._ExpandableContainerWidget = function(view) |
| +{ |
| + WebInspector.VBox.call(this, true); |
| + this.element.classList.add("flex-none"); |
| + this.registerRequiredCSS("ui/viewContainers.css"); |
| + |
| + this._titleElement = createElementWithClass("div", "expandable-view-title"); |
| + this._titleElement.textContent = view.title(); |
| + this._titleElement.tabIndex = 0; |
| + this._titleElement.addEventListener("click", this._toggleExpanded.bind(this), false); |
| + this._titleElement.addEventListener("keydown", this._onTitleKeyDown.bind(this), false); |
| + this.contentElement.insertBefore(this._titleElement, this.contentElement.firstChild); |
| + |
| + this.contentElement.createChild("content"); |
| + this._view = view; |
| +} |
| + |
| +WebInspector.ViewManager._ExpandableContainerWidget.prototype = { |
| + /** |
| + * @return {!Promise<!WebInspector.Widget>} |
| + */ |
| + _materialize: function() |
|
dgozman
2016/08/05 01:32:09
All this boilerplate code...
|
| + { |
| + if (this._materializePromise) |
| + return this._materializePromise; |
| + var promises = []; |
| + promises.push(this._view.toolbarItems().then(toolbarItems => { |
| + if (toolbarItems.length) { |
| + var toolbar = new WebInspector.Toolbar("", this._titleElement); |
| + for (var item of toolbarItems) |
| + toolbar.appendToolbarItem(item); |
| + } |
| + })); |
| + promises.push(this._view.widget().then(widget => { |
| + this._widget = widget; |
| + widget.show(this.element); |
| + })); |
| + this._materializePromise = Promise.all(promises).then(() => this); |
| + return this._materializePromise; |
| + }, |
| + |
| + _expand: function() |
| + { |
| + if (this._titleElement.classList.contains("expanded")) |
| + return; |
| + this._titleElement.classList.add("expanded"); |
| + this._materialize().then(() => this._widget.show(this.element)); |
| + }, |
| + |
| + _collapse: function() |
| + { |
| + if (!this._titleElement.classList.contains("expanded")) |
| + return; |
| + this._titleElement.classList.remove("expanded"); |
| + this._materialize().then(() => this._widget.detach()); |
| + }, |
| + |
| + _toggleExpanded: function() |
| + { |
| + if (this._titleElement.classList.contains("expanded")) |
| + this._collapse(); |
| + else |
| + this._expand(); |
| + }, |
| + |
| + /** |
| + * @param {!Event} event |
| + */ |
| + _onTitleKeyDown: function(event) |
| + { |
| + if (isEnterKey(event) || event.keyCode === WebInspector.KeyboardShortcut.Keys.Space.code) |
| + this._toggleExpanded(); |
| + }, |
| + |
| + __proto__: WebInspector.VBox.prototype |
| +} |
| + |
| +/** |
| + * @constructor |
| * @implements {WebInspector.TabbedViewLocation} |
| * @param {!WebInspector.ViewManager} manager |
| - * @param {string} location |
| + * @param {string=} location |
| * @param {boolean=} restoreSelection |
| * @param {boolean=} enableMoreTabsButton |
| */ |
| @@ -162,7 +466,7 @@ WebInspector.ViewManager._TabbedLocation = function(manager, location, restoreSe |
| this._manager = manager; |
| this._tabbedPane = new WebInspector.TabbedPane(); |
| this._location = location; |
| - /** @type {!Object.<string, !Promise.<?WebInspector.Widget>>} */ |
| + /** @type {!Object.<string, !Promise>} */ |
| this._promiseForId = {}; |
| this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); |
| @@ -170,7 +474,11 @@ WebInspector.ViewManager._TabbedLocation = function(manager, location, restoreSe |
| this._closeableTabSetting = WebInspector.settings.createSetting(location + "-closeableTabs", {}); |
| if (restoreSelection) |
| this._lastSelectedTabSetting = WebInspector.settings.createSetting(location + "-selectedTab", ""); |
| - this._initialize(); |
| + |
| + /** @type {!Map.<string, !WebInspector.View>} */ |
| + this._views = new Map(); |
| + this._populateLocation(); |
| + |
| if (enableMoreTabsButton) { |
| var toolbar = new WebInspector.Toolbar("drawer-toolbar"); |
| toolbar.appendToolbarItem(new WebInspector.ToolbarMenuButton(this._appendTabsToMenu.bind(this))); |
| @@ -198,19 +506,19 @@ WebInspector.ViewManager._TabbedLocation.prototype = { |
| return this._tabbedPane; |
| }, |
| - _initialize: function() |
| + _populateLocation: function() |
| { |
| - /** @type {!Map.<string, !Runtime.Extension>} */ |
| - this._extensions = new Map(); |
| - var extensions = this._manager._viewsForLocation(this._location); |
| - |
| - for (var i = 0; i < extensions.length; ++i) { |
| - var id = extensions[i].descriptor()["id"]; |
| - this._extensions.set(id, extensions[i]); |
| - if (this._isPermanentTab(id)) |
| - this._appendTab(extensions[i]); |
| - else if (this._isCloseableTab(id) && this._closeableTabSetting.get()[id]) |
| - this._appendTab(extensions[i]); |
| + if (!this._location) |
| + return; |
| + for (var view of this._manager._viewsForLocation(this._location)) { |
| + var id = view.viewId(); |
| + this._views.set(id, view); |
| + if (view.isTransient()) |
| + continue; |
| + if (!view.isCloseable()) |
| + this._appendTab(view); |
| + else if (this._closeableTabSetting.get()[id]) |
| + this._appendTab(view); |
| } |
| }, |
| @@ -224,59 +532,46 @@ WebInspector.ViewManager._TabbedLocation.prototype = { |
| }, |
| /** |
| - * @param {string} id |
| - * @return {boolean} |
| + * @param {!WebInspector.ContextMenu} contextMenu |
| */ |
| - _isPermanentTab: function(id) |
| + _appendTabsToMenu: function(contextMenu) |
| { |
| - return this._extensions.get(id).descriptor()["persistence"] === "permanent" || !this._extensions.get(id).descriptor()["persistence"]; |
| + for (var view of this._views.values()) { |
| + var title = WebInspector.UIString(view.title()); |
| + contextMenu.appendItem(title, this.showView.bind(this, view)); |
| + } |
| }, |
| /** |
| - * @param {string} id |
| - * @return {boolean} |
| + * @param {!WebInspector.View} view |
| */ |
| - _isCloseableTab: function(id) |
| + _appendTab: function(view) |
| { |
| - return this._extensions.get(id).descriptor()["persistence"] === "closeable"; |
| + this._tabbedPane.appendTab(view.viewId(), view.title(), new WebInspector.Widget(), undefined, false, view.isCloseable() || view.isTransient()); |
| }, |
| /** |
| - * @param {!WebInspector.ContextMenu} contextMenu |
| + * @override |
| + * @param {!WebInspector.View} view |
| */ |
| - _appendTabsToMenu: function(contextMenu) |
| + appendView: function(view) |
| { |
| - var extensions = self.runtime.extensions("view", undefined, true); |
| - for (var extension of extensions) { |
| - if (extension.descriptor()["location"] !== this._location) |
| - continue; |
| - var title = WebInspector.UIString(extension.title()); |
| - contextMenu.appendItem(title, this.showView.bind(this, extension.descriptor()["id"])); |
| + if (!this._tabbedPane.hasTab(view.viewId())) { |
|
dgozman
2016/08/05 01:32:09
Who calls it twice for the same id?
|
| + this._views.set(view.viewId(), view); |
| + this._appendTab(view); |
| } |
| }, |
| /** |
| - * @param {!Runtime.Extension} extension |
| - */ |
| - _appendTab: function(extension) |
| - { |
| - var descriptor = extension.descriptor(); |
| - var id = descriptor["id"]; |
| - var title = WebInspector.UIString(extension.title()); |
| - var closeable = descriptor["persistence"] === "closeable" || descriptor["persistence"] === "temporary"; |
| - this._tabbedPane.appendTab(id, title, new WebInspector.Widget(), undefined, false, closeable); |
| - }, |
| - |
| - /** |
| * @override |
| - * @param {string} id |
| + * @param {!WebInspector.View} view |
| */ |
| - showView: function(id) |
| + showView: function(view) |
| { |
| - if (!this._tabbedPane.hasTab(id)) |
| - this._appendTab(/** @type {!Runtime.Extension} */(this._extensions.get(id))); |
| + if (!this._tabbedPane.hasTab(view.viewId())) |
| + this._appendTab(view); |
| this._tabbedPane.focus(); |
| - this._tabbedPane.selectTab(id); |
| + this._tabbedPane.selectTab(view.viewId()); |
| }, |
| /** |
| @@ -287,13 +582,13 @@ WebInspector.ViewManager._TabbedLocation.prototype = { |
| var tabId = /** @type {string} */ (event.data.tabId); |
| if (this._lastSelectedTabSetting && event.data["isUserGesture"]) |
| this._lastSelectedTabSetting.set(tabId); |
| - if (!this._extensions.has(tabId)) |
| + var view = this._views.get(tabId); |
| + if (!view) |
| return; |
| - this._viewForId(tabId); |
| + this._materializeWidget(view); |
| - var descriptor = this._extensions.get(tabId).descriptor(); |
| - if (descriptor["persistence"] === "closeable") { |
| + if (view.isCloseable()) { |
| var tabs = this._closeableTabSetting.get(); |
| if (!tabs[tabId]) { |
| tabs[tabId] = true; |
| @@ -317,29 +612,82 @@ WebInspector.ViewManager._TabbedLocation.prototype = { |
| }, |
| /** |
| - * @param {string} id |
| - * @return {!Promise.<?WebInspector.Widget>} |
| + * @param {!WebInspector.View} view |
| */ |
| - _viewForId: function(id) |
| + _materializeWidget: function(view) |
| { |
| - if (this._promiseForId[id]) |
| - return this._promiseForId[id]; |
| + if (this._promiseForId[view.viewId()]) |
| + return; |
| - var promise = this._extensions.get(id).instance(); |
| - this._promiseForId[id] = /** @type {!Promise.<?WebInspector.Widget>} */ (promise); |
| - return promise.then(cacheView.bind(this)); |
| + var widget = new WebInspector.ViewManager._ContainerWidget(view); |
| + var promise = widget._materialize().then(() => { |
| + this._tabbedPane.changeTabView(view.viewId(), widget); |
| + }); |
| + this._promiseForId[view.viewId()] = promise; |
| + } |
| +} |
| - /** |
| - * @param {!Object} object |
| - * @this {WebInspector.ViewManager._TabbedLocation} |
| - */ |
| - function cacheView(object) |
| - { |
| - var view = /** @type {!WebInspector.Widget} */ (object); |
| - this._tabbedPane.changeTabView(id, view); |
| - return view; |
| +/** |
| + * @constructor |
| + * @implements {WebInspector.ViewLocation} |
| + * @param {!WebInspector.ViewManager} manager |
| + * @param {string=} location |
| + */ |
| +WebInspector.ViewManager._StackLocation = function(manager, location) |
| +{ |
| + this._manager = manager; |
| + this._location = location; |
| + this._vbox = new WebInspector.VBox(); |
| + /** @type {!Map<string, !WebInspector.ViewManager._ExpandableContainerWidget>} */ |
| + this._expandableContainers = new Map(); |
| + this._populateLocation(); |
| +} |
| + |
| +WebInspector.ViewManager._StackLocation.prototype = { |
| + /** |
| + * @override |
| + * @return {!WebInspector.Widget} |
| + */ |
| + widget: function() |
| + { |
| + return this._vbox; |
| + }, |
| + |
| + /** |
| + * @override |
| + * @param {!WebInspector.View} view |
| + */ |
| + appendView: function(view) |
| + { |
| + var container = this._expandableContainers.get(view.viewId()); |
| + if (!container) { |
|
dgozman
2016/08/05 01:32:09
Let's move this if to showView, and don't call app
pfeldman
2016/08/05 04:42:16
We need it for appendView.
|
| + container = new WebInspector.ViewManager._ExpandableContainerWidget(view); |
| + container.show(this._vbox.contentElement); |
| + this._expandableContainers.set(view.viewId(), container); |
| } |
| + }, |
| + |
| + /** |
| + * @override |
| + * @param {!WebInspector.View} view |
| + */ |
| + showView: function(view) |
| + { |
| + this.appendView(view); |
| + var container = this._expandableContainers.get(view.viewId()); |
| + container._expand(); |
| + }, |
| + |
| + _populateLocation: function() |
| + { |
| + if (!this._location) |
| + return; |
| + for (var view of this._manager._viewsForLocation(this._location)) |
| + this.appendView(view); |
| } |
| } |
| -WebInspector.viewManager = new WebInspector.ViewManager(); |
| +/** |
| + * @type {!WebInspector.ViewManager} |
| + */ |
| +WebInspector.viewManager; |