|  | # Copyright (c) 2016 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. | 
|  |  | 
|  | """Represents the trace of a page load.""" | 
|  |  | 
|  | import datetime | 
|  | try: | 
|  | import ujson as json | 
|  | except ImportError: | 
|  | import json | 
|  | import time | 
|  |  | 
|  | import devtools_monitor | 
|  | import page_track | 
|  | import request_track | 
|  | import tracing_track | 
|  |  | 
|  |  | 
|  | class LoadingTrace(object): | 
|  | """Represents the trace of a page load.""" | 
|  | _URL_KEY = 'url' | 
|  | _METADATA_KEY = 'metadata' | 
|  | _PAGE_KEY = 'page_track' | 
|  | _REQUEST_KEY = 'request_track' | 
|  | _TRACING_KEY = 'tracing_track' | 
|  |  | 
|  | def __init__(self, url, metadata, page, request, track): | 
|  | """Initializes a loading trace instance. | 
|  |  | 
|  | Args: | 
|  | url: (str) URL that has been loaded | 
|  | metadata: (dict) Metadata associated with the load. | 
|  | page: (PageTrack) instance of PageTrack. | 
|  | request: (RequestTrack) instance of RequestTrack. | 
|  | track: (TracingTrack) instance of TracingTrack. | 
|  | """ | 
|  | self.url = url | 
|  | self.metadata = metadata | 
|  | self.page_track = page | 
|  | self.request_track = request | 
|  | self._tracing_track = track | 
|  | self._tracing_json_str = None | 
|  |  | 
|  | def ToJsonDict(self): | 
|  | """Returns a dictionary representing this instance.""" | 
|  | result = {self._URL_KEY: self.url, self._METADATA_KEY: self.metadata, | 
|  | self._PAGE_KEY: self.page_track.ToJsonDict(), | 
|  | self._REQUEST_KEY: self.request_track.ToJsonDict(), | 
|  | self._TRACING_KEY: (self.tracing_track.ToJsonDict() | 
|  | if self.tracing_track else None)} | 
|  | return result | 
|  |  | 
|  | def ToJsonFile(self, json_path): | 
|  | """Save a json file representing this instance.""" | 
|  | json_dict = self.ToJsonDict() | 
|  | with open(json_path, 'w') as output_file: | 
|  | json.dump(json_dict, output_file) | 
|  |  | 
|  | @classmethod | 
|  | def FromJsonDict(cls, json_dict): | 
|  | """Returns an instance from a dictionary returned by ToJsonDict().""" | 
|  | keys = (cls._URL_KEY, cls._METADATA_KEY, cls._PAGE_KEY, cls._REQUEST_KEY, | 
|  | cls._TRACING_KEY) | 
|  | assert all(key in json_dict for key in keys) | 
|  | page = page_track.PageTrack.FromJsonDict(json_dict[cls._PAGE_KEY]) | 
|  | request = request_track.RequestTrack.FromJsonDict( | 
|  | json_dict[cls._REQUEST_KEY]) | 
|  | track = tracing_track.TracingTrack.FromJsonDict( | 
|  | json_dict[cls._TRACING_KEY]) | 
|  | return LoadingTrace(json_dict[cls._URL_KEY], json_dict[cls._METADATA_KEY], | 
|  | page, request, track) | 
|  |  | 
|  | @classmethod | 
|  | def FromJsonFile(cls, json_path): | 
|  | """Returns an instance from a json file saved by ToJsonFile().""" | 
|  | with open(json_path) as input_file: | 
|  | return cls.FromJsonDict(json.load(input_file)) | 
|  |  | 
|  | @classmethod | 
|  | def RecordUrlNavigation( | 
|  | cls, url, connection, chrome_metadata, categories, | 
|  | timeout_seconds=devtools_monitor.DEFAULT_TIMEOUT_SECONDS, | 
|  | stop_delay_multiplier=0): | 
|  | """Create a loading trace by using controller to fetch url. | 
|  |  | 
|  | Args: | 
|  | url: (str) url to fetch. | 
|  | connection: An opened devtools connection. | 
|  | chrome_metadata: Dictionary of chrome metadata. | 
|  | categories: as in tracing_track.TracingTrack | 
|  | timeout_seconds: monitoring connection timeout in seconds. | 
|  | stop_delay_multiplier: How long to wait after page load completed before | 
|  | tearing down, relative to the time it took to reach the page load to | 
|  | complete. | 
|  |  | 
|  | Returns: | 
|  | LoadingTrace instance. | 
|  | """ | 
|  | page = page_track.PageTrack(connection) | 
|  | request = request_track.RequestTrack(connection) | 
|  | trace = tracing_track.TracingTrack(connection, categories) | 
|  | start_date_str = datetime.datetime.utcnow().isoformat() | 
|  | seconds_since_epoch=time.time() | 
|  | connection.MonitorUrl(url, | 
|  | timeout_seconds=timeout_seconds, | 
|  | stop_delay_multiplier=stop_delay_multiplier) | 
|  | trace = cls(url, chrome_metadata, page, request, trace) | 
|  | trace.metadata.update(date=start_date_str, | 
|  | seconds_since_epoch=seconds_since_epoch) | 
|  | return trace | 
|  |  | 
|  | @property | 
|  | def tracing_track(self): | 
|  | if not self._tracing_track: | 
|  | self._RestoreTracingTrack() | 
|  | return self._tracing_track | 
|  |  | 
|  | def Slim(self): | 
|  | """Slims the memory usage of a trace by dropping the TraceEvents from it. | 
|  |  | 
|  | The tracing track is restored on-demand when accessed. | 
|  | """ | 
|  | self._tracing_json_str = json.dumps(self._tracing_track.ToJsonDict()) | 
|  | self._tracing_track = None | 
|  |  | 
|  | def _RestoreTracingTrack(self): | 
|  | if not self._tracing_json_str: | 
|  | return None | 
|  | self._tracing_track = tracing_track.TracingTrack.FromJsonDict( | 
|  | json.loads(self._tracing_json_str)) | 
|  | self._tracing_json_str = None |