Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: src/plugin/PluginWbPassThrough.cpp

Issue 4974480757620736: Issue #1356 - Improve detection of the issuer of the request (Closed)
Patch Set: Created Oct. 14, 2014, 11:23 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #include "PluginStdAfx.h" 1 #include "PluginStdAfx.h"
2 2
3 #include "PluginWbPassThrough.h" 3 #include "PluginWbPassThrough.h"
4 #include "PluginClient.h" 4 #include "PluginClient.h"
5 #include "PluginClientFactory.h" 5 #include "PluginClientFactory.h"
6 #ifdef SUPPORT_FILTER 6 #ifdef SUPPORT_FILTER
7 #include "PluginFilter.h" 7 #include "PluginFilter.h"
8 #endif 8 #endif
9 #include "PluginSettings.h" 9 #include "PluginSettings.h"
10 #include "PluginClass.h" 10 #include "PluginClass.h"
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 //////////////////////////////////////////////////////////////////////////////// //////// 120 //////////////////////////////////////////////////////////////////////////////// ////////
121 //WBPassthruSink 121 //WBPassthruSink
122 //Monitor and/or cancel every request and responde 122 //Monitor and/or cancel every request and responde
123 //WB makes, including images, sounds, scripts, etc 123 //WB makes, including images, sounds, scripts, etc
124 //////////////////////////////////////////////////////////////////////////////// //////// 124 //////////////////////////////////////////////////////////////////////////////// ////////
125 HRESULT WBPassthruSink::OnStart(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSin k, 125 HRESULT WBPassthruSink::OnStart(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSin k,
126 IInternetBindInfo *pOIBindInfo, DWORD grfPI, HAN DLE_PTR dwReserved, 126 IInternetBindInfo *pOIBindInfo, DWORD grfPI, HAN DLE_PTR dwReserved,
127 IInternetProtocol* pTargetProtocol, bool& handle d) 127 IInternetProtocol* pTargetProtocol, bool& handle d)
128 { 128 {
129 m_pTargetProtocol = pTargetProtocol; 129 m_pTargetProtocol = pTargetProtocol;
130 bool isBlocked = false;
131 CString src = szUrl;
Eric 2014/10/15 17:02:33 The review I just posted converts this CString to
132 DEBUG_GENERAL(src);
133 CPluginClient::UnescapeUrl(src);
134
135 // call the impl of the base class as soon as possible because it initializes the base class 130 // call the impl of the base class as soon as possible because it initializes the base class
136 // members, used by this method. It queries for the required interfaces. 131 // members, used by this method. It queries for the required interfaces.
137 HRESULT hr = BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwRese rved, pTargetProtocol); 132 HRESULT hr = BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwRese rved, pTargetProtocol);
138 if (FAILED(hr)) 133 if (FAILED(hr))
139 { 134 {
140 return hr; 135 return hr;
141 } 136 }
142 137 return hr;
143 CString mimeType;
144 if (pOIBindInfo)
145 {
146 ULONG resLen = 0;
147
148 // Apparently IE will report random mime type if there's more then 1 in the list.
149 // So we get the whole list and just use the first one (top priority one)
150 LPOLESTR mime[10];
151 pOIBindInfo->GetBindString(BINDSTRING_ACCEPT_MIMES, mime, 10, &resLen);
152 if (mime && resLen > 0)
153 {
154 mimeType.SetString(mime[0]);
155 }
156 LPOLESTR bindString = nullptr;
157 pOIBindInfo->GetBindString(BINDSTRING_FLAG_BIND_TO_OBJECT, &bindString, 1, & resLen);
158 LPOLESTR domainRetrieved = nullptr;
159 if (resLen == 0 || wcscmp(bindString, L"FALSE") == 0)
160 {
161 HRESULT hr = pOIBindInfo->GetBindString(BINDSTRING_XDR_ORIGIN, &domainRetr ieved, 1, &resLen);
162 if ((hr == S_OK) && domainRetrieved && (resLen > 0))
163 {
164 m_boundDomain = domainRetrieved;
165 }
166 }
167 // We can obtain IBindCtx* here, but IEnumString obtained via IBindCtx::Enum ObjectParam
168 // does not return any parameter, so it's useless.
169 }
170
171 CString cookie;
172 ULONG len1 = 2048;
173 ULONG len2 = 2048;
174
175 #ifdef SUPPORT_FILTER
176 CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId());
177 CPluginClient* client = CPluginClient::GetInstance();
178
179 if (tab && client)
180 {
181 CString documentUrl = tab->GetDocumentUrl();
182 // Page is identical to document => don't block
183 if (documentUrl == src)
184 {
185 // fall through
186 }
187 else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhi telistedUrl(std::wstring(documentUrl)))
188 {
189 m_boundDomain = tab->GetDocumentUrl();
190 m_contentType = CFilter::contentTypeAny;
191 #ifdef SUPPORT_FRAME_CACHING
192 if (nullptr != tab && tab->IsFrameCached(src))
193 {
194 m_contentType = CFilter::contentTypeSubdocument;
195 }
196 else
197 #endif // SUPPORT_FRAME_CACHING
198 {
199 m_contentType = GetContentType(mimeType, m_boundDomain, src);
200 }
201 }
202 }
203
204 if (nullptr == tab)
205 {
206 m_contentType = GetContentType(mimeType, m_boundDomain, src);
207 }
208
209 {
210 // Here is the heuristic which detects the requests issued by Flash.ocx.
211 // It turned out that the implementation from ''Flash.ocx'' (tested version is 15.0.0.152)
212 // returns quite minimal configuration in comparison with the implementation from Microsofts'
213 // libraries (see grfBINDF and bindInfo.dwOptions). The impl from MS often i ncludes something
214 // else.
215 ATL::CComPtr<IBindStatusCallback> bscb;
216 if (SUCCEEDED(QueryServiceFromClient(&bscb)) && !!bscb)
217 {
218 DWORD grfBINDF = 0;
219 BINDINFO bindInfo = {};
220 bindInfo.cbSize = sizeof(bindInfo);
221 if (SUCCEEDED(bscb->GetBindInfo(&grfBINDF, &bindInfo))
222 && (BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE| BINDF_PULLDATA) == grfBINDF
223 && (BINDINFO_OPTIONS_ENABLE_UTF8 | BINDINFO_OPTIONS_USE_IE_ENCODING) == bindInfo.dwOptions
224 )
225 {
226 m_contentType = CFilter::EContentType::contentTypeObjectSubrequest;
227 }
228 }
229 }
230
231 // The descision about EContentType::contentTypeAny is made later in
232 // WBPassthruSink::BeginningTransaction. Sometimes here we cannot detect the r equest type, but
233 // in WBPassthruSink::BeginningTransaction the header Accept is available whic h allows to
234 // obtain the "request type" in our terminology.
235 if (nullptr != client
236 && CFilter::EContentType::contentTypeAny != m_contentType
237 && client->ShouldBlock(src, m_contentType, m_boundDomain, true))
238 {
239 isBlocked = true;
240 }
241
242 // For IE6 and earlier there is iframe back button issue, so avoid it.
243 if (isBlocked && client->GetIEVersion() > 6)
244 {
245 handled = true;
246 if (CFilter::EContentType::contentTypeImage == m_contentType)
247 {
248 // IE shows a cross that img is not loaded
249 return INET_E_REDIRECT_FAILED;
250 }
251 if (CFilter::EContentType::contentTypeSubdocument == m_contentType)
252 {
253 PassthroughAPP::CustomSinkStartPolicy<WBPassthru, WBPassthruSink>::GetProt ocol(this)->m_shouldSupplyCustomContent = true;
254 m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L"t ext/html");
255 m_spInternetProtocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION, 0, static _cast<ULONG>(g_blockedByABPPage.size()));
256 return S_OK;
257 }
258 if (CFilter::EContentType::contentTypeScript == m_contentType)
259 {
260 m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L"t ext/javascript");
261 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:");
262 return INET_E_REDIRECT_FAILED;
263 }
264 if (CFilter::EContentType::contentTypeXmlHttpRequest == m_contentType)
265 {
266 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:");
267 return INET_E_REDIRECT_FAILED;
268 }
269 if (CFilter::EContentType::contentTypeAny != m_contentType)
270 {
271 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:");
272 return INET_E_REDIRECT_FAILED;
273 }
274 }
275 #endif // SUPPORT_FILTER
276
277 return isBlocked ? S_FALSE : hr;
278 } 138 }
279 139
280 HRESULT WBPassthruSink::OnRead(void* pv, ULONG cb, ULONG* pcbRead) 140 HRESULT WBPassthruSink::OnRead(void* pv, ULONG cb, ULONG* pcbRead)
281 { 141 {
282 if (nullptr == pv) 142 if (nullptr == pv)
283 { 143 {
284 return E_POINTER; 144 return E_POINTER;
285 } 145 }
286 if (nullptr == pcbRead) 146 if (nullptr == pcbRead)
287 { 147 {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 IInternetProtocolSink::Switch with PD_FORCE_SWITCH flag in 194 IInternetProtocolSink::Switch with PD_FORCE_SWITCH flag in
335 PROTOCOLDATA::grfFlags, eventually URLMon will turn around and call 195 PROTOCOLDATA::grfFlags, eventually URLMon will turn around and call
336 IInternetProtocol::Continue on the main thread. 196 IInternetProtocol::Continue on the main thread.
337 197
338 Or, if you happen to have a window handy that was created on the main 198 Or, if you happen to have a window handy that was created on the main
339 thread, you can post yourself a message. 199 thread, you can post yourself a message.
340 " 200 "
341 */ 201 */
342 return m_spInternetProtocolSink ? m_spInternetProtocolSink->Switch(pProtocolDa ta) : E_UNEXPECTED; 202 return m_spInternetProtocolSink ? m_spInternetProtocolSink->Switch(pProtocolDa ta) : E_UNEXPECTED;
343 } 203 }
204 std::wstring ExtractHTTPHeader(const std::wstring& allHeaders, const std::wstrin g& targetHeaderName)
205 {
206 auto targetHeaderBeginsAt = allHeaders.find(targetHeaderName);
207 if (std::string::npos == targetHeaderBeginsAt )
208 {
209 return L"";
210 }
211 targetHeaderBeginsAt += targetHeaderName.length();
212 auto targetHeaderEndsAt = allHeaders.find(L"\n", targetHeaderBeginsAt);
213 if (std::string::npos == targetHeaderEndsAt)
214 {
215 return L"";
216 }
217 return allHeaders.substr(targetHeaderBeginsAt, targetHeaderEndsAt - targetHead erBeginsAt);
218 }
219
Eric 2014/10/15 17:02:33 If we really need both standard and wide versions
220 std::string ExtractHTTPHeader(const std::string& allHeaders, const std::string& targetHeaderName)
221 {
222 auto targetHeaderBeginsAt = allHeaders.find(targetHeaderName);
223 if (std::string::npos == targetHeaderBeginsAt )
224 {
225 return "";
226 }
227 targetHeaderBeginsAt += targetHeaderName.length();
228 auto targetHeaderEndsAt = allHeaders.find("\r\n", targetHeaderBeginsAt);
229 if (std::string::npos == targetHeaderEndsAt)
230 {
231 return "";
232 }
233 return allHeaders.substr(targetHeaderBeginsAt, targetHeaderEndsAt - targetHead erBeginsAt);
234 }
344 235
345 STDMETHODIMP WBPassthruSink::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeade rs, DWORD dwReserved, LPWSTR* pszAdditionalHeaders) 236 STDMETHODIMP WBPassthruSink::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeade rs, DWORD dwReserved, LPWSTR* pszAdditionalHeaders)
346 { 237 {
347 if (pszAdditionalHeaders) 238 if (pszAdditionalHeaders)
348 { 239 {
349 *pszAdditionalHeaders = nullptr; 240 *pszAdditionalHeaders = nullptr;
350 } 241 }
351 242
Eric 2014/10/15 17:02:33 I'm assuming this is new code that's not showing u
243 CComPtr<IHttpNegotiate> spHttpNegotiate;
244 QueryServiceFromClient(&spHttpNegotiate);
245 HRESULT nativeHresult = spHttpNegotiate ? spHttpNegotiate->BeginningTransactio n(szURL, szHeaders,dwReserved, pszAdditionalHeaders) : S_OK;
Eric 2014/10/15 17:02:33 It would seem to me that if the service query fail
246
247 bool isBlocked = false;
248 CString src = szURL;
249 DEBUG_GENERAL(src);
250 CPluginClient::UnescapeUrl(src);
251
352 CPluginClient* client = nullptr; 252 CPluginClient* client = nullptr;
353 if (CFilter::EContentType::contentTypeAny == m_contentType && (client = CPlugi nClient::GetInstance())) 253 if (CFilter::EContentType::contentTypeAny == m_contentType && (client = CPlugi nClient::GetInstance()))
354 { 254 {
355 auto acceptHeader = [&]() -> std::string 255 auto acceptHeader = [&]() -> std::string
Eric 2014/10/15 17:02:33 This function does not look like a good candidate
Eric 2014/10/15 17:02:33 Also, it would be better generally to have 'accept
Oleksandr 2014/10/24 09:59:18 I think it's good to leave this as is, since it he
356 { 256 {
357 // Despite there is HTTP_QUERY_ACCEPT and other query info flags, they don 't work here, 257 // Despite there is HTTP_QUERY_ACCEPT and other query info flags, they don 't work here,
358 // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does dork. 258 // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does dork.
Eric 2014/10/15 17:02:33 Doesn't HTTP_QUERY_CUSTOM work? If so, that would
Oleksandr 2014/10/24 09:59:18 No, it doesn't work. Added a comment about that.
359 ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo; 259 ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo;
360 HRESULT hr = m_spTargetProtocol->QueryInterface(&winInetHttpInfo); 260 HRESULT hr = m_spTargetProtocol->QueryInterface(&winInetHttpInfo);
361 if(FAILED(hr)) 261 if(FAILED(hr))
362 { 262 {
363 return ""; 263 return "";
364 } 264 }
365 DWORD size = 0; 265 DWORD size = 0;
366 DWORD flags = 0; 266 DWORD flags = 0;
367 hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_F LAG_REQUEST_HEADERS, 267 hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_F LAG_REQUEST_HEADERS,
368 /*buffer*/nullptr, /* get size */&size, &flags, /*reserved*/ 0); 268 /*buffer*/nullptr, /* get size */&size, &flags, /*reserved*/ 0);
369 if(FAILED(hr)) 269 if(FAILED(hr))
370 { 270 {
371 return ""; 271 return "";
372 } 272 }
373 std::string buf(size, '\0'); 273 std::string buf(size, '\0');
Eric 2014/10/15 17:02:33 I'll simply register my distaste for writing into
374 hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_F LAG_REQUEST_HEADERS, 274 hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_F LAG_REQUEST_HEADERS,
375 &buf[0], &size, &flags, 0); 275 &buf[0], &size, &flags, 0);
376 if(FAILED(hr)) 276 if(FAILED(hr))
377 { 277 {
378 return ""; 278 return "";
379 } 279 }
380 char acceptHeader[] = "Accept:"; 280 return ExtractHTTPHeader(buf, "Accept:");
381 auto acceptHeaderBeginsAt = buf.find(acceptHeader);
382 if (std::string::npos == acceptHeaderBeginsAt)
383 {
384 return "";
385 }
386 acceptHeaderBeginsAt += sizeof(acceptHeader);
387 auto acceptHeaderEndsAt = buf.find("\n", acceptHeaderBeginsAt);
388 if (std::string::npos == acceptHeaderEndsAt)
389 {
390 return "";
391 }
392 return buf.substr(acceptHeaderBeginsAt, acceptHeaderEndsAt - acceptHeaderB eginsAt);
393 }(); 281 }();
394 m_contentType = GetContentTypeFromMimeType(ATL::CString(acceptHeader.c_str() )); 282 m_contentType = GetContentTypeFromMimeType(ATL::CString(acceptHeader.c_str() ));
395 bool isBlocked = client->ShouldBlock(szURL, m_contentType, m_boundDomain, /* debug flag but must be set*/true); 283
284 m_boundDomain = ExtractHTTPHeader(std::wstring(*pszAdditionalHeaders), L"Ref erer:").c_str();
Eric 2014/10/15 17:02:33 In general, use 'ToCString()' here preferably. It
Eric 2014/10/15 17:02:33 Is it the case the "Referer" only ever appears in
sergei 2014/10/28 14:08:46 It indeed looks inconsistent although there is may
Oleksandr 2014/10/30 23:40:48 In my tests the Referer is available through Query
sergei 2014/10/31 12:47:57 It's OK for me.
285 m_boundDomain = m_boundDomain.Trim(L" \r");
286 CPluginClient::UnescapeUrl(m_boundDomain);
287
288 CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId());
289 CPluginClient* client = CPluginClient::GetInstance();
290
291 if (tab && client)
292 {
293 CString documentUrl = tab->GetDocumentUrl();
294 // Page is identical to document => don't block
295 if (documentUrl == src)
296 {
297 return nativeHresult;
298 }
299 else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsW hitelistedUrl(std::wstring(documentUrl)))
300 {
Eric 2014/10/15 17:02:33 FYI. A solid reason to need a rewrite. The change
301 #ifdef SUPPORT_FRAME_CACHING
302 if (tab->IsFrameCached(src))
303 {
304 m_contentType = CFilter::contentTypeSubdocument;
305 }
306 #endif // SUPPORT_FRAME_CACHING
307 }
308 }
309
310 {
311 // Here is the heuristic which detects the requests issued by Flash.ocx.
312 // It turned out that the implementation from ''Flash.ocx'' (tested versio n is 15.0.0.152)
313 // returns quite minimal configuration in comparison with the implementati on from Microsofts'
314 // libraries (see grfBINDF and bindInfo.dwOptions). The impl from MS often includes something
315 // else.
316 ATL::CComPtr<IBindStatusCallback> bscb;
317 if (SUCCEEDED(QueryServiceFromClient(&bscb)) && !!bscb)
318 {
319 DWORD grfBINDF = 0;
320 BINDINFO bindInfo = {};
321 bindInfo.cbSize = sizeof(bindInfo);
322 if (SUCCEEDED(bscb->GetBindInfo(&grfBINDF, &bindInfo))
323 && (BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE| BINDF_PULLDATA) == grfBIN DF
324 && (BINDINFO_OPTIONS_ENABLE_UTF8 | BINDINFO_OPTIONS_USE_IE_ENCODING) = = bindInfo.dwOptions
325 )
326 {
327 m_contentType = CFilter::EContentType::contentTypeObjectSubrequest;
328 }
329 }
330 }
331
332 if (nullptr != client
333 && client->ShouldBlock(src, m_contentType, m_boundDomain, true))
334 {
335 isBlocked = true;
336 }
337
338 // For IE6 and earlier there is iframe back button issue, so avoid it.
339 if (isBlocked && client->GetIEVersion() > 6)
340 {
341 if (CFilter::EContentType::contentTypeImage == m_contentType)
342 {
343 // IE shows a cross that img is not loaded
344 return INET_E_REDIRECT_FAILED;
345 }
346 if (CFilter::EContentType::contentTypeSubdocument == m_contentType)
347 {
348 PassthroughAPP::CustomSinkStartPolicy<WBPassthru, WBPassthruSink>::GetPr otocol(this)->m_shouldSupplyCustomContent = true;
349 m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L "text/html");
350 m_spInternetProtocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION, 0, stat ic_cast<ULONG>(g_blockedByABPPage.size()));
351 return S_OK;
352 }
353 if (CFilter::EContentType::contentTypeScript == m_contentType)
354 {
355 m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L "text/javascript");
356 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:" );
357 return INET_E_REDIRECT_FAILED;
358 }
359 if (CFilter::EContentType::contentTypeXmlHttpRequest == m_contentType)
360 {
361 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:" );
362 return INET_E_REDIRECT_FAILED;
363 }
364 if (CFilter::EContentType::contentTypeAny != m_contentType)
365 {
366 m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:" );
367 return INET_E_REDIRECT_FAILED;
368 }
369 }
396 if (isBlocked) 370 if (isBlocked)
397 { 371 {
398 m_blockedInTransaction = true; 372 m_blockedInTransaction = true;
399 return E_ABORT; 373 return E_ABORT;
400 } 374 }
401 } 375 }
402 CComPtr<IHttpNegotiate> spHttpNegotiate; 376
403 QueryServiceFromClient(&spHttpNegotiate); 377 return nativeHresult;
404 return spHttpNegotiate ? spHttpNegotiate->BeginningTransaction(szURL, szHeader s,dwReserved, pszAdditionalHeaders) : S_OK;
405 } 378 }
406 379
407 STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponse Headers, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) 380 STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponse Headers, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
408 { 381 {
409 if (pszAdditionalRequestHeaders) 382 if (pszAdditionalRequestHeaders)
410 { 383 {
411 *pszAdditionalRequestHeaders = 0; 384 *pszAdditionalRequestHeaders = 0;
412 } 385 }
413 386
414 CComPtr<IHttpNegotiate> spHttpNegotiate; 387 CComPtr<IHttpNegotiate> spHttpNegotiate;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 } 442 }
470 443
471 STDMETHODIMP WBPassthru::UnlockRequest() 444 STDMETHODIMP WBPassthru::UnlockRequest()
472 { 445 {
473 if (!m_hasOriginalStartCalled) 446 if (!m_hasOriginalStartCalled)
474 { 447 {
475 return S_OK; 448 return S_OK;
476 } 449 }
477 return BaseClass::UnlockRequest(); 450 return BaseClass::UnlockRequest();
478 } 451 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld