Web Inspector: add TabNavigation diagnostic event and related hooks
authorbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Dec 2019 23:28:27 +0000 (23:28 +0000)
committerbburg@apple.com <bburg@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 16 Dec 2019 23:28:27 +0000 (23:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=205138
<rdar://problem/57855456>

Reviewed by Devin Rousso.

This patch adds a new recorder for the TabNavigation diagnostic event.

The bulk of this patch is to find all callsites that can possibly change the active
tab and annotate them with the type of interaction (tab click, link click,
keyboard shortcut, inspect, and others). This patch was developed through
trial and error by logging the diagnostic events and debugging any scenarios
where a tab navigation is not correctly annotated with the initiating interaction.

* UserInterface/Main.html: Add new file.
* UserInterface/Base/Main.js:
(WI.contentLoaded): Register new recorder.
(WI._handleSettingsKeyboardShortcut): Annotate as keyboard shortcut.
- Add options argument to most WI.show*Tab functions, and forward to the underlying
TabBrowser or TabBar calls. This allows initiatorHint to be used in these cases.
- Add other annotations to linkifyElement

* UserInterface/Views/TabBrowser.js:
(WI.TabBrowser.prototype.showTabForContentView):
(WI.TabBrowser.prototype._tabBarItemSelected):
- Try to infer an initiator for the tab navigation from TabBrowser API arguments or from TabBar's event.
- Add an enum with TabNavigationInitiator values.

* UserInterface/Base/DOMUtilities.js:
Clickable element links should be reported as link clicks. Add an annotation
so that it isn't reported as "Inspect" (due to going through DOMManager.inspectElement).

* UserInterface/Controllers/CallFrameTreeController.js:
(WI.CallFrameTreeController):
(WI.CallFrameTreeController.prototype._showSourceCodeLocation):
This is mainly used by Canvas tab. Annotate call frame links as link clicks.

* UserInterface/Controllers/DOMManager.js:
(WI.DOMManager.prototype.inspectElement):
Accept an options argument. This is used to forward the initiatorHint to
the listener of this event, WI._domNodeWasInspected, so it can forward the
initiatorHint further on.

* UserInterface/Protocol/InspectorFrontendAPI.js:
(InspectorFrontendAPI.setTimelineProfilingEnabled):
(InspectorFrontendAPI.showConsole):
(InspectorFrontendAPI.showResources):
(InspectorFrontendAPI.showTimelines):
(InspectorFrontendAPI.showMainResourceForFrame):
Annotate these as FrontendAPI calls. Mainly used by Develop menu items in Safari.

* UserInterface/Views/ContextMenuUtilities.js:
(WI.appendContextMenuItemsForSourceCode):
(WI.appendContextMenuItemsForURL):
Annotate as context menu.

* UserInterface/Views/DOMNodeTreeElement.js:
(WI.DOMNodeTreeElement):
(WI.DOMNodeTreeElement.prototype.populateContextMenu):
Annotate as context menu.

* UserInterface/Views/DOMTreeElement.js:
(WI.DOMTreeElement.prototype._buildTagDOM):

* UserInterface/Views/DefaultDashboardView.js:
(WI.DefaultDashboardView.prototype._resourcesItemWasClicked):
(WI.DefaultDashboardView.prototype._networkItemWasClicked):
(WI.DefaultDashboardView.prototype._timelineItemWasClicked):
(WI.DefaultDashboardView.prototype._consoleItemWasClicked):
Annotate as dashboard.

* UserInterface/Views/LegacyTabBar.js:
(WI.LegacyTabBar.prototype.set selectedTabBarItem):
Include the inferred initiator in the event that is dispatched.

(WI.LegacyTabBar.prototype.selectTabBarItemWithInitiator):
Added. This is a convenience method that temporarily sets the
initiator before invoking the setter (which reads the initator).

(WI.LegacyTabBar.prototype._handleMouseDown):
(WI.LegacyTabBar.prototype._handleClick):
(WI.LegacyTabBar.prototype._handleNewTabClick):
Treat these as "tab clicks".

* UserInterface/Views/TabBar.js:
(WI.TabBar.prototype.set selectedTabBarItem):
(WI.TabBar.prototype.selectTabBarItemWithInitiator):
(WI.TabBar.prototype._handleMouseDown):
(WI.TabBar.prototype._handleClick):
Changes from LegacyTabBar have been copied to this version, as it's a
drop-in replacement.

* UserInterface/Views/LogContentView.js:
(WI.LogContentView.prototype._showConsoleTab):
Treat the console chevron as a "button click".

* UserInterface/Views/NewTabContentView.js:
(WI.NewTabContentView.prototype._createNewTabWithType):
Treat each tab button as a "button click".

* UserInterface/Views/RecordingActionTreeElement.js:
(WI.RecordingActionTreeElement.prototype.populateContextMenu):
Annotate as context menu.

* UserInterface/Views/ResourceTimelineDataGridNode.js:
(WI.ResourceTimelineDataGridNode.prototype._dataGridNodeGoToArrowClicked):
Annotate as link click.

* UserInterface/Views/SearchResultTreeElement.js:
(WI.SearchResultTreeElement):
(WI.SearchResultTreeElement.prototype.populateContextMenu):
Annotate as context menu.

* UserInterface/Views/SourceCodeTextEditor.js:
(WI.SourceCodeTextEditor.prototype.textEditorGutterContextMenu):
Annotate as context menu.

(WI.SourceCodeTextEditor.prototype._showPopoverForObject.):
(WI.SourceCodeTextEditor.prototype._showPopoverForObject):
Annotate elements in popover as link click.

* UserInterface/Views/SourceCodeTreeElement.js:
(WI.SourceCodeTreeElement):
(WI.SourceCodeTreeElement.prototype._handleToggleBlackboxedImageElementClicked):
Annotate as context menu.

* UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
(WI.SpreadsheetCSSStyleDeclarationSection.prototype._populateIconElementContextMenu):
Annotate as context menu.

* UserInterface/Views/SpreadsheetStyleProperty.js:
(WI.SpreadsheetStyleProperty.prototype._setupJumpToSymbol):
Annotate as link click.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@253591 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/DOMUtilities.js
Source/WebInspectorUI/UserInterface/Base/Main.js
Source/WebInspectorUI/UserInterface/Controllers/CallFrameTreeController.js
Source/WebInspectorUI/UserInterface/Controllers/DOMManager.js
Source/WebInspectorUI/UserInterface/Controllers/TabNavigationDiagnosticEventRecorder.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Protocol/InspectorFrontendAPI.js
Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js
Source/WebInspectorUI/UserInterface/Views/DOMNodeTreeElement.js
Source/WebInspectorUI/UserInterface/Views/DOMTreeElement.js
Source/WebInspectorUI/UserInterface/Views/DefaultDashboardView.js
Source/WebInspectorUI/UserInterface/Views/LegacyTabBar.js
Source/WebInspectorUI/UserInterface/Views/LogContentView.js
Source/WebInspectorUI/UserInterface/Views/NewTabContentView.js
Source/WebInspectorUI/UserInterface/Views/RecordingActionTreeElement.js
Source/WebInspectorUI/UserInterface/Views/ResourceTimelineDataGridNode.js
Source/WebInspectorUI/UserInterface/Views/SearchResultTreeElement.js
Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js
Source/WebInspectorUI/UserInterface/Views/SourceCodeTreeElement.js
Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js
Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js
Source/WebInspectorUI/UserInterface/Views/TabBar.js
Source/WebInspectorUI/UserInterface/Views/TabBrowser.js

index 60ee575..0ad2365 100644 (file)
@@ -1,3 +1,140 @@
+2019-12-11  Brian Burg  <bburg@apple.com>
+
+        Web Inspector: add TabNavigation diagnostic event and related hooks
+        https://bugs.webkit.org/show_bug.cgi?id=205138
+        <rdar://problem/57855456>
+
+        Reviewed by Devin Rousso.
+
+        This patch adds a new recorder for the TabNavigation diagnostic event.
+
+        The bulk of this patch is to find all callsites that can possibly change the active
+        tab and annotate them with the type of interaction (tab click, link click,
+        keyboard shortcut, inspect, and others). This patch was developed through
+        trial and error by logging the diagnostic events and debugging any scenarios
+        where a tab navigation is not correctly annotated with the initiating interaction.
+
+        * UserInterface/Main.html: Add new file.
+        * UserInterface/Base/Main.js:
+        (WI.contentLoaded): Register new recorder.
+        (WI._handleSettingsKeyboardShortcut): Annotate as keyboard shortcut.
+        - Add options argument to most WI.show*Tab functions, and forward to the underlying
+        TabBrowser or TabBar calls. This allows initiatorHint to be used in these cases.
+        - Add other annotations to linkifyElement
+
+        * UserInterface/Views/TabBrowser.js:
+        (WI.TabBrowser.prototype.showTabForContentView):
+        (WI.TabBrowser.prototype._tabBarItemSelected):
+        - Try to infer an initiator for the tab navigation from TabBrowser API arguments or from TabBar's event.
+        - Add an enum with TabNavigationInitiator values.
+
+        * UserInterface/Base/DOMUtilities.js:
+        Clickable element links should be reported as link clicks. Add an annotation
+        so that it isn't reported as "Inspect" (due to going through DOMManager.inspectElement).
+
+        * UserInterface/Controllers/CallFrameTreeController.js:
+        (WI.CallFrameTreeController):
+        (WI.CallFrameTreeController.prototype._showSourceCodeLocation):
+        This is mainly used by Canvas tab. Annotate call frame links as link clicks.
+
+        * UserInterface/Controllers/DOMManager.js:
+        (WI.DOMManager.prototype.inspectElement):
+        Accept an options argument. This is used to forward the initiatorHint to
+        the listener of this event, WI._domNodeWasInspected, so it can forward the
+        initiatorHint further on.
+
+        * UserInterface/Protocol/InspectorFrontendAPI.js:
+        (InspectorFrontendAPI.setTimelineProfilingEnabled):
+        (InspectorFrontendAPI.showConsole):
+        (InspectorFrontendAPI.showResources):
+        (InspectorFrontendAPI.showTimelines):
+        (InspectorFrontendAPI.showMainResourceForFrame):
+        Annotate these as FrontendAPI calls. Mainly used by Develop menu items in Safari.
+
+        * UserInterface/Views/ContextMenuUtilities.js:
+        (WI.appendContextMenuItemsForSourceCode):
+        (WI.appendContextMenuItemsForURL):
+        Annotate as context menu.
+
+        * UserInterface/Views/DOMNodeTreeElement.js:
+        (WI.DOMNodeTreeElement):
+        (WI.DOMNodeTreeElement.prototype.populateContextMenu):
+        Annotate as context menu.
+
+        * UserInterface/Views/DOMTreeElement.js:
+        (WI.DOMTreeElement.prototype._buildTagDOM):
+
+
+        * UserInterface/Views/DefaultDashboardView.js:
+        (WI.DefaultDashboardView.prototype._resourcesItemWasClicked):
+        (WI.DefaultDashboardView.prototype._networkItemWasClicked):
+        (WI.DefaultDashboardView.prototype._timelineItemWasClicked):
+        (WI.DefaultDashboardView.prototype._consoleItemWasClicked):
+        Annotate as dashboard.
+
+        * UserInterface/Views/LegacyTabBar.js:
+        (WI.LegacyTabBar.prototype.set selectedTabBarItem):
+        Include the inferred initiator in the event that is dispatched.
+
+        (WI.LegacyTabBar.prototype.selectTabBarItemWithInitiator):
+        Added. This is a convenience method that temporarily sets the
+        initiator before invoking the setter (which reads the initator).
+
+        (WI.LegacyTabBar.prototype._handleMouseDown):
+        (WI.LegacyTabBar.prototype._handleClick):
+        (WI.LegacyTabBar.prototype._handleNewTabClick):
+        Treat these as "tab clicks".
+
+        * UserInterface/Views/TabBar.js:
+        (WI.TabBar.prototype.set selectedTabBarItem):
+        (WI.TabBar.prototype.selectTabBarItemWithInitiator):
+        (WI.TabBar.prototype._handleMouseDown):
+        (WI.TabBar.prototype._handleClick):
+        Changes from LegacyTabBar have been copied to this version, as it's a
+        drop-in replacement.
+
+        * UserInterface/Views/LogContentView.js:
+        (WI.LogContentView.prototype._showConsoleTab):
+        Treat the console chevron as a "button click".
+
+        * UserInterface/Views/NewTabContentView.js:
+        (WI.NewTabContentView.prototype._createNewTabWithType):
+        Treat each tab button as a "button click".
+
+        * UserInterface/Views/RecordingActionTreeElement.js:
+        (WI.RecordingActionTreeElement.prototype.populateContextMenu):
+        Annotate as context menu.
+
+        * UserInterface/Views/ResourceTimelineDataGridNode.js:
+        (WI.ResourceTimelineDataGridNode.prototype._dataGridNodeGoToArrowClicked):
+        Annotate as link click.
+
+        * UserInterface/Views/SearchResultTreeElement.js:
+        (WI.SearchResultTreeElement):
+        (WI.SearchResultTreeElement.prototype.populateContextMenu):
+        Annotate as context menu.
+
+        * UserInterface/Views/SourceCodeTextEditor.js:
+        (WI.SourceCodeTextEditor.prototype.textEditorGutterContextMenu):
+        Annotate as context menu.
+
+        (WI.SourceCodeTextEditor.prototype._showPopoverForObject.):
+        (WI.SourceCodeTextEditor.prototype._showPopoverForObject):
+        Annotate elements in popover as link click.
+
+        * UserInterface/Views/SourceCodeTreeElement.js:
+        (WI.SourceCodeTreeElement):
+        (WI.SourceCodeTreeElement.prototype._handleToggleBlackboxedImageElementClicked):
+        Annotate as context menu.
+
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype._populateIconElementContextMenu):
+        Annotate as context menu.
+
+        * UserInterface/Views/SpreadsheetStyleProperty.js:
+        (WI.SpreadsheetStyleProperty.prototype._setupJumpToSymbol):
+        Annotate as link click.
+
 2019-12-15  Emilio Cobos Álvarez  <emilio@crisal.io>
 
         Remove -webkit-marquee.
