OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 case WebURLRequest::kRequestContextXSLT: | 134 case WebURLRequest::kRequestContextXSLT: |
135 return "XSLT"; | 135 return "XSLT"; |
136 } | 136 } |
137 NOTREACHED(); | 137 NOTREACHED(); |
138 return "resource"; | 138 return "resource"; |
139 } | 139 } |
140 | 140 |
141 } // namespace | 141 } // namespace |
142 | 142 |
143 static void MeasureStricterVersionOfIsMixedContent(Frame& frame, | 143 static void MeasureStricterVersionOfIsMixedContent(Frame& frame, |
144 const KURL& url) { | 144 const KURL& url, |
| 145 const LocalFrame* source) { |
145 // We're currently only checking for mixed content in `https://*` contexts. | 146 // We're currently only checking for mixed content in `https://*` contexts. |
146 // What about other "secure" contexts the SchemeRegistry knows about? We'll | 147 // What about other "secure" contexts the SchemeRegistry knows about? We'll |
147 // use this method to measure the occurrence of non-webby mixed content to | 148 // use this method to measure the occurrence of non-webby mixed content to |
148 // make sure we're not breaking the world without realizing it. | 149 // make sure we're not breaking the world without realizing it. |
149 SecurityOrigin* origin = frame.GetSecurityContext()->GetSecurityOrigin(); | 150 SecurityOrigin* origin = frame.GetSecurityContext()->GetSecurityOrigin(); |
150 if (MixedContentChecker::IsMixedContent(origin, url)) { | 151 if (MixedContentChecker::IsMixedContent(origin, url)) { |
151 if (origin->Protocol() != "https") { | 152 if (origin->Protocol() != "https") { |
152 UseCounter::Count( | 153 UseCounter::Count( |
153 &frame, | 154 source, |
154 UseCounter::kMixedContentInNonHTTPSFrameThatRestrictsMixedContent); | 155 UseCounter::kMixedContentInNonHTTPSFrameThatRestrictsMixedContent); |
155 } | 156 } |
156 } else if (!SecurityOrigin::IsSecure(url) && | 157 } else if (!SecurityOrigin::IsSecure(url) && |
157 SchemeRegistry::ShouldTreatURLSchemeAsSecure(origin->Protocol())) { | 158 SchemeRegistry::ShouldTreatURLSchemeAsSecure(origin->Protocol())) { |
158 UseCounter::Count( | 159 UseCounter::Count( |
159 &frame, | 160 source, |
160 UseCounter::kMixedContentInSecureFrameThatDoesNotRestrictMixedContent); | 161 UseCounter::kMixedContentInSecureFrameThatDoesNotRestrictMixedContent); |
161 } | 162 } |
162 } | 163 } |
163 | 164 |
164 bool RequestIsSubframeSubresource(Frame* frame, | 165 bool RequestIsSubframeSubresource(Frame* frame, |
165 WebURLRequest::FrameType frame_type) { | 166 WebURLRequest::FrameType frame_type) { |
166 return (frame && frame != frame->Tree().Top() && | 167 return (frame && frame != frame->Tree().Top() && |
167 frame_type != WebURLRequest::kFrameTypeNested); | 168 frame_type != WebURLRequest::kFrameTypeNested); |
168 } | 169 } |
169 | 170 |
(...skipping 18 matching lines...) Expand all Loading... |
188 if (is_allowed && url.ProtocolIs("http") && | 189 if (is_allowed && url.ProtocolIs("http") && |
189 NetworkUtils::IsLocalHostname(url.Host(), nullptr)) | 190 NetworkUtils::IsLocalHostname(url.Host(), nullptr)) |
190 is_allowed = false; | 191 is_allowed = false; |
191 return !is_allowed; | 192 return !is_allowed; |
192 } | 193 } |
193 | 194 |
194 // static | 195 // static |
195 Frame* MixedContentChecker::InWhichFrameIsContentMixed( | 196 Frame* MixedContentChecker::InWhichFrameIsContentMixed( |
196 Frame* frame, | 197 Frame* frame, |
197 WebURLRequest::FrameType frame_type, | 198 WebURLRequest::FrameType frame_type, |
198 const KURL& url) { | 199 const KURL& url, |
| 200 const LocalFrame* source) { |
199 // We only care about subresource loads; top-level navigations cannot be mixed | 201 // We only care about subresource loads; top-level navigations cannot be mixed |
200 // content. Neither can frameless requests. | 202 // content. Neither can frameless requests. |
201 if (frame_type == WebURLRequest::kFrameTypeTopLevel || !frame) | 203 if (frame_type == WebURLRequest::kFrameTypeTopLevel || !frame) |
202 return nullptr; | 204 return nullptr; |
203 | 205 |
204 // Check the top frame first. | 206 // Check the top frame first. |
205 Frame& top = frame->Tree().Top(); | 207 Frame& top = frame->Tree().Top(); |
206 MeasureStricterVersionOfIsMixedContent(top, url); | 208 MeasureStricterVersionOfIsMixedContent(top, url, source); |
207 if (IsMixedContent(top.GetSecurityContext()->GetSecurityOrigin(), url)) | 209 if (IsMixedContent(top.GetSecurityContext()->GetSecurityOrigin(), url)) |
208 return ⊤ | 210 return ⊤ |
209 | 211 |
210 MeasureStricterVersionOfIsMixedContent(*frame, url); | 212 MeasureStricterVersionOfIsMixedContent(*frame, url, source); |
211 if (IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(), url)) | 213 if (IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(), url)) |
212 return frame; | 214 return frame; |
213 | 215 |
214 // No mixed content, no problem. | 216 // No mixed content, no problem. |
215 return nullptr; | 217 return nullptr; |
216 } | 218 } |
217 | 219 |
218 // static | 220 // static |
219 void MixedContentChecker::LogToConsoleAboutFetch( | 221 void MixedContentChecker::LogToConsoleAboutFetch( |
220 LocalFrame* frame, | 222 LocalFrame* frame, |
(...skipping 17 matching lines...) Expand all Loading... |
238 ConsoleMessage::Create(kSecurityMessageSource, message_level, message, | 240 ConsoleMessage::Create(kSecurityMessageSource, message_level, message, |
239 std::move(source_location))); | 241 std::move(source_location))); |
240 } else { | 242 } else { |
241 frame->GetDocument()->AddConsoleMessage( | 243 frame->GetDocument()->AddConsoleMessage( |
242 ConsoleMessage::Create(kSecurityMessageSource, message_level, message)); | 244 ConsoleMessage::Create(kSecurityMessageSource, message_level, message)); |
243 } | 245 } |
244 } | 246 } |
245 | 247 |
246 // static | 248 // static |
247 void MixedContentChecker::Count(Frame* frame, | 249 void MixedContentChecker::Count(Frame* frame, |
248 WebURLRequest::RequestContext request_context) { | 250 WebURLRequest::RequestContext request_context, |
249 UseCounter::Count(frame, UseCounter::kMixedContentPresent); | 251 const LocalFrame* source) { |
| 252 UseCounter::Count(source, UseCounter::kMixedContentPresent); |
250 | 253 |
251 // Roll blockable content up into a single counter, count unblocked types | 254 // Roll blockable content up into a single counter, count unblocked types |
252 // individually so we can determine when they can be safely moved to the | 255 // individually so we can determine when they can be safely moved to the |
253 // blockable category: | 256 // blockable category: |
254 WebMixedContentContextType context_type = | 257 WebMixedContentContextType context_type = |
255 WebMixedContent::ContextTypeFromRequestContext( | 258 WebMixedContent::ContextTypeFromRequestContext( |
256 request_context, | 259 request_context, |
257 frame->GetSettings()->GetStrictMixedContentCheckingForPlugin()); | 260 frame->GetSettings()->GetStrictMixedContentCheckingForPlugin()); |
258 if (context_type == WebMixedContentContextType::kBlockable) { | 261 if (context_type == WebMixedContentContextType::kBlockable) { |
259 UseCounter::Count(frame, UseCounter::kMixedContentBlockable); | 262 UseCounter::Count(source, UseCounter::kMixedContentBlockable); |
260 return; | 263 return; |
261 } | 264 } |
262 | 265 |
263 UseCounter::Feature feature; | 266 UseCounter::Feature feature; |
264 switch (request_context) { | 267 switch (request_context) { |
265 case WebURLRequest::kRequestContextAudio: | 268 case WebURLRequest::kRequestContextAudio: |
266 feature = UseCounter::kMixedContentAudio; | 269 feature = UseCounter::kMixedContentAudio; |
267 break; | 270 break; |
268 case WebURLRequest::kRequestContextDownload: | 271 case WebURLRequest::kRequestContextDownload: |
269 feature = UseCounter::kMixedContentDownload; | 272 feature = UseCounter::kMixedContentDownload; |
(...skipping 14 matching lines...) Expand all Loading... |
284 feature = UseCounter::kMixedContentPrefetch; | 287 feature = UseCounter::kMixedContentPrefetch; |
285 break; | 288 break; |
286 case WebURLRequest::kRequestContextVideo: | 289 case WebURLRequest::kRequestContextVideo: |
287 feature = UseCounter::kMixedContentVideo; | 290 feature = UseCounter::kMixedContentVideo; |
288 break; | 291 break; |
289 | 292 |
290 default: | 293 default: |
291 NOTREACHED(); | 294 NOTREACHED(); |
292 return; | 295 return; |
293 } | 296 } |
294 UseCounter::Count(frame, feature); | 297 UseCounter::Count(source, feature); |
295 } | 298 } |
296 | 299 |
297 // static | 300 // static |
298 bool MixedContentChecker::ShouldBlockFetch( | 301 bool MixedContentChecker::ShouldBlockFetch( |
299 LocalFrame* frame, | 302 LocalFrame* frame, |
300 WebURLRequest::RequestContext request_context, | 303 WebURLRequest::RequestContext request_context, |
301 WebURLRequest::FrameType frame_type, | 304 WebURLRequest::FrameType frame_type, |
302 ResourceRequest::RedirectStatus redirect_status, | 305 ResourceRequest::RedirectStatus redirect_status, |
303 const KURL& url, | 306 const KURL& url, |
304 SecurityViolationReportingPolicy reporting_policy) { | 307 SecurityViolationReportingPolicy reporting_policy) { |
305 // Frame-level loads are checked by the browser if PlzNavigate is enabled. No | 308 // Frame-level loads are checked by the browser if PlzNavigate is enabled. No |
306 // need to check them again here. | 309 // need to check them again here. |
307 if (frame->GetSettings()->GetBrowserSideNavigationEnabled() && | 310 if (frame->GetSettings()->GetBrowserSideNavigationEnabled() && |
308 frame_type != WebURLRequest::kFrameTypeNone) { | 311 frame_type != WebURLRequest::kFrameTypeNone) { |
309 return false; | 312 return false; |
310 } | 313 } |
311 | 314 |
312 Frame* effective_frame = EffectiveFrameForFrameType(frame, frame_type); | 315 Frame* effective_frame = EffectiveFrameForFrameType(frame, frame_type); |
313 Frame* mixed_frame = | 316 Frame* mixed_frame = |
314 InWhichFrameIsContentMixed(effective_frame, frame_type, url); | 317 InWhichFrameIsContentMixed(effective_frame, frame_type, url, frame); |
315 if (!mixed_frame) | 318 if (!mixed_frame) |
316 return false; | 319 return false; |
317 | 320 |
318 MixedContentChecker::Count(mixed_frame, request_context); | 321 MixedContentChecker::Count(mixed_frame, request_context, frame); |
319 if (ContentSecurityPolicy* policy = | 322 if (ContentSecurityPolicy* policy = |
320 frame->GetSecurityContext()->GetContentSecurityPolicy()) | 323 frame->GetSecurityContext()->GetContentSecurityPolicy()) |
321 policy->ReportMixedContent(url, redirect_status); | 324 policy->ReportMixedContent(url, redirect_status); |
322 | 325 |
323 Settings* settings = mixed_frame->GetSettings(); | 326 Settings* settings = mixed_frame->GetSettings(); |
324 // Use the current local frame's client; the embedder doesn't distinguish | 327 // Use the current local frame's client; the embedder doesn't distinguish |
325 // mixed content signals from different frames on the same page. | 328 // mixed content signals from different frames on the same page. |
326 LocalFrameClient* client = frame->Loader().Client(); | 329 LocalFrameClient* client = frame->Loader().Client(); |
327 ContentSettingsClient* content_settings_client = | 330 ContentSettingsClient* content_settings_client = |
328 frame->GetContentSettingsClient(); | 331 frame->GetContentSettingsClient(); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
368 // subframes, unless all insecure content is allowed. This is to avoid the | 371 // subframes, unless all insecure content is allowed. This is to avoid the |
369 // following situation: https://a.com embeds https://b.com, which loads a | 372 // following situation: https://a.com embeds https://b.com, which loads a |
370 // script over insecure HTTP. The user opts to allow the insecure content, | 373 // script over insecure HTTP. The user opts to allow the insecure content, |
371 // thinking that they are allowing an insecure script to run on | 374 // thinking that they are allowing an insecure script to run on |
372 // https://a.com and not realizing that they are in fact allowing an | 375 // https://a.com and not realizing that they are in fact allowing an |
373 // insecure script on https://b.com. | 376 // insecure script on https://b.com. |
374 if (!settings->GetAllowRunningOfInsecureContent() && | 377 if (!settings->GetAllowRunningOfInsecureContent() && |
375 RequestIsSubframeSubresource(effective_frame, frame_type) && | 378 RequestIsSubframeSubresource(effective_frame, frame_type) && |
376 IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(), | 379 IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(), |
377 url)) { | 380 url)) { |
378 UseCounter::Count(mixed_frame, | 381 UseCounter::Count(frame, |
379 UseCounter::kBlockableMixedContentInSubframeBlocked); | 382 UseCounter::kBlockableMixedContentInSubframeBlocked); |
380 allowed = false; | 383 allowed = false; |
381 break; | 384 break; |
382 } | 385 } |
383 | 386 |
384 bool should_ask_embedder = | 387 bool should_ask_embedder = |
385 !strict_mode && settings && | 388 !strict_mode && settings && |
386 (!settings->GetStrictlyBlockBlockableMixedContent() || | 389 (!settings->GetStrictlyBlockBlockableMixedContent() || |
387 settings->GetAllowRunningOfInsecureContent()); | 390 settings->GetAllowRunningOfInsecureContent()); |
388 allowed = should_ask_embedder && | 391 allowed = should_ask_embedder && |
389 content_settings_client->AllowRunningInsecureContent( | 392 content_settings_client->AllowRunningInsecureContent( |
390 settings && settings->GetAllowRunningOfInsecureContent(), | 393 settings && settings->GetAllowRunningOfInsecureContent(), |
391 security_origin, url); | 394 security_origin, url); |
392 if (allowed) { | 395 if (allowed) { |
393 client->DidRunInsecureContent(security_origin, url); | 396 client->DidRunInsecureContent(security_origin, url); |
394 UseCounter::Count(mixed_frame, | 397 UseCounter::Count(frame, UseCounter::kMixedContentBlockableAllowed); |
395 UseCounter::kMixedContentBlockableAllowed); | |
396 } | 398 } |
397 break; | 399 break; |
398 } | 400 } |
399 | 401 |
400 case WebMixedContentContextType::kShouldBeBlockable: | 402 case WebMixedContentContextType::kShouldBeBlockable: |
401 allowed = !strict_mode; | 403 allowed = !strict_mode; |
402 if (allowed) | 404 if (allowed) |
403 client->DidDisplayInsecureContent(); | 405 client->DidDisplayInsecureContent(); |
404 break; | 406 break; |
405 case WebMixedContentContextType::kNotMixedContent: | 407 case WebMixedContentContextType::kNotMixedContent: |
(...skipping 27 matching lines...) Expand all Loading... |
433 allowed ? kWarningMessageLevel : kErrorMessageLevel; | 435 allowed ? kWarningMessageLevel : kErrorMessageLevel; |
434 frame->GetDocument()->AddConsoleMessage( | 436 frame->GetDocument()->AddConsoleMessage( |
435 ConsoleMessage::Create(kSecurityMessageSource, message_level, message)); | 437 ConsoleMessage::Create(kSecurityMessageSource, message_level, message)); |
436 } | 438 } |
437 | 439 |
438 // static | 440 // static |
439 bool MixedContentChecker::ShouldBlockWebSocket( | 441 bool MixedContentChecker::ShouldBlockWebSocket( |
440 LocalFrame* frame, | 442 LocalFrame* frame, |
441 const KURL& url, | 443 const KURL& url, |
442 SecurityViolationReportingPolicy reporting_policy) { | 444 SecurityViolationReportingPolicy reporting_policy) { |
443 Frame* mixed_frame = | 445 Frame* mixed_frame = InWhichFrameIsContentMixed( |
444 InWhichFrameIsContentMixed(frame, WebURLRequest::kFrameTypeNone, url); | 446 frame, WebURLRequest::kFrameTypeNone, url, frame); |
445 if (!mixed_frame) | 447 if (!mixed_frame) |
446 return false; | 448 return false; |
447 | 449 |
448 UseCounter::Count(mixed_frame, UseCounter::kMixedContentPresent); | 450 UseCounter::Count(frame, UseCounter::kMixedContentPresent); |
449 UseCounter::Count(mixed_frame, UseCounter::kMixedContentWebSocket); | 451 UseCounter::Count(frame, UseCounter::kMixedContentWebSocket); |
450 if (ContentSecurityPolicy* policy = | 452 if (ContentSecurityPolicy* policy = |
451 frame->GetSecurityContext()->GetContentSecurityPolicy()) { | 453 frame->GetSecurityContext()->GetContentSecurityPolicy()) { |
452 policy->ReportMixedContent(url, | 454 policy->ReportMixedContent(url, |
453 ResourceRequest::RedirectStatus::kNoRedirect); | 455 ResourceRequest::RedirectStatus::kNoRedirect); |
454 } | 456 } |
455 | 457 |
456 Settings* settings = mixed_frame->GetSettings(); | 458 Settings* settings = mixed_frame->GetSettings(); |
457 // Use the current local frame's client; the embedder doesn't distinguish | 459 // Use the current local frame's client; the embedder doesn't distinguish |
458 // mixed content signals from different frames on the same page. | 460 // mixed content signals from different frames on the same page. |
459 ContentSettingsClient* content_settings_client = | 461 ContentSettingsClient* content_settings_client = |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
491 LocalFrame* frame, | 493 LocalFrame* frame, |
492 const KURL& url, | 494 const KURL& url, |
493 SecurityViolationReportingPolicy reporting_policy) { | 495 SecurityViolationReportingPolicy reporting_policy) { |
494 // For whatever reason, some folks handle forms via JavaScript, and submit to | 496 // For whatever reason, some folks handle forms via JavaScript, and submit to |
495 // `javascript:void(0)` rather than calling `preventDefault()`. We | 497 // `javascript:void(0)` rather than calling `preventDefault()`. We |
496 // special-case `javascript:` URLs here, as they don't introduce MixedContent | 498 // special-case `javascript:` URLs here, as they don't introduce MixedContent |
497 // for form submissions. | 499 // for form submissions. |
498 if (url.ProtocolIs("javascript")) | 500 if (url.ProtocolIs("javascript")) |
499 return false; | 501 return false; |
500 | 502 |
501 Frame* mixed_frame = | 503 Frame* mixed_frame = InWhichFrameIsContentMixed( |
502 InWhichFrameIsContentMixed(frame, WebURLRequest::kFrameTypeNone, url); | 504 frame, WebURLRequest::kFrameTypeNone, url, frame); |
503 if (!mixed_frame) | 505 if (!mixed_frame) |
504 return false; | 506 return false; |
505 | 507 |
506 UseCounter::Count(mixed_frame, UseCounter::kMixedContentPresent); | 508 UseCounter::Count(frame, UseCounter::kMixedContentPresent); |
507 | 509 |
508 // Use the current local frame's client; the embedder doesn't distinguish | 510 // Use the current local frame's client; the embedder doesn't distinguish |
509 // mixed content signals from different frames on the same page. | 511 // mixed content signals from different frames on the same page. |
510 frame->Loader().Client()->DidContainInsecureFormAction(); | 512 frame->Loader().Client()->DidContainInsecureFormAction(); |
511 | 513 |
512 if (reporting_policy == SecurityViolationReportingPolicy::kReport) { | 514 if (reporting_policy == SecurityViolationReportingPolicy::kReport) { |
513 String message = String::Format( | 515 String message = String::Format( |
514 "Mixed Content: The page at '%s' was loaded over a secure connection, " | 516 "Mixed Content: The page at '%s' was loaded over a secure connection, " |
515 "but contains a form which targets an insecure endpoint '%s'. This " | 517 "but contains a form which targets an insecure endpoint '%s'. This " |
516 "endpoint should be made available over a secure connection.", | 518 "endpoint should be made available over a secure connection.", |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
612 } | 614 } |
613 } | 615 } |
614 | 616 |
615 WebMixedContentContextType MixedContentChecker::ContextTypeForInspector( | 617 WebMixedContentContextType MixedContentChecker::ContextTypeForInspector( |
616 LocalFrame* frame, | 618 LocalFrame* frame, |
617 const ResourceRequest& request) { | 619 const ResourceRequest& request) { |
618 Frame* effective_frame = | 620 Frame* effective_frame = |
619 EffectiveFrameForFrameType(frame, request.GetFrameType()); | 621 EffectiveFrameForFrameType(frame, request.GetFrameType()); |
620 | 622 |
621 Frame* mixed_frame = InWhichFrameIsContentMixed( | 623 Frame* mixed_frame = InWhichFrameIsContentMixed( |
622 effective_frame, request.GetFrameType(), request.Url()); | 624 effective_frame, request.GetFrameType(), request.Url(), frame); |
623 if (!mixed_frame) | 625 if (!mixed_frame) |
624 return WebMixedContentContextType::kNotMixedContent; | 626 return WebMixedContentContextType::kNotMixedContent; |
625 | 627 |
626 // See comment in shouldBlockFetch() about loading the main resource of a | 628 // See comment in shouldBlockFetch() about loading the main resource of a |
627 // subframe. | 629 // subframe. |
628 if (request.GetFrameType() == WebURLRequest::kFrameTypeNested && | 630 if (request.GetFrameType() == WebURLRequest::kFrameTypeNested && |
629 !SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled( | 631 !SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled( |
630 request.Url().Protocol())) { | 632 request.Url().Protocol())) { |
631 return WebMixedContentContextType::kOptionallyBlockable; | 633 return WebMixedContentContextType::kOptionallyBlockable; |
632 } | 634 } |
633 | 635 |
634 bool strict_mixed_content_checking_for_plugin = | 636 bool strict_mixed_content_checking_for_plugin = |
635 mixed_frame->GetSettings() && | 637 mixed_frame->GetSettings() && |
636 mixed_frame->GetSettings()->GetStrictMixedContentCheckingForPlugin(); | 638 mixed_frame->GetSettings()->GetStrictMixedContentCheckingForPlugin(); |
637 return WebMixedContent::ContextTypeFromRequestContext( | 639 return WebMixedContent::ContextTypeFromRequestContext( |
638 request.GetRequestContext(), strict_mixed_content_checking_for_plugin); | 640 request.GetRequestContext(), strict_mixed_content_checking_for_plugin); |
639 } | 641 } |
640 | 642 |
641 } // namespace blink | 643 } // namespace blink |
OLD | NEW |