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

Side by Side Diff: chrome/android/java_staging/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months 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
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.compositor;
6
7 import android.content.Context;
8 import android.graphics.Canvas;
9 import android.graphics.Paint;
10 import android.graphics.Rect;
11 import android.graphics.RectF;
12 import android.os.Bundle;
13 import android.os.Handler;
14 import android.support.v4.view.ViewCompat;
15 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
16 import android.support.v4.widget.ExploreByTouchHelper;
17 import android.util.AttributeSet;
18 import android.util.Pair;
19 import android.view.MotionEvent;
20 import android.view.SurfaceHolder;
21 import android.view.SurfaceView;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.view.accessibility.AccessibilityEvent;
25 import android.view.inputmethod.InputMethodManager;
26 import android.widget.FrameLayout;
27
28 import com.google.android.apps.chrome.R;
29
30 import org.chromium.base.SysUtils;
31 import org.chromium.base.TraceEvent;
32 import org.chromium.base.annotations.SuppressFBWarnings;
33 import org.chromium.chrome.browser.EmptyTabObserver;
34 import org.chromium.chrome.browser.Tab;
35 import org.chromium.chrome.browser.TabObserver;
36 import org.chromium.chrome.browser.compositor.Invalidator.Client;
37 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
38 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerHost;
39 import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
40 import org.chromium.chrome.browser.compositor.layouts.components.VirtualView;
41 import org.chromium.chrome.browser.compositor.layouts.content.ContentOffsetProvi der;
42 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
43 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDe legate;
44 import org.chromium.chrome.browser.device.DeviceClassManager;
45 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
46 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.Fullscreen Listener;
47 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
48 import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
49 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
50 import org.chromium.chrome.browser.widget.ControlContainer;
51 import org.chromium.content.browser.ContentReadbackHandler;
52 import org.chromium.content.browser.ContentViewCore;
53 import org.chromium.content.browser.SPenSupport;
54 import org.chromium.ui.UiUtils;
55 import org.chromium.ui.base.DeviceFormFactor;
56 import org.chromium.ui.base.WindowAndroid;
57 import org.chromium.ui.resources.ResourceManager;
58 import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
59 import org.chromium.ui.resources.dynamics.ViewResourceAdapter;
60
61 import java.util.ArrayList;
62 import java.util.List;
63
64 /**
65 * This class holds a {@link CompositorView}. This level of indirection is neede d to benefit from
66 * the {@link android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEv ent)} capability on
67 * available on {@link android.view.ViewGroup}s.
68 * This class also holds the {@link LayoutManager} responsible to describe the i tems to be
69 * drawn by the UI compositor on the native side.
70 */
71 public class CompositorViewHolder extends FrameLayout
72 implements LayoutManagerHost, LayoutRenderHost, Invalidator.Host, Fullsc reenListener {
73 private static List<View> sCachedViewList = new ArrayList<View>();
74 private static List<ContentViewCore> sCachedCVCList = new ArrayList<ContentV iewCore>();
75
76 private boolean mIsKeyboardShowing = false;
77
78 private final Invalidator mInvalidator = new Invalidator();
79 private LayoutManager mLayoutManager;
80 private LayerTitleCache mLayerTitleCache;
81 private CompositorView mCompositorView;
82
83 private boolean mContentOverlayVisiblity = true;
84
85 private int mPendingSwapBuffersCount;
86
87 private final ArrayList<Invalidator.Client> mPendingInvalidations =
88 new ArrayList<Invalidator.Client>();
89 private boolean mSkipInvalidation = false;
90
91 private boolean mSkipNextToolbarTextureUpdate = false;
92
93 /**
94 * A task to be performed after a resize event.
95 */
96 private Runnable mPostHideKeyboardTask;
97
98 private TabModelSelector mTabModelSelector;
99 private ChromeFullscreenManager mFullscreenManager;
100 private View mAccessibilityView;
101 private CompositorAccessibilityProvider mNodeProvider;
102 private boolean mFullscreenTouchEvent = false;
103 private float mLastContentOffset = 0;
104 private float mLastVisibleContentOffset = 0;
105
106 /** The toolbar control container. **/
107 private ControlContainer mControlContainer;
108
109 /** The currently visible Tab. */
110 private Tab mTabVisible;
111
112 /** The currently attached View. */
113 private View mView;
114
115 private TabObserver mTabObserver;
116 private boolean mEnableCompositorTabStrip;
117
118 // Cache objects that should not be created frequently.
119 private final Rect mCacheViewport = new Rect();
120 private final Rect mCacheVisibleViewport = new Rect();
121
122 // If we've drawn at least one frame.
123 private boolean mHasDrawnOnce = false;
124
125 /**
126 * This view is created on demand to display debugging information.
127 */
128 private static class DebugOverlay extends View {
129 private final List<Pair<Rect, Integer>> mRectangles = new ArrayList<Pair <Rect, Integer>>();
130 private final Paint mPaint = new Paint();
131 private boolean mFirstPush = true;
132
133 /**
134 * @param context The current Android's context.
135 */
136 public DebugOverlay(Context context) {
137 super(context);
138 }
139
140 /**
141 * Pushes a rectangle to be drawn on the screen on top of everything.
142 *
143 * @param rect The rectangle to be drawn on screen
144 * @param color The color of the rectangle
145 */
146 public void pushRect(Rect rect, int color) {
147 if (mFirstPush) {
148 mRectangles.clear();
149 mFirstPush = false;
150 }
151 mRectangles.add(new Pair<Rect, Integer>(rect, color));
152 invalidate();
153 }
154
155 @SuppressFBWarnings("NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
156 @Override
157 protected void onDraw(Canvas canvas) {
158 for (int i = 0; i < mRectangles.size(); i++) {
159 mPaint.setColor(mRectangles.get(i).second);
160 canvas.drawRect(mRectangles.get(i).first, mPaint);
161 }
162 mFirstPush = true;
163 }
164 }
165
166 private DebugOverlay mDebugOverlay;
167
168 private View mUrlBar;
169
170 /**
171 * Creates a {@link CompositorView}.
172 * @param c The Context to create this {@link CompositorView} in.
173 */
174 public CompositorViewHolder(Context c) {
175 super(c);
176
177 internalInit();
178 }
179
180 /**
181 * Creates a {@link CompositorView}.
182 * @param c The Context to create this {@link CompositorView} in.
183 * @param attrs The AttributeSet used to create this {@link CompositorView}.
184 */
185 public CompositorViewHolder(Context c, AttributeSet attrs) {
186 super(c, attrs);
187
188 internalInit();
189 }
190
191 private void internalInit() {
192 mTabObserver = new EmptyTabObserver() {
193 @Override
194 public void onContentChanged(Tab tab) {
195 CompositorViewHolder.this.onContentChanged();
196 }
197
198 @Override
199 public void onOverlayContentViewCoreAdded(Tab tab, ContentViewCore c ontent) {
200 initializeContentViewCore(content);
201 setSizeOfUnattachedView(content.getContainerView());
202 }
203 };
204
205 mEnableCompositorTabStrip = DeviceFormFactor.isTablet(getContext());
206
207 addOnLayoutChangeListener(new OnLayoutChangeListener() {
208 @Override
209 public void onLayoutChange(View v, int left, int top, int right, int bottom,
210 int oldLeft, int oldTop, int oldRight, int oldBottom) {
211 propagateViewportToLayouts(right - left, bottom - top);
212
213 // If there's an event that needs to occur after the keyboard is hidden, post
214 // it as a delayed event. Otherwise this happens in the midst o f the
215 // ContentView's relayout, which causes the ContentView to relay out on top of the
216 // stack view. The 30ms is arbitrary, hoping to let the view ge t one repaint
217 // in so the full page is shown.
218 if (mPostHideKeyboardTask != null) {
219 new Handler().postDelayed(mPostHideKeyboardTask, 30);
220 mPostHideKeyboardTask = null;
221 }
222 }
223 });
224
225 mCompositorView = new CompositorView(getContext(), this);
226 addView(mCompositorView,
227 new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutPa rams.WRAP_CONTENT));
228 }
229
230 /**
231 * @param layoutManager The {@link LayoutManager} instance that will be driv ing what
232 * shows in this {@link CompositorViewHolder}.
233 */
234 public void setLayoutManager(LayoutManager layoutManager) {
235 mLayoutManager = layoutManager;
236 propagateViewportToLayouts(getWidth(), getHeight());
237 }
238
239 /**
240 * @param view The root view of the hierarchy.
241 */
242 public void setRootView(View view) {
243 mCompositorView.setRootView(view);
244 }
245
246 /**
247 * @param controlContainer The ControlContainer.
248 */
249 public void setControlContainer(ControlContainer controlContainer) {
250 DynamicResourceLoader loader = mCompositorView.getResourceManager() != n ull
251 ? mCompositorView.getResourceManager().getDynamicResourceLoader( )
252 : null;
253 if (loader != null && mControlContainer != null) {
254 loader.unregisterResource(R.id.control_container);
255 loader.unregisterResource(R.id.progress);
256 }
257 mControlContainer = controlContainer;
258 if (loader != null && mControlContainer != null) {
259 loader.registerResource(
260 R.id.control_container, mControlContainer.getToolbarResource Adapter());
261
262 ViewResourceAdapter progressAdapter = mControlContainer.getProgressR esourceAdapter();
263 if (progressAdapter != null) {
264 loader.registerResource(R.id.progress, progressAdapter);
265 }
266 }
267 }
268
269 /**
270 * @return The CompositorView.
271 */
272 public SurfaceHolder.Callback2 getSurfaceHolderCallback2() {
273 return mCompositorView;
274 }
275
276 /**
277 * Reset command line flags. This gets called after the native library finis hes
278 * loading.
279 */
280 public void resetFlags() {
281 mCompositorView.resetFlags();
282 }
283
284 /**
285 * Should be called for cleanup when the CompositorView instance is no longe r used.
286 */
287 public void shutDown() {
288 setTab(null);
289 if (mLayerTitleCache != null) mLayerTitleCache.shutDown();
290 mCompositorView.shutDown();
291 }
292
293 /**
294 * This is called when the native library are ready.
295 */
296 public void onNativeLibraryReady(
297 WindowAndroid windowAndroid, TabContentManager tabContentManager) {
298 assert mLayerTitleCache == null : "Should be called once";
299
300 if (DeviceClassManager.enableLayerDecorationCache()) {
301 mLayerTitleCache = new LayerTitleCache(getContext());
302 }
303
304 mCompositorView.initNativeCompositor(
305 SysUtils.isLowEndDevice(), windowAndroid, mLayerTitleCache, tabC ontentManager);
306
307 if (mLayerTitleCache != null) {
308 mLayerTitleCache.setResourceManager(getResourceManager());
309 }
310
311 if (mControlContainer != null) {
312 mCompositorView.getResourceManager().getDynamicResourceLoader().regi sterResource(
313 R.id.control_container, mControlContainer.getToolbarResource Adapter());
314
315 ViewResourceAdapter progressAdapter = mControlContainer.getProgressR esourceAdapter();
316 if (progressAdapter != null) {
317 mCompositorView.getResourceManager().getDynamicResourceLoader(). registerResource(
318 R.id.progress, progressAdapter);
319 }
320 }
321 }
322
323 @Override
324 public ResourceManager getResourceManager() {
325 return mCompositorView.getResourceManager();
326 }
327
328 public ContentOffsetProvider getContentOffsetProvider() {
329 return mCompositorView;
330 }
331
332 /**
333 * @return The content readback handler.
334 */
335 public ContentReadbackHandler getContentReadbackHandler() {
336 if (mCompositorView == null) return null;
337 return mCompositorView.getContentReadbackHandler();
338 }
339
340 /**
341 * @return The {@link DynamicResourceLoader} for registering resources.
342 */
343 public DynamicResourceLoader getDynamicResourceLoader() {
344 return mCompositorView.getResourceManager().getDynamicResourceLoader();
345 }
346
347 /**
348 * @return The {@link Invalidator} instance that is driven by this {@link Co mpositorViewHolder}.
349 */
350 public Invalidator getInvalidator() {
351 return mInvalidator;
352 }
353
354 @Override
355 public boolean onInterceptTouchEvent(MotionEvent e) {
356 super.onInterceptTouchEvent(e);
357
358 if (mLayoutManager == null) return false;
359
360 mFullscreenTouchEvent = false;
361 if (mFullscreenManager != null && mFullscreenManager.onInterceptMotionEv ent(e)
362 && !mEnableCompositorTabStrip) {
363 // Don't eat the event if the new tab strip is enabled.
364 mFullscreenTouchEvent = true;
365 return true;
366 }
367
368 setContentViewMotionEventOffsets(e, false);
369 return mLayoutManager.onInterceptTouchEvent(e, mIsKeyboardShowing);
370 }
371
372 @Override
373 public boolean onTouchEvent(MotionEvent e) {
374 if (mFullscreenManager != null) mFullscreenManager.onMotionEvent(e);
375 if (mFullscreenTouchEvent) return true;
376 boolean consumed = mLayoutManager != null && mLayoutManager.onTouchEvent (e);
377 setContentViewMotionEventOffsets(e, true);
378 return consumed;
379 }
380
381 @Override
382 public boolean onInterceptHoverEvent(MotionEvent e) {
383 setContentViewMotionEventOffsets(e, true);
384 return super.onInterceptHoverEvent(e);
385 }
386
387 @Override
388 public boolean dispatchHoverEvent(MotionEvent e) {
389 if (mNodeProvider != null) {
390 if (mNodeProvider.dispatchHoverEvent(e)) {
391 return true;
392 }
393 }
394 return super.dispatchHoverEvent(e);
395 }
396
397 /**
398 * @return The {@link LayoutManager} associated with this view.
399 */
400 public LayoutManager getLayoutManager() {
401 return mLayoutManager;
402 }
403
404 /**
405 * @return The SurfaceView used by the Compositor.
406 */
407 public SurfaceView getSurfaceView() {
408 return mCompositorView;
409 }
410
411 @Override
412 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
413 super.onSizeChanged(w, h, oldw, oldh);
414 if (mLayoutManager == null) return;
415
416 sCachedViewList.clear();
417 mLayoutManager.getActiveLayout().getAllViews(sCachedViewList);
418
419 boolean resized = false;
420 for (int i = 0; i < sCachedViewList.size(); i++) {
421 resized |= setSizeOfUnattachedView(sCachedViewList.get(i));
422 }
423 sCachedViewList.clear();
424
425 if (resized) requestRender();
426 }
427
428 @Override
429 public void onPhysicalBackingSizeChanged(int width, int height) {
430 if (mLayoutManager == null) return;
431
432 sCachedCVCList.clear();
433 mLayoutManager.getActiveLayout().getAllContentViewCores(sCachedCVCList);
434
435 for (int i = 0; i < sCachedCVCList.size(); i++) {
436 sCachedCVCList.get(i).onPhysicalBackingSizeChanged(width, height);
437 }
438 sCachedCVCList.clear();
439 }
440
441 @Override
442 public void onOverdrawBottomHeightChanged(int overdrawHeight) {
443 if (mLayoutManager == null) return;
444
445 sCachedCVCList.clear();
446 mLayoutManager.getActiveLayout().getAllContentViewCores(sCachedCVCList);
447
448 for (int i = 0; i < sCachedCVCList.size(); i++) {
449 sCachedCVCList.get(i).onOverdrawBottomHeightChanged(overdrawHeight);
450 }
451 sCachedCVCList.clear();
452
453 mSkipNextToolbarTextureUpdate = true;
454 requestRender();
455 }
456
457 @Override
458 public int getCurrentOverdrawBottomHeight() {
459 if (mTabVisible != null) {
460 float overdrawBottomHeight = mTabVisible.getFullscreenOverdrawBottom HeightPix();
461 if (!Float.isNaN(overdrawBottomHeight)) {
462 return (int) overdrawBottomHeight;
463 }
464 }
465 return mCompositorView.getOverdrawBottomHeight();
466 }
467
468 /**
469 * Called whenever the host activity is started.
470 */
471 public void onStart() {
472 if (mFullscreenManager != null) {
473 mLastContentOffset = mFullscreenManager.getContentOffset();
474 mLastVisibleContentOffset = mFullscreenManager.getVisibleContentOffs et();
475 mFullscreenManager.addListener(this);
476 }
477 requestRender();
478 }
479
480 /**
481 * Called whenever the host activity is stopped.
482 */
483 public void onStop() {
484 if (mFullscreenManager != null) mFullscreenManager.removeListener(this);
485 }
486
487 @Override
488 public void onContentOffsetChanged(float offset) {
489 mLastContentOffset = offset;
490 propagateViewportToLayouts(getWidth(), getHeight());
491 }
492
493 @Override
494 public void onVisibleContentOffsetChanged(float offset) {
495 mLastVisibleContentOffset = offset;
496 propagateViewportToLayouts(getWidth(), getHeight());
497 requestRender();
498 }
499
500 @Override
501 public void onToggleOverlayVideoMode(boolean enabled) {
502 if (mCompositorView != null) {
503 mCompositorView.setOverlayVideoMode(enabled);
504 }
505 }
506
507 private void setContentViewMotionEventOffsets(MotionEvent e, boolean canClea r) {
508 // TODO(dtrainor): Factor this out to LayoutDriver.
509 if (e == null || mTabVisible == null) return;
510
511 ContentViewCore contentViewCore = mTabVisible.getContentViewCore();
512 if (contentViewCore == null) return;
513
514 int actionMasked = e.getActionMasked();
515
516 if (SPenSupport.isSPenSupported(getContext())) {
517 actionMasked = SPenSupport.convertSPenEventAction(actionMasked);
518 }
519
520 if (actionMasked == MotionEvent.ACTION_DOWN
521 || actionMasked == MotionEvent.ACTION_HOVER_ENTER) {
522 if (mLayoutManager != null) mLayoutManager.getViewportPixel(mCacheVi ewport);
523 contentViewCore.setCurrentMotionEventOffsets(-mCacheViewport.left, - mCacheViewport.top);
524 } else if (canClear && (actionMasked == MotionEvent.ACTION_UP
525 || actionMasked == MotionEvent.ACTION_CAN CEL
526 || actionMasked == MotionEvent.ACTION_HOV ER_EXIT)) {
527 contentViewCore.setCurrentMotionEventOffsets(0.f, 0.f);
528 }
529 }
530
531 private void propagateViewportToLayouts(int contentWidth, int contentHeight) {
532 int heightMinusTopControls = contentHeight - getTopControlsHeightPixels( );
533 mCacheViewport.set(0, (int) mLastContentOffset, contentWidth, contentHei ght);
534 mCacheVisibleViewport.set(0, (int) mLastVisibleContentOffset, contentWid th, contentHeight);
535 // TODO(changwan): check if this can be merged with setContentMotionEven tOffsets.
536 if (mTabVisible != null && mTabVisible.getContentViewCore() != null) {
537 mTabVisible.getContentViewCore().setSmartClipOffsets(
538 -mCacheViewport.left, -mCacheViewport.top);
539 }
540 if (mLayoutManager != null) {
541 mLayoutManager.pushNewViewport(
542 mCacheViewport, mCacheVisibleViewport, heightMinusTopControl s);
543 }
544 }
545
546 /**
547 * To be called once a frame before commit.
548 */
549 @Override
550 public void onCompositorLayout() {
551 TraceEvent.begin("CompositorViewHolder:layout");
552 if (mLayoutManager != null) {
553 mLayoutManager.onUpdate();
554 mCompositorView.finalizeLayers(mLayoutManager, mSkipNextToolbarTextu reUpdate);
555 // TODO(changwan): Check if this hack can be removed.
556 // This is a hack to draw one more frame if the screen just rotated for Nexus 10 + L.
557 // See http://crbug/440469 for more.
558 if (mSkipNextToolbarTextureUpdate) {
559 requestRender();
560 }
561 }
562
563 TraceEvent.end("CompositorViewHolder:layout");
564 mSkipNextToolbarTextureUpdate = false;
565 }
566
567 @Override
568 public void requestRender() {
569 mCompositorView.requestRender();
570 }
571
572 @Override
573 public void onSurfaceCreated() {
574 mPendingSwapBuffersCount = 0;
575 flushInvalidation();
576 }
577
578 @Override
579 public void onSwapBuffersCompleted(int pendingSwapBuffersCount) {
580 TraceEvent.instant("onSwapBuffersCompleted");
581
582 // Wait until the second frame to turn off the placeholder background on
583 // tablets so the tab strip has time to start drawing.
584 final ViewGroup controlContainer = (ViewGroup) mControlContainer;
585 if (controlContainer != null && controlContainer.getBackground() != null && mHasDrawnOnce) {
586 post(new Runnable() {
587 @Override
588 public void run() {
589 controlContainer.setBackgroundResource(0);
590 }
591 });
592 }
593
594 mHasDrawnOnce = true;
595
596 mPendingSwapBuffersCount = pendingSwapBuffersCount;
597
598 if (!mSkipInvalidation || pendingSwapBuffersCount == 0) flushInvalidatio n();
599 mSkipInvalidation = !mSkipInvalidation;
600 }
601
602 @Override
603 public void setContentOverlayVisibility(boolean show) {
604 if (show != mContentOverlayVisiblity) {
605 mContentOverlayVisiblity = show;
606 updateContentOverlayVisibility(mContentOverlayVisiblity);
607 }
608 }
609
610 @Override
611 public LayoutRenderHost getLayoutRenderHost() {
612 return this;
613 }
614
615 @Override
616 public int getLayoutTabsDrawnCount() {
617 return mCompositorView.getLastLayerCount();
618 }
619
620 @Override
621 public void pushDebugRect(Rect rect, int color) {
622 if (mDebugOverlay == null) {
623 mDebugOverlay = new DebugOverlay(getContext());
624 addView(mDebugOverlay);
625 }
626 mDebugOverlay.pushRect(rect, color);
627 }
628
629 @Override
630 public void loadPersitentTextureDataIfNeeded() {}
631
632 @Override
633 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
634 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
635 mIsKeyboardShowing = UiUtils.isKeyboardShowing(getContext(), this);
636 }
637
638 @Override
639 protected void onLayout(boolean changed, int l, int t, int r, int b) {
640 if (changed) {
641 propagateViewportToLayouts(r - l, b - t);
642 }
643 super.onLayout(changed, l, t, r, b);
644
645 invalidateAccessibilityProvider();
646 }
647
648 @Override
649 public void clearChildFocus(View child) {
650 // Override this method so that the ViewRoot doesn't go looking for a ne w
651 // view to take focus. It will find the URL Bar, focus it, then refocus this
652 // later, causing a keyboard flicker.
653 }
654
655 @Override
656 public ChromeFullscreenManager getFullscreenManager() {
657 return mFullscreenManager;
658 }
659
660 /**
661 * Sets a fullscreen handler.
662 * @param fullscreen A fullscreen handler.
663 */
664 public void setFullscreenHandler(ChromeFullscreenManager fullscreen) {
665 mFullscreenManager = fullscreen;
666 if (mFullscreenManager != null) {
667 mLastContentOffset = mFullscreenManager.getContentOffset();
668 mLastVisibleContentOffset = mFullscreenManager.getVisibleContentOffs et();
669 mFullscreenManager.addListener(this);
670 }
671 propagateViewportToLayouts(getWidth(), getHeight());
672 }
673
674 /**
675 * Note that the returned rect is reused for other calls.
676 */
677 @Override
678 public Rect getVisibleViewport(Rect rect) {
679 if (rect == null) rect = new Rect();
680 rect.set(0, (int) mLastVisibleContentOffset, getWidth(), getHeight());
681 return rect;
682 }
683
684 @Override
685 public boolean areTopControlsPermanentlyHidden() {
686 return mFullscreenManager != null && mFullscreenManager.areTopControlsPe rmanentlyHidden();
687 }
688
689 @Override
690 public int getTopControlsHeightPixels() {
691 return mFullscreenManager != null ? mFullscreenManager.getTopControlsHei ght() : 0;
692 }
693
694 /**
695 * Sets the URL bar. This is needed so that the ContentViewHolder can find o ut
696 * whether it can claim focus.
697 */
698 public void setUrlBar(View urlBar) {
699 mUrlBar = urlBar;
700 }
701
702 @Override
703 protected void onAttachedToWindow() {
704 mInvalidator.set(this);
705 super.onAttachedToWindow();
706 }
707
708 @Override
709 protected void onDetachedFromWindow() {
710 if (mLayoutManager != null) mLayoutManager.destroy();
711 flushInvalidation();
712 mInvalidator.set(null);
713 super.onDetachedFromWindow();
714
715 // Removes the accessibility node provider from this view.
716 if (mNodeProvider != null) {
717 mAccessibilityView.setAccessibilityDelegate(null);
718 mNodeProvider = null;
719 removeView(mAccessibilityView);
720 mAccessibilityView = null;
721 }
722 }
723
724 /**
725 * @return True if the currently active content view is shown in the normal interactive mode.
726 */
727 public boolean isTabInteractive() {
728 return mLayoutManager != null && mLayoutManager.getActiveLayout() != nul l
729 && mLayoutManager.getActiveLayout().isTabInteractive() && mConte ntOverlayVisiblity
730 && mView != null;
731 }
732
733 /**
734 * Hides the the keyboard if it was opened for the ContentView.
735 * @param postHideTask A task to run after the keyboard is done hiding and t he view's
736 * layout has been updated. If the keyboard was not shown, the task will run
737 * immediately.
738 */
739 public void hideKeyboard(Runnable postHideTask) {
740 // When this is called we actually want to hide the keyboard whatever ow ns it.
741 // This includes hiding the keyboard, and dropping focus from the URL ba r.
742 // See http://crbug/236424
743 // TODO(aberent) Find a better place to put this, possibly as part of a wider
744 // redesign of focus control.
745 if (mUrlBar != null) mUrlBar.clearFocus();
746 boolean wasVisible = false;
747 if (hasFocus()) {
748 wasVisible = UiUtils.hideKeyboard(this);
749 }
750 if (wasVisible) {
751 mPostHideKeyboardTask = postHideTask;
752 } else {
753 postHideTask.run();
754 }
755 }
756
757 /**
758 * Sets the appropriate objects this class should represent.
759 * @param tabModelSelector The {@link TabModelSelector} this View sho uld hold and
760 * represent.
761 * @param tabCreatorManager The {@link TabCreatorManager} for this vie w.
762 * @param tabContentManager The {@link TabContentManager} for the tabs .
763 * @param androidContentContainer The {@link ViewGroup} the {@link LayoutMan ager} should bind
764 * Android content to.
765 * @param contextualSearchManager A {@link ContextualSearchManagementDelegat e} instance.
766 */
767 public void onFinishNativeInitialization(TabModelSelector tabModelSelector,
768 TabCreatorManager tabCreatorManager, TabContentManager tabContentMan ager,
769 ViewGroup androidContentContainer,
770 ContextualSearchManagementDelegate contextualSearchManager) {
771 assert mLayoutManager != null;
772 mLayoutManager.init(tabModelSelector, tabCreatorManager, tabContentManag er,
773 androidContentContainer, contextualSearchManager,
774 mCompositorView.getResourceManager().getDynamicResourceLoader()) ;
775 mTabModelSelector = tabModelSelector;
776 tabModelSelector.addObserver(new EmptyTabModelSelectorObserver() {
777 @Override
778 public void onChange() {
779 onContentChanged();
780 }
781
782 @Override
783 public void onNewTabCreated(Tab tab) {
784 initializeTab(tab);
785 }
786 });
787
788 onContentChanged();
789 }
790
791 private void updateContentOverlayVisibility(boolean show) {
792 if (mView == null) return;
793
794 sCachedCVCList.clear();
795 if (mLayoutManager != null) {
796 mLayoutManager.getActiveLayout().getAllContentViewCores(sCachedCVCLi st);
797 }
798 if (show) {
799 if (mView.getParent() != this) {
800 // Make sure the view isn't a child of something else before we attempt to add it.
801 if (mView.getParent() != null && mView.getParent() instanceof Vi ewGroup) {
802 ((ViewGroup) mView.getParent()).removeView(mView);
803 }
804
805 for (int i = 0; i < sCachedCVCList.size(); i++) {
806 ContentViewCore content = sCachedCVCList.get(i);
807 assert content.isAlive();
808 content.getContainerView().setVisibility(View.VISIBLE);
809 if (mFullscreenManager != null) {
810 mFullscreenManager.updateContentViewViewportSize(content );
811 }
812 }
813
814 FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutPa rams(
815 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
816 addView(mView, layoutParams);
817 setFocusable(false);
818 setFocusableInTouchMode(false);
819
820 // Claim focus for the new view unless the user is currently usi ng the URL bar.
821 if (mUrlBar == null || !mUrlBar.hasFocus()) mView.requestFocus() ;
822 }
823 } else {
824 if (mView.getParent() == this) {
825 setFocusable(true);
826 setFocusableInTouchMode(true);
827
828 for (int i = 0; i < sCachedCVCList.size(); i++) {
829 ContentViewCore content = sCachedCVCList.get(i);
830 if (content.isAlive()) content.getContainerView().setVisibil ity(View.INVISIBLE);
831 }
832
833 if (hasFocus()) {
834 InputMethodManager manager = (InputMethodManager) getContext ().getSystemService(
835 Context.INPUT_METHOD_SERVICE);
836 if (manager.isActive(this)) {
837 manager.hideSoftInputFromWindow(getWindowToken(), 0, nul l);
838 }
839 }
840 removeView(mView);
841 }
842 }
843 sCachedCVCList.clear();
844 }
845
846 @Override
847 public void onContentChanged() {
848 if (mTabModelSelector == null) {
849 // Not yet initialized, onContentChanged() will eventually get calle d by
850 // setTabModelSelector.
851 return;
852 }
853 Tab tab = mTabModelSelector.getCurrentTab();
854 setTab(tab);
855 }
856
857 @Override
858 public void onContentViewCoreAdded(ContentViewCore content) {
859 // TODO(dtrainor): Look into rolling this into onContentChanged().
860 initializeContentViewCore(content);
861 setSizeOfUnattachedView(content.getContainerView());
862 }
863
864 private void setTab(Tab tab) {
865 if (tab != null && tab.isFrozen()) tab.unfreezeContents();
866
867 View newView = tab != null ? tab.getView() : null;
868 if (mView == newView) return;
869
870 // TODO(dtrainor): Look into changing this only if the views differ, but still parse the
871 // ContentViewCore list even if they're the same.
872 updateContentOverlayVisibility(false);
873
874 if (mTabVisible != tab) {
875 if (mTabVisible != null) mTabVisible.removeObserver(mTabObserver);
876 if (tab != null) tab.addObserver(mTabObserver);
877 }
878
879 mTabVisible = tab;
880 mView = newView;
881
882 updateContentOverlayVisibility(mContentOverlayVisiblity);
883
884 if (mTabVisible != null) initializeTab(mTabVisible);
885 }
886
887 /**
888 * Sets the correct size for all {@link View}s on {@code tab} and sets the c orrect rendering
889 * parameters on all {@link ContentViewCore}s on {@code tab}.
890 * @param tab The {@link Tab} to initialize.
891 */
892 private void initializeTab(Tab tab) {
893 sCachedCVCList.clear();
894 if (mLayoutManager != null) {
895 mLayoutManager.getActiveLayout().getAllContentViewCores(sCachedCVCLi st);
896 }
897
898 for (int i = 0; i < sCachedCVCList.size(); i++) {
899 initializeContentViewCore(sCachedCVCList.get(i));
900 }
901 sCachedCVCList.clear();
902
903 sCachedViewList.clear();
904 tab.getAllViews(sCachedViewList);
905
906 for (int i = 0; i < sCachedViewList.size(); i++) {
907 View view = sCachedViewList.get(i);
908 // Calling View#measure() and View#layout() on a View before adding it to the view
909 // hierarchy seems to cause issues with compound drawables on some v ersions of Android.
910 // We don't need to proactively size the NTP as we don't need the An droid view to render
911 // if it's not actually attached to the view hierarchy (http://crbug .com/462114).
912 if (view == tab.getView() && tab.isNativePage()) continue;
913 setSizeOfUnattachedView(view);
914 }
915 sCachedViewList.clear();
916 }
917
918 /**
919 * Initializes the rendering surface parameters of {@code contentViewCore}. Note that this does
920 * not size the actual {@link ContentViewCore}.
921 * @param contentViewCore The {@link ContentViewCore} to initialize.
922 */
923 private void initializeContentViewCore(ContentViewCore contentViewCore) {
924 contentViewCore.setCurrentMotionEventOffsets(0.f, 0.f);
925 contentViewCore.setTopControlsHeight(
926 getTopControlsHeightPixels(), contentViewCore.doTopControlsShrin kBlinkSize());
927 contentViewCore.onPhysicalBackingSizeChanged(
928 mCompositorView.getWidth(), mCompositorView.getHeight());
929 contentViewCore.onOverdrawBottomHeightChanged(mCompositorView.getOverdra wBottomHeight());
930 }
931
932 /**
933 * Resize {@code view} to match the size of this {@link FrameLayout}. This will only happen if
934 * {@code view} is not {@code null} and if {@link View#getWindowToken()} ret urns {@code null}
935 * (the {@link View} is not part of the view hierarchy).
936 * @param view The {@link View} to resize.
937 * @return Whether or not {@code view} was resized.
938 */
939 private boolean setSizeOfUnattachedView(View view) {
940 // Need to call layout() for the following View if it is not attached to the view hierarchy.
941 // Calling onSizeChanged() is dangerous because if the View has a differ ent size than the
942 // ContentViewCore it might think a future size update is a NOOP and not call
943 // onSizeChanged() on the ContentViewCore.
944 if (view == null || view.getWindowToken() != null) return false;
945 int width = getWidth();
946 int height = getHeight();
947 view.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
948 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
949 view.layout(0, 0, width, height);
950 return true;
951 }
952
953 @Override
954 public TitleCache getTitleCache() {
955 return mLayerTitleCache;
956 }
957
958 @Override
959 public void deferInvalidate(Client client) {
960 if (mPendingSwapBuffersCount <= 0) {
961 client.doInvalidate();
962 } else if (!mPendingInvalidations.contains(client)) {
963 mPendingInvalidations.add(client);
964 }
965 }
966
967 private void flushInvalidation() {
968 if (mPendingInvalidations.isEmpty()) return;
969 TraceEvent.instant("CompositorViewHolder.flushInvalidation");
970 for (int i = 0; i < mPendingInvalidations.size(); i++) {
971 mPendingInvalidations.get(i).doInvalidate();
972 }
973 mPendingInvalidations.clear();
974 }
975
976 @Override
977 public void invalidateAccessibilityProvider() {
978 if (mNodeProvider != null) {
979 mNodeProvider.invalidateRoot();
980 }
981 }
982
983 /**
984 * Called when the accessibility enabled state changes.
985 * @param enabled Whether accessibility is enabled.
986 */
987 public void onAccessibilityStatusChanged(boolean enabled) {
988 // Instantiate and install the accessibility node provider on this view if necessary.
989 // This overrides any hover event listeners or accessibility delegates
990 // that may have been added elsewhere.
991 if (enabled && (mNodeProvider == null)) {
992 mAccessibilityView = new View(getContext());
993 addView(mAccessibilityView);
994 mNodeProvider = new CompositorAccessibilityProvider(mAccessibilityVi ew);
995 ViewCompat.setAccessibilityDelegate(mAccessibilityView, mNodeProvide r);
996 }
997 }
998
999 /**
1000 * Class used to provide a virtual view hierarchy to the Accessibility
1001 * framework for this view and its contained items.
1002 * <p>
1003 * <strong>NOTE:</strong> This class is fully backwards compatible for
1004 * compilation, but will only provide touch exploration on devices running
1005 * Ice Cream Sandwich and above.
1006 * </p>
1007 */
1008 private class CompositorAccessibilityProvider extends ExploreByTouchHelper {
1009 private final float mDpToPx;
1010 List<VirtualView> mVirtualViews = new ArrayList<VirtualView>();
1011 private final Rect mPlaceHolderRect = new Rect(0, 0, 1, 1);
1012 private static final String PLACE_HOLDER_STRING = "";
1013 private final RectF mTouchTarget = new RectF();
1014 private final Rect mPixelRect = new Rect();
1015
1016 public CompositorAccessibilityProvider(View forView) {
1017 super(forView);
1018 mDpToPx = getContext().getResources().getDisplayMetrics().density;
1019 }
1020
1021 @Override
1022 protected int getVirtualViewAt(float x, float y) {
1023 if (mVirtualViews == null) return INVALID_ID;
1024 for (int i = 0; i < mVirtualViews.size(); i++) {
1025 if (mVirtualViews.get(i).checkClicked(x / mDpToPx, y / mDpToPx)) {
1026 return i;
1027 }
1028 }
1029 return INVALID_ID;
1030 }
1031
1032 @Override
1033 protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
1034 if (mLayoutManager == null) return;
1035 mVirtualViews.clear();
1036 mLayoutManager.getVirtualViews(mVirtualViews);
1037 for (int i = 0; i < mVirtualViews.size(); i++) {
1038 virtualViewIds.add(i);
1039 }
1040 }
1041
1042 @Override
1043 protected boolean onPerformActionForVirtualView(
1044 int virtualViewId, int action, Bundle arguments) {
1045 switch (action) {
1046 case AccessibilityNodeInfoCompat.ACTION_CLICK:
1047 return true;
1048 }
1049
1050 return false;
1051 }
1052
1053 @Override
1054 protected void onPopulateEventForVirtualView(int virtualViewId, Accessib ilityEvent event) {
1055 if (mVirtualViews == null || mVirtualViews.size() <= virtualViewId) {
1056 // TODO(clholgat): Remove this work around when the Android bug is fixed.
1057 // crbug.com/420177
1058 event.setContentDescription(PLACE_HOLDER_STRING);
1059 return;
1060 }
1061 VirtualView view = mVirtualViews.get(virtualViewId);
1062
1063 event.setContentDescription(view.getAccessibilityDescription());
1064 event.setClassName(CompositorViewHolder.class.getName());
1065 }
1066
1067 @Override
1068 protected void onPopulateNodeForVirtualView(
1069 int virtualViewId, AccessibilityNodeInfoCompat node) {
1070 if (mVirtualViews == null || mVirtualViews.size() <= virtualViewId) {
1071 // TODO(clholgat): Remove this work around when the Android bug is fixed.
1072 // crbug.com/420177
1073 node.setBoundsInParent(mPlaceHolderRect);
1074 node.setContentDescription(PLACE_HOLDER_STRING);
1075 return;
1076 }
1077 VirtualView view = mVirtualViews.get(virtualViewId);
1078 view.getTouchTarget(mTouchTarget);
1079
1080 node.setBoundsInParent(rectToPx(mTouchTarget));
1081 node.setContentDescription(view.getAccessibilityDescription());
1082 node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
1083 node.addAction(AccessibilityNodeInfoCompat.ACTION_FOCUS);
1084 node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK);
1085 }
1086
1087 private Rect rectToPx(RectF rect) {
1088 rect.roundOut(mPixelRect);
1089 mPixelRect.left = (int) (mPixelRect.left * mDpToPx);
1090 mPixelRect.top = (int) (mPixelRect.top * mDpToPx);
1091 mPixelRect.right = (int) (mPixelRect.right * mDpToPx);
1092 mPixelRect.bottom = (int) (mPixelRect.bottom * mDpToPx);
1093
1094 // Don't let any zero sized rects through, they'll cause parent
1095 // size errors in L.
1096 if (mPixelRect.width() == 0) {
1097 mPixelRect.right = mPixelRect.left + 1;
1098 }
1099 if (mPixelRect.height() == 0) {
1100 mPixelRect.bottom = mPixelRect.top + 1;
1101 }
1102 return mPixelRect;
1103 }
1104 }
1105 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698