Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: Source/devtools/front_end/network/HAREntry.js

Issue 667623002: DevTools: make extension server a part of core, panels' code should depend on it. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: for review Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 // See http://www.softwareishard.com/blog/har-12-spec/
32 // for HAR specification.
33
34 // FIXME: Some fields are not yet supported due to back-end limitations.
35 // See https://bugs.webkit.org/show_bug.cgi?id=58127 for details.
36
37 /**
38 * @constructor
39 * @param {!WebInspector.NetworkRequest} request
40 */
41 WebInspector.HAREntry = function(request)
42 {
43 this._request = request;
44 }
45
46 WebInspector.HAREntry.prototype = {
47 /**
48 * @return {!Object}
49 */
50 build: function()
51 {
52 var entry = {
53 startedDateTime: new Date(this._request.startTime * 1000),
54 time: this._request.timing ? WebInspector.HAREntry._toMilliseconds(t his._request.duration) : 0,
55 request: this._buildRequest(),
56 response: this._buildResponse(),
57 cache: { }, // Not supported yet.
58 timings: this._buildTimings()
59 };
60
61 if (this._request.connectionId !== "0")
62 entry.connection = this._request.connectionId;
63 var page = this._request.target().networkLog.pageLoadForRequest(this._re quest);
64 if (page)
65 entry.pageref = "page_" + page.id;
66 return entry;
67 },
68
69 /**
70 * @return {!Object}
71 */
72 _buildRequest: function()
73 {
74 var headersText = this._request.requestHeadersText();
75 var res = {
76 method: this._request.requestMethod,
77 url: this._buildRequestURL(this._request.url),
78 httpVersion: this._request.requestHttpVersion(),
79 headers: this._request.requestHeaders(),
80 queryString: this._buildParameters(this._request.queryParameters || []),
81 cookies: this._buildCookies(this._request.requestCookies || []),
82 headersSize: headersText ? headersText.length : -1,
83 bodySize: this.requestBodySize
84 };
85 if (this._request.requestFormData)
86 res.postData = this._buildPostData();
87
88 return res;
89 },
90
91 /**
92 * @return {!Object}
93 */
94 _buildResponse: function()
95 {
96 var headersText = this._request.responseHeadersText;
97 return {
98 status: this._request.statusCode,
99 statusText: this._request.statusText,
100 httpVersion: this._request.responseHttpVersion(),
101 headers: this._request.responseHeaders,
102 cookies: this._buildCookies(this._request.responseCookies || []),
103 content: this._buildContent(),
104 redirectURL: this._request.responseHeaderValue("Location") || "",
105 headersSize: headersText ? headersText.length : -1,
106 bodySize: this.responseBodySize,
107 _error: this._request.localizedFailDescription
108 };
109 },
110
111 /**
112 * @return {!Object}
113 */
114 _buildContent: function()
115 {
116 var content = {
117 size: this._request.resourceSize,
118 mimeType: this._request.mimeType || "x-unknown",
119 // text: this._request.content // TODO: pull out into a boolean flag , as content can be huge (and needs to be requested with an async call)
120 };
121 var compression = this.responseCompression;
122 if (typeof compression === "number")
123 content.compression = compression;
124 return content;
125 },
126
127 /**
128 * @return {!Object}
129 */
130 _buildTimings: function()
131 {
132 // Order of events: request_start = 0, [proxy], [dns], [connect [ssl]], [send], receive_headers_end
133 // HAR 'blocked' time is time before first network activity.
134
135 var timing = this._request.timing;
136 if (!timing)
137 return {blocked: -1, dns: -1, connect: -1, send: 0, wait: 0, receive : 0, ssl: -1};
138
139 function firstNonNegative(values)
140 {
141 for (var i = 0; i < values.length; ++i) {
142 if (values[i] >= 0)
143 return values[i];
144 }
145 console.assert(false, "Incomplete requet timing information.");
146 }
147
148 var blocked = firstNonNegative([timing.dnsStart, timing.connectStart, ti ming.sendStart]);
149
150 var dns = -1;
151 if (timing.dnsStart >= 0)
152 dns = firstNonNegative([timing.connectStart, timing.sendStart]) - ti ming.dnsStart;
153
154 var connect = -1;
155 if (timing.connectStart >= 0)
156 connect = timing.sendStart - timing.connectStart;
157
158 var send = timing.sendEnd - timing.sendStart;
159 var wait = timing.receiveHeadersEnd - timing.sendEnd;
160 var receive = WebInspector.HAREntry._toMilliseconds(this._request.durati on) - timing.receiveHeadersEnd;
161
162 var ssl = -1;
163 if (timing.sslStart >= 0 && timing.sslEnd >= 0)
164 ssl = timing.sslEnd - timing.sslStart;
165
166 return {blocked: blocked, dns: dns, connect: connect, send: send, wait: wait, receive: receive, ssl: ssl};
167 },
168
169 /**
170 * @return {!Object}
171 */
172 _buildPostData: function()
173 {
174 var res = {
175 mimeType: this._request.requestContentType(),
176 text: this._request.requestFormData
177 };
178 if (this._request.formParameters)
179 res.params = this._buildParameters(this._request.formParameters);
180 return res;
181 },
182
183 /**
184 * @param {!Array.<!Object>} parameters
185 * @return {!Array.<!Object>}
186 */
187 _buildParameters: function(parameters)
188 {
189 return parameters.slice();
190 },
191
192 /**
193 * @param {string} url
194 * @return {string}
195 */
196 _buildRequestURL: function(url)
197 {
198 return url.split("#", 2)[0];
199 },
200
201 /**
202 * @param {!Array.<!WebInspector.Cookie>} cookies
203 * @return {!Array.<!Object>}
204 */
205 _buildCookies: function(cookies)
206 {
207 return cookies.map(this._buildCookie.bind(this));
208 },
209
210 /**
211 * @param {!WebInspector.Cookie} cookie
212 * @return {!Object}
213 */
214 _buildCookie: function(cookie)
215 {
216 return {
217 name: cookie.name(),
218 value: cookie.value(),
219 path: cookie.path(),
220 domain: cookie.domain(),
221 expires: cookie.expiresDate(new Date(this._request.startTime * 1000) ),
222 httpOnly: cookie.httpOnly(),
223 secure: cookie.secure()
224 };
225 },
226
227 /**
228 * @return {number}
229 */
230 get requestBodySize()
231 {
232 return !this._request.requestFormData ? 0 : this._request.requestFormDat a.length;
233 },
234
235 /**
236 * @return {number}
237 */
238 get responseBodySize()
239 {
240 if (this._request.cached() || this._request.statusCode === 304)
241 return 0;
242 if (!this._request.responseHeadersText)
243 return -1;
244 return this._request.transferSize - this._request.responseHeadersText.le ngth;
245 },
246
247 /**
248 * @return {number|undefined}
249 */
250 get responseCompression()
251 {
252 if (this._request.cached() || this._request.statusCode === 304 || this._ request.statusCode === 206)
253 return;
254 if (!this._request.responseHeadersText)
255 return;
256 return this._request.resourceSize - this.responseBodySize;
257 }
258 }
259
260 /**
261 * @param {number} time
262 * @return {number}
263 */
264 WebInspector.HAREntry._toMilliseconds = function(time)
265 {
266 return time === -1 ? -1 : time * 1000;
267 }
268
269 /**
270 * @constructor
271 * @param {!Array.<!WebInspector.NetworkRequest>} requests
272 */
273 WebInspector.HARLog = function(requests)
274 {
275 this._requests = requests;
276 }
277
278 WebInspector.HARLog.prototype = {
279 /**
280 * @return {!Object}
281 */
282 build: function()
283 {
284 return {
285 version: "1.2",
286 creator: this._creator(),
287 pages: this._buildPages(),
288 entries: this._requests.map(this._convertResource.bind(this))
289 }
290 },
291
292 _creator: function()
293 {
294 var webKitVersion = /AppleWebKit\/([^ ]+)/.exec(window.navigator.userAge nt);
295
296 return {
297 name: "WebInspector",
298 version: webKitVersion ? webKitVersion[1] : "n/a"
299 };
300 },
301
302 /**
303 * @return {!Array.<!Object>}
304 */
305 _buildPages: function()
306 {
307 var seenIdentifiers = {};
308 var pages = [];
309 for (var i = 0; i < this._requests.length; ++i) {
310 var page = this._requests[i].target().networkLog.pageLoadForRequest( this._requests[i]);
311 if (!page || seenIdentifiers[page.id])
312 continue;
313 seenIdentifiers[page.id] = true;
314 pages.push(this._convertPage(page));
315 }
316 return pages;
317 },
318
319 /**
320 * @param {!WebInspector.PageLoad} page
321 * @return {!Object}
322 */
323 _convertPage: function(page)
324 {
325 return {
326 startedDateTime: new Date(page.startTime * 1000),
327 id: "page_" + page.id,
328 title: page.url, // We don't have actual page title here. URL is pro bably better than nothing.
329 pageTimings: {
330 onContentLoad: this._pageEventTime(page, page.contentLoadTime),
331 onLoad: this._pageEventTime(page, page.loadTime)
332 }
333 }
334 },
335
336 /**
337 * @param {!WebInspector.NetworkRequest} request
338 * @return {!Object}
339 */
340 _convertResource: function(request)
341 {
342 return (new WebInspector.HAREntry(request)).build();
343 },
344
345 /**
346 * @param {!WebInspector.PageLoad} page
347 * @param {number} time
348 * @return {number}
349 */
350 _pageEventTime: function(page, time)
351 {
352 var startTime = page.startTime;
353 if (time === -1 || startTime === -1)
354 return -1;
355 return WebInspector.HAREntry._toMilliseconds(time - startTime);
356 }
357 }
358
359 /**
360 * @constructor
361 */
362 WebInspector.HARWriter = function()
363 {
364 }
365
366 WebInspector.HARWriter.prototype = {
367 /**
368 * @param {!WebInspector.OutputStream} stream
369 * @param {!Array.<!WebInspector.NetworkRequest>} requests
370 * @param {!WebInspector.Progress} progress
371 */
372 write: function(stream, requests, progress)
373 {
374 this._stream = stream;
375 this._harLog = (new WebInspector.HARLog(requests)).build();
376 this._pendingRequests = 1; // Guard against completing resource transfer before all requests are made.
377 var entries = this._harLog.entries;
378 for (var i = 0; i < entries.length; ++i) {
379 var content = requests[i].content;
380 if (typeof content === "undefined" && requests[i].finished) {
381 ++this._pendingRequests;
382 requests[i].requestContent(this._onContentAvailable.bind(this, e ntries[i]));
383 } else if (content !== null)
384 entries[i].response.content.text = content;
385 }
386 var compositeProgress = new WebInspector.CompositeProgress(progress);
387 this._writeProgress = compositeProgress.createSubProgress();
388 if (--this._pendingRequests) {
389 this._requestsProgress = compositeProgress.createSubProgress();
390 this._requestsProgress.setTitle(WebInspector.UIString("Collecting co ntent…"));
391 this._requestsProgress.setTotalWork(this._pendingRequests);
392 } else
393 this._beginWrite();
394 },
395
396 /**
397 * @param {!Object} entry
398 * @param {?string} content
399 */
400 _onContentAvailable: function(entry, content)
401 {
402 if (content !== null)
403 entry.response.content.text = content;
404 if (this._requestsProgress)
405 this._requestsProgress.worked();
406 if (!--this._pendingRequests) {
407 this._requestsProgress.done();
408 this._beginWrite();
409 }
410 },
411
412 _beginWrite: function()
413 {
414 const jsonIndent = 2;
415 this._text = JSON.stringify({log: this._harLog}, null, jsonIndent);
416 this._writeProgress.setTitle(WebInspector.UIString("Writing file…"));
417 this._writeProgress.setTotalWork(this._text.length);
418 this._bytesWritten = 0;
419 this._writeNextChunk(this._stream);
420 },
421
422 /**
423 * @param {!WebInspector.OutputStream} stream
424 * @param {string=} error
425 */
426 _writeNextChunk: function(stream, error)
427 {
428 if (this._bytesWritten >= this._text.length || error) {
429 stream.close();
430 this._writeProgress.done();
431 return;
432 }
433 const chunkSize = 100000;
434 var text = this._text.substring(this._bytesWritten, this._bytesWritten + chunkSize);
435 this._bytesWritten += text.length;
436 stream.write(text, this._writeNextChunk.bind(this));
437 this._writeProgress.setWorked(this._bytesWritten);
438 }
439 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698