NetworkManager.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. /*
  2. * Copyright (C) 2011 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. * @constructor
  32. * @extends {WebInspector.Object}
  33. */
  34. WebInspector.NetworkManager = function()
  35. {
  36. WebInspector.Object.call(this);
  37. this._dispatcher = new WebInspector.NetworkDispatcher(this);
  38. if (WebInspector.settings.cacheDisabled.get())
  39. NetworkAgent.setCacheDisabled(true);
  40. NetworkAgent.enable();
  41. WebInspector.settings.cacheDisabled.addChangeListener(this._cacheDisabledSettingChanged, this);
  42. }
  43. WebInspector.NetworkManager.EventTypes = {
  44. RequestStarted: "RequestStarted",
  45. RequestUpdated: "RequestUpdated",
  46. RequestFinished: "RequestFinished",
  47. RequestUpdateDropped: "RequestUpdateDropped"
  48. }
  49. WebInspector.NetworkManager._MIMETypes = {
  50. "text/html": {"document": true},
  51. "text/xml": {"document": true},
  52. "text/plain": {"document": true},
  53. "application/xhtml+xml": {"document": true},
  54. "text/css": {"stylesheet": true},
  55. "text/xsl": {"stylesheet": true},
  56. "image/jpg": {"image": true},
  57. "image/jpeg": {"image": true},
  58. "image/pjpeg": {"image": true},
  59. "image/png": {"image": true},
  60. "image/gif": {"image": true},
  61. "image/bmp": {"image": true},
  62. "image/svg+xml": {"image": true, "font": true, "document": true},
  63. "image/vnd.microsoft.icon": {"image": true},
  64. "image/webp": {"image": true},
  65. "image/x-icon": {"image": true},
  66. "image/x-xbitmap": {"image": true},
  67. "font/ttf": {"font": true},
  68. "font/otf": {"font": true},
  69. "font/woff": {"font": true},
  70. "font/woff2": {"font": true},
  71. "font/truetype": {"font": true},
  72. "font/opentype": {"font": true},
  73. "application/octet-stream": {"font": true, "image": true},
  74. "application/font-woff": {"font": true},
  75. "application/x-font-woff": {"font": true},
  76. "application/x-font-type1": {"font": true},
  77. "application/x-font-ttf": {"font": true},
  78. "application/x-truetype-font": {"font": true},
  79. "text/javascript": {"script": true},
  80. "text/ecmascript": {"script": true},
  81. "application/javascript": {"script": true},
  82. "application/ecmascript": {"script": true},
  83. "application/x-javascript": {"script": true},
  84. "application/json": {"script": true},
  85. "text/javascript1.1": {"script": true},
  86. "text/javascript1.2": {"script": true},
  87. "text/javascript1.3": {"script": true},
  88. "text/jscript": {"script": true},
  89. "text/livescript": {"script": true},
  90. }
  91. WebInspector.NetworkManager.prototype = {
  92. /**
  93. * @param {string} url
  94. * @return {WebInspector.NetworkRequest}
  95. */
  96. inflightRequestForURL: function(url)
  97. {
  98. return this._dispatcher._inflightRequestsByURL[url];
  99. },
  100. /**
  101. * @param {WebInspector.Event} event
  102. */
  103. _cacheDisabledSettingChanged: function(event)
  104. {
  105. var enabled = /** @type {boolean} */ (event.data);
  106. NetworkAgent.setCacheDisabled(enabled);
  107. },
  108. __proto__: WebInspector.Object.prototype
  109. }
  110. /**
  111. * @constructor
  112. * @implements {NetworkAgent.Dispatcher}
  113. */
  114. WebInspector.NetworkDispatcher = function(manager)
  115. {
  116. this._manager = manager;
  117. this._inflightRequestsById = {};
  118. this._inflightRequestsByURL = {};
  119. InspectorBackend.registerNetworkDispatcher(this);
  120. }
  121. WebInspector.NetworkDispatcher.prototype = {
  122. /**
  123. * @param {NetworkAgent.Headers} headersMap
  124. * @return {!Array.<!WebInspector.NetworkRequest.NameValue>}
  125. */
  126. _headersMapToHeadersArray: function(headersMap)
  127. {
  128. var result = [];
  129. for (var name in headersMap) {
  130. var values = headersMap[name].split("\n");
  131. for (var i = 0; i < values.length; ++i)
  132. result.push({name: name, value: values[i]});
  133. }
  134. return result;
  135. },
  136. /**
  137. * @param {WebInspector.NetworkRequest} networkRequest
  138. * @param {NetworkAgent.Request} request
  139. */
  140. _updateNetworkRequestWithRequest: function(networkRequest, request)
  141. {
  142. networkRequest.requestMethod = request.method;
  143. networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
  144. networkRequest.requestFormData = request.postData;
  145. },
  146. /**
  147. * @param {WebInspector.NetworkRequest} networkRequest
  148. * @param {NetworkAgent.Response=} response
  149. */
  150. _updateNetworkRequestWithResponse: function(networkRequest, response)
  151. {
  152. if (!response)
  153. return;
  154. if (response.url && networkRequest.url !== response.url)
  155. networkRequest.url = response.url;
  156. networkRequest.mimeType = response.mimeType;
  157. networkRequest.statusCode = response.status;
  158. networkRequest.statusText = response.statusText;
  159. networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
  160. if (response.headersText)
  161. networkRequest.responseHeadersText = response.headersText;
  162. if (response.requestHeaders)
  163. networkRequest.requestHeaders = this._headersMapToHeadersArray(response.requestHeaders);
  164. if (response.requestHeadersText)
  165. networkRequest.requestHeadersText = response.requestHeadersText;
  166. networkRequest.connectionReused = response.connectionReused;
  167. networkRequest.connectionId = response.connectionId;
  168. if (response.fromDiskCache)
  169. networkRequest.cached = true;
  170. else
  171. networkRequest.timing = response.timing;
  172. if (!this._mimeTypeIsConsistentWithType(networkRequest)) {
  173. WebInspector.console.addMessage(WebInspector.ConsoleMessage.create(WebInspector.ConsoleMessage.MessageSource.Network,
  174. WebInspector.ConsoleMessage.MessageLevel.Log,
  175. WebInspector.UIString("Resource interpreted as %s but transferred with MIME type %s: \"%s\".", networkRequest.type.title(), networkRequest.mimeType, networkRequest.url),
  176. WebInspector.ConsoleMessage.MessageType.Log,
  177. "",
  178. 0,
  179. 0,
  180. 1,
  181. [],
  182. null,
  183. networkRequest.requestId));
  184. }
  185. },
  186. /**
  187. * @param {WebInspector.NetworkRequest} networkRequest
  188. * @return {boolean}
  189. */
  190. _mimeTypeIsConsistentWithType: function(networkRequest)
  191. {
  192. // If status is an error, content is likely to be of an inconsistent type,
  193. // as it's going to be an error message. We do not want to emit a warning
  194. // for this, though, as this will already be reported as resource loading failure.
  195. // Also, if a URL like http://localhost/wiki/load.php?debug=true&lang=en produces text/css and gets reloaded,
  196. // it is 304 Not Modified and its guessed mime-type is text/php, which is wrong.
  197. // Don't check for mime-types in 304-resources.
  198. if (networkRequest.hasErrorStatusCode() || networkRequest.statusCode === 304 || networkRequest.statusCode === 204)
  199. return true;
  200. if (typeof networkRequest.type === "undefined"
  201. || networkRequest.type === WebInspector.resourceTypes.Other
  202. || networkRequest.type === WebInspector.resourceTypes.XHR
  203. || networkRequest.type === WebInspector.resourceTypes.WebSocket)
  204. return true;
  205. if (!networkRequest.mimeType)
  206. return true; // Might be not known for cached resources with null responses.
  207. if (networkRequest.mimeType in WebInspector.NetworkManager._MIMETypes)
  208. return networkRequest.type.name() in WebInspector.NetworkManager._MIMETypes[networkRequest.mimeType];
  209. return false;
  210. },
  211. /**
  212. * @param {NetworkAgent.Response} response
  213. * @return {boolean}
  214. */
  215. _isNull: function(response)
  216. {
  217. if (!response)
  218. return true;
  219. return !response.status && !response.mimeType && (!response.headers || !Object.keys(response.headers).length);
  220. },
  221. /**
  222. * @param {NetworkAgent.RequestId} requestId
  223. * @param {PageAgent.FrameId} frameId
  224. * @param {NetworkAgent.LoaderId} loaderId
  225. * @param {string} documentURL
  226. * @param {NetworkAgent.Request} request
  227. * @param {NetworkAgent.Timestamp} time
  228. * @param {NetworkAgent.Initiator} initiator
  229. * @param {NetworkAgent.Response=} redirectResponse
  230. */
  231. requestWillBeSent: function(requestId, frameId, loaderId, documentURL, request, time, initiator, redirectResponse)
  232. {
  233. var networkRequest = this._inflightRequestsById[requestId];
  234. if (networkRequest) {
  235. // FIXME: move this check to the backend.
  236. if (!redirectResponse)
  237. return;
  238. this.responseReceived(requestId, frameId, loaderId, time, PageAgent.ResourceType.Other, redirectResponse);
  239. networkRequest = this._appendRedirect(requestId, time, request.url);
  240. } else
  241. networkRequest = this._createNetworkRequest(requestId, frameId, loaderId, request.url, documentURL, initiator);
  242. networkRequest.hasNetworkData = true;
  243. this._updateNetworkRequestWithRequest(networkRequest, request);
  244. networkRequest.startTime = time;
  245. this._startNetworkRequest(networkRequest);
  246. },
  247. /**
  248. * @param {NetworkAgent.RequestId} requestId
  249. */
  250. requestServedFromCache: function(requestId)
  251. {
  252. var networkRequest = this._inflightRequestsById[requestId];
  253. if (!networkRequest)
  254. return;
  255. networkRequest.cached = true;
  256. },
  257. /**
  258. * @param {NetworkAgent.RequestId} requestId
  259. * @param {PageAgent.FrameId} frameId
  260. * @param {NetworkAgent.LoaderId} loaderId
  261. * @param {NetworkAgent.Timestamp} time
  262. * @param {PageAgent.ResourceType} resourceType
  263. * @param {NetworkAgent.Response} response
  264. */
  265. responseReceived: function(requestId, frameId, loaderId, time, resourceType, response)
  266. {
  267. // FIXME: move this check to the backend.
  268. if (this._isNull(response))
  269. return;
  270. var networkRequest = this._inflightRequestsById[requestId];
  271. if (!networkRequest) {
  272. // We missed the requestWillBeSent.
  273. var eventData = {};
  274. eventData.url = response.url;
  275. eventData.frameId = frameId;
  276. eventData.loaderId = loaderId;
  277. eventData.resourceType = resourceType;
  278. eventData.mimeType = response.mimeType;
  279. this._manager.dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdateDropped, eventData);
  280. return;
  281. }
  282. networkRequest.responseReceivedTime = time;
  283. networkRequest.type = WebInspector.resourceTypes[resourceType];
  284. this._updateNetworkRequestWithResponse(networkRequest, response);
  285. this._updateNetworkRequest(networkRequest);
  286. },
  287. /**
  288. * @param {NetworkAgent.RequestId} requestId
  289. * @param {NetworkAgent.Timestamp} time
  290. * @param {number} dataLength
  291. * @param {number} encodedDataLength
  292. */
  293. dataReceived: function(requestId, time, dataLength, encodedDataLength)
  294. {
  295. var networkRequest = this._inflightRequestsById[requestId];
  296. if (!networkRequest)
  297. return;
  298. networkRequest.resourceSize += dataLength;
  299. if (encodedDataLength != -1)
  300. networkRequest.increaseTransferSize(encodedDataLength);
  301. networkRequest.endTime = time;
  302. this._updateNetworkRequest(networkRequest);
  303. },
  304. /**
  305. * @param {NetworkAgent.RequestId} requestId
  306. * @param {NetworkAgent.Timestamp} finishTime
  307. */
  308. loadingFinished: function(requestId, finishTime)
  309. {
  310. var networkRequest = this._inflightRequestsById[requestId];
  311. if (!networkRequest)
  312. return;
  313. this._finishNetworkRequest(networkRequest, finishTime);
  314. },
  315. /**
  316. * @param {NetworkAgent.RequestId} requestId
  317. * @param {NetworkAgent.Timestamp} time
  318. * @param {string} localizedDescription
  319. * @param {boolean=} canceled
  320. */
  321. loadingFailed: function(requestId, time, localizedDescription, canceled)
  322. {
  323. var networkRequest = this._inflightRequestsById[requestId];
  324. if (!networkRequest)
  325. return;
  326. networkRequest.failed = true;
  327. networkRequest.canceled = canceled;
  328. networkRequest.localizedFailDescription = localizedDescription;
  329. this._finishNetworkRequest(networkRequest, time);
  330. },
  331. /**
  332. * @param {NetworkAgent.RequestId} requestId
  333. * @param {string} requestURL
  334. */
  335. webSocketCreated: function(requestId, requestURL)
  336. {
  337. var networkRequest = new WebInspector.NetworkRequest(requestId, requestURL, "", "", "");
  338. networkRequest.type = WebInspector.resourceTypes.WebSocket;
  339. this._startNetworkRequest(networkRequest);
  340. },
  341. /**
  342. * @param {NetworkAgent.RequestId} requestId
  343. * @param {NetworkAgent.Timestamp} time
  344. * @param {NetworkAgent.WebSocketRequest} request
  345. */
  346. webSocketWillSendHandshakeRequest: function(requestId, time, request)
  347. {
  348. var networkRequest = this._inflightRequestsById[requestId];
  349. if (!networkRequest)
  350. return;
  351. networkRequest.requestMethod = "GET";
  352. networkRequest.requestHeaders = this._headersMapToHeadersArray(request.headers);
  353. networkRequest.startTime = time;
  354. this._updateNetworkRequest(networkRequest);
  355. },
  356. /**
  357. * @param {NetworkAgent.RequestId} requestId
  358. * @param {NetworkAgent.Timestamp} time
  359. * @param {NetworkAgent.WebSocketResponse} response
  360. */
  361. webSocketHandshakeResponseReceived: function(requestId, time, response)
  362. {
  363. var networkRequest = this._inflightRequestsById[requestId];
  364. if (!networkRequest)
  365. return;
  366. networkRequest.statusCode = response.status;
  367. networkRequest.statusText = response.statusText;
  368. networkRequest.responseHeaders = this._headersMapToHeadersArray(response.headers);
  369. networkRequest.responseReceivedTime = time;
  370. this._updateNetworkRequest(networkRequest);
  371. },
  372. /**
  373. * @param {NetworkAgent.RequestId} requestId
  374. * @param {NetworkAgent.Timestamp} time
  375. * @param {NetworkAgent.WebSocketFrame} response
  376. */
  377. webSocketFrameReceived: function(requestId, time, response)
  378. {
  379. var networkRequest = this._inflightRequestsById[requestId];
  380. if (!networkRequest)
  381. return;
  382. networkRequest.addFrame(response, time);
  383. networkRequest.responseReceivedTime = time;
  384. this._updateNetworkRequest(networkRequest);
  385. },
  386. /**
  387. * @param {NetworkAgent.RequestId} requestId
  388. * @param {NetworkAgent.Timestamp} time
  389. * @param {NetworkAgent.WebSocketFrame} response
  390. */
  391. webSocketFrameSent: function(requestId, time, response)
  392. {
  393. var networkRequest = this._inflightRequestsById[requestId];
  394. if (!networkRequest)
  395. return;
  396. networkRequest.addFrame(response, time, true);
  397. networkRequest.responseReceivedTime = time;
  398. this._updateNetworkRequest(networkRequest);
  399. },
  400. /**
  401. * @param {NetworkAgent.RequestId} requestId
  402. * @param {NetworkAgent.Timestamp} time
  403. * @param {string} errorMessage
  404. */
  405. webSocketFrameError: function(requestId, time, errorMessage)
  406. {
  407. var networkRequest = this._inflightRequestsById[requestId];
  408. if (!networkRequest)
  409. return;
  410. networkRequest.addFrameError(errorMessage, time);
  411. networkRequest.responseReceivedTime = time;
  412. this._updateNetworkRequest(networkRequest);
  413. },
  414. /**
  415. * @param {NetworkAgent.RequestId} requestId
  416. * @param {NetworkAgent.Timestamp} time
  417. */
  418. webSocketClosed: function(requestId, time)
  419. {
  420. var networkRequest = this._inflightRequestsById[requestId];
  421. if (!networkRequest)
  422. return;
  423. this._finishNetworkRequest(networkRequest, time);
  424. },
  425. /**
  426. * @param {NetworkAgent.RequestId} requestId
  427. * @param {NetworkAgent.Timestamp} time
  428. * @param {string} redirectURL
  429. * @return {WebInspector.NetworkRequest}
  430. */
  431. _appendRedirect: function(requestId, time, redirectURL)
  432. {
  433. var originalNetworkRequest = this._inflightRequestsById[requestId];
  434. var previousRedirects = originalNetworkRequest.redirects || [];
  435. originalNetworkRequest.requestId = "redirected:" + requestId + "." + previousRedirects.length;
  436. delete originalNetworkRequest.redirects;
  437. if (previousRedirects.length > 0)
  438. originalNetworkRequest.redirectSource = previousRedirects[previousRedirects.length - 1];
  439. this._finishNetworkRequest(originalNetworkRequest, time);
  440. var newNetworkRequest = this._createNetworkRequest(requestId, originalNetworkRequest.frameId, originalNetworkRequest.loaderId,
  441. redirectURL, originalNetworkRequest.documentURL, originalNetworkRequest.initiator);
  442. newNetworkRequest.redirects = previousRedirects.concat(originalNetworkRequest);
  443. return newNetworkRequest;
  444. },
  445. /**
  446. * @param {WebInspector.NetworkRequest} networkRequest
  447. */
  448. _startNetworkRequest: function(networkRequest)
  449. {
  450. this._inflightRequestsById[networkRequest.requestId] = networkRequest;
  451. this._inflightRequestsByURL[networkRequest.url] = networkRequest;
  452. this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestStarted, networkRequest);
  453. },
  454. /**
  455. * @param {WebInspector.NetworkRequest} networkRequest
  456. */
  457. _updateNetworkRequest: function(networkRequest)
  458. {
  459. this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestUpdated, networkRequest);
  460. },
  461. /**
  462. * @param {WebInspector.NetworkRequest} networkRequest
  463. * @param {NetworkAgent.Timestamp} finishTime
  464. */
  465. _finishNetworkRequest: function(networkRequest, finishTime)
  466. {
  467. networkRequest.endTime = finishTime;
  468. networkRequest.finished = true;
  469. this._dispatchEventToListeners(WebInspector.NetworkManager.EventTypes.RequestFinished, networkRequest);
  470. delete this._inflightRequestsById[networkRequest.requestId];
  471. delete this._inflightRequestsByURL[networkRequest.url];
  472. },
  473. /**
  474. * @param {string} eventType
  475. * @param {WebInspector.NetworkRequest} networkRequest
  476. */
  477. _dispatchEventToListeners: function(eventType, networkRequest)
  478. {
  479. this._manager.dispatchEventToListeners(eventType, networkRequest);
  480. },
  481. /**
  482. * @param {NetworkAgent.RequestId} requestId
  483. * @param {string} frameId
  484. * @param {NetworkAgent.LoaderId} loaderId
  485. * @param {string} url
  486. * @param {string} documentURL
  487. * @param {NetworkAgent.Initiator} initiator
  488. */
  489. _createNetworkRequest: function(requestId, frameId, loaderId, url, documentURL, initiator)
  490. {
  491. var networkRequest = new WebInspector.NetworkRequest(requestId, url, documentURL, frameId, loaderId);
  492. networkRequest.initiator = initiator;
  493. return networkRequest;
  494. }
  495. }
  496. /**
  497. * @type {?WebInspector.NetworkManager}
  498. */
  499. WebInspector.networkManager = null;