Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/resources/FrameSelector.js

Issue 2773583002: [DevTools] Introduce a sidebar with a drop-down
Patch Set: [DevTools] Introduce a sidebar with a drop-down Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 Resources.FrameSelector = class extends Common.Object {
dgozman 2017/03/24 17:06:54 Let's think about reusing this in Console right no
eostroukhov 2017/04/04 23:47:17 Acknowledged.
6 constructor() {
7 super();
8 /** @type {!Map<string, !Resources._FrameTreeElement>} */
9 this._treeElementForFrameId = new Map();
10
11 this._button = createElement('button');
12 this._button.className = 'frame-selector';
13 this._button.appendChild(UI.Icon.create('largeicon-navigator-frame'));
14 this._nameLabel = this._button.createChild('span', 'frame-name');
15 this._button.appendChild(UI.Icon.create('smallicon-dropdown-arrow'));
16
17 this._button.addEventListener('click', () => this._showGlassPane());
18
19 this._tree = new UI.TreeOutlineInShadow();
20 this._tree.element.classList.add('frame-selector-popup');
21 this._tree.element.classList.add('filter-all');
22
23 var eventTarget = this._tree.contentElement;
24 eventTarget.addEventListener('click', () => this._commitSelection());
25 eventTarget.addEventListener('mouseleave', () => this._onmouseleave(), false );
26 eventTarget.addEventListener('mousemove', event => this._onmousemove(event), false);
27 eventTarget.addEventListener('keydown', event => this._keyDown(event));
28
29 this._button.ownerDocument.defaultView.addEventListener('blur', () => this._ hideGlassPane(null));
30
31 this._glassPane = new UI.GlassPane();
32 this._glassPane.setAnchorBehavior(UI.GlassPane.AnchorBehavior.PreferBottom);
33 this._glassPane.setSizeBehavior(UI.GlassPane.SizeBehavior.SetExactWidthMaxHe ight);
34 this._glassPane.setSetOutsideClickCallback(event => this._hideGlassPane(/** @type {!Node} */ (event.target)));
35
36 UI.createShadowRootWithCoreStyles(this._glassPane.contentElement, 'resources /frameSelector.css')
37 .appendChild(this._tree.element);
38
39 // Suppress the popup once - e.g. because user clicked on the button while t he popup is visible
40 // which should not instantly reopen popup.
41 this._suppressNextPopup = false;
42
43 /** @type {?Resources._FrameTreeElement} */
44 this._previousHoveredElement = null;
45
46 /** @type {?Element} */
47 this._storedFocus = null;
48
49 /** @type {?SDK.ResourceTreeFrame} */
50 this._selectedFrame = null;
51
52 /** @type {!Array<?Element>} */
53 this._focusedElementsStack = [];
54
55 this._initialize();
56 }
57
58 _initialize() {
59 function addListener(eventType, handler, target) {
dgozman 2017/03/24 17:06:54 Just spell it out 3 times - makes it easier to sea
eostroukhov 2017/04/04 23:47:17 Done, but I did that lambda so I don't have to rep
60 SDK.targetManager.addModelListener(SDK.ResourceTreeModel, eventType, event => handler.call(target, event.data));
61 }
62 addListener(SDK.ResourceTreeModel.Events.FrameAdded, this.frameAdded, this);
63 addListener(SDK.ResourceTreeModel.Events.FrameNavigated, this.frameNavigated , this);
64 addListener(SDK.ResourceTreeModel.Events.FrameDetached, this.frameDetached, this);
65
66 var mainTarget = SDK.targetManager.mainTarget();
dgozman 2017/03/24 17:06:54 Let's make this multitarget right away.
eostroukhov 2017/04/04 23:47:17 Please take a look.
67 var resourceTreeModel = mainTarget && mainTarget.hasDOMCapability() && SDK.R esourceTreeModel.fromTarget(mainTarget);
dgozman 2017/03/24 17:06:54 You don't have to check capability anymore: mainTa
eostroukhov 2017/04/04 23:47:17 Done.
68 var mainFrame = resourceTreeModel.mainFrame;
69 if (mainFrame)
70 this.populateFrame(mainFrame);
71
72 this.selectFrame(null);
73 }
74
75 /**
76 * @param {!SDK.ResourceTreeFrame} frame
77 * @returns {?SDK.ResourceTreeFrame}
78 */
79 static _getParentFrame(frame) {
80 var parentFrame = frame.parentFrame;
81 if (parentFrame)
82 return parentFrame;
83 var parentTarget = frame.target().parentTarget();
84 while (parentTarget && !parentTarget.hasDOMCapability())
85 parentTarget = parentTarget.parentTarget();
86 if (!parentTarget)
87 return null;
88 var model = SDK.ResourceTreeModel.fromTarget(parentTarget);
89 return model.mainFrame;
90 }
91
92 /**
93 * @returns {!Element}
94 */
95 get element() {
96 return this._button;
97 }
98
99 /**
100 * @param {!Element} root
101 * @return {function()} Call to remove the listener
102 */
103 trackFocus(root) {
104 var listener = (event => {
105 this._focusedElementsStack = [event.target.ownerDocument.deepActiveElement (), this._focusedElementsStack[0]];
106 });
107 root.element.addEventListener('focusin', listener);
108 return function() {
109 root.removeEventListener('focusin', listener);
110 };
111 }
112
113 /**
114 * @return {?SDK.ResourceTreeFrame} frame
115 */
116 selectedFrame() {
117 return this._selectedFrame;
118 }
119
120 selectRootFrame() {
121 var firstElement = this._tree.firstChild();
122 this.selectFrame(firstElement && firstElement.frame);
123 }
124
125 _showGlassPane() {
126 var doc = this._button.ownerDocument;
127 if (!doc || this._suppressNextPopup) {
128 this._suppressNextPopup = false;
129 return;
130 }
131
132 // Restored focus should not go to button but to previously focused componen t
133 this._storedFocus = this._focusedElementsStack[0];
134 if (this._storedFocus === this._button)
135 this._storedFocus = this._focusedElementsStack[1];
136
137 var anchorBox = this._button.boxInWindow(window);
138 // Account for border
139 anchorBox.x = 0;
140 anchorBox.width -= 4;
141 anchorBox.height -= 8;
142 var width = Math.max(300, anchorBox.width);
143 this._glassPane.setMaxContentSize(new UI.Size(width, 300));
144 // Position popup relative to arrow dowm and not just below the button
145 // anchorBox.height = childBox.y + childBox.height - anchorBox.y;
dgozman 2017/03/24 17:06:54 Commented code.
eostroukhov 2017/04/04 23:47:17 Oops. Fixed!
146 this._glassPane.setContentAnchorBox(anchorBox);
147 this._glassPane.show(doc);
148 this._tree.firstChild().expand();
149 this._tree.focus();
150 }
151
152 /**
153 * @param {?Node} targetNode
154 */
155 _hideGlassPane(targetNode) {
156 this._suppressNextPopup = this._button.isSelfOrAncestor(targetNode);
157 this._glassPane.hide();
158 if (this._storedFocus) {
159 this._storedFocus.focus();
160 this._storedFocus = null;
161 }
162 }
163
164 /**
165 * @param {!Event} event
166 */
167 _keyDown(event) {
168 if (!isEscKey(event) && !isEnterKey(event))
169 return;
170 if (isEnterKey(event))
171 this._commitSelection();
172 this._hideGlassPane(null);
173 event.consume();
174 }
175
176 /**
177 * @param {!SDK.ResourceTreeFrame} frame
178 */
179 frameAdded(frame) {
180 var parentFrame = Resources.FrameSelector._getParentFrame(frame);
181 var parentTreeElement = parentFrame ? this._treeElementForFrameId.get(parent Frame.id) : this._tree;
182 if (!parentTreeElement) {
183 console.warn('No frame to route ' + frame.url + ' to.');
184 return;
185 }
186
187 var frameTreeElement = new Resources._FrameTreeElement(frame);
188 this._treeElementForFrameId.set(frame.id, frameTreeElement);
189 parentTreeElement.appendChild(frameTreeElement);
190 }
191
192 /**
193 * @param {!SDK.ResourceTreeFrame} frame
194 */
195 frameDetached(frame) {
196 var frameTreeElement = this._treeElementForFrameId.get(frame.id);
197 if (!frameTreeElement)
198 return;
199
200 this._treeElementForFrameId.remove(frame.id);
201 if (frameTreeElement.parent)
202 frameTreeElement.parent.removeChild(frameTreeElement);
203 }
204
205 /**
206 * @param {!SDK.ResourceTreeFrame} frame
207 */
208 frameNavigated(frame) {
209 if (!Resources.FrameSelector._getParentFrame(frame))
210 return;
211 var frameTreeElement = this._treeElementForFrameId.get(frame.id);
212 if (frameTreeElement)
213 frameTreeElement.frameNavigated(frame);
214 }
215
216 reset() {
217 this._tree.removeChildren();
218 this._treeElementForFrameId.clear();
219 }
220
221 /**
222 * @param {!SDK.ResourceTreeFrame} frame
223 */
224 populateFrame(frame) {
225 this.frameAdded(frame);
226 for (var child of frame.childFrames)
227 this.populateFrame(child);
228 }
229
230 /**
231 * @param {?SDK.ResourceTreeFrame} frame
232 */
233 selectFrame(frame) {
234 this._selectedFrame = frame;
235
236 var displayName = frame ? frame.displayName() : '';
237 var frameName = frame ? frame.name : '';
238
239 this._nameLabel.textContent = frameName || displayName;
240 this._button.title = displayName;
241 this._hideGlassPane(null);
242 }
243
244 _commitSelection() {
245 var selected = this._tree.selectedTreeElement;
246 var frame = selected && selected.frame;
247 this.selectFrame(frame);
248 this.dispatchEventToListeners(Resources.FrameSelector.Events.FrameSelected, frame);
249 }
250
251 /**
252 * @param {!Event} event
253 */
254 _onmousemove(event) {
255 var nodeUnderMouse = event.target;
256 if (!nodeUnderMouse)
257 return;
258
259 var listNode = nodeUnderMouse.enclosingNodeOrSelfWithNodeName('li');
260 if (!listNode)
261 return;
262
263 var element = listNode.treeElement;
264 if (this._previousHoveredElement === element)
265 return;
266
267 if (this._previousHoveredElement) {
268 this._previousHoveredElement.hovered = false;
269 this._previousHoveredElement = null;
270 }
271
272 if (element instanceof Resources._FrameTreeElement) {
273 this._previousHoveredElement = element;
274 element.select(false, false);
275 element.hovered = true;
276 }
277 }
278
279 _onmouseleave() {
280 if (this._previousHoveredElement) {
281 this._previousHoveredElement.hovered = false;
282 this._previousHoveredElement = null;
283 }
284 }
285 };
286
287 /**
288 * @enum {symbol}
289 */
290 Resources.FrameSelector.Events = {
291 FrameSelected: Symbol('FrameSelected')
292 };
293
294 Resources._FrameTreeElement = class extends UI.TreeElement {
dgozman 2017/03/24 17:06:54 Why do we need a treeoutline? Let's reuse Filtered
eostroukhov 2017/04/04 23:47:17 Done.
295 /**
296 * @param {!SDK.ResourceTreeFrame} frame
297 */
298 constructor(frame) {
299 super('', false);
300 this.frame = frame;
301 this.frameNavigated(frame);
302 }
303
304 frameNavigated(frame) {
305 this.frame = frame;
306 this.title = frame.displayName();
307 }
308
309 get itemURL() {
dgozman 2017/03/24 17:06:54 Unused.
eostroukhov 2017/04/04 23:47:17 Done.
310 return 'frame://' + encodeURI(this.titleAsText());
311 }
312
313 set hovered(hovered) {
314 if (hovered) {
315 this.listItemElement.classList.add('hovered');
316 var domModel = SDK.DOMModel.fromTarget(this.frame.target());
317 if (domModel)
318 domModel.highlightFrame(this.frame.id);
319 } else {
320 this.listItemElement.classList.remove('hovered');
321 SDK.DOMModel.hideDOMNodeHighlight();
322 }
323 }
324 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698