diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java
index 2f731016e..15bd03556 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java
@@ -69,7 +69,9 @@
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Set;
import java.util.function.Consumer;
import androidx.annotation.IntDef;
@@ -144,6 +146,7 @@ public void run() {
private boolean mConnectionAvailable = true;
private AudioManager mAudioManager;
private Widget mActiveDialog;
+ private Set mPoorPerformanceWhiteList;
private boolean callOnAudioManager(Consumer fn) {
if (mAudioManager == null) {
@@ -243,6 +246,7 @@ protected void onCreate(Bundle savedInstanceState) {
GeolocationWrapper.update(this);
mConnectivityReceiver = new ConnectivityReceiver();
+ mPoorPerformanceWhiteList = new HashSet<>();
}
protected void initializeWorld() {
@@ -852,6 +856,38 @@ public void dismiss() {
});
}
+ @Keep
+ @SuppressWarnings("unused")
+ private void handlePoorPerformance() {
+ runOnUiThread(() -> {
+ if (!mSettings.isPerformanceMonitorEnabled()) {
+ return;
+ }
+ // Don't block poorly performing immersive pages.
+ if (mIsPresentingImmersive) {
+ return;
+ }
+ if (mWindowWidget == null) {
+ return;
+ }
+ final String originalUrl = SessionStore.get().getCurrentUri();
+ if (mPoorPerformanceWhiteList.contains(originalUrl)) {
+ return;
+ }
+ SessionStore.get().loadUri("about:blank");
+ final String[] buttons = {getString(R.string.ok_button), null, getString(R.string.performance_unblock_page)};
+ mWindowWidget.showButtonPrompt(getString(R.string.performance_title), getString(R.string.performance_message), buttons, new GeckoSession.PromptDelegate.ButtonCallback() {
+ @Override
+ public void confirm(int button) {
+ if (button == 0) {
+ mPoorPerformanceWhiteList.add(originalUrl);
+ SessionStore.get().loadUri(originalUrl);
+ }
+ }
+ });
+ });
+ }
+
void createOffscreenDisplay() {
int[] ids = new int[1];
GLES20.glGenTextures(1, ids, 0);
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java
index 90241c842..b786ae7bb 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java
@@ -44,6 +44,7 @@ SettingsStore getInstance(final @NonNull Context aContext) {
public final static boolean CONSOLE_LOGS_DEFAULT = false;
public final static boolean ENV_OVERRIDE_DEFAULT = false;
public final static boolean MULTIPROCESS_DEFAULT = false;
+ public final static boolean PERFORMANCE_MONITOR_DEFAULT = true;
public final static boolean TRACKING_DEFAULT = true;
public final static boolean SERVO_DEFAULT = false;
public final static int UA_MODE_DEFAULT = GeckoSessionSettings.USER_AGENT_MODE_VR;
@@ -179,6 +180,16 @@ public void setMultiprocessEnabled(boolean isEnabled) {
editor.commit();
}
+ public boolean isPerformanceMonitorEnabled() {
+ return mPrefs.getBoolean(mContext.getString(R.string.settings_key_performance_monitor), PERFORMANCE_MONITOR_DEFAULT);
+ }
+
+ public void setPerformanceMonitorEnabled(boolean isEnabled) {
+ SharedPreferences.Editor editor = mPrefs.edit();
+ editor.putBoolean(mContext.getString(R.string.settings_key_performance_monitor), isEnabled);
+ editor.commit();
+ }
+
public boolean isServoEnabled() {
return isServoAvailable() && mPrefs.getBoolean(mContext.getString(R.string.settings_key_servo), SERVO_DEFAULT);
}
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java
index cc5c91a42..97dea4bd3 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java
@@ -598,6 +598,16 @@ public void showAlert(String title, @NonNull String msg, @NonNull AlertCallback
mAlertPrompt.show(REQUEST_FOCUS);
}
+ public void showButtonPrompt(String title, @NonNull String msg, @NonNull String[] btnMsg, @NonNull ButtonCallback callback) {
+ mConfirmPrompt = new ConfirmPromptWidget(getContext());
+ mConfirmPrompt.mWidgetPlacement.parentHandle = getHandle();
+ mConfirmPrompt.setTitle(title);
+ mConfirmPrompt.setMessage(msg);
+ mConfirmPrompt.setButtons(btnMsg);
+ mConfirmPrompt.setDelegate(callback);
+ mConfirmPrompt.show(REQUEST_FOCUS);
+ }
+
// PromptDelegate
@Override
diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java
index 81d99d59e..4d04317c6 100644
--- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java
+++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/settings/DeveloperOptionsView.java
@@ -27,6 +27,7 @@ class DeveloperOptionsView extends SettingsView {
private SwitchSetting mRemoteDebuggingSwitch;
private SwitchSetting mConsoleLogsSwitch;
private SwitchSetting mMultiprocessSwitch;
+ private SwitchSetting mPerformanceSwitch;
private SwitchSetting mServoSwitch;
private SingleEditSetting mHomepageEdit;
private String mDefaultHomepageUrl;
@@ -73,6 +74,10 @@ private void initialize(Context aContext) {
mMultiprocessSwitch.setOnCheckedChangeListener(mMultiprocessListener);
setMultiprocess(SettingsStore.getInstance(getContext()).isMultiprocessEnabled(), false);
+ mPerformanceSwitch = findViewById(R.id.performance_monitor_switch);
+ mPerformanceSwitch.setOnCheckedChangeListener(mPerformanceListener);
+ setPerformance(SettingsStore.getInstance(getContext()).isPerformanceMonitorEnabled(), false);
+
mServoSwitch = findViewById(R.id.servo_switch);
if (!isServoAvailable()) {
mServoSwitch.setVisibility(View.GONE);
@@ -130,6 +135,10 @@ protected void onDismiss() {
setMultiprocess(value, doApply);
};
+ private SwitchSetting.OnCheckedChangeListener mPerformanceListener = (compoundButton, value, doApply) -> {
+ setPerformance(value, doApply);
+ };
+
private SwitchSetting.OnCheckedChangeListener mServoListener = (compoundButton, b, doApply) -> {
setServo(b, true);
};
@@ -200,6 +209,14 @@ private void setMultiprocess(boolean value, boolean doApply) {
}
}
+ private void setPerformance(boolean value, boolean doApply) {
+ mPerformanceSwitch.setOnCheckedChangeListener(null);
+ mPerformanceSwitch.setValue(value, false);
+ mPerformanceSwitch.setOnCheckedChangeListener(mPerformanceListener);
+
+ SettingsStore.getInstance(getContext()).setPerformanceMonitorEnabled(value);
+ }
+
private void setServo(boolean value, boolean doApply) {
mServoSwitch.setOnCheckedChangeListener(null);
mServoSwitch.setValue(value, false);
diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp
index 290f9cce6..596944179 100644
--- a/app/src/main/cpp/BrowserWorld.cpp
+++ b/app/src/main/cpp/BrowserWorld.cpp
@@ -39,6 +39,7 @@
#include "vrb/ModelLoaderAndroid.h"
#include "vrb/NodeFactoryObj.h"
#include "vrb/ParserObj.h"
+#include "vrb/PerformanceMonitor.h"
#include "vrb/RenderContext.h"
#include "vrb/RenderState.h"
#include "vrb/SurfaceTextureFactory.h"
@@ -118,6 +119,25 @@ SurfaceObserver::SurfaceTextureCreationError(const std::string& aName, const std
}
+class PerformanceObserver;
+typedef std::shared_ptr PerformanceObserverPtr;
+
+class PerformanceObserver : public PerformanceMonitorObserver {
+public:
+ void PoorPerformanceDetected(const double& aTargetFrameRate, const double& aAverageFrameRate) override;
+ void PerformanceRestored(const double& aTargetFrameRate, const double& aAverageFrameRate) override;
+};
+
+void
+PerformanceObserver::PoorPerformanceDetected(const double& aTargetFrameRate, const double& aAverageFrameRate) {
+ crow::VRBrowser::HandlePoorPerformance();
+}
+
+void
+PerformanceObserver::PerformanceRestored(const double& aTargetFrameRate, const double& aAverageFrameRate) {
+
+}
+
} // namespace
namespace crow {
@@ -160,6 +180,7 @@ struct BrowserWorld::State {
LoadingAnimationPtr loadingAnimation;
SplashAnimationPtr splashAnimation;
VRVideoPtr vrVideo;
+ PerformanceMonitorPtr monitor;
State() : paused(true), glInitialized(false), modelsLoaded(false), env(nullptr), cylinderDensity(0.0f), nearClip(0.1f),
farClip(300.0f), activity(nullptr), windowsInitialized(false), exitImmersiveRequested(false), loaderDelay(0) {
@@ -183,6 +204,8 @@ struct BrowserWorld::State {
fadeAnimation = FadeAnimation::Create(create);
loadingAnimation = LoadingAnimation::Create(create);
splashAnimation = SplashAnimation::Create(create);
+ monitor = PerformanceMonitor::Create(create);
+ monitor->AddPerformanceMonitorObserver(std::make_shared());
}
void CheckBackButton();
@@ -540,12 +563,14 @@ void
BrowserWorld::Pause() {
ASSERT_ON_RENDER_THREAD();
m.paused = true;
+ m.monitor->Pause();
}
void
BrowserWorld::Resume() {
ASSERT_ON_RENDER_THREAD();
m.paused = false;
+ m.monitor->Resume();
}
bool
diff --git a/app/src/main/cpp/VRBrowser.cpp b/app/src/main/cpp/VRBrowser.cpp
index a44dea145..009164fba 100644
--- a/app/src/main/cpp/VRBrowser.cpp
+++ b/app/src/main/cpp/VRBrowser.cpp
@@ -48,29 +48,32 @@ const char* kSetDeviceType = "setDeviceType";
const char* kSetDeviceTypeSignature = "(I)V";
const char* kHaltActivity = "haltActivity";
const char* kHaltActivitySignature = "(I)V";
-
-JNIEnv* sEnv;
-jclass sBrowserClass;
-jobject sActivity;
-jmethodID sDispatchCreateWidget;
-jmethodID sDispatchCreateWidgetLayer;
-jmethodID sHandleMotionEvent;
-jmethodID sHandleScrollEvent;
-jmethodID sHandleAudioPose;
-jmethodID sHandleGesture;
-jmethodID sHandleResize;
-jmethodID sHandleBack;
-jmethodID sRegisterExternalContext;
-jmethodID sPauseCompositor;
-jmethodID sResumeCompositor;
-jmethodID sRenderPointerLayer;
-jmethodID sGetStorageAbsolutePath;
-jmethodID sIsOverrideEnvPathEnabled;
-jmethodID sGetActiveEnvironment;
-jmethodID sGetPointerColor;
-jmethodID sAreLayersEnabled;
-jmethodID sSetDeviceType;
-jmethodID sHaltActivity;
+const char* kHandlePoorPerformance = "handlePoorPerformance";
+const char* kHandlePoorPerformanceSignature = "()V";
+
+JNIEnv* sEnv = nullptr;
+jclass sBrowserClass = nullptr;
+jobject sActivity = nullptr;
+jmethodID sDispatchCreateWidget = nullptr;
+jmethodID sDispatchCreateWidgetLayer = nullptr;
+jmethodID sHandleMotionEvent = nullptr;
+jmethodID sHandleScrollEvent = nullptr;
+jmethodID sHandleAudioPose = nullptr;
+jmethodID sHandleGesture = nullptr;
+jmethodID sHandleResize = nullptr;
+jmethodID sHandleBack = nullptr;
+jmethodID sRegisterExternalContext = nullptr;
+jmethodID sPauseCompositor = nullptr;
+jmethodID sResumeCompositor = nullptr;
+jmethodID sRenderPointerLayer = nullptr;
+jmethodID sGetStorageAbsolutePath = nullptr;
+jmethodID sIsOverrideEnvPathEnabled = nullptr;
+jmethodID sGetActiveEnvironment = nullptr;
+jmethodID sGetPointerColor = nullptr;
+jmethodID sAreLayersEnabled = nullptr;
+jmethodID sSetDeviceType = nullptr;
+jmethodID sHaltActivity = nullptr;
+jmethodID sHandlePoorPerformance = nullptr;
}
namespace crow {
@@ -109,6 +112,7 @@ VRBrowser::InitializeJava(JNIEnv* aEnv, jobject aActivity) {
sAreLayersEnabled = FindJNIMethodID(sEnv, sBrowserClass, kAreLayersEnabled, kAreLayersEnabledSignature);
sSetDeviceType = FindJNIMethodID(sEnv, sBrowserClass, kSetDeviceType, kSetDeviceTypeSignature);
sHaltActivity = FindJNIMethodID(sEnv, sBrowserClass, kHaltActivity, kHaltActivitySignature);
+ sHandlePoorPerformance = FindJNIMethodID(sEnv, sBrowserClass, kHandlePoorPerformance, kHandlePoorPerformanceSignature);
}
void
@@ -316,5 +320,11 @@ VRBrowser::HaltActivity(const jint aReason) {
CheckJNIException(sEnv, __FUNCTION__);
}
+void
+VRBrowser::HandlePoorPerformance() {
+ if (!ValidateMethodID(sEnv, sActivity, sHandlePoorPerformance, __FUNCTION__)) { return; }
+ sEnv->CallVoidMethod(sActivity, sHandlePoorPerformance);
+ CheckJNIException(sEnv, __FUNCTION__);
+}
} // namespace crow
diff --git a/app/src/main/cpp/VRBrowser.h b/app/src/main/cpp/VRBrowser.h
index 09e837af5..6e485a9f9 100644
--- a/app/src/main/cpp/VRBrowser.h
+++ b/app/src/main/cpp/VRBrowser.h
@@ -37,6 +37,7 @@ int32_t GetPointerColor();
bool AreLayersEnabled();
void SetDeviceType(const jint aType);
void HaltActivity(const jint aReason);
+void HandlePoorPerformance();
} // namespace VRBrowser;
} // namespace crow
diff --git a/app/src/main/cpp/vrb b/app/src/main/cpp/vrb
index 7164170fb..bb4354fa8 160000
--- a/app/src/main/cpp/vrb
+++ b/app/src/main/cpp/vrb
@@ -1 +1 @@
-Subproject commit 7164170fb86d98b19f913859a18fb128c8749cb6
+Subproject commit bb4354fa8b2cbaae88e7bcc3b2d095341a0138a5
diff --git a/app/src/main/res/layout/options_developer.xml b/app/src/main/res/layout/options_developer.xml
index 817ab84a5..bfb434740 100644
--- a/app/src/main/res/layout/options_developer.xml
+++ b/app/src/main/res/layout/options_developer.xml
@@ -71,6 +71,12 @@
android:layout_height="wrap_content"
app:description="@string/developer_options_multiprocess" />
+
+
settings_console_logs
settings_environment_override
settings_environment_multiprocess
+ settings_performance_monitor
settings_environment_servo
settings_tracking_protection
settings_user_agent_version
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 89a90379c..e14e3d6d5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -232,6 +232,12 @@
A dialog should appear with a field labeled with the text, 'Enable Multiprocess'. -->
Enable Multiprocess
+
+ Enable Performance Monitor
+
Enable Servo
@@ -699,4 +705,11 @@
%1$s does not have permission to run on this device and will now exit.
+
+
+ Unblock Page
+
+ Poor Performance Detected
+
+ The current web page is affecting performance and has been suspended. Reducing the window size can help improve a web page with poor performance.