index 02fb24a..42b39a1 100644 (file)
@@ -85,7 +85,9 @@ WI.linkifyNodeReferenceElement = function(node, element, options = {})
 WI.bindInteractionsForNodeToElement = function(node, element, options = {}) {
     if (!options.ignoreClick) {
         element.addEventListener("click", (event) => {
-            WI.domManager.inspectElement(node.id);
+            WI.domManager.inspectElement(node.id, {
+                initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
+            });
         });
     }
 
index 136251e..e9ebd68 100644 (file)
@@ -333,8 +333,8 @@ WI.contentLoaded = function()
     WI._decreaseZoomKeyboardShortcut2 = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Shift, WI.KeyboardShortcut.Key.Minus, boundDecreaseZoom);
     WI._resetZoomKeyboardShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl, "0", WI._resetZoom);
 
-    WI._showTabAtIndexKeyboardShortcuts = [1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Option, `${i}`, (event) => { WI._showTabAtIndex(i); }));
-    WI._openNewTabKeyboardShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Option, "T", WI.showNewTabTab);
+    WI._showTabAtIndexKeyboardShortcuts = [1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Option, `${i}`, (event) => { WI._showTabAtIndexFromShortcut(i); }));
+    WI._openNewTabKeyboardShortcut = new WI.KeyboardShortcut(WI.KeyboardShortcut.Modifier.CommandOrControl | WI.KeyboardShortcut.Modifier.Option, "T", WI._showNewTabFromShortcut);
 
     WI.tabBrowser = new WI.TabBrowser(document.getElementById("tab-browser"), WI.tabBar, WI.navigationSidebar, WI.detailsSidebar);
     WI.tabBrowser.addEventListener(WI.TabBrowser.Event.SelectedTabContentViewDidChange, WI._tabBrowserSelectedTabContentViewDidChange);
