Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:
description: 'JDK version'
required: false
type: string
default: "17"
default: "21"
jdk_distribution:
description: 'JDK distribution'
required: false
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ android {

java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
languageVersion = JavaLanguageVersion.of(21)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@
import org.chromium.base.PathUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.components.signin.AccountManagerFacadeImpl;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.SystemAccountManagerDelegate;
import org.chromium.content_public.browser.BrowserStartupController;
import org.chromium.content_public.browser.DeviceUtils;
import org.chromium.ui.base.ResourceBundle;
Expand Down Expand Up @@ -191,27 +188,32 @@ private void initBrowserProcess(Context context) {
CommandLine.init(new String[] {});
if (BuildConfig.DEBUG)
CommandLine.getInstance().appendSwitchWithValue("enable-logging", "stderr");

// Disable AndroidUseCorrectWindowBounds (enabled by the field-trial testing config in
// M14x): it makes GetBoundsInRootWindow / window.outerWidth/Height report the physical
// Android panel instead of the content surface. Wolvic renders to an offscreen VR
// texture larger than the panel, and YouTube sizes its fullscreen <video> against
// window.outerWidth/Height -- so the panel value shrinks the video. The view (surface)
// bounds are the correct "window" bounds for us (the pre-M14x behaviour).
String disableFeatures = "AndroidUseCorrectWindowBounds";
if (BuildConfig.FLAVOR_abi == "x64")
CommandLine.getInstance().appendSwitchWithValue("disable-features", "Vulkan");
disableFeatures += ",Vulkan";
CommandLine.getInstance().appendSwitchWithValue("disable-features", disableFeatures);
Comment thread
svillar marked this conversation as resolved.

// Enable WebXR Hand Input, which is disabled by default in blink (experimental)
CommandLine.getInstance().appendSwitchWithValue("enable-features", "WebXRHandInput");

setupWebGLMSAA();
DeviceUtils.addDeviceSpecificUserAgentSwitch();
DeviceUtils.updateDeviceSpecificUserAgentSwitch(context);
LibraryLoader.getInstance().ensureInitialized();

// Initialize the AccountManagerFacade with the correct AccountManagerDelegate. Must be done
// only once and before AccountManagerFacadeProvider.getInstance() is invoked.
AccountManagerFacadeProvider.setInstance(
new AccountManagerFacadeImpl(new SystemAccountManagerDelegate()));

BrowserStartupController.getInstance().startBrowserProcessesAsync(
LibraryProcessType.PROCESS_BROWSER, true /* startGpuProcess */, false /* startMinimalBrowser */,
false /* singleProcess */, false /* scheduleFlushStartupTasks */,
new BrowserStartupController.StartupCallback() {
@Override
public void onSuccess() {
Log.i(LOGTAG, "The browser process started!");
public void onSuccess(BrowserStartupController.StartupMetrics metrics) {
Log.i(LOGTAG, "The browser process started!" + metrics.getTotalDurationOfPostedTasksMs());
Comment thread
svillar marked this conversation as resolved.
mIsReady = true;
mCallbacks.forEach(callback -> {
callback.onReady();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.igalia.wolvic.browser.api.WSessionState;
import com.igalia.wolvic.browser.api.WTextInput;
import com.igalia.wolvic.browser.api.WWebResponse;
import org.chromium.content_public.browser.Visibility;
import org.chromium.content_public.browser.WebContents;
import org.chromium.wolvic.DownloadManagerBridge;
import org.chromium.wolvic.PasswordForm;
Expand Down Expand Up @@ -130,9 +131,9 @@ public void setActive(boolean active) {
assert mTab.getActiveWebContents() != null;
WebContents webContents = mTab.getActiveWebContents();
if (active) {
webContents.onShow();
webContents.updateWebContentsVisibility(Visibility.VISIBLE);
} else {
webContents.onHide();
webContents.updateWebContentsVisibility(Visibility.HIDDEN);
webContents.suspendAllMediaPlayers();
}
webContents.setAudioMuted(!active);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import androidx.annotation.NonNull;

import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.MediaSessionObserver;
import org.chromium.content_public.browser.MediaSession;
import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.wolvic.Tab;
Expand All @@ -15,6 +15,7 @@
* Controls a single tab content in a browser for chromium backend.
*/
public class TabImpl extends Tab {
private SessionImpl mSession;
private TabMediaSessionObserver mTabMediaSessionObserver;
private TabWebContentsDelegate mTabWebContentsDelegate;
private TabWebContentsObserver mWebContentsObserver;
Expand All @@ -30,25 +31,39 @@ public TabImpl(@NonNull Context context, @NonNull SessionImpl session, WebConten
}

private void registerCallbacks(@NonNull SessionImpl session) {
mTabMediaSessionObserver = new TabMediaSessionObserver(mWebContents, session);
mSession = session;

mTabWebContentsDelegate = new TabWebContentsDelegate(session, mWebContents);
setWebContentsDelegate(mWebContents, mTabWebContentsDelegate);

mWebContentsObserver = new TabWebContentsObserver(this, session);

// The native MediaSession is created lazily (on first media use), so it usually does not
// exist yet and MediaSession.fromWebContents() returns null. If it already exists, observe
// it now; otherwise TabWebContentsObserver.mediaSessionCreated() does it when the session
// appears. The session is created at most once per WebContents.
MediaSession mediaSession = MediaSession.fromWebContents(mWebContents);
if (mediaSession != null)
createMediaSessionObserver(mediaSession);

SelectionPopupController controller =
SelectionPopupController.fromWebContents(mWebContents);
controller.setDelegate(
new SelectionPopupControllerDelegate(mWebContents,
controller.getDelegateEventHandler(), session));
}

/* package */ void createMediaSessionObserver(@NonNull MediaSession mediaSession) {
mTabMediaSessionObserver = new TabMediaSessionObserver(mediaSession, mWebContents, mSession);
}

public void exitFullScreen() {
mWebContents.exitFullscreen();
}

public void onMediaFullscreen(boolean isFullscreen) {
mTabMediaSessionObserver.onMediaFullscreen(isFullscreen);
if (mTabMediaSessionObserver != null)
mTabMediaSessionObserver.onMediaFullscreen(isFullscreen);
}

public void purgeHistory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ public class TabMediaSessionObserver extends MediaSessionObserver implements Med
private boolean mIsSuspended = false;
private boolean mRunUpdatingPositionTask = false;

public TabMediaSessionObserver(@NonNull WebContents webContents, @NonNull SessionImpl session) {
super(MediaSession.fromWebContents(webContents));
public TabMediaSessionObserver(@NonNull MediaSession mediaSession,
@NonNull WebContents webContents, @NonNull SessionImpl session) {
super(mediaSession);

mSession = session;
mMediaImageManager =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
import org.chromium.components.find_in_page.FindMatchRectsDetails;
import org.chromium.components.find_in_page.FindNotificationDetails;
import org.chromium.content_public.browser.InvalidateTypes;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents;
import org.chromium.url.GURL;
import org.chromium.wolvic.Tab;
import org.chromium.wolvic.WolvicWebContentsDelegate;
import org.json.JSONException;
import org.json.JSONObject;
Expand Down Expand Up @@ -55,7 +53,7 @@ public boolean takeFocus(boolean reverse) {
}

@Override
public void enterFullscreenModeForTab(boolean prefersNavigationBar, boolean prefersStatusBar) {
public void enterFullscreenModeForTab(RenderFrameHost renderFrameHost, boolean prefersNavigationBar, boolean prefersStatusBar, long displayId) {
@Nullable WSession.ContentDelegate delegate = mSession.getContentDelegate();
if (delegate == null) return;

Expand Down Expand Up @@ -148,26 +146,6 @@ public void closeContents() {
}, 0);
}

@Override
public void onUpdateUrl(GURL url) {
String newUrl = YoutubeUrlHelper.maybeRewriteYoutubeURL(url);
// If mobile Youtube URL is detected, redirect to the desktop version.
if (!url.getSpec().equals(newUrl)) {
LoadUrlParams params = new LoadUrlParams(newUrl);
mWebContents.getNavigationController().setEntryExtraData(
mWebContents.getNavigationController().getLastCommittedEntryIndex(),
Tab.NAVIGATION_ENTRY_MARKED_AS_SKIPPED_KEY,
Tab.NAVIGATION_ENTRY_MARKED_AS_SKIPPED_VALUE);
mWebContents.getNavigationController().loadUrl(params);
return;
}

WSession.NavigationDelegate delegate = mSession.getNavigationDelegate();
if (delegate != null) {
delegate.onLocationChange(mSession, mWebContents.getVisibleUrl().getSpec());
}
}

@Override
public void showRepostFormWarningDialog() {
mSession.getChromiumPromptDelegate().onRepostConfirmWarningDialog().then(result -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
import com.igalia.wolvic.browser.api.WWebRequestError;

import org.chromium.base.ContextUtils;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content_public.browser.LifecycleState;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.MediaSession;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
Expand Down Expand Up @@ -63,6 +66,25 @@ public void didRedirectNavigation(NavigationHandle navigationHandle) {
public void didStartNavigationInPrimaryMainFrame(NavigationHandle navigationHandle) {
super.didStartNavigationInPrimaryMainFrame(navigationHandle);

// Rewrite mobile YouTube watch URLs to their desktop variant before the request
// is initiated. The load is deferred because starting a navigation synchronously
// from a navigation callback can corrupt the native navigation objects (same
// reason onCreateNewWindow/closeContents post their work).
GURL url = navigationHandle.getUrl();
String rewrittenUrl = YoutubeUrlHelper.maybeRewriteYoutubeURL(url);
if (!url.getSpec().equals(rewrittenUrl)) {
WebContents webContents = getWebContents();
if (webContents != null) {
PostTask.postDelayedTask(TaskTraits.UI_DEFAULT, () -> {
if (!webContents.isDestroyed()) {
webContents.getNavigationController().loadUrl(
new LoadUrlParams(rewrittenUrl));
}
}, 0);
}
return;
}

WSession.NavigationDelegate delegate = mSession.getNavigationDelegate();
if (delegate == null)
return;
Expand Down Expand Up @@ -130,7 +152,7 @@ public void onCreateNewPaymentHandler(final WebContents newWebContents) {
return;
}

Context context = mWebContents.get().getTopLevelNativeWindow().getContext().get();
Context context = getWebContents().getTopLevelNativeWindow().getContext().get();
PaymentRequestUI paymentHandler = new PaymentRequestUI(context, newWebContents, null);
final TabCompositorView compositorView = paymentHandler.getCompositorView();
assert newWebContents.getViewAndroidDelegate() != null
Expand All @@ -154,16 +176,16 @@ public void onCreateNewPaymentHandler(final WebContents newWebContents) {
// Show Compositor View after attaching to the parent view.
compositorView.setCurrentWebContents(newWebContents);

mPaymentWebContentsObserver = new WebContentsObserver(newWebContents) {
mPaymentWebContentsObserver = new WebContentsObserver() {
@Override
public void destroy() {
public void webContentsDestroyed() {
mSession.releaseOverlayDisplay(compositorView);
mTab.setPaymentWebContents(null, null, null);

contentDelegate.onHidePaymentHandler(mSession);
newWebContents.removeObserver(this);
}
};
mPaymentWebContentsObserver.observe(newWebContents);
}

@Override
Expand Down Expand Up @@ -201,7 +223,7 @@ public X509Certificate certificate() {
private void dispatchCanGoBackOrForward() {
@Nullable WSession.NavigationDelegate delegate = mSession.getNavigationDelegate();
if (delegate != null) {
WebContents webContents = mWebContents.get();
WebContents webContents = getWebContents();
if (webContents == null)
return;

Expand Down Expand Up @@ -230,4 +252,9 @@ public void didFirstVisuallyNonEmptyPaint() {
public void hasEffectivelyFullscreenVideoChange(boolean isFullscreen) {
mSession.getTab().onMediaFullscreen(isFullscreen);
}

@Override
public void mediaSessionCreated(MediaSession mediaSession) {
mTab.createMediaSessionObserver(mediaSession);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ public static String maybeRewriteYoutubeURL(GURL url) {
return url.getSpec();
}

return ensureAppIsSetToDesktop(Uri.parse(url.getSpec())).toString();
// Already in desktop mode: return the spec verbatim so callers can rely on
// this being idempotent and never re-trigger a navigation.
Uri uri = Uri.parse(url.getSpec());
if ("desktop".equals(uri.getQueryParameter("app"))) {
return url.getSpec();
}

return ensureAppIsSetToDesktop(uri).toString();
}

private static Uri ensureAppIsSetToDesktop(Uri uri) {
Expand Down
Loading