| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2011, 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 are | 5 * modification, are permitted provided that the following conditions are |
| 6 * met: | 6 * met: |
| 7 * | 7 * |
| 8 * * Redistributions of source code must retain the above copyright | 8 * * 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 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 233 // invocations. | 233 // invocations. |
| 234 static Vector<std::unique_ptr<ScopedPageLoadDeferrer>>& | 234 static Vector<std::unique_ptr<ScopedPageLoadDeferrer>>& |
| 235 pageLoadDeferrerStack() { | 235 pageLoadDeferrerStack() { |
| 236 DEFINE_STATIC_LOCAL(Vector<std::unique_ptr<ScopedPageLoadDeferrer>>, | 236 DEFINE_STATIC_LOCAL(Vector<std::unique_ptr<ScopedPageLoadDeferrer>>, |
| 237 deferrerStack, ()); | 237 deferrerStack, ()); |
| 238 return deferrerStack; | 238 return deferrerStack; |
| 239 } | 239 } |
| 240 | 240 |
| 241 // Ensure that the WebDragOperation enum values stay in sync with the original | 241 // Ensure that the WebDragOperation enum values stay in sync with the original |
| 242 // DragOperation constants. | 242 // DragOperation constants. |
| 243 // TODO(paulmeyer): Move this into WebFrameWidgetBase once all drag-and-drop |
| 244 // functions are out of WebViewImpl. |
| 243 #define STATIC_ASSERT_ENUM(a, b) \ | 245 #define STATIC_ASSERT_ENUM(a, b) \ |
| 244 static_assert(static_cast<int>(a) == static_cast<int>(b), \ | 246 static_assert(static_cast<int>(a) == static_cast<int>(b), \ |
| 245 "mismatching enum : " #a) | 247 "mismatching enum : " #a) |
| 246 STATIC_ASSERT_ENUM(DragOperationNone, WebDragOperationNone); | 248 STATIC_ASSERT_ENUM(DragOperationNone, WebDragOperationNone); |
| 247 STATIC_ASSERT_ENUM(DragOperationCopy, WebDragOperationCopy); | 249 STATIC_ASSERT_ENUM(DragOperationCopy, WebDragOperationCopy); |
| 248 STATIC_ASSERT_ENUM(DragOperationLink, WebDragOperationLink); | 250 STATIC_ASSERT_ENUM(DragOperationLink, WebDragOperationLink); |
| 249 STATIC_ASSERT_ENUM(DragOperationGeneric, WebDragOperationGeneric); | 251 STATIC_ASSERT_ENUM(DragOperationGeneric, WebDragOperationGeneric); |
| 250 STATIC_ASSERT_ENUM(DragOperationPrivate, WebDragOperationPrivate); | 252 STATIC_ASSERT_ENUM(DragOperationPrivate, WebDragOperationPrivate); |
| 251 STATIC_ASSERT_ENUM(DragOperationMove, WebDragOperationMove); | 253 STATIC_ASSERT_ENUM(DragOperationMove, WebDragOperationMove); |
| 252 STATIC_ASSERT_ENUM(DragOperationDelete, WebDragOperationDelete); | 254 STATIC_ASSERT_ENUM(DragOperationDelete, WebDragOperationDelete); |
| 253 STATIC_ASSERT_ENUM(DragOperationEvery, WebDragOperationEvery); | 255 STATIC_ASSERT_ENUM(DragOperationEvery, WebDragOperationEvery); |
| 254 | 256 |
| 255 static bool shouldUseExternalPopupMenus = false; | 257 static bool shouldUseExternalPopupMenus = false; |
| 256 | 258 |
| 257 namespace { | 259 namespace { |
| 258 | 260 |
| 259 class UserGestureNotifier { | |
| 260 public: | |
| 261 // If a UserGestureIndicator is created for a user gesture since the last | |
| 262 // page load and *userGestureObserved is false, the UserGestureNotifier | |
| 263 // will notify the client and set *userGestureObserved to true. | |
| 264 UserGestureNotifier(WebLocalFrameImpl*, bool* userGestureObserved); | |
| 265 ~UserGestureNotifier(); | |
| 266 | |
| 267 private: | |
| 268 Persistent<WebLocalFrameImpl> m_frame; | |
| 269 bool* const m_userGestureObserved; | |
| 270 }; | |
| 271 | |
| 272 UserGestureNotifier::UserGestureNotifier(WebLocalFrameImpl* frame, | |
| 273 bool* userGestureObserved) | |
| 274 : m_frame(frame), m_userGestureObserved(userGestureObserved) { | |
| 275 DCHECK(m_userGestureObserved); | |
| 276 } | |
| 277 | |
| 278 UserGestureNotifier::~UserGestureNotifier() { | |
| 279 if (!*m_userGestureObserved && | |
| 280 m_frame->frame()->document()->hasReceivedUserGesture()) { | |
| 281 *m_userGestureObserved = true; | |
| 282 if (m_frame && m_frame->autofillClient()) | |
| 283 m_frame->autofillClient()->firstUserGestureObserved(); | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 class EmptyEventListener final : public EventListener { | 261 class EmptyEventListener final : public EventListener { |
| 288 public: | 262 public: |
| 289 static EmptyEventListener* create() { return new EmptyEventListener(); } | 263 static EmptyEventListener* create() { return new EmptyEventListener(); } |
| 290 | 264 |
| 291 bool operator==(const EventListener& other) const override { | 265 bool operator==(const EventListener& other) const override { |
| 292 return this == &other; | 266 return this == &other; |
| 293 } | 267 } |
| 294 | 268 |
| 295 private: | 269 private: |
| 296 EmptyEventListener() : EventListener(CPPEventListenerType) {} | 270 EmptyEventListener() : EventListener(CPPEventListenerType) {} |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 402 m_doubleTapZoomPageScaleFactor(0), | 376 m_doubleTapZoomPageScaleFactor(0), |
| 403 m_doubleTapZoomPending(false), | 377 m_doubleTapZoomPending(false), |
| 404 m_enableFakePageScaleAnimationForTesting(false), | 378 m_enableFakePageScaleAnimationForTesting(false), |
| 405 m_fakePageScaleAnimationPageScaleFactor(0), | 379 m_fakePageScaleAnimationPageScaleFactor(0), |
| 406 m_fakePageScaleAnimationUseAnchor(false), | 380 m_fakePageScaleAnimationUseAnchor(false), |
| 407 m_doingDragAndDrop(false), | 381 m_doingDragAndDrop(false), |
| 408 m_ignoreInputEvents(false), | 382 m_ignoreInputEvents(false), |
| 409 m_compositorDeviceScaleFactorOverride(0), | 383 m_compositorDeviceScaleFactorOverride(0), |
| 410 m_suppressNextKeypressEvent(false), | 384 m_suppressNextKeypressEvent(false), |
| 411 m_imeAcceptEvents(true), | 385 m_imeAcceptEvents(true), |
| 412 m_operationsAllowed(WebDragOperationNone), | |
| 413 m_dragOperation(WebDragOperationNone), | |
| 414 m_devToolsEmulator(nullptr), | 386 m_devToolsEmulator(nullptr), |
| 415 m_isTransparent(false), | 387 m_isTransparent(false), |
| 416 m_tabsToLinks(false), | 388 m_tabsToLinks(false), |
| 417 m_layerTreeView(nullptr), | 389 m_layerTreeView(nullptr), |
| 418 m_rootLayer(nullptr), | 390 m_rootLayer(nullptr), |
| 419 m_rootGraphicsLayer(nullptr), | 391 m_rootGraphicsLayer(nullptr), |
| 420 m_visualViewportContainerLayer(nullptr), | 392 m_visualViewportContainerLayer(nullptr), |
| 421 m_matchesHeuristicsForGpuRasterization(false), | 393 m_matchesHeuristicsForGpuRasterization(false), |
| 422 m_flingModifier(0), | 394 m_flingModifier(0), |
| 423 m_flingSourceDevice(WebGestureDeviceUninitialized), | 395 m_flingSourceDevice(WebGestureDeviceUninitialized), |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 | 447 |
| 476 WebViewImpl::~WebViewImpl() { | 448 WebViewImpl::~WebViewImpl() { |
| 477 DCHECK(!m_page); | 449 DCHECK(!m_page); |
| 478 | 450 |
| 479 // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline | 451 // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline |
| 480 // in destructor. m_linkHighlightsTimeline might be destroyed earlier | 452 // in destructor. m_linkHighlightsTimeline might be destroyed earlier |
| 481 // than m_linkHighlights. | 453 // than m_linkHighlights. |
| 482 DCHECK(m_linkHighlights.isEmpty()); | 454 DCHECK(m_linkHighlights.isEmpty()); |
| 483 } | 455 } |
| 484 | 456 |
| 457 WebViewImpl::UserGestureNotifier::UserGestureNotifier(WebViewImpl* view) |
| 458 : m_frame(view->mainFrameImpl()), |
| 459 m_userGestureObserved(&view->m_userGestureObserved) { |
| 460 DCHECK(m_userGestureObserved); |
| 461 } |
| 462 |
| 463 WebViewImpl::UserGestureNotifier::~UserGestureNotifier() { |
| 464 if (!*m_userGestureObserved && |
| 465 m_frame->frame()->document()->hasReceivedUserGesture()) { |
| 466 *m_userGestureObserved = true; |
| 467 if (m_frame && m_frame->autofillClient()) |
| 468 m_frame->autofillClient()->firstUserGestureObserved(); |
| 469 } |
| 470 } |
| 471 |
| 485 WebDevToolsAgentImpl* WebViewImpl::mainFrameDevToolsAgentImpl() { | 472 WebDevToolsAgentImpl* WebViewImpl::mainFrameDevToolsAgentImpl() { |
| 486 WebLocalFrameImpl* mainFrame = mainFrameImpl(); | 473 WebLocalFrameImpl* mainFrame = mainFrameImpl(); |
| 487 return mainFrame ? mainFrame->devToolsAgentImpl() : nullptr; | 474 return mainFrame ? mainFrame->devToolsAgentImpl() : nullptr; |
| 488 } | 475 } |
| 489 | 476 |
| 490 InspectorOverlay* WebViewImpl::inspectorOverlay() { | 477 InspectorOverlay* WebViewImpl::inspectorOverlay() { |
| 491 if (WebDevToolsAgentImpl* devtools = mainFrameDevToolsAgentImpl()) | 478 if (WebDevToolsAgentImpl* devtools = mainFrameDevToolsAgentImpl()) |
| 492 return devtools->overlay(); | 479 return devtools->overlay(); |
| 493 return nullptr; | 480 return nullptr; |
| 494 } | 481 } |
| (...skipping 1619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2114 | 2101 |
| 2115 WebInputEventResult WebViewImpl::handleInputEvent( | 2102 WebInputEventResult WebViewImpl::handleInputEvent( |
| 2116 const WebInputEvent& inputEvent) { | 2103 const WebInputEvent& inputEvent) { |
| 2117 // TODO(dcheng): The fact that this is getting called when there is no local | 2104 // TODO(dcheng): The fact that this is getting called when there is no local |
| 2118 // main frame is problematic and probably indicates a bug in the input event | 2105 // main frame is problematic and probably indicates a bug in the input event |
| 2119 // routing code. | 2106 // routing code. |
| 2120 if (!mainFrameImpl()) | 2107 if (!mainFrameImpl()) |
| 2121 return WebInputEventResult::NotHandled; | 2108 return WebInputEventResult::NotHandled; |
| 2122 | 2109 |
| 2123 WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient(); | 2110 WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient(); |
| 2124 UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved); | 2111 UserGestureNotifier notifier(this); |
| 2125 // On the first input event since page load, |notifier| instructs the | 2112 // On the first input event since page load, |notifier| instructs the |
| 2126 // autofill client to unblock values of password input fields of any forms | 2113 // autofill client to unblock values of password input fields of any forms |
| 2127 // on the page. There is a single input event, GestureTap, which can both | 2114 // on the page. There is a single input event, GestureTap, which can both |
| 2128 // be the first event after page load, and cause a form submission. In that | 2115 // be the first event after page load, and cause a form submission. In that |
| 2129 // case, the form submission happens before the autofill client is told | 2116 // case, the form submission happens before the autofill client is told |
| 2130 // to unblock the password values, and so the password values are not | 2117 // to unblock the password values, and so the password values are not |
| 2131 // submitted. To avoid that, GestureTap is handled explicitly: | 2118 // submitted. To avoid that, GestureTap is handled explicitly: |
| 2132 if (inputEvent.type == WebInputEvent::GestureTap && autofillClient) { | 2119 if (inputEvent.type == WebInputEvent::GestureTap && autofillClient) { |
| 2133 m_userGestureObserved = true; | 2120 m_userGestureObserved = true; |
| 2134 autofillClient->firstUserGestureObserved(); | 2121 autofillClient->firstUserGestureObserved(); |
| (...skipping 1372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3507 | 3494 |
| 3508 void WebViewImpl::dragSourceSystemDragEnded() { | 3495 void WebViewImpl::dragSourceSystemDragEnded() { |
| 3509 // It's possible for us to get this callback while not doing a drag if | 3496 // It's possible for us to get this callback while not doing a drag if |
| 3510 // it's from a previous page that got unloaded. | 3497 // it's from a previous page that got unloaded. |
| 3511 if (m_doingDragAndDrop) { | 3498 if (m_doingDragAndDrop) { |
| 3512 m_page->dragController().dragEnded(); | 3499 m_page->dragController().dragEnded(); |
| 3513 m_doingDragAndDrop = false; | 3500 m_doingDragAndDrop = false; |
| 3514 } | 3501 } |
| 3515 } | 3502 } |
| 3516 | 3503 |
| 3517 WebDragOperation WebViewImpl::dragTargetDragEnter( | |
| 3518 const WebDragData& webDragData, | |
| 3519 const WebPoint& pointInViewport, | |
| 3520 const WebPoint& screenPoint, | |
| 3521 WebDragOperationsMask operationsAllowed, | |
| 3522 int modifiers) { | |
| 3523 DCHECK(!m_currentDragData); | |
| 3524 | |
| 3525 m_currentDragData = DataObject::create(webDragData); | |
| 3526 m_operationsAllowed = operationsAllowed; | |
| 3527 | |
| 3528 return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragEnter, | |
| 3529 modifiers); | |
| 3530 } | |
| 3531 | |
| 3532 WebDragOperation WebViewImpl::dragTargetDragOver( | |
| 3533 const WebPoint& pointInViewport, | |
| 3534 const WebPoint& screenPoint, | |
| 3535 WebDragOperationsMask operationsAllowed, | |
| 3536 int modifiers) { | |
| 3537 m_operationsAllowed = operationsAllowed; | |
| 3538 | |
| 3539 return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragOver, | |
| 3540 modifiers); | |
| 3541 } | |
| 3542 | |
| 3543 void WebViewImpl::dragTargetDragLeave() { | |
| 3544 DCHECK(m_currentDragData); | |
| 3545 | |
| 3546 DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(), | |
| 3547 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3548 | |
| 3549 m_page->dragController().dragExited(&dragData); | |
| 3550 | |
| 3551 // FIXME: why is the drag scroll timer not stopped here? | |
| 3552 | |
| 3553 m_dragOperation = WebDragOperationNone; | |
| 3554 m_currentDragData = nullptr; | |
| 3555 } | |
| 3556 | |
| 3557 void WebViewImpl::dragTargetDrop(const WebDragData& webDragData, | |
| 3558 const WebPoint& pointInViewport, | |
| 3559 const WebPoint& screenPoint, | |
| 3560 int modifiers) { | |
| 3561 WebPoint pointInRootFrame( | |
| 3562 page()->frameHost().visualViewport().viewportToRootFrame( | |
| 3563 pointInViewport)); | |
| 3564 | |
| 3565 DCHECK(m_currentDragData); | |
| 3566 m_currentDragData = DataObject::create(webDragData); | |
| 3567 UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved); | |
| 3568 | |
| 3569 // If this webview transitions from the "drop accepting" state to the "not | |
| 3570 // accepting" state, then our IPC message reply indicating that may be in- | |
| 3571 // flight, or else delayed by javascript processing in this webview. If a | |
| 3572 // drop happens before our IPC reply has reached the browser process, then | |
| 3573 // the browser forwards the drop to this webview. So only allow a drop to | |
| 3574 // proceed if our webview m_dragOperation state is not DragOperationNone. | |
| 3575 | |
| 3576 if (m_dragOperation == | |
| 3577 WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop. | |
| 3578 dragTargetDragLeave(); | |
| 3579 return; | |
| 3580 } | |
| 3581 | |
| 3582 m_currentDragData->setModifiers(modifiers); | |
| 3583 DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, | |
| 3584 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3585 | |
| 3586 m_page->dragController().performDrag(&dragData); | |
| 3587 | |
| 3588 m_dragOperation = WebDragOperationNone; | |
| 3589 m_currentDragData = nullptr; | |
| 3590 } | |
| 3591 | |
| 3592 void WebViewImpl::spellingMarkers(WebVector<uint32_t>* markers) { | 3504 void WebViewImpl::spellingMarkers(WebVector<uint32_t>* markers) { |
| 3593 Vector<uint32_t> result; | 3505 Vector<uint32_t> result; |
| 3594 for (Frame* frame = m_page->mainFrame(); frame; | 3506 for (Frame* frame = m_page->mainFrame(); frame; |
| 3595 frame = frame->tree().traverseNext()) { | 3507 frame = frame->tree().traverseNext()) { |
| 3596 if (!frame->isLocalFrame()) | 3508 if (!frame->isLocalFrame()) |
| 3597 continue; | 3509 continue; |
| 3598 const DocumentMarkerVector& documentMarkers = | 3510 const DocumentMarkerVector& documentMarkers = |
| 3599 toLocalFrame(frame)->document()->markers().markers(); | 3511 toLocalFrame(frame)->document()->markers().markers(); |
| 3600 for (size_t i = 0; i < documentMarkers.size(); ++i) | 3512 for (size_t i = 0; i < documentMarkers.size(); ++i) |
| 3601 result.append(documentMarkers[i]->hash()); | 3513 result.append(documentMarkers[i]->hash()); |
| 3602 } | 3514 } |
| 3603 markers->assign(result); | 3515 markers->assign(result); |
| 3604 } | 3516 } |
| 3605 | 3517 |
| 3606 void WebViewImpl::removeSpellingMarkersUnderWords( | 3518 void WebViewImpl::removeSpellingMarkersUnderWords( |
| 3607 const WebVector<WebString>& words) { | 3519 const WebVector<WebString>& words) { |
| 3608 Vector<String> convertedWords; | 3520 Vector<String> convertedWords; |
| 3609 convertedWords.append(words.data(), words.size()); | 3521 convertedWords.append(words.data(), words.size()); |
| 3610 | 3522 |
| 3611 for (Frame* frame = m_page->mainFrame(); frame; | 3523 for (Frame* frame = m_page->mainFrame(); frame; |
| 3612 frame = frame->tree().traverseNext()) { | 3524 frame = frame->tree().traverseNext()) { |
| 3613 if (frame->isLocalFrame()) | 3525 if (frame->isLocalFrame()) |
| 3614 toLocalFrame(frame)->removeSpellingMarkersUnderWords(convertedWords); | 3526 toLocalFrame(frame)->removeSpellingMarkersUnderWords(convertedWords); |
| 3615 } | 3527 } |
| 3616 } | 3528 } |
| 3617 | 3529 |
| 3618 WebDragOperation WebViewImpl::dragTargetDragEnterOrOver( | |
| 3619 const WebPoint& pointInViewport, | |
| 3620 const WebPoint& screenPoint, | |
| 3621 DragAction dragAction, | |
| 3622 int modifiers) { | |
| 3623 DCHECK(m_currentDragData); | |
| 3624 | |
| 3625 WebPoint pointInRootFrame( | |
| 3626 page()->frameHost().visualViewport().viewportToRootFrame( | |
| 3627 pointInViewport)); | |
| 3628 | |
| 3629 m_currentDragData->setModifiers(modifiers); | |
| 3630 DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, | |
| 3631 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3632 | |
| 3633 DragSession dragSession; | |
| 3634 dragSession = m_page->dragController().dragEnteredOrUpdated(&dragData); | |
| 3635 | |
| 3636 DragOperation dropEffect = dragSession.operation; | |
| 3637 | |
| 3638 // Mask the drop effect operation against the drag source's allowed | |
| 3639 // operations. | |
| 3640 if (!(dropEffect & dragData.draggingSourceOperationMask())) | |
| 3641 dropEffect = DragOperationNone; | |
| 3642 | |
| 3643 m_dragOperation = static_cast<WebDragOperation>(dropEffect); | |
| 3644 | |
| 3645 return m_dragOperation; | |
| 3646 } | |
| 3647 | |
| 3648 void WebViewImpl::sendResizeEventAndRepaint() { | 3530 void WebViewImpl::sendResizeEventAndRepaint() { |
| 3649 // FIXME: This is wrong. The FrameView is responsible sending a resizeEvent | 3531 // FIXME: This is wrong. The FrameView is responsible sending a resizeEvent |
| 3650 // as part of layout. Layout is also responsible for sending invalidations | 3532 // as part of layout. Layout is also responsible for sending invalidations |
| 3651 // to the embedder. This method and all callers may be wrong. -- eseidel. | 3533 // to the embedder. This method and all callers may be wrong. -- eseidel. |
| 3652 if (mainFrameImpl()->frameView()) { | 3534 if (mainFrameImpl()->frameView()) { |
| 3653 // Enqueues the resize event. | 3535 // Enqueues the resize event. |
| 3654 mainFrameImpl()->frame()->document()->enqueueResizeEvent(); | 3536 mainFrameImpl()->frame()->document()->enqueueResizeEvent(); |
| 3655 } | 3537 } |
| 3656 | 3538 |
| 3657 if (m_client) { | 3539 if (m_client) { |
| (...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4440 if (focusedFrame->localFrameRoot() != mainFrameImpl()->frame()) | 4322 if (focusedFrame->localFrameRoot() != mainFrameImpl()->frame()) |
| 4441 return nullptr; | 4323 return nullptr; |
| 4442 return focusedFrame; | 4324 return focusedFrame; |
| 4443 } | 4325 } |
| 4444 | 4326 |
| 4445 LocalFrame* WebViewImpl::focusedLocalFrameAvailableForIme() const { | 4327 LocalFrame* WebViewImpl::focusedLocalFrameAvailableForIme() const { |
| 4446 return m_imeAcceptEvents ? focusedLocalFrameInWidget() : nullptr; | 4328 return m_imeAcceptEvents ? focusedLocalFrameInWidget() : nullptr; |
| 4447 } | 4329 } |
| 4448 | 4330 |
| 4449 } // namespace blink | 4331 } // namespace blink |
| OLD | NEW |