Merge "Add API to WebView functor to support overlays"
This commit is contained in:
committed by
Android (Google) Code Review
commit
902fa1c0b9
@@ -26,6 +26,35 @@
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
namespace {
|
||||
class ScopedCurrentFunctor {
|
||||
public:
|
||||
ScopedCurrentFunctor(WebViewFunctor* functor) {
|
||||
ALOG_ASSERT(!sCurrentFunctor);
|
||||
ALOG_ASSERT(functor);
|
||||
sCurrentFunctor = functor;
|
||||
}
|
||||
~ScopedCurrentFunctor() {
|
||||
ALOG_ASSERT(sCurrentFunctor);
|
||||
sCurrentFunctor = nullptr;
|
||||
}
|
||||
|
||||
static ASurfaceControl* getSurfaceControl() {
|
||||
ALOG_ASSERT(sCurrentFunctor);
|
||||
return sCurrentFunctor->getSurfaceControl();
|
||||
}
|
||||
static void mergeTransaction(ASurfaceTransaction* transaction) {
|
||||
ALOG_ASSERT(sCurrentFunctor);
|
||||
sCurrentFunctor->mergeTransaction(transaction);
|
||||
}
|
||||
|
||||
private:
|
||||
static WebViewFunctor* sCurrentFunctor;
|
||||
};
|
||||
|
||||
WebViewFunctor* ScopedCurrentFunctor::sCurrentFunctor = nullptr;
|
||||
} // namespace
|
||||
|
||||
RenderMode WebViewFunctor_queryPlatformRenderMode() {
|
||||
auto pipelineType = Properties::getRenderPipelineType();
|
||||
switch (pipelineType) {
|
||||
@@ -83,7 +112,15 @@ void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
|
||||
if (!mHasContext) {
|
||||
mHasContext = true;
|
||||
}
|
||||
mCallbacks.gles.draw(mFunctor, mData, drawInfo);
|
||||
ScopedCurrentFunctor currentFunctor(this);
|
||||
|
||||
WebViewOverlayData overlayParams = {
|
||||
// TODO:
|
||||
.overlaysMode = OverlaysMode::Disabled,
|
||||
.getSurfaceControl = currentFunctor.getSurfaceControl,
|
||||
.mergeTransaction = currentFunctor.mergeTransaction,
|
||||
};
|
||||
mCallbacks.gles.draw(mFunctor, mData, drawInfo, overlayParams);
|
||||
}
|
||||
|
||||
void WebViewFunctor::initVk(const VkFunctorInitParams& params) {
|
||||
@@ -98,7 +135,15 @@ void WebViewFunctor::initVk(const VkFunctorInitParams& params) {
|
||||
|
||||
void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {
|
||||
ATRACE_NAME("WebViewFunctor::drawVk");
|
||||
mCallbacks.vk.draw(mFunctor, mData, params);
|
||||
ScopedCurrentFunctor currentFunctor(this);
|
||||
|
||||
WebViewOverlayData overlayParams = {
|
||||
// TODO
|
||||
.overlaysMode = OverlaysMode::Disabled,
|
||||
.getSurfaceControl = currentFunctor.getSurfaceControl,
|
||||
.mergeTransaction = currentFunctor.mergeTransaction,
|
||||
};
|
||||
mCallbacks.vk.draw(mFunctor, mData, params, overlayParams);
|
||||
}
|
||||
|
||||
void WebViewFunctor::postDrawVk() {
|
||||
@@ -118,6 +163,20 @@ void WebViewFunctor::destroyContext() {
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewFunctor::removeOverlays() {
|
||||
ScopedCurrentFunctor currentFunctor(this);
|
||||
mCallbacks.removeOverlays(mFunctor, mData, currentFunctor.mergeTransaction);
|
||||
}
|
||||
|
||||
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
|
||||
// TODO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
WebViewFunctorManager& WebViewFunctorManager::instance() {
|
||||
static WebViewFunctorManager sInstance;
|
||||
return sInstance;
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
|
||||
void postDrawVk() { mReference.postDrawVk(); }
|
||||
|
||||
void removeOverlays() { mReference.removeOverlays(); }
|
||||
|
||||
private:
|
||||
friend class WebViewFunctor;
|
||||
|
||||
@@ -71,6 +73,10 @@ public:
|
||||
void drawVk(const VkFunctorDrawParams& params);
|
||||
void postDrawVk();
|
||||
void destroyContext();
|
||||
void removeOverlays();
|
||||
|
||||
ASurfaceControl* getSurfaceControl();
|
||||
void mergeTransaction(ASurfaceTransaction* transaction);
|
||||
|
||||
sp<Handle> createHandle() {
|
||||
LOG_ALWAYS_FATAL_IF(mCreatedHandle);
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
|
||||
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
|
||||
|
||||
#ifdef __ANDROID__ // Layoutlib does not support surface control
|
||||
#include <android/surface_control.h>
|
||||
#else
|
||||
// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They
|
||||
// won't be used.
|
||||
typedef void* ASurfaceControl;
|
||||
typedef void* ASurfaceTransaction;
|
||||
#endif
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <private/hwui/DrawGlInfo.h>
|
||||
#include <private/hwui/DrawVkInfo.h>
|
||||
@@ -28,6 +37,14 @@ enum class RenderMode {
|
||||
Vulkan,
|
||||
};
|
||||
|
||||
enum class OverlaysMode {
|
||||
// Indicated that webview should not promote anything to overlays this draw
|
||||
// and remove all visible overlays.
|
||||
Disabled,
|
||||
// Indicates that webview can use overlays.
|
||||
Enabled
|
||||
};
|
||||
|
||||
// Static for the lifetime of the process
|
||||
ANDROID_API RenderMode WebViewFunctor_queryPlatformRenderMode();
|
||||
|
||||
@@ -35,6 +52,23 @@ struct WebViewSyncData {
|
||||
bool applyForceDark;
|
||||
};
|
||||
|
||||
struct WebViewOverlayData {
|
||||
// Desired overlay mode for this draw.
|
||||
OverlaysMode overlaysMode;
|
||||
|
||||
// Returns parent ASurfaceControl for WebView overlays. It will be have same
|
||||
// geometry as the surface we draw into and positioned below it (underlay).
|
||||
// This does not pass ownership to webview, but guaranteed to be alive until
|
||||
// transaction from next removeOverlays call or functor destruction will be
|
||||
// finished.
|
||||
ASurfaceControl* (*getSurfaceControl)();
|
||||
|
||||
// Merges WebView transaction to be applied synchronously with current draw.
|
||||
// This doesn't pass ownership of the transaction, changes will be copied and
|
||||
// webview can free transaction right after the call.
|
||||
void (*mergeTransaction)(ASurfaceTransaction*);
|
||||
};
|
||||
|
||||
struct WebViewFunctorCallbacks {
|
||||
// kModeSync, called on RenderThread
|
||||
void (*onSync)(int functor, void* data, const WebViewSyncData& syncData);
|
||||
@@ -48,16 +82,23 @@ struct WebViewFunctorCallbacks {
|
||||
// this functor had ever been drawn.
|
||||
void (*onDestroyed)(int functor, void* data);
|
||||
|
||||
// Called on render thread to force webview hide all overlays and stop updating them.
|
||||
// Should happen during hwui draw (e.g can be called instead of draw if webview
|
||||
// isn't visible and won't receive draw) and support MergeTransaction call.
|
||||
void (*removeOverlays)(int functor, void* data, void (*mergeTransaction)(ASurfaceTransaction*));
|
||||
|
||||
union {
|
||||
struct {
|
||||
// Called on RenderThread. initialize is guaranteed to happen before this call
|
||||
void (*draw)(int functor, void* data, const DrawGlInfo& params);
|
||||
void (*draw)(int functor, void* data, const DrawGlInfo& params,
|
||||
const WebViewOverlayData& overlayParams);
|
||||
} gles;
|
||||
struct {
|
||||
// Called either the first time the functor is used or the first time it's used after
|
||||
// a call to onContextDestroyed.
|
||||
void (*initialize)(int functor, void* data, const VkFunctorInitParams& params);
|
||||
void (*draw)(int functor, void* data, const VkFunctorDrawParams& params);
|
||||
void (*draw)(int functor, void* data, const VkFunctorDrawParams& params,
|
||||
const WebViewOverlayData& overlayParams);
|
||||
void (*postDraw)(int functor, void*);
|
||||
} vk;
|
||||
};
|
||||
|
||||
@@ -323,7 +323,8 @@ public:
|
||||
};
|
||||
switch (mode) {
|
||||
case RenderMode::OpenGL_ES:
|
||||
callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params) {
|
||||
callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params,
|
||||
const WebViewOverlayData& overlay_params) {
|
||||
expectOnRenderThread("draw");
|
||||
sMockFunctorCounts[functor].glesDraw++;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
|
||||
#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_FN_H_
|
||||
|
||||
#include <android/surface_control.h>
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -21,7 +22,31 @@ extern "C" {
|
||||
//
|
||||
// 1 is Android Q. This matches kAwDrawGLInfoVersion version 3.
|
||||
// 2 Adds transfer_function_* and color_space_toXYZD50 to AwDrawFn_DrawGLParams.
|
||||
static const int kAwDrawFnVersion = 2;
|
||||
// 3 Adds SurfaceControl related functions.
|
||||
static const int kAwDrawFnVersion = 3;
|
||||
|
||||
// Returns parent ASurfaceControl for WebView overlays. It will be have same
|
||||
// geometry as the surface we draw into and positioned below it (underlay).
|
||||
// This does not pass ownership to webview, but guaranteed to be alive until
|
||||
// transaction from next removeOverlays call or functor destruction will be
|
||||
// finished.
|
||||
typedef ASurfaceControl* AwDrawFn_GetSurfaceControl();
|
||||
|
||||
// Merges WebView transaction to be applied synchronously with current draw.
|
||||
// This doesn't pass ownership of the transaction, changes will be copied and
|
||||
// webview can free transaction right after the call.
|
||||
typedef void AwDrawFn_MergeTransaction(ASurfaceTransaction* transaction);
|
||||
|
||||
enum AwDrawFnOverlaysMode {
|
||||
// Indicated that webview should not promote anything to overlays this draw
|
||||
// and remove all visible overlays.
|
||||
// Added in version 3.
|
||||
AW_DRAW_FN_OVERLAYS_MODE_DISABLED = 0,
|
||||
|
||||
// Indicates that webview can use overlays.
|
||||
// Added in version 3.
|
||||
AW_DRAW_FN_OVERLAYS_MODE_ENABLED = 1,
|
||||
};
|
||||
|
||||
struct AwDrawFn_OnSyncParams {
|
||||
int version;
|
||||
@@ -60,6 +85,19 @@ struct AwDrawFn_DrawGLParams {
|
||||
float transfer_function_e;
|
||||
float transfer_function_f;
|
||||
float color_space_toXYZD50[9];
|
||||
|
||||
// Input: Indicates how webview should use overlays for this draw.
|
||||
// Added in version 3.
|
||||
AwDrawFnOverlaysMode overlays_mode;
|
||||
|
||||
// Input: WebView can call it to obtain parent surface control for overlays.
|
||||
// Added in version 3.
|
||||
AwDrawFn_GetSurfaceControl* get_surface_control;
|
||||
|
||||
// Input: WebView call this to apply ASurfaceTransaction synchronously with
|
||||
// the draw.
|
||||
// Added in version 3.
|
||||
AwDrawFn_MergeTransaction* merge_transaction;
|
||||
};
|
||||
|
||||
struct AwDrawFn_InitVkParams {
|
||||
@@ -122,12 +160,33 @@ struct AwDrawFn_DrawVkParams {
|
||||
int clip_top;
|
||||
int clip_right;
|
||||
int clip_bottom;
|
||||
|
||||
// Input: Indicates how webview should use overlays for this draw.
|
||||
// Added in version 3.
|
||||
AwDrawFnOverlaysMode overlays_mode;
|
||||
|
||||
// Input: WebView can call it to obtain parent surface control for overlays.
|
||||
// Added in version 3.
|
||||
AwDrawFn_GetSurfaceControl* get_surface_control;
|
||||
|
||||
// Input: WebView call this to apply ASurfaceTransaction synchronously with
|
||||
// the draw.
|
||||
// Added in version 3.
|
||||
AwDrawFn_MergeTransaction* merge_transaction;
|
||||
};
|
||||
|
||||
struct AwDrawFn_PostDrawVkParams {
|
||||
int version;
|
||||
};
|
||||
|
||||
struct AwDrawFn_RemoveOverlaysParams {
|
||||
int version;
|
||||
// Input: WebView call this to apply ASurfaceTransaction synchronously with
|
||||
// the draw.
|
||||
// Added in version 3.
|
||||
AwDrawFn_MergeTransaction* merge_transaction;
|
||||
};
|
||||
|
||||
// Called on render thread while UI thread is blocked. Called for both GL and
|
||||
// VK.
|
||||
typedef void AwDrawFn_OnSync(int functor,
|
||||
@@ -166,8 +225,15 @@ typedef void AwDrawFn_PostDrawVk(int functor,
|
||||
void* data,
|
||||
AwDrawFn_PostDrawVkParams* params);
|
||||
|
||||
// Can be called to make webview hide all overlays and stop updating them until
|
||||
// next draw. WebView must obtain new ASurfaceControl after this call to use as
|
||||
// parent for the overlays on next draw.
|
||||
typedef void AwDrawFn_RemoveOverlays(int functor,
|
||||
void* data,
|
||||
AwDrawFn_RemoveOverlaysParams* params);
|
||||
|
||||
struct AwDrawFnFunctorCallbacks {
|
||||
// No version here since this is passed from chromium to android.
|
||||
// version is passed in CreateFunctor call.
|
||||
AwDrawFn_OnSync* on_sync;
|
||||
AwDrawFn_OnContextDestroyed* on_context_destroyed;
|
||||
AwDrawFn_OnDestroyed* on_destroyed;
|
||||
@@ -175,6 +241,8 @@ struct AwDrawFnFunctorCallbacks {
|
||||
AwDrawFn_InitVk* init_vk;
|
||||
AwDrawFn_DrawVk* draw_vk;
|
||||
AwDrawFn_PostDrawVk* post_draw_vk;
|
||||
// Added in version 3.
|
||||
AwDrawFn_RemoveOverlays* remove_overlays;
|
||||
};
|
||||
|
||||
enum AwDrawFnRenderMode {
|
||||
@@ -185,10 +253,16 @@ enum AwDrawFnRenderMode {
|
||||
// Get the render mode. Result is static for the process.
|
||||
typedef AwDrawFnRenderMode AwDrawFn_QueryRenderMode(void);
|
||||
|
||||
// Create a functor. |functor_callbacks| should be valid until OnDestroyed.
|
||||
// This available up to version 3, use the one below.
|
||||
typedef int AwDrawFn_CreateFunctor(void* data,
|
||||
AwDrawFnFunctorCallbacks* functor_callbacks);
|
||||
|
||||
// Create a functor. |functor_callbacks| should be valid until OnDestroyed.
|
||||
typedef int AwDrawFn_CreateFunctor_v3(
|
||||
void* data,
|
||||
int version,
|
||||
AwDrawFnFunctorCallbacks* functor_callbacks);
|
||||
|
||||
// May be called on any thread to signal that the functor should be destroyed.
|
||||
// The functor will receive an onDestroyed when the last usage of it is
|
||||
// released, and it should be considered alive & active until that point.
|
||||
@@ -197,8 +271,11 @@ typedef void AwDrawFn_ReleaseFunctor(int functor);
|
||||
struct AwDrawFnFunctionTable {
|
||||
int version;
|
||||
AwDrawFn_QueryRenderMode* query_render_mode;
|
||||
// Available up to version 3.
|
||||
AwDrawFn_CreateFunctor* create_functor;
|
||||
AwDrawFn_ReleaseFunctor* release_functor;
|
||||
// Added in version 3.
|
||||
AwDrawFn_CreateFunctor_v3* create_functor_v3;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -32,6 +32,15 @@ struct SupportData {
|
||||
AwDrawFnFunctorCallbacks callbacks;
|
||||
};
|
||||
|
||||
AwDrawFnOverlaysMode GetOverlaysMode(uirenderer::OverlaysMode overlays_mode) {
|
||||
switch (overlays_mode) {
|
||||
case uirenderer::OverlaysMode::Disabled:
|
||||
return AW_DRAW_FN_OVERLAYS_MODE_DISABLED;
|
||||
case uirenderer::OverlaysMode::Enabled:
|
||||
return AW_DRAW_FN_OVERLAYS_MODE_ENABLED;
|
||||
}
|
||||
}
|
||||
|
||||
void onSync(int functor, void* data,
|
||||
const uirenderer::WebViewSyncData& syncData) {
|
||||
AwDrawFn_OnSyncParams params = {
|
||||
@@ -53,8 +62,20 @@ void onDestroyed(int functor, void* data) {
|
||||
delete support;
|
||||
}
|
||||
|
||||
void removeOverlays(int functor, void* data,
|
||||
AwDrawFn_MergeTransaction merge_transaction) {
|
||||
AwDrawFn_RemoveOverlaysParams params = {
|
||||
.version = kAwDrawFnVersion,
|
||||
.merge_transaction = merge_transaction
|
||||
};
|
||||
SupportData* support = static_cast<SupportData*>(data);
|
||||
if (support->callbacks.remove_overlays)
|
||||
support->callbacks.remove_overlays(functor, support->data, ¶ms);
|
||||
}
|
||||
|
||||
void draw_gl(int functor, void* data,
|
||||
const uirenderer::DrawGlInfo& draw_gl_params) {
|
||||
const uirenderer::DrawGlInfo& draw_gl_params,
|
||||
const uirenderer::WebViewOverlayData& overlay_params) {
|
||||
float gabcdef[7];
|
||||
draw_gl_params.color_space_ptr->transferFn(gabcdef);
|
||||
AwDrawFn_DrawGLParams params = {
|
||||
@@ -73,6 +94,9 @@ void draw_gl(int functor, void* data,
|
||||
.transfer_function_d = gabcdef[4],
|
||||
.transfer_function_e = gabcdef[5],
|
||||
.transfer_function_f = gabcdef[6],
|
||||
.overlays_mode = GetOverlaysMode(overlay_params.overlaysMode),
|
||||
.get_surface_control = overlay_params.getSurfaceControl,
|
||||
.merge_transaction = overlay_params.mergeTransaction
|
||||
};
|
||||
COMPILE_ASSERT(NELEM(params.transform) == NELEM(draw_gl_params.transform),
|
||||
mismatched_transform_matrix_sizes);
|
||||
@@ -118,7 +142,9 @@ void initializeVk(int functor, void* data,
|
||||
support->callbacks.init_vk(functor, support->data, ¶ms);
|
||||
}
|
||||
|
||||
void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw_vk_params) {
|
||||
void drawVk(int functor, void* data,
|
||||
const uirenderer::VkFunctorDrawParams& draw_vk_params,
|
||||
const uirenderer::WebViewOverlayData& overlay_params) {
|
||||
SupportData* support = static_cast<SupportData*>(data);
|
||||
float gabcdef[7];
|
||||
draw_vk_params.color_space_ptr->transferFn(gabcdef);
|
||||
@@ -142,6 +168,9 @@ void drawVk(int functor, void* data, const uirenderer::VkFunctorDrawParams& draw
|
||||
.clip_top = draw_vk_params.clip_top,
|
||||
.clip_right = draw_vk_params.clip_right,
|
||||
.clip_bottom = draw_vk_params.clip_bottom,
|
||||
.overlays_mode = GetOverlaysMode(overlay_params.overlaysMode),
|
||||
.get_surface_control = overlay_params.getSurfaceControl,
|
||||
.merge_transaction = overlay_params.mergeTransaction
|
||||
};
|
||||
COMPILE_ASSERT(sizeof(params.color_space_toXYZD50) == sizeof(skcms_Matrix3x3),
|
||||
gamut_transform_size_mismatch);
|
||||
@@ -161,12 +190,14 @@ void postDrawVk(int functor, void* data) {
|
||||
support->callbacks.post_draw_vk(functor, support->data, ¶ms);
|
||||
}
|
||||
|
||||
int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
|
||||
int CreateFunctor_v3(void* data, int version,
|
||||
AwDrawFnFunctorCallbacks* functor_callbacks) {
|
||||
static bool callbacks_initialized = false;
|
||||
static uirenderer::WebViewFunctorCallbacks webview_functor_callbacks = {
|
||||
.onSync = &onSync,
|
||||
.onContextDestroyed = &onContextDestroyed,
|
||||
.onDestroyed = &onDestroyed,
|
||||
.removeOverlays = &removeOverlays,
|
||||
};
|
||||
if (!callbacks_initialized) {
|
||||
switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
|
||||
@@ -183,8 +214,23 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
|
||||
}
|
||||
SupportData* support = new SupportData{
|
||||
.data = data,
|
||||
.callbacks = *functor_callbacks,
|
||||
};
|
||||
|
||||
// These callbacks are available on all versions.
|
||||
support->callbacks = {
|
||||
.on_sync = functor_callbacks->on_sync,
|
||||
.on_context_destroyed = functor_callbacks->on_context_destroyed,
|
||||
.on_destroyed = functor_callbacks->on_destroyed,
|
||||
.draw_gl = functor_callbacks->draw_gl,
|
||||
.init_vk = functor_callbacks->init_vk,
|
||||
.draw_vk = functor_callbacks->draw_vk,
|
||||
.post_draw_vk = functor_callbacks->post_draw_vk,
|
||||
};
|
||||
|
||||
if (version >= 3) {
|
||||
support->callbacks.remove_overlays = functor_callbacks->remove_overlays;
|
||||
}
|
||||
|
||||
int functor = uirenderer::WebViewFunctor_create(
|
||||
support, webview_functor_callbacks,
|
||||
uirenderer::WebViewFunctor_queryPlatformRenderMode());
|
||||
@@ -192,6 +238,12 @@ int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
|
||||
return functor;
|
||||
}
|
||||
|
||||
int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
|
||||
const int kVersionForDeprecatedCreateFunctor = 2;
|
||||
return CreateFunctor_v3(data, kVersionForDeprecatedCreateFunctor,
|
||||
functor_callbacks);
|
||||
}
|
||||
|
||||
void ReleaseFunctor(int functor) {
|
||||
uirenderer::WebViewFunctor_release(functor);
|
||||
}
|
||||
@@ -211,6 +263,7 @@ jlong GetDrawFnFunctionTable() {
|
||||
.query_render_mode = &QueryRenderMode,
|
||||
.create_functor = &CreateFunctor,
|
||||
.release_functor = &ReleaseFunctor,
|
||||
.create_functor_v3 = &CreateFunctor_v3,
|
||||
};
|
||||
return reinterpret_cast<intptr_t>(&function_table);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user