@@ -553,6 +553,7 @@ WI.contentLoaded = function()
     if (InspectorFrontendHost.supportsDiagnosticLogging) {
         WI.diagnosticController = new WI.DiagnosticController;
         WI.diagnosticController.addRecorder(new WI.TabActivityDiagnosticEventRecorder(WI.diagnosticController));
+        WI.diagnosticController.addRecorder(new WI.TabNavigationDiagnosticEventRecorder(WI.diagnosticController));
     }
 };
 
@@ -673,8 +674,11 @@ WI._openDefaultTab = function(event)
 
 WI._handleSettingsKeyboardShortcut = function(event)
 {
-    if (event.keyIdentifier === "U+002C") // ","
-        WI.tabBrowser.showTabForContentView(WI.settingsTabContentView);
+    if (event.keyIdentifier === "U+002C") { // ","
+        WI.tabBrowser.showTabForContentView(WI.settingsTabContentView, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.KeyboardShortcut,
+        });
+    }
 };
 
 WI._tryToRestorePendingTabs = function()
@@ -756,10 +760,10 @@ WI.createNewTabWithType = function(tabType, options = {})
     });
 
     if (shouldReplaceTab)
-        WI.tabBrowser.closeTabForContentView(referencedView, {suppressAnimations});
+        WI.tabBrowser.closeTabForContentView(referencedView, {...options, suppressAnimations});
 
     if (shouldShowNewTab)
-        WI.tabBrowser.showTabForContentView(tabContentView);
+        WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.activateExtraDomains = function(domains)
@@ -926,6 +930,9 @@ WI.openURL = function(url, frame, options = {})
         resource = WI.mainTarget.resourceCollection.resourceForURL(removeURLFragment(url));
 
     if (resource) {
+        // Context menu selections may go through this code path; don't clobber the previously-set hint.
+        if (!options.initiatorHint)
+            options.initiatorHint = WI.TabBrowser.TabNavigationInitiator.LinkClick;
         let positionToReveal = new WI.SourceCodePosition(options.lineNumber, 0);
         WI.showSourceCode(resource, {...options, positionToReveal});
         return;
@@ -1006,7 +1013,7 @@ WI.hideSplitConsole = function()
     WI.consoleDrawer.collapsed = true;
 };
 
-WI.showConsoleTab = function(requestedScope)
+WI.showConsoleTab = function(requestedScope, options = {})
 {
     requestedScope = requestedScope || WI.LogContentView.Scopes.All;
 
@@ -1014,7 +1021,8 @@ WI.showConsoleTab = function(requestedScope)
 
     WI.consoleContentView.scopeBar.item(requestedScope).selected = true;
 
-    WI.showRepresentedObject(WI._consoleRepresentedObject);
+    const cookie = null;
+    WI.showRepresentedObject(WI._consoleRepresentedObject, cookie, options);
 
     console.assert(WI.isShowingConsoleTab());
 };
