Permission message rules: Each rule must have >= 1 required permissions
[chromium-blink-merge.git] / tools / measure_page_load_time / ie_bho / MeasurePageLoadTimeBHO.cpp
blob3de87f3c8037d888381bb94d2a99ca8fc36ff03d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Implements a Browser Helper Object (BHO) which opens a socket
6 // and waits to receive URLs over it. Visits those URLs, measuring
7 // how long it takes between the start of navigation and the
8 // DocumentComplete event, and returns the time in milliseconds as
9 // a string to the caller.
11 #include "stdafx.h"
12 #include "MeasurePageLoadTimeBHO.h"
14 #define MAX_URL 1024 // size of URL buffer
15 #define MAX_PAGELOADTIME (4*60*1000) // assume all pages take < 4 minutes
16 #define PORT 42492 // port to listen on. Also jhaas's
17 // old MSFT employee number
20 // Static function to serve as thread entry point, takes a "this"
21 // pointer as pParam and calls the method in the object
22 static DWORD WINAPI ProcessPageTimeRequests(LPVOID pThis) {
23 reinterpret_cast<CMeasurePageLoadTimeBHO*>(pThis)->ProcessPageTimeRequests();
25 return 0;
29 STDMETHODIMP CMeasurePageLoadTimeBHO::SetSite(IUnknown* pUnkSite)
31 if (pUnkSite != NULL)
33 // Cache the pointer to IWebBrowser2.
34 HRESULT hr = pUnkSite->QueryInterface(IID_IWebBrowser2, (void **)&m_spWebBrowser);
35 if (SUCCEEDED(hr))
37 // Register to sink events from DWebBrowserEvents2.
38 hr = DispEventAdvise(m_spWebBrowser);
39 if (SUCCEEDED(hr))
41 m_fAdvised = TRUE;
44 // Stash the interface in the global interface table
45 CComGITPtr<IWebBrowser2> git(m_spWebBrowser);
46 m_dwCookie = git.Detach();
48 // Create the event to be signaled when navigation completes.
49 // Start it in nonsignaled state, and allow it to be triggered
50 // when the initial page load is done.
51 m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
53 // Create a thread to wait on the socket
54 HANDLE hThread = CreateThread(NULL, 0, ::ProcessPageTimeRequests, this, 0, NULL);
57 else
59 // Unregister event sink.
60 if (m_fAdvised)
62 DispEventUnadvise(m_spWebBrowser);
63 m_fAdvised = FALSE;
66 // Release cached pointers and other resources here.
67 m_spWebBrowser.Release();
70 // Call base class implementation.
71 return IObjectWithSiteImpl<CMeasurePageLoadTimeBHO>::SetSite(pUnkSite);
75 void STDMETHODCALLTYPE CMeasurePageLoadTimeBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL)
77 if (pDisp == m_spWebBrowser)
79 // Fire the event when the page is done loading
80 // to unblock the other thread.
81 SetEvent(m_hEvent);
86 void CMeasurePageLoadTimeBHO::ProcessPageTimeRequests()
88 CoInitialize(NULL);
90 // The event will start in nonsignaled state, meaning that
91 // the initial page load isn't done yet. Wait for that to
92 // finish before doing anything.
94 // It seems to be the case that the BHO will get loaded
95 // and SetSite called always before the initial page load
96 // even begins, but just to be on the safe side, we won't
97 // wait indefinitely.
98 WaitForSingleObject(m_hEvent, MAX_PAGELOADTIME);
100 // Retrieve the web browser interface from the global table
101 CComGITPtr<IWebBrowser2> git(m_dwCookie);
102 IWebBrowser2* browser;
103 git.CopyTo(&browser);
105 // Create a listening socket
106 m_sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
107 if (m_sockListen == SOCKET_ERROR)
108 ErrorExit();
110 BOOL on = TRUE;
111 if (setsockopt(m_sockListen, SOL_SOCKET, SO_REUSEADDR,
112 (const char*)&on, sizeof(on)))
113 ErrorExit();
115 // Bind the listening socket
116 SOCKADDR_IN addrBind;
118 addrBind.sin_family = AF_INET;
119 addrBind.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
120 addrBind.sin_port = htons(PORT);
122 if (bind(m_sockListen, (sockaddr*)&addrBind, sizeof(addrBind)))
123 ErrorExit();
125 // Listen for incoming connections
126 if (listen(m_sockListen, 1))
127 ErrorExit();
129 // Ensure the socket is blocking... it should be by default, but
130 // it can't hurt to make sure
131 unsigned long nNonblocking = 0;
132 if (ioctlsocket(m_sockListen, FIONBIO, &nNonblocking))
133 ErrorExit();
135 m_sockTransport = 0;
137 // Loop indefinitely waiting for connections
138 while(1)
140 SOCKADDR_IN addrConnected;
141 int sConnected = sizeof(addrConnected);
143 // Wait for a client to connect and send a URL
144 m_sockTransport = accept(
145 m_sockListen, (sockaddr*)&addrConnected, &sConnected);
147 if (m_sockTransport == SOCKET_ERROR)
148 ErrorExit();
150 char pbBuffer[MAX_URL], strURL[MAX_URL];
151 DWORD cbRead, cbWritten;
153 bool fDone = false;
155 // Loop until we're done with this client
156 while (!fDone)
158 *strURL = '\0';
159 bool fReceivedCR = false;
163 // Only receive up to the first carriage return
164 cbRead = recv(m_sockTransport, pbBuffer, MAX_URL-1, MSG_PEEK);
166 // An error on read most likely means that the remote peer
167 // closed the connection. Go back to waiting
168 if (cbRead == 0)
170 fDone = true;
171 break;
174 // Null terminate the received characters so strchr() is safe
175 pbBuffer[cbRead] = '\0';
177 if(char* pchFirstCR = strchr(pbBuffer, '\n'))
179 cbRead = (DWORD)(pchFirstCR - pbBuffer + 1);
180 fReceivedCR = true;
183 // The below call will not block, since we determined with
184 // MSG_PEEK that at least cbRead bytes are in the TCP receive buffer
185 recv(m_sockTransport, pbBuffer, cbRead, 0);
186 pbBuffer[cbRead] = '\0';
188 strcat_s(strURL, sizeof(strURL), pbBuffer);
189 } while (!fReceivedCR);
191 // If an error occurred while reading, exit this loop
192 if (fDone)
193 break;
195 // Strip the trailing CR and/or LF
196 int i;
197 for (i = (int)strlen(strURL)-1; i >= 0 && isspace(strURL[i]); i--)
199 strURL[i] = '\0';
202 if (i < 0)
204 // Sending a carriage return on a line by itself means that
205 // the client is done making requests
206 fDone = true;
208 else
210 // Send the browser to the requested URL
211 CComVariant vNavFlags( navNoReadFromCache );
212 CComVariant vTargetFrame("_self");
213 CComVariant vPostData("");
214 CComVariant vHTTPHeaders("");
216 ResetEvent(m_hEvent);
217 DWORD dwStartTime = GetTickCount();
219 HRESULT hr = browser->Navigate(
220 CComBSTR(strURL),
221 &vNavFlags,
222 &vTargetFrame, // TargetFrameName
223 &vPostData, // PostData
224 &vHTTPHeaders // Headers
227 // The main browser thread will call OnDocumentComplete() when
228 // the page is done loading, which will in turn trigger
229 // m_hEvent. Wait here until then; the event will reset itself
230 // once this thread is released
231 if (WaitForSingleObject(m_hEvent, MAX_PAGELOADTIME) == WAIT_TIMEOUT)
233 sprintf_s(pbBuffer, sizeof(pbBuffer), "%s,timeout\n", strURL);
235 browser->Stop();
237 else
239 // Format the elapsed time as a string
240 DWORD dwLoadTime = GetTickCount() - dwStartTime;
241 sprintf_s(
242 pbBuffer, sizeof(pbBuffer), "%s,%d\n", strURL, dwLoadTime);
245 // Send the result. Just in case the TCP buffer can't handle
246 // the whole thing, send in parts if necessary
247 char *chSend = pbBuffer;
249 while (*chSend)
251 cbWritten = send(
252 m_sockTransport, chSend, (int)strlen(chSend), 0);
254 // Error on send probably means connection reset by peer
255 if (cbWritten == 0)
257 fDone = true;
258 break;
261 chSend += cbWritten;
266 // Close the transport socket and wait for another connection
267 closesocket(m_sockTransport);
268 m_sockTransport = 0;
273 void CMeasurePageLoadTimeBHO::ErrorExit()
275 // Unlink from IE, close the sockets, then terminate this
276 // thread
277 SetSite(NULL);
279 if (m_sockTransport && m_sockTransport != SOCKET_ERROR)
281 closesocket(m_sockTransport);
282 m_sockTransport = 0;
285 if (m_sockListen && m_sockListen != SOCKET_ERROR)
287 closesocket(m_sockListen);
288 m_sockListen = 0;
291 TerminateThread(GetCurrentThread(), -1);