| 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 223 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 // invocations. | 234 // invocations. |
| 235 static Vector<std::unique_ptr<ScopedPageLoadDeferrer>>& | 235 static Vector<std::unique_ptr<ScopedPageLoadDeferrer>>& |
| 236 pageLoadDeferrerStack() { | 236 pageLoadDeferrerStack() { |
| 237 DEFINE_STATIC_LOCAL(Vector<std::unique_ptr<ScopedPageLoadDeferrer>>, | 237 DEFINE_STATIC_LOCAL(Vector<std::unique_ptr<ScopedPageLoadDeferrer>>, |
| 238 deferrerStack, ()); | 238 deferrerStack, ()); |
| 239 return deferrerStack; | 239 return deferrerStack; |
| 240 } | 240 } |
| 241 | 241 |
| 242 // Ensure that the WebDragOperation enum values stay in sync with the original | 242 // Ensure that the WebDragOperation enum values stay in sync with the original |
| 243 // DragOperation constants. | 243 // DragOperation constants. |
| 244 // TODO(paulmeyer): Move this into WebFrameWidgetBase once all drag-and-drop |
| 245 // functions are out of WebViewImpl. See crbug.com/647249. |
| 244 #define STATIC_ASSERT_ENUM(a, b) \ | 246 #define STATIC_ASSERT_ENUM(a, b) \ |
| 245 static_assert(static_cast<int>(a) == static_cast<int>(b), \ | 247 static_assert(static_cast<int>(a) == static_cast<int>(b), \ |
| 246 "mismatching enum : " #a) | 248 "mismatching enum : " #a) |
| 247 STATIC_ASSERT_ENUM(DragOperationNone, WebDragOperationNone); | 249 STATIC_ASSERT_ENUM(DragOperationNone, WebDragOperationNone); |
| 248 STATIC_ASSERT_ENUM(DragOperationCopy, WebDragOperationCopy); | 250 STATIC_ASSERT_ENUM(DragOperationCopy, WebDragOperationCopy); |
| 249 STATIC_ASSERT_ENUM(DragOperationLink, WebDragOperationLink); | 251 STATIC_ASSERT_ENUM(DragOperationLink, WebDragOperationLink); |
| 250 STATIC_ASSERT_ENUM(DragOperationGeneric, WebDragOperationGeneric); | 252 STATIC_ASSERT_ENUM(DragOperationGeneric, WebDragOperationGeneric); |
| 251 STATIC_ASSERT_ENUM(DragOperationPrivate, WebDragOperationPrivate); | 253 STATIC_ASSERT_ENUM(DragOperationPrivate, WebDragOperationPrivate); |
| 252 STATIC_ASSERT_ENUM(DragOperationMove, WebDragOperationMove); | 254 STATIC_ASSERT_ENUM(DragOperationMove, WebDragOperationMove); |
| 253 STATIC_ASSERT_ENUM(DragOperationDelete, WebDragOperationDelete); | 255 STATIC_ASSERT_ENUM(DragOperationDelete, WebDragOperationDelete); |
| 254 STATIC_ASSERT_ENUM(DragOperationEvery, WebDragOperationEvery); | 256 STATIC_ASSERT_ENUM(DragOperationEvery, WebDragOperationEvery); |
| 255 | 257 |
| 256 static bool shouldUseExternalPopupMenus = false; | 258 static bool shouldUseExternalPopupMenus = false; |
| 257 | 259 |
| 258 namespace { | 260 namespace { |
| 259 | 261 |
| 260 class UserGestureNotifier { | |
| 261 public: | |
| 262 // If a UserGestureIndicator is created for a user gesture since the last | |
| 263 // page load and *userGestureObserved is false, the UserGestureNotifier | |
| 264 // will notify the client and set *userGestureObserved to true. | |
| 265 UserGestureNotifier(WebLocalFrameImpl*, bool* userGestureObserved); | |
| 266 ~UserGestureNotifier(); | |
| 267 | |
| 268 private: | |
| 269 Persistent<WebLocalFrameImpl> m_frame; | |
| 270 bool* const m_userGestureObserved; | |
| 271 }; | |
| 272 | |
| 273 UserGestureNotifier::UserGestureNotifier(WebLocalFrameImpl* frame, | |
| 274 bool* userGestureObserved) | |
| 275 : m_frame(frame), m_userGestureObserved(userGestureObserved) { | |
| 276 DCHECK(m_userGestureObserved); | |
| 277 } | |
| 278 | |
| 279 UserGestureNotifier::~UserGestureNotifier() { | |
| 280 if (!*m_userGestureObserved && | |
| 281 m_frame->frame()->document()->hasReceivedUserGesture()) { | |
| 282 *m_userGestureObserved = true; | |
| 283 if (m_frame && m_frame->autofillClient()) | |
| 284 m_frame->autofillClient()->firstUserGestureObserved(); | |
| 285 } | |
| 286 } | |
| 287 | |
| 288 class EmptyEventListener final : public EventListener { | 262 class EmptyEventListener final : public EventListener { |
| 289 public: | 263 public: |
| 290 static EmptyEventListener* create() { return new EmptyEventListener(); } | 264 static EmptyEventListener* create() { return new EmptyEventListener(); } |
| 291 | 265 |
| 292 bool operator==(const EventListener& other) const override { | 266 bool operator==(const EventListener& other) const override { |
| 293 return this == &other; | 267 return this == &other; |
| 294 } | 268 } |
| 295 | 269 |
| 296 private: | 270 private: |
| 297 EmptyEventListener() : EventListener(CPPEventListenerType) {} | 271 EmptyEventListener() : EventListener(CPPEventListenerType) {} |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 m_doubleTapZoomPageScaleFactor(0), | 377 m_doubleTapZoomPageScaleFactor(0), |
| 404 m_doubleTapZoomPending(false), | 378 m_doubleTapZoomPending(false), |
| 405 m_enableFakePageScaleAnimationForTesting(false), | 379 m_enableFakePageScaleAnimationForTesting(false), |
| 406 m_fakePageScaleAnimationPageScaleFactor(0), | 380 m_fakePageScaleAnimationPageScaleFactor(0), |
| 407 m_fakePageScaleAnimationUseAnchor(false), | 381 m_fakePageScaleAnimationUseAnchor(false), |
| 408 m_doingDragAndDrop(false), | 382 m_doingDragAndDrop(false), |
| 409 m_ignoreInputEvents(false), | 383 m_ignoreInputEvents(false), |
| 410 m_compositorDeviceScaleFactorOverride(0), | 384 m_compositorDeviceScaleFactorOverride(0), |
| 411 m_suppressNextKeypressEvent(false), | 385 m_suppressNextKeypressEvent(false), |
| 412 m_imeAcceptEvents(true), | 386 m_imeAcceptEvents(true), |
| 413 m_operationsAllowed(WebDragOperationNone), | |
| 414 m_dragOperation(WebDragOperationNone), | |
| 415 m_devToolsEmulator(nullptr), | 387 m_devToolsEmulator(nullptr), |
| 416 m_isTransparent(false), | 388 m_isTransparent(false), |
| 417 m_tabsToLinks(false), | 389 m_tabsToLinks(false), |
| 418 m_layerTreeView(nullptr), | 390 m_layerTreeView(nullptr), |
| 419 m_rootLayer(nullptr), | 391 m_rootLayer(nullptr), |
| 420 m_rootGraphicsLayer(nullptr), | 392 m_rootGraphicsLayer(nullptr), |
| 421 m_visualViewportContainerLayer(nullptr), | 393 m_visualViewportContainerLayer(nullptr), |
| 422 m_matchesHeuristicsForGpuRasterization(false), | 394 m_matchesHeuristicsForGpuRasterization(false), |
| 423 m_flingModifier(0), | 395 m_flingModifier(0), |
| 424 m_flingSourceDevice(WebGestureDeviceUninitialized), | 396 m_flingSourceDevice(WebGestureDeviceUninitialized), |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 | 448 |
| 477 WebViewImpl::~WebViewImpl() { | 449 WebViewImpl::~WebViewImpl() { |
| 478 DCHECK(!m_page); | 450 DCHECK(!m_page); |
| 479 | 451 |
| 480 // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline | 452 // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline |
| 481 // in destructor. m_linkHighlightsTimeline might be destroyed earlier | 453 // in destructor. m_linkHighlightsTimeline might be destroyed earlier |
| 482 // than m_linkHighlights. | 454 // than m_linkHighlights. |
| 483 DCHECK(m_linkHighlights.isEmpty()); | 455 DCHECK(m_linkHighlights.isEmpty()); |
| 484 } | 456 } |
| 485 | 457 |
| 458 WebViewImpl::UserGestureNotifier::UserGestureNotifier(WebViewImpl* view) |
| 459 : m_frame(view->mainFrameImpl()), |
| 460 m_userGestureObserved(&view->m_userGestureObserved) { |
| 461 DCHECK(m_userGestureObserved); |
| 462 } |
| 463 |
| 464 WebViewImpl::UserGestureNotifier::~UserGestureNotifier() { |
| 465 if (!*m_userGestureObserved && |
| 466 m_frame->frame()->document()->hasReceivedUserGesture()) { |
| 467 *m_userGestureObserved = true; |
| 468 if (m_frame && m_frame->autofillClient()) |
| 469 m_frame->autofillClient()->firstUserGestureObserved(); |
| 470 } |
| 471 } |
| 472 |
| 486 WebDevToolsAgentImpl* WebViewImpl::mainFrameDevToolsAgentImpl() { | 473 WebDevToolsAgentImpl* WebViewImpl::mainFrameDevToolsAgentImpl() { |
| 487 WebLocalFrameImpl* mainFrame = mainFrameImpl(); | 474 WebLocalFrameImpl* mainFrame = mainFrameImpl(); |
| 488 return mainFrame ? mainFrame->devToolsAgentImpl() : nullptr; | 475 return mainFrame ? mainFrame->devToolsAgentImpl() : nullptr; |
| 489 } | 476 } |
| 490 | 477 |
| 491 InspectorOverlay* WebViewImpl::inspectorOverlay() { | 478 InspectorOverlay* WebViewImpl::inspectorOverlay() { |
| 492 if (WebDevToolsAgentImpl* devtools = mainFrameDevToolsAgentImpl()) | 479 if (WebDevToolsAgentImpl* devtools = mainFrameDevToolsAgentImpl()) |
| 493 return devtools->overlay(); | 480 return devtools->overlay(); |
| 494 return nullptr; | 481 return nullptr; |
| 495 } | 482 } |
| (...skipping 1635 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2131 | 2118 |
| 2132 WebInputEventResult WebViewImpl::handleInputEvent( | 2119 WebInputEventResult WebViewImpl::handleInputEvent( |
| 2133 const WebInputEvent& inputEvent) { | 2120 const WebInputEvent& inputEvent) { |
| 2134 // TODO(dcheng): The fact that this is getting called when there is no local | 2121 // TODO(dcheng): The fact that this is getting called when there is no local |
| 2135 // main frame is problematic and probably indicates a bug in the input event | 2122 // main frame is problematic and probably indicates a bug in the input event |
| 2136 // routing code. | 2123 // routing code. |
| 2137 if (!mainFrameImpl()) | 2124 if (!mainFrameImpl()) |
| 2138 return WebInputEventResult::NotHandled; | 2125 return WebInputEventResult::NotHandled; |
| 2139 | 2126 |
| 2140 WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient(); | 2127 WebAutofillClient* autofillClient = mainFrameImpl()->autofillClient(); |
| 2141 UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved); | 2128 UserGestureNotifier notifier(this); |
| 2142 // On the first input event since page load, |notifier| instructs the | 2129 // On the first input event since page load, |notifier| instructs the |
| 2143 // autofill client to unblock values of password input fields of any forms | 2130 // autofill client to unblock values of password input fields of any forms |
| 2144 // on the page. There is a single input event, GestureTap, which can both | 2131 // on the page. There is a single input event, GestureTap, which can both |
| 2145 // be the first event after page load, and cause a form submission. In that | 2132 // be the first event after page load, and cause a form submission. In that |
| 2146 // case, the form submission happens before the autofill client is told | 2133 // case, the form submission happens before the autofill client is told |
| 2147 // to unblock the password values, and so the password values are not | 2134 // to unblock the password values, and so the password values are not |
| 2148 // submitted. To avoid that, GestureTap is handled explicitly: | 2135 // submitted. To avoid that, GestureTap is handled explicitly: |
| 2149 if (inputEvent.type == WebInputEvent::GestureTap && autofillClient) { | 2136 if (inputEvent.type == WebInputEvent::GestureTap && autofillClient) { |
| 2150 m_userGestureObserved = true; | 2137 m_userGestureObserved = true; |
| 2151 autofillClient->firstUserGestureObserved(); | 2138 autofillClient->firstUserGestureObserved(); |
| (...skipping 1278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3430 | 3417 |
| 3431 void WebViewImpl::dragSourceSystemDragEnded() { | 3418 void WebViewImpl::dragSourceSystemDragEnded() { |
| 3432 // It's possible for us to get this callback while not doing a drag if | 3419 // It's possible for us to get this callback while not doing a drag if |
| 3433 // it's from a previous page that got unloaded. | 3420 // it's from a previous page that got unloaded. |
| 3434 if (m_doingDragAndDrop) { | 3421 if (m_doingDragAndDrop) { |
| 3435 m_page->dragController().dragEnded(); | 3422 m_page->dragController().dragEnded(); |
| 3436 m_doingDragAndDrop = false; | 3423 m_doingDragAndDrop = false; |
| 3437 } | 3424 } |
| 3438 } | 3425 } |
| 3439 | 3426 |
| 3440 WebDragOperation WebViewImpl::dragTargetDragEnter( | |
| 3441 const WebDragData& webDragData, | |
| 3442 const WebPoint& pointInViewport, | |
| 3443 const WebPoint& screenPoint, | |
| 3444 WebDragOperationsMask operationsAllowed, | |
| 3445 int modifiers) { | |
| 3446 DCHECK(!m_currentDragData); | |
| 3447 | |
| 3448 m_currentDragData = DataObject::create(webDragData); | |
| 3449 m_operationsAllowed = operationsAllowed; | |
| 3450 | |
| 3451 return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragEnter, | |
| 3452 modifiers); | |
| 3453 } | |
| 3454 | |
| 3455 WebDragOperation WebViewImpl::dragTargetDragOver( | |
| 3456 const WebPoint& pointInViewport, | |
| 3457 const WebPoint& screenPoint, | |
| 3458 WebDragOperationsMask operationsAllowed, | |
| 3459 int modifiers) { | |
| 3460 m_operationsAllowed = operationsAllowed; | |
| 3461 | |
| 3462 return dragTargetDragEnterOrOver(pointInViewport, screenPoint, DragOver, | |
| 3463 modifiers); | |
| 3464 } | |
| 3465 | |
| 3466 void WebViewImpl::dragTargetDragLeave() { | |
| 3467 DCHECK(m_currentDragData); | |
| 3468 | |
| 3469 DragData dragData(m_currentDragData.get(), IntPoint(), IntPoint(), | |
| 3470 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3471 | |
| 3472 m_page->dragController().dragExited(&dragData); | |
| 3473 | |
| 3474 // FIXME: why is the drag scroll timer not stopped here? | |
| 3475 | |
| 3476 m_dragOperation = WebDragOperationNone; | |
| 3477 m_currentDragData = nullptr; | |
| 3478 } | |
| 3479 | |
| 3480 void WebViewImpl::dragTargetDrop(const WebDragData& webDragData, | |
| 3481 const WebPoint& pointInViewport, | |
| 3482 const WebPoint& screenPoint, | |
| 3483 int modifiers) { | |
| 3484 WebPoint pointInRootFrame( | |
| 3485 page()->frameHost().visualViewport().viewportToRootFrame( | |
| 3486 pointInViewport)); | |
| 3487 | |
| 3488 DCHECK(m_currentDragData); | |
| 3489 m_currentDragData = DataObject::create(webDragData); | |
| 3490 UserGestureNotifier notifier(mainFrameImpl(), &m_userGestureObserved); | |
| 3491 | |
| 3492 // If this webview transitions from the "drop accepting" state to the "not | |
| 3493 // accepting" state, then our IPC message reply indicating that may be in- | |
| 3494 // flight, or else delayed by javascript processing in this webview. If a | |
| 3495 // drop happens before our IPC reply has reached the browser process, then | |
| 3496 // the browser forwards the drop to this webview. So only allow a drop to | |
| 3497 // proceed if our webview m_dragOperation state is not DragOperationNone. | |
| 3498 | |
| 3499 if (m_dragOperation == | |
| 3500 WebDragOperationNone) { // IPC RACE CONDITION: do not allow this drop. | |
| 3501 dragTargetDragLeave(); | |
| 3502 return; | |
| 3503 } | |
| 3504 | |
| 3505 m_currentDragData->setModifiers(modifiers); | |
| 3506 DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, | |
| 3507 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3508 | |
| 3509 m_page->dragController().performDrag(&dragData); | |
| 3510 | |
| 3511 m_dragOperation = WebDragOperationNone; | |
| 3512 m_currentDragData = nullptr; | |
| 3513 } | |
| 3514 | |
| 3515 void WebViewImpl::spellingMarkers(WebVector<uint32_t>* markers) { | 3427 void WebViewImpl::spellingMarkers(WebVector<uint32_t>* markers) { |
| 3516 Vector<uint32_t> result; | 3428 Vector<uint32_t> result; |
| 3517 for (Frame* frame = m_page->mainFrame(); frame; | 3429 for (Frame* frame = m_page->mainFrame(); frame; |
| 3518 frame = frame->tree().traverseNext()) { | 3430 frame = frame->tree().traverseNext()) { |
| 3519 if (!frame->isLocalFrame()) | 3431 if (!frame->isLocalFrame()) |
| 3520 continue; | 3432 continue; |
| 3521 const DocumentMarkerVector& documentMarkers = | 3433 const DocumentMarkerVector& documentMarkers = |
| 3522 toLocalFrame(frame)->document()->markers().markers(); | 3434 toLocalFrame(frame)->document()->markers().markers(); |
| 3523 for (size_t i = 0; i < documentMarkers.size(); ++i) | 3435 for (size_t i = 0; i < documentMarkers.size(); ++i) |
| 3524 result.append(documentMarkers[i]->hash()); | 3436 result.append(documentMarkers[i]->hash()); |
| 3525 } | 3437 } |
| 3526 markers->assign(result); | 3438 markers->assign(result); |
| 3527 } | 3439 } |
| 3528 | 3440 |
| 3529 void WebViewImpl::removeSpellingMarkersUnderWords( | 3441 void WebViewImpl::removeSpellingMarkersUnderWords( |
| 3530 const WebVector<WebString>& words) { | 3442 const WebVector<WebString>& words) { |
| 3531 Vector<String> convertedWords; | 3443 Vector<String> convertedWords; |
| 3532 convertedWords.append(words.data(), words.size()); | 3444 convertedWords.append(words.data(), words.size()); |
| 3533 | 3445 |
| 3534 for (Frame* frame = m_page->mainFrame(); frame; | 3446 for (Frame* frame = m_page->mainFrame(); frame; |
| 3535 frame = frame->tree().traverseNext()) { | 3447 frame = frame->tree().traverseNext()) { |
| 3536 if (frame->isLocalFrame()) | 3448 if (frame->isLocalFrame()) |
| 3537 toLocalFrame(frame)->removeSpellingMarkersUnderWords(convertedWords); | 3449 toLocalFrame(frame)->removeSpellingMarkersUnderWords(convertedWords); |
| 3538 } | 3450 } |
| 3539 } | 3451 } |
| 3540 | 3452 |
| 3541 WebDragOperation WebViewImpl::dragTargetDragEnterOrOver( | |
| 3542 const WebPoint& pointInViewport, | |
| 3543 const WebPoint& screenPoint, | |
| 3544 DragAction dragAction, | |
| 3545 int modifiers) { | |
| 3546 DCHECK(m_currentDragData); | |
| 3547 | |
| 3548 WebPoint pointInRootFrame( | |
| 3549 page()->frameHost().visualViewport().viewportToRootFrame( | |
| 3550 pointInViewport)); | |
| 3551 | |
| 3552 m_currentDragData->setModifiers(modifiers); | |
| 3553 DragData dragData(m_currentDragData.get(), pointInRootFrame, screenPoint, | |
| 3554 static_cast<DragOperation>(m_operationsAllowed)); | |
| 3555 | |
| 3556 DragSession dragSession; | |
| 3557 dragSession = m_page->dragController().dragEnteredOrUpdated(&dragData); | |
| 3558 | |
| 3559 DragOperation dropEffect = dragSession.operation; | |
| 3560 | |
| 3561 // Mask the drop effect operation against the drag source's allowed | |
| 3562 // operations. | |
| 3563 if (!(dropEffect & dragData.draggingSourceOperationMask())) | |
| 3564 dropEffect = DragOperationNone; | |
| 3565 | |
| 3566 m_dragOperation = static_cast<WebDragOperation>(dropEffect); | |
| 3567 | |
| 3568 return m_dragOperation; | |
| 3569 } | |
| 3570 | |
| 3571 void WebViewImpl::sendResizeEventAndRepaint() { | 3453 void WebViewImpl::sendResizeEventAndRepaint() { |
| 3572 // FIXME: This is wrong. The FrameView is responsible sending a resizeEvent | 3454 // FIXME: This is wrong. The FrameView is responsible sending a resizeEvent |
| 3573 // as part of layout. Layout is also responsible for sending invalidations | 3455 // as part of layout. Layout is also responsible for sending invalidations |
| 3574 // to the embedder. This method and all callers may be wrong. -- eseidel. | 3456 // to the embedder. This method and all callers may be wrong. -- eseidel. |
| 3575 if (mainFrameImpl()->frameView()) { | 3457 if (mainFrameImpl()->frameView()) { |
| 3576 // Enqueues the resize event. | 3458 // Enqueues the resize event. |
| 3577 mainFrameImpl()->frame()->document()->enqueueResizeEvent(); | 3459 mainFrameImpl()->frame()->document()->enqueueResizeEvent(); |
| 3578 } | 3460 } |
| 3579 | 3461 |
| 3580 if (m_client) { | 3462 if (m_client) { |
| (...skipping 785 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4366 if (focusedFrame->localFrameRoot() != mainFrameImpl()->frame()) | 4248 if (focusedFrame->localFrameRoot() != mainFrameImpl()->frame()) |
| 4367 return nullptr; | 4249 return nullptr; |
| 4368 return focusedFrame; | 4250 return focusedFrame; |
| 4369 } | 4251 } |
| 4370 | 4252 |
| 4371 LocalFrame* WebViewImpl::focusedLocalFrameAvailableForIme() const { | 4253 LocalFrame* WebViewImpl::focusedLocalFrameAvailableForIme() const { |
| 4372 return m_imeAcceptEvents ? focusedLocalFrameInWidget() : nullptr; | 4254 return m_imeAcceptEvents ? focusedLocalFrameInWidget() : nullptr; |
| 4373 } | 4255 } |
| 4374 | 4256 |
| 4375 } // namespace blink | 4257 } // namespace blink |
| OLD | NEW |