@@ -1024,12 +1032,12 @@ WI.isShowingConsoleTab = function()
     return WI.tabBrowser.selectedTabContentView instanceof WI.ConsoleTabContentView;
 };
 
-WI.showElementsTab = function()
+WI.showElementsTab = function(options = {})
 {
     var tabContentView = WI.tabBrowser.bestTabContentViewForClass(WI.ElementsTabContentView);
     if (!tabContentView)
         tabContentView = new WI.ElementsTabContentView;
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.isShowingElementsTab = function()
@@ -1049,7 +1057,7 @@ WI.showSourcesTab = function(options = {})
     if (options.showScopeChainSidebar)
         tabContentView.showScopeChainDetailsSidebarPanel();
 
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.isShowingSourcesTab = function()
@@ -1057,21 +1065,21 @@ WI.isShowingSourcesTab = function()
     return WI.tabBrowser.selectedTabContentView instanceof WI.SourcesTabContentView;
 };
 
-WI.showStorageTab = function()
+WI.showStorageTab = function(options = {})
 {
     var tabContentView = WI.tabBrowser.bestTabContentViewForClass(WI.StorageTabContentView);
     if (!tabContentView)
         tabContentView = new WI.StorageTabContentView;
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
-WI.showNetworkTab = function()
+WI.showNetworkTab = function(options = {})
 {
     let tabContentView = WI.tabBrowser.bestTabContentViewForClass(WI.NetworkTabContentView);
     if (!tabContentView)
         tabContentView = new WI.NetworkTabContentView;
 
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.isShowingNetworkTab = function()
@@ -1084,12 +1092,12 @@ WI.isShowingSearchTab = function()
     return WI.tabBrowser.selectedTabContentView instanceof WI.SearchTabContentView;
 };
 
-WI.showTimelineTab = function()
+WI.showTimelineTab = function(options = {})
 {
     var tabContentView = WI.tabBrowser.bestTabContentViewForClass(WI.TimelineTabContentView);
     if (!tabContentView)
         tabContentView = new WI.TimelineTabContentView;
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.isShowingTimelineTab = function()
@@ -1109,7 +1117,7 @@ WI.showLayersTab = function(options = {})
         tabContentView = new WI.LayersTabContentView;
     if (options.nodeToSelect)
         tabContentView.selectLayerForNode(options.nodeToSelect);
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, options);
 };
 
 WI.isShowingLayersTab = function()
@@ -1119,7 +1127,7 @@ WI.isShowingLayersTab = function()
 
 WI.showSettingsTab = function(options = {})
 {
-    WI.tabBrowser.showTabForContentView(WI.settingsTabContentView);
+    WI.tabBrowser.showTabForContentView(WI.settingsTabContentView, options);
 
     if (options.blackboxPatternToSelect)
         WI.settingsTabContentView.selectBlackboxPattern(options.blackboxPatternToSelect);
@@ -1257,12 +1265,11 @@ WI.showRepresentedObject = function(representedObject, cookie, options = {})
     tabContentView.showRepresentedObject(representedObject, cookie);
 };
 
-WI.showLocalResourceOverride = function(localResourceOverride)
+WI.showLocalResourceOverride = function(localResourceOverride, options = {})
 {
     console.assert(localResourceOverride instanceof WI.LocalResourceOverride);
     const cookie = null;
-    const options = {ignoreNetworkTab: true, ignoreSearchTab: true};
-    WI.showRepresentedObject(localResourceOverride, cookie, options);
+    WI.showRepresentedObject(localResourceOverride, cookie, {...options, ignoreNetworkTab: true, ignoreSearchTab: true});
 };
 
 WI.showMainFrameDOMTree = function(nodeToSelect, options = {})
@@ -1283,7 +1290,8 @@ WI.showSourceCodeForFrame = function(frameIdentifier, options = {})
 
     WI._frameIdentifierToShowSourceCodeWhenAvailable = undefined;
 
-    WI.showRepresentedObject(frame, null, options);
+    const cookie = null;
+    WI.showRepresentedObject(frame, cookie, options);
 };
 
 WI.showSourceCode = function(sourceCode, options = {})
@@ -1379,7 +1387,10 @@ WI._searchTextDidChange = function(event)
     var searchQuery = WI._searchToolbarItem.text;
     WI._searchToolbarItem.text = "";
 
-    WI.tabBrowser.showTabForContentView(tabContentView);
+    WI.tabBrowser.showTabForContentView(tabContentView, {
+        // Classify this as a keyboard shortcut, as the only other way to get to Search Tab is via TabBar itself.
+        initiatorHint: WI.TabBrowser.TabNavigationInitiator.KeyboardShortcut,
+    });
 
     tabContentView.performSearch(searchQuery);
 };
@@ -1960,13 +1971,13 @@ WI._moveWindowMouseDown = function(event)
 
 WI._domStorageWasInspected = function(event)
 {
-    WI.showStorageTab();
+    WI.showStorageTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.Inspect});
     WI.showRepresentedObject(event.data.domStorage, null, {ignoreSearchTab: true});
 };
 
 WI._databaseWasInspected = function(event)
 {
-    WI.showStorageTab();
+    WI.showStorageTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.Inspect});
     WI.showRepresentedObject(event.data.database, null, {ignoreSearchTab: true});
 };
 
@@ -1976,8 +1987,12 @@ WI._domNodeWasInspected = function(event)
 
     InspectorFrontendHost.bringToFront();
 
-    WI.showElementsTab();
-    WI.showMainFrameDOMTree(event.data.node, {ignoreSearchTab: true});
+    // The event can override the initiator, in cases where the Inspect code path is used internally.
+    let options = {
+        initiatorHint: event.data.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Inspect,
+    };
+    WI.showElementsTab(options);
+    WI.showMainFrameDOMTree(event.data.node, {...options, ignoreSearchTab: true});
 };
 
 WI._inspectModeStateChanged = function(event)
@@ -2676,10 +2691,18 @@ WI.setLayoutDirection = function(value)
     InspectorFrontendHost.reopen();
 };
 
