Commit 020b8a30 authored by Xi Han's avatar Xi Han Committed by Commit Bot

[Instant Start] Use tab ID to get JPEG thumbnail.

This is a precursor CL for https://crrev.com/c/1992341 which creates
the TabContentManager pre-native, and splits its creation into
creation + post-native initialization.

In this CL, we refactor TabContentManager::getTabThumbnailWithCallback
to use a tab ID instead of Tab which can't be created in pre-native.

Bug: 1041865
Change-Id: I8a0c82e40b5e0f05974a66bceb7cc96d2e4bcf5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2003433Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Commit-Queue: Xi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736990}
parent 52ead7cf
......@@ -404,7 +404,7 @@ public class StartSurfaceLayoutPerfTest {
"The thumbnail " + etc1File.getName() + " is not found",
DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL);
File jpegFile = TabContentManager.getTabThumbnailFileJpeg(tab);
File jpegFile = TabContentManager.getTabThumbnailFileJpeg(tab.getId());
CriteriaHelper.pollInstrumentationThread(jpegFile::exists,
"The thumbnail " + jpegFile.getName() + " is not found",
DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL);
......
......@@ -1409,7 +1409,7 @@ public class StartSurfaceLayoutTest {
TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel();
for (int i = 0; i < currentModel.getCount(); i++) {
Tab tab = currentModel.getTabAt(i);
Bitmap bitmap = TabContentManager.getJpegForTab(tab);
Bitmap bitmap = TabContentManager.getJpegForTab(tab.getId());
bitmap = Bitmap.createScaledBitmap(
bitmap, bitmap.getWidth(), (int) (bitmap.getWidth() * 1.0 / 0.75), false);
encodeJpeg(tab, bitmap);
......@@ -1418,17 +1418,17 @@ public class StartSurfaceLayoutTest {
private void encodeJpeg(Tab tab, Bitmap bitmap) throws IOException {
FileOutputStream outputStream =
new FileOutputStream(TabContentManager.getTabThumbnailFileJpeg(tab));
new FileOutputStream(TabContentManager.getTabThumbnailFileJpeg(tab.getId()));
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
outputStream.close();
Bitmap decodedBitmap = TabContentManager.getJpegForTab(tab);
Bitmap decodedBitmap = TabContentManager.getJpegForTab(tab.getId());
}
private void verifyAllThumbnailHasAspectRatio(double ratio) {
TabModel currentModel = mActivityTestRule.getActivity().getCurrentTabModel();
for (int i = 0; i < currentModel.getCount(); i++) {
Tab tab = currentModel.getTabAt(i);
Bitmap bitmap = TabContentManager.getJpegForTab(tab);
Bitmap bitmap = TabContentManager.getJpegForTab(tab.getId());
double bitmapRatio = bitmap.getWidth() * 1.0 / bitmap.getHeight();
assertTrue("Actual ratio: " + bitmapRatio + "; Expected ratio: " + ratio,
Math.abs(bitmapRatio - ratio) <= TabContentManager.ASPECT_RATIO_PRECISION);
......
......@@ -124,18 +124,19 @@ public class MultiThumbnailCardProvider implements TabListMediator.ThumbnailProv
// Fetching the favicon after getting the live thumbnail would lead to
// visible flicker.
final AtomicReference<Drawable> lastFavicon = new AtomicReference<>();
mTabContentManager.getTabThumbnailWithCallback(mTabs.get(i), thumbnail -> {
drawThumbnailBitmapOnCanvasWithFrame(thumbnail, index);
if (lastFavicon.get() != null) {
drawFaviconThenMaybeSendBack(lastFavicon.get(), index);
} else {
mTabListFaviconProvider.getFaviconForUrlAsync(
url, isIncognito, (Drawable favicon) -> {
lastFavicon.set(favicon);
drawFaviconThenMaybeSendBack(favicon, index);
});
}
}, mForceUpdate && i == 0, mWriteToCache && i == 0);
mTabContentManager.getTabThumbnailWithCallback(
mTabs.get(i).getId(), thumbnail -> {
drawThumbnailBitmapOnCanvasWithFrame(thumbnail, index);
if (lastFavicon.get() != null) {
drawFaviconThenMaybeSendBack(lastFavicon.get(), index);
} else {
mTabListFaviconProvider.getFaviconForUrlAsync(
url, isIncognito, (Drawable favicon) -> {
lastFavicon.set(favicon);
drawFaviconThenMaybeSendBack(favicon, index);
});
}
}, mForceUpdate && i == 0, mWriteToCache && i == 0);
} else {
drawThumbnailBitmapOnCanvasWithFrame(null, i);
if (mText != null && i == 3) {
......@@ -298,17 +299,20 @@ public class MultiThumbnailCardProvider implements TabListMediator.ThumbnailProv
@Override
public void getTabThumbnailWithCallback(
Tab tab, Callback<Bitmap> finalCallback, boolean forceUpdate, boolean writeToCache) {
int tabId, Callback<Bitmap> finalCallback, boolean forceUpdate, boolean writeToCache) {
if (mTabModelSelector.getTabModelFilterProvider()
.getCurrentTabModelFilter()
.getRelatedTabList(tab.getId())
.getRelatedTabList(tabId)
.size()
== 1) {
mTabContentManager.getTabThumbnailWithCallback(
tab, finalCallback, forceUpdate, writeToCache);
tabId, finalCallback, forceUpdate, writeToCache);
return;
}
Tab tab = mTabModelSelector.getTabById(tabId);
if (tab == null) return;
new MultiThumbnailFetcher(tab, finalCallback, forceUpdate, writeToCache).fetch();
}
}
......@@ -98,7 +98,7 @@ class TabListMediator {
* @see TabContentManager#getTabThumbnailWithCallback
*/
void getTabThumbnailWithCallback(
Tab tab, Callback<Bitmap> callback, boolean forceUpdate, boolean writeToCache);
int tabId, Callback<Bitmap> callback, boolean forceUpdate, boolean writeToCache);
}
/**
......@@ -176,8 +176,9 @@ class TabListMediator {
callback.onResult(bitmap);
};
sFetchCountForTesting++;
int tabId = mTab != null ? mTab.getId() : Tab.INVALID_TAB_ID;
mThumbnailProvider.getTabThumbnailWithCallback(
mTab, forking, mForceUpdate, mWriteToCache);
tabId, forking, mForceUpdate, mWriteToCache);
}
}
......
......@@ -29,7 +29,6 @@ import org.junit.runner.RunWith;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.flags.FeatureUtilities;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
......@@ -70,7 +69,7 @@ public class TabListViewHolderTest extends DummyUiActivityTestCase {
private TabListMediator.ThumbnailFetcher mMockThumbnailProvider =
new TabListMediator.ThumbnailFetcher(new TabListMediator.ThumbnailProvider() {
@Override
public void getTabThumbnailWithCallback(Tab tab, Callback<Bitmap> callback,
public void getTabThumbnailWithCallback(int tabId, Callback<Bitmap> callback,
boolean forceUpdate, boolean writeToCache) {
Bitmap bitmap = mShouldReturnBitmap
? Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
......
......@@ -450,7 +450,7 @@ public class TabUiTestHelper {
"The thumbnail " + etc1File.getName() + " is not found",
DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL);
File jpegFile = TabContentManager.getTabThumbnailFileJpeg(tab);
File jpegFile = TabContentManager.getTabThumbnailFileJpeg(tab.getId());
CriteriaHelper.pollInstrumentationThread(jpegFile::exists,
"The thumbnail " + jpegFile.getName() + " is not found",
DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL);
......
......@@ -261,7 +261,7 @@ public class TabListMediatorUnitTest {
doNothing()
.when(mTabContentManager)
.getTabThumbnailWithCallback(any(), any(), anyBoolean(), anyBoolean());
.getTabThumbnailWithCallback(anyInt(), any(), anyBoolean(), anyBoolean());
doReturn(mTabModel).when(mTabModelSelector).getCurrentModel();
doReturn(tabModelList).when(mTabModelSelector).getModels();
doReturn(mTabModelFilterProvider).when(mTabModelSelector).getTabModelFilterProvider();
......
......@@ -153,7 +153,7 @@ public class TabSwitcherMediatorUnitTest {
doNothing()
.when(mTabContentManager)
.getTabThumbnailWithCallback(any(), any(), anyBoolean(), anyBoolean());
.getTabThumbnailWithCallback(anyInt(), any(), anyBoolean(), anyBoolean());
doReturn(mResources).when(mContext).getResources();
doReturn(mTabModel).when(mTabModelSelector).getCurrentModel();
......
......@@ -769,8 +769,8 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
TraceEvent.begin("ChromeActivity:CompositorInitialization");
super.initializeCompositor();
setTabContentManager(new TabContentManager(
this, getContentOffsetProvider(), DeviceClassManager.enableSnapshots()));
setTabContentManager(new TabContentManager(this, getContentOffsetProvider(),
DeviceClassManager.enableSnapshots(), mTabModelSelector::getTabById));
mCompositorViewHolder.onNativeLibraryReady(getWindowAndroid(), getTabContentManager());
if (isContextualSearchAllowed() && ContextualSearchFieldTrial.isEnabled()) {
......
......@@ -85,6 +85,7 @@ public class TabContentManager {
new ArrayList<ThumbnailChangeListener>();
private boolean mSnapshotsEnabled;
private final TabFinder mTabFinder;
/**
* Listener to receive the "Last Thumbnail" event. "Last Thumbnail" is the first time
......@@ -108,6 +109,11 @@ public class TabContentManager {
public void onThumbnailChange(int id);
}
/**
* The interface to get a {@link Tab} from a tab ID.
*/
public interface TabFinder { Tab getTabById(int id); }
/**
* @param context The context that this cache is created in.
* @param resourceId The resource that this value might be defined in.
......@@ -139,10 +145,12 @@ public class TabContentManager {
/**
* @param context The context that this cache is created in.
* @param contentOffsetProvider The provider of content parameter.
* @param tabFinder The helper function to get tab from an ID.
*/
public TabContentManager(Context context, ContentOffsetProvider contentOffsetProvider,
boolean snapshotsEnabled) {
boolean snapshotsEnabled, TabFinder tabFinder) {
mContentOffsetProvider = contentOffsetProvider;
mTabFinder = tabFinder;
mSnapshotsEnabled = snapshotsEnabled;
// Override the cache size on the command line with --thumbnails=100
......@@ -327,32 +335,37 @@ public class TabContentManager {
/**
* Call to get a thumbnail for a given tab through a {@link Callback}. If there is
* no up-to-date thumbnail on disk for the given tab, callback returns null.
* @param tab The tab to get the thumbnail for.
* @param tabId The ID of the tab to get the thumbnail for.
* @param callback The callback to send the {@link Bitmap} with. Can be called up to twice when
* {@code forceUpdate}; otherwise always called exactly once.
* @param forceUpdate Whether to obtain the thumbnail from the live content.
* @param writeBack When {@code forceUpdate}, whether to write the thumbnail to cache.
*/
public void getTabThumbnailWithCallback(@NonNull Tab tab, @NonNull Callback<Bitmap> callback,
public void getTabThumbnailWithCallback(@NonNull int tabId, @NonNull Callback<Bitmap> callback,
boolean forceUpdate, boolean writeBack) {
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
if (!mSnapshotsEnabled) return;
if (!forceUpdate) {
assert !writeBack : "writeBack is ignored if not forceUpdate";
getTabThumbnailFromDisk(tab, callback);
getTabThumbnailFromDisk(tabId, callback);
return;
}
if (mNativeTabContentManager == 0) return;
// Reading thumbnail from disk is faster than taking screenshot from live Tab, so fetch
// that first even if |forceUpdate|.
getTabThumbnailFromDisk(tab, (diskBitmap) -> {
getTabThumbnailFromDisk(tabId, (diskBitmap) -> {
if (diskBitmap != null) callback.onResult(diskBitmap);
Tab tab = mTabFinder.getTabById(tabId);
if (tab == null) return;
captureThumbnail(tab, writeBack, (bitmap) -> {
// Null check to avoid having a Bitmap from getTabThumbnailFromDisk() but
// cleared here.
// If invalidation is not needed, readbackNativeBitmap() might not do anything and
// send back null.
// If invalidation is not needed, readbackNativeBitmap() might not do anything
// and send back null.
if (bitmap != null) {
callback.onResult(bitmap);
}
......@@ -369,11 +382,11 @@ public class TabContentManager {
}
/**
* @param tab The {@link Tab} the thumbnail is for.
* @param tabId The ID of the {@link Tab} the thumbnail is for.
* @return The file storing the thumbnail in JPEG format of a certain {@link Tab}.
*/
public static File getTabThumbnailFileJpeg(Tab tab) {
return new File(PathUtils.getThumbnailCacheDirectory(), tab.getId() + ".jpeg");
public static File getTabThumbnailFileJpeg(int tabId) {
return new File(PathUtils.getThumbnailCacheDirectory(), tabId + ".jpeg");
}
/**
......@@ -402,27 +415,27 @@ public class TabContentManager {
}
@VisibleForTesting
public static Bitmap getJpegForTab(Tab tab) {
File file = getTabThumbnailFileJpeg(tab);
public static Bitmap getJpegForTab(int tabId) {
File file = getTabThumbnailFileJpeg(tabId);
if (!file.isFile()) return null;
return BitmapFactory.decodeFile(file.getPath());
}
private void getTabThumbnailFromDisk(@NonNull Tab tab, @NonNull Callback<Bitmap> callback) {
private void getTabThumbnailFromDisk(@NonNull int tabId, @NonNull Callback<Bitmap> callback) {
mOnTheFlyRequests++;
mRequests++;
// Try JPEG thumbnail first before using the more costly
// TabContentManagerJni.get().getEtc1TabThumbnail.
TraceEvent.startAsync("GetTabThumbnailFromDisk", tab.getId());
TraceEvent.startAsync("GetTabThumbnailFromDisk", tabId);
new AsyncTask<Bitmap>() {
@Override
public Bitmap doInBackground() {
return getJpegForTab(tab);
return getJpegForTab(tabId);
}
@Override
public void onPostExecute(Bitmap jpeg) {
TraceEvent.finishAsync("GetTabThumbnailFromDisk", tab.getId());
TraceEvent.finishAsync("GetTabThumbnailFromDisk", tabId);
mOnTheFlyRequests--;
if (mOnTheFlyRequests == 0 && !mLastThumbnailHappened) {
mLastThumbnailHappened = true;
......@@ -436,16 +449,16 @@ public class TabContentManager {
: 1.0 * jpeg.getWidth() / jpeg.getHeight();
// Retry fetching thumbnail once for all tabs that are:
// * Thumbnail's aspect ratio is different from the expected ratio.
if (!mRefectchedTabIds.contains(tab.getId())
if (!mRefectchedTabIds.contains(tabId)
&& Math.abs(jpegAspectRatio - mExpectedThumbnailAspectRatio)
>= ASPECT_RATIO_PRECISION) {
RecordHistogram.recordEnumeratedHistogram(UMA_THUMBNAIL_FETCHING_RESULT,
ThumbnailFetchingResult.GOT_DIFFERENT_ASPECT_RATIO_JPEG,
ThumbnailFetchingResult.NUM_ENTRIES);
mRefectchedTabIds.add(tab.getId());
mRefectchedTabIds.add(tabId);
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
TabContentManagerJni.get().getEtc1TabThumbnail(mNativeTabContentManager,
TabContentManager.this, tab.getId(),
TabContentManager.this, tabId,
(etc1) -> callback.onResult(etc1));
return;
}
......@@ -458,7 +471,7 @@ public class TabContentManager {
}
if (mNativeTabContentManager == 0 || !mSnapshotsEnabled) return;
TabContentManagerJni.get().getEtc1TabThumbnail(
mNativeTabContentManager, TabContentManager.this, tab.getId(), (etc1) -> {
mNativeTabContentManager, TabContentManager.this, tabId, (etc1) -> {
if (etc1 != null) {
RecordHistogram.recordEnumeratedHistogram(
UMA_THUMBNAIL_FETCHING_RESULT,
......
......@@ -78,8 +78,8 @@ public class TabModelSelectorObserverTestRule extends ChromeBrowserTestRule {
};
TabModelOrderController orderController = new TabModelOrderControllerImpl(mSelector);
TabContentManager tabContentManager =
new TabContentManager(InstrumentationRegistry.getTargetContext(), null, false);
TabContentManager tabContentManager = new TabContentManager(
InstrumentationRegistry.getTargetContext(), null, false, mSelector::getTabById);
TabPersistencePolicy persistencePolicy = new TabbedModeTabPersistencePolicy(0, false);
TabPersistentStore tabPersistentStore =
new TabPersistentStore(persistencePolicy, mSelector, null, null);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment