blob: 253d6afe4559a81d7fb0eead70c6d4fee1418c3c [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {inflate} from 'pako';
import {assertTrue} from '../base/logging';
import {globals} from './globals';
function readText(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
if (typeof reader.result === 'string') {
return resolve(reader.result);
}
};
reader.onerror = err => {
reject(err);
};
reader.readAsText(blob);
});
}
export async function isLegacyTrace(file: File): Promise<boolean> {
const fileName = file.name.toLowerCase();
if (fileName.endsWith('.json') || fileName.endsWith('.json.gz') ||
fileName.endsWith('.zip') || fileName.endsWith('.ctrace') ||
fileName.endsWith('.html')) {
return true;
}
// Sometimes systrace formatted traces end with '.trace'. This is a
// little generic to assume all such traces are systrace format though
// so we read the beginning of the file and check to see if is has the
// systrace header (several comment lines):
if (fileName.endsWith('.trace')) {
const header = await readText(file.slice(0, 512));
const lines = header.split('\n');
let commentCount = 0;
for (const line of lines) {
if (line.startsWith('#')) {
commentCount++;
}
}
if (commentCount > 5) {
return true;
}
}
return false;
}
export function openFileWithLegacyTraceViewer(file: File) {
const reader = new FileReader();
reader.onload = () => {
if (reader.result instanceof ArrayBuffer) {
return openBufferWithLegacyTraceViewer(
file.name, reader.result, reader.result.byteLength);
} else {
const str = reader.result as string;
return openBufferWithLegacyTraceViewer(file.name, str, str.length);
}
};
reader.onerror = err => {
console.error(err);
};
if (file.name.endsWith('.gz') || file.name.endsWith('.zip') ||
file.name.endsWith('.ctrace')) {
reader.readAsArrayBuffer(file);
} else {
reader.readAsText(file);
}
}
export function openBufferWithLegacyTraceViewer(
name: string, data: ArrayBuffer|string, size: number) {
if (data instanceof ArrayBuffer) {
assertTrue(size <= data.byteLength);
if (size !== data.byteLength) {
data = data.slice(0, size);
}
// Handle .ctrace files.
const enc = new TextDecoder('utf-8');
const header = enc.decode(data.slice(0, 7));
if (header === 'TRACE:\n') {
data = inflate(new Uint8Array(data.slice(7, size)), {to: 'string'});
}
}
document.body.style.transition =
'filter 1s ease, transform 1s cubic-bezier(0.985, 0.005, 1.000, 0.225)';
document.body.style.filter = 'grayscale(1) blur(10px) opacity(0)';
document.body.style.transform = 'scale(0)';
const transitionPromise = new Promise(resolve => {
document.body.addEventListener('transitionend', (e: TransitionEvent) => {
if (e.propertyName === 'transform') {
resolve();
}
});
});
const loadPromise = new Promise(resolve => {
fetch('/assets/catapult_trace_viewer.html').then(resp => {
resp.text().then(content => {
resolve(content);
});
});
});
Promise.all([loadPromise, transitionPromise]).then(args => {
const fetchResult = args[0] as string;
replaceWindowWithTraceViewer(name, data, fetchResult);
});
}
// Replaces the contents of the current window with the Catapult's legacy
// trace viewer HTML, passed in |htmlContent|.
// This is in its own function to avoid leaking variables from the current
// document we are about to destroy.
function replaceWindowWithTraceViewer(
name: string, data: ArrayBuffer|string, htmlContent: string) {
globals.shutdown();
const newWin = window.open('', '_self') as Window;
newWin.document.open('text/html', 'replace');
newWin.document.addEventListener('readystatechange', () => {
const doc = newWin.document;
if (doc.readyState !== 'complete') return;
const ctl = doc.querySelector('x-profiling-view') as TraceViewerAPI;
ctl.setActiveTrace(name, data);
});
newWin.document.write(htmlContent);
newWin.document.close();
}
// TraceViewer method that we wire up to trigger the file load.
interface TraceViewerAPI extends Element {
setActiveTrace(name: string, data: ArrayBuffer|string): void;
}