-WI._showTabAtIndex = function(i)
+WI._showNewTabFromShortcut = function()
 {
-    if (i <= WI.tabBar.tabBarItems.length)
-        WI.tabBar.selectedTabBarItem = i - 1;
+    WI.showNewTabTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.KeyboardShortcut});
+}
+
+WI._showTabAtIndexFromShortcut = function(i)
+{
+    if (i <= WI.tabBar.tabBarItems.length) {
+        WI.tabBar.selectTabBarItem(i - 1, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.KeyboardShortcut,
+        });
+    }
 };
 
 WI._showJavaScriptTypeInformationSettingChanged = function(event)
@@ -2866,6 +2889,9 @@ WI.linkifySourceCode = function(sourceCode, sourceCodePosition, options = {})
 WI.linkifyElement = function(linkElement, sourceCodeLocation, options = {}) {
     console.assert(sourceCodeLocation);
 
+    if (!options.initiatorHint)
+        options.initiatorHint = WI.TabBrowser.TabNavigationInitiator.LinkClick;
+
     function showSourceCodeLocation(event)
     {
         event.stopPropagation();
index 65cb59a..0db5ac4 100644 (file)
@@ -88,6 +88,8 @@ WI.CallFrameTreeController = class CallFrameTreeController extends WI.Object
         WI.showSourceCodeLocation(callFrame.sourceCodeLocation, {
             ignoreNetworkTab: true,
             ignoreSearchTab: true,
+            // Treat call frame clicks as link clicks since it jumps to a source location.
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
         });
     }
 };
index d1943c3..1a8df68 100644 (file)
@@ -460,13 +460,16 @@ WI.DOMManager = class DOMManager extends WI.Object
         return this._restoreSelectedNodeIsAllowed;
     }
 
