// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @constructor
 * @extends {WebInspector.VBox}
 */
WebInspector.DevicesView = function()
{
    WebInspector.VBox.call(this, true);
    this.registerRequiredCSS("devices/devicesView.css");
    this.contentElement.classList.add("devices-view");

    var hbox = this.contentElement.createChild("div", "hbox devices-container");
    var sidebar = hbox.createChild("div", "devices-sidebar");
    sidebar.createChild("div", "devices-view-title").createTextChild(WebInspector.UIString("Devices"));
    this._sidebarList = sidebar.createChild("div", "devices-sidebar-list");

    this._discoveryView = new WebInspector.DevicesView.DiscoveryView();
    this._sidebarListSpacer = this._sidebarList.createChild("div", "devices-sidebar-spacer");
    this._discoveryListItem = this._sidebarList.createChild("div", "devices-sidebar-item");
    this._discoveryListItem.textContent = WebInspector.UIString("Settings");
    this._discoveryListItem.addEventListener("click", this._selectSidebarListItem.bind(this, this._discoveryListItem, this._discoveryView));

    /** @type {!Map<string, !WebInspector.DevicesView.DeviceView>} */
    this._viewById = new Map();
    /** @type {!Array<!Adb.Device>} */
    this._devices = [];
    /** @type {!Map<string, !Element>} */
    this._listItemById = new Map();

    this._viewContainer = hbox.createChild("div", "flex-auto vbox");

    var discoveryFooter = this.contentElement.createChild("div", "devices-footer");
    this._deviceCountSpan = discoveryFooter.createChild("span");
    discoveryFooter.createChild("span").textContent = WebInspector.UIString(" Read ");
    discoveryFooter.appendChild(WebInspector.linkifyURLAsNode("https://developers.google.com/chrome-developer-tools/docs/remote-debugging", WebInspector.UIString("remote debugging documentation"), undefined, true));
    discoveryFooter.createChild("span").textContent = WebInspector.UIString(" for more information.");
    this._updateFooter();
    this._selectSidebarListItem(this._discoveryListItem, this._discoveryView);

    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DevicesUpdated, this._devicesUpdated, this);
    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DevicesDiscoveryConfigChanged, this._devicesDiscoveryConfigChanged, this);
    InspectorFrontendHost.events.addEventListener(InspectorFrontendHostAPI.Events.DevicesPortForwardingStatusChanged, this._devicesPortForwardingStatusChanged, this);

    this.contentElement.tabIndex = 0;
    this.setDefaultFocusedElement(this.contentElement);
}

WebInspector.DevicesView.prototype = {
    /**
     * @param {!Element} listItem
     * @param {!WebInspector.Widget} view
     */
    _selectSidebarListItem: function(listItem, view)
    {
        if (this._selectedListItem === listItem)
            return;

        if (this._selectedListItem) {
            this._selectedListItem.classList.remove("selected");
            this._visibleView.detach();
        }

        this._visibleView = view;
        this._selectedListItem = listItem;
        this._visibleView.show(this._viewContainer);
        this._selectedListItem.classList.add("selected");
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _devicesUpdated: function(event)
    {
        this._devices = /** @type {!Array.<!Adb.Device>} */ (event.data).slice().filter(d => d.adbSerial.toUpperCase() !== "WEBRTC");
        for (var device of this._devices) {
            if (!device.adbConnected)
                device.adbModel = WebInspector.UIString("Unknown");
        }

        var ids = new Set();
        for (var device of this._devices)
            ids.add(device.id);

        var selectedRemoved = false;
        for (var deviceId of this._viewById.keys()) {
            if (!ids.has(deviceId)) {
                var listItem = /** @type {!Element} */ (this._listItemById.get(deviceId));
                this._listItemById.remove(deviceId);
                this._viewById.remove(deviceId);
                listItem.remove();
                if (listItem === this._selectedListItem)
                    selectedRemoved = true;
            }
        }

        for (var device of this._devices) {
            var view = this._viewById.get(device.id);
            var listItem = this._listItemById.get(device.id);

            if (!view) {
                view = new WebInspector.DevicesView.DeviceView();
                this._viewById.set(device.id, view);
                listItem = this._createSidebarListItem(view);
                this._listItemById.set(device.id, listItem);
                this._sidebarList.insertBefore(listItem, this._sidebarListSpacer);
            }

            listItem._title.textContent = device.adbModel;
            listItem._status.textContent = device.adbConnected ? WebInspector.UIString("Connected") : WebInspector.UIString("Pending Authorization");
            listItem.classList.toggle("device-connected", device.adbConnected);
            view.update(device);
        }

        if (selectedRemoved)
            this._selectSidebarListItem(this._discoveryListItem, this._discoveryView);

        this._updateFooter();
    },

    /**
     * @param {!WebInspector.Widget} view
     * @return {!Element}
     */
    _createSidebarListItem: function(view)
    {
        var listItem = createElementWithClass("div", "devices-sidebar-item");
        listItem.addEventListener("click", this._selectSidebarListItem.bind(this, listItem, view));
        listItem._title = listItem.createChild("div", "devices-sidebar-item-title");
        listItem._status = listItem.createChild("div", "devices-sidebar-item-status");
        return listItem;
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _devicesDiscoveryConfigChanged: function(event)
    {
        var discoverUsbDevices = /** @type {boolean} */ (event.data["discoverUsbDevices"]);
        var portForwardingEnabled = /** @type {boolean} */ (event.data["portForwardingEnabled"]);
        var portForwardingConfig = /** @type {!Adb.PortForwardingConfig} */ (event.data["portForwardingConfig"]);
        this._discoveryView.discoveryConfigChanged(discoverUsbDevices, portForwardingEnabled, portForwardingConfig);
    },

    /**
     * @param {!WebInspector.Event} event
     */
    _devicesPortForwardingStatusChanged: function(event)
    {
        var status = /** @type {!Adb.PortForwardingStatus} */ (event.data);
        for (var deviceId in status) {
            var view = this._viewById.get(deviceId);
            if (view)
                view.portForwardingStatusChanged(status[deviceId]);
        }
        for (var deviceId of this._viewById.keys()) {
            var view = this._viewById.get(deviceId);
            if (view && !(deviceId in status))
                view.portForwardingStatusChanged({ports: {}, browserId: ""});
        }
    },

    _updateFooter: function()
    {
        this._deviceCountSpan.textContent =
            !this._devices.length ? WebInspector.UIString("No devices detected.") :
                this._devices.length === 1 ? WebInspector.UIString("1 device detected.") : WebInspector.UIString("%d devices detected.", this._devices.length);
    },

    /**
     * @override
     */
    wasShown: function()
    {
        WebInspector.PanelWithSidebar.prototype.wasShown.call(this);
        InspectorFrontendHost.setDevicesUpdatesEnabled(true);
    },

    /**
     * @override
     */
    willHide: function()
    {
        WebInspector.PanelWithSidebar.prototype.wasShown.call(this);
        InspectorFrontendHost.setDevicesUpdatesEnabled(false);
    },

    __proto__: WebInspector.VBox.prototype
}

/**
 * @return {!WebInspector.DevicesView}
 */
WebInspector.DevicesView._instance = function()
{
    if (!WebInspector.DevicesView._instanceObject)
        WebInspector.DevicesView._instanceObject = new WebInspector.DevicesView();
    return WebInspector.DevicesView._instanceObject;
}


/**
 * @constructor
 * @extends {WebInspector.VBox}
 * @implements {WebInspector.ListWidget.Delegate}
 */
WebInspector.DevicesView.DiscoveryView = function()
{
    WebInspector.VBox.call(this);
    this.setMinimumSize(100, 100);
    this.element.classList.add("discovery-view");

    this.contentElement.createChild("div", "hbox device-text-row").createChild("div", "view-title").textContent = WebInspector.UIString("Settings");

    var discoverUsbDevicesCheckbox = createCheckboxLabel(WebInspector.UIString("Discover USB devices"));
    discoverUsbDevicesCheckbox.classList.add("usb-checkbox");
    this.element.appendChild(discoverUsbDevicesCheckbox);
    this._discoverUsbDevicesCheckbox = discoverUsbDevicesCheckbox.checkboxElement;
    this._discoverUsbDevicesCheckbox.addEventListener("click", this._updateDiscoveryConfig.bind(this), false);

    var help = this.element.createChild("div", "discovery-help");
    help.createChild("span").textContent = WebInspector.UIString("Need help? Read Chrome ");
    help.appendChild(WebInspector.linkifyURLAsNode("https://developers.google.com/chrome-developer-tools/docs/remote-debugging", WebInspector.UIString("remote debugging documentation."), undefined, true));

    var portForwardingHeader = this.element.createChild("div", "port-forwarding-header");
    var portForwardingEnabledCheckbox = createCheckboxLabel(WebInspector.UIString("Port forwarding"));
    portForwardingEnabledCheckbox.classList.add("port-forwarding-checkbox");
    portForwardingHeader.appendChild(portForwardingEnabledCheckbox);
    this._portForwardingEnabledCheckbox = portForwardingEnabledCheckbox.checkboxElement;
    this._portForwardingEnabledCheckbox.addEventListener("click", this._updateDiscoveryConfig.bind(this), false);

    var portForwardingFooter = this.element.createChild("div", "port-forwarding-footer");
    portForwardingFooter.createChild("span").textContent = WebInspector.UIString("Define the listening port on your device that maps to a port accessible from your development machine. ");
    portForwardingFooter.appendChild(WebInspector.linkifyURLAsNode("https://developer.chrome.com/devtools/docs/remote-debugging#port-forwarding", WebInspector.UIString("Learn more"), undefined, true));

    this._list = new WebInspector.ListWidget(this);
    this._list.registerRequiredCSS("devices/devicesView.css");
    this._list.element.classList.add("port-forwarding-list");
    var placeholder = createElementWithClass("div", "port-forwarding-list-empty");
    placeholder.textContent = WebInspector.UIString("No rules");
    this._list.setEmptyPlaceholder(placeholder);
    this._list.show(this.element);

    this.element.appendChild(createTextButton(WebInspector.UIString("Add rule"), this._addRuleButtonClicked.bind(this), "add-rule-button"));

    /** @type {!Array<!Adb.PortForwardingRule>} */
    this._portForwardingConfig = [];
}

WebInspector.DevicesView.DiscoveryView.prototype = {
    _addRuleButtonClicked: function()
    {
        this._list.addNewItem(this._portForwardingConfig.length, {port: "", address: ""});
    },

    /**
     * @param {boolean} discoverUsbDevices
     * @param {boolean} portForwardingEnabled
     * @param {!Adb.PortForwardingConfig} portForwardingConfig
     */
    discoveryConfigChanged: function(discoverUsbDevices, portForwardingEnabled, portForwardingConfig)
    {
        this._discoverUsbDevicesCheckbox.checked = discoverUsbDevices;
        this._portForwardingEnabledCheckbox.checked = portForwardingEnabled;

        this._portForwardingConfig = [];
        this._list.clear();
        for (var key of Object.keys(portForwardingConfig)) {
            var rule = /** @type {!Adb.PortForwardingRule} */ ({port: key, address: portForwardingConfig[key]});
            this._portForwardingConfig.push(rule);
            this._list.appendItem(rule, true);
        }
    },

    /**
     * @override
     * @param {*} item
     * @param {boolean} editable
     * @return {!Element}
     */
    renderItem: function(item, editable)
    {
        var rule = /** @type {!Adb.PortForwardingRule} */ (item);
        var element = createElementWithClass("div", "port-forwarding-list-item");
        var port = element.createChild("div", "port-forwarding-value port-forwarding-port");
        port.createChild("span", "port-localhost").textContent = WebInspector.UIString("localhost:");
        port.createTextChild(rule.port);
        element.createChild("div", "port-forwarding-separator");
        element.createChild("div", "port-forwarding-value").textContent = rule.address;
        return element;
    },

    /**
     * @override
     * @param {*} item
     * @param {number} index
     */
    removeItemRequested: function(item, index)
    {
        this._portForwardingConfig.splice(index, 1);
        this._list.removeItem(index);
        this._updateDiscoveryConfig();
    },

    /**
     * @override
     * @param {*} item
     * @param {!WebInspector.ListWidget.Editor} editor
     * @param {boolean} isNew
     */
    commitEdit: function(item, editor, isNew)
    {
        var rule = /** @type {!Adb.PortForwardingRule} */ (item);
        rule.port = editor.control("port").value.trim();
        rule.address = editor.control("address").value.trim();
        if (isNew)
            this._portForwardingConfig.push(rule);
        this._updateDiscoveryConfig();
    },

    /**
     * @override
     * @param {*} item
     * @return {!WebInspector.ListWidget.Editor}
     */
    beginEdit: function(item)
    {
        var rule = /** @type {!Adb.PortForwardingRule} */ (item);
        var editor = this._createEditor();
        editor.control("port").value = rule.port;
        editor.control("address").value = rule.address;
        return editor;
    },

    /**
     * @return {!WebInspector.ListWidget.Editor}
     */
    _createEditor: function()
    {
        if (this._editor)
            return this._editor;

        var editor = new WebInspector.ListWidget.Editor();
        this._editor = editor;
        var content = editor.contentElement();
        var fields = content.createChild("div", "port-forwarding-edit-row");
        fields.createChild("div", "port-forwarding-value port-forwarding-port").appendChild(editor.createInput("port", "text", "Device port (3333)", portValidator.bind(this)));
        fields.createChild("div", "port-forwarding-separator port-forwarding-separator-invisible");
        fields.createChild("div", "port-forwarding-value").appendChild(editor.createInput("address", "text", "Local address (dev.example.corp:3333)", addressValidator));
        return editor;

        /**
         * @param {*} item
         * @param {number} index
         * @param {!HTMLInputElement|!HTMLSelectElement} input
         * @this {WebInspector.DevicesView.DiscoveryView}
         * @return {boolean}
         */
        function portValidator(item, index, input)
        {
            var value = input.value.trim();
            var match = value.match(/^(\d+)$/);
            if (!match)
                return false;
            var port = parseInt(match[1], 10);
            if (port < 1024 || port > 65535)
                return false;
            for (var i = 0; i < this._portForwardingConfig.length; ++i) {
                if (i !== index && this._portForwardingConfig[i].port === value)
                    return false;
            }
            return true;
        }

        /**
         * @param {*} item
         * @param {number} index
         * @param {!HTMLInputElement|!HTMLSelectElement} input
         * @return {boolean}
         */
        function addressValidator(item, index, input)
        {
            var match = input.value.trim().match(/^([a-zA-Z0-9\.\-_]+):(\d+)$/);
            if (!match)
                return false;
            var port = parseInt(match[2], 10);
            return port <= 65535;
        }
    },

    _updateDiscoveryConfig: function()
    {
        var configMap = /** @type {!Adb.PortForwardingConfig} */ ({});
        for (var rule of this._portForwardingConfig)
            configMap[rule.port] = rule.address;
        InspectorFrontendHost.setDevicesDiscoveryConfig(this._discoverUsbDevicesCheckbox.checked, this._portForwardingEnabledCheckbox.checked, configMap);
    },

    __proto__: WebInspector.VBox.prototype
}


/**
 * @constructor
 * @extends {WebInspector.VBox}
 */
WebInspector.DevicesView.DeviceView = function()
{
    WebInspector.VBox.call(this);
    this.setMinimumSize(100, 100);
    this.contentElement.classList.add("device-view");

    var topRow = this.contentElement.createChild("div", "hbox device-text-row");
    this._deviceTitle = topRow.createChild("div", "view-title");
    this._deviceSerial = topRow.createChild("div", "device-serial");
    this._portStatus = this.contentElement.createChild("div", "device-port-status hidden");

    this._deviceOffline = this.contentElement.createChild("div");
    this._deviceOffline.textContent = WebInspector.UIString("Pending authentication: please accept debugging session on the device.");

    this._noBrowsers = this.contentElement.createChild("div");
    this._noBrowsers.textContent = WebInspector.UIString("No browsers detected.");

    this._browsers = this.contentElement.createChild("div", "device-browser-list vbox");

    /** @type {!Map<string, !WebInspector.DevicesView.BrowserSection>} */
    this._browserById = new Map();

    this._device = null;
}

/** @typedef {!{browser: ?Adb.Browser, element: !Element, title: !Element, pages: !Element, viewMore: !Element, newTab: !Element, pageSections: !Map<string, !WebInspector.DevicesView.PageSection>}} */
WebInspector.DevicesView.BrowserSection;

/** @typedef {!{page: ?Adb.Page, element: !Element, title: !Element, url: !Element, inspect: !Element}} */
WebInspector.DevicesView.PageSection;

WebInspector.DevicesView.DeviceView.prototype = {
    /**
     * @param {!Adb.Device} device
     */
    update: function(device)
    {
        if (!this._device || this._device.adbModel !== device.adbModel)
            this._deviceTitle.textContent = device.adbModel;

        if (!this._device || this._device.adbSerial !== device.adbSerial)
            this._deviceSerial.textContent = "#" + device.adbSerial;

        this._deviceOffline.classList.toggle("hidden", device.adbConnected);
        this._noBrowsers.classList.toggle("hidden", !device.adbConnected || !!device.browsers.length);
        this._browsers.classList.toggle("hidden", !device.adbConnected || !device.browsers.length);

        var browserIds = new Set();
        for (var browser of device.browsers)
            browserIds.add(browser.id);

        for (var browserId of this._browserById.keys()) {
            if (!browserIds.has(browserId)) {
                this._browserById.get(browserId).element.remove();
                this._browserById.remove(browserId);
            }
        }

        for (var browser of device.browsers) {
            var section = this._browserById.get(browser.id);
            if (!section) {
                section = this._createBrowserSection();
                this._browserById.set(browser.id, section);
                this._browsers.appendChild(section.element);
            }
            this._updateBrowserSection(section, browser);
        }

        this._device = device;
    },

    /**
     * @return {!WebInspector.DevicesView.BrowserSection}
     */
    _createBrowserSection: function()
    {
        var element = createElementWithClass("div", "vbox flex-none");
        var topRow = element.createChild("div", "");
        var title = topRow.createChild("div", "device-browser-title");

        var newTabRow = element.createChild("div", "device-browser-new-tab");
        newTabRow.createChild("div", "").textContent = WebInspector.UIString("New tab:");
        var newTabInput = newTabRow.createChild("input", "");
        newTabInput.type = "text";
        newTabInput.placeholder = WebInspector.UIString("Enter URL");
        newTabInput.addEventListener("keydown", newTabKeyDown, false);
        var newTabButton = createTextButton(WebInspector.UIString("Open"), openNewTab);
        newTabRow.appendChild(newTabButton);

        var pages = element.createChild("div", "device-page-list vbox");

        var viewMore = element.createChild("div", "device-view-more");
        viewMore.addEventListener("click", viewMoreClick, false);
        updateViewMoreTitle();

        var section = {browser: null, element: element, title: title, pages: pages, viewMore: viewMore, newTab: newTabRow, pageSections: new Map()};
        return section;

        function viewMoreClick()
        {
            pages.classList.toggle("device-view-more-toggled");
            updateViewMoreTitle();
        }

        function updateViewMoreTitle()
        {
            viewMore.textContent = pages.classList.contains("device-view-more-toggled") ? WebInspector.UIString("View less tabs\u2026") : WebInspector.UIString("View more tabs\u2026");
        }

        /**
         * @param {!Event} event
         */
        function newTabKeyDown(event)
        {
            if (event.key === "Enter") {
                event.consume(true);
                openNewTab();
            }
        }

        function openNewTab()
        {
            if (section.browser) {
                InspectorFrontendHost.openRemotePage(section.browser.id, newTabInput.value.trim() || "about:blank");
                newTabInput.value = "";
            }
        }
    },

    /**
     * @param {!WebInspector.DevicesView.BrowserSection} section
     * @param {!Adb.Browser} browser
     */
    _updateBrowserSection: function(section, browser)
    {
        if (!section.browser || section.browser.adbBrowserName !== browser.adbBrowserName || section.browser.adbBrowserVersion !== browser.adbBrowserVersion) {
            if (browser.adbBrowserVersion)
                section.title.textContent = String.sprintf("%s (%s)", browser.adbBrowserName, browser.adbBrowserVersion);
            else
                section.title.textContent = browser.adbBrowserName;
        }

        var pageIds = new Set();
        for (var page of browser.pages)
            pageIds.add(page.id);

        for (var pageId of section.pageSections.keys()) {
            if (!pageIds.has(pageId)) {
                section.pageSections.get(pageId).element.remove();
                section.pageSections.remove(pageId);
            }
        }

        for (var index = 0; index < browser.pages.length; ++index) {
            var page = browser.pages[index];
            var pageSection = section.pageSections.get(page.id);
            if (!pageSection) {
                pageSection = this._createPageSection();
                section.pageSections.set(page.id, pageSection);
                section.pages.appendChild(pageSection.element);
            }
            this._updatePageSection(pageSection, page);
            if (!index && section.pages.firstChild !== pageSection.element)
                section.pages.insertBefore(pageSection.element, section.pages.firstChild);
        }

        var kViewMoreCount = 3;
        for (var index = 0, element = section.pages.firstChild; element; element = element.nextSibling, ++index)
            element.classList.toggle("device-view-more-page", index >= kViewMoreCount);
        section.viewMore.classList.toggle("device-needs-view-more", browser.pages.length > kViewMoreCount);
        section.newTab.classList.toggle("hidden", !browser.adbBrowserChromeVersion);
        section.browser = browser;
    },

    /**
     * @return {!WebInspector.DevicesView.PageSection}
     */
    _createPageSection: function()
    {
        var element = createElementWithClass("div", "vbox");

        var titleRow = element.createChild("div", "device-page-title-row");
        var title = titleRow.createChild("div", "device-page-title");
        var inspect = createTextButton(WebInspector.UIString("Inspect"), doAction.bind(null, "inspect"), "device-inspect-button");
        titleRow.appendChild(inspect);

        var toolbar = new WebInspector.Toolbar("");
        toolbar.appendToolbarItem(new WebInspector.ToolbarMenuButton(appendActions));
        titleRow.appendChild(toolbar.element);

        var url = element.createChild("div", "device-page-url");
        var section = {page: null, element: element, title: title, url: url, inspect: inspect};
        return section;

        /**
         * @param {!WebInspector.ContextMenu} contextMenu
         */
        function appendActions(contextMenu)
        {
            contextMenu.appendItem(WebInspector.UIString("Reload"), doAction.bind(null, "reload"));
            contextMenu.appendItem(WebInspector.UIString("Focus"), doAction.bind(null, "activate"));
            contextMenu.appendItem(WebInspector.UIString("Close"), doAction.bind(null, "close"));
        }

        /**
         * @param {string} action
         */
        function doAction(action)
        {
            if (section.page)
                InspectorFrontendHost.performActionOnRemotePage(section.page.id, action);
        }
    },

    /**
     * @param {!WebInspector.DevicesView.PageSection} section
     * @param {!Adb.Page} page
     */
    _updatePageSection: function(section, page)
    {
        if (!section.page || section.page.name !== page.name) {
            section.title.textContent = page.name;
            section.title.title = page.name;
        }
        if (!section.page || section.page.url !== page.url) {
            section.url.textContent = "";
            section.url.appendChild(WebInspector.linkifyURLAsNode(page.url, undefined, undefined, true));
        }
        section.inspect.disabled = page.adbAttachedForeign;

        section.page = page;
    },

    /**
     * @param {!Adb.DevicePortForwardingStatus} status
     */
    portForwardingStatusChanged: function(status)
    {
        var json = JSON.stringify(status);
        if (json === this._cachedPortStatus)
            return;
        this._cachedPortStatus = json;

        this._portStatus.removeChildren();
        this._portStatus.createChild("div", "device-port-status-text").textContent = WebInspector.UIString("Port Forwarding:");
        var connected = [];
        var transient = [];
        var error = [];
        var empty = true;
        for (var port in status.ports) {
            if (!status.ports.hasOwnProperty(port))
                continue;

            empty = false;
            var portStatus = status.ports[port];
            var portNumber = createElementWithClass("div", "device-view-port-number monospace");
            portNumber.textContent = ":" + port;
            if (portStatus >= 0)
                this._portStatus.appendChild(portNumber);
            else
                this._portStatus.insertBefore(portNumber, this._portStatus.firstChild);

            var portIcon = createElementWithClass("div", "device-view-port-icon");
            if (portStatus >= 0) {
                connected.push(port);
            } else if (portStatus === -1 || portStatus === -2) {
                portIcon.classList.add("device-view-port-icon-transient");
                transient.push(port);
            } else if (portStatus < 0) {
                portIcon.classList.add("device-view-port-icon-error");
                error.push(port);
            }
            this._portStatus.insertBefore(portIcon, portNumber);
        }

        var title = [];
        if (connected.length)
            title.push(WebInspector.UIString("Connected: %s", connected.join(", ")));
        if (transient.length)
            title.push(WebInspector.UIString("Transient: %s", transient.join(", ")));
        if (error.length)
            title.push(WebInspector.UIString("Error: %s", error.join(", ")));
        this._portStatus.title = title.join("; ");
        this._portStatus.classList.toggle("hidden", empty);
    },

    __proto__: WebInspector.VBox.prototype
}
