Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(183)

Side by Side Diff: views/accessibility/native_view_accessibility_win.cc

Issue 8391010: Improve omnibox accessibility on Windows. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/accessibility/native_view_accessibility_win.h ('k') | views/view.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <atlbase.h>
6 #include <atlcom.h>
7
5 #include "views/accessibility/native_view_accessibility_win.h" 8 #include "views/accessibility/native_view_accessibility_win.h"
6 9
10 #include "third_party/iaccessible2/ia2_api_all.h"
7 #include "ui/base/accessibility/accessible_view_state.h" 11 #include "ui/base/accessibility/accessible_view_state.h"
8 #include "ui/base/view_prop.h" 12 #include "ui/base/view_prop.h"
9 #include "views/widget/native_widget_win.h" 13 #include "views/widget/native_widget_win.h"
10 #include "views/widget/widget.h" 14 #include "views/widget/widget.h"
11 15
12 using ui::AccessibilityTypes; 16 using ui::AccessibilityTypes;
13 17
14 namespace views { 18 // static
15 const char kViewsNativeHostPropForAccessibility[] = 19 long NativeViewAccessibilityWin::next_unique_id_ = 1;
16 "Views_NativeViewHostHWNDForAccessibility";
17 }
18 20
19 // static 21 // static
20 scoped_refptr<NativeViewAccessibilityWin> NativeViewAccessibilityWin::Create( 22 scoped_refptr<NativeViewAccessibilityWin> NativeViewAccessibilityWin::Create(
21 views::View* view) { 23 views::View* view) {
22 CComObject<NativeViewAccessibilityWin>* instance = NULL; 24 CComObject<NativeViewAccessibilityWin>* instance = NULL;
23 HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance( 25 HRESULT hr = CComObject<NativeViewAccessibilityWin>::CreateInstance(
24 &instance); 26 &instance);
25 DCHECK(SUCCEEDED(hr)); 27 DCHECK(SUCCEEDED(hr));
26 instance->set_view(view); 28 instance->set_view(view);
27 return scoped_refptr<NativeViewAccessibilityWin>(instance); 29 return scoped_refptr<NativeViewAccessibilityWin>(instance);
28 } 30 }
29 31
30 NativeViewAccessibilityWin::NativeViewAccessibilityWin() : view_(NULL) { 32 NativeViewAccessibilityWin::NativeViewAccessibilityWin()
33 : view_(NULL),
34 unique_id_(next_unique_id_++) {
31 } 35 }
32 36
33 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() { 37 NativeViewAccessibilityWin::~NativeViewAccessibilityWin() {
34 } 38 }
35 39
36 // TODO(ctguil): Handle case where child View is not contained by parent. 40 // TODO(ctguil): Handle case where child View is not contained by parent.
37 STDMETHODIMP NativeViewAccessibilityWin::accHitTest( 41 STDMETHODIMP NativeViewAccessibilityWin::accHitTest(
38 LONG x_left, LONG y_top, VARIANT* child) { 42 LONG x_left, LONG y_top, VARIANT* child) {
39 if (!child) 43 if (!child)
40 return E_INVALIDARG; 44 return E_INVALIDARG;
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after
410 414
411 ui::AccessibleViewState state; 415 ui::AccessibleViewState state;
412 view_->GetAccessibleState(&state); 416 view_->GetAccessibleState(&state);
413 role->vt = VT_I4; 417 role->vt = VT_I4;
414 role->lVal = MSAARole(state.role); 418 role->lVal = MSAARole(state.role);
415 return S_OK; 419 return S_OK;
416 } 420 }
417 421
418 STDMETHODIMP NativeViewAccessibilityWin::get_accState( 422 STDMETHODIMP NativeViewAccessibilityWin::get_accState(
419 VARIANT var_id, VARIANT* state) { 423 VARIANT var_id, VARIANT* state) {
424 // This returns MSAA states. See also the IAccessible2 interface
425 // get_states().
426
420 if (!IsValidId(var_id) || !state) 427 if (!IsValidId(var_id) || !state)
421 return E_INVALIDARG; 428 return E_INVALIDARG;
422 429
423 if (!view_) 430 if (!view_)
424 return E_FAIL; 431 return E_FAIL;
425 432
426 state->vt = VT_I4; 433 state->vt = VT_I4;
427 434
428 // Retrieve all currently applicable states of the parent. 435 // Retrieve all currently applicable states of the parent.
429 SetState(state, view_); 436 SetState(state, view_);
(...skipping 23 matching lines...) Expand all
453 *value = SysAllocString(temp_value.c_str()); 460 *value = SysAllocString(temp_value.c_str());
454 } else { 461 } else {
455 // If view has no value, fall back into the default implementation. 462 // If view has no value, fall back into the default implementation.
456 *value = NULL; 463 *value = NULL;
457 return E_NOTIMPL; 464 return E_NOTIMPL;
458 } 465 }
459 466
460 return S_OK; 467 return S_OK;
461 } 468 }
462 469
463 // Helper functions.
464
465 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const {
466 if (nav_dir == NAVDIR_RIGHT || nav_dir == NAVDIR_DOWN ||
467 nav_dir == NAVDIR_NEXT) {
468 return true;
469 }
470 return false;
471 }
472
473 bool NativeViewAccessibilityWin::IsValidNav(
474 int nav_dir, int start_id, int lower_bound, int upper_bound) const {
475 if (IsNavDirNext(nav_dir)) {
476 if ((start_id + 1) > upper_bound) {
477 return false;
478 }
479 } else {
480 if ((start_id - 1) <= lower_bound) {
481 return false;
482 }
483 }
484 return true;
485 }
486
487 bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const {
488 // View accessibility returns an IAccessible for each view so we only support
489 // the CHILDID_SELF id.
490 return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal);
491 }
492
493 void NativeViewAccessibilityWin::SetState(
494 VARIANT* msaa_state, views::View* view) {
495 // Ensure the output param is initialized to zero.
496 msaa_state->lVal = 0;
497
498 // Default state; all views can have accessibility focus.
499 msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE;
500
501 if (!view)
502 return;
503
504 if (!view->IsEnabled())
505 msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE;
506 if (!view->IsVisible())
507 msaa_state->lVal |= STATE_SYSTEM_INVISIBLE;
508 if (view->IsHotTracked())
509 msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED;
510 if (view->HasFocus())
511 msaa_state->lVal |= STATE_SYSTEM_FOCUSED;
512
513 // Add on any view-specific states.
514 ui::AccessibleViewState view_state;
515 view->GetAccessibleState(&view_state);
516 msaa_state->lVal |= MSAAState(view_state.state);
517 }
518
519 // IAccessible functions not supported. 470 // IAccessible functions not supported.
520 471
521 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) { 472 STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) {
522 if (selected) 473 if (selected)
523 selected->vt = VT_EMPTY; 474 selected->vt = VT_EMPTY;
524 return E_NOTIMPL; 475 return E_NOTIMPL;
525 } 476 }
526 477
527 STDMETHODIMP NativeViewAccessibilityWin::accSelect( 478 STDMETHODIMP NativeViewAccessibilityWin::accSelect(
528 LONG flagsSelect, VARIANT var_id) { 479 LONG flagsSelect, VARIANT var_id) {
(...skipping 23 matching lines...) Expand all
552 // Deprecated. 503 // Deprecated.
553 return E_NOTIMPL; 504 return E_NOTIMPL;
554 } 505 }
555 506
556 STDMETHODIMP NativeViewAccessibilityWin::put_accValue( 507 STDMETHODIMP NativeViewAccessibilityWin::put_accValue(
557 VARIANT var_id, BSTR put_val) { 508 VARIANT var_id, BSTR put_val) {
558 // Deprecated. 509 // Deprecated.
559 return E_NOTIMPL; 510 return E_NOTIMPL;
560 } 511 }
561 512
513 //
514 // IAccessible2
515 //
516
517 STDMETHODIMP NativeViewAccessibilityWin::role(LONG* role) {
518 if (!view_)
519 return E_FAIL;
520
521 if (!role)
522 return E_INVALIDARG;
523
524 ui::AccessibleViewState state;
525 view_->GetAccessibleState(&state);
526 *role = MSAARole(state.role);
527 return S_OK;
528 }
529
530 STDMETHODIMP NativeViewAccessibilityWin::get_states(AccessibleStates* states) {
531 // This returns IAccessible2 states, which supplement MSAA states.
532 // See also the MSAA interface get_accState.
533
534 if (!view_)
535 return E_FAIL;
536
537 if (!states)
538 return E_INVALIDARG;
539
540 ui::AccessibleViewState state;
541 view_->GetAccessibleState(&state);
542
543 // There are only a couple of states we need to support
544 // in IAccessible2. If any more are added, we may want to
545 // add a helper function like MSAAState.
546 *states = IA2_STATE_OPAQUE;
547 if (state.state & AccessibilityTypes::STATE_EDITABLE)
548 *states |= IA2_STATE_EDITABLE;
549
550 return S_OK;
551 }
552
553 STDMETHODIMP NativeViewAccessibilityWin::get_uniqueID(LONG* unique_id) {
554 if (!view_)
555 return E_FAIL;
556
557 if (!unique_id)
558 return E_INVALIDARG;
559
560 *unique_id = unique_id_;
561 return S_OK;
562 }
563
564 STDMETHODIMP NativeViewAccessibilityWin::get_windowHandle(HWND* window_handle) {
565 if (!view_)
566 return E_FAIL;
567
568 if (!window_handle)
569 return E_INVALIDARG;
570
571 *window_handle = view_->GetWidget()->GetNativeView();
572 return S_OK;
573 }
574
575 //
576 // IAccessibleText
577 //
578
579 STDMETHODIMP NativeViewAccessibilityWin::get_nCharacters(LONG* n_characters) {
580 if (!view_)
581 return E_FAIL;
582
583 if (!n_characters)
584 return E_INVALIDARG;
585
586 string16 text = TextForIAccessibleText();
587 *n_characters = static_cast<LONG>(text.size());
588 return S_OK;
589 }
590
591 STDMETHODIMP NativeViewAccessibilityWin::get_caretOffset(LONG* offset) {
592 if (!view_)
593 return E_FAIL;
594
595 if (!offset)
596 return E_INVALIDARG;
597
598 ui::AccessibleViewState state;
599 view_->GetAccessibleState(&state);
600 *offset = static_cast<LONG>(state.selection_end);
601 return S_OK;
602 }
603
604 STDMETHODIMP NativeViewAccessibilityWin::get_nSelections(LONG* n_selections) {
605 if (!view_)
606 return E_FAIL;
607
608 if (!n_selections)
609 return E_INVALIDARG;
610
611 ui::AccessibleViewState state;
612 view_->GetAccessibleState(&state);
613 if (state.selection_start != state.selection_end)
614 *n_selections = 1;
615 else
616 *n_selections = 0;
617 return S_OK;
618 }
619
620 STDMETHODIMP NativeViewAccessibilityWin::get_selection(LONG selection_index,
621 LONG* start_offset,
622 LONG* end_offset) {
623 if (!view_)
624 return E_FAIL;
625
626 if (!start_offset || !end_offset || selection_index != 0)
627 return E_INVALIDARG;
628
629 ui::AccessibleViewState state;
630 view_->GetAccessibleState(&state);
631 *start_offset = static_cast<LONG>(state.selection_start);
632 *end_offset = static_cast<LONG>(state.selection_end);
633 return S_OK;
634 }
635
636 STDMETHODIMP NativeViewAccessibilityWin::get_text(LONG start_offset,
637 LONG end_offset,
638 BSTR* text) {
639 if (!view_)
640 return E_FAIL;
641
642 ui::AccessibleViewState state;
643 view_->GetAccessibleState(&state);
644 string16 text_str = TextForIAccessibleText();
645 LONG len = static_cast<LONG>(text_str.size());
646
647 if (start_offset == IA2_TEXT_OFFSET_LENGTH) {
648 start_offset = len;
649 } else if (start_offset == IA2_TEXT_OFFSET_CARET) {
650 start_offset = static_cast<LONG>(state.selection_end);
651 }
652 if (end_offset == IA2_TEXT_OFFSET_LENGTH) {
653 end_offset = static_cast<LONG>(text_str.size());
654 } else if (end_offset == IA2_TEXT_OFFSET_CARET) {
655 end_offset = static_cast<LONG>(state.selection_end);
656 }
657
658 // The spec allows the arguments to be reversed.
659 if (start_offset > end_offset) {
660 LONG tmp = start_offset;
661 start_offset = end_offset;
662 end_offset = tmp;
663 }
664
665 // The spec does not allow the start or end offsets to be out or range;
666 // we must return an error if so.
667 if (start_offset < 0)
668 return E_INVALIDARG;
669 if (end_offset > len)
670 return E_INVALIDARG;
671
672 string16 substr = text_str.substr(start_offset, end_offset - start_offset);
673 if (substr.empty())
674 return S_FALSE;
675
676 *text = SysAllocString(substr.c_str());
677 DCHECK(*text);
678 return S_OK;
679 }
680
681 STDMETHODIMP NativeViewAccessibilityWin::get_offsetAtPoint(
682 LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) {
683 if (!view_)
684 return E_FAIL;
685
686 if (!offset)
687 return E_INVALIDARG;
688
689 // We don't support this method, but we have to return something
690 // rather than E_NOTIMPL or screen readers will complain.
691 *offset = 0;
692 return S_OK;
693 }
694
695 //
696 // IServiceProvider methods.
697 //
698
699 STDMETHODIMP NativeViewAccessibilityWin::QueryService(
700 REFGUID guidService, REFIID riid, void** object) {
701 if (!view_)
702 return E_FAIL;
703
704 if (guidService == IID_IAccessible ||
705 guidService == IID_IAccessible2 ||
706 guidService == IID_IAccessibleText) {
707 return QueryInterface(riid, object);
708 }
709
710 *object = NULL;
711 return E_FAIL;
712 }
713
714 //
715 // Static methods.
716 //
717
562 int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) { 718 int32 NativeViewAccessibilityWin::MSAAEvent(AccessibilityTypes::Event event) {
563 switch (event) { 719 switch (event) {
564 case AccessibilityTypes::EVENT_ALERT: 720 case AccessibilityTypes::EVENT_ALERT:
565 return EVENT_SYSTEM_ALERT; 721 return EVENT_SYSTEM_ALERT;
566 case AccessibilityTypes::EVENT_FOCUS: 722 case AccessibilityTypes::EVENT_FOCUS:
567 return EVENT_OBJECT_FOCUS; 723 return EVENT_OBJECT_FOCUS;
568 case AccessibilityTypes::EVENT_MENUSTART: 724 case AccessibilityTypes::EVENT_MENUSTART:
569 return EVENT_SYSTEM_MENUSTART; 725 return EVENT_SYSTEM_MENUSTART;
570 case AccessibilityTypes::EVENT_MENUEND: 726 case AccessibilityTypes::EVENT_MENUEND:
571 return EVENT_SYSTEM_MENUEND; 727 return EVENT_SYSTEM_MENUEND;
572 case AccessibilityTypes::EVENT_MENUPOPUPSTART: 728 case AccessibilityTypes::EVENT_MENUPOPUPSTART:
573 return EVENT_SYSTEM_MENUPOPUPSTART; 729 return EVENT_SYSTEM_MENUPOPUPSTART;
574 case AccessibilityTypes::EVENT_MENUPOPUPEND: 730 case AccessibilityTypes::EVENT_MENUPOPUPEND:
575 return EVENT_SYSTEM_MENUPOPUPEND; 731 return EVENT_SYSTEM_MENUPOPUPEND;
576 case AccessibilityTypes::EVENT_NAME_CHANGED: 732 case AccessibilityTypes::EVENT_NAME_CHANGED:
577 return EVENT_OBJECT_NAMECHANGE; 733 return EVENT_OBJECT_NAMECHANGE;
578 case AccessibilityTypes::EVENT_TEXT_CHANGED: 734 case AccessibilityTypes::EVENT_TEXT_CHANGED:
579 return EVENT_OBJECT_VALUECHANGE; 735 return EVENT_OBJECT_VALUECHANGE;
580 case AccessibilityTypes::EVENT_SELECTION_CHANGED: 736 case AccessibilityTypes::EVENT_SELECTION_CHANGED:
581 return EVENT_OBJECT_TEXTSELECTIONCHANGED; 737 return IA2_EVENT_TEXT_CARET_MOVED;
582 case AccessibilityTypes::EVENT_VALUE_CHANGED: 738 case AccessibilityTypes::EVENT_VALUE_CHANGED:
583 return EVENT_OBJECT_VALUECHANGE; 739 return EVENT_OBJECT_VALUECHANGE;
584 default: 740 default:
585 // Not supported or invalid event. 741 // Not supported or invalid event.
586 NOTREACHED(); 742 NOTREACHED();
587 return -1; 743 return -1;
588 } 744 }
589 } 745 }
590 746
591 int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) { 747 int32 NativeViewAccessibilityWin::MSAARole(AccessibilityTypes::Role role) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 case AccessibilityTypes::ROLE_WINDOW: 805 case AccessibilityTypes::ROLE_WINDOW:
650 return ROLE_SYSTEM_WINDOW; 806 return ROLE_SYSTEM_WINDOW;
651 case AccessibilityTypes::ROLE_CLIENT: 807 case AccessibilityTypes::ROLE_CLIENT:
652 default: 808 default:
653 // This is the default role for MSAA. 809 // This is the default role for MSAA.
654 return ROLE_SYSTEM_CLIENT; 810 return ROLE_SYSTEM_CLIENT;
655 } 811 }
656 } 812 }
657 813
658 int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) { 814 int32 NativeViewAccessibilityWin::MSAAState(AccessibilityTypes::State state) {
815 // This maps MSAA states for get_accState(). See also the IAccessible2
816 // interface get_states().
817
659 int32 msaa_state = 0; 818 int32 msaa_state = 0;
660 if (state & AccessibilityTypes::STATE_CHECKED) 819 if (state & AccessibilityTypes::STATE_CHECKED)
661 msaa_state |= STATE_SYSTEM_CHECKED; 820 msaa_state |= STATE_SYSTEM_CHECKED;
662 if (state & AccessibilityTypes::STATE_COLLAPSED) 821 if (state & AccessibilityTypes::STATE_COLLAPSED)
663 msaa_state |= STATE_SYSTEM_COLLAPSED; 822 msaa_state |= STATE_SYSTEM_COLLAPSED;
664 if (state & AccessibilityTypes::STATE_DEFAULT) 823 if (state & AccessibilityTypes::STATE_DEFAULT)
665 msaa_state |= STATE_SYSTEM_DEFAULT; 824 msaa_state |= STATE_SYSTEM_DEFAULT;
666 if (state & AccessibilityTypes::STATE_EXPANDED) 825 if (state & AccessibilityTypes::STATE_EXPANDED)
667 msaa_state |= STATE_SYSTEM_EXPANDED; 826 msaa_state |= STATE_SYSTEM_EXPANDED;
668 if (state & AccessibilityTypes::STATE_HASPOPUP) 827 if (state & AccessibilityTypes::STATE_HASPOPUP)
(...skipping 13 matching lines...) Expand all
682 if (state & AccessibilityTypes::STATE_READONLY) 841 if (state & AccessibilityTypes::STATE_READONLY)
683 msaa_state |= STATE_SYSTEM_READONLY; 842 msaa_state |= STATE_SYSTEM_READONLY;
684 if (state & AccessibilityTypes::STATE_SELECTED) 843 if (state & AccessibilityTypes::STATE_SELECTED)
685 msaa_state |= STATE_SYSTEM_SELECTED; 844 msaa_state |= STATE_SYSTEM_SELECTED;
686 if (state & AccessibilityTypes::STATE_FOCUSED) 845 if (state & AccessibilityTypes::STATE_FOCUSED)
687 msaa_state |= STATE_SYSTEM_FOCUSED; 846 msaa_state |= STATE_SYSTEM_FOCUSED;
688 if (state & AccessibilityTypes::STATE_UNAVAILABLE) 847 if (state & AccessibilityTypes::STATE_UNAVAILABLE)
689 msaa_state |= STATE_SYSTEM_UNAVAILABLE; 848 msaa_state |= STATE_SYSTEM_UNAVAILABLE;
690 return msaa_state; 849 return msaa_state;
691 } 850 }
851
852 //
853 // Private methods.
854 //
855
856 bool NativeViewAccessibilityWin::IsNavDirNext(int nav_dir) const {
857 return (nav_dir == NAVDIR_RIGHT ||
858 nav_dir == NAVDIR_DOWN ||
859 nav_dir == NAVDIR_NEXT);
860 }
861
862 bool NativeViewAccessibilityWin::IsValidNav(
863 int nav_dir, int start_id, int lower_bound, int upper_bound) const {
864 if (IsNavDirNext(nav_dir)) {
865 if ((start_id + 1) > upper_bound) {
866 return false;
867 }
868 } else {
869 if ((start_id - 1) <= lower_bound) {
870 return false;
871 }
872 }
873 return true;
874 }
875
876 bool NativeViewAccessibilityWin::IsValidId(const VARIANT& child) const {
877 // View accessibility returns an IAccessible for each view so we only support
878 // the CHILDID_SELF id.
879 return (VT_I4 == child.vt) && (CHILDID_SELF == child.lVal);
880 }
881
882 void NativeViewAccessibilityWin::SetState(
883 VARIANT* msaa_state, views::View* view) {
884 // Ensure the output param is initialized to zero.
885 msaa_state->lVal = 0;
886
887 // Default state; all views can have accessibility focus.
888 msaa_state->lVal |= STATE_SYSTEM_FOCUSABLE;
889
890 if (!view)
891 return;
892
893 if (!view->IsEnabled())
894 msaa_state->lVal |= STATE_SYSTEM_UNAVAILABLE;
895 if (!view->IsVisible())
896 msaa_state->lVal |= STATE_SYSTEM_INVISIBLE;
897 if (view->IsHotTracked())
898 msaa_state->lVal |= STATE_SYSTEM_HOTTRACKED;
899 if (view->HasFocus())
900 msaa_state->lVal |= STATE_SYSTEM_FOCUSED;
901
902 // Add on any view-specific states.
903 ui::AccessibleViewState view_state;
904 view->GetAccessibleState(&view_state);
905 msaa_state->lVal |= MSAAState(view_state.state);
906 }
907
908 string16 NativeViewAccessibilityWin::TextForIAccessibleText() {
909 ui::AccessibleViewState state;
910 view_->GetAccessibleState(&state);
911 if (state.role == AccessibilityTypes::ROLE_TEXT)
912 return state.value;
913 else
914 return state.name;
915 }
OLDNEW
« no previous file with comments | « views/accessibility/native_view_accessibility_win.h ('k') | views/view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698