// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {BrowserBridge} from './browser_bridge.js';
// <if expr="chromeos_ash">
import {CrosView} from './chromeos_view.js';
// </if>
import {DnsView} from './dns_view.js';
import {DomainSecurityPolicyView} from './domain_security_policy_view.js';
import {EventsView} from './events_view.js';
import {ProxyView} from './proxy_view.js';
import {SharedDictionaryView} from './shared_dictionary_view.js';
import {SocketsView} from './sockets_view.js';
import {TabSwitcherView} from './tab_switcher_view.js';
import {hasTouchScreen} from './util.js';
import {WindowView} from './view.js';
/** @type {?MainView} */
let instance = null;
/**
* This class is the root view object of the page. It owns all the other
* views, and manages switching between them. It is also responsible for
* initializing the views and the BrowserBridge.
*/
export class MainView extends WindowView {
constructor() {
if (hasTouchScreen()) {
document.body.classList.add('touch');
}
const tabSwitcher = new TabSwitcherView();
super(tabSwitcher);
this.tabSwitcher_ = tabSwitcher;
// Create the tab switcher.
this.initTabs_();
// Trigger initial layout.
this.resetGeometry();
window.onhashchange = this.onUrlHashChange_.bind(this);
// Select the initial view based on the current URL.
window.onhashchange();
}
// This is exposed for testing.
tabSwitcher() {
return this.tabSwitcher_;
}
initTabs_() {
this.tabIdToHash_ = {};
this.hashToTabId_ = {};
this.tabSwitcher_.setOnTabSwitched(this.onTabSwitched_.bind(this));
// Helper function to add a tab given the class for a view singleton.
const addTab = function(viewClass) {
const tabId = viewClass.TAB_ID;
const tabHash = viewClass.TAB_HASH;
const tabName = viewClass.TAB_NAME;
const view = viewClass.getInstance();
if (!tabId || !view || !tabHash || !tabName) {
throw Error('Invalid view class for tab');
}
if (tabHash.charAt(0) !== '#') {
throw Error('Tab hashes must start with a #');
}
this.tabSwitcher_.addTab(tabId, view, tabName, tabHash);
this.tabIdToHash_[tabId] = tabHash;
this.hashToTabId_[tabHash] = tabId;
}.bind(this);
// Populate the main tabs.
addTab(EventsView);
addTab(ProxyView);
addTab(DnsView);
addTab(SocketsView);
addTab(DomainSecurityPolicyView);
addTab(SharedDictionaryView);
// <if expr="chromeos_ash">
addTab(CrosView);
// </if>
}
/**
* This function is called by the tab switcher when the current tab has been
* changed. It will update the current URL to reflect the new active tab,
* so the back can be used to return to previous view.
*/
onTabSwitched_(oldTabId, newTabId) {
// Change the URL to match the new tab.
const newTabHash = this.tabIdToHash_[newTabId];
const parsed = parseUrlHash_(window.location.hash);
if (parsed.tabHash !== newTabHash) {
window.location.hash = newTabHash;
}
}
onUrlHashChange_() {
const parsed = parseUrlHash_(window.location.hash);
if (!parsed) {
return;
}
// Redirect deleted pages to #events page, which contains instructions
// about migrating to using net-export and the external netlog_viewer.
if ([
'#capture',
'#import',
'#export',
'#timeline',
'#alt-svc',
'#http2',
'#quic',
'#reporting',
'#httpCache',
'#modules',
'#bandwidth',
'#prerender',
].includes(parsed.tabHash)) {
parsed.tabHash = EventsView.TAB_HASH;
}
// <if expr="not chromeos_ash">
// Don't switch to the chromeos view if not on chromeos.
if (parsed.tabHash === '#chromeos') {
parsed.tabHash = EventsView.TAB_HASH;
}
// </if>
if (!parsed.tabHash) {
// Default to the events tab.
parsed.tabHash = EventsView.TAB_HASH;
}
const tabId = this.hashToTabId_[parsed.tabHash];
if (tabId) {
this.tabSwitcher_.switchToTab(tabId);
if (parsed.parameters) {
const view = this.tabSwitcher_.getTabView(tabId);
view.setParameters(parsed.parameters);
}
}
}
static getInstance() {
return instance || (instance = new MainView());
}
}
/**
* Takes the current hash in form of "#tab¶m1=value1¶m2=value2&..."
* and parses it into a dictionary.
*
* Parameters and values are decoded with decodeURIComponent().
*/
function parseUrlHash_(hash) {
const parameters = hash.split('&');
let tabHash = parameters[0];
if (tabHash === '' || tabHash === '#') {
tabHash = undefined;
}
// Split each string except the first around the '='.
let paramDict = null;
for (let i = 1; i < parameters.length; i++) {
const paramStrings = parameters[i].split('=');
if (paramStrings.length !== 2) {
continue;
}
if (paramDict == null) {
paramDict = {};
}
const key = decodeURIComponent(paramStrings[0]);
const value = decodeURIComponent(paramStrings[1]);
paramDict[key] = value;
}
return {tabHash: tabHash, parameters: paramDict};
}