Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit d6079e9

Browse files
authored
Add WebXR settings and permission flow (#3019)
1 parent 8cd832d commit d6079e9

39 files changed

+915
-316
lines changed

app/src/common/shared/org/mozilla/vrbrowser/browser/PermissionDelegate.java

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,31 @@
22

33
import android.Manifest;
44
import android.app.Activity;
5+
import android.app.Application;
56
import android.content.Context;
67
import android.content.pm.PackageManager;
78
import android.util.Log;
89

910
import androidx.annotation.NonNull;
11+
import androidx.annotation.Nullable;
12+
import androidx.lifecycle.Observer;
1013

1114
import org.mozilla.geckoview.GeckoSession;
1215
import org.mozilla.vrbrowser.PlatformActivity;
1316
import org.mozilla.vrbrowser.R;
17+
import org.mozilla.vrbrowser.browser.engine.Session;
18+
import org.mozilla.vrbrowser.browser.engine.SessionState;
1419
import org.mozilla.vrbrowser.browser.engine.SessionStore;
20+
import org.mozilla.vrbrowser.db.SitePermission;
21+
import org.mozilla.vrbrowser.ui.viewmodel.SitePermissionViewModel;
1522
import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate;
1623
import org.mozilla.vrbrowser.ui.widgets.dialogs.PermissionWidget;
17-
import org.mozilla.vrbrowser.utils.DeviceType;
1824
import org.mozilla.vrbrowser.utils.SystemUtils;
25+
import org.mozilla.vrbrowser.utils.UrlUtils;
1926

2027
import java.util.ArrayList;
2128
import java.util.Arrays;
29+
import java.util.List;
2230

2331
public class PermissionDelegate implements GeckoSession.PermissionDelegate, WidgetManagerDelegate.PermissionListener {
2432

@@ -30,12 +38,16 @@ public class PermissionDelegate implements GeckoSession.PermissionDelegate, Widg
3038
private WidgetManagerDelegate mWidgetManager;
3139
private GeckoSession.PermissionDelegate.Callback mCallback;
3240
private PermissionWidget mPermissionWidget;
41+
private SitePermissionViewModel mSitePermissionModel;
42+
private List<SitePermission> mSitePermissions;
3343

3444
public PermissionDelegate(Context aContext, WidgetManagerDelegate aWidgetManager) {
3545
mContext = aContext;
3646
mWidgetManager = aWidgetManager;
3747
mWidgetManager.addPermissionListener(this);
3848
SessionStore.get().setPermissionDelegate(this);
49+
mSitePermissionModel = new SitePermissionViewModel((Application)aContext.getApplicationContext());
50+
mSitePermissionModel.getAll().observeForever(mSitePermissionObserver);
3951
}
4052

4153
public void setParentWidgetHandle(int aHandle) {
@@ -72,7 +84,37 @@ public void handlePermission(final String aUri, final PermissionWidget.Permissio
7284
mPermissionWidget.showPrompt(aUri, aType, aCallback);
7385
}
7486

87+
private Observer<List<SitePermission>> mSitePermissionObserver = sites -> {
88+
mSitePermissions = sites;
89+
};
90+
91+
void handleWebXRPermission(GeckoSession aGeckoSession, final String aUri, final Callback aCallback) {
92+
Session session = SessionStore.get().getSession(aGeckoSession);
93+
if (session == null || !SettingsStore.getInstance(mContext).isWebXREnabled()) {
94+
aCallback.reject();
95+
return;
96+
}
97+
final String domain = UrlUtils.getHost(aUri);
98+
99+
@Nullable SitePermission site = null;
100+
if (mSitePermissions != null) {
101+
site = mSitePermissions.stream()
102+
.filter(sitePermission -> sitePermission.category == SitePermission.SITE_PERMISSION_WEBXR &&
103+
sitePermission.url.equalsIgnoreCase(domain))
104+
.findFirst().orElse(null);
105+
}
106+
107+
if (site == null || site.allowed) {
108+
aCallback.grant();
109+
session.setWebXRState(SessionState.WEBXR_ALLOWED);
110+
} else {
111+
aCallback.reject();
112+
session.setWebXRState(SessionState.WEBXR_BLOCKED);
113+
}
114+
}
115+
75116
public void release() {
117+
mSitePermissionModel.getAll().removeObserver(mSitePermissionObserver);
76118
mWidgetManager.removePermissionListener(this);
77119
SessionStore.get().setPermissionDelegate(null);
78120
mCallback = null;
@@ -116,7 +158,7 @@ public void onAndroidPermissionsRequest(GeckoSession aSession, String[] permissi
116158
public void onContentPermissionRequest(GeckoSession aSession, String aUri, int aType, Callback callback) {
117159
Log.d(LOGTAG, "onContentPermissionRequest: " + aUri + " " + aType);
118160
if (aType == PERMISSION_XR) {
119-
callback.grant();
161+
handleWebXRPermission(aSession, aUri, callback);
120162
return;
121163
}
122164

@@ -231,7 +273,24 @@ public void reject() {
231273
}
232274
}
233275

234-
public boolean isPermissionDialogVisible() {
235-
return mPermissionWidget != null && mPermissionWidget.isVisible();
276+
public void setPermissionAllowed(String uri, @SitePermission.Category int category, boolean allowed) {
277+
@Nullable SitePermission site = mSitePermissions.stream()
278+
.filter((item) -> item.category == category && item.url.equals(uri))
279+
.findFirst().orElse(null);
280+
boolean wasAllowed = site == null || site.allowed;
281+
if (allowed == wasAllowed) {
282+
return;
283+
}
284+
if (allowed) {
285+
mSitePermissions.removeIf(sitePermission -> sitePermission.url.equals(uri));
286+
mSitePermissionModel.deleteSite(site);
287+
} else {
288+
if (site == null) {
289+
site = new SitePermission(uri, false, SitePermission.SITE_PERMISSION_WEBXR);
290+
mSitePermissions.add(site);
291+
}
292+
site.allowed = false;
293+
mSitePermissionModel.insertSite(site);
294+
}
236295
}
237296
}

app/src/common/shared/org/mozilla/vrbrowser/browser/PromptDelegate.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
import org.mozilla.vrbrowser.R;
1818
import org.mozilla.vrbrowser.VRBrowserApplication;
1919
import org.mozilla.vrbrowser.browser.engine.Session;
20-
import org.mozilla.vrbrowser.db.PopUpSite;
21-
import org.mozilla.vrbrowser.ui.viewmodel.PopUpsViewModel;
20+
import org.mozilla.vrbrowser.db.SitePermission;
21+
import org.mozilla.vrbrowser.ui.viewmodel.SitePermissionViewModel;
2222
import org.mozilla.vrbrowser.ui.widgets.UIWidget;
2323
import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement;
2424
import org.mozilla.vrbrowser.ui.widgets.WindowWidget;
2525
import org.mozilla.vrbrowser.ui.widgets.dialogs.PopUpBlockDialogWidget;
26+
import org.mozilla.vrbrowser.ui.widgets.dialogs.QuickPermissionWidget;
2627
import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget;
2728
import org.mozilla.vrbrowser.ui.widgets.prompts.AuthPromptWidget;
2829
import org.mozilla.vrbrowser.ui.widgets.prompts.ChoicePromptWidget;
@@ -51,15 +52,15 @@ public interface PopUpDelegate {
5152
private ConfirmPromptWidget mSlowScriptPrompt;
5253
private Context mContext;
5354
private WindowWidget mAttachedWindow;
54-
private List<PopUpSite> mAllowedPopUpSites;
55-
private PopUpsViewModel mViewModel;
55+
private List<SitePermission> mAllowedPopUpSites;
56+
private SitePermissionViewModel mViewModel;
5657
private AppExecutors mExecutors;
5758
private PopUpDelegate mPopupDelegate;
5859

5960
public PromptDelegate(@NonNull Context context) {
6061
mContext = context;
6162
mExecutors = ((VRBrowserApplication)context.getApplicationContext()).getExecutors();
62-
mViewModel = new PopUpsViewModel(((Application)context.getApplicationContext()));
63+
mViewModel = new SitePermissionViewModel(((Application)context.getApplicationContext()));
6364
mAllowedPopUpSites = new ArrayList<>();
6465
}
6566

@@ -71,7 +72,7 @@ public void attachToWindow(@NonNull WindowWidget window) {
7172

7273
mAttachedWindow = window;
7374
mAttachedWindow.addWindowListener(this);
74-
mViewModel.getAll().observeForever(mObserver);
75+
mViewModel.getAll(SitePermission.SITE_PERMISSION_POPUP).observeForever(mPopUpSiteObserver);
7576

7677
if (getSession() != null) {
7778
setUpSession(getSession());
@@ -87,7 +88,7 @@ public void detachFromWindow() {
8788
mAttachedWindow.removeWindowListener(this);
8889
mAttachedWindow = null;
8990
}
90-
mViewModel.getAll().removeObserver(mObserver);
91+
mViewModel.getAll(SitePermission.SITE_PERMISSION_POPUP).removeObserver(mPopUpSiteObserver);
9192

9293
clearPopUps();
9394
}
@@ -262,8 +263,8 @@ public void dismiss() {
262263
return result;
263264
}
264265

265-
private Observer<List<PopUpSite>> mObserver = popUpSites -> {
266-
mAllowedPopUpSites = popUpSites;
266+
private Observer<List<SitePermission>> mPopUpSiteObserver = sites -> {
267+
mAllowedPopUpSites = sites;
267268
};
268269

269270
@Nullable
@@ -278,7 +279,7 @@ public GeckoResult<PromptResponse> onPopupPrompt(@NonNull GeckoSession geckoSess
278279
final int sessionId = geckoSession.hashCode();
279280
final String uri = mAttachedWindow.getSession().getCurrentUri();
280281

281-
Optional<PopUpSite> site = mAllowedPopUpSites.stream().filter((item) -> item.url.equals(uri)).findFirst();
282+
Optional<SitePermission> site = mAllowedPopUpSites.stream().filter((item) -> item.url.equals(uri)).findFirst();
282283
if (site.isPresent()) {
283284
mAttachedWindow.postDelayed(() -> {
284285
if (site.get().allowed) {
@@ -349,15 +350,16 @@ public boolean hasPendingPopUps(GeckoSession session) {
349350

350351
private void showPopUp(int sessionId, @NonNull Pair<String, LinkedList<PopUpRequest>> requests) {
351352
String uri = requests.first;
352-
Optional<PopUpSite> site = mAllowedPopUpSites.stream().filter((item) -> item.url.equals(uri)).findFirst();
353+
Optional<SitePermission> site = mAllowedPopUpSites.stream().filter((item) -> item.url.equals(uri)).findFirst();
353354
if (!site.isPresent()) {
354355
mPopUpPrompt = new PopUpBlockDialogWidget(mContext);
355356
mPopUpPrompt.setButtonsDelegate(index -> {
356357
boolean allowed = index != PopUpBlockDialogWidget.NEGATIVE;
357358
boolean askAgain = mPopUpPrompt.askAgain();
358359
if (allowed && !askAgain) {
359-
mAllowedPopUpSites.add(new PopUpSite(uri, allowed));
360-
mViewModel.insertSite(uri, allowed);
360+
SitePermission permission = new SitePermission(uri, allowed, SitePermission.SITE_PERMISSION_POPUP);
361+
mAllowedPopUpSites.add(permission);
362+
mViewModel.insertSite(permission);
361363
}
362364

363365
if (allowed) {

app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ SettingsStore getInstance(final @NonNull Context aContext) {
7676
public final static boolean AUTOPLAY_ENABLED = false;
7777
public final static boolean DEBUG_LOGGING_DEFAULT = false;
7878
public final static boolean POP_UPS_BLOCKING_DEFAULT = true;
79+
public final static boolean WEBXR_ENABLED_DEFAULT = true;
7980
public final static boolean TELEMETRY_STATUS_UPDATE_SENT_DEFAULT = false;
8081
public final static boolean BOOKMARKS_SYNC_DEFAULT = true;
8182
public final static boolean HISTORY_SYNC_DEFAULT = true;
@@ -593,6 +594,16 @@ public void setPopUpsBlockingEnabled(boolean isEnabled) {
593594
editor.commit();
594595
}
595596

597+
public boolean isWebXREnabled() {
598+
return mPrefs.getBoolean(mContext.getString(R.string.settings_key_webxr), WEBXR_ENABLED_DEFAULT);
599+
}
600+
601+
public void setWebXREnabled(boolean isEnabled) {
602+
SharedPreferences.Editor editor = mPrefs.edit();
603+
editor.putBoolean(mContext.getString(R.string.settings_key_webxr), isEnabled);
604+
editor.commit();
605+
}
606+
596607
public void setWhatsNewDisplayed(boolean isEnabled) {
597608
SharedPreferences.Editor editor = mPrefs.edit();
598609
editor.putBoolean(mContext.getString(R.string.settings_key_whats_new_displayed), isEnabled);

app/src/common/shared/org/mozilla/vrbrowser/browser/engine/Session.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public class Session implements ContentBlocking.Delegate, GeckoSession.Navigatio
7474
private transient CopyOnWriteArrayList<VideoAvailabilityListener> mVideoAvailabilityListeners;
7575
private transient CopyOnWriteArrayList<BitmapChangedListener> mBitmapChangedListeners;
7676
private transient CopyOnWriteArrayList<GeckoSession.SelectionActionDelegate> mSelectionActionListeners;
77+
private transient CopyOnWriteArrayList<WebXRStateChangedListener> mWebXRStateListeners;
7778

7879
private SessionState mState;
7980
private transient CopyOnWriteArrayList<Runnable> mQueuedCalls = new CopyOnWriteArrayList<>();
@@ -91,6 +92,10 @@ public interface BitmapChangedListener {
9192
void onBitmapChanged(Session aSession, Bitmap aBitmap);
9293
}
9394

95+
public interface WebXRStateChangedListener {
96+
void onWebXRStateChanged(Session aSession, @SessionState.WebXRState int aWebXRState);
97+
}
98+
9499
@IntDef(value = { SESSION_OPEN, SESSION_DO_NOT_OPEN})
95100
public @interface SessionOpenModeFlags {}
96101
public static final int SESSION_OPEN = 0;
@@ -121,6 +126,7 @@ private void initialize() {
121126
mVideoAvailabilityListeners = new CopyOnWriteArrayList<>();
122127
mSelectionActionListeners = new CopyOnWriteArrayList<>();
123128
mBitmapChangedListeners = new CopyOnWriteArrayList<>();
129+
mWebXRStateListeners = new CopyOnWriteArrayList<>();
124130

125131
if (mPrefs != null) {
126132
mPrefs.registerOnSharedPreferenceChangeListener(this);
@@ -165,6 +171,7 @@ protected void shutdown() {
165171
mVideoAvailabilityListeners.clear();
166172
mSelectionActionListeners.clear();
167173
mBitmapChangedListeners.clear();
174+
mWebXRStateListeners.clear();
168175

169176
if (mPrefs != null) {
170177
mPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -185,6 +192,10 @@ private void dumpAllState() {
185192
for (VideoAvailabilityListener listener: mVideoAvailabilityListeners) {
186193
dumpState(listener);
187194
}
195+
196+
for (WebXRStateChangedListener listener: mWebXRStateListeners) {
197+
dumpState(listener);
198+
}
188199
}
189200

190201
private void dumpState(GeckoSession.NavigationDelegate aListener) {
@@ -215,6 +226,10 @@ private void dumpState(VideoAvailabilityListener aListener) {
215226
aListener.onVideoAvailabilityChanged(mState.mMediaElements != null && mState.mMediaElements.size() > 0);
216227
}
217228

229+
private void dumpState(WebXRStateChangedListener aListener) {
230+
aListener.onWebXRStateChanged(this, mState.mWebXRState);
231+
}
232+
218233
private void flushQueuedEvents() {
219234
for (Runnable call: mQueuedCalls) {
220235
call.run();
@@ -302,6 +317,15 @@ public void removeBitmapChangedListener(BitmapChangedListener aListener) {
302317
mBitmapChangedListeners.remove(aListener);
303318
}
304319

320+
public void addWebXRStateChangedListener(WebXRStateChangedListener aListener) {
321+
mWebXRStateListeners.add(aListener);
322+
dumpState(aListener);
323+
}
324+
325+
public void removeWebXRStateChangedListener(WebXRStateChangedListener aListener) {
326+
mWebXRStateListeners.remove(aListener);
327+
}
328+
305329
private void setupSessionListeners(GeckoSession aSession) {
306330
aSession.setNavigationDelegate(this);
307331
aSession.setProgressDelegate(this);
@@ -757,6 +781,15 @@ public boolean isPrivateMode() {
757781
return false;
758782
}
759783

784+
public void setWebXRState(@SessionState.WebXRState int aWebXRState) {
785+
if (aWebXRState != mState.mWebXRState) {
786+
mState.mWebXRState = aWebXRState;
787+
for (WebXRStateChangedListener listener: mWebXRStateListeners) {
788+
dumpState(listener);
789+
}
790+
}
791+
}
792+
760793
// Session Settings
761794

762795
protected void setServo(final boolean enabled) {
@@ -993,6 +1026,7 @@ public void onPageStart(@NonNull GeckoSession aSession, @NonNull String aUri) {
9931026
TelemetryWrapper.startPageLoadTime();
9941027
GleanMetricsService.startPageLoadTime();
9951028

1029+
setWebXRState(SessionState.WEBXR_UNUSED);
9961030
for (GeckoSession.ProgressDelegate listener : mProgressListeners) {
9971031
listener.onPageStart(aSession, aUri);
9981032
}

app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionState.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.mozilla.vrbrowser.browser.engine;
22

3+
import androidx.annotation.IntDef;
4+
35
import com.google.gson.Gson;
46
import com.google.gson.JsonParser;
57
import com.google.gson.TypeAdapter;
@@ -20,6 +22,12 @@
2022

2123
@JsonAdapter(SessionState.SessionStateAdapterFactory.class)
2224
public class SessionState {
25+
@IntDef(value = { WEBXR_UNUSED, WEBXR_ALLOWED, WEBXR_BLOCKED})
26+
public @interface WebXRState {}
27+
public static final int WEBXR_UNUSED = 0;
28+
public static final int WEBXR_ALLOWED = 1;
29+
public static final int WEBXR_BLOCKED = 2;
30+
2331
private transient boolean mIsActive;
2432
public boolean mCanGoBack;
2533
public boolean mCanGoForward;
@@ -34,6 +42,7 @@ public class SessionState {
3442
public transient GeckoDisplay mDisplay;
3543
public SessionSettings mSettings;
3644
public transient ArrayList<Media> mMediaElements = new ArrayList<>();
45+
public transient @WebXRState int mWebXRState = WEBXR_UNUSED;
3746
@JsonAdapter(SessionState.GeckoSessionStateAdapter.class)
3847
public GeckoSession.SessionState mSessionState;
3948
public long mLastUse;

app/src/common/shared/org/mozilla/vrbrowser/browser/engine/SessionStore.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.mozilla.vrbrowser.browser.HistoryStore;
1717
import org.mozilla.vrbrowser.browser.PermissionDelegate;
1818
import org.mozilla.vrbrowser.browser.Services;
19+
import org.mozilla.vrbrowser.db.SitePermission;
1920
import org.mozilla.vrbrowser.utils.SystemUtils;
2021

2122
import java.util.ArrayList;
@@ -136,6 +137,10 @@ public void suspendAllInactiveSessions() {
136137
return mSessions.stream().filter(session -> session.getId().equals(aId)).findFirst().orElse(null);
137138
}
138139

140+
public @Nullable Session getSession(GeckoSession aGeckoSession) {
141+
return mSessions.stream().filter(session -> session.getGeckoSession() == aGeckoSession).findFirst().orElse(null);
142+
}
143+
139144
public void setActiveSession(Session aSession) {
140145
if (aSession != null) {
141146
aSession.setActive(true);
@@ -311,4 +316,10 @@ public void onMediaPermissionRequest(@NonNull GeckoSession session, @NonNull Str
311316
mPermissionDelegate.onMediaPermissionRequest(session, uri, video, audio, callback);
312317
}
313318
}
319+
320+
public void setPermissionAllowed(String uri, @SitePermission.Category int category, boolean allowed) {
321+
if (mPermissionDelegate != null) {
322+
mPermissionDelegate.setPermissionAllowed(uri, category, allowed);
323+
}
324+
}
314325
}

0 commit comments

Comments
 (0)