Skip to content

Commit

Permalink
Display a custom page for Dev Tools Discovery
Browse files Browse the repository at this point in the history
This enables a custom resource handler for CEF to allow overriding of
individual internal files that CEF attempts to load.

The Dev Tools Discovery page is exposed via Remote Debugging & provides
a list of all loaded browser pages to easily open Dev Tools from a
single location.

This updated version introduces a number of enhancements:
1. Items are now ordered by category
2. Sub-pages are listed under parent pages
3. Built-in refresh button
4. OBS version number in page body based on User Agent
5. Clicking items opens them in a new tab by default
6. Custom styling
  • Loading branch information
WizardCM authored and Lain-B committed May 21, 2023
1 parent 995ad53 commit 2e7030c
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 0 deletions.
38 changes: 38 additions & 0 deletions browser-app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ CefRefPtr<CefBrowserProcessHandler> BrowserApp::GetBrowserProcessHandler()
return this;
}

CefRefPtr<CefResourceBundleHandler> BrowserApp::GetResourceBundleHandler()
{
return this;
}

BrowserApp::~BrowserApp()
{
if (devToolsFile != nullptr)
free(devToolsFile);
}

void BrowserApp::OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar)
{
registrar->AddCustomScheme("http",
Expand Down Expand Up @@ -407,6 +418,33 @@ bool BrowserApp::Execute(const CefString &name, CefRefPtr<CefV8Value>,
return true;
}

bool BrowserApp::GetDataResource(int resource_id, void *&data,
size_t &data_size)
{
if (resource_id == IDR_CEF_DEVTOOLS_DISCOVERY_PAGE &&
devToolsFile != nullptr) {
data = devToolsFile;
data_size = strlen(devToolsFile);
return true;
}
return false;
}

bool BrowserApp::GetLocalizedString(int, CefString &)
{
return false;
};

bool BrowserApp::GetDataResourceForScale(int, ScaleFactor, void *&, size_t &)
{
return false;
};

void BrowserApp::SetDevToolsFile(char *data)
{
devToolsFile = strdup(data);
}

#ifdef ENABLE_BROWSER_QT_LOOP
Q_DECLARE_METATYPE(MessageTask);
MessageObject messageObject;
Expand Down
16 changes: 16 additions & 0 deletions browser-app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ extern void QueueBrowserTask(CefRefPtr<CefBrowser> browser, BrowserFunc func);
class BrowserApp : public CefApp,
public CefRenderProcessHandler,
public CefBrowserProcessHandler,
public CefResourceBundleHandler,
public CefV8Handler {

void ExecuteJSFunction(CefRefPtr<CefBrowser> browser,
Expand All @@ -77,17 +78,21 @@ class BrowserApp : public CefApp,
bool shared_texture_available;
CallbackMap callbackMap;
int callbackId;
char *devToolsFile;

public:
inline BrowserApp(bool shared_texture_available_ = false)
: shared_texture_available(shared_texture_available_)
{
}
~BrowserApp();

virtual CefRefPtr<CefRenderProcessHandler>
GetRenderProcessHandler() override;
virtual CefRefPtr<CefBrowserProcessHandler>
GetBrowserProcessHandler() override;
virtual CefRefPtr<CefResourceBundleHandler>
GetResourceBundleHandler() override;
virtual void OnBeforeChildProcessLaunch(
CefRefPtr<CefCommandLine> command_line) override;
virtual void OnRegisterCustomSchemes(
Expand All @@ -109,6 +114,16 @@ class BrowserApp : public CefApp,
CefRefPtr<CefV8Value> &retval,
CefString &exception) override;

/* CefResourceBundleHandler */
virtual bool GetDataResource(int resource_id, void *&data,
size_t &data_size) override;
virtual bool GetLocalizedString(int message_id,
CefString &string) override;
virtual bool GetDataResourceForScale(int resource_id,
ScaleFactor scale_factor,
void *&data,
size_t &data_size) override;

#ifdef ENABLE_BROWSER_QT_LOOP
virtual void OnScheduleMessagePumpWork(int64 delay_ms) override;
QTimer frameTimer;
Expand All @@ -123,6 +138,7 @@ class BrowserApp : public CefApp,
void SetDocumentVisibility(CefRefPtr<CefBrowser> browser,
bool isVisible);
#endif
void SetDevToolsFile(char *data);

IMPLEMENT_REFCOUNTING(BrowserApp);
};
2 changes: 2 additions & 0 deletions cef-headers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <include/cef_render_process_handler.h>
#include <include/cef_request_context_handler.h>
#include <include/cef_jsdialog_handler.h>
#include <include/cef_resource_bundle_handler.h>
#include <include/cef_pack_resources.h>
#if defined(__APPLE__)
#include "include/wrapper/cef_library_loader.h"
#endif
Expand Down
144 changes: 144 additions & 0 deletions data/dev_tools_discovery.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<html>

<head>
<!-- Custom variant of dev_tools_discovery.html used internally by CEF -->
<title>OBS Browser Remote Debugging</title>
<style>
html {
font-family: Arial, Helvetica, sans-serif;
}

button#refresh {
vertical-align: text-bottom;
}

#items>li>button {
background: none;
border: none;
cursor: pointer;
padding-top: 0;
padding-bottom: 0;
}

a {
color: #585858;
}

a:visited {
color: #7e7070;
}

a:hover {
color: #000;
text-decoration: none;
}

.hidden {
display: none;
}
</style>

<script>
let children = {};
function onLoad() {
const list = document.getElementById('items');
while (list.firstChild) {
list.removeChild(list.firstChild);
}
const t = new Date().getTime();
fetch(`/json/list?t=${t}`)
.then(resp => resp.json())
.then(onFetch)
.catch(() => {
list.innerText = 'Failed to fetch list. Did OBS close?';
});
fetch(`/json/version?t=${t}`)
.then(resp => resp.json())
.then(onVersion);
}

function onFetch(resp) {
resp.sort((a, b) => {
const p = 'page';
if (a.type === p && b.type !== p)
return -1;
if (a.type !== p && b.type === p)
return 1;
return 0;
});
for (const el of resp) {
children[el.id] = [];
}
for (const el of resp) {
if (el.parentId) children[el.parentId].push(el);
}
generateList(resp);
}

function onVersion(resp) {
const v = document.getElementById('version');
const uA = resp['User-Agent'].split(' ');
const rV = uA.filter(r => r.startsWith('OBS/'));
v.innerText = rV[0].replace('/', ' ');
}

function generateList(resp) {
for (const el of resp) {
appendItem(el);
}
}

function makeLink(devtoolsFrontendUrl, url, title) {
let linkEl;
const listItem = document.createElement('li');
if (devtoolsFrontendUrl) {
linkEl = document.createElement('a');
linkEl.title = url;
linkEl.href = devtoolsFrontendUrl;
linkEl.target = '_blank';
} else {
linkEl = document.createElement('span');
linkEl.title = 'Already debugging';
}
const text = document.createElement('span');
text.innerText = title || '(untitled tab)';

linkEl.appendChild(text);
listItem.appendChild(linkEl);
return listItem;
}

function appendChildren(item, idChildren) {
const childList = document.createElement('ul');
for (const ch of idChildren) {
const framePath = new URL(ch.title);
const innerTitle = `${framePath.hostname}${framePath.pathname}`;
const itemChild = makeLink(ch.devtoolsFrontendUrl, ch.url, innerTitle);
childList.appendChild(itemChild);
}
item.appendChild(childList);
}

function appendItem(data) {
const { title, devtoolsFrontendUrl, id, parentId, type, url } = data;
if (type !== 'page') return;
const item = makeLink(devtoolsFrontendUrl, url, title);

if (children[id].length) appendChildren(item, children[id]);
document.getElementById('items').appendChild(item);
}
</script>
</head>

<body onload="onLoad()">
<h3 id="heading">
<span id="version"></span>
<span>Inspectable Browser Sources & Docks</span>
<button id="refresh" onclick="onLoad()" type="button">&#10227; Refresh</button>
</h3>
<ul id="items">

</ul>
</body>

</html>
4 changes: 4 additions & 0 deletions obs-browser-plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ static void BrowserInit(void)

app = new BrowserApp(tex_sharing_avail);

BPtr<char> pagesPath = obs_module_file("dev_tools_discovery.html");
BPtr<char> pagesFile = os_quick_read_utf8_file(pagesPath);
app->SetDevToolsFile(pagesFile);

#ifdef _WIN32
CefExecuteProcess(args, app, nullptr);
#endif
Expand Down

0 comments on commit 2e7030c

Please sign in to comment.