Home | History | Annotate | Download | only in tquery
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright 2015 The Chromium Authors. All rights reserved.
      4 Use of this source code is governed by a BSD-style license that can be
      5 found in the LICENSE file.
      6 -->
      7 
      8 <link rel="import" href="/base/task.html">
      9 <link rel="import" href="/core/filter.html">
     10 <link rel="import" href="/core/scripting_object.html">
     11 <link rel="import" href="/extras/tquery/context.html">
     12 <link rel="import" href="/extras/tquery/filter_all_of.html">
     13 <link rel="import" href="/extras/tquery/filter_any_of.html">
     14 <link rel="import" href="/extras/tquery/filter_has_ancestor.html">
     15 <link rel="import" href="/extras/tquery/filter_has_duration.html">
     16 <link rel="import" href="/extras/tquery/filter_has_title.html">
     17 <link rel="import" href="/extras/tquery/filter_is_top_level.html">
     18 <link rel="import" href="/model/event_set.html">
     19 
     20 <script>
     21 'use strict';
     22 
     23 tr.exportTo('tr.e.tquery', function() {
     24   function TQuery(brushingStateController) {
     25     tr.c.ScriptingObject.call(this);
     26 
     27     this.brushingStateController_ = brushingStateController;
     28     this.parent_ = undefined;
     29     this.filterExpression_ = undefined;
     30     // Memoized filtering result.
     31     this.selection_ = undefined;
     32   };
     33 
     34   TQuery.prototype = {
     35     __proto__: tr.c.ScriptingObject.prototype,
     36 
     37     onModelChanged: function() {
     38       this.selection_ = undefined;
     39     },
     40 
     41     get brushingStateController() {
     42       return this.brushingStateController_;
     43     },
     44 
     45     // Append a new filter expression to this query and return a query node
     46     // that represents the result.
     47     filter: function(filterExpression) {
     48       var result = new TQuery(this.brushingStateController_);
     49       result.parent_ = this;
     50       result.filterExpression_ =
     51           tr.e.tquery.Filter.normalizeFilterExpression(filterExpression);
     52       return result;
     53     },
     54 
     55     // Creates a graph of {Task} objects which will compute the selections for
     56     // this filter object and all of its parents. The return value is an object
     57     // with the following fields:
     58     //  - rootTask: {Task} which should be executed to kick off processing for
     59     //              the entire task graph.
     60     //  - lastTask: The final {Task} of the graph. Can be used by the caller to
     61     //              enqueue additional processing at the end.
     62     //  - lastNode: The last filter object in the task. It's selection property
     63     //              will contain the filtering result once |finalTask|
     64     //              completes.
     65     createFilterTaskGraph_: function() {
     66       // List of nodes in order from the current one to the root.
     67       var nodes = [];
     68       var node = this;
     69       while (node !== undefined) {
     70         nodes.push(node);
     71         node = node.parent_;
     72       }
     73 
     74       var rootTask = new tr.b.Task();
     75       var lastTask = rootTask;
     76       for (var i = nodes.length - 1; i >= 0; i--) {
     77         var node = nodes[i];
     78         // Reuse any memoized result.
     79         if (node.selection_ !== undefined)
     80           continue;
     81         node.selection_ = new tr.model.EventSet();
     82         if (node.parent_ === undefined) {
     83           // If this is the root, start by collecting all objects from the
     84           // brushing state controller.
     85           lastTask = lastTask.after(
     86               this.selectEverythingAsTask_(node.selection_));
     87         } else {
     88           // Otherwise execute the filter expression for this node and fill
     89           // in its selection.
     90           var prevNode = nodes[i + 1];
     91           lastTask = this.createFilterTaskForNode_(lastTask, node, prevNode);
     92         }
     93       }
     94       return {rootTask: rootTask, lastTask: lastTask, lastNode: node};
     95     },
     96 
     97     createFilterTaskForNode_: function(lastTask, node, prevNode) {
     98       return lastTask.after(function() {
     99         // TODO(skyostil): Break into subtasks.
    100         node.evaluateFilterExpression_(
    101             prevNode.selection_, node.selection_);
    102       }, this);
    103     },
    104 
    105     // Applies the result of a filter expression for a given event and all
    106     // of its subslices and adds the matching events to an output selection.
    107     evaluateFilterExpression_: function(inputSelection, outputSelection) {
    108       var seenEvents = {};
    109       inputSelection.forEach(function(event) {
    110         var context = new tr.e.tquery.Context();
    111         context.event = event;
    112         this.evaluateFilterExpressionForEvent_(
    113             context, inputSelection, outputSelection, seenEvents);
    114       }.bind(this));
    115     },
    116 
    117     evaluateFilterExpressionForEvent_: function(
    118         context, inputSelection, outputSelection, seenEvents) {
    119       var event = context.event;
    120       if (inputSelection.contains(event) && !seenEvents[event.guid]) {
    121         seenEvents[event.guid] = true;
    122         if (!this.filterExpression_ ||
    123             this.filterExpression_.evaluate(context))
    124           outputSelection.push(event);
    125       }
    126       if (!event.subSlices)
    127         return;
    128       context = context.push(event);
    129       for (var i = 0; i < event.subSlices.length; i++) {
    130         context.event = event.subSlices[i];
    131         this.evaluateFilterExpressionForEvent_(
    132             context, inputSelection, outputSelection, seenEvents);
    133       }
    134     },
    135 
    136     // Show the result as a highlight on the brushing state controller. Returns
    137     // a {Task} which runs the query and sets the highlight.
    138     show: function() {
    139       var graph = this.createFilterTaskGraph_();
    140 
    141       graph.lastTask = graph.lastTask.after(function() {
    142         this.brushingStateController.showScriptControlSelection(
    143             graph.lastNode.selection_);
    144       }, this);
    145       return graph.rootTask;
    146     },
    147 
    148     // Returns a task that fills the given selection with everything reachable
    149     // by the brushing state controller.
    150     selectEverythingAsTask_: function(selection) {
    151       var passThroughFilter = new tr.c.Filter();
    152       var filterTask = this.brushingStateController.
    153           addAllEventsMatchingFilterToSelectionAsTask(passThroughFilter,
    154               selection);
    155       return filterTask;
    156     },
    157 
    158     get selection() {
    159       if (this.selection_ === undefined) {
    160         var graph = this.createFilterTaskGraph_();
    161         tr.b.Task.RunSynchronously(graph.rootTask);
    162       }
    163       return this.selection_;
    164     }
    165   };
    166   tr.c.ScriptingObjectRegistry.register(
    167       new TQuery(),
    168       {
    169         name: '$t'
    170       }
    171   );
    172 
    173   return {
    174     TQuery: TQuery
    175   };
    176 });
    177 </script>
    178