15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/browser/devtools/devtools_netlog_observer.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h" 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/content_browser_client.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/common/resource_response.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/load_flags.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_response_headers.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_util.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/spdy/spdy_header_block.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_netlog_params.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace content { 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const size_t kMaxNumEntries = 1000; 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver* DevToolsNetLogObserver::instance_ = NULL; 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver::DevToolsNetLogObserver() { 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver::~DevToolsNetLogObserver() { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver::ResourceInfo* 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver::GetResourceInfo(uint32 id) { 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) RequestToInfoMap::iterator it = request_to_info_.find(id); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it != request_to_info_.end()) 34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) return it->second.get(); 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DevToolsNetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The events that the Observer is interested in only occur on the IO thread. 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (entry.source().type == net::NetLog::SOURCE_URL_REQUEST) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) OnAddURLRequestEntry(entry); 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DevToolsNetLogObserver::OnAddURLRequestEntry( 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const net::NetLog::Entry& entry) { 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_begin = entry.phase() == net::NetLog::PHASE_BEGIN; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_end = entry.phase() == net::NetLog::PHASE_END; 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (entry.type() == net::NetLog::TYPE_URL_REQUEST_START_JOB) { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (is_begin) { 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int load_flags; 577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) scoped_ptr<base::Value> event_param(entry.ParametersToValue()); 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!net::StartEventLoadFlagsFromEventParams(event_param.get(), 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &load_flags)) { 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!(load_flags & net::LOAD_REPORT_RAW_HEADERS)) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (request_to_info_.size() > kMaxNumEntries) { 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "The raw headers observer url request count has grown " 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "larger than expected, resetting"; 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_to_info_.clear(); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_to_info_[entry.source().id] = new ResourceInfo(); 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (entry.type() == net::NetLog::TYPE_REQUEST_ALIVE) { 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Cleanup records based on the TYPE_REQUEST_ALIVE entry. 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) if (is_end) 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) request_to_info_.erase(entry.source().id); 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResourceInfo* info = GetResourceInfo(entry.source().id); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!info) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch (entry.type()) { 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case net::NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS: { 887d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string request_line; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net::HttpRequestHeaders request_headers; 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!net::HttpRequestHeaders::FromNetLogParam(event_params.get(), 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &request_headers, 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &request_line)) { 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We need to clear headers in case the same url_request is reused for 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // several http requests (e.g. see http://crbug.com/80157). 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers.clear(); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (net::HttpRequestHeaders::Iterator it(request_headers); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) it.GetNext();) { 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers.push_back(std::make_pair(it.name(), it.value())); 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers_text = request_line + request_headers.ToString(); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case net::NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS: { 1107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net::SpdyHeaderBlock request_headers; 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!net::SpdyHeaderBlockFromNetLogParam(event_params.get(), 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &request_headers)) { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We need to clear headers in case the same url_request is reused for 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // several http requests (e.g. see http://crbug.com/80157). 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers.clear(); 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (net::SpdyHeaderBlock::const_iterator it = request_headers.begin(); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) it != request_headers.end(); ++it) { 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers.push_back(std::make_pair(it->first, it->second)); 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->request_headers_text = ""; 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case net::NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS: { 1307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles) scoped_ptr<base::Value> event_params(entry.ParametersToValue()); 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_refptr<net::HttpResponseHeaders> response_headers; 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!net::HttpResponseHeaders::FromNetLogParam(event_params.get(), 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &response_headers)) { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->http_status_code = response_headers->response_code(); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->http_status_text = response_headers->GetStatusText(); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string name, value; 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We need to clear headers in case the same url_request is reused for 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // several http requests (e.g. see http://crbug.com/80157). 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->response_headers.clear(); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (void* it = NULL; 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response_headers->EnumerateHeaderLines(&it, &name, &value); ) { 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) info->response_headers.push_back(std::make_pair(name, value)); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!info->request_headers_text.empty()) { 153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) info->response_headers_text = 154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) net::HttpUtil::ConvertHeadersBackToHTTPResponse( 155a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) response_headers->raw_headers()); 156a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // SPDY request. 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) info->response_headers_text = ""; 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) default: 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DevToolsNetLogObserver::Attach() { 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(!instance_); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net::NetLog* net_log = GetContentClient()->browser()->GetNetLog(); 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (net_log) { 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance_ = new DevToolsNetLogObserver(); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net_log->AddThreadSafeObserver(instance_, net::NetLog::LOG_ALL_BUT_BYTES); 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DevToolsNetLogObserver::Detach() { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (instance_) { 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Safest not to do this in the destructor to maintain thread safety across 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // refactorings. 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance_->net_log()->RemoveThreadSafeObserver(instance_); 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete instance_; 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) instance_ = NULL; 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DevToolsNetLogObserver* DevToolsNetLogObserver::GetInstance() { 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return instance_; 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void DevToolsNetLogObserver::PopulateResponseInfo( 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) net::URLRequest* request, 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ResourceResponse* response) { 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!(request->load_flags() & net::LOAD_REPORT_RAW_HEADERS)) 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) uint32 source_id = request->net_log().source().id; 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DevToolsNetLogObserver* dev_tools_net_log_observer = 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DevToolsNetLogObserver::GetInstance(); 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (dev_tools_net_log_observer == NULL) 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) response->head.devtools_info = 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) dev_tools_net_log_observer->GetResourceInfo(source_id); 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace content 211