-    inspectElement(nodeId)
+    inspectElement(nodeId, options = {})
     {
         var node = this._idToDOMNode[nodeId];
         if (!node || !node.ownerDocument)
             return;
 
-        this.dispatchEventToListeners(WI.DOMManager.Event.DOMNodeWasInspected, {node});
+        // This code path is hit by "Reveal in DOM Tree" and clicking element links/console widgets.
+        // Unless overridden by callers, assume that this is navigation is initiated by a Inspect mode.
+        let initiatorHint = options.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Inspect;
+        this.dispatchEventToListeners(WI.DOMManager.Event.DOMNodeWasInspected, {node, initiatorHint});
 
         this._inspectModeEnabled = false;
         this.dispatchEventToListeners(WI.DOMManager.Event.InspectModeStateChanged);
diff --git a/Source/WebInspectorUI/UserInterface/Controllers/TabNavigationDiagnosticEventRecorder.js b/Source/WebInspectorUI/UserInterface/Controllers/TabNavigationDiagnosticEventRecorder.js
new file mode 100644 (file)
index 0000000..137394a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+WI.TabNavigationDiagnosticEventRecorder = class TabNavigationDiagnosticEventRecorder extends WI.DiagnosticEventRecorder
+{
+    constructor(controller)
+    {
+        super("TabNavigation", controller);
+    }
+
+    // Protected
+
+    setup()
+    {
+        WI.tabBrowser.addEventListener(WI.TabBrowser.Event.SelectedTabContentViewDidChange, this._selectedTabContentViewDidChange, this);
+    }
+
+    teardown()
+    {
+        WI.tabBrowser.removeEventListener(WI.TabBrowser.Event.SelectedTabContentViewDidChange, this._selectedTabContentViewDidChange, this);
+    }
+
+    // Private
+
+    _selectedTabContentViewDidChange(event)
+    {
+        let outgoingTabType = event.data.outgoingTab.identifier;
+        let incomingTabType = event.data.incomingTab.identifier;
+        let initiator = WI.TabNavigationDiagnosticEventRecorder.tabBrowserInitiatorToEventInitiator(event.data.initiator || WI.TabBrowser.TabNavigationInitiator.Unknown);
+        if (!initiator) {
+            // If initiator is null, then there is a missing value in the switch. This is a programming error.
+            WI.reportInternalError("Value of 'initiator' could not be parsed: " + event.data.initiator);
+            return;
+        }
+
+        this.logDiagnosticEvent(this.name, {outgoingTabType, incomingTabType, initiator});
+    }
+
+    static tabBrowserInitiatorToEventInitiator(tabBrowserInitiator)
+    {
+        switch (tabBrowserInitiator) {
+        case WI.TabBrowser.TabNavigationInitiator.TabClick:
+            return "tab-click";
+        case WI.TabBrowser.TabNavigationInitiator.LinkClick:
+            return "link-click";
+        case WI.TabBrowser.TabNavigationInitiator.ButtonClick:
+            return "button-click";
+        case WI.TabBrowser.TabNavigationInitiator.ContextMenu:
+            return "context-menu";
+        case WI.TabBrowser.TabNavigationInitiator.Dashboard:
+            return "dashboard";
+        case WI.TabBrowser.TabNavigationInitiator.Breakpoint:
+            return "breakpoint";
+        case WI.TabBrowser.TabNavigationInitiator.Inspect:
+            return "inspect";
+        case WI.TabBrowser.TabNavigationInitiator.KeyboardShortcut:
+            return "keyboard-shortcut";
+        case WI.TabBrowser.TabNavigationInitiator.FrontendAPI:
+            return "frontend-api";
+        case WI.TabBrowser.TabNavigationInitiator.Unknown:
+            return "unknown";
+        }
+
+        console.error("Unhandled initiator type: " + tabBrowserInitiator);
+        return null;
+    }
+};
+
index 6004403..c826d51 100644 (file)
     <script src="Controllers/DiagnosticController.js"></script>
     <script src="Controllers/DiagnosticEventRecorder.js"></script>
     <script src="Controllers/TabActivityDiagnosticEventRecorder.js"></script>
+    <script src="Controllers/TabNavigationDiagnosticEventRecorder.js"></script>
 
     <script src="Base/Main.js"></script>
     <script src="Controllers/AppControllerBase.js"></script>
index e9a9165..d2dbe1d 100644 (file)
@@ -51,7 +51,9 @@ InspectorFrontendAPI = {
             return;
         }
 
-        WI.showTimelineTab();
+        WI.showTimelineTab({
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.FrontendAPI
+        });
 
         if (WI.timelineManager.isCapturing() === enabled)
             return;
@@ -97,7 +99,10 @@ InspectorFrontendAPI = {
 
     showConsole: function()
     {
-        WI.showConsoleTab();
+        const requestedScope = null;
+        WI.showConsoleTab(requestedScope, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.FrontendAPI,
+        });
 
         WI.quickConsole.prompt.focus();
 
@@ -121,22 +126,26 @@ InspectorFrontendAPI = {
 
     showResources: function()
     {
-        WI.showSourcesTab();
+        WI.showSourcesTab({
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.FrontendAPI,
+        });
     },
 
     // COMPATIBILITY (iOS 13): merged into InspectorFrontendAPI.setTimelineProfilingEnabled.
     showTimelines: function()
     {
-        WI.showTimelineTab();
+        WI.showTimelineTab({
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.FrontendAPI
+        });
     },
 
     showMainResourceForFrame: function(frameIdentifier)
     {
-        const options = {
+        WI.showSourceCodeForFrame(frameIdentifier, {
             ignoreNetworkTab: true,
             ignoreSearchTab: true,
-        };
-        WI.showSourceCodeForFrame(frameIdentifier, options);
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.FrontendAPI,
+        });
     },
 
     contextMenuItemSelected: function(id)
index 419d18e..c615604 100644 (file)
@@ -77,7 +77,9 @@ WI.appendContextMenuItemsForSourceCode = function(contextMenu, sourceCodeOrLocat
                 let resource = sourceCode;
                 let localResourceOverride = await resource.createLocalResourceOverride();
                 WI.networkManager.addLocalResourceOverride(localResourceOverride);
-                WI.showLocalResourceOverride(localResourceOverride);
+                WI.showLocalResourceOverride(localResourceOverride, {
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                });
             });
         } else {
             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(sourceCode.url);
@@ -85,7 +87,9 @@ WI.appendContextMenuItemsForSourceCode = function(contextMenu, sourceCodeOrLocat
                 contextMenu.appendSeparator();
 
                 contextMenu.appendItem(WI.UIString("Reveal Local Override"), () => {
-                    WI.showLocalResourceOverride(localResourceOverride);
+                    WI.showLocalResourceOverride(localResourceOverride, {
+                        initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                    });
                 });
 
                 contextMenu.appendItem(localResourceOverride.disabled ? WI.UIString("Enable Local Override") : WI.UIString("Disable Local Override"), () => {
@@ -120,6 +124,7 @@ WI.appendContextMenuItemsForSourceCode = function(contextMenu, sourceCodeOrLocat
             contextMenu.appendItem(WI.UIString("Reveal Blackbox Pattern"), () => {
                 WI.showSettingsTab({
                     blackboxPatternToSelect: blackboxData.regex,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
                 });
             });
         } else {
@@ -182,6 +187,7 @@ WI.appendContextMenuItemsForURL = function(contextMenu, url, options = {})
         return;
 
     function showResourceWithOptions(options) {
+        options.initiatorHint = WI.TabBrowser.TabNavigationInitiator.ContextMenu;
         if (options.location)
             WI.showSourceCodeLocation(options.location, options);
         else if (options.sourceCode)
@@ -319,13 +325,18 @@ WI.appendContextMenuItemsForDOMNode = function(contextMenu, domNode, options = {
 
         if (!options.excludeRevealElement && InspectorBackend.hasDomain("DOM") && attached) {
             contextMenu.appendItem(WI.repeatedUIString.revealInDOMTree(), () => {
-                WI.domManager.inspectElement(domNode.id);
+                WI.domManager.inspectElement(domNode.id, {
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                });
             });
         }
 
         if (InspectorBackend.hasDomain("LayerTree") && attached) {
             contextMenu.appendItem(WI.UIString("Reveal in Layers Tab", "Open Layers tab and select the layer corresponding to this node"), () => {
-                WI.showLayersTab({nodeToSelect: domNode});
+                WI.showLayersTab({
+                    nodeToSelect: domNode,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                });
             });
         }
 
index bf0d979..52bf9a8 100644 (file)
@@ -60,7 +60,9 @@ WI.DOMNodeTreeElement = class DOMNodeTreeElement extends WI.GeneralTreeElement
         contextMenu.appendSeparator();
 
         contextMenu.appendItem(WI.repeatedUIString.revealInDOMTree(), () => {
-            WI.domManager.inspectElement(this.representedObject.id);
+            WI.domManager.inspectElement(this.representedObject.id, {
+                initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+            });
         });
     }
 };
index ddda311..1fcd7fa 100644 (file)
@@ -1420,7 +1420,9 @@ WI.DOMTreeElement = class DOMTreeElement extends WI.TreeElement
             let goToArrowElement = parentElement.appendChild(WI.createGoToArrowButton());
             goToArrowElement.title = WI.UIString("Reveal in Elements Tab");
             goToArrowElement.addEventListener("click", (event) => {
-                WI.domManager.inspectElement(this.representedObject.id);
+                WI.domManager.inspectElement(this.representedObject.id, {
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
+                });
             });
         }
     }
index be3c11b..d624932 100644 (file)
@@ -133,22 +133,24 @@ WI.DefaultDashboardView = class DefaultDashboardView extends WI.DashboardView
 
     _resourcesItemWasClicked()
     {
-        WI.showSourcesTab();
+        WI.showSourcesTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.Dashboard});
     }
 
     _networkItemWasClicked()
     {
-        WI.showNetworkTab();
+        WI.showNetworkTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.Dashboard});
     }
 
     _timelineItemWasClicked()
     {
-        WI.showTimelineTab();
+        WI.showTimelineTab({initiatorHint: WI.TabBrowser.TabNavigationInitiator.Dashboard});
     }
 
     _consoleItemWasClicked(scope)
     {
-        WI.showConsoleTab(scope);
+        WI.showConsoleTab(scope, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.Dashboard,
+        });
     }
 
     _setConsoleItemValue(itemName, newValue)
index 64fe23d..dd1bbdd 100644 (file)
@@ -191,7 +191,8 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
             if (!nextTabBarItem || nextTabBarItem instanceof WI.PinnedTabBarItem)
                 nextTabBarItem = this._tabBarItems[index - 1];
 
