blob: 6d96a757375b1fc9d453988c124d3bfadd17d62b [file] [log] [blame]
// Copyright 2019 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.
cr.define('extensions', function() {
'use strict';
/** @interface */
class ActivityLogEventDelegate {
/** @return {!ChromeEvent} */
getOnExtensionActivity() {}
}
/**
* Process activity for the stream. In the case of content scripts, we split
* the activity for every script invoked.
* @param {!chrome.activityLogPrivate.ExtensionActivity}
* activity
* @return {!Array<!extensions.StreamItem>}
*/
function processActivityForStream(activity) {
const activityType = activity.activityType;
const timestamp = activity.time;
const isContentScript = activityType ===
chrome.activityLogPrivate.ExtensionActivityType.CONTENT_SCRIPT;
const args = isContentScript ? JSON.stringify([]) : activity.args;
let streamItemNames = [activity.apiCall];
// TODO(kelvinjiang): Reuse logic from activity_log_history and refactor
// some of the processing code into a separate file in a follow up CL.
if (isContentScript) {
streamItemNames = activity.args ? JSON.parse(activity.args) : [];
assert(Array.isArray(streamItemNames), 'Invalid data for script names.');
}
const other = activity.other;
const webRequestInfo = other && other.webRequest;
return streamItemNames.map(name => ({
args,
argUrl: activity.argUrl,
activityType,
name,
pageUrl: activity.pageUrl,
timestamp,
webRequestInfo,
expanded: false,
}));
}
const ActivityLogStream = Polymer({
is: 'activity-log-stream',
properties: {
/** @type {string} */
extensionId: String,
/** @type {!extensions.ActivityLogEventDelegate} */
delegate: Object,
/** @private */
isStreamOn_: {
type: Boolean,
value: false,
},
/** @private {!Array<!extensions.StreamItem>} */
activityStream_: {
type: Array,
value: () => [],
},
/** @private {!Array<!extensions.StreamItem>} */
filteredActivityStream_: {
type: Array,
computed:
'computeFilteredActivityStream_(activityStream_.*, lastSearch_)',
},
/** @private */
lastSearch_: {
type: String,
value: '',
},
},
listeners: {
'resize-stream': 'onResizeStream_',
},
/**
* Instance of |extensionActivityListener_| bound to |this|.
* @private {!Function}
*/
listenerInstance_: () => {},
/** @override */
attached: function() {
// Since this component is not restamped, this will only be called once
// in its lifecycle.
this.listenerInstance_ = this.extensionActivityListener_.bind(this);
this.startStream();
},
/** @private */
onResizeStream_: function(e) {
this.$$('iron-list').notifyResize();
},
clearStream: function() {
this.splice('activityStream_', 0, this.activityStream_.length);
},
startStream: function() {
if (this.isStreamOn_) {
return;
}
this.isStreamOn_ = true;
this.delegate.getOnExtensionActivity().addListener(
this.listenerInstance_);
},
pauseStream: function() {
if (!this.isStreamOn_) {
return;
}
this.delegate.getOnExtensionActivity().removeListener(
this.listenerInstance_);
this.isStreamOn_ = false;
},
/** @private */
onToggleButtonClick_: function() {
if (this.isStreamOn_) {
this.pauseStream();
} else {
this.startStream();
}
},
/**
* @private
* @return {boolean}
*/
isStreamEmpty_: function() {
return this.activityStream_.length == 0;
},
/**
* @private
* @return {boolean}
*/
isFilteredStreamEmpty_: function() {
return this.filteredActivityStream_.length == 0;
},
/**
* @private
* @return {boolean}
*/
shouldShowEmptySearchMessage_: function() {
return !this.isStreamEmpty_() && this.isFilteredStreamEmpty_();
},
/**
* @private
* @param {!chrome.activityLogPrivate.ExtensionActivity} activity
*/
extensionActivityListener_: function(activity) {
if (activity.extensionId != this.extensionId) {
return;
}
this.splice(
'activityStream_', this.activityStream_.length, 0,
...processActivityForStream(activity));
// Used to update the scrollbar.
this.$$('iron-list').notifyResize();
},
/**
* @private
* @param {!CustomEvent<string>} e
*/
onSearchChanged_: function(e) {
// Remove all whitespaces from the search term, as API call names and
// URLs should not contain any whitespace. As of now, only single term
// search queries are allowed.
const searchTerm = e.detail.replace(/\s+/g, '').toLowerCase();
if (searchTerm === this.lastSearch_) {
return;
}
this.lastSearch_ = searchTerm;
},
/**
* @private
* @return {!Array<!extensions.StreamItem>}
*/
computeFilteredActivityStream_: function() {
if (!this.lastSearch_) {
return this.activityStream_.slice();
}
// Match on these properties for each activity.
const propNames = [
'name',
'pageUrl',
'activityType',
];
return this.activityStream_.filter(act => {
return propNames.some(prop => {
return act[prop] &&
act[prop].toLowerCase().includes(this.lastSearch_);
});
});
},
});
return {
ActivityLogStream: ActivityLogStream,
ActivityLogEventDelegate: ActivityLogEventDelegate,
};
});