blob: 55c76a9055506ffe181e20e2f86a52050fb8ec48 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// <if expr="is_ios">
import 'chrome://resources/js/ios/web_ui.js';
// </if>
import {addWebUiListener} from 'chrome://resources/js/cr.js';
import {$} from 'chrome://resources/js/util_ts.js';
/**
* Main entry point called once the page has loaded.
*/
function onLoad() {
const view = NetExportView.getInstance();
view.initialize();
}
document.addEventListener('DOMContentLoaded', onLoad);
const kIdStateDivUninitialized = 'state-uninitialized';
const kIdStateDivInitial = 'state-initial';
const kIdStateDivLogging = 'state-logging';
const kIdStateDivStopped = 'state-stopped';
const kIdStartLoggingButton = 'start-logging';
const kIdStopLoggingButton = 'stop-logging';
const kIdEmailLogButton = 'mobile-email';
const kIdShowFileButton = 'show-file';
const kIdCaptureModeLogging = 'capture-mode-logging';
const kIdFilePathLogging = 'file-path-logging';
const kIdCaptureModeStopped = 'capture-mode-stopped';
const kIdFilePathStoppedLogging = 'file-path-stopped';
const kIdStartOverButton = 'startover';
const kIdPrivacyReadMoreLink = 'privacy-read-more-link';
const kIdPrivacyReadMoreDiv = 'privacy-read-more';
const kIdTooBigReadMoreLink = 'toobig-read-more-link';
const kIdTooBigReadMoreDiv = 'toobig-read-more';
const kIdLogMaxFileSizeInput = 'log-max-filesize';
/**
* This class handles the presentation of the net-export view. Used as a
* singleton.
*/
class NetExportView {
constructor() {
this.infoForLoggedFile_ = null;
}
initialize() {
// Tell NetExportMessageHandler to notify the UI of future state changes
// from this point on and listen for net-log-info-changed events.
addWebUiListener(
'net-log-info-changed', info => this.onExportNetLogInfoChanged_(info));
chrome.send('enableNotifyUIWithState');
}
/**
* Starts saving NetLog data to a file.
*/
onStartLogging_() {
// Determine the capture mode to use.
const logMode =
document.querySelector('input[name="log-mode"]:checked').value;
// Determine the maximum file size, as the number of bytes (or -1 to mean
// no limit)
let maxLogFileSizeBytes = -1;
const fileSizeString = $(kIdLogMaxFileSizeInput).value;
const numMegabytes = parseFloat(fileSizeString);
if (!isNaN(numMegabytes)) {
// Convert to an integral number of bytes.
maxLogFileSizeBytes = Math.round(numMegabytes * 1024 * 1024);
}
chrome.send('startNetLog', [logMode, maxLogFileSizeBytes]);
}
/**
* Stops saving NetLog data to a file.
*/
onStopLogging_() {
chrome.send('stopNetLog');
}
/**
* Sends NetLog data via email from browser (mobile only).
*/
onSendEmail_() {
chrome.send('sendNetLog');
}
/**
* Reveals the log file in the shell (i.e. selects it in the Finder on
* Mac).
*/
onShowFile_() {
chrome.send('showFile');
}
/**
* Transitions back to the "Start logging to disk" state.
*/
onStartOver_() {
this.infoForLoggedFile_ = null;
this.renderInitial_();
}
/**
* Updates the UI to reflect the current state. The state transitions are
* sent by the browser controller (NetLogFileWriter):
*
* * UNINITIALIZED - This is the initial state when net-export is opened
* for the first time, or there was an error during initialization.
* This state is short-lived and likely not observed; will
* immediately transition to INITIALIZING).
*
* * INITIALIZING - On desktop UI this is pretty much a no-op. On the
* mobile UI, this state is when the controller checks the disk for
* a previous net-log file (from past run of the browser). After
* success will transition to NOT_LOGGING. On failure will
* transition to UNINITIALIZED (rare).
*
* * NOT_LOGGING - This is the steady-state. It means initialization
* completed and we are not currently logging. Being in this state
* either means:
* (1) We were logging and then the user stopped (earlier states
* were STATE_LOGGING / STATE_STOPPING_LOG).
* (2) We have never started logging (previous state was
* INITIALIZING).
*
* * STARTING_LOG - This state means the user has clicked the "Start log"
* button and logging is about to get started (files may not have
* been created yet).
*
* * LOGGING - This state means that logging is currently in progress.
* The destination path of the log, and the capture mode are known
* and will be reflected in the parameters.
*
* * STOPPING_LOG - This state means the user has clicked the "Stop
* logging" button, and the log file is in the process of being
* finalized. Once the state transitions to NOT_LOGGING then the log
* is complete, and can safely be copied/emailed.
*/
onExportNetLogInfoChanged_(info) {
switch (info.state) {
case 'UNINITIALIZED':
case 'INITIALIZING':
this.renderUninitialized_();
break;
case 'NOT_LOGGING':
if (this.infoForLoggedFile_) {
// There is no "stopped logging" state. We manufacture that in the
// UI in response to a transition from LOGGING --> NOT_LOGGING.
this.renderStoppedLogging_(this.infoForLoggedFile_);
// TODO(eroman): prevent future state transitions. In desktop UI
// could start logging in a new tab, and it would reset this one.
} else if (info.logExists) {
// In the mobile UI, initialization may have found a
// pre-existing log file.
this.renderStoppedLogging_(info);
} else {
this.renderInitial_();
}
break;
case 'STARTING_LOG':
// This is a short-lived state, no need to do anything special.
// Disabling the buttons would be nice, however it is not crucial as
// the controller will reject commands while in this state anyway.
this.renderInitial_();
break;
case 'LOGGING':
// Cache the last information for this logging session, so once
// logging is stopped will know what path information to display.
this.infoForLoggedFile_ = info;
this.renderLogging_(info);
break;
case 'STOPPING_LOG':
// This is a short-lived state, no need to do anything special.
this.renderLogging_(info);
break;
}
}
/**
* Updates the UI to display the "uninitialized" state. This is only
* visible for a short period of time, or longer if initialization failed
* (and didn't transition to a different state).
*/
renderUninitialized_() {
this.showStateDiv_(kIdStateDivUninitialized);
}
/**
* Updates the UI to display the "initial" state. This is the state when
* logging has not been started yet, and there are controls to start
* logging.
*/
renderInitial_() {
this.showStateDiv_(kIdStateDivInitial);
$(kIdStartLoggingButton).onclick = this.onStartLogging_.bind(this);
}
/**
* Updates the UI to display the "logging" state. This is the state while
* capturing is in progress and being written to disk.
*/
renderLogging_(info) {
this.showStateDiv_(kIdStateDivLogging);
this.setFavicon_(
'data:image/svg+xml,<svg version="1.1" ' +
'xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">' +
'<circle cx="16" cy="16" r="14" fill="red" stroke="black" /></svg>');
$(kIdStopLoggingButton).onclick = this.onStopLogging_.bind(this);
$(kIdCaptureModeLogging).textContent = this.getCaptureModeText_(info);
$(kIdFilePathLogging).textContent = info.file;
}
/*
* Updates the UI to display the state when logging has stopped.
*/
renderStoppedLogging_(info) {
this.setFavicon_('data:image/x-icon;base64,');
this.showStateDiv_(kIdStateDivStopped);
// The email button is only available in the mobile UI.
if ($(kIdEmailLogButton)) {
$(kIdEmailLogButton).onclick = this.onSendEmail_.bind(this);
}
// The show file button is only available in the desktop UI.
if ($(kIdShowFileButton)) {
$(kIdShowFileButton).onclick = this.onShowFile_.bind(this);
}
$(kIdStartOverButton).onclick = this.onStartOver_.bind(this);
$(kIdFilePathStoppedLogging).textContent = info.file;
$(kIdCaptureModeStopped).textContent = this.getCaptureModeText_(info);
// Hook up the "read more..." link for privacy information.
$(kIdPrivacyReadMoreLink).onclick =
this.showPrivacyReadMore_.bind(this, true);
this.showPrivacyReadMore_(false);
// Hook up the "read more..." link for reducing log size information.
$(kIdTooBigReadMoreLink).onclick =
this.showTooBigReadMore_.bind(this, true);
this.showTooBigReadMore_(false);
}
/**
* Gets the textual label for a capture mode from the HTML.
*/
getCaptureModeText_(info) {
// TODO(eroman): Should not hardcode "Unknown" (will not work properly if
// the HTML is internationalized).
if (!info.logCaptureModeKnown) {
return 'Unknown';
}
const radioButton = document.querySelector(
'input[name="log-mode"][value="' + info.captureMode + '"]');
if (!radioButton) {
return 'Unknown';
}
return radioButton.parentElement.textContent;
}
showPrivacyReadMore_(show) {
$(kIdPrivacyReadMoreDiv).hidden = !show;
$(kIdPrivacyReadMoreLink).hidden = show;
}
showTooBigReadMore_(show) {
$(kIdTooBigReadMoreDiv).hidden = !show;
$(kIdTooBigReadMoreLink).hidden = show;
}
showStateDiv_(divId) {
const kAllDivIds = [
kIdStateDivUninitialized,
kIdStateDivInitial,
kIdStateDivLogging,
kIdStateDivStopped,
];
for (const curDivId of kAllDivIds) {
$(curDivId).hidden = divId !== curDivId;
}
}
/**
* Sets the icon for the tab to reflect current capturing state.
*/
setFavicon_(dataUrl) {
document.getElementById('fav-icon').href = dataUrl;
}
static getInstance() {
return instance || (instance = new NetExportView());
}
}
/** @type {?NetExportView} */
let instance = null;