-            this.selectedTabBarItem = nextTabBarItem;
+            let initiatorHint = options.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Unknown;
+            this.selectTabBarItem(nextTabBarItem, {initiatorHint});
         }
 
         if (this.element.classList.contains("animating")) {
@@ -362,6 +363,11 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
 
     set selectedTabBarItem(tabBarItemOrIndex)
     {
+        this.selectTabBarItem(tabBarItemOrIndex);
+    }
+
+    selectTabBarItem(tabBarItemOrIndex, options = {})
+    {
         let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
         if (tabBarItem === this._newTabTabBarItem || tabBarItem === this._tabPickerTabBarItem) {
             // Get the last normal tab item if the item is not selectable.
@@ -384,7 +390,8 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
                 this.needsLayout();
         }
 
-        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected, {previousTabBarItem});
+        let initiatorHint = options.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Unknown;
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected, {previousTabBarItem, initiatorHint});
     }
 
     get tabBarItems()
@@ -621,7 +628,9 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
 
             for (let item of this._hiddenTabBarItems) {
                 contextMenu.appendItem(item.title, () => {
-                    this.selectedTabBarItem = item;
+                    this.selectTabBarItem(item, {
+                        initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                    });
                 });
             }
 
@@ -633,7 +642,9 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
         if (closeButtonElement)
             return;
 
-        this.selectedTabBarItem = tabBarItem;
+        this.selectTabBarItem(tabBarItem, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.TabClick,
+        });
 
         if (tabBarItem instanceof WI.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
             return;
@@ -682,7 +693,11 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
                 return;
 
             if (!event.altKey) {
-                this.removeTabBarItem(tabBarItem, {suppressExpansion: true});
+                let options = {
+                    suppressExpansion: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.TabClick,
+                };
+                this.removeTabBarItem(tabBarItem, options);
                 return;
             }
 
@@ -848,7 +863,8 @@ WI.LegacyTabBar = class LegacyTabBar extends WI.View
 
     _handleNewTabClick(event)
     {
-        WI.showNewTabTab();
+        let options = {initiatorHint: WI.TabBrowser.TabNavigationInitiator.TabClick};
+        WI.showNewTabTab(options);
     }
 
     _handleNewTabMouseEnter(event)
index ce5455c..e46ed95 100644 (file)
@@ -810,7 +810,10 @@ WI.LogContentView = class LogContentView extends WI.ContentView
 
     _showConsoleTab()
     {
-        WI.showConsoleTab();
+        const requestedScope = null;
+        WI.showConsoleTab(requestedScope, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.ButtonClick,
+        });
     }
 
     _clearLog()
index ec4d375..a9b235f 100644 (file)
@@ -119,7 +119,8 @@ WI.NewTabContentView = class NewTabContentView extends WI.TabContentView
         const options = {
             referencedView: this,
             shouldReplaceTab: !canCreateAdditionalTabs || !WI.modifierKeys.metaKey,
-            shouldShowNewTab: !WI.modifierKeys.metaKey
+            shouldShowNewTab: !WI.modifierKeys.metaKey,
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.ButtonClick,
         };
         WI.createNewTabWithType(tabType, options);
     }
index bf14b1f..2696a7c 100644 (file)
@@ -434,6 +434,7 @@ WI.RecordingActionTreeElement = class RecordingActionTreeElement extends WI.Gene
                 WI.showSourceCodeLocation(sourceCodeLocation, {
                     ignoreNetworkTab: true,
                     ignoreSearchTab: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
                 });
             });
 
index 695d4be..7eafe11 100644 (file)
@@ -282,6 +282,7 @@ WI.ResourceTimelineDataGridNode = class ResourceTimelineDataGridNode extends WI.
         const options = {
             ignoreNetworkTab: true,
             ignoreSearchTab: true,
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
         };
         WI.showSourceCode(this.resource, options);
     }
index b345b65..c2fad5b 100644 (file)
@@ -96,6 +96,7 @@ WI.SearchResultTreeElement = class SearchResultTreeElement extends WI.GeneralTre
             contextMenu.appendItem(WI.UIString("Reveal in Elements Tab"), () => {
                 WI.showMainFrameDOMTree(this.representedObject.domNode, {
                     ignoreSearchTab: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
                 });
             });
         } else if (this.representedObject instanceof WI.SourceCodeSearchMatchObject) {
@@ -103,6 +104,7 @@ WI.SearchResultTreeElement = class SearchResultTreeElement extends WI.GeneralTre
                 WI.showOriginalOrFormattedSourceCodeTextRange(this.representedObject.sourceCodeTextRange, {
                     ignoreNetworkTab: true,
                     ignoreSearchTab: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
                 });
             });
         }
index 54fc01c..77a0d6f 100644 (file)
@@ -1262,7 +1262,10 @@ WI.SourceCodeTextEditor = class SourceCodeTextEditor extends WI.TextEditor
             if (!WI.isShowingSourcesTab()) {
                 contextMenu.appendSeparator();
                 contextMenu.appendItem(WI.UIString("Reveal in Sources Tab"), () => {
-                    WI.showSourcesTab({breakpointToSelect: breakpoints[0]});
+                    WI.showSourcesTab({
+                        breakpointToSelect: breakpoints[0],
+                        initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
+                    });
                 });
             }
 
@@ -1940,7 +1943,9 @@ WI.SourceCodeTextEditor = class SourceCodeTextEditor extends WI.TextEditor
 
                 var goToButton = titleElement.appendChild(WI.createGoToArrowButton());
                 goToButton.addEventListener("click", function() {
-                    WI.domManager.inspectElement(nodeId);
+                    WI.domManager.inspectElement(nodeId, {
+                        initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
+                    });
                 });
             });
         }
index c3838c0..d827441 100644 (file)
@@ -236,6 +236,7 @@ WI.SourceCodeTreeElement = class SourceCodeTreeElement extends WI.FolderizedTree
         if (blackboxData && blackboxData.type === WI.DebuggerManager.BlackboxType.Pattern) {
             WI.showSettingsTab({
                 blackboxPatternToSelect: blackboxData.regex,
+                initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
             });
             return;
         }
index ff5210c..740b3c6 100644 (file)
@@ -558,6 +558,7 @@ WI.SpreadsheetCSSStyleDeclarationSection = class SpreadsheetCSSStyleDeclarationS
                 WI.showSourceCodeLocation(this._style.ownerRule.sourceCodeLocation, {
                     ignoreNetworkTab: true,
                     ignoreSearchTab: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
                 });
             });
         }
index d993a76..77e5287 100644 (file)
@@ -850,6 +850,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
             const options = {
                 ignoreNetworkTab: true,
                 ignoreSearchTab: true,
+                initiatorHint: WI.TabBrowser.TabNavigationInitiator.LinkClick,
             };
             let sourceCode = sourceCodeLocation.sourceCode;
             WI.showSourceCodeLocation(sourceCode.createSourceCodeLocation(range.startLine, range.startColumn), options);
index a2b5cb0..801b17d 100644 (file)
@@ -335,6 +335,11 @@ WI.TabBar = class TabBar extends WI.View
 
     set selectedTabBarItem(tabBarItemOrIndex)
     {
+        this.selectTabBarItem(tabBarItemOrIndex);
+    }
+
+    selectTabBarItem(tabBarItemOrIndex, options = {})
+    {
         let tabBarItem = this._findTabBarItem(tabBarItemOrIndex);
         if (tabBarItem === this._tabPickerTabBarItem) {
             // Get the last normal tab item if the item is not selectable.
@@ -357,7 +362,8 @@ WI.TabBar = class TabBar extends WI.View
                 this.needsLayout();
         }
 
-        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected, {previousTabBarItem});
+        let initiatorHint = options.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Unknown;
+        this.dispatchEventToListeners(WI.TabBar.Event.TabBarItemSelected, {previousTabBarItem, initiatorHint});
     }
 
     get tabBarItems()
@@ -586,7 +592,9 @@ WI.TabBar = class TabBar extends WI.View
 
             for (let item of this._hiddenTabBarItems) {
                 contextMenu.appendItem(item.title, () => {
-                    this.selectedTabBarItem = item;
+                    this.selectTabBarItem(item, {
+                        initiator: WI.TabBrowser.TabNavigationInitiator.ContextMenu
+                    });
                 });
             }
 
@@ -598,7 +606,9 @@ WI.TabBar = class TabBar extends WI.View
         if (closeButtonElement)
             return;
 
-        this.selectedTabBarItem = tabBarItem;
+        this.selectTabBarItem(tabBarItem, {
+            initiatorHint: WI.TabBrowser.TabNavigationInitiator.TabClick
+        });
 
         if (tabBarItem instanceof WI.PinnedTabBarItem || !this._hasMoreThanOneNormalTab())
             return;
@@ -647,7 +657,11 @@ WI.TabBar = class TabBar extends WI.View
                 return;
 
             if (!event.altKey) {
-                this.removeTabBarItem(tabBarItem, {suppressExpansion: true});
+                let options = {
+                    suppressExpansion: true,
+                    initiatorHint: WI.TabBrowser.TabNavigationInitiator.TabClick,
+                };
+                this.removeTabBarItem(tabBarItem, options);
                 return;
             }
 
index 688bff7..84bed5e 100644 (file)
@@ -179,7 +179,7 @@ WI.TabBrowser = class TabBrowser extends WI.View
         if (!this.addTabForContentView(tabContentView, options))
             return false;
 
-        this._tabBar.selectedTabBarItem = tabContentView.tabBarItem;
+        this._tabBar.selectTabBarItem(tabContentView.tabBarItem, options);
 
         // FIXME: this is a workaround for <https://webkit.org/b/151876>.
         // Without this extra call, we might never lay out the child tab
@@ -257,7 +257,10 @@ WI.TabBrowser = class TabBrowser extends WI.View
             tabContentView.updateLayout(WI.View.LayoutReason.Resize);
         }
 
-        this.dispatchEventToListeners(WI.TabBrowser.Event.SelectedTabContentViewDidChange);
+        let outgoingTab = event.data.previousTabBarItem ? event.data.previousTabBarItem.representedObject : null;
+        let incomingTab = tabContentView;
+        let initiator = event.data.initiatorHint || WI.TabBrowser.TabNavigationInitiator.Unknown;
+        this.dispatchEventToListeners(WI.TabBrowser.Event.SelectedTabContentViewDidChange, {outgoingTab, incomingTab, initiator});
 
         this._restoreFocusedNodeForTabContentView(tabContentView);
     }
@@ -485,6 +488,38 @@ WI.TabBrowser = class TabBrowser extends WI.View
 WI.TabBrowser.NeedsResizeLayoutSymbol = Symbol("needs-resize-layout");
 WI.TabBrowser.FocusedNodeSymbol = Symbol("focused-node");
 
+WI.TabBrowser.TabNavigationInitiator = {
+    // Initiated by clicking on the TabBar UI (switching, opening, closing).
+    TabClick: "tab-browser-tab-navigation-initiator-tab-click",
+
+    // Initiated by clicking a URL, symbol, go-to-arrow, or other link to a resource/source code location.
+    LinkClick: "tab-browser-tab-navigation-initiator-link-click",
+
+    // Initiated by clicking miscellaneous UI (i.e., Quick Console's chevron, New Tab Tab's buttons).
+    ButtonClick: "tab-browser-tab-navigation-initiator-button-click",
+
+    // Initiated by selecting a context menu item in Web Inspector (i.e., "Reveal in Network Tab").
+    ContextMenu: "tab-browser-tab-navigation-initiator-context-menu",
+
+    // Initiated by clicking a dashboard element.
+    Dashboard: "tab-browser-tab-navigation-initiator-dashboard",
+
+    // Initiated by automatically switching tabs when a breakpoint is hit.
+    Breakpoint: "tab-browser-tab-navigation-initiator-breakpoint",
+
+    // Initiated by inspecting a DOM element, database, or other object via Console API's inspect() or live node selection.
+    Inspect: "tab-browser-tab-navigation-initiator-inspect",
+
+    // Initiated by keyboard shortcut (tab switching, new tab, search bar).
+    KeyboardShortcut: "tab-browser-tab-navigation-initiator-keyboard-shortcut",
+
+    // Initiated from outside of Web Inspector (Develop Menu, _WKInspector SPI).
+    FrontendAPI: "tab-browser-tab-navigation-initiator-frontend-api",
+
+    // Uncategorized; these should be investigated and categorized as one of the above.
+    Unknown: "tab-browser-tab-navigation-initiator-unknown"
+}
+
 WI.TabBrowser.Event = {
     SelectedTabContentViewDidChange: "tab-browser-selected-tab-content-view-did-change"
 };