Commit 069c76c2 authored by klausw's avatar klausw Committed by Commit bot

WebVR: implement insecure content warning display

On WebVR pages loaded over insecure transport, show a small "Not secure"
warning overlay permanently, and a transient more verbose warning for
a short time on first entering the page.

It uses the information from bajones's "Expose secure origin state to
WebVR renderer" implementation.

The message strings were previously added to chrome/app/generated_resources.grd in https://codereview.chromium.org/2368443002/

BUG=389343,644492
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2363553003
Cr-Commit-Position: refs/heads/master@{#422647}
parent 7395f42e
......@@ -72,6 +72,23 @@ static constexpr float kReticleDistanceMultiplier = 1.5f;
// UI element 0 is the browser content rectangle.
static constexpr int kBrowserUiElementId = 0;
// Positions and sizes of statically placed UI elements in the UI texture.
// TODO(klausw): replace the hardcoded positions with JS position/offset
// retrieval once the infrastructure for that is hooked up.
//
// UI is designed with 1 pixel = 1mm at 1m distance. It's rescaled to
// maintain the same angular resolution if placed closer or further.
// The warning overlays should be fairly close since they cut holes
// into geometry (they ignore the Z buffer), leading to odd effects
// if they are far away.
static constexpr vr_shell::Recti kWebVrWarningTransientRect = {
0, 128, 512, 256};
static constexpr vr_shell::Recti kWebVrWarningPermanentRect = {0, 0, 512, 128};
static constexpr float kWebVrWarningDistance = 0.7f; // meters
static constexpr float kWebVrWarningPermanentAngle = 16.3f; // degrees up
// How long the transient warning needs to be displayed.
static constexpr int64_t kWebVrWarningSeconds = 30;
vr_shell::VrShell* g_instance;
static const char kVrShellUIURL[] = "chrome://vr-shell-ui";
......@@ -309,6 +326,9 @@ void VrShell::DrawFrame(JNIEnv* env, const JavaParamRef<jobject>& obj) {
if (webvr_mode_) {
DrawWebVr();
if (!webvr_secure_origin_) {
DrawWebVrOverlay(target_time.monotonic_system_time_nanos);
}
} else {
DrawVrShell(head_pose);
}
......@@ -372,6 +392,19 @@ void VrShell::DrawEye(const gvr::Mat4f& view_matrix,
DrawCursor(render_matrix);
}
bool VrShell::IsUiTextureReady() {
return ui_tex_width_ > 0 && ui_tex_height_ > 0;
}
Rectf VrShell::MakeUiGlCopyRect(Recti pixel_rect) {
CHECK(IsUiTextureReady());
return Rectf({
static_cast<float>(pixel_rect.x) / ui_tex_width_,
static_cast<float>(pixel_rect.y) / ui_tex_height_,
static_cast<float>(pixel_rect.width) / ui_tex_width_,
static_cast<float>(pixel_rect.height) / ui_tex_height_});
}
void VrShell::DrawUI(const gvr::Mat4f& render_matrix) {
for (const auto& rect : scene_.GetUiElements()) {
if (!rect->visible) {
......@@ -468,10 +501,86 @@ void VrShell::DrawWebVr() {
glViewport(0, 0, render_size_.width, render_size_.height);
vr_shell_renderer_->GetWebVrRenderer()->Draw(content_texture_id_);
}
void VrShell::DrawWebVrOverlay(int64_t present_time_nanos) {
// Draw WebVR security warning overlays for each eye. This uses the
// eye-from-head matrices but not the pose, goal is to place the icons in an
// eye-relative position so that they follow along with head rotations.
gvr::Mat4f left_eye_view_matrix =
gvr_api_->GetEyeFromHeadMatrix(GVR_LEFT_EYE);
gvr::Mat4f right_eye_view_matrix =
gvr_api_->GetEyeFromHeadMatrix(GVR_RIGHT_EYE);
if (!webvr_secure_origin_) {
// TODO(klausw): Draw the insecure origin warning here.
buffer_viewport_list_->GetBufferViewport(GVR_LEFT_EYE,
buffer_viewport_.get());
DrawWebVrEye(left_eye_view_matrix, *buffer_viewport_, present_time_nanos);
buffer_viewport_list_->GetBufferViewport(GVR_RIGHT_EYE,
buffer_viewport_.get());
DrawWebVrEye(right_eye_view_matrix, *buffer_viewport_, present_time_nanos);
}
void VrShell::DrawWebVrEye(const gvr::Mat4f& view_matrix,
const gvr::BufferViewport& params,
int64_t present_time_nanos) {
gvr::Recti pixel_rect =
CalculatePixelSpaceRect(render_size_, params.GetSourceUv());
glViewport(pixel_rect.left, pixel_rect.bottom,
pixel_rect.right - pixel_rect.left,
pixel_rect.top - pixel_rect.bottom);
glScissor(pixel_rect.left, pixel_rect.bottom,
pixel_rect.right - pixel_rect.left,
pixel_rect.top - pixel_rect.bottom);
gvr::Mat4f projection_matrix =
PerspectiveMatrixFromView(params.GetSourceFov(), kZNear, kZFar);
if (!IsUiTextureReady()) {
// If the UI texture hasn't been initialized yet, we can't draw the overlay.
return;
}
// Show IDS_WEBSITE_SETTINGS_INSECURE_WEBVR_CONTENT_PERMANENT text.
gvr::Mat4f icon_pos;
SetIdentityM(icon_pos);
// The UI is designed in pixels with the assumption that 1px = 1mm at 1m
// distance. Scale mm-to-m and adjust to keep the same angular size if the
// distance changes.
const float small_icon_width =
kWebVrWarningPermanentRect.width / 1000.f * kWebVrWarningDistance;
const float small_icon_height =
kWebVrWarningPermanentRect.height / 1000.f * kWebVrWarningDistance;
const float small_icon_angle =
kWebVrWarningPermanentAngle * M_PI / 180.f; // Degrees to radians.
ScaleM(icon_pos, icon_pos, small_icon_width, small_icon_height, 1.0f);
TranslateM(icon_pos, icon_pos, 0.0f, 0.0f, -kWebVrWarningDistance);
icon_pos = MatrixMul(
QuatToMatrix(QuatFromAxisAngle({1.f, 0.f, 0.f}, small_icon_angle)),
icon_pos);
gvr::Mat4f combined = MatrixMul(projection_matrix,
MatrixMul(view_matrix, icon_pos));
vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
ui_texture_id_, combined, MakeUiGlCopyRect(kWebVrWarningPermanentRect));
// Check if we also need to show the transient warning.
if (present_time_nanos > webvr_warning_end_nanos_) {
return;
}
// Show IDS_WEBSITE_SETTINGS_INSECURE_WEBVR_CONTENT_TRANSIENT text.
SetIdentityM(icon_pos);
const float large_icon_width =
kWebVrWarningTransientRect.width / 1000.f * kWebVrWarningDistance;
const float large_icon_height =
kWebVrWarningTransientRect.height / 1000.f * kWebVrWarningDistance;
ScaleM(icon_pos, icon_pos, large_icon_width, large_icon_height, 1.0f);
TranslateM(icon_pos, icon_pos, 0.0f, 0.0f, -kWebVrWarningDistance);
combined = MatrixMul(projection_matrix,
MatrixMul(view_matrix, icon_pos));
vr_shell_renderer_->GetTexturedQuadRenderer()->Draw(
ui_texture_id_, combined, MakeUiGlCopyRect(kWebVrWarningTransientRect));
}
void VrShell::OnPause(JNIEnv* env, const JavaParamRef<jobject>& obj) {
......@@ -518,6 +627,13 @@ void VrShell::SetWebVrMode(JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
bool enabled) {
webvr_mode_ = enabled;
if (enabled) {
int64_t now = gvr::GvrApi::GetTimePointNow().monotonic_system_time_nanos;
constexpr int64_t seconds_to_nanos = 1000 * 1000 * 1000;
webvr_warning_end_nanos_ = now + kWebVrWarningSeconds * seconds_to_nanos;
} else {
webvr_warning_end_nanos_ = 0;
}
}
void VrShell::SetWebVRSecureOrigin(bool secure_origin) {
......
......@@ -94,13 +94,21 @@ class VrShell : public device::GvrDelegate {
private:
virtual ~VrShell();
void LoadUIContent();
bool IsUiTextureReady();
// Converts a pixel rectangle to (0..1) float texture coordinates.
// Callers need to ensure that the texture width/height is
// initialized by checking IsUiTextureReady() first.
Rectf MakeUiGlCopyRect(Recti pixel_rect);
void DrawVrShell(const gvr::Mat4f& head_pose);
void DrawEye(const gvr::Mat4f& view_matrix,
const gvr::BufferViewport& params);
void DrawContentRect();
void DrawWebVr();
void DrawUI(const gvr::Mat4f& render_matrix);
void DrawCursor(const gvr::Mat4f& render_matrix);
void DrawWebVr();
void DrawWebVrOverlay(int64_t present_time_nanos);
void DrawWebVrEye(const gvr::Mat4f& view_matrix,
const gvr::BufferViewport& params,
int64_t present_time_nanos);
void UpdateController(const gvr::Vec3f& forward_vector);
......@@ -124,7 +132,6 @@ class VrShell : public device::GvrDelegate {
std::unique_ptr<gvr::SwapChain> swap_chain_;
gvr::Sizei render_size_;
float cursor_distance_;
std::queue<base::Callback<void()>> task_queue_;
base::Lock task_queue_lock_;
......@@ -148,6 +155,7 @@ class VrShell : public device::GvrDelegate {
bool webvr_mode_ = false;
bool webvr_secure_origin_ = false;
int64_t webvr_warning_end_nanos_ = 0;
base::WeakPtrFactory<VrShell> weak_ptr_factory_;
......
......@@ -199,9 +199,9 @@
<include name="IDR_SNIPPETS_INTERNALS_JS" file="resources\snippets_internals.js" compress="gzip" type="BINDATA" />
<!-- TODO: Enable only for VrShell, independent of Android. -->
<if expr="enable_vr_shell">
<include name="IDR_VR_SHELL_UI_HTML" file="resources\vr_shell\vr_shell_ui.html" allowexternalscript="true" flattenhtml="true" compress="gzip" type="BINDATA" />
<include name="IDR_VR_SHELL_UI_CSS" file="resources\vr_shell\vr_shell_ui.css" compress="gzip" type="BINDATA" />
<include name="IDR_VR_SHELL_UI_JS" file="resources\vr_shell\vr_shell_ui.js" compress="gzip" type="BINDATA" />
<include name="IDR_VR_SHELL_UI_HTML" file="resources\vr_shell\vr_shell_ui.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" />
<include name="IDR_VR_SHELL_UI_CSS" file="resources\vr_shell\vr_shell_ui.css" type="BINDATA" />
<include name="IDR_VR_SHELL_UI_JS" file="resources\vr_shell\vr_shell_ui.js" type="BINDATA" />
</if>
</if>
<include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" />
......
......@@ -4,7 +4,6 @@
html {
background-color: rgba(255, 255, 255, 0.2);
font-size: 20px;
}
.ui-button {
......@@ -15,3 +14,83 @@ html {
text-align: center;
vertical-align: middle;
}
.webvr-message-box {
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
}
/* Permanent security warning for WebVR. This uses a fixed-size absolutely
* positioned DIV, the rectangle must match kWebVrWarningPermanentRect
* in vr_shell.cc for use as a GL textured quad. */
#webvr-not-secure-permanent {
height: 128px;
left: 0;
top: 0;
width: 512px;
}
#webvr-not-secure-permanent .webvr-not-secure-icon {
display: inline-block;
margin: 20px 0;
vertical-align: middle;
}
#webvr-not-secure-permanent .webvr-string {
display: inline-block;
margin: 20px 10.5px;
vertical-align: middle;
}
/* This is a single-line (nowrap) short message. The width has some elasticity
* for translations, and the underlying string had a request to translators
* to keep it short. The maximum is based on the fixed-size container DIV,
* excess will be clipped. There's space for about twice the length of the
* English "Not secure" message. */
#webvr-not-secure-permanent .webvr-box {
background-color: white;
border-radius: 6px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
box-sizing: border-box;
color: #444;
font-size: 26px;
height: 78px;
max-width: 472px;
min-width: 226px;
overflow: hidden;
padding: 0 20px;
white-space: nowrap;
}
/* Transient security warning for WebVR. This uses a fixed-size absolutely
* positioned DIV, the rectangle must match kWebVrWarningTransientRect
* in vr_shell.cc for use as a GL textured quad. */
#webvr-not-secure-transient {
height: 256px;
left: 0;
top: 128px;
width: 512px;
}
/* This uses fixed width but the height has some elasticity for translations.
* The maximum is based on the fixed-size container DIV, excess will be
* clipped. */
#webvr-not-secure-transient > div {
background-color: rgba(26, 26, 26, 0.8);
border-radius: 6px;
box-sizing: border-box;
color: white;
display: flex;
flex-direction: column;
font-size: 26px;
justify-content: center;
line-height: 1.4;
max-height: 210px;
min-height: 160px;
padding: 20px;
text-align: center;
width: 512px;
}
......@@ -15,8 +15,22 @@ found in the LICENSE file.
</if>
<title>Vr Shell UIs</title>
<link rel="stylesheet" href="vr_shell_ui.css">
<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
</head>
<body>
<div id="webvr-not-secure-permanent" class="webvr-message-box">
<div class="webvr-box">
<img class="webvr-not-secure-icon" width="36" height="36"
src="../../../../ui/webui/resources/images/i_circle.svg">
<div class="webvr-string">$i18n{insecureWebVrContentPermanent}</div>
</div>
</div>
<div id="webvr-not-secure-transient" class="webvr-message-box">
<div>
<div>$i18n{insecureWebVrContentTransient}</div>
</div>
</div>
</body>
<!-- Run script after creating body, to let it add its own elements. -->
......
......@@ -182,7 +182,7 @@ var vrShellUi = (function() {
for (var i = 0; i < buttons.length; i++) {
var b = document.createElement('div');
b.position = 'absolute';
b.style.top = '200px';
b.style.top = '384px';
b.style.left = 50 * i + 'px';
b.style.width = '50px';
b.style.height = '50px';
......@@ -197,7 +197,7 @@ var vrShellUi = (function() {
document.body.appendChild(b);
// Add a UI rectangle for the button.
var el = new UiElement(50 * i, 200, 50, 50, buttonWidth, buttonHeight);
var el = new UiElement(50 * i, 384, 50, 50, buttonWidth, buttonHeight);
el.parentId = 0;
el.setAnchoring(XAnchoring.XNONE, YAnchoring.YBOTTOM);
el.setTranslation(buttonStartPosition + buttonSpacing * i, -0.3, 0.0);
......
......@@ -10,7 +10,9 @@
#include "content/public/browser/web_ui.h"
#if !defined(ENABLE_VR_SHELL_UI_DEV)
#include "chrome/browser/browser_process.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "content/public/browser/web_ui_data_source.h"
#else
#include <map>
......@@ -179,7 +181,17 @@ content::WebUIDataSource* CreateVrShellUIHTMLSource() {
source->AddResourcePath("vr_shell_ui.js", IDR_VR_SHELL_UI_JS);
source->AddResourcePath("vr_shell_ui.css", IDR_VR_SHELL_UI_CSS);
source->SetDefaultResource(IDR_VR_SHELL_UI_HTML);
source->DisableI18nAndUseGzipForAllPaths();
// We're localizing strings, so we can't currently use gzip since it's
// incompatible with i18n. TODO(klausw): re-enable gzip once an i18n
// compatible variant of WebUIDataSource's DisableI18nAndUseGzipForAllPaths
// gets added, and add compress=gzip to browser_resources.grd as appropriate.
source->AddLocalizedString(
"insecureWebVrContentPermanent",
IDS_WEBSITE_SETTINGS_INSECURE_WEBVR_CONTENT_PERMANENT);
source->AddLocalizedString(
"insecureWebVrContentTransient",
IDS_WEBSITE_SETTINGS_INSECURE_WEBVR_CONTENT_TRANSIENT);
return source;
}
#endif
......
<svg width="36px" height="36px"
viewBox="0 0 36 36" version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M18,0 C8.064,0 0,8.064 0,18 C0,27.936 8.064,36 18,36
C27.936,36 36,27.936 36,18 C36,8.064 27.936,0 18,0 L18,0 Z
M18,32 C10.2825,32 4,25.7175 4,18 C4,10.2825 10.2825,4 18,4
C25.7175,4 32,10.2825 32,18 C32,25.7175 25.7175,32 18,32
L18,32 Z M16,27 L20,27 L20,17 L16,17 L16,27 L16,27 Z M16,13
L20,13 L20,9 L16,9 L16,13 L16,13 Z"
fill="#5A5A5A"></path>
</g>
</svg>
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