Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2012 Adobe Systems Incorporated. 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
      9  *    copyright notice, this list of conditions and the following
     10  *    disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above
     12  *    copyright notice, this list of conditions and the following
     13  *    disclaimer in the documentation and/or other materials
     14  *    provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     20  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
     21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
     27  * OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 /**
     31  * @constructor
     32  * @extends {WebInspector.SidebarView}
     33  */
     34 WebInspector.CSSNamedFlowCollectionsView = function()
     35 {
     36     WebInspector.SidebarView.call(this, WebInspector.SidebarView.SidebarPosition.Start);
     37     this.registerRequiredCSS("cssNamedFlows.css");
     38 
     39     this._namedFlows = {};
     40     this._contentNodes = {};
     41     this._regionNodes = {};
     42 
     43     this.element.classList.add("css-named-flow-collections-view");
     44     this.element.classList.add("fill");
     45 
     46     this._statusElement = document.createElement("span");
     47     this._statusElement.textContent = WebInspector.UIString("CSS Named Flows");
     48 
     49     var sidebarHeader = this.firstElement().createChild("div", "tabbed-pane-header selected sidebar-header");
     50     var tab = sidebarHeader.createChild("div", "tabbed-pane-header-tab");
     51     tab.createChild("span", "tabbed-pane-header-tab-title").textContent = WebInspector.UIString("CSS Named Flows");
     52 
     53     this._sidebarContentElement = this.firstElement().createChild("div", "sidebar-content outline-disclosure");
     54     this._flowListElement = this._sidebarContentElement.createChild("ol");
     55     this._flowTree = new TreeOutline(this._flowListElement);
     56 
     57     this._emptyElement = document.createElement("div");
     58     this._emptyElement.classList.add("info");
     59     this._emptyElement.textContent = WebInspector.UIString("No CSS Named Flows");
     60 
     61     this._tabbedPane = new WebInspector.TabbedPane();
     62     this._tabbedPane.closeableTabs = true;
     63     this._tabbedPane.show(this.secondElement());
     64 }
     65 
     66 WebInspector.CSSNamedFlowCollectionsView.prototype = {
     67     showInDrawer: function()
     68     {
     69         WebInspector.inspectorView.showCloseableViewInDrawer("css-flows", WebInspector.UIString("CSS Flows"), this);
     70     },
     71 
     72     reset: function()
     73     {
     74         if (!this._document)
     75             return;
     76 
     77         WebInspector.cssModel.getNamedFlowCollectionAsync(this._document.id, this._resetNamedFlows.bind(this));
     78     },
     79 
     80     /**
     81      * @param {!WebInspector.DOMDocument} document
     82      */
     83     _setDocument: function(document)
     84     {
     85         this._document = document;
     86         this.reset();
     87     },
     88 
     89     /**
     90      * @param {!WebInspector.Event} event
     91      */
     92     _documentUpdated: function(event)
     93     {
     94         var document = /** @type {!WebInspector.DOMDocument} */ (event.data);
     95         this._setDocument(document);
     96     },
     97 
     98     /**
     99      * @param {boolean} hasContent
    100      */
    101     _setSidebarHasContent: function(hasContent)
    102     {
    103         if (hasContent) {
    104             if (!this._emptyElement.parentNode)
    105                 return;
    106 
    107             this._sidebarContentElement.removeChild(this._emptyElement);
    108             this._sidebarContentElement.appendChild(this._flowListElement);
    109         } else {
    110             if (!this._flowListElement.parentNode)
    111                 return;
    112 
    113             this._sidebarContentElement.removeChild(this._flowListElement);
    114             this._sidebarContentElement.appendChild(this._emptyElement);
    115         }
    116     },
    117 
    118     /**
    119      * @param {!WebInspector.NamedFlow} flow
    120      */
    121     _appendNamedFlow: function(flow)
    122     {
    123         var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name);
    124         var flowContainer = { flow: flow, flowHash: flowHash };
    125 
    126         for (var i = 0; i < flow.content.length; ++i)
    127             this._contentNodes[flow.content[i]] = flowHash;
    128         for (var i = 0; i < flow.regions.length; ++i)
    129             this._regionNodes[flow.regions[i].nodeId] = flowHash;
    130 
    131         var flowTreeItem = new WebInspector.FlowTreeElement(flowContainer);
    132         flowTreeItem.onselect = this._selectNamedFlowTab.bind(this, flowHash);
    133 
    134         flowContainer.flowTreeItem = flowTreeItem;
    135         this._namedFlows[flowHash] = flowContainer;
    136 
    137         if (!this._flowTree.children.length)
    138             this._setSidebarHasContent(true);
    139         this._flowTree.appendChild(flowTreeItem);
    140     },
    141 
    142     /**
    143      * @param {string} flowHash
    144      */
    145     _removeNamedFlow: function(flowHash)
    146     {
    147         var flowContainer = this._namedFlows[flowHash];
    148 
    149         if (this._tabbedPane._tabsById[flowHash])
    150             this._tabbedPane.closeTab(flowHash);
    151         this._flowTree.removeChild(flowContainer.flowTreeItem);
    152 
    153         var flow = flowContainer.flow;
    154         for (var i = 0; i < flow.content.length; ++i)
    155             delete this._contentNodes[flow.content[i]];
    156         for (var i = 0; i < flow.regions.length; ++i)
    157             delete this._regionNodes[flow.regions[i].nodeId];
    158 
    159         delete this._namedFlows[flowHash];
    160 
    161         if (!this._flowTree.children.length)
    162             this._setSidebarHasContent(false);
    163     },
    164 
    165     /**
    166      * @param {!WebInspector.NamedFlow} flow
    167      */
    168     _updateNamedFlow: function(flow)
    169     {
    170         var flowHash = this._hashNamedFlow(flow.documentNodeId, flow.name);
    171         var flowContainer = this._namedFlows[flowHash];
    172 
    173         if (!flowContainer)
    174             return;
    175 
    176         var oldFlow = flowContainer.flow;
    177         flowContainer.flow = flow;
    178 
    179         for (var i = 0; i < oldFlow.content.length; ++i)
    180             delete this._contentNodes[oldFlow.content[i]];
    181         for (var i = 0; i < oldFlow.regions.length; ++i)
    182             delete this._regionNodes[oldFlow.regions[i].nodeId];
    183 
    184         for (var i = 0; i < flow.content.length; ++i)
    185             this._contentNodes[flow.content[i]] = flowHash;
    186         for (var i = 0; i < flow.regions.length; ++i)
    187             this._regionNodes[flow.regions[i].nodeId] = flowHash;
    188 
    189         flowContainer.flowTreeItem.setOverset(flow.overset);
    190 
    191         if (flowContainer.flowView)
    192             flowContainer.flowView.flow = flow;
    193     },
    194 
    195     /**
    196      * @param {?WebInspector.NamedFlowCollection} namedFlowCollection
    197      */
    198     _resetNamedFlows: function(namedFlowCollection)
    199     {
    200         for (var flowHash in this._namedFlows)
    201             this._removeNamedFlow(flowHash);
    202 
    203         var namedFlows = namedFlowCollection ? namedFlowCollection.namedFlowMap : {};
    204         for (var flowName in namedFlows)
    205             this._appendNamedFlow(namedFlows[flowName]);
    206 
    207         if (!this._flowTree.children.length)
    208             this._setSidebarHasContent(false);
    209         else
    210             this._showNamedFlowForNode(WebInspector.panel("elements").treeOutline.selectedDOMNode());
    211     },
    212 
    213     /**
    214      * @param {!WebInspector.Event} event
    215      */
    216     _namedFlowCreated: function(event)
    217     {
    218         // FIXME: We only have support for Named Flows in the main document.
    219         if (event.data.documentNodeId !== this._document.id)
    220             return;
    221 
    222         var flow = /** @type {!WebInspector.NamedFlow} */ (event.data);
    223         this._appendNamedFlow(flow);
    224     },
    225 
    226     /**
    227      * @param {!WebInspector.Event} event
    228      */
    229     _namedFlowRemoved: function(event)
    230     {
    231         // FIXME: We only have support for Named Flows in the main document.
    232         if (event.data.documentNodeId !== this._document.id)
    233             return;
    234 
    235         this._removeNamedFlow(this._hashNamedFlow(event.data.documentNodeId, event.data.flowName));
    236     },
    237 
    238     /**
    239      * @param {!WebInspector.Event} event
    240      */
    241     _regionLayoutUpdated: function(event)
    242     {
    243         // FIXME: We only have support for Named Flows in the main document.
    244         if (event.data.documentNodeId !== this._document.id)
    245             return;
    246 
    247         var flow = /** @type {!WebInspector.NamedFlow} */ (event.data);
    248         this._updateNamedFlow(flow);
    249     },
    250 
    251     /**
    252      * @param {!WebInspector.Event} event
    253      */
    254     _regionOversetChanged: function(event)
    255     {
    256         // FIXME: We only have support for Named Flows in the main document.
    257         if (event.data.documentNodeId !== this._document.id)
    258             return;
    259 
    260         var flow = /** @type {!WebInspector.NamedFlow} */ (event.data);
    261         this._updateNamedFlow(flow);
    262     },
    263 
    264     /**
    265      * @param {!DOMAgent.NodeId} documentNodeId
    266      * @param {string} flowName
    267      */
    268     _hashNamedFlow: function(documentNodeId, flowName)
    269     {
    270         return documentNodeId + "|" + flowName;
    271     },
    272 
    273     /**
    274      * @param {string} flowHash
    275      */
    276     _showNamedFlow: function(flowHash)
    277     {
    278         this._selectNamedFlowInSidebar(flowHash);
    279         this._selectNamedFlowTab(flowHash);
    280     },
    281 
    282     /**
    283      * @param {string} flowHash
    284      */
    285     _selectNamedFlowInSidebar: function(flowHash)
    286     {
    287         this._namedFlows[flowHash].flowTreeItem.select(true);
    288     },
    289 
    290     /**
    291      * @param {string} flowHash
    292      * @return {boolean}
    293      */
    294     _selectNamedFlowTab: function(flowHash)
    295     {
    296         var flowContainer = this._namedFlows[flowHash];
    297 
    298         if (this._tabbedPane.selectedTabId === flowHash)
    299             return false;
    300 
    301         if (!this._tabbedPane.selectTab(flowHash)) {
    302             if (!flowContainer.flowView)
    303                 flowContainer.flowView = new WebInspector.CSSNamedFlowView(flowContainer.flow);
    304 
    305             this._tabbedPane.appendTab(flowHash, flowContainer.flow.name, flowContainer.flowView);
    306             this._tabbedPane.selectTab(flowHash);
    307         }
    308         return false;
    309     },
    310 
    311     /**
    312      * @param {!WebInspector.Event} event
    313      */
    314     _selectedNodeChanged: function(event)
    315     {
    316         var node = /** @type {!WebInspector.DOMNode} */ (event.data);
    317         this._showNamedFlowForNode(node);
    318     },
    319 
    320     /**
    321      * @param {!WebInspector.Event} event
    322      */
    323     _tabSelected: function(event)
    324     {
    325         this._selectNamedFlowInSidebar(event.data.tabId);
    326     },
    327 
    328     /**
    329      * @param {!WebInspector.Event} event
    330      */
    331     _tabClosed: function(event)
    332     {
    333         this._namedFlows[event.data.tabId].flowTreeItem.deselect();
    334     },
    335 
    336     /**
    337      * @param {?WebInspector.DOMNode} node
    338      */
    339     _showNamedFlowForNode: function(node)
    340     {
    341         if (!node)
    342             return;
    343 
    344         if (this._regionNodes[node.id]) {
    345             this._showNamedFlow(this._regionNodes[node.id]);
    346             return;
    347         }
    348 
    349         while (node) {
    350             if (this._contentNodes[node.id]) {
    351                 this._showNamedFlow(this._contentNodes[node.id]);
    352                 return;
    353             }
    354 
    355             node = node.parentNode;
    356         }
    357     },
    358 
    359     wasShown: function()
    360     {
    361         WebInspector.SidebarView.prototype.wasShown.call(this);
    362 
    363         WebInspector.domAgent.requestDocument(this._setDocument.bind(this));
    364 
    365         WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this);
    366 
    367         WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this);
    368         WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this);
    369         WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this);
    370         WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.RegionOversetChanged, this._regionOversetChanged, this);
    371 
    372         WebInspector.panel("elements").treeOutline.addEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
    373 
    374         this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
    375         this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
    376     },
    377 
    378     willHide: function()
    379     {
    380         WebInspector.domAgent.removeEventListener(WebInspector.DOMAgent.Events.DocumentUpdated, this._documentUpdated, this);
    381 
    382         WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowCreated, this._namedFlowCreated, this);
    383         WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.NamedFlowRemoved, this._namedFlowRemoved, this);
    384         WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.RegionLayoutUpdated, this._regionLayoutUpdated, this);
    385         WebInspector.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.RegionOversetChanged, this._regionOversetChanged, this);
    386 
    387         WebInspector.panel("elements").treeOutline.removeEventListener(WebInspector.ElementsTreeOutline.Events.SelectedNodeChanged, this._selectedNodeChanged, this);
    388 
    389         this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this);
    390         this._tabbedPane.removeEventListener(WebInspector.TabbedPane.EventTypes.TabClosed, this._tabClosed, this);
    391     },
    392 
    393     __proto__: WebInspector.SidebarView.prototype
    394 }
    395 
    396 /**
    397  * @constructor
    398  * @extends {TreeElement}
    399  */
    400 WebInspector.FlowTreeElement = function(flowContainer)
    401 {
    402     var container = document.createElement("div");
    403     container.createChild("div", "selection");
    404     container.createChild("span", "title").createChild("span").textContent = flowContainer.flow.name;
    405 
    406     TreeElement.call(this, container, flowContainer, false);
    407 
    408     this._overset = false;
    409     this.setOverset(flowContainer.flow.overset);
    410 }
    411 
    412 WebInspector.FlowTreeElement.prototype = {
    413     /**
    414      * @param {boolean} newOverset
    415      */
    416     setOverset: function(newOverset)
    417     {
    418         if (this._overset === newOverset)
    419             return;
    420 
    421         if (newOverset) {
    422             this.title.classList.add("named-flow-overflow");
    423             this.tooltip = WebInspector.UIString("Overflows.");
    424         } else {
    425             this.title.classList.remove("named-flow-overflow");
    426             this.tooltip = "";
    427         }
    428 
    429         this._overset = newOverset;
    430     },
    431 
    432     __proto__: TreeElement.prototype
    433 }
    434