Compare commits
99
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db070f0003 | ||
|
|
628ee50c99 | ||
|
|
0048d50fa9 | ||
|
|
9dedcd5fe5 | ||
|
|
1fff802335 | ||
|
|
bd49435b11 | ||
|
|
81e10aa37e | ||
|
|
0c10df5c94 | ||
|
|
07259a27f0 | ||
|
|
b2594a4ace | ||
|
|
ce61835a81 | ||
|
|
30e29845c4 | ||
|
|
d8662c5dd9 | ||
|
|
8a2a9ead92 | ||
|
|
efdbd34b0b | ||
|
|
d2fa69f593 | ||
|
|
18d0a1a525 | ||
|
|
0a5e50a9fe | ||
|
|
3f5b7ed020 | ||
|
|
9d9802c660 | ||
|
|
27c579652a | ||
|
|
8ffa6b3c13 | ||
|
|
83f6efc33d | ||
|
|
47f1928610 | ||
|
|
7b3d961465 | ||
|
|
22b2e18d91 | ||
|
|
f6c9c0c59f | ||
|
|
d8c9d24a9c | ||
|
|
a466b73267 | ||
|
|
1ce14fb014 | ||
|
|
8bbf60fb38 | ||
|
|
f2d7c02290 | ||
|
|
65ddfc21b6 | ||
|
|
163619de60 | ||
|
|
29c4d554a0 | ||
|
|
3e59569d0b | ||
|
|
fb8483407b | ||
|
|
ec8b7f86d0 | ||
|
|
9899d59010 | ||
|
|
789e976ace | ||
|
|
d5bf2555a3 | ||
|
|
be672813f6 | ||
|
|
746a7be08c | ||
|
|
c222ee80fa | ||
|
|
4950bd038d | ||
|
|
058336a071 | ||
|
|
6823388a85 | ||
|
|
b68d097eb6 | ||
|
|
c97a22776d | ||
|
|
c484dbcca5 | ||
|
|
54f15d7afe | ||
|
|
05441188be | ||
|
|
25a2d31be7 | ||
|
|
58a4e0d545 | ||
|
|
ad06b4ce5e | ||
|
|
be3827b088 | ||
|
|
a136aaf502 | ||
|
|
40d91a8eae | ||
|
|
5b81a81ac4 | ||
|
|
328ddb2927 | ||
|
|
06b9622302 | ||
|
|
1218649202 | ||
|
|
cbea8f5a53 | ||
|
|
f42e644486 | ||
|
|
a40490b946 | ||
|
|
ad98693f3d | ||
|
|
7b4aaca975 | ||
|
|
400e59a96d | ||
|
|
09276b72a6 | ||
|
|
ec18304362 | ||
|
|
4cf411f393 | ||
|
|
a0f135b94f | ||
|
|
2b0935adf4 | ||
|
|
cbdc5fa85f | ||
|
|
2a06c40a09 | ||
|
|
f499e3dd36 | ||
|
|
48d21fa2bf | ||
|
|
c91c69272b | ||
|
|
91f4fa744e | ||
|
|
c2abd4aaa6 | ||
|
|
617ad84f96 | ||
|
|
33b6bfa1b9 | ||
|
|
4e03afd191 | ||
|
|
3512b73bc3 | ||
|
|
d0ac45cb08 | ||
|
|
ab3d950bd3 | ||
|
|
44ddd79356 | ||
|
|
054ddf661f | ||
|
|
da466a3bdd | ||
|
|
42f0c2d78f | ||
|
|
5853784a31 | ||
|
|
5b20295620 | ||
|
|
68e151c2a3 | ||
|
|
dd764a70a2 | ||
|
|
12defe0d12 | ||
|
|
8868dd5785 | ||
|
|
e787d585a4 | ||
|
|
5eb7518ce2 | ||
|
|
5be93da111 |
No files matched your search
@@ -0,0 +1,8 @@
|
||||
TRADEMARK NOTICE
|
||||
================
|
||||
|
||||
The name "Goanna" and visual representations (logo) have not been
|
||||
registered by the Copyright owners as a trademark in any jurisdiction.
|
||||
However, because Goanna has been distributed and maintained world-wide,
|
||||
continually since 2015, the Copyright owners claim "common-law trademark
|
||||
protection" in any jurisdiction where common-law trademark is recognized.
|
||||
@@ -18,10 +18,6 @@
|
||||
#endif
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
extern "C" __attribute__((weak,visibility("default"))) int atk_bridge_adaptor_init(int*, char **[]);
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@@ -33,12 +29,20 @@ extern "C" {
|
||||
typedef GType (* AtkGetTypeType) (void);
|
||||
typedef void (*GnomeAccessibilityInit) (void);
|
||||
typedef void (*GnomeAccessibilityShutdown) (void);
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
typedef int (*AtkBridgeAdaptorInit) (int*, char***);
|
||||
#endif
|
||||
}
|
||||
|
||||
static PRLibrary* sATKLib = nullptr;
|
||||
static const char sATKLibName[] = "libatk-1.0.so.0";
|
||||
static const char sATKHyperlinkImplGetTypeSymbol[] =
|
||||
"atk_hyperlink_impl_get_type";
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
static PRLibrary* sAtkBridgeAdaptorLib = nullptr;
|
||||
static const char sAtkBridgeAdaptorLibName[] = "libatk-bridge-2.0.so.0";
|
||||
static const char sAtkBridgeAdaptorInitSymbol[] = "atk_bridge_adaptor_init";
|
||||
#endif
|
||||
|
||||
gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
|
||||
gpointer);
|
||||
@@ -127,6 +131,38 @@ LoadGtkModule(GnomeAccessibilityModule& aModule)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
static bool
|
||||
InitAtkBridgeAdaptor()
|
||||
{
|
||||
AtkBridgeAdaptorInit initBridge =
|
||||
reinterpret_cast<AtkBridgeAdaptorInit>(
|
||||
PR_FindFunctionSymbolAndLibrary(sAtkBridgeAdaptorInitSymbol,
|
||||
&sAtkBridgeAdaptorLib));
|
||||
|
||||
if (!initBridge) {
|
||||
sAtkBridgeAdaptorLib = PR_LoadLibrary(sAtkBridgeAdaptorLibName);
|
||||
if (sAtkBridgeAdaptorLib) {
|
||||
initBridge =
|
||||
reinterpret_cast<AtkBridgeAdaptorInit>(
|
||||
PR_FindFunctionSymbol(sAtkBridgeAdaptorLib,
|
||||
sAtkBridgeAdaptorInitSymbol));
|
||||
if (!initBridge) {
|
||||
PR_UnloadLibrary(sAtkBridgeAdaptorLib);
|
||||
sAtkBridgeAdaptorLib = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!initBridge) {
|
||||
return false;
|
||||
}
|
||||
|
||||
initBridge(nullptr, nullptr);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
a11y::PlatformInit()
|
||||
{
|
||||
@@ -181,9 +217,7 @@ a11y::PlatformInit()
|
||||
// Init atk-bridge now
|
||||
PR_SetEnv("NO_AT_BRIDGE=0");
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
if (atk_bridge_adaptor_init) {
|
||||
atk_bridge_adaptor_init(nullptr, nullptr);
|
||||
} else
|
||||
if (!InitAtkBridgeAdaptor())
|
||||
#endif
|
||||
{
|
||||
nsresult rv = LoadGtkModule(sAtkBridge);
|
||||
@@ -228,6 +262,13 @@ a11y::PlatformShutdown()
|
||||
sAtkBridge.init = nullptr;
|
||||
sAtkBridge.shutdown = nullptr;
|
||||
}
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
if (sAtkBridgeAdaptorLib) {
|
||||
// Do not shutdown/unload atk-bridge; an exit function registered by
|
||||
// the bridge will take care of it.
|
||||
sAtkBridgeAdaptorLib = nullptr;
|
||||
}
|
||||
#endif
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
if (sGail.lib) {
|
||||
// Do not shutdown gail because
|
||||
|
||||
@@ -43,8 +43,6 @@ gyp_vars.update({
|
||||
'arm_neon': 0,
|
||||
'arm_neon_optional': 1,
|
||||
|
||||
'moz_webrtc_omx': 0,
|
||||
'moz_webrtc_mediacodec': 0,
|
||||
|
||||
# Turn off multi monitor screen share
|
||||
'multi_monitor_screenshare%' : 0,
|
||||
|
||||
@@ -344,7 +344,7 @@ def split_triplet(triplet):
|
||||
if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu):
|
||||
canonical_cpu = 'x86'
|
||||
endianness = 'little'
|
||||
elif cpu in ('x86_64', 'ia64'):
|
||||
elif cpu == 'x86_64':
|
||||
canonical_cpu = cpu
|
||||
endianness = 'little'
|
||||
elif cpu in ('s390', 's390x'):
|
||||
|
||||
@@ -164,6 +164,7 @@ def old_configure_options(*options):
|
||||
'--enable-negotiateauth',
|
||||
'--enable-nfc',
|
||||
'--enable-npapi',
|
||||
'--enable-npapi-gtk2',
|
||||
'--enable-nspr-build',
|
||||
'--enable-official-branding',
|
||||
'--enable-official-vendor',
|
||||
|
||||
@@ -88,6 +88,11 @@ check_and_add_gcc_warning('-Wno-error=deprecated-declarations')
|
||||
# false positives depending on optimization
|
||||
check_and_add_gcc_warning('-Wno-error=array-bounds')
|
||||
|
||||
# GCC 9+ value-range analysis trips -Wformat-overflow on null %s args in debug
|
||||
# spew (e.g. js/src/jit/x64/BaseAssembler-x64.h:595); harmless for our build,
|
||||
# and js/src forces -Werror=format so this must be downgraded here to reach it.
|
||||
check_and_add_gcc_warning('-Wno-error=format-overflow')
|
||||
|
||||
# can't get rid of those PGO warnings
|
||||
check_and_add_gcc_warning('-Wno-error=coverage-mismatch', when='MOZ_PGO')
|
||||
|
||||
|
||||
@@ -290,11 +290,6 @@ fi
|
||||
if [ -n "$LD_LIBRARY_PATH_64" ]; then
|
||||
LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"}
|
||||
fi
|
||||
#
|
||||
#
|
||||
## Set SHLIB_PATH for HPUX
|
||||
SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"}
|
||||
#
|
||||
## Set LIBPATH for AIX
|
||||
LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"}
|
||||
#
|
||||
@@ -339,7 +334,6 @@ then
|
||||
fi
|
||||
echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH"
|
||||
echo " LIBRARY_PATH=$LIBRARY_PATH"
|
||||
echo " SHLIB_PATH=$SHLIB_PATH"
|
||||
echo " LIBPATH=$LIBPATH"
|
||||
echo " ADDON_PATH=$ADDON_PATH"
|
||||
echo " MOZ_PROGRAM=$MOZ_PROGRAM"
|
||||
@@ -350,7 +344,7 @@ then
|
||||
fi
|
||||
#
|
||||
export MOZILLA_FIVE_HOME LD_LIBRARY_PATH
|
||||
export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
|
||||
export LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH
|
||||
|
||||
if [ $moz_debug -eq 1 ]
|
||||
then
|
||||
|
||||
@@ -448,7 +448,6 @@ gtk/gtkx.h
|
||||
gtk/gtkunixprint.h
|
||||
HIToolbox/HIToolbox.h
|
||||
hlink.h
|
||||
ia64/sys/inline.h
|
||||
Icons.h
|
||||
iconv.h
|
||||
ieeefp.h
|
||||
|
||||
@@ -204,21 +204,22 @@ function initialHTML(doc) {
|
||||
os = "linux";
|
||||
}
|
||||
|
||||
let base = doc.createElement("base");
|
||||
base.href = "resource://devtools/client/jsonview/";
|
||||
// The appRoot URI is prepended to all URIs instead of using a <base> element
|
||||
// because the latter can cause spurious network requests (UXP Issue #3122)
|
||||
let appRoot = "resource://devtools/client/jsonview/";
|
||||
|
||||
let style = doc.createElement("link");
|
||||
style.rel = "stylesheet";
|
||||
style.type = "text/css";
|
||||
style.href = "css/main.css";
|
||||
style.href = appRoot +"css/main.css";
|
||||
|
||||
let script = doc.createElement("script");
|
||||
script.src = "lib/require.js";
|
||||
script.dataset.main = "viewer-config";
|
||||
script.src = appRoot + "lib/require.js";
|
||||
script.dataset.main = appRoot + "viewer-config.js";
|
||||
script.defer = true;
|
||||
|
||||
let head = doc.createElement("head");
|
||||
head.append(base, style, script);
|
||||
head.append(style, script);
|
||||
|
||||
return "<!DOCTYPE html>\n" +
|
||||
startTag("html", {
|
||||
|
||||
@@ -4,6 +4,8 @@ subsuite = devtools
|
||||
support-files =
|
||||
array_json.json
|
||||
array_json.json^headers^
|
||||
csp_json.json
|
||||
csp_json.json^headers^
|
||||
doc_frame_script.js
|
||||
head.js
|
||||
invalid_json.json
|
||||
@@ -22,6 +24,7 @@ subsuite = clipboard
|
||||
subsuite = clipboard
|
||||
[browser_jsonview_copy_rawdata.js]
|
||||
subsuite = clipboard
|
||||
[browser_jsonview_csp_json.js]
|
||||
[browser_jsonview_filter.js]
|
||||
[browser_jsonview_invalid_json.js]
|
||||
[browser_jsonview_valid_json.js]
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_JSON_URL = URL_ROOT + "csp_json.json";
|
||||
|
||||
add_task(function* () {
|
||||
info("Test CSP JSON started");
|
||||
|
||||
yield addJsonViewTab(TEST_JSON_URL);
|
||||
|
||||
let count = yield getElementCount(".jsonPanelBox .treeTable .treeRow");
|
||||
is(count, 1, "There must be one row");
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"csp": true}
|
||||
@@ -0,0 +1,2 @@
|
||||
Content-Type: application/json
|
||||
Content-Security-Policy: default-src 'none'; base-uri 'none';
|
||||
@@ -23,7 +23,7 @@
|
||||
* of the code base, so it's consistent and modules can be easily reused.
|
||||
*/
|
||||
require.config({
|
||||
baseUrl: ".",
|
||||
baseUrl: "resource://devtools/client/jsonview/",
|
||||
paths: {
|
||||
"devtools/client/shared": "resource://devtools/client/shared",
|
||||
"devtools/shared": "resource://devtools/shared",
|
||||
|
||||
@@ -103,12 +103,6 @@ function StyleSheetEditor(styleSheet, win, file, isNew, walker, highlighter) {
|
||||
}
|
||||
};
|
||||
|
||||
this._styleSheetFilePath = null;
|
||||
if (styleSheet.href &&
|
||||
Services.io.extractScheme(this.styleSheet.href) == "file") {
|
||||
this._styleSheetFilePath = this.styleSheet.href;
|
||||
}
|
||||
|
||||
this._onPropertyChange = this._onPropertyChange.bind(this);
|
||||
this._onError = this._onError.bind(this);
|
||||
this._onMediaRuleMatchesChange = this._onMediaRuleMatchesChange.bind(this);
|
||||
@@ -664,8 +658,7 @@ StyleSheetEditor.prototype = {
|
||||
if (this._friendlyName) {
|
||||
defaultName = OS.Path.basename(this._friendlyName);
|
||||
}
|
||||
showFilePicker(file || this._styleSheetFilePath, true, this._window,
|
||||
onFile, defaultName);
|
||||
showFilePicker(file, true, this._window, onFile, defaultName);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,98 +1,112 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that 'Save' function works.
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
|
||||
var tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
var FileUtils = tempScope.FileUtils;
|
||||
var NetUtil = tempScope.NetUtil;
|
||||
|
||||
add_task(function* () {
|
||||
let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
|
||||
yield copy(TESTCASE_URI_CSS, "simple.css");
|
||||
let uri = Services.io.newFileURI(htmlFile);
|
||||
let filePath = uri.resolve("");
|
||||
|
||||
let { ui } = yield openStyleEditorForURL(filePath);
|
||||
|
||||
let editor = ui.editors[0];
|
||||
yield editor.getSourceEditor();
|
||||
|
||||
info("Editing the style sheet.");
|
||||
let dirty = editor.sourceEditor.once("dirty-change");
|
||||
let beginCursor = {line: 0, ch: 0};
|
||||
editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
|
||||
ok(editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is present in the corresponding summary.");
|
||||
|
||||
info("Saving the changes.");
|
||||
dirty = editor.sourceEditor.once("dirty-change");
|
||||
|
||||
editor.saveToFile(null, function (file) {
|
||||
ok(file, "file should get saved directly when using a file:// URI");
|
||||
});
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), true, "Editor is clean.");
|
||||
ok(!editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is not present in the corresponding summary.");
|
||||
});
|
||||
|
||||
function copy(srcChromeURL, destFileName) {
|
||||
let deferred = defer();
|
||||
let destFile = FileUtils.getFile("ProfD", [destFileName]);
|
||||
write(read(srcChromeURL), destFile, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function read(srcChromeURL) {
|
||||
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.getService(Ci.nsIScriptableInputStream);
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: srcChromeURL,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let input = channel.open2();
|
||||
scriptableStream.init(input);
|
||||
|
||||
let data = "";
|
||||
while (input.available()) {
|
||||
data = data.concat(scriptableStream.read(input.available()));
|
||||
}
|
||||
scriptableStream.close();
|
||||
input.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function write(data, file, callback) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let istream = converter.convertToInputStream(data);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function (status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
info("Couldn't write to " + file.path);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(file);
|
||||
});
|
||||
}
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
// Test that 'Save' function works.
|
||||
|
||||
const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
|
||||
const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
|
||||
|
||||
var tempScope = {};
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
|
||||
var FileUtils = tempScope.FileUtils;
|
||||
var NetUtil = tempScope.NetUtil;
|
||||
|
||||
add_task(function* () {
|
||||
let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
|
||||
const cssFile = await copy(TESTCASE_URI_CSS, "simple.css");
|
||||
let uri = Services.io.newFileURI(htmlFile);
|
||||
let filePath = uri.resolve("");
|
||||
|
||||
let { ui } = yield openStyleEditorForURL(filePath);
|
||||
|
||||
let editor = ui.editors[0];
|
||||
yield editor.getSourceEditor();
|
||||
|
||||
is(
|
||||
editor.savedFile,
|
||||
null,
|
||||
"savedFile should not be pre-populated from the source file"
|
||||
);
|
||||
|
||||
info("Editing the style sheet.");
|
||||
let dirty = editor.sourceEditor.once("dirty-change");
|
||||
let beginCursor = {line: 0, ch: 0};
|
||||
editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
|
||||
ok(editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is present in the corresponding summary.");
|
||||
|
||||
info(
|
||||
"Saving the changes with an explicit file (simulating a user-chosen save location)."
|
||||
);
|
||||
dirty = editor.sourceEditor.once("dirty-change");
|
||||
|
||||
editor.saveToFile(cssFile, function (file) {
|
||||
ok(file, "file should get saved when explicitly passing a file");
|
||||
});
|
||||
|
||||
yield dirty;
|
||||
|
||||
is(editor.sourceEditor.isClean(), true, "Editor is clean.");
|
||||
ok(!editor.summary.classList.contains("unsaved"),
|
||||
"Star icon is not present in the corresponding summary.");
|
||||
|
||||
is(
|
||||
editor.savedFile?.path,
|
||||
cssFile.path,
|
||||
"savedFile should now be set on the editor"
|
||||
);
|
||||
});
|
||||
|
||||
function copy(srcChromeURL, destFileName) {
|
||||
let deferred = defer();
|
||||
let destFile = FileUtils.getFile("ProfD", [destFileName]);
|
||||
write(read(srcChromeURL), destFile, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function read(srcChromeURL) {
|
||||
let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.getService(Ci.nsIScriptableInputStream);
|
||||
|
||||
let channel = NetUtil.newChannel({
|
||||
uri: srcChromeURL,
|
||||
loadUsingSystemPrincipal: true
|
||||
});
|
||||
let input = channel.open2();
|
||||
scriptableStream.init(input);
|
||||
|
||||
let data = "";
|
||||
while (input.available()) {
|
||||
data = data.concat(scriptableStream.read(input.available()));
|
||||
}
|
||||
scriptableStream.close();
|
||||
input.close();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function write(data, file, callback) {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
|
||||
converter.charset = "UTF-8";
|
||||
|
||||
let istream = converter.convertToInputStream(data);
|
||||
let ostream = FileUtils.openSafeFileOutputStream(file);
|
||||
|
||||
NetUtil.asyncCopy(istream, ostream, function (status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
info("Couldn't write to " + file.path);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(file);
|
||||
});
|
||||
}
|
||||
@@ -28,7 +28,10 @@ add_task(function* () {
|
||||
let HTMLFile = yield copy(TESTCASE_URI_HTML, ["sourcemaps.html"]);
|
||||
let CSSFile = yield copy(TESTCASE_URI_CSS,
|
||||
["sourcemap-css", "sourcemaps.css"]);
|
||||
yield copy(TESTCASE_URI_SCSS, ["sourcemap-sass", "sourcemaps.scss"]);
|
||||
const SCSSFile = await copy(TESTCASE_URI_SCSS, [
|
||||
"sourcemap-sass",
|
||||
"sourcemaps.scss",
|
||||
]);
|
||||
yield copy(TESTCASE_URI_MAP, ["sourcemap-css", "sourcemaps.css.map"]);
|
||||
yield copy(TESTCASE_URI_REG_CSS, ["simple.css"]);
|
||||
|
||||
@@ -59,7 +62,7 @@ add_task(function* () {
|
||||
|
||||
// Edit and save Sass in the editor. This will start off a file-watching
|
||||
// process waiting for the CSS file to change.
|
||||
yield editSCSS(editor);
|
||||
yield editSCSS(SCSSFile, editor);
|
||||
|
||||
// We can't run Sass or another compiler, so we fake it by just
|
||||
// directly changing the CSS file.
|
||||
@@ -73,12 +76,12 @@ add_task(function* () {
|
||||
is(color, "rgb(0, 0, 255)", "div is blue after saving file");
|
||||
});
|
||||
|
||||
function editSCSS(editor) {
|
||||
function editSCSS(SCSSFile, editor) {
|
||||
let deferred = defer();
|
||||
|
||||
editor.sourceEditor.setText(CSS_TEXT);
|
||||
|
||||
editor.saveToFile(null, function (file) {
|
||||
editor.saveToFile(SCSSFile, function (file) {
|
||||
ok(file, "Scss file should be saved");
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ static RedirEntry kRedirMap[] = {
|
||||
{ "config", "chrome://global/content/config.xul", 0 },
|
||||
{ "console", "chrome://global/content/console.xul", 0 },
|
||||
{
|
||||
"credits", "http://www.palemoon.org/Contributors.shtml",
|
||||
"credits", "https://net.riverstonerelay.jihad-browser/credits",
|
||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
||||
nsIAboutModule::MAKE_LINKABLE
|
||||
},
|
||||
|
||||
@@ -199,6 +199,7 @@
|
||||
#include "nsICommandManager.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
#include "nsIBadCertListener2.h" // JIHAD: route cert errors to the embedder chrome
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIIDNService.h"
|
||||
#include "nsIInputStreamChannel.h"
|
||||
@@ -1074,6 +1075,33 @@ nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
|
||||
} else if (aIID.Equals(NS_GET_IID(nsITabChild))) {
|
||||
*aSink = GetTabChild().take();
|
||||
return *aSink ? NS_OK : NS_ERROR_FAILURE;
|
||||
} else if (aIID.Equals(NS_GET_IID(nsIBadCertListener2))) {
|
||||
// JIHAD: hand the embedder's chrome the certificate error.
|
||||
//
|
||||
// NSS asks the channel's notification callbacks for nsIBadCertListener2
|
||||
// (SSLServerCertVerification.cpp, CertErrorRunnable::CheckCertOverrides) and passes
|
||||
// it the nsISSLStatus carrying the SERVER CERTIFICATE. That call is the one and only
|
||||
// place an embedder is handed the cert while it still exists — the same shape the
|
||||
// Atlas browser gets for free from WebKit's load-failed-with-tls-errors signal, which
|
||||
// delivers the GTlsCertificate as a signal argument.
|
||||
//
|
||||
// Without this hop nothing answers: the docShell's fallthrough is
|
||||
// nsDocLoader::GetInterface, which Queries the docloader ITSELF, so the request never
|
||||
// reaches nsIWebBrowserChrome. The embedder is then left trying to recover the cert
|
||||
// afterwards, and there is no way to: the failed channel's securityInfo has a null
|
||||
// SSLStatus, and UXP has no nsIRecentBadCerts service. Measured 2026-08-04 — the
|
||||
// SSL-confirm dialog worked, the user accepted, and the accept could do nothing
|
||||
// because RememberValidityOverride had no nsIX509Cert to take.
|
||||
//
|
||||
// Routed through the tree owner, exactly as nsIWebBrowserChromeFocus and nsIPrompt
|
||||
// already are (nsDocShellTreeOwner::GetInterface).
|
||||
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
||||
GetTreeOwner(getter_AddRefs(treeOwner));
|
||||
nsCOMPtr<nsIInterfaceRequestor> req(do_QueryInterface(treeOwner));
|
||||
if (req) {
|
||||
return req->GetInterface(aIID, aSink);
|
||||
}
|
||||
return NS_NOINTERFACE;
|
||||
} else if (aIID.Equals(NS_GET_IID(nsIContentFrameMessageManager))) {
|
||||
nsCOMPtr<nsITabChild> tabChild =
|
||||
do_GetInterface(static_cast<nsIDocShell*>(this));
|
||||
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
int32_t written = PR_Write(mFD, mData, mLength);
|
||||
int32_t written = mLength < INT32_MAX ? PR_Write(mFD, mData, mLength) : -1;
|
||||
if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
|
||||
return NS_DispatchToMainThread(
|
||||
new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE));
|
||||
|
||||
@@ -787,6 +787,12 @@ nsresult
|
||||
nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_NPAPI
|
||||
// JIHAD DIAGNOSTIC (R7): entry + the four early-out conditions. If this line never appears the
|
||||
// plugin was never asked for at all (no nsPluginFrame -> no HasNewFrame), which is a different
|
||||
// bug from being asked for and refused.
|
||||
fprintf(stderr, "[jihad-npapi] InstantiatePluginInstance(aIsLoading=%d) owner=%d type=%d "
|
||||
"mIsLoading=%d instantiating=%d\n", (int)aIsLoading, (int)!!mInstanceOwner,
|
||||
(int)mType, (int)mIsLoading, (int)mInstantiating);
|
||||
if (mInstanceOwner || mType != eType_Plugin || (mIsLoading != aIsLoading) ||
|
||||
mInstantiating) {
|
||||
// If we hit this assertion it's probably because LoadObject re-entered :(
|
||||
@@ -822,10 +828,12 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
|
||||
NS_ENSURE_TRUE(mInstantiating, NS_OK);
|
||||
|
||||
if (!thisContent->GetPrimaryFrame()) {
|
||||
// JIHAD DIAGNOSTIC (R7): a silent NS_OK return — the single most likely place for a plugin
|
||||
// to "do nothing" with no error anywhere.
|
||||
fprintf(stderr, "[jihad-npapi] NO PRIMARY FRAME -> not instantiating\n");
|
||||
LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
||||
|
||||
@@ -846,6 +854,8 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading)
|
||||
rv = pluginHost->InstantiatePluginInstance(mContentType,
|
||||
mURI.get(), this,
|
||||
getter_AddRefs(newOwner));
|
||||
fprintf(stderr, "[jihad-npapi] pluginHost->InstantiatePluginInstance rv=0x%x owner=%d\n",
|
||||
(unsigned)rv, (int)!!newOwner);
|
||||
|
||||
// XXX(johns): We don't suspend native inside stopping plugins...
|
||||
if (appShell) {
|
||||
@@ -2378,6 +2388,11 @@ nsObjectLoadingContent::LoadObject(bool aNotify,
|
||||
FallbackType clickToPlayReason;
|
||||
if (!mActivated && (mType == eType_Null || mType == eType_Plugin) &&
|
||||
!ShouldPlay(clickToPlayReason, false)) {
|
||||
// JIHAD DIAGNOSTIC (R7): this is where a plugin that resolved fine is silently demoted to
|
||||
// eType_Null. Every path out of ShouldPlay that returns false lands here, so one line here
|
||||
// names the reason instead of instrumenting each exit.
|
||||
fprintf(stderr, "[jihad-npapi] demoted to fallback: mType=%d fallbackReason=%d\n",
|
||||
(int)mType, (int)clickToPlayReason);
|
||||
LOG(("OBJLC [%p]: Marking plugin as click-to-play", this));
|
||||
mType = eType_Null;
|
||||
fallbackType = clickToPlayReason;
|
||||
@@ -2887,9 +2902,16 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
|
||||
|
||||
#ifdef MOZ_ENABLE_NPAPI
|
||||
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
|
||||
if ((caps & eSupportPlugins) &&
|
||||
pluginHost &&
|
||||
pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone)) {
|
||||
bool jihadHave = pluginHost &&
|
||||
pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone);
|
||||
// JIHAD DIAGNOSTIC (R7 windowless port): says, for one MIME type, whether the element
|
||||
// advertised plugin capability and whether the host has a plugin — the two facts that decide
|
||||
// if the NPAPI subsystem is entered at all. Added because a plugin that never instantiates
|
||||
// produces NO plugin logging whatsoever, which is indistinguishable from one that was never
|
||||
// asked for.
|
||||
fprintf(stderr, "[jihad-npapi] GetTypeOfContent mime=%s caps=0x%x supportsPlugins=%d have=%d\n",
|
||||
nsCString(aMIMEType).get(), caps, (int)!!(caps & eSupportPlugins), (int)jihadHave);
|
||||
if ((caps & eSupportPlugins) && jihadHave) {
|
||||
// ShouldPlay will handle checking for disabled plugins
|
||||
return eType_Plugin;
|
||||
}
|
||||
@@ -3486,9 +3508,15 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
|
||||
aReason = eFallbackClickToPlay;
|
||||
|
||||
uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
|
||||
pluginHost->GetStateForType(mContentType, nsPluginHost::eExcludeNone,
|
||||
nsresult jihadStateRv = pluginHost->GetStateForType(mContentType, nsPluginHost::eExcludeNone,
|
||||
&enabledState);
|
||||
// JIHAD DIAGNOSTIC (R7): note that enabledState is pre-set to STATE_DISABLED and the rv of
|
||||
// GetStateForType is NOT checked upstream, so a failing lookup silently blocks the plugin
|
||||
// with no other trace. Log the rv as well as the value.
|
||||
fprintf(stderr, "[jihad-npapi] ShouldPlay type=%s enabledState=%u rv=0x%x\n",
|
||||
mContentType.get(), enabledState, (unsigned)jihadStateRv);
|
||||
if (nsIPluginTag::STATE_DISABLED == enabledState) {
|
||||
fprintf(stderr, "[jihad-npapi] ShouldPlay -> FALSE (eFallbackDisabled)\n");
|
||||
aReason = eFallbackDisabled;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,44 @@ WebGLTransformFeedback::Delete()
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
// Validation helper for beginning/resuming the TF and computing the
|
||||
// vertex capacity.
|
||||
bool
|
||||
WebGLTransformFeedback::PrepareTransformFeedback() {
|
||||
const auto& prog = mContext->mCurrentProgram;
|
||||
if (!prog ||
|
||||
!prog->IsLinked() ||
|
||||
!prog->LinkInfo()->componentsPerTFVert.size()) {
|
||||
mContext->ErrorInvalidOperation("Current program not valid for transform feedback.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& linkInfo = prog->LinkInfo();
|
||||
const auto& componentsPerTFVert = linkInfo->componentsPerTFVert;
|
||||
|
||||
mActive_VertCapacity = 0;
|
||||
|
||||
size_t minVertCapacity = SIZE_MAX;
|
||||
for (size_t i = 0; i < componentsPerTFVert.size(); i++) {
|
||||
const auto& indexedBinding = mIndexedBindings[i];
|
||||
const auto& componentsPerVert = componentsPerTFVert[i];
|
||||
|
||||
const auto& buffer = indexedBinding.mBufferBinding;
|
||||
if (!buffer) {
|
||||
mContext->ErrorInvalidOperation("No buffer attached to required transform feedback index %u.",
|
||||
(uint32_t)i);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert;
|
||||
minVertCapacity = std::min(minVertCapacity, vertCapacity);
|
||||
}
|
||||
|
||||
mActive_VertCapacity = minVertCapacity;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
{
|
||||
@@ -59,37 +97,10 @@ WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& prog = mContext->mCurrentProgram;
|
||||
if (!prog ||
|
||||
!prog->IsLinked() ||
|
||||
!prog->LinkInfo()->componentsPerTFVert.size())
|
||||
{
|
||||
mContext->ErrorInvalidOperation("%s: Current program not valid for transform"
|
||||
" feedback.",
|
||||
funcName);
|
||||
if (!PrepareTransformFeedback()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& linkInfo = prog->LinkInfo();
|
||||
const auto& componentsPerTFVert = linkInfo->componentsPerTFVert;
|
||||
|
||||
size_t minVertCapacity = SIZE_MAX;
|
||||
for (size_t i = 0; i < componentsPerTFVert.size(); i++) {
|
||||
const auto& indexedBinding = mIndexedBindings[i];
|
||||
const auto& componentsPerVert = componentsPerTFVert[i];
|
||||
|
||||
const auto& buffer = indexedBinding.mBufferBinding;
|
||||
if (!buffer) {
|
||||
mContext->ErrorInvalidOperation("%s: No buffer attached to required transform"
|
||||
" feedback index %u.",
|
||||
funcName, (uint32_t)i);
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t vertCapacity = buffer->ByteLength() / 4 / componentsPerVert;
|
||||
minVertCapacity = std::min(minVertCapacity, vertCapacity);
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
@@ -101,10 +112,9 @@ WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
|
||||
mIsActive = true;
|
||||
MOZ_ASSERT(!mIsPaused);
|
||||
|
||||
mActive_Program = prog;
|
||||
mActive_Program = mContext->mCurrentProgram;
|
||||
mActive_PrimMode = primMode;
|
||||
mActive_VertPosition = 0;
|
||||
mActive_VertCapacity = minVertCapacity;
|
||||
|
||||
////
|
||||
|
||||
@@ -173,6 +183,12 @@ WebGLTransformFeedback::ResumeTransformFeedback()
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-run prepare in case some of the buffers have been modified
|
||||
// while the TF was paused.
|
||||
if (!PrepareTransformFeedback()) {
|
||||
return;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
|
||||
private:
|
||||
~WebGLTransformFeedback();
|
||||
bool PrepareTransformFeedback();
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Shared)
|
||||
|
||||
@@ -186,6 +188,8 @@ SetBaseURIUsingFirstBaseWithHref(nsIDocument* aDocument, nsIContent* aMustMatch)
|
||||
newBaseURI = nullptr;
|
||||
}
|
||||
if (csp && newBaseURI) {
|
||||
bool isLocalResource = false;
|
||||
rv = NS_URIChainHasFlags(newBaseURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &isLocalResource);
|
||||
// base-uri is only enforced if explicitly defined in the
|
||||
// policy - do *not* consult default-src, see:
|
||||
// http://www.w3.org/TR/CSP2/#directive-default-src
|
||||
@@ -193,7 +197,7 @@ SetBaseURIUsingFirstBaseWithHref(nsIDocument* aDocument, nsIContent* aMustMatch)
|
||||
rv = csp->Permits(newBaseURI, nsIContentSecurityPolicy::BASE_URI_DIRECTIVE,
|
||||
true /* aSpecific */, true /* aSendViolationReports */,
|
||||
&cspPermitsBaseURI);
|
||||
if (NS_FAILED(rv) || !cspPermitsBaseURI) {
|
||||
if ((NS_FAILED(rv) || !cspPermitsBaseURI) && !isLocalResource) {
|
||||
newBaseURI = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +105,6 @@ gDownMixMatrices[CUSTOM_CHANNEL_LAYOUTS*(CUSTOM_CHANNEL_LAYOUTS - 1)/2] =
|
||||
* Given an array of input channels, downmix to aOutputChannelCount, and copy
|
||||
* the results to the channel buffers in aOutputChannels. Don't call this with
|
||||
* input count <= output count.
|
||||
* Each of aOutputChannels must be non-null.
|
||||
* aChannelArray elements may be null.
|
||||
*/
|
||||
template<typename T>
|
||||
void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
@@ -121,11 +119,7 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
if (inputChannelCount > 6) {
|
||||
// Just drop the unknown channels.
|
||||
for (uint32_t o = 0; o < aOutputChannelCount; ++o) {
|
||||
if (inputChannels[o]) {
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
} else {
|
||||
std::fill_n(aOutputChannels[o], aDuration, static_cast<T>(0));
|
||||
}
|
||||
PodCopy(aOutputChannels[o], inputChannels[o], aDuration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -144,19 +138,13 @@ void AudioChannelsDownMix(const nsTArray<const T*>& aChannelArray,
|
||||
// want an input channel to contribute to nothing
|
||||
T outputChannels[CUSTOM_CHANNEL_LAYOUTS + 1] = {0};
|
||||
for (uint32_t c = 0; c < inputChannelCount; ++c) {
|
||||
if (inputChannels[c]) {
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c] *
|
||||
(static_cast<const T*>(inputChannels[c]))[s];
|
||||
}
|
||||
outputChannels[m.mInputDestination[c]] +=
|
||||
m.mInputCoefficient[c]*(static_cast<const T*>(inputChannels[c]))[s];
|
||||
}
|
||||
// Utilize the fact that in every layout, C is the third channel.
|
||||
if (m.mCExtraDestination != IGNORE) {
|
||||
if (inputChannels[SURROUND_C]) {
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C] *
|
||||
(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
}
|
||||
outputChannels[m.mCExtraDestination] +=
|
||||
m.mInputCoefficient[SURROUND_C]*(static_cast<const T*>(inputChannels[SURROUND_C]))[s];
|
||||
}
|
||||
|
||||
for (uint32_t c = 0; c < aOutputChannelCount; ++c) {
|
||||
|
||||
@@ -121,19 +121,13 @@ AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
|
||||
}
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const AudioDataValue*>(nullptr));
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
|
||||
for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
|
||||
AudioDataValue* ptr =
|
||||
PointerForOffsetInChannel(buf.Elements(), outBufferLength,
|
||||
aOutputChannels, channel, offsetSamples);
|
||||
if (channelData[channel]) {
|
||||
PodCopy(
|
||||
ptr,
|
||||
reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
} else {
|
||||
PodZero(ptr, frames);
|
||||
}
|
||||
PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
|
||||
frames);
|
||||
}
|
||||
MOZ_ASSERT(channelData.Length() == aOutputChannels);
|
||||
} else if (channelData.Length() > aOutputChannels) {
|
||||
|
||||
@@ -61,22 +61,15 @@ template <typename SrcT, typename DestT>
|
||||
static void
|
||||
InterleaveAndConvertBuffer(const SrcT* const* aSourceChannels,
|
||||
uint32_t aLength, float aVolume,
|
||||
uint32_t aChannelCount,
|
||||
uint32_t aChannels,
|
||||
DestT* aOutput)
|
||||
{
|
||||
for (size_t channel = 0; channel < aChannelCount; ++channel) {
|
||||
DestT* output = aOutput;
|
||||
if (aSourceChannels[channel]) {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
output += aChannelCount;
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
*output = static_cast<DestT>(0);
|
||||
output += aChannelCount;
|
||||
}
|
||||
DestT* output = aOutput;
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
for (size_t channel = 0; channel < aChannels; ++channel) {
|
||||
float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
|
||||
*output = FloatToAudioSample<DestT>(v);
|
||||
++output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +93,7 @@ DeinterleaveAndConvertBuffer(const SrcT* aSourceBuffer,
|
||||
class SilentChannel
|
||||
{
|
||||
public:
|
||||
static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
|
||||
static const int AUDIO_PROCESSING_FRAMES = 4096; /* > 10ms of 48KHz audio */
|
||||
static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES];
|
||||
// We take advantage of the fact that zero in float and zero in int have the
|
||||
// same all-zeros bit layout.
|
||||
@@ -415,7 +408,7 @@ void WriteChunk(AudioChunk& aChunk,
|
||||
if (channelData.Length() < aOutputChannels) {
|
||||
// Up-mix. Note that this might actually make channelData have more
|
||||
// than aOutputChannels temporarily.
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, static_cast<const SrcT*>(nullptr));
|
||||
AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<SrcT>());
|
||||
}
|
||||
if (channelData.Length() > aOutputChannels) {
|
||||
// Down-mix.
|
||||
|
||||
@@ -26,7 +26,7 @@ ThreadSharedFloatArrayBufferList::Create(uint32_t aChannelCount,
|
||||
new ThreadSharedFloatArrayBufferList(aChannelCount);
|
||||
|
||||
for (uint32_t i = 0; i < aChannelCount; ++i) {
|
||||
float* channelData = js_pod_malloc<float>(aLength);
|
||||
float* channelData = js_pod_calloc<float>(aLength);
|
||||
if (!channelData) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ public:
|
||||
}
|
||||
/**
|
||||
* Create with buffers suitable for transfer to
|
||||
* JS_NewArrayBufferWithContents(). The buffer contents are uninitialized
|
||||
* and so should be set using GetDataForWrite().
|
||||
* JS_NewArrayBufferWithContents(). The buffer contents are
|
||||
* zero-initialized; callers should set them using GetDataForWrite().
|
||||
*/
|
||||
static already_AddRefed<ThreadSharedFloatArrayBufferList>
|
||||
Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&);
|
||||
|
||||
@@ -29,8 +29,7 @@ enum {
|
||||
* Abstract interface for managing audio and video devices. Each platform
|
||||
* must implement a concrete class that will map these classes and methods
|
||||
* to the appropriate backend. For example, on Desktop platforms, these will
|
||||
* correspond to equivalent webrtc (GIPS) calls, and on B2G they will map to
|
||||
* a Gonk interface.
|
||||
* correspond to equivalent webrtc (GIPS) calls.
|
||||
*/
|
||||
class MediaEngineVideoSource;
|
||||
class MediaEngineAudioSource;
|
||||
@@ -302,7 +301,6 @@ public:
|
||||
|
||||
/* If implementation of MediaEngineSource supports TakePhoto(), the picture
|
||||
* should be return via aCallback object. Otherwise, it returns NS_ERROR_NOT_IMPLEMENTED.
|
||||
* Currently, only Gonk MediaEngineSource implementation supports it.
|
||||
*/
|
||||
virtual nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) = 0;
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource,
|
||||
return aSource->AppendToTrack(aID, &(segment));
|
||||
}
|
||||
|
||||
// Sub-classes (B2G or desktop) should overload one of both of these two methods
|
||||
// to provide capabilities
|
||||
// Subclasses should overload one or both of these methods to provide
|
||||
// capabilities.
|
||||
size_t
|
||||
MediaEngineCameraVideoSource::NumCapabilities() const
|
||||
{
|
||||
@@ -324,21 +324,6 @@ MediaEngineCameraVideoSource::SetName(nsString aName)
|
||||
bool hasFacingMode = false;
|
||||
VideoFacingModeEnum facingMode = VideoFacingModeEnum::User;
|
||||
|
||||
// Set facing mode based on device name.
|
||||
#if defined(ANDROID)
|
||||
// Names are generated. Example: "Camera 0, Facing back, Orientation 90"
|
||||
//
|
||||
// See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
|
||||
// webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
|
||||
|
||||
if (aName.Find(NS_LITERAL_STRING("Facing back")) != kNotFound) {
|
||||
hasFacingMode = true;
|
||||
facingMode = VideoFacingModeEnum::Environment;
|
||||
} else if (aName.Find(NS_LITERAL_STRING("Facing front")) != kNotFound) {
|
||||
hasFacingMode = true;
|
||||
facingMode = VideoFacingModeEnum::User;
|
||||
}
|
||||
#endif // ANDROID
|
||||
#ifdef XP_MACOSX
|
||||
// Kludge to test user-facing cameras on OSX.
|
||||
if (aName.Find(NS_LITERAL_STRING("Face")) != -1) {
|
||||
|
||||
@@ -108,7 +108,7 @@ protected:
|
||||
nsTArray<PrincipalHandle> mPrincipalHandles; // Directly mapped to mSources.
|
||||
RefPtr<layers::Image> mImage;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
int mWidth, mHeight; // protected with mMonitor on Gonk due to different threading
|
||||
int mWidth, mHeight; // protected with mMonitor
|
||||
// end of data protected by mMonitor
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#ifdef FF
|
||||
#undef FF
|
||||
#endif
|
||||
#include "webrtc/modules/audio_device/opensl/single_rw_fifo.h"
|
||||
#include "SingleRwFifo.h"
|
||||
|
||||
#define CHANNELS 1
|
||||
#define ENCODING "L16"
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "SingleRwFifo.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
UpdatePos(int pos, int capacity)
|
||||
{
|
||||
return (pos + 1) % capacity;
|
||||
}
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace subtle {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
inline void
|
||||
MemoryBarrier()
|
||||
{
|
||||
::MemoryBarrier();
|
||||
}
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
inline void
|
||||
MemoryBarrier()
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
#else
|
||||
#error Add an implementation of MemoryBarrier() for this platform!
|
||||
#endif
|
||||
|
||||
} // namespace subtle
|
||||
|
||||
SingleRwFifo::SingleRwFifo(int capacity)
|
||||
: capacity_(capacity)
|
||||
, size_(0)
|
||||
, read_pos_(0)
|
||||
, write_pos_(0)
|
||||
{
|
||||
queue_.reset(new int8_t*[capacity_]);
|
||||
}
|
||||
|
||||
SingleRwFifo::~SingleRwFifo()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SingleRwFifo::Push(int8_t* mem)
|
||||
{
|
||||
assert(mem);
|
||||
|
||||
// Ensure that there is space for the new data in the FIFO. There is only one
|
||||
// writer, so the other thread is guaranteed only to decrease the size.
|
||||
const int free_slots = capacity() - size();
|
||||
if (free_slots <= 0) {
|
||||
// Size can be queried outside of Push. The caller is expected to ensure
|
||||
// that Push will be successful before calling it.
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
queue_[write_pos_] = mem;
|
||||
// Ensure that |size_| is updated after the stored pointer is visible.
|
||||
subtle::MemoryBarrier();
|
||||
++size_;
|
||||
write_pos_ = UpdatePos(write_pos_, capacity());
|
||||
}
|
||||
|
||||
int8_t*
|
||||
SingleRwFifo::Pop()
|
||||
{
|
||||
int8_t* ret_val = nullptr;
|
||||
if (size() <= 0) {
|
||||
// Size can be queried outside of Pop. The caller is expected to ensure
|
||||
// that Pop will be successful before calling it.
|
||||
assert(false);
|
||||
return ret_val;
|
||||
}
|
||||
ret_val = queue_[read_pos_];
|
||||
// Ensure that |size_| is updated after the stored pointer is read.
|
||||
subtle::MemoryBarrier();
|
||||
--size_;
|
||||
read_pos_ = UpdatePos(read_pos_, capacity());
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
+5
-8
@@ -8,8 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#define WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#ifndef DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
#define DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
|
||||
#include "webrtc/base/scoped_ptr.h"
|
||||
#include "webrtc/system_wrappers/interface/atomic32.h"
|
||||
@@ -17,10 +17,9 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Implements a lock-free FIFO losely based on
|
||||
// Implements a lock-free FIFO loosely based on
|
||||
// http://src.chromium.org/viewvc/chrome/trunk/src/media/base/audio_fifo.cc
|
||||
// Note that this class assumes there is one producer (writer) and one
|
||||
// consumer (reader) thread.
|
||||
// This class assumes there is one producer and one consumer thread.
|
||||
class SingleRwFifo {
|
||||
public:
|
||||
explicit SingleRwFifo(int capacity);
|
||||
@@ -29,8 +28,6 @@ class SingleRwFifo {
|
||||
void Push(int8_t* mem);
|
||||
int8_t* Pop();
|
||||
|
||||
void Clear();
|
||||
|
||||
int size() { return size_.Value(); }
|
||||
int capacity() const { return capacity_; }
|
||||
|
||||
@@ -46,4 +43,4 @@ class SingleRwFifo {
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_AUDIO_DEVICE_ANDROID_SINGLE_RW_FIFO_H_
|
||||
#endif // DOM_MEDIA_WEBRTC_SINGLE_RW_FIFO_H_
|
||||
@@ -38,6 +38,7 @@ if CONFIG['MOZ_WEBRTC']:
|
||||
# MediaEngineWebRTC.cpp needs to be built separately.
|
||||
SOURCES += [
|
||||
'MediaEngineWebRTC.cpp',
|
||||
'SingleRwFifo.cpp',
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
@@ -68,8 +69,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
# Suppress some GCC/clang warnings being treated as errors:
|
||||
# - about attributes on forward declarations for types that are already
|
||||
# defined, which complains about important MOZ_EXPORT attributes for
|
||||
# android API types
|
||||
# defined, which complains about important MOZ_EXPORT attributes.
|
||||
if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
|
||||
CXXFLAGS += [
|
||||
'-Wno-error=attributes',
|
||||
|
||||
@@ -577,6 +577,166 @@ typedef struct _NPEvent
|
||||
} NPEvent;
|
||||
#elif defined(XP_UNIX) && defined(MOZ_X11)
|
||||
typedef XEvent NPEvent;
|
||||
#elif defined(XP_UNIX) && defined(MOZ_WIDGET_HEADLESS)
|
||||
/*
|
||||
* JIHAD (webOS, cavekit-addons-extensions.md R7) — THE PALM EVENT PROTOCOL.
|
||||
*
|
||||
* webOS plugins do not speak X11. Palm's own npapi.h defines NPEvent, under XP_WEBOS, as the
|
||||
* union below; the device's Adobe Flash was compiled against exactly that header and its
|
||||
* NPP_HandleEvent reads these fields. Reproduced here from the webOS WebKit fork's
|
||||
* WebCore/plugins/npapi.h (the XP_WEBOS block, NpPalmEventsEnum through the NPEvent union),
|
||||
* so that the shape crossing our NPAPI boundary is the shape the plugin was built to read.
|
||||
*
|
||||
* This replaces `typedef void* NPEvent` for the headless toolkit, and that arm was not merely
|
||||
* a placeholder — it was load-bearing in the wrong direction. NPRemoteEvent is a raw memcpy of
|
||||
* sizeof(NPEvent) (dom/plugins/ipc/NPEventUnix.h), so with a void* NPEvent the parent shipped
|
||||
* four bytes of its own address space to the child and the child handed the plugin a pointer
|
||||
* into a foreign process. No event could ever have carried a payload. Making NPEvent a real
|
||||
* POD is what lets the whole existing NPP_HandleEvent path work unmodified, parent to child.
|
||||
*
|
||||
* Gated on MOZ_WIDGET_HEADLESS because that is precisely the configuration this port builds
|
||||
* (--enable-default-toolkit=cairo-headless, no X anywhere in the bundle) and the only one that
|
||||
* reaches this arm. A non-webOS headless embedding would get these structs too; that is
|
||||
* harmless, since the alternative it loses is an NPEvent that could not carry an event.
|
||||
*
|
||||
* POINTER-BEARING MEMBERS DO NOT SURVIVE IPC. dstBuffer/graphicsContext (draw),
|
||||
* touches (touch) and reserved (system) are process-local. Pen, key and system events use
|
||||
* none of them; the draw event is built inside the child (PluginInstanceChild.cpp) and never
|
||||
* crosses the boundary, which is why it stays valid.
|
||||
*
|
||||
* One deliberate omission from Palm's union: NpPalmAccelerometerEvent. It is 16 bytes against
|
||||
* NpPalmDrawEvent's 44, so leaving it out changes sizeof(NPEvent) by nothing — and the union
|
||||
* is only ever read through the plugin's OWN copy of this declaration, so an unused member
|
||||
* here is unobservable. It is left out rather than copied because it carries a struct-scoped
|
||||
* `enum Orientation` whose tag would leak to file scope, for no gain.
|
||||
*/
|
||||
typedef enum NpPalmEventsEnum
|
||||
{
|
||||
npPalmPenDownEvent = 1 << 0,
|
||||
npPalmPenUpEvent = 1 << 1,
|
||||
npPalmPenMoveEvent = 1 << 2,
|
||||
npPalmKeyDownEvent = 1 << 3,
|
||||
npPalmKeyUpEvent = 1 << 4,
|
||||
npPalmKeyRepeatEvent = 1 << 5,
|
||||
npPalmKeyPressEvent = 1 << 6,
|
||||
npPalmDrawEvent = 1 << 7,
|
||||
npPalmSystemEvent = 1 << 8,
|
||||
npPalmGestureEvent = 1 << 9,
|
||||
npPalmAccelerometerEvent = 1 << 10,
|
||||
npPalmTouchStartEvent = 1 << 11,
|
||||
npPalmTouchMoveEvent = 1 << 12,
|
||||
npPalmTouchEndEvent = 1 << 13,
|
||||
npPalmTouchCancelledEvent = 1 << 14,
|
||||
npPalmPenDoubleClickEvent = 1 << 15,
|
||||
npPalmPenClickEvent = 1 << 16
|
||||
} NpPalmEventsEnum;
|
||||
|
||||
typedef enum NpPalmSystemEventsEnum
|
||||
{
|
||||
npPalmPauseEvent = 1,
|
||||
npPalmResumeEvent = 2,
|
||||
npPalmGainFocusEvent = 3,
|
||||
npPalmLoseFocusEvent = 4,
|
||||
npPalmDimmedEvent = 5,
|
||||
npPalmSetFullScreenEvent = 6,
|
||||
npPalmUnsetFullScreenEvent = 7,
|
||||
npPalmPageLoadingEvent = 8,
|
||||
npPalmPageLoadCompleteEvent = 9,
|
||||
npPalmViewPortChangedEvent = 10,
|
||||
npPalmSpotlightStartEvent = 11,
|
||||
npPalmSpotlightEndEvent = 12
|
||||
} NpPalmSystemEventsEnum;
|
||||
|
||||
typedef enum NpPalmKeyModifiersEnum
|
||||
{
|
||||
npPalmCtrlKeyModifier = 1 << 0,
|
||||
npPalmAltKeyModifier = 1 << 1,
|
||||
npPalmShiftKeyModifier = 1 << 2,
|
||||
npPalmMetaKeyModifier = 1 << 3
|
||||
} NpPalmKeyModifiersEnum;
|
||||
|
||||
typedef struct NpPalmKeyEvent
|
||||
{
|
||||
int32_t chr; /* 32-bit so any Unicode character fits */
|
||||
int32_t modifiers;
|
||||
int32_t rawkeyCode;
|
||||
int32_t rawModifier;
|
||||
} NpPalmKeyEvent;
|
||||
|
||||
typedef struct NpPalmPenEvent
|
||||
{
|
||||
int32_t xCoord, yCoord; /* plugin-local, in the plugin's own unscaled pixels */
|
||||
int32_t modifiers;
|
||||
} NpPalmPenEvent;
|
||||
|
||||
typedef struct NpPalmTouchPoint
|
||||
{
|
||||
int32_t xCoord, yCoord;
|
||||
} NpPalmTouchPoint;
|
||||
|
||||
typedef struct NpPalmTouchEvent
|
||||
{
|
||||
int32_t touchCount;
|
||||
NpPalmTouchPoint* touches; /* process-local — never send over IPC */
|
||||
int32_t modifiers;
|
||||
} NpPalmTouchEvent;
|
||||
|
||||
typedef struct NpPalmDrawEvent
|
||||
{
|
||||
void* dstBuffer; /* process-local; API 1.0 raster destination */
|
||||
uint32_t dstRowBytes;
|
||||
int32_t srcLeft;
|
||||
int32_t srcRight;
|
||||
int32_t srcTop;
|
||||
int32_t srcBottom;
|
||||
void* graphicsContext; /* process-local; the Piranha PGContext, API 2.0 */
|
||||
int32_t dstLeft;
|
||||
int32_t dstRight;
|
||||
int32_t dstTop;
|
||||
int32_t dstBottom;
|
||||
} NpPalmDrawEvent;
|
||||
|
||||
typedef struct NpPalmSystemEvent
|
||||
{
|
||||
NpPalmSystemEventsEnum type;
|
||||
int32_t value;
|
||||
int32_t viewLeft;
|
||||
int32_t viewRight;
|
||||
int32_t viewTop;
|
||||
int32_t viewBottom;
|
||||
void* reserved; /* process-local */
|
||||
} NpPalmSystemEvent;
|
||||
|
||||
typedef enum NpPalmGestureEventEnum
|
||||
{
|
||||
npPalmGestureStartEvent = 1,
|
||||
npPalmGestureChangeEvent = 2,
|
||||
npPalmGestureEndEvent = 3,
|
||||
npPalmGestureSingleTapEvent = 4
|
||||
} NpPalmGestureEventEnum;
|
||||
|
||||
typedef struct NpPalmGestureEvent
|
||||
{
|
||||
NpPalmGestureEventEnum type;
|
||||
int32_t x, y;
|
||||
float scale, rotate;
|
||||
int32_t center_x, center_y;
|
||||
int32_t modifiers;
|
||||
} NpPalmGestureEvent;
|
||||
|
||||
typedef struct NPEvent
|
||||
{
|
||||
NpPalmEventsEnum eventType;
|
||||
|
||||
union {
|
||||
NpPalmKeyEvent keyEvent;
|
||||
NpPalmPenEvent penEvent;
|
||||
NpPalmDrawEvent drawEvent;
|
||||
NpPalmSystemEvent systemEvent;
|
||||
NpPalmGestureEvent gestureEvent;
|
||||
NpPalmTouchEvent touchEvent;
|
||||
} data;
|
||||
} NPEvent;
|
||||
#else
|
||||
typedef void* NPEvent;
|
||||
#endif
|
||||
|
||||
@@ -341,6 +341,10 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
|
||||
RefPtr<nsNPAPIPlugin> plugin = new nsNPAPIPlugin();
|
||||
|
||||
PluginLibrary* pluginLib = GetNewPluginLibrary(aPluginTag);
|
||||
// JIHAD DIAGNOSTIC (R7): GetNewPluginLibrary is where the OOP decision is made —
|
||||
// RunPluginOOP() ? PluginModuleParent::LoadModule (launches plugin-container) : a
|
||||
// PluginPRLibrary loaded in-process. A null here is the plugin-container launch failing.
|
||||
fprintf(stderr, "[jihad-npapi] GetNewPluginLibrary lib=%d\n", (int)!!pluginLib);
|
||||
if (!pluginLib) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -386,6 +390,7 @@ nsNPAPIPlugin::CreatePlugin(nsPluginTag *aPluginTag, nsNPAPIPlugin** aResult)
|
||||
#else
|
||||
NPError pluginCallError;
|
||||
nsresult rv = pluginLib->NP_Initialize(&sBrowserFuncs, &plugin->mPluginFuncs, &pluginCallError);
|
||||
fprintf(stderr, "[jihad-npapi] NP_Initialize rv=0x%x npErr=%d\n", (unsigned)rv, (int)pluginCallError);
|
||||
if (rv != NS_OK || pluginCallError != NPERR_NO_ERROR) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -1939,7 +1944,16 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
|
||||
}
|
||||
|
||||
case NPNVSupportsWindowless: {
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || \
|
||||
// MOZ_WIDGET_HEADLESS added for Jihad Browser. This list enumerates the toolkits that can
|
||||
// host a windowless plugin, and it was written before a headless toolkit existed — so a
|
||||
// cairo-headless build answered "windowless unsupported" to every plugin, which is the
|
||||
// opposite of the truth. Headless is the toolkit that MOST needs windowless: there is no X
|
||||
// server, no Drawable and no XEmbed, so windowed hosting is impossible and drawing into a
|
||||
// memory bitmap we own is the only thing that can work. gfxPlatformHeadless already returns
|
||||
// true from SupportsPluginDirectBitmapDrawing(), i.e. the drawing model this unlocks
|
||||
// (NPDrawingModelAsyncBitmapSurface) is implemented for this platform; only this
|
||||
// advertisement was missing, and with it false no plugin would ever choose windowless.
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_HEADLESS) || \
|
||||
(defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
|
||||
*(NPBool*)result = true;
|
||||
#else
|
||||
|
||||
@@ -843,6 +843,9 @@ nsPluginHost::InstantiatePluginInstance(const nsACString& aMimeType, nsIURI* aUR
|
||||
}
|
||||
|
||||
rv = SetUpPluginInstance(aMimeType, aURL, instanceOwner);
|
||||
// JIHAD DIAGNOSTIC (R7): the real rv is flattened to NS_ERROR_FAILURE below, which is what
|
||||
// made this failure opaque from the caller's side.
|
||||
fprintf(stderr, "[jihad-npapi] SetUpPluginInstance rv=0x%x\n", (unsigned)rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
instanceOwner->Destroy();
|
||||
return NS_ERROR_FAILURE;
|
||||
@@ -961,7 +964,12 @@ nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
|
||||
#endif
|
||||
|
||||
RefPtr<nsNPAPIPlugin> plugin;
|
||||
GetPlugin(aMimeType, getter_AddRefs(plugin));
|
||||
nsresult jihadGetRv = GetPlugin(aMimeType, getter_AddRefs(plugin));
|
||||
// JIHAD DIAGNOSTIC (R7): GetPlugin -> CreateNPAPIPlugin -> nsNPAPIPlugin::CreatePlugin, which
|
||||
// for an OOP build is PluginModuleParent::LoadModule (the plugin-container launch). Its rv is
|
||||
// DISCARDED here; only the null check survives, so a launch failure is invisible.
|
||||
fprintf(stderr, "[jihad-npapi] GetPlugin rv=0x%x plugin=%d\n",
|
||||
(unsigned)jihadGetRv, (int)!!plugin);
|
||||
if (!plugin) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@@ -987,6 +995,7 @@ nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
|
||||
// except in some cases not Java, see bug 140931
|
||||
// our COM pointer will free the peer
|
||||
nsresult rv = instance->Initialize(plugin.get(), aOwner, aMimeType);
|
||||
fprintf(stderr, "[jihad-npapi] instance->Initialize rv=0x%x\n", (unsigned)rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
mInstances.RemoveElement(instance.get());
|
||||
aOwner->SetInstance(nullptr);
|
||||
@@ -4201,3 +4210,62 @@ PluginDestructionGuard::DelayDestroy(nsNPAPIPluginInstance *aInstance)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── JIHAD (webOS, cavekit-addons-extensions.md R7) ────────────────────────────────────────
|
||||
//
|
||||
// A C entry point for the render daemon, following the jihad_offscreen_* precedent in
|
||||
// widget/PuppetWidget.cpp: the daemon links against libxul but cannot see internal classes,
|
||||
// and nsNPAPIPluginInstance::HandleEvent is neither exported nor virtual, so there is no
|
||||
// other way to reach a plugin from outside.
|
||||
//
|
||||
// This exists for the frozen YAP command pair asyncCmdPluginSpotlightStart/End. The card
|
||||
// scrims and smart-zooms to a plugin, then tells the server the rect it settled on; the
|
||||
// server's job is to pass that on to the plugin as Palm's npPalmSpotlightStartEvent(11) /
|
||||
// npPalmSpotlightEndEvent(12). It is not cosmetic: Flash routes 11 to the same handler as
|
||||
// npPalmSetFullScreenEvent(6) and 12 to the same as npPalmUnsetFullScreenEvent(7)
|
||||
// (libflashplayer.so jump table at 0x44e0c — entries 10 and 11 target 0x44fa0/0x44e80,
|
||||
// identical to entries 5 and 6), so this is how a plugin is told it went fullscreen.
|
||||
//
|
||||
// Broadcast to every running instance rather than targeting one: the spotlight is a
|
||||
// page-level, one-plugin-at-a-time state, this daemon serves a single card, and the
|
||||
// alternative — matching the rect against each instance's frame — would invent a
|
||||
// disambiguation rule that the contract does not define. Returns how many instances were
|
||||
// told, so a caller can tell "no plugin" from "delivered".
|
||||
extern "C" MOZ_EXPORT int
|
||||
jihad_plugin_palm_spotlight(int aOn, int aLeft, int aTop, int aRight, int aBottom)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_NPAPI
|
||||
RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
|
||||
if (!host) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NPEvent ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.eventType = npPalmSystemEvent;
|
||||
ev.data.systemEvent.type = aOn ? npPalmSpotlightStartEvent : npPalmSpotlightEndEvent;
|
||||
// Palm's host leaves systemEvent.value uninitialised here and fills only the rect
|
||||
// (libWebKitLuna handlePluginSpotlightStart 0x4e6cec-0x4e6d24, which never writes [sp+8]).
|
||||
// The memset above zeroes it, which is strictly tidier and observably the same.
|
||||
ev.data.systemEvent.viewLeft = aLeft;
|
||||
ev.data.systemEvent.viewTop = aTop;
|
||||
ev.data.systemEvent.viewRight = aRight;
|
||||
ev.data.systemEvent.viewBottom = aBottom;
|
||||
|
||||
int delivered = 0;
|
||||
// Copy the instance list first: HandleEvent re-enters the plugin, which may run script and
|
||||
// tear an instance down underneath us.
|
||||
nsTArray<RefPtr<nsNPAPIPluginInstance>> instances(host->JihadRunningInstances());
|
||||
for (uint32_t i = 0; i < instances.Length(); ++i) {
|
||||
int16_t response = kNPEventNotHandled;
|
||||
if (NS_SUCCEEDED(instances[i]->HandleEvent(&ev, &response,
|
||||
NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO))) {
|
||||
delivered++;
|
||||
}
|
||||
}
|
||||
return delivered;
|
||||
#else
|
||||
(void)aOn; (void)aLeft; (void)aTop; (void)aRight; (void)aBottom;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -274,6 +274,13 @@ public:
|
||||
InfallibleTArray<nsCString>& result,
|
||||
bool firstMatchOnly);
|
||||
|
||||
// Jihad R7 (webOS): a snapshot of the live instances, for jihad_plugin_palm_spotlight.
|
||||
// A COPY, not a reference: dispatching an event re-enters the plugin, which can run script
|
||||
// and destroy an instance, and mInstances is mutated by that teardown.
|
||||
const nsTArray<RefPtr<nsNPAPIPluginInstance>>& JihadRunningInstances() const {
|
||||
return mInstances;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class nsPluginUnloadRunnable;
|
||||
|
||||
|
||||
@@ -569,6 +569,75 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect)
|
||||
GetContentsScaleFactor(&scaleFactor);
|
||||
rect.ScaleRoundOut(scaleFactor);
|
||||
mPluginFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
|
||||
|
||||
// JIHAD (2026-08-09): drive the offscreen daemon's re-capture for WINDOWLESS plugins.
|
||||
//
|
||||
// A windowless plugin has no mWidget, so the mWidget->Invalidate() branch above (which is what
|
||||
// ultimately reaches PuppetWidget::Invalidate and sets the daemon's mJihadDirty flag) never
|
||||
// runs. InvalidateLayer() alone only marks the plugin's ImageLayer for recomposition; with the
|
||||
// in-process BasicLayerManager and no separate compositor, that repaints the widget's own layer
|
||||
// backbuffer but never triggers jihad_offscreen_render_region, the path that actually renders a
|
||||
// frame into the adapter's shared buffer for the card. Measured: Flash pushes a fresh magenta
|
||||
// frame into its ImageContainer ~15x/s and BasicImageLayer::Paint runs on each, yet
|
||||
// render_region fired ONCE (at load) so the card froze on the pre-content frame. The control
|
||||
// plugin only appeared to work because its static fill was already present at that single
|
||||
// capture. Invalidate the nearest real widget here so every async plugin frame marks the daemon
|
||||
// dirty and gets composited to the card.
|
||||
//
|
||||
// THE RECT MATTERS, and the first version of this got it wrong. It invalidated
|
||||
// nearest->GetBounds() — the WIDGET's bounds, i.e. the whole viewport — on the reasoning
|
||||
// that the daemon re-renders the whole document per capture anyway. That reasoning stopped
|
||||
// being true when the daemon learned to repaint only the damaged rows: a whole-viewport
|
||||
// software render costs ~40 ms on this hardware against ~4 ms for a 320x240 plugin box, and
|
||||
// a full-viewport damage box silently forced the expensive path on every single frame.
|
||||
// Measured: an animated SWF ran at 19 fps with 1 partial repaint in 39, and the damage box
|
||||
// logged as 768x942 with the plugin's own 320x240 unioned uselessly into it.
|
||||
//
|
||||
// So invalidate the plugin's own area, translated from plugin-local into the widget's
|
||||
// coordinates the same way the rest of this file does it: content-box origin relative to the
|
||||
// frame, offset to the widget, in device px.
|
||||
if (!mWidget) {
|
||||
nsIWidget* nearest = mPluginFrame->GetNearestWidget();
|
||||
if (nearest) {
|
||||
nsIntRect widgetRect = rect;
|
||||
nsPresContext* pc = mPluginFrame->PresContext();
|
||||
// Offset to the ROOT frame, which is what the widget's coordinates are relative to.
|
||||
// GetReferenceFrame() was the first attempt and it is wrong here: for a plugin it
|
||||
// resolves to a frame that makes the offset come out 0, so every invalidation landed at
|
||||
// the viewport's top-left corner instead of on the plugin. That is not a small error —
|
||||
// the damage-only repaint then faithfully refreshed the wrong 320x240 box and the
|
||||
// plugin's own area never updated again, which reads on screen as the animation
|
||||
// freezing after its first frame. This is the same offset nsPluginFrame itself uses to
|
||||
// build NPWindow.x/y.
|
||||
nsIFrame* rootFrame = nullptr;
|
||||
if (nsIPresShell* ps = mPluginFrame->PresContext()->GetPresShell()) {
|
||||
rootFrame = ps->GetRootFrame();
|
||||
}
|
||||
nsPoint origin = mPluginFrame->GetContentRectRelativeToSelf().TopLeft() +
|
||||
(rootFrame ? mPluginFrame->GetOffsetToCrossDoc(rootFrame) : nsPoint());
|
||||
widgetRect.MoveBy(pc->AppUnitsToDevPixels(origin.x),
|
||||
pc->AppUnitsToDevPixels(origin.y));
|
||||
// A plugin is entitled to report an invalid rect larger than itself, and an empty one
|
||||
// would invalidate nothing at all; clamp to the plugin's own box, and fall back to the
|
||||
// widget bounds if that leaves nothing (correctness before speed).
|
||||
nsIntRect pluginBox(pc->AppUnitsToDevPixels(origin.x),
|
||||
pc->AppUnitsToDevPixels(origin.y),
|
||||
pc->AppUnitsToDevPixels(
|
||||
mPluginFrame->GetContentRectRelativeToSelf().width),
|
||||
pc->AppUnitsToDevPixels(
|
||||
mPluginFrame->GetContentRectRelativeToSelf().height));
|
||||
widgetRect = widgetRect.Intersect(pluginBox);
|
||||
if (widgetRect.IsEmpty()) {
|
||||
widgetRect = pluginBox;
|
||||
}
|
||||
if (widgetRect.IsEmpty()) {
|
||||
nearest->Invalidate(nearest->GetBounds());
|
||||
} else {
|
||||
nearest->Invalidate(LayoutDeviceIntRect(widgetRect.x, widgetRect.y,
|
||||
widgetRect.width, widgetRect.height));
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -2530,6 +2599,122 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const WidgetGUIEvent& anEvent)
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
#endif
|
||||
|
||||
// Guard MUST match the one npapi.h uses to define NPEvent as Palm's union. Any other
|
||||
// predicate compiles this against `typedef void* NPEvent` and does not build.
|
||||
#if defined(XP_UNIX) && defined(MOZ_WIDGET_HEADLESS)
|
||||
// JIHAD (webOS, cavekit-addons-extensions.md R7) — DELIVER INPUT IN THE PALM PROTOCOL.
|
||||
//
|
||||
// Everything above this point is a platform arm: XP_MACOSX, XP_WIN, MOZ_X11. A
|
||||
// cairo-headless build matches none of them, so this function used to reduce to
|
||||
// `return nsEventStatus_eIgnore;` and a windowless plugin received no mouse, key, focus or
|
||||
// blur event in its whole lifetime. Measured on the TouchPad before this branch existed: a
|
||||
// tap hit-tested the <EMBED>, the daemon logged `mouseSend <EMBED> ... : down`, and the
|
||||
// plugin's NPP_HandleEvent was called exactly once ever — for the child's own gain-focus
|
||||
// injection. Nothing upstream is at fault; the DOM listeners, the hit test, the windowless
|
||||
// guards and the IPC hop are all live and correct (see the Dispatch*ToPlugin callers).
|
||||
//
|
||||
// The event shape is Palm's, because NPEvent on this platform IS Palm's union (npapi.h) and
|
||||
// the device's Flash was compiled against that declaration.
|
||||
{
|
||||
NPEvent pluginEvent;
|
||||
memset(&pluginEvent, 0, sizeof(pluginEvent));
|
||||
|
||||
if (anEvent.mClass == eMouseEventClass) {
|
||||
const WidgetMouseEvent& mouseEvent = *anEvent.AsMouseEvent();
|
||||
|
||||
switch (anEvent.mMessage) {
|
||||
case eMouseDown: pluginEvent.eventType = npPalmPenDownEvent; break;
|
||||
case eMouseUp: pluginEvent.eventType = npPalmPenUpEvent; break;
|
||||
case eMouseMove: pluginEvent.eventType = npPalmPenMoveEvent; break;
|
||||
// The synthesized click/dblclick that follows a down/up pair. Palm's host sends these
|
||||
// as their own event types rather than swallowing them (libWebKitLuna
|
||||
// handleMouseEvent 0x4e946c-0x4e94d8 maps click -> 65536 and dblclick -> 32768, in
|
||||
// ADDITION to the down/up pair, not instead of it), so this is what a plugin written
|
||||
// for webOS was built to see. The X11 arm above drops them instead, which is right
|
||||
// for X11 and wrong here.
|
||||
//
|
||||
// Flash itself ignores both: its NPP_HandleEvent jump table (libflashplayer.so
|
||||
// 0x4514c-0x4522c) compares only against 1/2/4/8/16/64/128/256/512, and anything
|
||||
// else falls through to "not handled" — confirmed on device, where a tap logs
|
||||
// `palm event 0x10000 ... handled=0` between two handled pen events. Sending them
|
||||
// anyway is deliberate: the contract is the host's, not this one plugin's, and a
|
||||
// plugin that acts only on the click is otherwise silently unusable.
|
||||
case eMouseClick: pluginEvent.eventType = npPalmPenClickEvent; break;
|
||||
case eMouseDoubleClick: pluginEvent.eventType = npPalmPenDoubleClickEvent; break;
|
||||
default: break; // over/out/wheel/aux: no Palm equivalent, drop
|
||||
}
|
||||
|
||||
if (pluginEvent.eventType) {
|
||||
// The plugin-local point, computed exactly as the MOZ_X11 arm above computes it. This
|
||||
// is also what Palm's own host does — PluginView's webOS block uses the renderer's
|
||||
// absoluteToLocal() on the same absolute location — so the two hosts agree on the
|
||||
// space without either being ported to the other.
|
||||
//
|
||||
// Deliberately NO zoom or scroll arithmetic here. The card's fit-zoom is not an engine
|
||||
// zoom (it is applied when the daemon scales the capture context), layout.css.
|
||||
// devPixelsPerPx is pinned to 1.0, and the daemon has already mapped card px to CSS
|
||||
// viewport px before dispatching. Adding a factor here is how plugin input drifts away
|
||||
// from where page content thinks it was clicked; the criterion is that both go through
|
||||
// the same transform, and they do precisely because this arm adds none.
|
||||
const nsPresContext* presContext = mPluginFrame->PresContext();
|
||||
nsPoint appPoint =
|
||||
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mPluginFrame) -
|
||||
mPluginFrame->GetContentRectRelativeToSelf().TopLeft();
|
||||
pluginEvent.data.penEvent.xCoord =
|
||||
presContext->AppUnitsToDevPixels(appPoint.x);
|
||||
pluginEvent.data.penEvent.yCoord =
|
||||
presContext->AppUnitsToDevPixels(appPoint.y);
|
||||
pluginEvent.data.penEvent.modifiers =
|
||||
(mouseEvent.IsControl() ? npPalmCtrlKeyModifier : 0) |
|
||||
(mouseEvent.IsAlt() ? npPalmAltKeyModifier : 0) |
|
||||
(mouseEvent.IsShift() ? npPalmShiftKeyModifier : 0) |
|
||||
(mouseEvent.IsMeta() ? npPalmMetaKeyModifier : 0);
|
||||
}
|
||||
} else if (anEvent.mClass == eKeyboardEventClass) {
|
||||
const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent();
|
||||
|
||||
switch (anEvent.mMessage) {
|
||||
case eKeyDown: pluginEvent.eventType = npPalmKeyDownEvent; break;
|
||||
case eKeyUp: pluginEvent.eventType = npPalmKeyUpEvent; break;
|
||||
case eKeyPress: pluginEvent.eventType = npPalmKeyPressEvent; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (pluginEvent.eventType) {
|
||||
// There is no native key event to copy from on this platform (mPluginEvent is a
|
||||
// Windows concept), so the Palm fields are synthesized from the widget event.
|
||||
// rawkeyCode/rawModifier are Palm's "pass the unprocessed event through" pair, which
|
||||
// for a synthesized event is the same key code and modifier set.
|
||||
int32_t mods =
|
||||
(keyEvent.IsControl() ? npPalmCtrlKeyModifier : 0) |
|
||||
(keyEvent.IsAlt() ? npPalmAltKeyModifier : 0) |
|
||||
(keyEvent.IsShift() ? npPalmShiftKeyModifier : 0) |
|
||||
(keyEvent.IsMeta() ? npPalmMetaKeyModifier : 0);
|
||||
pluginEvent.data.keyEvent.chr =
|
||||
static_cast<int32_t>(keyEvent.PseudoCharCode());
|
||||
pluginEvent.data.keyEvent.modifiers = mods;
|
||||
pluginEvent.data.keyEvent.rawkeyCode = static_cast<int32_t>(keyEvent.mKeyCode);
|
||||
pluginEvent.data.keyEvent.rawModifier = mods;
|
||||
}
|
||||
} else if (anEvent.mMessage == eFocus || anEvent.mMessage == eBlur) {
|
||||
// Focus arrives as a bare WidgetGUIEvent with a null widget (see DispatchFocusToPlugin),
|
||||
// so it must not touch the coordinate path above.
|
||||
pluginEvent.eventType = npPalmSystemEvent;
|
||||
pluginEvent.data.systemEvent.type =
|
||||
(anEvent.mMessage == eFocus) ? npPalmGainFocusEvent : npPalmLoseFocusEvent;
|
||||
}
|
||||
|
||||
if (pluginEvent.eventType) {
|
||||
int16_t response = kNPEventNotHandled;
|
||||
mInstance->HandleEvent(&pluginEvent, &response,
|
||||
NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
|
||||
if (response == kNPEventHandled) {
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -2977,23 +3162,40 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
|
||||
if (!mWidget) {
|
||||
// native (single process)
|
||||
mWidget = do_CreateInstance(kWidgetCID, &rv);
|
||||
nsWidgetInitData initData;
|
||||
initData.mWindowType = eWindowType_plugin;
|
||||
initData.mUnicode = false;
|
||||
initData.clipChildren = true;
|
||||
initData.clipSiblings = true;
|
||||
rv = mWidget->Create(parentWidget.get(), nullptr,
|
||||
LayoutDeviceIntRect(0, 0, 0, 0), &initData);
|
||||
if (NS_FAILED(rv)) {
|
||||
mWidget->Destroy();
|
||||
mWidget = nullptr;
|
||||
return rv;
|
||||
if (!mWidget) {
|
||||
// JIHAD (2026-10-05): A BUILD WITH NO CHILD-WINDOW WIDGET HAS NO WINDOWED PLUGINS.
|
||||
// The offscreen embedding registers no widget for kWidgetCID, so this came back
|
||||
// null and the Create() call below dereferenced it: the daemon died with SIGSEGV on
|
||||
// every page whose Flash movie was embedded the ordinary way. A plugin is windowed
|
||||
// unless it asks otherwise, and Flash asks only for wmode=opaque/transparent — the
|
||||
// default, wmode=window, is what most of the web uses (found on two "Flash test"
|
||||
// sites; every test page of ours had wmode=opaque). webOS's own browser never had
|
||||
// plugin windows either and handed every plugin a drawable; its Flash build draws
|
||||
// through the Palm window structure in both modes. So do the same: with no window
|
||||
// to give, the instance is windowless.
|
||||
mInstance->SetWindowless(true);
|
||||
windowless = true;
|
||||
} else {
|
||||
nsWidgetInitData initData;
|
||||
initData.mWindowType = eWindowType_plugin;
|
||||
initData.mUnicode = false;
|
||||
initData.clipChildren = true;
|
||||
initData.clipSiblings = true;
|
||||
rv = mWidget->Create(parentWidget.get(), nullptr,
|
||||
LayoutDeviceIntRect(0, 0, 0, 0), &initData);
|
||||
if (NS_FAILED(rv)) {
|
||||
mWidget->Destroy();
|
||||
mWidget = nullptr;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mWidget->EnableDragDrop(true);
|
||||
mWidget->Show(false);
|
||||
mWidget->Enable(false);
|
||||
if (mWidget) {
|
||||
mWidget->EnableDragDrop(true);
|
||||
mWidget->Show(false);
|
||||
mWidget->Enable(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (mPluginFrame) {
|
||||
|
||||
@@ -194,7 +194,7 @@ static void FreeStringArray(uint32_t variants, char ** array)
|
||||
|
||||
static bool CanLoadPlugin(char16ptr_t aBinaryPath)
|
||||
{
|
||||
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
|
||||
#if defined(_M_IX86) || defined(_M_X64)
|
||||
bool canLoad = false;
|
||||
|
||||
HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ,
|
||||
@@ -214,8 +214,6 @@ static bool CanLoadPlugin(char16ptr_t aBinaryPath)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_I386);
|
||||
#elif defined(_M_X64)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
|
||||
#elif defined(_M_IA64)
|
||||
canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,12 @@ BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_METHOD;
|
||||
|
||||
// Jihad R7: end-of-stream is what tells a Flash player the movie is complete. A player that
|
||||
// is never told stops on frame zero, which looks exactly like a player that cannot draw.
|
||||
fprintf(stderr, "[jihad-npapi-child] RecvNPP_DestroyStream reason=%d state=%d\n",
|
||||
(int)reason, (int)mState);
|
||||
fflush(stderr);
|
||||
|
||||
if (ALIVE != mState)
|
||||
NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?");
|
||||
|
||||
@@ -232,8 +238,12 @@ BrowserStreamChild::Deliver()
|
||||
if (kStreamOpen == mStreamStatus)
|
||||
mStreamStatus = NPRES_DONE;
|
||||
|
||||
(void) mInstance->mPluginIface
|
||||
NPError dsrv = mInstance->mPluginIface
|
||||
->destroystream(&mInstance->mData, &mStream, mStreamStatus);
|
||||
// Jihad R7: the delivery site, as opposed to RecvNPP_DestroyStream which only queues it.
|
||||
fprintf(stderr, "[jihad-npapi-child] NPP_DestroyStream status=%d rv=%d\n",
|
||||
(int)mStreamStatus, (int)dsrv);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (DESTROYED == mDestroyPending && mNotifyPending) {
|
||||
NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?");
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* NPN entry points exported as GLOBAL SYMBOLS, for plugins that expect the old Netscape
|
||||
* convention where the host browser exports them rather than only passing an NPNetscapeFuncs
|
||||
* table to NP_Initialize.
|
||||
*
|
||||
* WHY THIS IS ITS OWN TRANSLATION UNIT. The obvious place for these is PluginModuleChild.cpp,
|
||||
* and it does not work: npapi.h already declares NPN_InvalidateRect, and every header in this
|
||||
* build is compiled under `-include config/gcc_hidden.h`, which pushes hidden visibility. GCC
|
||||
* takes visibility from the FIRST declaration it sees, so a later definition carrying
|
||||
* __attribute__((visibility("default"))) is silently ignored and the symbol lands in the
|
||||
* object as GLOBAL HIDDEN — present to `nm`, absent from the dynamic table, and therefore
|
||||
* impossible to interpose. Verified with readelf. This file deliberately includes no NPAPI
|
||||
* headers at all and describes the functions with void* so there is no prior declaration to
|
||||
* inherit hidden visibility from.
|
||||
*
|
||||
* WHY IT MATTERS. webOS's Flash imports NPN_CreateObject and NPN_InvalidateRect as undefined
|
||||
* dynamic symbols. Left alone they bind into libWebKitLuna.so — another browser engine that
|
||||
* is in this process because Flash (and our Piranha drawing path) pull it in. Two consequences,
|
||||
* both measured:
|
||||
* 1. Flash's repaint requests go to LunaSysMgr's WebKit and never reach our engine, so the
|
||||
* plugin is only ever painted when something else happens to invalidate it. That alone
|
||||
* makes an animating plugin look frozen.
|
||||
* 2. It is a latent crash: UXP stores a PluginInstanceChild* in NPP::ndata, while
|
||||
* libWebKitLuna's NPN_InvalidateRect casts that same field to a WebCore::PluginView* and
|
||||
* makes a virtual call on it — a wild vtable dispatch on the first repaint.
|
||||
*
|
||||
* The forwarding pointers are installed by PluginModuleChild once the browser function table
|
||||
* exists; until then these are safe no-ops.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
void (*gJihadNPNInvalidateRect)(void* aNPP, void* aRect) = 0;
|
||||
void* (*gJihadNPNCreateObject)(void* aNPP, void* aClass) = 0;
|
||||
|
||||
// Read by the host repaint clock in PluginInstanceChild.cpp: while this is zero the plugin has
|
||||
// never asked to be repainted, so the host has to drive it.
|
||||
unsigned gJihadNPNInvalidateRectCalls = 0;
|
||||
|
||||
__attribute__((visibility("default"))) void
|
||||
NPN_InvalidateRect(void* aNPP, void* aRect)
|
||||
{
|
||||
// The first call is the signal that the plugin's frame clock is finally running; without
|
||||
// it the plugin is only ever painted by an unrelated invalidation, which is
|
||||
// indistinguishable on screen from a plugin that draws nothing. Log it once, and log the
|
||||
// no-forwarder case too — arriving before PluginModuleChild installs the pointers is a
|
||||
// dropped repaint, not a no-op.
|
||||
gJihadNPNInvalidateRectCalls++;
|
||||
static int sLogged = 0;
|
||||
if (sLogged < 8) {
|
||||
sLogged++;
|
||||
fprintf(stderr, "[jihad-npapi-child] NPN_InvalidateRect npp=%p rect=%p fwd=%p\n",
|
||||
aNPP, aRect, (void*)gJihadNPNInvalidateRect);
|
||||
fflush(stderr);
|
||||
}
|
||||
if (gJihadNPNInvalidateRect) {
|
||||
gJihadNPNInvalidateRect(aNPP, aRect);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void*
|
||||
NPN_CreateObject(void* aNPP, void* aClass)
|
||||
{
|
||||
return gJihadNPNCreateObject ? gJihadNPNCreateObject(aNPP, aClass) : 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -125,6 +125,29 @@ child:
|
||||
// of its parent has changed
|
||||
async ContentsScaleFactorChanged(double aContentsScaleFactor);
|
||||
|
||||
// Jihad (webOS, cavekit-addons-extensions.md R7): ask the plugin for ONE frame, now.
|
||||
//
|
||||
// Palm's host issued npPalmDrawEvent every time the PAGE painted, so Flash can sit on its own
|
||||
// frame clock and expect to be asked. Patch 0026 got it drawing by giving the CHILD a timer,
|
||||
// but that timer free-runs against the daemon's tick, so a frame lands at a drifting phase and
|
||||
// waits up to a whole tick to be published. Measured on device with a 30 fps SWF: 1:1 delivery
|
||||
// and a 31-34 ms average, but a gap spread of 9-62 ms, which is what the eye reads as stutter.
|
||||
//
|
||||
// CAUTION, measured 2026-08-10 and it contradicts what 0026 assumed: the plugin DOES invalidate
|
||||
// itself, ~22 times a second, alongside our ~27. The counter 0026 reads to conclude otherwise
|
||||
// (gJihadNPNInvalidateRectCalls) only sees calls that bind the NPN_InvalidateRect SYMBOL, and
|
||||
// an ordinary NPAPI plugin goes through the NPNetscapeFuncs table instead. So this message is
|
||||
// currently a SECOND producer rather than the only one, and whether it should exist at all for
|
||||
// a self-invalidating plugin is open — see the R7 frame-rate criterion.
|
||||
//
|
||||
// This is the pull that removes the second clock. ASYNC on purpose: the stock host pulls the
|
||||
// plugin synchronously inside its page paint (PluginViewPalm), but out-of-process that shape
|
||||
// is intr NPP_HandleEvent, which would block the daemon's main thread — and therefore its
|
||||
// tick and its YAP socket — for a full IPC round trip plus a Flash paint on every frame.
|
||||
// Async keeps the phase lock and costs nothing: the child coalesces duplicate requests via
|
||||
// mCurrentInvalidateTask, so an extra request while one is in flight is a no-op.
|
||||
async JihadRequestFrame();
|
||||
|
||||
// ********************** Async plugins rendering
|
||||
// see https://wiki.mozilla.org/Gecko:AsyncPluginPainting
|
||||
// **********************
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -36,7 +36,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#ifdef MOZ_ENABLE_NPAPI_GTK2
|
||||
#include "gtk2xtbin.h"
|
||||
#endif
|
||||
|
||||
@@ -141,6 +141,10 @@ protected:
|
||||
virtual bool
|
||||
RecvContentsScaleFactorChanged(const double& aContentsScaleFactor) override;
|
||||
|
||||
// Jihad: the host is driving the frame clock — draw one now. See PPluginInstance.ipdl.
|
||||
virtual bool
|
||||
RecvJihadRequestFrame() override;
|
||||
|
||||
virtual bool
|
||||
AnswerNPP_Destroy(NPError* result) override;
|
||||
|
||||
@@ -462,6 +466,8 @@ private:
|
||||
NPSetWindowCallbackStruct mWsInfo;
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
bool mXEmbed;
|
||||
#endif
|
||||
#ifdef MOZ_ENABLE_NPAPI_GTK2
|
||||
XtClient mXtClient;
|
||||
#endif
|
||||
#elif defined(OS_WIN)
|
||||
@@ -639,6 +645,22 @@ private:
|
||||
// and p->getvalue return always false
|
||||
bool mIsTransparent;
|
||||
|
||||
// Jihad R7 (webOS): npPalmGainFocusEvent is a once-per-instance startup signal, but the
|
||||
// setwindow that carries it runs again on every resize and scroll.
|
||||
bool mJihadSentPalmGainFocus;
|
||||
|
||||
// Jihad R7 (webOS): cap on the input-delivery breadcrumb in AnswerNPP_HandleEvent. A
|
||||
// pen-move stream is unbounded; the daemon log is the only field-debug channel there is.
|
||||
uint32_t mJihadInputEventsLogged;
|
||||
|
||||
public:
|
||||
// Jihad R7 (webOS): repaint this instance from the host's own clock. See
|
||||
// JihadGlibPumpTask::Run — a webOS plugin is not obliged to ask for repaints, because the
|
||||
// host it was written for painted it as part of every page paint.
|
||||
void JihadPalmRepaintTick();
|
||||
|
||||
private:
|
||||
|
||||
// Surface type optimized of parent process
|
||||
gfxSurfaceType mSurfaceType;
|
||||
|
||||
|
||||
@@ -115,6 +115,16 @@ PluginInstanceParent::LookupPluginInstanceByID(uintptr_t aId)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Jihad: every live instance, so a C entry point can reach one. jihad_plugin_frame_seq needs no
|
||||
// instance because it only counts; a frame REQUEST has to be sent on an actual actor.
|
||||
//
|
||||
// A plain file-static array maintained in the ctor/dtor below, not a hashtable: this is at most a
|
||||
// couple of entries on a single-card embedding, it is only ever touched on the main thread (the
|
||||
// daemon calls the export from inside its own tick, on that same thread), and the alternative —
|
||||
// walking nsPluginHost's nsNPAPIPluginInstance list the way jihad_plugin_palm_spotlight does —
|
||||
// would still have to get from there to the PluginInstanceParent actor to call Send on it.
|
||||
static nsTArray<PluginInstanceParent*>* gJihadLiveInstances = nullptr;
|
||||
|
||||
PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
|
||||
NPP npp,
|
||||
const nsCString& aMimeType,
|
||||
@@ -145,10 +155,24 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
|
||||
sPluginInstanceList = new nsClassHashtable<nsVoidPtrHashKey, PluginInstanceParent>();
|
||||
}
|
||||
#endif
|
||||
// Jihad: join the live list that jihad_plugin_request_frame walks. Leaked on purpose — it
|
||||
// outlives every instance and there is no shutdown hook here that is guaranteed to run
|
||||
// after the last dtor.
|
||||
if (!gJihadLiveInstances) {
|
||||
gJihadLiveInstances = new nsTArray<PluginInstanceParent*>();
|
||||
}
|
||||
gJihadLiveInstances->AppendElement(this);
|
||||
}
|
||||
|
||||
PluginInstanceParent::~PluginInstanceParent()
|
||||
{
|
||||
// Jihad: leave the live list FIRST. Anything below can run script or spin the loop, and a
|
||||
// frame request delivered to a half-destroyed actor is a use-after-free rather than a
|
||||
// dropped frame.
|
||||
if (gJihadLiveInstances) {
|
||||
gJihadLiveInstances->RemoveElement(this);
|
||||
}
|
||||
|
||||
if (mNPP)
|
||||
mNPP->pdata = nullptr;
|
||||
|
||||
@@ -269,6 +293,8 @@ PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle*
|
||||
XID id;
|
||||
#elif defined(XP_DARWIN)
|
||||
intptr_t id;
|
||||
#elif defined(MOZ_WIDGET_HEADLESS)
|
||||
intptr_t id; // Jihad headless: no native window id
|
||||
#else
|
||||
#warning Implement me
|
||||
#endif
|
||||
@@ -769,6 +795,51 @@ PluginInstanceParent::RecvShowDirectBitmap(Shmem&& buffer,
|
||||
return false;
|
||||
}
|
||||
|
||||
// NO COMPOSITOR PROCESS: composite the plugin's bitmap directly.
|
||||
//
|
||||
// Everything below this point routes the plugin's pixels through a TextureClient to a
|
||||
// compositor, and that requires ImageBridgeChild — which only exists with off-main-thread
|
||||
// composition. This embedder deliberately runs OMTC off (the offscreen renderer uses the
|
||||
// in-process BasicLayerManager and hands finished frames to the browser adapter itself),
|
||||
// so ImageBridgeChild::GetSingleton() is null and TextureClientRecycleAllocator is built
|
||||
// around a null KnowsCompositor. The first plugin paint then dereferenced it and took the
|
||||
// whole daemon down with it — upstream never sees this because the DXGI path a few
|
||||
// hundred lines up checks the forwarder and the direct-bitmap path does not.
|
||||
//
|
||||
// A BasicLayerManager does not need a TextureClient at all: it draws an ImageLayer from
|
||||
// the image's own SourceSurface. So wrap the pixels in a plain SourceSurfaceImage and
|
||||
// hand that to the container.
|
||||
//
|
||||
// The pixels are COPIED, not wrapped. `source` above wraps the shmem the plugin drew
|
||||
// into, and the plugin owns that buffer and may draw the next frame into it as soon as
|
||||
// this call returns; the texture path's UpdateFromSurface copies for the same reason.
|
||||
if (!ImageBridgeChild::GetSingleton()) {
|
||||
RefPtr<gfx::DataSourceSurface> copy =
|
||||
gfx::Factory::CreateDataSourceSurface(size, format);
|
||||
if (!copy) {
|
||||
NS_WARNING("Could not allocate a surface for plugin bitmap!");
|
||||
return false;
|
||||
}
|
||||
{
|
||||
gfx::DataSourceSurface::ScopedMap srcMap(source, gfx::DataSourceSurface::READ);
|
||||
gfx::DataSourceSurface::ScopedMap dstMap(copy, gfx::DataSourceSurface::WRITE);
|
||||
if (!srcMap.IsMapped() || !dstMap.IsMapped()) {
|
||||
return false;
|
||||
}
|
||||
const int32_t bpp = BytesPerPixel(format);
|
||||
const int32_t rowBytes = size.width * bpp;
|
||||
for (int32_t y = 0; y < size.height; y++) {
|
||||
memcpy(dstMap.GetData() + (ptrdiff_t)y * dstMap.GetStride(),
|
||||
srcMap.GetData() + (ptrdiff_t)y * srcMap.GetStride(),
|
||||
rowBytes);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(size, copy);
|
||||
SetCurrentImage(image);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allocate a texture for the compositor.
|
||||
RefPtr<TextureClientRecycleAllocator> allocator = mParent->EnsureTextureAllocatorForDirectBitmap();
|
||||
RefPtr<TextureClient> texture = allocator->CreateOrRecycle(
|
||||
@@ -864,6 +935,55 @@ PluginInstanceParent::RecvShowDirectDXGISurface(const WindowsHandle& handle,
|
||||
}
|
||||
#endif // MOZ_ENABLE_NPAPI
|
||||
|
||||
// Jihad: monotonic count of plugin frames that have reached the parent. Process-wide rather
|
||||
// than per-instance, matching how the daemon consumes it — the embedding is single-page and the
|
||||
// question it answers is "has ANY plugin produced a new frame since I last published". A plain
|
||||
// uint32_t incremented and read on the same (main) thread; the daemon reads it from the same
|
||||
// thread inside its tick, so no atomics are needed and none are used elsewhere in this bridge.
|
||||
static uint32_t gJihadPluginFrameSeq = 0;
|
||||
|
||||
extern "C" {
|
||||
// Weak-linked from the daemon (render/goanna/GoannaRenderPage.cpp), the same way
|
||||
// jihad_offscreen_take_dirty is: an older libxul simply does not export it, and the daemon then
|
||||
// falls back to its dirty-flag behaviour instead of failing to load.
|
||||
MOZ_EXPORT uint32_t jihad_plugin_frame_seq(void)
|
||||
{
|
||||
return gJihadPluginFrameSeq;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// Ask every live plugin for one frame. Returns how many were asked, so the daemon can tell "no
|
||||
// plugin" from "delivered" — the same contract as jihad_plugin_palm_spotlight.
|
||||
//
|
||||
// WEAK on the daemon side: a libxul without this symbol keeps the child's own timer as the frame
|
||||
// clock (patch 0026 behaviour), which is slower to settle but never wrong.
|
||||
MOZ_EXPORT int jihad_plugin_request_frame(void)
|
||||
{
|
||||
#ifdef MOZ_ENABLE_NPAPI
|
||||
if (!gJihadLiveInstances) {
|
||||
return 0;
|
||||
}
|
||||
// Copy first: SendJihadRequestFrame can fail and tear the actor down, which would mutate the
|
||||
// array underneath the loop. Same reasoning as the instance-list copy in
|
||||
// jihad_plugin_palm_spotlight (nsPluginHost.cpp).
|
||||
nsTArray<PluginInstanceParent*> snap(*gJihadLiveInstances);
|
||||
int asked = 0;
|
||||
for (uint32_t i = 0; i < snap.Length(); ++i) {
|
||||
if (!gJihadLiveInstances->Contains(snap[i])) {
|
||||
continue; // torn down by an earlier Send in this same loop
|
||||
}
|
||||
if (snap[i]->SendJihadRequestFrame()) {
|
||||
asked++;
|
||||
}
|
||||
}
|
||||
return asked;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceParent::RecvShow(const NPRect& updatedRect,
|
||||
const SurfaceDescriptor& newSurface,
|
||||
@@ -877,6 +997,27 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
|
||||
|
||||
MOZ_ASSERT(!IsUsingDirectDrawing());
|
||||
|
||||
// FRAME-RATE READOUT, parent side. Paired with the child's `palm draw:` line: the child
|
||||
// reports how often the plugin was ASKED to draw, this reports how many of those frames
|
||||
// actually crossed the process boundary. A gap between the two numbers is the difference
|
||||
// between "the plugin is slow" and "we are dropping its frames", which are opposite bugs.
|
||||
{
|
||||
static uint32_t sShows = 0;
|
||||
static uint32_t sLastReport = 0;
|
||||
sShows++;
|
||||
uint32_t nowMs = uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
|
||||
if (!sLastReport) {
|
||||
sLastReport = nowMs;
|
||||
} else if (nowMs - sLastReport >= 2000) {
|
||||
fprintf(stderr, "[jihad-npapi] RecvShow: %u in %ums (%.1f fps)\n",
|
||||
sShows, nowMs - sLastReport,
|
||||
sShows * 1000.0 / double(nowMs - sLastReport));
|
||||
fflush(stderr);
|
||||
sShows = 0;
|
||||
sLastReport = nowMs;
|
||||
}
|
||||
}
|
||||
|
||||
// XXXjwatt rewrite to use Moz2D
|
||||
RefPtr<gfxASurface> surface;
|
||||
if (newSurface.type() == SurfaceDescriptor::TShmem) {
|
||||
@@ -959,14 +1100,75 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
|
||||
updatedRect.bottom - updatedRect.top);
|
||||
surface->MarkDirty(ur);
|
||||
|
||||
bool isPlugin = true;
|
||||
RefPtr<gfx::SourceSurface> sourceSurface =
|
||||
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface, isPlugin);
|
||||
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), sourceSurface);
|
||||
// JIHAD (2026-08-09): route the legacy windowless show through the SAME
|
||||
// representation the direct-bitmap path uses, for the SAME reasons.
|
||||
//
|
||||
// Background: the webOS npPalmDrawEvent path (PaintRectToPlatformSurface) makes Flash
|
||||
// blit its stage into mCurrentSurface — measured full-magenta in the child — and ships
|
||||
// it here via SendShow. The control plugin's pixels reach the screen through
|
||||
// RecvShowDirectBitmap; Flash's did not, although both feed the one mImageContainer the
|
||||
// BasicLayerManager reads. Two concrete differences made the legacy path invisible:
|
||||
// 1. It WRAPPED the shmem (GetSourceSurfaceForSurface(nullptr,…)) instead of copying.
|
||||
// The child double-buffers and SwapSurfaces() immediately after SendShow returns,
|
||||
// so a wrapped front buffer is the very buffer the child repaints next — the image
|
||||
// the layer later samples is racing the child. RecvShowDirectBitmap copies for
|
||||
// exactly this reason ("the plugin owns that buffer and may draw the next frame
|
||||
// into it as soon as this call returns").
|
||||
// 2. It stamped no frameID. SetCurrentImage() on the working path assigns a monotonic
|
||||
// ++mFrameID; the ImageContainer/ImageLayer treats a repeated frameID as "no new
|
||||
// frame". A fixed 0 can be coalesced away.
|
||||
// Copying into a fresh DataSourceSurface and stamping a monotonic frameID makes this
|
||||
// path identical in substance to the one already proven to composite here.
|
||||
IntSize ssize = surface->GetSize();
|
||||
RefPtr<Image> image;
|
||||
RefPtr<gfxImageSurface> imgSurf = surface->GetAsImageSurface();
|
||||
if (imgSurf) {
|
||||
// Always present the copy as B8G8R8A8, and force the alpha channel opaque when the
|
||||
// source carries no alpha (RGB24 / B8G8R8X8). This is Bug 1196927 in this file's own
|
||||
// words (see PluginInstanceChild::PaintRectToSurface): Cairo/Skia leave the X byte of
|
||||
// a BGRX surface zeroed, and a BasicImageLayer then composites those pixels multiplied
|
||||
// by alpha 0 — i.e. fully transparent. That is exactly why Flash's magenta stage
|
||||
// arrived here (RecvShow logged px0=0xffff00ff) yet BasicImageLayer::Paint drew
|
||||
// nothing visible, while the control plugin — which ships B8G8R8A8 — composited fine
|
||||
// through the identical layer path. webOS windowless plugins are opaque
|
||||
// (wmode=opaque forces the windowless instance in the first place), so forcing alpha
|
||||
// opaque is correct here.
|
||||
const SurfaceFormat srcFmt = surface->GetSurfaceFormat();
|
||||
const bool srcHasAlpha =
|
||||
srcFmt != SurfaceFormat::B8G8R8X8 &&
|
||||
srcFmt != SurfaceFormat::R8G8B8X8;
|
||||
RefPtr<gfx::DataSourceSurface> copy =
|
||||
gfx::Factory::CreateDataSourceSurface(ssize, SurfaceFormat::B8G8R8A8);
|
||||
if (copy) {
|
||||
gfx::DataSourceSurface::ScopedMap dstMap(copy, gfx::DataSourceSurface::WRITE);
|
||||
if (dstMap.IsMapped()) {
|
||||
const int32_t rowBytes = ssize.width * 4;
|
||||
for (int32_t y = 0; y < ssize.height; y++) {
|
||||
uint8_t* dstRow = dstMap.GetData() + (ptrdiff_t)y * dstMap.GetStride();
|
||||
memcpy(dstRow, imgSurf->Data() + (ptrdiff_t)y * imgSurf->Stride(),
|
||||
rowBytes);
|
||||
if (!srcHasAlpha) {
|
||||
for (int32_t x = 0; x < ssize.width; x++) {
|
||||
dstRow[x * 4 + 3] = 0xff; // BGRA: byte 3 is alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
image = new SourceSurfaceImage(ssize, copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!image) {
|
||||
// Fallback to the original wrap if the copy path could not be taken.
|
||||
bool isPlugin = true;
|
||||
RefPtr<gfx::SourceSurface> sourceSurface =
|
||||
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface, isPlugin);
|
||||
image = new SourceSurfaceImage(ssize, sourceSurface);
|
||||
}
|
||||
|
||||
ImageContainer::NonOwningImage holder(image);
|
||||
holder.mFrameID = ++mFrameID;
|
||||
AutoTArray<ImageContainer::NonOwningImage,1> imageList;
|
||||
imageList.AppendElement(
|
||||
ImageContainer::NonOwningImage(image));
|
||||
imageList.AppendElement(holder);
|
||||
|
||||
ImageContainer *container = GetImageContainer();
|
||||
container->SetCurrentImages(imageList);
|
||||
@@ -976,6 +1178,17 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
|
||||
}
|
||||
|
||||
mFrontSurface = surface;
|
||||
// Jihad: count DELIVERED plugin frames so the embedder can tell a new one from a repeat.
|
||||
// The daemon's only frame signal is PuppetWidget's sticky dirty boolean, which carries no
|
||||
// identity: the refresh driver and ordinary page damage set the same flag, so "there is
|
||||
// damage" cannot answer "did the plugin draw again". Measured 2026-08-10 on a 30 fps SWF,
|
||||
// that ambiguity is what made both available policies wrong — publishing on every dirty
|
||||
// tick sent ~78 card frames for ~60 plugin frames (duplicates, and 22-44 deferred because
|
||||
// the adapter could not drain them), while pacing on a fixed 33 ms grid sent ~57 for ~68
|
||||
// (dropped frames, so the animation stepped two positions at once). The stock webOS host
|
||||
// never had to choose: it pulls the plugin synchronously inside the page paint, so it is
|
||||
// 1:1 by construction. This counter is what lets the daemon be 1:1 too.
|
||||
++gJihadPluginFrameSeq;
|
||||
RecvNPN_InvalidateRect(updatedRect);
|
||||
|
||||
PLUGIN_LOG_DEBUG((" (RecvShow invalidated for surface %p)",
|
||||
@@ -1301,7 +1514,11 @@ PluginInstanceParent::GetImageContainer()
|
||||
return mImageContainer;
|
||||
}
|
||||
|
||||
if (IsUsingDirectDrawing()) {
|
||||
if (IsUsingDirectDrawing() && ImageBridgeChild::GetSingleton()) {
|
||||
// ASYNCHRONOUS means "shared with the compositor through ImageBridge". Without an
|
||||
// ImageBridge the async container silently gets no image client and never publishes
|
||||
// anything, so with OMTC off this has to be an ordinary local container — which is
|
||||
// what the BasicLayerManager reads from. See RecvShowDirectBitmap.
|
||||
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
|
||||
} else {
|
||||
mImageContainer = LayerManager::CreateImageContainer();
|
||||
|
||||
@@ -117,6 +117,8 @@ typedef HWND NativeWindowHandle;
|
||||
typedef XID NativeWindowHandle;
|
||||
#elif defined(XP_DARWIN)
|
||||
typedef intptr_t NativeWindowHandle; // never actually used, will always be 0
|
||||
#elif defined(MOZ_WIDGET_HEADLESS)
|
||||
typedef intptr_t NativeWindowHandle; // Jihad headless: no native window handle
|
||||
#else
|
||||
#error Need NativeWindowHandle for this platform
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#include <gtk/gtk.h>
|
||||
#if (GTK_MAJOR_VERSION == 3)
|
||||
#include <gtk/gtkx.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "nsIFile.h"
|
||||
@@ -57,6 +60,9 @@ using namespace mozilla::ipc;
|
||||
using namespace mozilla::plugins;
|
||||
using namespace mozilla::widget;
|
||||
|
||||
// Defined at the bottom of this file; see the NPN SYMBOL INTERPOSITION block there.
|
||||
namespace mozilla { namespace plugins { void JihadInstallNPNInterposition(); } }
|
||||
|
||||
#if defined(XP_WIN)
|
||||
const wchar_t * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
|
||||
const wchar_t * kMozillaWindowClass = L"MozillaWindowClass";
|
||||
@@ -229,6 +235,10 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
|
||||
{
|
||||
NS_ASSERTION(aChannel, "need a channel");
|
||||
|
||||
// Point the exported NPN_* globals at the real implementations before any plugin can be
|
||||
// loaded, so a plugin that resolves them as symbols reaches us rather than libWebKitLuna.
|
||||
JihadInstallNPNInterposition();
|
||||
|
||||
if (!InitGraphics())
|
||||
return false;
|
||||
|
||||
@@ -248,7 +258,11 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
|
||||
nsPluginFile pluginFile(localFile);
|
||||
|
||||
nsPluginInfo info = nsPluginInfo();
|
||||
if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary))) {
|
||||
nsresult jihadRv = pluginFile.GetPluginInfo(info, &mLibrary);
|
||||
fprintf(stderr, "[jihad-npapi-child] GetPluginInfo rv=0x%x lib=%d\n",
|
||||
(unsigned)jihadRv, (int)!!mLibrary);
|
||||
fflush(stderr);
|
||||
if (NS_FAILED(jihadRv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1101,7 +1115,11 @@ _getvalue(NPP aNPP,
|
||||
return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
|
||||
}
|
||||
else {
|
||||
#ifdef MOZ_ENABLE_NPAPI_GTK2
|
||||
*(void **)aValue = xt_client_get_display();
|
||||
#else
|
||||
*(void **)aValue = DefaultXDisplay();
|
||||
#endif
|
||||
}
|
||||
return NPERR_NO_ERROR;
|
||||
}
|
||||
@@ -1836,6 +1854,8 @@ bool
|
||||
PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv)
|
||||
{
|
||||
*rv = DoNP_Initialize(aSettings);
|
||||
fprintf(stderr, "[jihad-npapi-child] AnswerNP_Initialize rv=%d\n", (int)*rv);
|
||||
fflush(stderr);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1875,6 +1895,8 @@ PluginModuleChild::DoNP_Initialize(const PluginSettings& aSettings)
|
||||
# error Please implement me for your platform
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "[jihad-npapi-child] DoNP_Initialize plugin returned %d\n", (int)result);
|
||||
fflush(stderr);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2610,3 +2632,31 @@ PluginModuleChild::RecvNPP_SetValue_NPNVaudioDeviceChangeDetails(
|
||||
#endif
|
||||
}
|
||||
#endif // MOZ_ENABLE_NPAPI
|
||||
|
||||
// ── NPN SYMBOL INTERPOSITION (Jihad, addons R7) ──────────────────────────────────────────
|
||||
//
|
||||
// The exported NPN_* globals themselves live in JihadNPNInterpose.cpp (they must, to escape
|
||||
// hidden visibility — see the long comment there). All that is needed here is to point them
|
||||
// at the same implementations the browser function table uses, which is done once the table
|
||||
// exists. Installed from a static initializer-free helper called by the child at startup.
|
||||
extern "C" {
|
||||
extern void (*gJihadNPNInvalidateRect)(void*, void*);
|
||||
extern void* (*gJihadNPNCreateObject)(void*, void*);
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
||||
void JihadInstallNPNInterposition()
|
||||
{
|
||||
const NPNetscapeFuncs& f = PluginModuleChild::sBrowserFuncs;
|
||||
gJihadNPNInvalidateRect = (void(*)(void*, void*))f.invalidaterect;
|
||||
gJihadNPNCreateObject = (void*(*)(void*, void*))f.createobject;
|
||||
fprintf(stderr, "[jihad-npapi-child] NPN interposition installed invalidaterect=%p "
|
||||
"createobject=%p\n",
|
||||
(void*)f.invalidaterect, (void*)f.createobject);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
} // namespace plugins
|
||||
} // namespace mozilla
|
||||
@@ -444,6 +444,8 @@ PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
|
||||
parent->mSubprocess->SetCallRunnableImmediately(!parent->mIsStartingAsync);
|
||||
bool launched = parent->mSubprocess->Launch(Move(onLaunchedRunnable),
|
||||
aPluginTag->mSandboxLevel);
|
||||
fprintf(stderr, "[jihad-npapi] LoadModule path=%s async=%d launched=%d\n",
|
||||
aFilePath, (int)parent->mIsStartingAsync, (int)launched);
|
||||
if (!launched) {
|
||||
// We never reached open
|
||||
parent->mShutdown = true;
|
||||
@@ -455,7 +457,10 @@ PluginModuleChromeParent::LoadModule(const char* aFilePath, uint32_t aPluginId,
|
||||
parent->mIsBlocklisted = NS_FAILED(rv) || blocklistState != 0;
|
||||
if (!parent->mIsStartingAsync) {
|
||||
int32_t launchTimeoutSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
|
||||
if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
|
||||
bool connected = parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000);
|
||||
fprintf(stderr, "[jihad-npapi] LoadModule WaitUntilConnected(%ds) = %d\n",
|
||||
(int)launchTimeoutSecs, (int)connected);
|
||||
if (!connected) {
|
||||
parent->mShutdown = true;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1134,9 +1139,20 @@ PluginModuleChromeParent::OnHangUIContinue()
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
// Defined with the rest of the Jihad CPU-boost helpers further down this file.
|
||||
namespace { void JihadCpuBoostReleaseAll(); }
|
||||
|
||||
void
|
||||
PluginModuleParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
// Release the CPU boost here as well as in NPP_Destroy, because on this port NPP_Destroy is
|
||||
// NOT the path a plugin usually leaves by: navigating away from a Flash page ends in the
|
||||
// child's own mozalloc_abort, so the instance never gets an orderly destroy and the boost
|
||||
// would leak — device-observed 2026-08-10, tunables still at 40/50000 after navigation with
|
||||
// no release logged. This runs for every ActorDestroyReason, normal and abnormal, which also
|
||||
// covers a plugin crash.
|
||||
JihadCpuBoostReleaseAll();
|
||||
|
||||
switch (why) {
|
||||
case AbnormalShutdown: {
|
||||
mShutdown = true;
|
||||
@@ -1271,6 +1287,172 @@ NP_BEGIN_MACRO \
|
||||
return i->func; \
|
||||
NP_END_MACRO
|
||||
|
||||
// ── Jihad: hold a CPU-frequency boost while a plugin instance is alive ──────────────────────
|
||||
//
|
||||
// WHY. The TouchPad's governor is Palm's `ondemandtcl`, whose stock `up_threshold` is 95 — it
|
||||
// only raises the clock when a single core exceeds 95% load. Flash never gets there: measured on
|
||||
// device, the daemon runs ~26% and plugin-container ~11% of two cores, so a passively-watched
|
||||
// animation sits at the FLOOR. Sampling `scaling_cur_freq` once a second during playback gave
|
||||
// 192000-384000 out of a 1188000 maximum, snapping back to 1188000 the moment the page was
|
||||
// navigated away. The whole system was only 69% busy, so this was never a capacity wall.
|
||||
//
|
||||
// Dropping `up_threshold` to 40 and `sampling_rate` to 50000 pins 1188000 and lifts the card
|
||||
// composite from 20.4-27.5 fps to 30.7-35.2, daemon paints 24.0-28.7 to 31.4-33.0, and the frame
|
||||
// gap MAXIMUM — the number that reads as stutter — from 63-81 ms to 49-56. It also clears the
|
||||
// audio xruns, because MP3 decode at 192 MHz underruns.
|
||||
//
|
||||
// NEVER WRITE `scaling_governor`. Switching away from `ondemandtcl` DEADLOCKS cpufreq in the
|
||||
// kernel: every later reader of any cpufreq node blocks in unkillable D state — the daemon,
|
||||
// `powerlog`, and the restore path itself — so the boost is never released and only a reboot
|
||||
// clears it (and a clean `reboot` will not run, because init waits on those D-state tasks).
|
||||
// Governor TUNABLE writes, which is all this does, are safe and return instantly.
|
||||
//
|
||||
// These are SYSTEM-WIDE tunables, so they are held only while a plugin instance exists and the
|
||||
// ORIGINAL values are read back and restored on teardown. The upstart job restores them again
|
||||
// from `post-stop`, because a daemon that dies holding the boost would otherwise leave the whole
|
||||
// device tuned for battery burn. Both hooks run on the main thread, so the counter needs no lock.
|
||||
namespace {
|
||||
|
||||
const char kJihadOndemandDir[] = "/sys/devices/system/cpu/cpufreq/ondemandtcl";
|
||||
|
||||
// The stock values, used ONLY as the restore fallback when the current value already equals what
|
||||
// we are about to write — which happens when another variant's daemon is holding the boost.
|
||||
// Saving the boosted value as "original" there would make the last release pin the device.
|
||||
const char kJihadStockUpThreshold[] = "95";
|
||||
const char kJihadStockSamplingRate[] = "200000";
|
||||
|
||||
// The instances that actually TOOK a boost, not a bare count. NPP_Destroy also runs for instances
|
||||
// that failed before the boost was taken, and decrementing a count there would release the boost
|
||||
// out from under a plugin that is still playing.
|
||||
nsTArray<NPP> gJihadBoostedInstances;
|
||||
bool gJihadBoostHeld = false;
|
||||
char gJihadSavedUpThreshold[32];
|
||||
char gJihadSavedSamplingRate[32];
|
||||
|
||||
bool
|
||||
JihadReadTunable(const char* aName, char* aOut, size_t aOutLen)
|
||||
{
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/%s", kJihadOndemandDir, aName);
|
||||
FILE* f = fopen(path, "r");
|
||||
if (!f) return false;
|
||||
char* got = fgets(aOut, aOutLen, f);
|
||||
fclose(f);
|
||||
if (!got) return false;
|
||||
// Trim the trailing newline so a value read back can be compared and rewritten verbatim.
|
||||
size_t n = strlen(aOut);
|
||||
while (n > 0 && (aOut[n - 1] == '\n' || aOut[n - 1] == '\r')) aOut[--n] = '\0';
|
||||
return n > 0;
|
||||
}
|
||||
|
||||
bool
|
||||
JihadWriteTunable(const char* aName, const char* aValue)
|
||||
{
|
||||
char path[256];
|
||||
snprintf(path, sizeof(path), "%s/%s", kJihadOndemandDir, aName);
|
||||
FILE* f = fopen(path, "w");
|
||||
if (!f) return false;
|
||||
bool ok = fputs(aValue, f) >= 0;
|
||||
ok = (fclose(f) == 0) && ok;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Env-tunable for the same reason the repaint divisor is: the right value is a property of the
|
||||
// device under load, not of the source, and a rebuild per candidate costs a 44 MB push.
|
||||
void
|
||||
JihadCpuBoostTargets(const char** aUpThreshold, const char** aSamplingRate)
|
||||
{
|
||||
static char sUp[32] = "40";
|
||||
static char sRate[32] = "50000";
|
||||
static bool sInit = false;
|
||||
if (!sInit) {
|
||||
sInit = true;
|
||||
const char* e = getenv("JIHAD_PLUGIN_CPU_UP_THRESHOLD");
|
||||
if (e && *e) {
|
||||
int v = atoi(e);
|
||||
if (v >= 5 && v <= 95) snprintf(sUp, sizeof(sUp), "%d", v);
|
||||
}
|
||||
e = getenv("JIHAD_PLUGIN_CPU_SAMPLING_RATE");
|
||||
if (e && *e) {
|
||||
int v = atoi(e);
|
||||
if (v >= 10000 && v <= 1000000) snprintf(sRate, sizeof(sRate), "%d", v);
|
||||
}
|
||||
}
|
||||
*aUpThreshold = sUp;
|
||||
*aSamplingRate = sRate;
|
||||
}
|
||||
|
||||
bool
|
||||
JihadCpuBoostEnabled()
|
||||
{
|
||||
const char* e = getenv("JIHAD_PLUGIN_CPU_BOOST");
|
||||
return !(e && e[0] == '0' && e[1] == '\0');
|
||||
}
|
||||
|
||||
void
|
||||
JihadCpuBoostAcquire(NPP aInstance)
|
||||
{
|
||||
if (!JihadCpuBoostEnabled() || gJihadBoostedInstances.Contains(aInstance)) return;
|
||||
gJihadBoostedInstances.AppendElement(aInstance);
|
||||
if (gJihadBoostedInstances.Length() != 1 || gJihadBoostHeld) return;
|
||||
|
||||
const char* wantUp;
|
||||
const char* wantRate;
|
||||
JihadCpuBoostTargets(&wantUp, &wantRate);
|
||||
|
||||
if (!JihadReadTunable("up_threshold", gJihadSavedUpThreshold,
|
||||
sizeof(gJihadSavedUpThreshold)) ||
|
||||
!strcmp(gJihadSavedUpThreshold, wantUp)) {
|
||||
strcpy(gJihadSavedUpThreshold, kJihadStockUpThreshold);
|
||||
}
|
||||
if (!JihadReadTunable("sampling_rate", gJihadSavedSamplingRate,
|
||||
sizeof(gJihadSavedSamplingRate)) ||
|
||||
!strcmp(gJihadSavedSamplingRate, wantRate)) {
|
||||
strcpy(gJihadSavedSamplingRate, kJihadStockSamplingRate);
|
||||
}
|
||||
|
||||
bool ok = JihadWriteTunable("up_threshold", wantUp);
|
||||
ok = JihadWriteTunable("sampling_rate", wantRate) && ok;
|
||||
gJihadBoostHeld = ok;
|
||||
fprintf(stderr, "[jihad-npapi] cpu boost acquire: up_threshold %s->%s sampling_rate %s->%s%s\n",
|
||||
gJihadSavedUpThreshold, wantUp, gJihadSavedSamplingRate, wantRate,
|
||||
ok ? "" : " FAILED (not root, or no ondemandtcl)");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
void
|
||||
JihadCpuBoostRelease(NPP aInstance)
|
||||
{
|
||||
if (!gJihadBoostedInstances.RemoveElement(aInstance)) return;
|
||||
if (!gJihadBoostedInstances.IsEmpty() || !gJihadBoostHeld) return;
|
||||
|
||||
JihadWriteTunable("up_threshold", gJihadSavedUpThreshold);
|
||||
JihadWriteTunable("sampling_rate", gJihadSavedSamplingRate);
|
||||
gJihadBoostHeld = false;
|
||||
fprintf(stderr, "[jihad-npapi] cpu boost release: up_threshold=%s sampling_rate=%s\n",
|
||||
gJihadSavedUpThreshold, gJihadSavedSamplingRate);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
// Drop every outstanding boost at once. Used when the plugin module itself goes away, which on
|
||||
// this port is the NORMAL exit path (see ActorDestroy), not just the crash path.
|
||||
void
|
||||
JihadCpuBoostReleaseAll()
|
||||
{
|
||||
if (gJihadBoostedInstances.IsEmpty()) return;
|
||||
gJihadBoostedInstances.Clear();
|
||||
if (!gJihadBoostHeld) return;
|
||||
|
||||
JihadWriteTunable("up_threshold", gJihadSavedUpThreshold);
|
||||
JihadWriteTunable("sampling_rate", gJihadSavedSamplingRate);
|
||||
gJihadBoostHeld = false;
|
||||
fprintf(stderr, "[jihad-npapi] cpu boost release (module gone): up_threshold=%s sampling_rate=%s\n",
|
||||
gJihadSavedUpThreshold, gJihadSavedSamplingRate);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
NPError
|
||||
PluginModuleParent::NPP_Destroy(NPP instance,
|
||||
NPSavedData** saved)
|
||||
@@ -1296,6 +1478,7 @@ PluginModuleParent::NPP_Destroy(NPP instance,
|
||||
instance->pdata = nullptr;
|
||||
|
||||
Unused << PluginInstanceParent::Call__delete__(parentInstance);
|
||||
JihadCpuBoostRelease(instance);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -2292,6 +2475,12 @@ PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
|
||||
|
||||
UpdatePluginTimeout();
|
||||
|
||||
// Taken ONLY here, on the single success path, and released in NPP_Destroy. Every failure
|
||||
// return above this point either deletes |parentInstance| itself or hands it to NPP_Destroy,
|
||||
// and a boost acquired on one of those paths would never be released — which on this device
|
||||
// means leaving the WHOLE system tuned for battery burn with nothing owning it.
|
||||
JihadCpuBoostAcquire(instance);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,9 +111,14 @@ PluginProcessChild::Init()
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[jihad-npapi-child] InitForChrome plugin=%s parentPid=%d\n",
|
||||
pluginFilename.c_str(), (int)ParentPid());
|
||||
fflush(stderr);
|
||||
bool retval = mPlugin.InitForChrome(pluginFilename, ParentPid(),
|
||||
IOThreadChild::message_loop(),
|
||||
IOThreadChild::channel());
|
||||
fprintf(stderr, "[jihad-npapi-child] InitForChrome = %d\n", (int)retval);
|
||||
fflush(stderr);
|
||||
#if defined(XP_MACOSX)
|
||||
if (nsCocoaFeatures::OnYosemiteOrLater()) {
|
||||
// Explicitly turn off CGEvent logging. This works around bug 1092855.
|
||||
|
||||
@@ -88,6 +88,10 @@ UNIFIED_SOURCES += [
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
# Jihad: must NOT be unified — it deliberately includes no NPAPI headers so its exported
|
||||
# NPN_* symbols escape the hidden-visibility pragma. Merging it with a TU that includes
|
||||
# npapi.h would reintroduce the prior hidden declaration and silently un-export them.
|
||||
'JihadNPNInterpose.cpp',
|
||||
'PluginInstanceChild.cpp', # 'PluginThreadCallback' : ambiguous symbol
|
||||
'PluginModuleChild.cpp', # Redefinition of mozilla::WindowsDllInterceptor sUser32Intercept
|
||||
]
|
||||
@@ -143,11 +147,13 @@ LOCAL_INCLUDES += [
|
||||
|
||||
DEFINES['FORCE_PR_LOG'] = True
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gtk3':
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
else:
|
||||
if (CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3' and
|
||||
CONFIG['MOZ_ENABLE_NPAPI'] and
|
||||
CONFIG['MOZ_ENABLE_NPAPI_GTK2']):
|
||||
# Force build against gtk+2 for struct offsets and such.
|
||||
CXXFLAGS += CONFIG['MOZ_GTK2_CFLAGS']
|
||||
else:
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ interface Element : Node {
|
||||
|
||||
[SameObject]
|
||||
readonly attribute NamedNodeMap attributes;
|
||||
[Pure]
|
||||
sequence<DOMString> getAttributeNames();
|
||||
[Pure]
|
||||
DOMString? getAttribute(DOMString name);
|
||||
@@ -159,7 +158,7 @@ interface Element : Node {
|
||||
* this property returns an object with computed values for grid
|
||||
* tracks and lines.
|
||||
*/
|
||||
[ChromeOnly, Pure]
|
||||
[ChromeOnly]
|
||||
sequence<Grid> getGridFragments();
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ interface HTMLElement : Element {
|
||||
[CEReactions, SetterThrows, Pure]
|
||||
attribute DOMString dir;
|
||||
|
||||
[CEReactions, GetterThrows, Pure]
|
||||
[CEReactions, GetterThrows]
|
||||
attribute [TreatNullAs=EmptyString] DOMString innerText;
|
||||
|
||||
// user interaction
|
||||
|
||||
@@ -51,7 +51,7 @@ interface HTMLInputElement : HTMLElement {
|
||||
attribute boolean formNoValidate;
|
||||
[CEReactions, Pure, SetterThrows]
|
||||
attribute DOMString formTarget;
|
||||
[CEReactions, Pure, SetterThrows]
|
||||
[CEReactions, SetterThrows]
|
||||
attribute unsigned long height;
|
||||
[Pure]
|
||||
attribute boolean indeterminate;
|
||||
|
||||
@@ -301,6 +301,7 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */)
|
||||
.setAsyncStack(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asyncstack")))
|
||||
.setWerror(GetWorkerPref<bool>(NS_LITERAL_CSTRING("werror")))
|
||||
.setStreams(GetWorkerPref<bool>(NS_LITERAL_CSTRING("streams")))
|
||||
.setWeakRefs(GetWorkerPref<bool>(NS_LITERAL_CSTRING("weakrefs")))
|
||||
.setExtraWarnings(GetWorkerPref<bool>(NS_LITERAL_CSTRING("strict")))
|
||||
.setArrayProtoValues(GetWorkerPref<bool>(
|
||||
NS_LITERAL_CSTRING("array_prototype_values")));
|
||||
|
||||
@@ -35,6 +35,7 @@ WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, O
|
||||
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.streams.enabled", StreamsEnabled, STREAMS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("javascript.options.weakrefs", WeakRefsEnabled, WEAKREFS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
||||
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
// Local Includes
|
||||
#include "nsDocShellTreeOwner.h"
|
||||
#include "nsIBadCertListener2.h" // JIHAD: route cert errors to the embedder chrome
|
||||
#include "nsWebBrowser.h"
|
||||
|
||||
// Helper Classes
|
||||
@@ -143,6 +144,15 @@ nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aIID.Equals(NS_GET_IID(nsIBadCertListener2))) {
|
||||
// JIHAD: the embedder's chrome is what implements this — see the matching comment in
|
||||
// nsDocShell::GetInterface. Same forwarding shape as nsIWebBrowserChromeFocus below.
|
||||
if (mWebBrowserChromeWeak != nullptr) {
|
||||
return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
|
||||
}
|
||||
return mOwnerWin ? mOwnerWin->QueryInterface(aIID, aSink) : NS_NOINTERFACE;
|
||||
}
|
||||
|
||||
if (aIID.Equals(NS_GET_IID(nsIWebBrowserChromeFocus))) {
|
||||
if (mWebBrowserChromeWeak != nullptr) {
|
||||
return mWebBrowserChromeWeak->QueryReferent(aIID, aSink);
|
||||
|
||||
@@ -390,6 +390,14 @@ nsWebBrowserFind::SetSelectionAndScroll(nsPIDOMWindowOuter* aWindow,
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
|
||||
// JIHAD: frame->GetSelectionController can leave selCon NULL, and this dereferenced it
|
||||
// unconditionally — a null deref that took the whole process down. It is reachable in an
|
||||
// offscreen embedding with no chrome: this is the ONLY thing that made find-in-page
|
||||
// unusable there (faultaddr=0x0, measured repeatedly). Guarding costs nothing and the
|
||||
// rest of the function already tolerates a null `selection`.
|
||||
if (!selCon) {
|
||||
return;
|
||||
}
|
||||
selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(selection));
|
||||
@@ -707,7 +715,24 @@ nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow, bool aWrapping,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
|
||||
// JIHAD: this same-origin check is why find-in-page took the whole process down in this
|
||||
// embedding, and the engine says why itself. nsContentUtils::SubjectPrincipal() begins:
|
||||
// JSContext* cx = GetCurrentJSContext();
|
||||
// if (!cx) {
|
||||
// MOZ_CRASH("Accessing the Subject Principal without an AutoJSAPI on the stack is forbidden");
|
||||
// }
|
||||
// An EMBEDDER calls nsIWebBrowserFind::FindNext from C++ with no script on the stack, so
|
||||
// there is no JSContext and MOZ_CRASH fires — a deliberate abort that presents as SIGSEGV at
|
||||
// address 0, which is precisely the fault this port recorded for two weeks and attributed to
|
||||
// "a frame-selection controller the offscreen browser doesn't set up". That was wrong: the
|
||||
// selection machinery is fine and the crash happens BEFORE any of it, on the security check.
|
||||
//
|
||||
// The check is meaningful only when there IS a caller to check. With no JS on the stack the
|
||||
// caller is native embedding code, which is already fully trusted — it is inside libxul's own
|
||||
// process, holding the nsIWebBrowserFind for that very docShell. So: consult the subject
|
||||
// principal only when a JSContext exists, and otherwise proceed.
|
||||
if (nsContentUtils::GetCurrentJSContext() &&
|
||||
!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
|
||||
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
@@ -809,8 +834,11 @@ nsWebBrowserFind::GetFrameSelection(nsPIDOMWindowOuter* aWindow)
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
if (frame) {
|
||||
frame->GetSelectionController(presContext, getter_AddRefs(selCon));
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(sel));
|
||||
// JIHAD: same unguarded deref as above — GetSelectionController can yield null.
|
||||
if (selCon) {
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(sel));
|
||||
}
|
||||
if (sel) {
|
||||
int32_t count = -1;
|
||||
sel->GetRangeCount(&count);
|
||||
@@ -820,7 +848,12 @@ nsWebBrowserFind::GetFrameSelection(nsPIDOMWindowOuter* aWindow)
|
||||
}
|
||||
}
|
||||
|
||||
// JIHAD: and the fallback QI is not guaranteed either — if the presShell does not answer
|
||||
// nsISelectionController we must return nothing rather than crash.
|
||||
selCon = do_QueryInterface(presShell);
|
||||
if (!selCon) {
|
||||
return nullptr;
|
||||
}
|
||||
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(sel));
|
||||
return sel.forget();
|
||||
|
||||
@@ -12,5 +12,6 @@ if CONFIG['NS_PRINTING']:
|
||||
DIRS += ['win']
|
||||
elif toolkit == 'cocoa':
|
||||
DIRS += ['mac']
|
||||
elif CONFIG['MOZ_PDF_PRINTING']:
|
||||
elif CONFIG['MOZ_PDF_PRINTING'] or toolkit == 'headless':
|
||||
# Jihad headless: reuse the gtk-free PDF printing prompt service.
|
||||
DIRS += ['unixshared']
|
||||
+1
-1
@@ -1374,7 +1374,7 @@ public:
|
||||
static already_AddRefed<ScaledFont>
|
||||
CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
|
||||
static already_AddRefed<ScaledFont>
|
||||
CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@
|
||||
#include "NativeFontResourceMac.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
|
||||
#include "ScaledFontFontconfig.h"
|
||||
#endif
|
||||
|
||||
@@ -584,7 +584,7 @@ Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, c
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
|
||||
already_AddRefed<ScaledFont>
|
||||
Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
|
||||
{
|
||||
|
||||
+3
-3
@@ -91,7 +91,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
|
||||
'JobScheduler_posix.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
|
||||
SOURCES += [
|
||||
'ScaledFontFontconfig.cpp',
|
||||
]
|
||||
@@ -209,12 +209,12 @@ FINAL_LIBRARY = 'xul'
|
||||
for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_CAIRO'):
|
||||
DEFINES[var] = True
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
|
||||
DEFINES['MOZ_ENABLE_FREETYPE'] = True
|
||||
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
|
||||
CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
|
||||
|
||||
LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
|
||||
|
||||
@@ -805,6 +805,10 @@ cairo_cff_font_read_cid_fontdict (cairo_cff_font_t *font, unsigned char *ptr)
|
||||
}
|
||||
operand = decode_integer (operand, &size);
|
||||
decode_integer (operand, &offset);
|
||||
if (unlikely (offset < 0 || (unsigned long)offset > font->data_length)) {
|
||||
status = CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
goto fail;
|
||||
}
|
||||
status = cff_dict_init (&font->fd_private_dict[i]);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
@@ -866,6 +870,8 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
|
||||
operand = cff_dict_get_operands (font->top_dict, CHARSTRINGS_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cff_index_read (&font->charstrings_index, &p, font->data_end);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
@@ -874,49 +880,58 @@ cairo_cff_font_read_top_dict (cairo_cff_font_t *font)
|
||||
if (font->is_cid) {
|
||||
operand = cff_dict_get_operands (font->top_dict, FDSELECT_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
status = cairo_cff_font_read_fdselect (font, font->data + offset);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_fdselect (font, p);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
|
||||
operand = cff_dict_get_operands (font->top_dict, FDARRAY_OP, &size);
|
||||
decode_integer (operand, &offset);
|
||||
status = cairo_cff_font_read_cid_fontdict (font, font->data + offset);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_cid_fontdict (font, p);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
} else {
|
||||
operand = cff_dict_get_operands (font->top_dict, PRIVATE_OP, &size);
|
||||
operand = decode_integer (operand, &size);
|
||||
decode_integer (operand, &offset);
|
||||
p = font->data + offset;
|
||||
if (unlikely (p < font->data || p > font->data_end))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
status = cairo_cff_font_read_private_dict (font,
|
||||
font->private_dict,
|
||||
&font->local_sub_index,
|
||||
font->data + offset,
|
||||
size);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
&font->local_sub_index,
|
||||
p,
|
||||
size);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Use maximum sized encoding to reserve space for later modification. */
|
||||
end_buf = encode_integer_max (buf, 0);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
goto fail;
|
||||
goto fail;
|
||||
|
||||
cff_dict_remove (font->top_dict, ENCODING_OP);
|
||||
cff_dict_remove (font->top_dict, PRIVATE_OP);
|
||||
@@ -985,26 +1000,26 @@ cairo_cff_font_set_ros_strings (cairo_cff_font_t *font)
|
||||
(unsigned char *)registry,
|
||||
strlen(registry));
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
sid2 = NUM_STD_STRINGS + _cairo_array_num_elements (&font->strings_subset_index);
|
||||
status = cff_index_append_copy (&font->strings_subset_index,
|
||||
(unsigned char *)ordering,
|
||||
strlen(ordering));
|
||||
strlen(ordering));
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
p = encode_integer (buf, sid1);
|
||||
p = encode_integer (p, sid2);
|
||||
p = encode_integer (p, 0);
|
||||
status = cff_dict_set_operands (font->top_dict, ROS_OP, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
p = encode_integer (buf, font->scaled_font_subset->num_glyphs);
|
||||
status = cff_dict_set_operands (font->top_dict, CIDCOUNT_OP, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1038,7 +1053,7 @@ cairo_cff_font_subset_dict_string(cairo_cff_font_t *font,
|
||||
p = encode_integer (buf, sid);
|
||||
status = cff_dict_set_operands (dict, operator, buf, p - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1101,19 +1116,19 @@ cairo_cff_font_subset_fontdict (cairo_cff_font_t *font)
|
||||
font->fdselect_subset = calloc (font->scaled_font_subset->num_glyphs,
|
||||
sizeof (int));
|
||||
if (unlikely (font->fdselect_subset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->fd_subset_map = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (font->fd_subset_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->private_dict_offset = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (font->private_dict_offset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
reverse_map = calloc (font->num_fontdicts, sizeof (int));
|
||||
if (unlikely (reverse_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
for (i = 0; i < font->num_fontdicts; i++)
|
||||
reverse_map[i] = -1;
|
||||
@@ -1143,22 +1158,22 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
|
||||
font->num_fontdicts = 1;
|
||||
font->fd_dict = malloc (sizeof (cairo_hash_table_t *));
|
||||
if (unlikely (font->fd_dict == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
if (cff_dict_init (&font->fd_dict[0])) {
|
||||
free (font->fd_dict);
|
||||
font->fd_dict = NULL;
|
||||
font->num_fontdicts = 0;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
free (font->fd_dict);
|
||||
font->fd_dict = NULL;
|
||||
font->num_fontdicts = 0;
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
font->fd_subset_map = malloc (sizeof (int));
|
||||
if (unlikely (font->fd_subset_map == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->private_dict_offset = malloc (sizeof (int));
|
||||
if (unlikely (font->private_dict_offset == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->fd_subset_map[0] = 0;
|
||||
font->num_subset_fontdicts = 1;
|
||||
@@ -1169,7 +1184,7 @@ cairo_cff_font_create_cid_fontdict (cairo_cff_font_t *font)
|
||||
end_buf = encode_integer_max (end_buf, 0);
|
||||
status = cff_dict_set_operands (font->fd_dict[0], PRIVATE_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -1208,7 +1223,7 @@ cairo_cff_font_subset_font (cairo_cff_font_t *font)
|
||||
|
||||
status = cairo_cff_font_set_ros_strings (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_subset_charstrings (font);
|
||||
if (unlikely (status))
|
||||
@@ -1219,7 +1234,7 @@ cairo_cff_font_subset_font (cairo_cff_font_t *font)
|
||||
else
|
||||
status = cairo_cff_font_create_cid_fontdict (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_subset_strings (font);
|
||||
if (unlikely (status))
|
||||
@@ -1271,7 +1286,7 @@ cairo_cff_font_write_name (cairo_cff_font_t *font)
|
||||
(unsigned char *) font->subset_font_name,
|
||||
strlen(font->subset_font_name));
|
||||
if (unlikely (status))
|
||||
goto FAIL;
|
||||
goto FAIL;
|
||||
|
||||
status = cff_index_write (&index, &font->output);
|
||||
if (unlikely (status))
|
||||
@@ -1464,9 +1479,9 @@ cairo_cff_font_write_cid_fontdict (cairo_cff_font_t *font)
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
|
||||
offset_array_ptr = (uint32_t *) _cairo_array_index (&font->output, offset_array);
|
||||
*offset_array_ptr = cpu_to_be32(_cairo_array_num_elements (&font->output) - offset_base);
|
||||
offset_array += sizeof(uint32_t);
|
||||
offset_array += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@@ -1566,15 +1581,15 @@ cairo_cff_font_write_cid_private_dict_and_local_sub (cairo_cff_font_t *font)
|
||||
0,
|
||||
font->fd_dict[0],
|
||||
font->private_dict);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_write_local_sub (font,
|
||||
0,
|
||||
font->private_dict,
|
||||
&font->local_sub_index);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
}
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
@@ -1698,7 +1713,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
backend = scaled_font_subset->scaled_font->backend;
|
||||
if (!backend->load_truetype_table)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
data_length = 0;
|
||||
status = backend->load_truetype_table( scaled_font_subset->scaled_font,
|
||||
@@ -1736,12 +1751,12 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
_cairo_array_init (&font->output, sizeof (char));
|
||||
status = _cairo_array_grow_by (&font->output, 4096);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
|
||||
font->subset_font_name = strdup (subset_name);
|
||||
if (unlikely (font->subset_font_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
}
|
||||
font->x_min = (int16_t) be16_to_cpu (head.x_min);
|
||||
font->y_min = (int16_t) be16_to_cpu (head.y_min);
|
||||
@@ -1755,18 +1770,18 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
font->font_name = NULL;
|
||||
status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
|
||||
&font->ps_name,
|
||||
&font->font_name);
|
||||
&font->ps_name,
|
||||
&font->font_name);
|
||||
if (_cairo_status_is_error (status))
|
||||
goto fail3;
|
||||
goto fail3;
|
||||
|
||||
/* If the PS name is not found, create a CairoFont-x-y name. */
|
||||
if (font->ps_name == NULL) {
|
||||
font->ps_name = malloc (30);
|
||||
if (unlikely (font->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(font->ps_name, 30, "CairoFont-%u-%u",
|
||||
scaled_font_subset->font_id,
|
||||
@@ -1781,7 +1796,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
status = cairo_cff_font_create_set_widths (font);
|
||||
if (unlikely (status))
|
||||
goto fail5;
|
||||
goto fail5;
|
||||
|
||||
font->data_length = data_length;
|
||||
font->data = malloc (data_length);
|
||||
@@ -1799,11 +1814,11 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t *scaled_font_subset,
|
||||
|
||||
status = cff_dict_init (&font->top_dict);
|
||||
if (unlikely (status))
|
||||
goto fail6;
|
||||
goto fail6;
|
||||
|
||||
status = cff_dict_init (&font->private_dict);
|
||||
if (unlikely (status))
|
||||
goto fail7;
|
||||
goto fail7;
|
||||
|
||||
cff_index_init (&font->strings_index);
|
||||
cff_index_init (&font->charstrings_index);
|
||||
@@ -1831,7 +1846,7 @@ fail5:
|
||||
free (font->widths);
|
||||
fail4:
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
fail3:
|
||||
free (font->subset_font_name);
|
||||
fail2:
|
||||
@@ -1848,7 +1863,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
|
||||
|
||||
free (font->widths);
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
free (font->ps_name);
|
||||
free (font->subset_font_name);
|
||||
_cairo_array_fini (&font->output);
|
||||
@@ -1902,7 +1917,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
|
||||
|
||||
cairo_status_t
|
||||
_cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
const char *subset_name,
|
||||
const char *subset_name,
|
||||
cairo_scaled_font_subset_t *font_subset)
|
||||
{
|
||||
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
||||
@@ -1913,32 +1928,32 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
status = _cairo_cff_font_create (font_subset, &font, subset_name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_generate (font, &data, &length);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
cff_subset->ps_name = strdup (font->ps_name);
|
||||
if (unlikely (cff_subset->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (font->font_name) {
|
||||
cff_subset->font_name = strdup (font->font_name);
|
||||
if (cff_subset->font_name == NULL) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
cff_subset->font_name = strdup (font->font_name);
|
||||
if (cff_subset->font_name == NULL) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
} else {
|
||||
cff_subset->font_name = NULL;
|
||||
cff_subset->font_name = NULL;
|
||||
}
|
||||
|
||||
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (cff_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
cff_subset->widths[i] = (double)font->widths[i]/font->units_per_em;
|
||||
@@ -1952,8 +1967,8 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
cff_subset->data = malloc (length);
|
||||
if (unlikely (cff_subset->data == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
memcpy (cff_subset->data, data, length);
|
||||
@@ -1967,7 +1982,7 @@ _cairo_cff_subset_init (cairo_cff_subset_t *cff_subset,
|
||||
free (cff_subset->widths);
|
||||
fail3:
|
||||
if (cff_subset->font_name)
|
||||
free (cff_subset->font_name);
|
||||
free (cff_subset->font_name);
|
||||
fail2:
|
||||
free (cff_subset->ps_name);
|
||||
fail1:
|
||||
@@ -1981,7 +1996,7 @@ _cairo_cff_subset_fini (cairo_cff_subset_t *subset)
|
||||
{
|
||||
free (subset->ps_name);
|
||||
if (subset->font_name)
|
||||
free (subset->font_name);
|
||||
free (subset->font_name);
|
||||
free (subset->widths);
|
||||
free (subset->data);
|
||||
}
|
||||
@@ -1996,7 +2011,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
|
||||
font = calloc (1, sizeof (cairo_cff_font_t));
|
||||
if (unlikely (font == NULL))
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
font->backend = NULL;
|
||||
font->scaled_font_subset = scaled_font_subset;
|
||||
@@ -2004,18 +2019,18 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
_cairo_array_init (&font->output, sizeof (char));
|
||||
status = _cairo_array_grow_by (&font->output, 4096);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
font->subset_font_name = strdup (subset_name);
|
||||
if (unlikely (font->subset_font_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
font->ps_name = strdup (subset_name);
|
||||
if (unlikely (font->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
}
|
||||
font->font_name = NULL;
|
||||
|
||||
@@ -2038,11 +2053,11 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t *scaled_font_subset
|
||||
|
||||
status = cff_dict_init (&font->top_dict);
|
||||
if (unlikely (status))
|
||||
goto fail4;
|
||||
goto fail4;
|
||||
|
||||
status = cff_dict_init (&font->private_dict);
|
||||
if (unlikely (status))
|
||||
goto fail5;
|
||||
goto fail5;
|
||||
|
||||
cff_index_init (&font->strings_index);
|
||||
cff_index_init (&font->charstrings_index);
|
||||
@@ -2068,7 +2083,7 @@ fail4:
|
||||
free (font->widths);
|
||||
fail3:
|
||||
if (font->font_name)
|
||||
free (font->font_name);
|
||||
free (font->font_name);
|
||||
free (font->ps_name);
|
||||
fail2:
|
||||
free (font->subset_font_name);
|
||||
@@ -2105,39 +2120,39 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
|
||||
end_buf = encode_integer (end_buf, type2_subset->x_max);
|
||||
end_buf = encode_integer (end_buf, type2_subset->y_max);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FONTBBOX_OP, buf, end_buf - buf);
|
||||
FONTBBOX_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
end_buf = encode_integer_max (buf, 0);
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
CHARSTRINGS_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
FDSELECT_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
FDARRAY_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cff_dict_set_operands (font->top_dict,
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
CHARSET_OP, buf, end_buf - buf);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = cairo_cff_font_set_ros_strings (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
/* Create CID FD dictionary */
|
||||
status = cairo_cff_font_create_cid_fontdict (font);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
/* Create charstrings */
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++) {
|
||||
@@ -2163,7 +2178,7 @@ cairo_cff_font_fallback_generate (cairo_cff_font_t *font,
|
||||
|
||||
cairo_status_t
|
||||
_cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
const char *subset_name,
|
||||
const char *subset_name,
|
||||
cairo_scaled_font_subset_t *font_subset)
|
||||
{
|
||||
cairo_cff_font_t *font = NULL; /* squelch bogus compiler warning */
|
||||
@@ -2175,27 +2190,27 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
status = _cairo_cff_font_fallback_create (font_subset, &font, subset_name);
|
||||
if (unlikely (status))
|
||||
return status;
|
||||
return status;
|
||||
|
||||
status = _cairo_type2_charstrings_init (&type2_subset, font_subset);
|
||||
if (unlikely (status))
|
||||
goto fail1;
|
||||
goto fail1;
|
||||
|
||||
status = cairo_cff_font_fallback_generate (font, &type2_subset, &data, &length);
|
||||
if (unlikely (status))
|
||||
goto fail2;
|
||||
goto fail2;
|
||||
|
||||
cff_subset->font_name = NULL;
|
||||
cff_subset->ps_name = strdup (font->ps_name);
|
||||
if (unlikely (cff_subset->ps_name == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
cff_subset->widths = calloc (sizeof (double), font->scaled_font_subset->num_glyphs);
|
||||
if (unlikely (cff_subset->widths == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
for (i = 0; i < font->scaled_font_subset->num_glyphs; i++)
|
||||
@@ -2210,8 +2225,8 @@ _cairo_cff_fallback_init (cairo_cff_subset_t *cff_subset,
|
||||
|
||||
cff_subset->data = malloc (length);
|
||||
if (unlikely (cff_subset->data == NULL)) {
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
goto fail4;
|
||||
}
|
||||
|
||||
memcpy (cff_subset->data, data, length);
|
||||
|
||||
@@ -87,12 +87,19 @@ public:
|
||||
bool
|
||||
BasicPlanarYCbCrImage::CopyData(const Data& aData)
|
||||
{
|
||||
RecyclingPlanarYCbCrImage::CopyData(aData);
|
||||
|
||||
if (mDelayedConversion) {
|
||||
if (!RecyclingPlanarYCbCrImage::CopyData(aData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The YCbCr copy above succeeded; delaying the RGB conversion is not a
|
||||
// failure. GetAsSourceSurface() converts on demand when mDecodedBuffer is
|
||||
// null. Returning false here made VideoData::CreateAndCopyData report OOM
|
||||
// (MEDIA_ERR_DECODE) for any frame decoded while BasicImageLayer::Paint had
|
||||
// swapped this factory into the video's ImageContainer.
|
||||
if (mDelayedConversion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do some sanity checks to prevent integer overflow
|
||||
if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION ||
|
||||
aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) {
|
||||
|
||||
+3
-2
@@ -529,7 +529,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:
|
||||
SOURCES += [
|
||||
'skia/src/ports/SkFontHost_mac.cpp',
|
||||
]
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'headless':
|
||||
UNIFIED_SOURCES += [
|
||||
'skia/src/ports/SkDebug_stdio.cpp',
|
||||
'skia/src/ports/SkOSFile_posix.cpp',
|
||||
@@ -665,6 +665,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
|
||||
'cocoa',
|
||||
'gtk2',
|
||||
'gtk3',
|
||||
'headless',
|
||||
'uikit',
|
||||
}:
|
||||
DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
|
||||
@@ -738,7 +739,7 @@ if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
|
||||
'-Wno-unused-private-field',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include "gfxPlatformGtk.h"
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_HEADLESS
|
||||
#include "gfxPlatformHeadless.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include "mozilla/X11Util.h"
|
||||
@@ -1764,7 +1767,11 @@ gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
|
||||
|
||||
// -- select the fonts to be used for the generic
|
||||
prefFonts = new PrefFontList; // can be empty but in practice won't happen
|
||||
#ifdef MOZ_WIDGET_HEADLESS
|
||||
uint32_t limit = gfxPlatformHeadless::GetPlatform()->MaxGenericSubstitions();
|
||||
#else
|
||||
uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
|
||||
#endif
|
||||
bool foundFontWithLang = false;
|
||||
for (int i = 0; i < faces->nfont; i++) {
|
||||
FcPattern* font = faces->fonts[i];
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#elif defined(XP_MACOSX)
|
||||
#include "gfxPlatformMac.h"
|
||||
#include "gfxQuartzSurface.h"
|
||||
#elif defined(MOZ_WIDGET_HEADLESS)
|
||||
#include "gfxPlatformHeadless.h"
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
#include "gfxPlatformGtk.h"
|
||||
#elif defined(ANDROID)
|
||||
@@ -572,6 +574,8 @@ gfxPlatform::Init()
|
||||
gPlatform = new gfxWindowsPlatform;
|
||||
#elif defined(XP_MACOSX)
|
||||
gPlatform = new gfxPlatformMac;
|
||||
#elif defined(MOZ_WIDGET_HEADLESS)
|
||||
gPlatform = new gfxPlatformHeadless;
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
gPlatform = new gfxPlatformGtk;
|
||||
#elif defined(ANDROID)
|
||||
@@ -2166,6 +2170,13 @@ gfxPlatform::UsesOffMainThreadCompositing()
|
||||
result |= gfxPrefs::LayersAccelerationForceEnabledDoNotUseDirectly();
|
||||
|
||||
#endif
|
||||
// Jihad Browser: the headless BrowserServer embedder has no compositor
|
||||
// process; allow forcing off-main-thread compositing off (use the in-process
|
||||
// BasicLayerManager) via env, since the gfxPrefs "Once" snapshot can miss a
|
||||
// grepref set during XRE_InitEmbedding2 init ordering.
|
||||
if (getenv("JIHAD_DISABLE_OMTC")) {
|
||||
result = false;
|
||||
}
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,17 @@ static cairo_user_data_key_t cairo_gdk_drawable_key;
|
||||
|
||||
gfxPlatformGtk::gfxPlatformGtk()
|
||||
{
|
||||
gtk_init(nullptr, nullptr);
|
||||
// Jihad: in JIHAD_OFFSCREEN (headless daemon) mode there is no X server on the
|
||||
// device. gtk_init() calls exit() if it cannot open a display; gtk_init_check()
|
||||
// returns false instead and leaves GTK usable for the non-X paths (fontconfig/
|
||||
// freetype font list, image/CPU surfaces via the BasicLayerManager). The X code
|
||||
// below is already guarded by GDK_IS_X11_DISPLAY(gdk_display_get_default()),
|
||||
// which is false with no display, so it is skipped cleanly.
|
||||
if (getenv("JIHAD_OFFSCREEN")) {
|
||||
gtk_init_check(nullptr, nullptr);
|
||||
} else {
|
||||
gtk_init(nullptr, nullptr);
|
||||
}
|
||||
|
||||
mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
|
||||
|
||||
@@ -325,8 +335,10 @@ gfxPlatformGtk::GetDPI()
|
||||
if (!sDPI) {
|
||||
// Make sure init is run so we have a resolution
|
||||
GdkScreen *screen = gdk_screen_get_default();
|
||||
gtk_settings_get_for_screen(screen);
|
||||
sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
|
||||
if (screen) { // null when headless (no X display)
|
||||
gtk_settings_get_for_screen(screen);
|
||||
sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
|
||||
}
|
||||
if (sDPI <= 0) {
|
||||
// Fall back to something sane
|
||||
sDPI = 96;
|
||||
|
||||
@@ -0,0 +1,834 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#define PANGO_ENABLE_BACKEND
|
||||
#define PANGO_ENABLE_ENGINE
|
||||
|
||||
#include "gfxPlatformHeadless.h"
|
||||
#include "prenv.h"
|
||||
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsUnicodeProperties.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxFcPlatformFontList.h"
|
||||
#include "gfxConfig.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxUserFontSet.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "gfxFT2FontBase.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "gfxTextRun.h"
|
||||
#include "VsyncSource.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "base/task.h"
|
||||
#include "base/thread.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
#include "cairo.h"
|
||||
|
||||
|
||||
#include "gfxImageSurface.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef MOZ_X11
|
||||
|
||||
#include "gfxXlibSurface.h"
|
||||
#include "cairo-xlib.h"
|
||||
#include "mozilla/X11Util.h"
|
||||
|
||||
#ifdef GL_PROVIDER_GLX
|
||||
#include "GLContextProvider.h"
|
||||
#include "GLContextGLX.h"
|
||||
#include "GLXLibrary.h"
|
||||
#endif
|
||||
|
||||
/* Undefine the Status from Xlib since it will conflict with system headers on OSX */
|
||||
#if defined(__APPLE__) && defined(Status)
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
#endif /* MOZ_X11 */
|
||||
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
#include "nsMathUtils.h"
|
||||
|
||||
#define GDK_PIXMAP_SIZE_MAX 32767
|
||||
|
||||
#define GFX_PREF_MAX_GENERIC_SUBSTITUTIONS "gfx.font_rendering.fontconfig.max_generic_substitutions"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::unicode;
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
static cairo_user_data_key_t cairo_gdk_drawable_key;
|
||||
#endif
|
||||
|
||||
gfxPlatformHeadless::gfxPlatformHeadless()
|
||||
{
|
||||
// Jihad: in JIHAD_OFFSCREEN (headless daemon) mode there is no X server on the
|
||||
// device. gtk_init() calls exit() if it cannot open a display; gtk_init_check()
|
||||
// returns false instead and leaves GTK usable for the non-X paths (fontconfig/
|
||||
// freetype font list, image/CPU surfaces via the BasicLayerManager). The X code
|
||||
// below is already guarded by GDK_IS_X11_DISPLAY(gdk_display_get_default()),
|
||||
// which is false with no display, so it is skipped cleanly.
|
||||
/* headless: no gtk_init */
|
||||
|
||||
mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
if (XRE_IsParentProcess()) {
|
||||
if (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
|
||||
mozilla::Preferences::GetBool("gfx.xrender.enabled"))
|
||||
{
|
||||
gfxVars::SetUseXRender(true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
|
||||
uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
|
||||
#ifdef USE_SKIA
|
||||
canvasMask |= BackendTypeBit(BackendType::SKIA);
|
||||
contentMask |= BackendTypeBit(BackendType::SKIA);
|
||||
#endif
|
||||
InitBackendPrefs(canvasMask, BackendType::CAIRO,
|
||||
contentMask, BackendType::CAIRO);
|
||||
|
||||
#ifdef MOZ_X11
|
||||
if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) {
|
||||
mCompositorDisplay = XOpenDisplay(nullptr);
|
||||
MOZ_ASSERT(mCompositorDisplay, "Failed to create compositor display!");
|
||||
} else {
|
||||
mCompositorDisplay = nullptr;
|
||||
}
|
||||
#endif // MOZ_X11
|
||||
}
|
||||
|
||||
gfxPlatformHeadless::~gfxPlatformHeadless()
|
||||
{
|
||||
#ifdef MOZ_X11
|
||||
if (mCompositorDisplay) {
|
||||
XCloseDisplay(mCompositorDisplay);
|
||||
}
|
||||
#endif // MOZ_X11
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatformHeadless::FlushContentDrawing()
|
||||
{
|
||||
#ifdef MOZ_X11
|
||||
if (gfxVars::UseXRender()) {
|
||||
XFlush(DefaultXDisplay());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
already_AddRefed<gfxASurface>
|
||||
gfxPlatformHeadless::CreateOffscreenSurface(const IntSize& aSize,
|
||||
gfxImageFormat aFormat)
|
||||
{
|
||||
if (!Factory::AllowedSurfaceSize(aSize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<gfxASurface> newSurface;
|
||||
bool needsClear = true;
|
||||
#ifdef MOZ_X11
|
||||
// XXX we really need a different interface here, something that passes
|
||||
// in more context, including the display and/or target surface type that
|
||||
// we should try to match
|
||||
GdkScreen *gdkScreen = gdk_screen_get_default();
|
||||
if (gdkScreen) {
|
||||
// When forcing PaintedLayers to use image surfaces for content,
|
||||
// force creation of gfxImageSurface surfaces.
|
||||
if (gfxVars::UseXRender()) {
|
||||
Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen);
|
||||
XRenderPictFormat* xrenderFormat =
|
||||
gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen),
|
||||
aFormat);
|
||||
|
||||
if (xrenderFormat) {
|
||||
newSurface = gfxXlibSurface::Create(screen, xrenderFormat,
|
||||
aSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!newSurface) {
|
||||
// We couldn't create a native surface for whatever reason;
|
||||
// e.g., no display, no RENDER, bad size, etc.
|
||||
// Fall back to image surface for the data.
|
||||
newSurface = new gfxImageSurface(aSize, aFormat);
|
||||
|
||||
// The gfxImageSurface ctor zeroes this for us, no need to
|
||||
// waste time clearing again
|
||||
needsClear = false;
|
||||
}
|
||||
|
||||
if (newSurface->CairoStatus()) {
|
||||
newSurface = nullptr; // surface isn't valid for some reason
|
||||
}
|
||||
|
||||
if (newSurface && needsClear) {
|
||||
gfxUtils::ClearThebesSurface(newSurface);
|
||||
}
|
||||
|
||||
return newSurface.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxPlatformHeadless::GetFontList(nsIAtom *aLangGroup,
|
||||
const nsACString& aGenericFamily,
|
||||
nsTArray<nsString>& aListOfFonts)
|
||||
{
|
||||
gfxPlatformFontList::PlatformFontList()->GetFontList(aLangGroup,
|
||||
aGenericFamily,
|
||||
aListOfFonts);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxPlatformHeadless::UpdateFontList()
|
||||
{
|
||||
gfxPlatformFontList::PlatformFontList()->UpdateFontList();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// xxx - this is ubuntu centric, need to go through other distros and flesh
|
||||
// out a more general list
|
||||
static const char kFontDejaVuSans[] = "DejaVu Sans";
|
||||
static const char kFontDejaVuSerif[] = "DejaVu Serif";
|
||||
static const char kFontFreeSans[] = "FreeSans";
|
||||
static const char kFontFreeSerif[] = "FreeSerif";
|
||||
static const char kFontTakaoPGothic[] = "TakaoPGothic";
|
||||
static const char kFontTwemojiMozilla[] = "Twemoji Mozilla";
|
||||
static const char kFontDroidSansFallback[] = "Droid Sans Fallback";
|
||||
static const char kFontWenQuanYiMicroHei[] = "WenQuanYi Micro Hei";
|
||||
static const char kFontNanumGothic[] = "NanumGothic";
|
||||
|
||||
void
|
||||
gfxPlatformHeadless::GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
nsTArray<const char*>& aFontList)
|
||||
{
|
||||
EmojiPresentation emoji = GetEmojiPresentation(aCh);
|
||||
EmojiPresentation eNext = GetEmojiPresentation(aNextCh, true);
|
||||
if (aNextCh != kVariationSelector15 &&
|
||||
emoji != EmojiPresentation::TextOnly &&
|
||||
(emoji != EmojiPresentation::TextDefault ||
|
||||
eNext == EmojiPresentation::EmojiComponent)) {
|
||||
aFontList.AppendElement(kFontTwemojiMozilla);
|
||||
}
|
||||
|
||||
aFontList.AppendElement(kFontDejaVuSerif);
|
||||
aFontList.AppendElement(kFontFreeSerif);
|
||||
aFontList.AppendElement(kFontDejaVuSans);
|
||||
aFontList.AppendElement(kFontFreeSans);
|
||||
|
||||
// add fonts for CJK ranges
|
||||
// xxx - this isn't really correct, should use the same CJK font ordering
|
||||
// as the pref font code
|
||||
if (aCh >= 0x3000 &&
|
||||
((aCh < 0xe000) ||
|
||||
(aCh >= 0xf900 && aCh < 0xfff0) ||
|
||||
((aCh >> 16) == 2))) {
|
||||
aFontList.AppendElement(kFontTakaoPGothic);
|
||||
aFontList.AppendElement(kFontDroidSansFallback);
|
||||
aFontList.AppendElement(kFontWenQuanYiMicroHei);
|
||||
aFontList.AppendElement(kFontNanumGothic);
|
||||
}
|
||||
}
|
||||
|
||||
gfxPlatformFontList*
|
||||
gfxPlatformHeadless::CreatePlatformFontList()
|
||||
{
|
||||
gfxPlatformFontList* list = new gfxFcPlatformFontList();
|
||||
if (NS_SUCCEEDED(list->InitFontList())) {
|
||||
return list;
|
||||
}
|
||||
gfxPlatformFontList::Shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsresult
|
||||
gfxPlatformHeadless::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
|
||||
{
|
||||
gfxPlatformFontList::PlatformFontList()->
|
||||
GetStandardFamilyName(aFontName, aFamilyName);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
gfxFontGroup *
|
||||
gfxPlatformHeadless::CreateFontGroup(const FontFamilyList& aFontFamilyList,
|
||||
const gfxFontStyle* aStyle,
|
||||
gfxTextPerfMetrics* aTextPerf,
|
||||
gfxUserFontSet* aUserFontSet,
|
||||
gfxFloat aDevToCssSize)
|
||||
{
|
||||
return new gfxFontGroup(aFontFamilyList, aStyle, aTextPerf,
|
||||
aUserFontSet, aDevToCssSize);
|
||||
}
|
||||
|
||||
gfxFontEntry*
|
||||
gfxPlatformHeadless::LookupLocalFont(const nsAString& aFontName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle)
|
||||
{
|
||||
gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
|
||||
return pfl->LookupLocalFont(aFontName, aWeight, aStretch,
|
||||
aStyle);
|
||||
}
|
||||
|
||||
gfxFontEntry*
|
||||
gfxPlatformHeadless::MakePlatformFont(const nsAString& aFontName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle,
|
||||
const uint8_t* aFontData,
|
||||
uint32_t aLength)
|
||||
{
|
||||
gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
|
||||
return pfl->MakePlatformFont(aFontName, aWeight, aStretch,
|
||||
aStyle, aFontData, aLength);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatformHeadless::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags)
|
||||
{
|
||||
// check for strange format flags
|
||||
NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED),
|
||||
"strange font format hint set");
|
||||
|
||||
// accept supported formats
|
||||
// Pango doesn't apply features from AAT TrueType extensions.
|
||||
// Assume that if this is the only SFNT format specified,
|
||||
// then AAT extensions are required for complex script support.
|
||||
if (aFormatFlags & gfxUserFontSet::FLAG_FORMATS_COMMON) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// reject all other formats, known and unknown
|
||||
if (aFormatFlags != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// no format hint set, need to look at data
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t sDPI = 0;
|
||||
|
||||
int32_t
|
||||
gfxPlatformHeadless::GetDPI()
|
||||
{
|
||||
if (!sDPI) {
|
||||
// Make sure init is run so we have a resolution
|
||||
sDPI = 96;
|
||||
if (sDPI <= 0) {
|
||||
// Fall back to something sane
|
||||
sDPI = 96;
|
||||
}
|
||||
}
|
||||
return sDPI;
|
||||
}
|
||||
|
||||
double
|
||||
gfxPlatformHeadless::GetDPIScale()
|
||||
{
|
||||
// Integer scale factors work well with GTK window scaling, image scaling,
|
||||
// and pixel alignment, but there is a range where 1 is too small and 2 is
|
||||
// too big. An additional step of 1.5 is added because this is common
|
||||
// scale on WINNT and at this ratio the advantages of larger rendering
|
||||
// outweigh the disadvantages from scaling and pixel mis-alignment.
|
||||
int32_t dpi = GetDPI();
|
||||
if (dpi < 144) {
|
||||
return 1.0;
|
||||
} else if (dpi < 168) {
|
||||
return 1.5;
|
||||
} else {
|
||||
return round(dpi/96.0);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gfxPlatformHeadless::UseImageOffscreenSurfaces()
|
||||
{
|
||||
return GetDefaultContentBackend() != mozilla::gfx::BackendType::CAIRO ||
|
||||
gfxPrefs::UseImageOffscreenSurfaces();
|
||||
}
|
||||
|
||||
gfxImageFormat
|
||||
gfxPlatformHeadless::GetOffscreenFormat()
|
||||
{
|
||||
// Make sure there is a screen
|
||||
|
||||
|
||||
return SurfaceFormat::X8R8G8B8_UINT32;
|
||||
}
|
||||
|
||||
void gfxPlatformHeadless::FontsPrefsChanged(const char *aPref)
|
||||
{
|
||||
// only checking for generic substitions, pass other changes up
|
||||
if (strcmp(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, aPref)) {
|
||||
gfxPlatform::FontsPrefsChanged(aPref);
|
||||
return;
|
||||
}
|
||||
|
||||
mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
|
||||
gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
|
||||
pfl->ClearGenericMappings();
|
||||
FlushFontAndWordCaches();
|
||||
}
|
||||
|
||||
uint32_t gfxPlatformHeadless::MaxGenericSubstitions()
|
||||
{
|
||||
if (mMaxGenericSubstitutions == UNINITIALIZED_VALUE) {
|
||||
mMaxGenericSubstitutions =
|
||||
Preferences::GetInt(GFX_PREF_MAX_GENERIC_SUBSTITUTIONS, 3);
|
||||
if (mMaxGenericSubstitutions < 0) {
|
||||
mMaxGenericSubstitutions = 3;
|
||||
}
|
||||
}
|
||||
|
||||
return uint32_t(mMaxGenericSubstitutions);
|
||||
}
|
||||
|
||||
void
|
||||
gfxPlatformHeadless::GetPlatformCMSOutputProfile(void *&mem, size_t &size)
|
||||
{
|
||||
mem = nullptr;
|
||||
size = 0;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
GdkDisplay *display = gdk_display_get_default();
|
||||
if (!GDK_IS_X11_DISPLAY(display))
|
||||
return;
|
||||
|
||||
const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA";
|
||||
const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE";
|
||||
|
||||
Atom edidAtom, iccAtom;
|
||||
Display *dpy = GDK_DISPLAY_XDISPLAY(display);
|
||||
// In xpcshell tests, we never initialize X and hence don't have a Display.
|
||||
// In this case, there's no output colour management to be done, so we just
|
||||
// return with nullptr.
|
||||
if (!dpy)
|
||||
return;
|
||||
|
||||
Window root = gdk_x11_get_default_root_xwindow();
|
||||
|
||||
Atom retAtom;
|
||||
int retFormat;
|
||||
unsigned long retLength, retAfter;
|
||||
unsigned char *retProperty ;
|
||||
|
||||
iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE);
|
||||
if (iccAtom) {
|
||||
// read once to get size, once for the data
|
||||
if (Success == XGetWindowProperty(dpy, root, iccAtom,
|
||||
0, INT_MAX /* length */,
|
||||
False, AnyPropertyType,
|
||||
&retAtom, &retFormat, &retLength,
|
||||
&retAfter, &retProperty)) {
|
||||
|
||||
if (retLength > 0) {
|
||||
void *buffer = malloc(retLength);
|
||||
if (buffer) {
|
||||
memcpy(buffer, retProperty, retLength);
|
||||
mem = buffer;
|
||||
size = retLength;
|
||||
}
|
||||
}
|
||||
|
||||
XFree(retProperty);
|
||||
if (size > 0) {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s successfully\n",
|
||||
ICC_PROFILE_ATOM_NAME);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE);
|
||||
if (edidAtom) {
|
||||
if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32,
|
||||
False, AnyPropertyType,
|
||||
&retAtom, &retFormat, &retLength,
|
||||
&retAfter, &retProperty)) {
|
||||
double gamma;
|
||||
qcms_CIE_xyY whitePoint;
|
||||
qcms_CIE_xyYTRIPLE primaries;
|
||||
|
||||
if (retLength != 128) {
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "Short EDID data\n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Format documented in "VESA E-EDID Implementation Guide"
|
||||
|
||||
gamma = (100 + retProperty[0x17]) / 100.0;
|
||||
whitePoint.x = ((retProperty[0x21] << 2) |
|
||||
(retProperty[0x1a] >> 2 & 3)) / 1024.0;
|
||||
whitePoint.y = ((retProperty[0x22] << 2) |
|
||||
(retProperty[0x1a] >> 0 & 3)) / 1024.0;
|
||||
whitePoint.Y = 1.0;
|
||||
|
||||
primaries.red.x = ((retProperty[0x1b] << 2) |
|
||||
(retProperty[0x19] >> 6 & 3)) / 1024.0;
|
||||
primaries.red.y = ((retProperty[0x1c] << 2) |
|
||||
(retProperty[0x19] >> 4 & 3)) / 1024.0;
|
||||
primaries.red.Y = 1.0;
|
||||
|
||||
primaries.green.x = ((retProperty[0x1d] << 2) |
|
||||
(retProperty[0x19] >> 2 & 3)) / 1024.0;
|
||||
primaries.green.y = ((retProperty[0x1e] << 2) |
|
||||
(retProperty[0x19] >> 0 & 3)) / 1024.0;
|
||||
primaries.green.Y = 1.0;
|
||||
|
||||
primaries.blue.x = ((retProperty[0x1f] << 2) |
|
||||
(retProperty[0x1a] >> 6 & 3)) / 1024.0;
|
||||
primaries.blue.y = ((retProperty[0x20] << 2) |
|
||||
(retProperty[0x1a] >> 4 & 3)) / 1024.0;
|
||||
primaries.blue.Y = 1.0;
|
||||
|
||||
XFree(retProperty);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
fprintf(stderr, "EDID gamma: %f\n", gamma);
|
||||
fprintf(stderr, "EDID whitepoint: %f %f %f\n",
|
||||
whitePoint.x, whitePoint.y, whitePoint.Y);
|
||||
fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n",
|
||||
primaries.Red.x, primaries.Red.y, primaries.Red.Y,
|
||||
primaries.Green.x, primaries.Green.y, primaries.Green.Y,
|
||||
primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y);
|
||||
#endif
|
||||
|
||||
qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size);
|
||||
|
||||
#ifdef DEBUG_tor
|
||||
if (size > 0) {
|
||||
fprintf(stderr,
|
||||
"ICM profile read from %s successfully\n",
|
||||
EDID1_ATOM_NAME);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
void
|
||||
gfxPlatformHeadless::SetGdkDrawable(cairo_surface_t *target,
|
||||
GdkDrawable *drawable)
|
||||
{
|
||||
if (cairo_surface_status(target))
|
||||
return;
|
||||
|
||||
g_object_ref(drawable);
|
||||
|
||||
cairo_surface_set_user_data (target,
|
||||
&cairo_gdk_drawable_key,
|
||||
drawable,
|
||||
g_object_unref);
|
||||
}
|
||||
|
||||
GdkDrawable *
|
||||
gfxPlatformHeadless::GetGdkDrawable(cairo_surface_t *target)
|
||||
{
|
||||
if (cairo_surface_status(target))
|
||||
return nullptr;
|
||||
|
||||
GdkDrawable *result;
|
||||
|
||||
result = (GdkDrawable*) cairo_surface_get_user_data (target,
|
||||
&cairo_gdk_drawable_key);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB)
|
||||
return nullptr;
|
||||
|
||||
// try looking it up in gdk's table
|
||||
result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target));
|
||||
if (result) {
|
||||
SetGdkDrawable(target, result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef GL_PROVIDER_GLX
|
||||
|
||||
class GLXVsyncSource final : public VsyncSource
|
||||
{
|
||||
public:
|
||||
GLXVsyncSource()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mGlobalDisplay = new GLXDisplay();
|
||||
}
|
||||
|
||||
virtual ~GLXVsyncSource()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
virtual Display& GetGlobalDisplay() override
|
||||
{
|
||||
return *mGlobalDisplay;
|
||||
}
|
||||
|
||||
class GLXDisplay final : public VsyncSource::Display
|
||||
{
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLXDisplay)
|
||||
|
||||
public:
|
||||
GLXDisplay() : mGLContext(nullptr)
|
||||
, mXDisplay(nullptr)
|
||||
, mSetupLock("GLXVsyncSetupLock")
|
||||
, mVsyncThread("GLXVsyncThread")
|
||||
, mVsyncTask(nullptr)
|
||||
, mVsyncEnabledLock("GLXVsyncEnabledLock")
|
||||
, mVsyncEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
// Sets up the display's GL context on a worker thread.
|
||||
// Required as GLContexts may only be used by the creating thread.
|
||||
// Returns true if setup was a success.
|
||||
bool Setup()
|
||||
{
|
||||
MonitorAutoLock lock(mSetupLock);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mVsyncThread.Start())
|
||||
return false;
|
||||
|
||||
RefPtr<Runnable> vsyncSetup = NewRunnableMethod(this, &GLXDisplay::SetupGLContext);
|
||||
mVsyncThread.message_loop()->PostTask(vsyncSetup.forget());
|
||||
// Wait until the setup has completed.
|
||||
lock.Wait();
|
||||
return mGLContext != nullptr;
|
||||
}
|
||||
|
||||
// Called on the Vsync thread to setup the GL context.
|
||||
void SetupGLContext()
|
||||
{
|
||||
MonitorAutoLock lock(mSetupLock);
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(!mGLContext, "GLContext already setup!");
|
||||
|
||||
// Create video sync timer on a separate Display to prevent locking the
|
||||
// main thread X display.
|
||||
mXDisplay = XOpenDisplay(nullptr);
|
||||
if (!mXDisplay) {
|
||||
lock.NotifyAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// Most compositors wait for vsync events on the root window.
|
||||
Window root = DefaultRootWindow(mXDisplay);
|
||||
int screen = DefaultScreen(mXDisplay);
|
||||
|
||||
ScopedXFree<GLXFBConfig> cfgs;
|
||||
GLXFBConfig config;
|
||||
int visid;
|
||||
if (!gl::GLContextGLX::FindFBConfigForWindow(mXDisplay, screen, root,
|
||||
&cfgs, &config, &visid)) {
|
||||
lock.NotifyAll();
|
||||
return;
|
||||
}
|
||||
|
||||
mGLContext = gl::GLContextGLX::CreateGLContext(
|
||||
gl::CreateContextFlags::NONE,
|
||||
gl::SurfaceCaps::Any(),
|
||||
nullptr,
|
||||
false,
|
||||
mXDisplay,
|
||||
root,
|
||||
config);
|
||||
|
||||
if (!mGLContext) {
|
||||
lock.NotifyAll();
|
||||
return;
|
||||
}
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
|
||||
// Test that SGI_video_sync lets us get the counter.
|
||||
unsigned int syncCounter = 0;
|
||||
if (gl::sGLXLibrary.xGetVideoSync(&syncCounter) != 0) {
|
||||
mGLContext = nullptr;
|
||||
}
|
||||
|
||||
lock.NotifyAll();
|
||||
}
|
||||
|
||||
virtual void EnableVsync() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGLContext, "GLContext not setup!");
|
||||
|
||||
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||
if (mVsyncEnabled) {
|
||||
return;
|
||||
}
|
||||
mVsyncEnabled = true;
|
||||
|
||||
// If the task has not nulled itself out, it hasn't yet realized
|
||||
// that vsync was disabled earlier, so continue its execution.
|
||||
if (!mVsyncTask) {
|
||||
mVsyncTask = NewRunnableMethod(this, &GLXDisplay::RunVsync);
|
||||
RefPtr<Runnable> addrefedTask = mVsyncTask;
|
||||
mVsyncThread.message_loop()->PostTask(addrefedTask.forget());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DisableVsync() override
|
||||
{
|
||||
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||
mVsyncEnabled = false;
|
||||
}
|
||||
|
||||
virtual bool IsVsyncEnabled() override
|
||||
{
|
||||
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||
return mVsyncEnabled;
|
||||
}
|
||||
|
||||
virtual void Shutdown() override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DisableVsync();
|
||||
|
||||
// Cleanup thread-specific resources before shutting down.
|
||||
RefPtr<Runnable> shutdownTask = NewRunnableMethod(this, &GLXDisplay::Cleanup);
|
||||
mVsyncThread.message_loop()->PostTask(shutdownTask.forget());
|
||||
|
||||
// Stop, waiting for the cleanup task to finish execution.
|
||||
mVsyncThread.Stop();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~GLXDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
void RunVsync()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
|
||||
unsigned int syncCounter = 0;
|
||||
gl::sGLXLibrary.xGetVideoSync(&syncCounter);
|
||||
for (;;) {
|
||||
{
|
||||
MonitorAutoLock lock(mVsyncEnabledLock);
|
||||
if (!mVsyncEnabled) {
|
||||
mVsyncTask = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TimeStamp lastVsync = TimeStamp::Now();
|
||||
bool useSoftware = false;
|
||||
|
||||
// Wait until the video sync counter reaches the next value by waiting
|
||||
// until the parity of the counter value changes.
|
||||
unsigned int nextSync = syncCounter + 1;
|
||||
int status;
|
||||
if ((status = gl::sGLXLibrary.xWaitVideoSync(2, nextSync % 2, &syncCounter)) != 0) {
|
||||
gfxWarningOnce() << "glXWaitVideoSync returned " << status;
|
||||
useSoftware = true;
|
||||
}
|
||||
|
||||
if (syncCounter == (nextSync - 1)) {
|
||||
gfxWarningOnce() << "glXWaitVideoSync failed to increment the sync counter.";
|
||||
useSoftware = true;
|
||||
}
|
||||
|
||||
if (useSoftware) {
|
||||
double remaining = (1000.f / 60.f) -
|
||||
(TimeStamp::Now() - lastVsync).ToMilliseconds();
|
||||
if (remaining > 0) {
|
||||
PlatformThread::Sleep(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
lastVsync = TimeStamp::Now();
|
||||
NotifyVsync(lastVsync);
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
mGLContext = nullptr;
|
||||
XCloseDisplay(mXDisplay);
|
||||
}
|
||||
|
||||
// Owned by the vsync thread.
|
||||
RefPtr<gl::GLContextGLX> mGLContext;
|
||||
_XDisplay* mXDisplay;
|
||||
Monitor mSetupLock;
|
||||
base::Thread mVsyncThread;
|
||||
RefPtr<Runnable> mVsyncTask;
|
||||
Monitor mVsyncEnabledLock;
|
||||
bool mVsyncEnabled;
|
||||
};
|
||||
private:
|
||||
// We need a refcounted VsyncSource::Display to use chromium IPC runnables.
|
||||
RefPtr<GLXDisplay> mGlobalDisplay;
|
||||
};
|
||||
|
||||
already_AddRefed<gfx::VsyncSource>
|
||||
gfxPlatformHeadless::CreateHardwareVsyncSource()
|
||||
{
|
||||
// Only use GLX vsync when the OpenGL compositor is being used.
|
||||
// The extra cost of initializing a GLX context while blocking the main
|
||||
// thread is not worth it when using basic composition.
|
||||
if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
|
||||
if (gl::sGLXLibrary.SupportsVideoSync()) {
|
||||
RefPtr<VsyncSource> vsyncSource = new GLXVsyncSource();
|
||||
VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
|
||||
if (!static_cast<GLXVsyncSource::GLXDisplay&>(display).Setup()) {
|
||||
NS_WARNING("Failed to setup GLContext, falling back to software vsync.");
|
||||
return gfxPlatform::CreateHardwareVsyncSource();
|
||||
}
|
||||
return vsyncSource.forget();
|
||||
}
|
||||
NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync.");
|
||||
}
|
||||
return gfxPlatform::CreateHardwareVsyncSource();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Not GLX-specific: must be defined regardless of GL_PROVIDER_GLX so the
|
||||
// vtable slot resolves in a headless (no X/GLX) build.
|
||||
bool
|
||||
gfxPlatformHeadless::SupportsApzTouchInput() const
|
||||
{
|
||||
int value = gfxPrefs::TouchEventsEnabled();
|
||||
return value == 1 || value == 2;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef GFX_PLATFORM_GTK_H
|
||||
#define GFX_PLATFORM_GTK_H
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "nsAutoRef.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
extern "C" {
|
||||
typedef struct _GdkDrawable GdkDrawable;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
struct _XDisplay;
|
||||
typedef struct _XDisplay Display;
|
||||
#endif // MOZ_X11
|
||||
|
||||
class gfxPlatformHeadless : public gfxPlatform {
|
||||
public:
|
||||
gfxPlatformHeadless();
|
||||
virtual ~gfxPlatformHeadless();
|
||||
|
||||
static gfxPlatformHeadless *GetPlatform() {
|
||||
return (gfxPlatformHeadless*) gfxPlatform::GetPlatform();
|
||||
}
|
||||
|
||||
virtual already_AddRefed<gfxASurface>
|
||||
CreateOffscreenSurface(const IntSize& aSize,
|
||||
gfxImageFormat aFormat) override;
|
||||
|
||||
virtual nsresult GetFontList(nsIAtom *aLangGroup,
|
||||
const nsACString& aGenericFamily,
|
||||
nsTArray<nsString>& aListOfFonts) override;
|
||||
|
||||
virtual nsresult UpdateFontList() override;
|
||||
|
||||
virtual void
|
||||
GetCommonFallbackFonts(uint32_t aCh, uint32_t aNextCh,
|
||||
Script aRunScript,
|
||||
nsTArray<const char*>& aFontList) override;
|
||||
|
||||
virtual gfxPlatformFontList* CreatePlatformFontList() override;
|
||||
|
||||
virtual nsresult GetStandardFamilyName(const nsAString& aFontName,
|
||||
nsAString& aFamilyName) override;
|
||||
|
||||
gfxFontGroup*
|
||||
CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
|
||||
const gfxFontStyle *aStyle,
|
||||
gfxTextPerfMetrics* aTextPerf,
|
||||
gfxUserFontSet *aUserFontSet,
|
||||
gfxFloat aDevToCssSize) override;
|
||||
|
||||
/**
|
||||
* Look up a local platform font using the full font face name (needed to
|
||||
* support @font-face src local() )
|
||||
*/
|
||||
virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle) override;
|
||||
|
||||
/**
|
||||
* Activate a platform font (needed to support @font-face src url() )
|
||||
*
|
||||
*/
|
||||
virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
|
||||
uint16_t aWeight,
|
||||
int16_t aStretch,
|
||||
uint8_t aStyle,
|
||||
const uint8_t* aFontData,
|
||||
uint32_t aLength) override;
|
||||
|
||||
/**
|
||||
* Check whether format is supported on a platform or not (if unclear,
|
||||
* returns true).
|
||||
*/
|
||||
virtual bool IsFontFormatSupported(nsIURI *aFontURI,
|
||||
uint32_t aFormatFlags) override;
|
||||
|
||||
/**
|
||||
* Calls XFlush if xrender is enabled.
|
||||
*/
|
||||
virtual void FlushContentDrawing() override;
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
static void SetGdkDrawable(cairo_surface_t *target,
|
||||
GdkDrawable *drawable);
|
||||
static GdkDrawable *GetGdkDrawable(cairo_surface_t *target);
|
||||
#endif
|
||||
|
||||
static int32_t GetDPI();
|
||||
static double GetDPIScale();
|
||||
|
||||
#ifdef MOZ_X11
|
||||
virtual void GetAzureBackendInfo(mozilla::widget::InfoObject &aObj) override {
|
||||
gfxPlatform::GetAzureBackendInfo(aObj);
|
||||
aObj.DefineProperty("CairoUseXRender", mozilla::gfx::gfxVars::UseXRender());
|
||||
}
|
||||
#endif
|
||||
|
||||
bool UseImageOffscreenSurfaces();
|
||||
|
||||
virtual gfxImageFormat GetOffscreenFormat() override;
|
||||
|
||||
bool SupportsApzWheelInput() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SupportsApzTouchInput() const override;
|
||||
|
||||
void FontsPrefsChanged(const char *aPref) override;
|
||||
|
||||
// maximum number of fonts to substitute for a generic
|
||||
uint32_t MaxGenericSubstitions();
|
||||
|
||||
bool SupportsPluginDirectBitmapDrawing() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef GL_PROVIDER_GLX
|
||||
already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
Display* GetCompositorDisplay() {
|
||||
return mCompositorDisplay;
|
||||
}
|
||||
#endif // MOZ_X11
|
||||
|
||||
protected:
|
||||
int8_t mMaxGenericSubstitutions;
|
||||
|
||||
private:
|
||||
virtual void GetPlatformCMSOutputProfile(void *&mem,
|
||||
size_t &size) override;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
Display* mCompositorDisplay;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* GFX_PLATFORM_GTK_H */
|
||||
@@ -74,6 +74,22 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
'gfxQuartzSurface.cpp',
|
||||
'PrintTargetCG.cpp',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'headless':
|
||||
# Jihad headless (no X/GTK): fontconfig/freetype fonts + a memory gfxPlatform.
|
||||
EXPORTS += [
|
||||
'gfxFT2FontBase.h',
|
||||
'gfxPlatformHeadless.h',
|
||||
]
|
||||
EXPORTS.mozilla.gfx += [
|
||||
'PrintTargetPDF.h',
|
||||
]
|
||||
SOURCES += [
|
||||
'gfxFcPlatformFontList.cpp',
|
||||
'gfxFT2FontBase.cpp',
|
||||
'gfxFT2Utils.cpp',
|
||||
'gfxPlatformHeadless.cpp',
|
||||
'PrintTargetPDF.cpp',
|
||||
]
|
||||
elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
EXPORTS += [
|
||||
'gfxFT2FontBase.h',
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
GeckoProgram(CONFIG['MOZ_CHILD_PROCESS_NAME'], linkage='dependent')
|
||||
|
||||
SOURCES += [
|
||||
# JihadPmLogSem.c interposes sem_open/sem_close for the ONE name "PmLogLib". The device's
|
||||
# Flash pulls in libPmLogLib.so, whose constructor sem_wait()s a named semaphore created
|
||||
# by webOS's glibc 2.8; glibc 2.21 changed sem_t's value word to (tokens << 1), so our
|
||||
# bundled 2.23 reads that semaphore as "no tokens, waiters pending" and the dlopen never
|
||||
# returns. It has to be in THIS directory: a dlopened plugin resolves its PLT against the
|
||||
# global scope, which the program image heads, and ipc/app is where the program is linked
|
||||
# (ipc/contentproc only produces an archive this link does not use).
|
||||
'../contentproc/JihadPmLogSem.c',
|
||||
'MozillaRuntimeMain.cpp',
|
||||
]
|
||||
|
||||
|
||||
@@ -146,7 +146,13 @@ MessageLoop::MessageLoop(Type type, nsIThread* aThread)
|
||||
#if defined(OS_MACOSX)
|
||||
pump_ = base::MessagePumpMac::Create();
|
||||
#elif defined(OS_LINUX) || defined(OS_BSD) || defined(OS_SOLARIS)
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
pump_ = new base::MessagePumpForUI();
|
||||
#else
|
||||
// Jihad headless: no GTK UI pump; the embedder drives events via
|
||||
// NS_ProcessNextEvent, so a default (non-UI) message pump suffices.
|
||||
pump_ = new base::MessagePumpDefault();
|
||||
#endif
|
||||
#endif // OS_LINUX
|
||||
} else if (type_ == TYPE_IO) {
|
||||
pump_ = new base::MessagePumpLibevent();
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Jihad Browser — the /PmLogLib named-semaphore ABI trap.
|
||||
*
|
||||
* Compiled into every one of our binaries that can dlopen a device NPAPI plugin: the
|
||||
* daemon (its plugin scan) and plugin-container (the plugin itself). Plain C, no headers
|
||||
* from UXP or from the daemon, so the same file drops into both build systems unchanged.
|
||||
*/
|
||||
|
||||
// ── WHAT THIS IS FOR ────────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// The device's libflashplayer.so has libWebKitLuna.so as a DT_NEEDED, which drags in
|
||||
// libPmLogLib.so. PmLogLib's ELF constructor opens the POSIX named semaphore "PmLogLib"
|
||||
// and sem_wait()s it to guard a SysV shared-memory table of log contexts that every
|
||||
// process on the device shares.
|
||||
//
|
||||
// glibc changed sem_t's layout in 2.21. The value word went from a raw token count to
|
||||
// (tokens << 1) | has-waiters. webOS 3's userland is glibc 2.8 — the OLD layout — and
|
||||
// everything we cross-build runs on the bundled glibc 2.23, the NEW one. /dev/shm/
|
||||
// sem.PmLogLib is created once per boot by whichever process opens it first, in that
|
||||
// process's layout, and then outlives every user of it.
|
||||
//
|
||||
// When a system process wins that race the word holds a raw 1, meaning "one token". Our
|
||||
// 2.23 sem_wait reads the same word as ZERO tokens with the waiters bit set, so it goes
|
||||
// straight to futex_wait and never returns. dlopen never completes and whoever called it
|
||||
// wedges: measured on device 2026-08-10 as
|
||||
// futex(0x2dd98000, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, ffffffff
|
||||
// immediately after the mmap of /dev/shm/sem.PmLogLib, with every library already loaded.
|
||||
//
|
||||
// That is why this reads as an intermittent, reboot-triggered failure on binaries nobody
|
||||
// touched: it is decided by a boot race, not by our code. The same dlopen run under the
|
||||
// device's own /lib/ld-linux.so.3 completes and returns both Flash MIME types.
|
||||
//
|
||||
// ── WHY INTERPOSE RATHER THAN REPAIR THE SEMAPHORE ──────────────────────────────────────
|
||||
//
|
||||
// Rewriting /dev/shm/sem.PmLogLib into our layout, or posting it once to make it readable
|
||||
// to us, both work — and both hand every OTHER process on the device an extra token on a
|
||||
// lock guarding a shared table. That silently weakens PmLogLib's mutual exclusion
|
||||
// system-wide to fix a browser. Interposing ONE name does not: PmLogLib gets a semaphore
|
||||
// that never leaves this process, so no other glibc ever parses its layout, and the file
|
||||
// in /dev/shm is not touched at all.
|
||||
//
|
||||
// Everything else is forwarded to the real implementation untouched, which MATTERS: our
|
||||
// own shared frame buffers are named semaphores too (/dev/shm/sem.browserserver.<pid>-<n>,
|
||||
// created here and opened by the adapter inside LunaSysMgr), and those genuinely must stay
|
||||
// shared across the process boundary.
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
// PmLogLib asks for the name WITHOUT a leading slash (verified on device: the interposer
|
||||
// logged sem_open("PmLogLib")). POSIX says a portable name starts with '/', and glibc
|
||||
// accepts both, so match both rather than trusting either spelling.
|
||||
// Link anchor. plugin-container builds this TU into a static archive, and an archive member
|
||||
// is only pulled in to satisfy an UNDEFINED symbol — sem_open is already defined by
|
||||
// libpthread, so nothing was undefined and the whole file was silently dropped from the
|
||||
// link. Calling this from the program's entry point forces the member in. The daemon links
|
||||
// the object directly and does not need it; it costs one call there.
|
||||
__attribute__((visibility("default")))
|
||||
int jihad_pmlog_sem_installed(void) { return 1; }
|
||||
|
||||
static int jihad_is_pmlog_name(const char* name)
|
||||
{
|
||||
if (!name) return 0;
|
||||
if (name[0] == '/') ++name;
|
||||
return strcmp(name, "PmLogLib") == 0;
|
||||
}
|
||||
|
||||
static sem_t g_pmlogSem;
|
||||
static pthread_once_t g_pmlogSemOnce = PTHREAD_ONCE_INIT;
|
||||
static unsigned int g_pmlogSemInitial = 1;
|
||||
|
||||
static void jihad_pmlog_sem_init(void)
|
||||
{
|
||||
// pshared = 0. Process-private is the whole point: it can never be observed, and so can
|
||||
// never be misread, by a glibc with a different sem_t layout.
|
||||
sem_init(&g_pmlogSem, 0, g_pmlogSemInitial);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
sem_t* sem_open(const char* name, int oflag, ...)
|
||||
{
|
||||
static sem_t* (*real_sem_open)(const char*, int, ...);
|
||||
if (!real_sem_open)
|
||||
real_sem_open = (sem_t* (*)(const char*, int, ...))dlsym(RTLD_NEXT, "sem_open");
|
||||
|
||||
// The mode/value pair is only present when O_CREAT is set; reading it otherwise walks off
|
||||
// the end of the argument list.
|
||||
mode_t mode = 0;
|
||||
unsigned int value = 0;
|
||||
const int creating = (oflag & O_CREAT) != 0;
|
||||
if (creating) {
|
||||
va_list ap;
|
||||
va_start(ap, oflag);
|
||||
mode = (mode_t)va_arg(ap, unsigned int);
|
||||
value = va_arg(ap, unsigned int);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (jihad_is_pmlog_name(name)) {
|
||||
if (creating) g_pmlogSemInitial = value;
|
||||
pthread_once(&g_pmlogSemOnce, jihad_pmlog_sem_init);
|
||||
return &g_pmlogSem;
|
||||
}
|
||||
|
||||
if (!real_sem_open) return SEM_FAILED;
|
||||
if (creating) return real_sem_open(name, oflag, mode, value);
|
||||
return real_sem_open(name, oflag);
|
||||
}
|
||||
|
||||
// PmLogLib closes the semaphore on unload. glibc's sem_close looks the pointer up in its
|
||||
// table of NAMED semaphores, would not find ours, and would return EINVAL — harmless today,
|
||||
// but only by luck. Answer for our own pointer and forward everything else.
|
||||
__attribute__((visibility("default")))
|
||||
int sem_close(sem_t* sem)
|
||||
{
|
||||
static int (*real_sem_close)(sem_t*);
|
||||
if (!real_sem_close)
|
||||
real_sem_close = (int (*)(sem_t*))dlsym(RTLD_NEXT, "sem_close");
|
||||
|
||||
// Deliberately NOT sem_destroy: the plugin can be unloaded and reloaded within one
|
||||
// process (the scan does exactly that), and a destroyed static would be reused.
|
||||
if (sem == &g_pmlogSem) return 0;
|
||||
|
||||
if (!real_sem_close) return 0;
|
||||
return real_sem_close(sem);
|
||||
}
|
||||
@@ -9,6 +9,10 @@ SOURCES += [
|
||||
'plugin-container.cpp',
|
||||
]
|
||||
|
||||
# JihadPmLogSem.c lives here next to its only caller but is compiled by ipc/app, which is
|
||||
# where the plugin-container PROGRAM is linked; this directory only produces an archive that
|
||||
# that link does not use. See ipc/app/moz.build.
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stdlib.h> // getenv / unsetenv (JIHAD: see main)
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
// JihadPmLogSem.c — C, and deliberately carries no header, so it drops unchanged into both
|
||||
// this build and the daemon's. Declared here rather than in one because that is the entire
|
||||
// interface: one call whose only job is to keep the archive member in the link.
|
||||
extern "C" int jihad_pmlog_sem_installed(void);
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include <windows.h>
|
||||
// we want a wmain entry point
|
||||
@@ -19,6 +25,82 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// JIHAD DIAGNOSTIC (R7): the child process dies with SIGSEGV between a successful
|
||||
// InitForChrome and handling its first IPC message, and the device's own minicore reporter
|
||||
// is out of report slots ("no room for new reports"), so nothing records where. This prints
|
||||
// the faulting address, a backtrace, and /proc/self/maps to stderr — which the child
|
||||
// inherits from the daemon, so it lands in the variant's daemon.log. The maps dump is the
|
||||
// part that matters on ARM: even a single frame is enough to name the faulting library.
|
||||
#if defined(OS_POSIX) || defined(XP_UNIX)
|
||||
#include <execinfo.h>
|
||||
#include <signal.h>
|
||||
#include <ucontext.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
jihad_child_fault_handler(int sig, siginfo_t* info, void* ctx)
|
||||
{
|
||||
void* frames[32];
|
||||
int n = backtrace(frames, 32);
|
||||
fprintf(stderr, "[jihad-npapi-child] FAULT sig=%d addr=%p pid=%d frames=%d\n",
|
||||
sig, info ? info->si_addr : nullptr, (int)getpid(), n);
|
||||
|
||||
// backtrace() returns nothing useful from a signal frame on ARM (no unwind tables
|
||||
// through the trampoline), so take the registers straight out of the signal context.
|
||||
// pc and lr against the maps below give the faulting library and offset, and
|
||||
// addr2line on the unstripped build turns that into file:line.
|
||||
#if defined(__arm__)
|
||||
if (ctx) {
|
||||
ucontext_t* uc = (ucontext_t*)ctx;
|
||||
fprintf(stderr, "[jihad-npapi-child] FAULT pc=%08lx lr=%08lx sp=%08lx r0=%08lx\n",
|
||||
(unsigned long)uc->uc_mcontext.arm_pc,
|
||||
(unsigned long)uc->uc_mcontext.arm_lr,
|
||||
(unsigned long)uc->uc_mcontext.arm_sp,
|
||||
(unsigned long)uc->uc_mcontext.arm_r0);
|
||||
}
|
||||
#else
|
||||
(void)ctx;
|
||||
#endif
|
||||
fflush(stderr);
|
||||
backtrace_symbols_fd(frames, n, STDERR_FILENO);
|
||||
|
||||
FILE* maps = fopen("/proc/self/maps", "r");
|
||||
if (maps) {
|
||||
char line[512];
|
||||
fputs("[jihad-npapi-child] --- maps ---\n", stderr);
|
||||
while (fgets(line, sizeof(line), maps)) {
|
||||
// Only the executable mappings; the full map is hundreds of lines of noise.
|
||||
if (strstr(line, "r-xp")) {
|
||||
fputs("[jihad-npapi-child] ", stderr);
|
||||
fputs(line, stderr);
|
||||
}
|
||||
}
|
||||
fclose(maps);
|
||||
}
|
||||
fflush(stderr);
|
||||
|
||||
// Restore the default disposition and re-raise so the exit status still says "crashed".
|
||||
signal(sig, SIG_DFL);
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
static void
|
||||
jihad_install_child_fault_handler()
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = jihad_child_fault_handler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGSEGV, &sa, nullptr);
|
||||
sigaction(SIGBUS, &sa, nullptr);
|
||||
sigaction(SIGILL, &sa, nullptr);
|
||||
sigaction(SIGFPE, &sa, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GMP
|
||||
#include "GMPLoader.h"
|
||||
|
||||
@@ -38,6 +120,41 @@ content_process_main(int argc, char* argv[])
|
||||
return 3;
|
||||
}
|
||||
|
||||
#if defined(OS_POSIX) || defined(XP_UNIX)
|
||||
jihad_install_child_fault_handler();
|
||||
#endif
|
||||
|
||||
// JIHAD (R7): force JihadPmLogSem.o into the link and say so. It interposes sem_open for
|
||||
// the single name "PmLogLib", without which dlopening the device's Flash deadlocks this
|
||||
// process forever — libPmLogLib's constructor waits on a named semaphore created by
|
||||
// webOS's glibc 2.8, whose sem_t value word our glibc 2.23 reads as "no tokens, waiters
|
||||
// pending". The call is also the only cheap proof the interposer survived the link: it
|
||||
// lives in a static archive, and an archive member with no undefined symbol to satisfy
|
||||
// is silently dropped. See render/goanna/JihadPmLogSem.c.
|
||||
fprintf(stderr, "[jihad-npapi-child] pmlog sem interposition=%d\n",
|
||||
jihad_pmlog_sem_installed());
|
||||
|
||||
// JIHAD DIAGNOSTIC (R7): the child's own view of its argv. Doubles as the control that
|
||||
// child stderr reaches the parent's log at all — every other child probe is worthless
|
||||
// until this line is seen.
|
||||
fprintf(stderr, "[jihad-npapi-child] main argc=%d\n", argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
fprintf(stderr, "[jihad-npapi-child] argv[%d]=%s\n", i, argv[i]);
|
||||
}
|
||||
fflush(stderr);
|
||||
|
||||
// JIHAD (2026-10-05): DO NOT HAND OUR LIBRARY PATH TO WHAT THE PLUGIN RUNS.
|
||||
// This process is started through the bundled loader with an explicit --library-path,
|
||||
// which is what its own later dlopen()s search; the LD_LIBRARY_PATH it also inherits
|
||||
// is not needed by it. But it is inherited again by anything the plugin spawns, and
|
||||
// Flash for Linux runs `sh -c "ps x | grep netscape"` at start-up: the device's own
|
||||
// /bin/sh (built for its glibc 2.8) then loads the bundled glibc 2.23 and dies with
|
||||
// SIGSEGV, leaving a crash report in /var/log/reports for every page with Flash on it.
|
||||
// Only when the bundled loader is in use; a desktop build keeps its environment.
|
||||
if (getenv("MOZ_CHILD_PROCESS_LOADER")) {
|
||||
unsetenv("LD_LIBRARY_PATH");
|
||||
}
|
||||
|
||||
#ifdef MOZ_GMP
|
||||
XREChildData childData;
|
||||
#endif
|
||||
|
||||
@@ -160,6 +160,13 @@ GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType proce
|
||||
|
||||
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
|
||||
|
||||
// JIHAD DIAGNOSTIC (R7): the resolved child path, and which branch produced it. In this
|
||||
// embedding the daemon is exec'd as `./ld-2.23.so … ./jihad-browserserver`, so argv[0] is the
|
||||
// LOADER — if the directory-service branch is not taken, the fallback dirname(argv[0]) is
|
||||
// relative to that, not to the daemon.
|
||||
fprintf(stderr, "[jihad-npapi] child binary path=%s (dirService=%d)\n",
|
||||
exePath.value().c_str(), (int)ShouldHaveDirectoryService());
|
||||
|
||||
return BinaryPathType::PluginContainer;
|
||||
}
|
||||
|
||||
@@ -662,6 +669,27 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
||||
|
||||
std::vector<std::string> childArgv;
|
||||
|
||||
// Optional ELF loader prefix for the child process.
|
||||
//
|
||||
// An embedder whose bundled glibc differs from the host system's cannot exec the child
|
||||
// binary directly: its PT_INTERP names the SYSTEM loader, which then resolves the child's
|
||||
// libraries against the bundled set and dies before main(). Exporting these two variables
|
||||
// makes the child be launched exactly the way such an embedder launches itself —
|
||||
// <loader> --library-path <path> <child> <args…>
|
||||
// — which runs the child in the same process image (no extra fork, no shell hop, so the
|
||||
// pid we hand to waitpid and the inherited IPC channel fd are unaffected).
|
||||
//
|
||||
// Both unset is upstream behaviour, unchanged.
|
||||
const char* childLoader = PR_GetEnv("MOZ_CHILD_PROCESS_LOADER");
|
||||
if (childLoader && *childLoader) {
|
||||
childArgv.push_back(childLoader);
|
||||
const char* loaderLibPath = PR_GetEnv("MOZ_CHILD_PROCESS_LOADER_PATH");
|
||||
if (loaderLibPath && *loaderLibPath) {
|
||||
childArgv.push_back("--library-path");
|
||||
childArgv.push_back(loaderLibPath);
|
||||
}
|
||||
}
|
||||
|
||||
childArgv.push_back(exePath.value());
|
||||
|
||||
if (pathType == BinaryPathType::Self) {
|
||||
|
||||
@@ -66,10 +66,28 @@ struct WeakCell {
|
||||
WeakCellKind kind;
|
||||
WeakRef<JSObject*> object;
|
||||
WeakRef<JS::Symbol*> symbol;
|
||||
bool enabled;
|
||||
|
||||
WeakCell() : kind(WeakCellKind::Empty), object(nullptr), symbol(nullptr) {}
|
||||
explicit WeakCell(JSObject* obj) : kind(WeakCellKind::Object), object(obj), symbol(nullptr) {}
|
||||
explicit WeakCell(JS::Symbol* sym) : kind(WeakCellKind::Symbol), object(nullptr), symbol(sym) {}
|
||||
WeakCell()
|
||||
: kind(WeakCellKind::Empty)
|
||||
, object(nullptr)
|
||||
, symbol(nullptr)
|
||||
, enabled(true)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JSObject* obj, bool enabled)
|
||||
: kind(WeakCellKind::Object)
|
||||
, object(obj)
|
||||
, symbol(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit WeakCell(JS::Symbol* sym, bool enabled)
|
||||
: kind(WeakCellKind::Symbol)
|
||||
, object(nullptr)
|
||||
, symbol(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
void clear() {
|
||||
kind = WeakCellKind::Empty;
|
||||
@@ -101,7 +119,9 @@ struct WeakCell {
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
@@ -110,7 +130,7 @@ struct WeakCell {
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target) {
|
||||
if (enabled && target) {
|
||||
if (trc->isMarkingTracer() && !target->asTenured().zone()->isCollecting())
|
||||
return;
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
@@ -124,14 +144,16 @@ struct WeakCell {
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!enabled) {
|
||||
// Do nothing
|
||||
} else if (IsInsideNursery(target)) {
|
||||
TraceManuallyBarrieredEdge(trc, object.unsafeGet(), name);
|
||||
} else if (target->asTenured().zone()->isCollecting()) {
|
||||
TraceWeakEdge(trc, &object, name);
|
||||
}
|
||||
} else if (kind == WeakCellKind::Symbol) {
|
||||
JS::Symbol* target = symbol.unbarrieredGet();
|
||||
if (target && target->asTenured().zone()->isCollecting())
|
||||
if (enabled && target && target->asTenured().zone()->isCollecting())
|
||||
TraceWeakEdge(trc, &symbol, name);
|
||||
}
|
||||
}
|
||||
@@ -143,22 +165,24 @@ struct FinalizationRecord {
|
||||
WeakCell unregisterToken;
|
||||
bool active;
|
||||
bool queued;
|
||||
bool enabled;
|
||||
|
||||
FinalizationRecord(HandleValue targetValue, HandleValue heldValue,
|
||||
HandleValue unregisterTokenValue)
|
||||
HandleValue unregisterTokenValue, bool enabled)
|
||||
: heldValue(heldValue),
|
||||
active(true),
|
||||
queued(false)
|
||||
queued(false),
|
||||
enabled(enabled)
|
||||
{
|
||||
if (targetValue.isObject())
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()));
|
||||
target = WeakCell(NormalizeWeakObject(&targetValue.toObject()), enabled);
|
||||
else
|
||||
target = WeakCell(targetValue.toSymbol());
|
||||
target = WeakCell(targetValue.toSymbol(), enabled);
|
||||
|
||||
if (unregisterTokenValue.isObject())
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()));
|
||||
unregisterToken = WeakCell(NormalizeWeakObject(&unregisterTokenValue.toObject()), enabled);
|
||||
else if (unregisterTokenValue.isSymbol())
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol());
|
||||
unregisterToken = WeakCell(unregisterTokenValue.toSymbol(), enabled);
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
@@ -217,13 +241,15 @@ struct FinalizationRegistryObject::Data {
|
||||
FinalizationRecordVector records;
|
||||
FinalizationRecordVector cleanupQueue;
|
||||
bool queuedForCleanup;
|
||||
bool enabled;
|
||||
|
||||
explicit Data(JSObject* cleanupCallback)
|
||||
explicit Data(JSObject* cleanupCallback, bool enabled)
|
||||
: cleanupCallback(cleanupCallback),
|
||||
cleanupJob(nullptr),
|
||||
records(SystemAllocPolicy()),
|
||||
cleanupQueue(SystemAllocPolicy()),
|
||||
queuedForCleanup(false)
|
||||
queuedForCleanup(false),
|
||||
enabled(enabled)
|
||||
{}
|
||||
|
||||
~Data() {
|
||||
@@ -232,11 +258,13 @@ struct FinalizationRegistryObject::Data {
|
||||
}
|
||||
|
||||
void trace(JSTracer* trc) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
if (enabled) {
|
||||
JS::TraceEdge(trc, &cleanupCallback, "FinalizationRegistry cleanup callback");
|
||||
if (cleanupJob.unbarrieredGet())
|
||||
JS::TraceEdge(trc, &cleanupJob, "FinalizationRegistry cleanup job");
|
||||
for (size_t i = 0; i < records.length(); i++)
|
||||
records[i]->trace(trc);
|
||||
}
|
||||
}
|
||||
|
||||
bool appendRecord(FinalizationRecord* record) {
|
||||
@@ -317,7 +345,7 @@ FinalizationRegistry_register_impl(JSContext* cx, const CallArgs& args)
|
||||
unregisterToken.setUndefined();
|
||||
}
|
||||
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken);
|
||||
FinalizationRecord* record = cx->new_<FinalizationRecord>(target, heldValue, unregisterToken, cx->options().weakRefs());
|
||||
if (!record)
|
||||
return false;
|
||||
|
||||
@@ -401,7 +429,7 @@ FinalizationRegistryObject::create(JSContext* cx, HandleObject cleanupCallback,
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
|
||||
Data* data = cx->new_<Data>(cleanupCallback);
|
||||
Data* data = cx->new_<Data>(cleanupCallback, cx->options().weakRefs());
|
||||
if (!data)
|
||||
return nullptr;
|
||||
|
||||
|
||||
@@ -140,9 +140,9 @@ WeakRefObject::create(JSContext* cx, HandleValue target, HandleObject proto /* =
|
||||
|
||||
Referent* data;
|
||||
if (target.isObject())
|
||||
data = cx->new_<Referent>(&target.toObject());
|
||||
data = cx->new_<Referent>(&target.toObject(), cx->options().weakRefs());
|
||||
else
|
||||
data = cx->new_<Referent>(target.toSymbol());
|
||||
data = cx->new_<Referent>(target.toSymbol(), cx->options().weakRefs());
|
||||
|
||||
if (!data)
|
||||
return nullptr;
|
||||
@@ -210,7 +210,9 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (IsInsideNursery(target)) {
|
||||
if (!data->enabled) {
|
||||
// Pref disabled, don't do anything.
|
||||
} else if (IsInsideNursery(target)) {
|
||||
// Weak edges must be tenured; trace strongly while referent is in the nursery.
|
||||
TraceManuallyBarrieredEdge(trc, data->objectTarget.unsafeGet(),
|
||||
"WeakRef nursery referent");
|
||||
@@ -220,7 +222,7 @@ WeakRefObject::trace(JSTracer* trc, JSObject* obj)
|
||||
} else {
|
||||
MOZ_ASSERT(data->isSymbol());
|
||||
JS::Symbol* target = data->symbolTarget.unbarrieredGet();
|
||||
if (target)
|
||||
if (data->enabled && target)
|
||||
TraceWeakEdge(trc, &data->symbolTarget, "WeakRef symbol referent");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,17 +22,27 @@ class WeakRefObject : public NativeObject
|
||||
Symbol
|
||||
};
|
||||
|
||||
explicit Referent(JSObject* obj)
|
||||
: kind(Kind::Object), objectTarget(obj), symbolTarget(nullptr) {}
|
||||
explicit Referent(JS::Symbol* sym)
|
||||
: kind(Kind::Symbol), objectTarget(nullptr), symbolTarget(sym) {}
|
||||
|
||||
explicit Referent(JSObject* obj, bool enabled)
|
||||
: kind(Kind::Object)
|
||||
, objectTarget(obj)
|
||||
, symbolTarget(nullptr)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
explicit Referent(JS::Symbol* sym, bool enabled)
|
||||
: kind(Kind::Symbol)
|
||||
, objectTarget(nullptr)
|
||||
, symbolTarget(sym)
|
||||
, enabled(enabled)
|
||||
{}
|
||||
|
||||
bool isObject() const { return kind == Kind::Object; }
|
||||
bool isSymbol() const { return kind == Kind::Symbol; }
|
||||
|
||||
Kind kind;
|
||||
WeakRef<JSObject*> objectTarget;
|
||||
WeakRef<JS::Symbol*> symbolTarget;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static const Class class_;
|
||||
|
||||
+10
-10
@@ -416,13 +416,13 @@ MapMemoryAt(void* desired, size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0)
|
||||
|
||||
// Solaris manages 64-bit address space in a different manner from every other
|
||||
// AMD64 operating system, but fortunately the fix is the same one
|
||||
// required for every operating system on 64-bit SPARC, Itanium, and ARM.
|
||||
// AMD64 operating system, but fortunately the fix is the same one
|
||||
// required for every operating system on 64-bit SPARC and ARM.
|
||||
// Most people's intuition failed them here and they thought this couldn't
|
||||
// possibly be correct on AMD64, but for Solaris/illumos it is.
|
||||
|
||||
{
|
||||
#if defined(__ia64__) || defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) || (defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
#if defined(__aarch64__) || (defined(__sun) && defined(__x86_64__)) || (defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
MOZ_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0);
|
||||
#endif
|
||||
void* region = mmap(desired, length, prot, flags, fd, offset);
|
||||
@@ -445,14 +445,14 @@ static inline void*
|
||||
MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
int flags = MAP_PRIVATE | MAP_ANON, int fd = -1, off_t offset = 0)
|
||||
{
|
||||
#if defined(__ia64__) || (defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__))
|
||||
#if defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__)
|
||||
/*
|
||||
* The JS engine assumes that all allocated pointers have their high 17 bits clear,
|
||||
* which ia64's mmap doesn't support directly. However, we can emulate it by passing
|
||||
* mmap an "addr" parameter with those bits clear. The mmap will return that address,
|
||||
* or the nearest available memory above that address, providing a near-guarantee
|
||||
* that those bits are clear. If they are not, we return nullptr below to indicate
|
||||
* out-of-memory.
|
||||
* which mmap on this platform doesn't support directly. However, we can emulate it
|
||||
* by passing mmap an "addr" parameter with those bits clear. The mmap will return
|
||||
* that address, or the nearest available memory above that address, providing a
|
||||
* near-guarantee that those bits are clear. If they are not, we return nullptr below
|
||||
* to indicate out-of-memory.
|
||||
*
|
||||
* The addr is chosen as 0x0000070000000000, which still allows about 120TB of virtual
|
||||
* address space.
|
||||
@@ -479,7 +479,7 @@ MapMemory(size_t length, int prot = PROT_READ | PROT_WRITE,
|
||||
* different due to the different mmap behavior.
|
||||
*
|
||||
* TODO: Merge with the above code block if this implementation works for
|
||||
* ia64 and sparc64.
|
||||
* sparc64.
|
||||
*/
|
||||
const uintptr_t start = UINT64_C(0x0000070000000000);
|
||||
const uintptr_t end = UINT64_C(0x0000800000000000);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// |jit-test| --ion-sink=on
|
||||
// Sink Algorithm should not move instruction into merge blocks
|
||||
// which have no corresponding pc.
|
||||
|
||||
|
||||
@@ -2439,7 +2439,7 @@ static const VMFunction GetOrCreateModuleMetaObjectInfo =
|
||||
void
|
||||
CodeGenerator::visitModuleMetadata(LModuleMetadata* lir)
|
||||
{
|
||||
pushArg(ImmPtr(lir->mir()->module()));
|
||||
pushArg(ImmGCPtr(lir->mir()->module()));
|
||||
callVM(GetOrCreateModuleMetaObjectInfo, lir);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ OptimizationInfo::initNormalOptimizationInfo()
|
||||
rangeAnalysis_ = true;
|
||||
reordering_ = true;
|
||||
sincos_ = true;
|
||||
sink_ = true;
|
||||
|
||||
registerAllocator_ = RegisterAllocator_Backtracking;
|
||||
|
||||
@@ -69,7 +68,6 @@ OptimizationInfo::initWasmOptimizationInfo()
|
||||
eliminateRedundantChecks_ = false;
|
||||
scalarReplacement_ = false; // wasm has no objects.
|
||||
sincos_ = false;
|
||||
sink_ = false;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
||||
@@ -86,9 +86,6 @@ class OptimizationInfo
|
||||
// Toggles whether sincos is used.
|
||||
bool sincos_;
|
||||
|
||||
// Toggles whether sink is used.
|
||||
bool sink_;
|
||||
|
||||
// Describes which register allocator to use.
|
||||
IonRegisterAllocator registerAllocator_;
|
||||
|
||||
@@ -194,10 +191,6 @@ class OptimizationInfo
|
||||
return sincos_ && !JitOptions.disableSincos;
|
||||
}
|
||||
|
||||
bool sinkEnabled() const {
|
||||
return sink_ && !JitOptions.disableSink;
|
||||
}
|
||||
|
||||
bool eaaEnabled() const {
|
||||
return eaa_ && !JitOptions.disableEaa;
|
||||
}
|
||||
|
||||
@@ -130,9 +130,6 @@ DefaultJitOptions::DefaultJitOptions()
|
||||
SET_DEFAULT(disableSincos, true);
|
||||
#endif
|
||||
|
||||
// Toggles whether sink code motion is globally disabled.
|
||||
SET_DEFAULT(disableSink, true);
|
||||
|
||||
// Whether functions are compiled immediately.
|
||||
SET_DEFAULT(eagerCompilation, false);
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ struct DefaultJitOptions
|
||||
bool disableCacheIR;
|
||||
bool disableSharedStubs;
|
||||
bool disableSincos;
|
||||
bool disableSink;
|
||||
bool eagerCompilation;
|
||||
bool forceInlineCaches;
|
||||
bool limitScriptSize;
|
||||
|
||||
@@ -44,9 +44,6 @@ CommonDominator(MBasicBlock* commonDominator, MBasicBlock* defBlock)
|
||||
bool
|
||||
Sink(MIRGenerator* mir, MIRGraph& graph)
|
||||
{
|
||||
TempAllocator& alloc = graph.alloc();
|
||||
bool sinkEnabled = mir->optimizationInfo().sinkEnabled();
|
||||
|
||||
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
|
||||
if (mir->shouldCancel("Sink"))
|
||||
return false;
|
||||
@@ -106,125 +103,6 @@ Sink(MIRGenerator* mir, MIRGraph& graph)
|
||||
JitSpewDef(JitSpew_Sink, " No live uses, recover the instruction on bailout\n", ins);
|
||||
continue;
|
||||
}
|
||||
|
||||
// This guard is temporarly moved here as the above code deals with
|
||||
// Dead Code elimination, which got moved into this Sink phase, as
|
||||
// the Dead Code elimination used to move instructions with no-live
|
||||
// uses to the bailout path.
|
||||
if (!sinkEnabled)
|
||||
continue;
|
||||
|
||||
// To move an effectful instruction, we would have to verify that the
|
||||
// side-effect is not observed. In the mean time, we just inhibit
|
||||
// this optimization on effectful instructions.
|
||||
if (ins->isEffectful())
|
||||
continue;
|
||||
|
||||
// If all the uses are under a loop, we might not want to work
|
||||
// against LICM by moving everything back into the loop, but if the
|
||||
// loop is it-self inside an if, then we still want to move the
|
||||
// computation under this if statement.
|
||||
while (block->loopDepth() < usesDominator->loopDepth()) {
|
||||
MOZ_ASSERT(usesDominator != usesDominator->immediateDominator());
|
||||
usesDominator = usesDominator->immediateDominator();
|
||||
}
|
||||
|
||||
// Only move instructions if there is a branch between the dominator
|
||||
// of the uses and the original instruction. This prevent moving the
|
||||
// computation of the arguments into an inline function if there is
|
||||
// no major win.
|
||||
MBasicBlock* lastJoin = usesDominator;
|
||||
while (*block != lastJoin && lastJoin->numPredecessors() == 1) {
|
||||
MOZ_ASSERT(lastJoin != lastJoin->immediateDominator());
|
||||
MBasicBlock* next = lastJoin->immediateDominator();
|
||||
if (next->numSuccessors() > 1)
|
||||
break;
|
||||
lastJoin = next;
|
||||
}
|
||||
if (*block == lastJoin)
|
||||
continue;
|
||||
|
||||
// Skip to the next instruction if we cannot find a common dominator
|
||||
// for all the uses of this instruction, or if the common dominator
|
||||
// correspond to the block of the current instruction.
|
||||
if (!usesDominator || usesDominator == *block)
|
||||
continue;
|
||||
|
||||
// Only instruction which can be recovered on bailout and which are
|
||||
// sinkable can be moved into blocks which are below while filling
|
||||
// the resume points with a clone which is recovered on bailout.
|
||||
|
||||
// If the instruction has live uses and if it is clonable, then we
|
||||
// can clone the instruction for all non-dominated uses and move the
|
||||
// instruction into the block which is dominating all live uses.
|
||||
if (!ins->canClone())
|
||||
continue;
|
||||
|
||||
// If the block is a split-edge block, which is created for folding
|
||||
// test conditions, then the block has no resume point and has
|
||||
// multiple predecessors. In such case, we cannot safely move
|
||||
// bailing instruction to these blocks as we have no way to bailout.
|
||||
if (!usesDominator->entryResumePoint() && usesDominator->numPredecessors() != 1)
|
||||
continue;
|
||||
|
||||
JitSpewDef(JitSpew_Sink, " Can Clone & Recover, sink instruction\n", ins);
|
||||
JitSpew(JitSpew_Sink, " into Block %u", usesDominator->id());
|
||||
|
||||
// Copy the arguments and clone the instruction.
|
||||
MDefinitionVector operands(alloc);
|
||||
for (size_t i = 0, end = ins->numOperands(); i < end; i++) {
|
||||
if (!operands.append(ins->getOperand(i)))
|
||||
return false;
|
||||
}
|
||||
|
||||
MInstruction* clone = ins->clone(alloc, operands);
|
||||
ins->block()->insertBefore(ins, clone);
|
||||
clone->setRecoveredOnBailout();
|
||||
|
||||
// We should not update the producer of the entry resume point, as
|
||||
// it cannot refer to any instruction within the basic block excepts
|
||||
// for Phi nodes.
|
||||
MResumePoint* entry = usesDominator->entryResumePoint();
|
||||
|
||||
// Replace the instruction by its clone in all the resume points /
|
||||
// recovered-on-bailout instructions which are not in blocks which
|
||||
// are dominated by the usesDominator block.
|
||||
for (MUseIterator i(ins->usesBegin()), e(ins->usesEnd()); i != e; ) {
|
||||
MUse* use = *i++;
|
||||
MNode* consumer = use->consumer();
|
||||
|
||||
// If the consumer is a Phi, then we look for the index of the
|
||||
// use to find the corresponding predecessor block, which is
|
||||
// then used as the consumer block.
|
||||
MBasicBlock* consumerBlock = consumer->block();
|
||||
if (consumer->isDefinition() && consumer->toDefinition()->isPhi()) {
|
||||
consumerBlock = consumerBlock->getPredecessor(
|
||||
consumer->toDefinition()->toPhi()->indexOf(use));
|
||||
}
|
||||
|
||||
// Keep the current instruction for all dominated uses, except
|
||||
// for the entry resume point of the block in which the
|
||||
// instruction would be moved into.
|
||||
if (usesDominator->dominates(consumerBlock) &&
|
||||
(!consumer->isResumePoint() || consumer->toResumePoint() != entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
use->replaceProducer(clone);
|
||||
}
|
||||
|
||||
// As we move this instruction in a different block, we should
|
||||
// verify that we do not carry over a resume point which would refer
|
||||
// to an outdated state of the control flow.
|
||||
if (ins->resumePoint())
|
||||
ins->clearResumePoint();
|
||||
|
||||
// Now, that all uses which are not dominated by usesDominator are
|
||||
// using the cloned instruction, we can safely move the instruction
|
||||
// into the usesDominator block.
|
||||
MInstruction* at = usesDominator->safeInsertTop(nullptr, MBasicBlock::IgnoreRecover);
|
||||
block->moveBefore(at, ins);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -320,7 +320,6 @@ size_t AssemblerLOONGARCH64::bytesNeeded() const {
|
||||
|
||||
// write a blob of binary into the instruction stream
|
||||
BufferOffset AssemblerLOONGARCH64::writeInst(uint32_t x, uint32_t* dest) {
|
||||
MOZ_ASSERT(hasCreator());
|
||||
if (dest == nullptr) {
|
||||
return m_buffer.putInt(x);
|
||||
}
|
||||
|
||||
@@ -212,6 +212,8 @@ static constexpr Register WasmTableCallIndexReg = ABINonArgReg3;
|
||||
static constexpr Register WasmIonExitRegCallee = t0;
|
||||
static constexpr Register WasmIonExitRegE0 = a0;
|
||||
static constexpr Register WasmIonExitRegE1 = a1;
|
||||
static constexpr Register WasmIonExitRegReturnData = JSReturnReg_Data;
|
||||
static constexpr Register WasmIonExitRegReturnType = JSReturnReg_Type;
|
||||
static constexpr Register WasmIonExitRegD0 = a0;
|
||||
static constexpr Register WasmIonExitRegD1 = a1;
|
||||
static constexpr Register WasmIonExitRegD2 = t0;
|
||||
|
||||
@@ -1013,8 +1013,8 @@ CodeGeneratorLoongArch64::visitTestI64AndBranch(LTestI64AndBranch* lir)
|
||||
void
|
||||
CodeGeneratorLoongArch64::setReturnDoubleRegs(LiveRegisterSet* regs)
|
||||
{
|
||||
MOZ_ASSERT(ReturnFloat32Reg.reg_ == FloatRegisters::f0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.reg_ == FloatRegisters::f0);
|
||||
MOZ_ASSERT(ReturnFloat32Reg.encoding() == FloatRegisters::f0);
|
||||
MOZ_ASSERT(ReturnDoubleReg.encoding() == FloatRegisters::f0);
|
||||
FloatRegister f1 = { FloatRegisters::f1, FloatRegisters::Single };
|
||||
regs->add(ReturnFloat32Reg);
|
||||
regs->add(f1);
|
||||
|
||||
@@ -2395,7 +2395,7 @@ void MacroAssemblerLOONGARCH64::wasmLoadImpl(const wasm::MemoryAccessDesc& acces
|
||||
Register ptrScratch,
|
||||
AnyRegister output, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2442,7 +2442,7 @@ void MacroAssemblerLOONGARCH64::wasmStoreImpl(const wasm::MemoryAccessDesc& acce
|
||||
Register memoryBase, Register ptr,
|
||||
Register ptrScratch, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2488,7 +2488,7 @@ void MacroAssemblerLOONGARCH64Compat::wasmLoadI64Impl(
|
||||
const wasm::MemoryAccessDesc& access, Register memoryBase, Register ptr,
|
||||
Register ptrScratch, Register64 output, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2534,7 +2534,7 @@ void MacroAssemblerLOONGARCH64Compat::wasmStoreI64Impl(
|
||||
const wasm::MemoryAccessDesc& access, Register64 value, Register memoryBase,
|
||||
Register ptr, Register ptrScratch, Register tmp) {
|
||||
uint32_t offset = access.offset();
|
||||
MOZ_ASSERT(offset < asMasm().wasmMaxOffsetGuardLimit());
|
||||
MOZ_ASSERT(offset < wasm::OffsetGuardLimit);
|
||||
MOZ_ASSERT_IF(offset, ptrScratch != InvalidReg);
|
||||
|
||||
// Maybe add the offset.
|
||||
@@ -2774,12 +2774,13 @@ CodeOffset MacroAssembler::nopPatchableToNearJump() {
|
||||
}
|
||||
|
||||
void MacroAssembler::patchNopToNearJump(uint8_t* jump, uint8_t* target) {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->encode() == NopInst);
|
||||
new (jump) InstJump(op_b, JOffImm26(target - jump));
|
||||
}
|
||||
|
||||
void MacroAssembler::patchNearJumpToNop(uint8_t* jump) {
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstJump>());
|
||||
MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->extractBitField(31, 26) ==
|
||||
(uint32_t)op_b >> 26);
|
||||
new (jump) InstNOP();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
|
||||
#include "jit/x86-shared/BaseAssembler-x86-shared.h"
|
||||
|
||||
// Jihad Browser: GCC 9+ -Wformat-overflow raises false positives on the debug
|
||||
// spew() format strings below (it deduces a null %s arg via value-range
|
||||
// analysis). js/src forces -Werror=format, which re-escalates this even when
|
||||
// downgraded on the command line, so silence the diagnostic at the source.
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# pragma GCC diagnostic ignored "-Wformat-overflow"
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ void unmapPages(void* p, size_t size) { }
|
||||
void*
|
||||
mapMemoryAt(void* desired, size_t length)
|
||||
{
|
||||
#if defined(__ia64__) || defined(__aarch64__) || \
|
||||
#if defined(__aarch64__) || \
|
||||
(defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0);
|
||||
#endif
|
||||
@@ -324,7 +324,7 @@ mapMemory(size_t length)
|
||||
int fd = -1;
|
||||
off_t offset = 0;
|
||||
// The test code must be aligned with the implementation in gc/Memory.cpp.
|
||||
#if defined(__ia64__) || (defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__))
|
||||
#if defined(__sparc__) && defined(__arch64__) && defined(__NetBSD__)
|
||||
void* region = mmap((void*)0x0000070000000000, length, prot, flags, fd, offset);
|
||||
if (region == MAP_FAILED)
|
||||
return nullptr;
|
||||
|
||||
+6
-3
@@ -999,7 +999,8 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
strictMode_(false),
|
||||
extraWarnings_(false),
|
||||
arrayProtoValues_(true),
|
||||
streams_(false)
|
||||
streams_(false),
|
||||
weakRefs_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1139,12 +1140,13 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool weakRefs() const { return true; }
|
||||
bool weakRefs() const { return weakRefs_; }
|
||||
ContextOptions& setWeakRefs(bool flag) {
|
||||
(void) flag;
|
||||
weakRefs_ = flag;
|
||||
return *this;
|
||||
}
|
||||
ContextOptions& toggleWeakRefs() {
|
||||
weakRefs_ = !weakRefs_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1165,6 +1167,7 @@ class JS_PUBLIC_API(ContextOptions) {
|
||||
bool extraWarnings_ : 1;
|
||||
bool arrayProtoValues_ : 1;
|
||||
bool streams_ : 1;
|
||||
bool weakRefs_ : 1;
|
||||
};
|
||||
|
||||
JS_PUBLIC_API(ContextOptions&)
|
||||
|
||||
+6
-3
@@ -4761,11 +4761,14 @@ GCRuntime::sweepFinalizationRegistries()
|
||||
if (!obj || !obj->is<FinalizationRegistryObject>()) {
|
||||
zone->finalizationRegistries.erase(zone->finalizationRegistries.begin() + i);
|
||||
continue;
|
||||
}
|
||||
|
||||
obj->as<FinalizationRegistryObject>().sweepAfterGC(rt);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
for (size_t i = 0; i < zone->finalizationRegistries.length(); i++) {
|
||||
JSObject* obj = zone->finalizationRegistries[i].unbarrieredGet();
|
||||
obj->as<FinalizationRegistryObject>().sweepAfterGC(rt);
|
||||
}
|
||||
zone->finalizationRegistries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-7
@@ -976,6 +976,12 @@ CanUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
static inline bool
|
||||
CanUpperCaseSpecialCasing(char16_t charCode)
|
||||
{
|
||||
// Update mapping for U+00DF LATIN SMALL LETTER SHARP S inline,
|
||||
// overriding Unicode data files to force the use of
|
||||
// U+1E9E LATIN CAPITAL LETTER SHARP S.
|
||||
if (charCode == unicode::LATIN_SMALL_LETTER_SHARP_S)
|
||||
return true;
|
||||
|
||||
return unicode::CanUpperCaseSpecialCasing(charCode);
|
||||
}
|
||||
|
||||
@@ -991,6 +997,11 @@ LengthUpperCaseSpecialCasing(Latin1Char charCode)
|
||||
static inline size_t
|
||||
LengthUpperCaseSpecialCasing(char16_t charCode)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased according to ICU or
|
||||
// to U+1E9E LATIN CAPITAL LETTER SHARP S.
|
||||
if (charCode == unicode::LATIN_SMALL_LETTER_SHARP_S)
|
||||
return 1;
|
||||
|
||||
MOZ_ASSERT(CanUpperCaseSpecialCasing(charCode));
|
||||
|
||||
return unicode::LengthUpperCaseSpecialCasing(charCode);
|
||||
@@ -1010,6 +1021,13 @@ AppendUpperCaseSpecialCasing(char16_t charCode, Latin1Char* elements, size_t* in
|
||||
static inline void
|
||||
AppendUpperCaseSpecialCasing(char16_t charCode, char16_t* elements, size_t* index)
|
||||
{
|
||||
// U+00DF LATIN SMALL LETTER SHARP S is uppercased according to ICU or
|
||||
// to U+1E9E LATIN CAPITAL LETTER SHARP S.
|
||||
if (charCode == unicode::LATIN_SMALL_LETTER_SHARP_S) {
|
||||
elements[(*index)++] = 0x1E9E;
|
||||
return;
|
||||
}
|
||||
|
||||
unicode::AppendUpperCaseSpecialCasing(charCode, elements, index);
|
||||
}
|
||||
|
||||
@@ -1025,6 +1043,7 @@ ToUpperCaseImpl(DestChar* destChars, const SrcChar* srcChars, size_t startIndex,
|
||||
MOZ_ASSERT(srcLength <= destLength);
|
||||
|
||||
size_t j = startIndex;
|
||||
size_t upperLength = srcLength;
|
||||
for (size_t i = startIndex; i < srcLength; i++) {
|
||||
char16_t c = srcChars[i];
|
||||
if (!IsSame<DestChar, Latin1Char>::value) {
|
||||
@@ -1040,9 +1059,10 @@ ToUpperCaseImpl(DestChar* destChars, const SrcChar* srcChars, size_t startIndex,
|
||||
}
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(c > 0x7f && CanUpperCaseSpecialCasing(static_cast<SrcChar>(c)))) {
|
||||
if (MOZ_UNLIKELY(c > 0x7f && CanUpperCaseSpecialCasing(static_cast<DestChar>(c)))) {
|
||||
// Return if the output buffer is too small.
|
||||
if (srcLength == destLength)
|
||||
upperLength += (LengthUpperCaseSpecialCasing(static_cast<DestChar>(c)) - 1);
|
||||
if (upperLength > destLength)
|
||||
return i;
|
||||
|
||||
AppendUpperCaseSpecialCasing(c, destChars, &j);
|
||||
@@ -1071,14 +1091,18 @@ ToUpperCaseImpl(Latin1Char* destChars, const char16_t* srcChars, size_t startInd
|
||||
|
||||
template <typename CharT>
|
||||
static size_t
|
||||
ToUpperCaseLength(const CharT* chars, size_t startIndex, size_t length)
|
||||
ToUpperCaseLength(const CharT* chars, size_t startIndex, size_t length, bool force16)
|
||||
{
|
||||
size_t upperLength = length;
|
||||
for (size_t i = startIndex; i < length; i++) {
|
||||
char16_t c = chars[i];
|
||||
|
||||
if (c > 0x7f && CanUpperCaseSpecialCasing(static_cast<CharT>(c)))
|
||||
upperLength += LengthUpperCaseSpecialCasing(static_cast<CharT>(c)) - 1;
|
||||
if (c > 0x7f &&
|
||||
(force16 && CanUpperCaseSpecialCasing(c) ||
|
||||
CanUpperCaseSpecialCasing(static_cast<CharT>(c))))
|
||||
upperLength += (force16
|
||||
? LengthUpperCaseSpecialCasing(c)
|
||||
: LengthUpperCaseSpecialCasing(static_cast<CharT>(c))) - 1;
|
||||
}
|
||||
return upperLength;
|
||||
}
|
||||
@@ -1117,7 +1141,7 @@ ToUpperCase(JSContext* cx, const SrcChar* chars, size_t startIndex, size_t lengt
|
||||
|
||||
size_t readChars = ToUpperCaseImpl(buf.get(), chars, startIndex, length, length);
|
||||
if (readChars < length) {
|
||||
size_t actualLength = ToUpperCaseLength(chars, readChars, length);
|
||||
size_t actualLength = ToUpperCaseLength(chars, readChars, length, !IsSame<DestChar, Latin1Char>::value);
|
||||
|
||||
*resultLength = actualLength;
|
||||
DestCharPtr buf2 = ReallocChars(cx, Move(buf), length + 1, actualLength + 1);
|
||||
@@ -1178,7 +1202,8 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
||||
//
|
||||
// If the original string is Latin-1, it can -- unless the string
|
||||
// contains U+00B5 MICRO SIGN or U+00FF SMALL LETTER Y WITH DIAERESIS,
|
||||
// the only Latin-1 codepoints that don't uppercase within Latin-1.
|
||||
// the only Latin-1 codepoints that don't uppercase within Latin-1, or
|
||||
// it contains U+00DF LATIN SMALL LETTER SHARP S, which we override.
|
||||
// Search for those codepoints to decide whether the new string can be
|
||||
// Latin-1.
|
||||
// If the original string is a two-byte string, its uppercase form is
|
||||
@@ -1195,6 +1220,9 @@ ToUpperCase(JSContext* cx, JSLinearString* str)
|
||||
MOZ_ASSERT(unicode::ToUpperCase(c) > JSString::MAX_LATIN1_CHAR);
|
||||
resultIsLatin1 = false;
|
||||
break;
|
||||
} else if (c == unicode::LATIN_SMALL_LETTER_SHARP_S) {
|
||||
resultIsLatin1 = false;
|
||||
break;
|
||||
} else {
|
||||
MOZ_ASSERT(unicode::ToUpperCase(c) <= JSString::MAX_LATIN1_CHAR);
|
||||
}
|
||||
|
||||
@@ -7709,15 +7709,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
||||
return OptionFailure("ion-sincos", str);
|
||||
}
|
||||
|
||||
if (const char* str = op.getStringOption("ion-sink")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::JitOptions.disableSink = false;
|
||||
else if (strcmp(str, "off") == 0)
|
||||
jit::JitOptions.disableSink = true;
|
||||
else
|
||||
return OptionFailure("ion-sink", str);
|
||||
}
|
||||
|
||||
if (const char* str = op.getStringOption("ion-loop-unrolling")) {
|
||||
if (strcmp(str, "on") == 0)
|
||||
jit::JitOptions.disableLoopUnrolling = false;
|
||||
@@ -8093,8 +8084,6 @@ main(int argc, char** argv, char** envp)
|
||||
|| !op.addStringOption('\0', "ion-sincos", "on/off",
|
||||
"Replace sin(x)/cos(x) to sincos(x) (default: off, on to enable)")
|
||||
#endif
|
||||
|| !op.addStringOption('\0', "ion-sink", "on/off",
|
||||
"Sink code motion (default: off, on to enable)")
|
||||
|| !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
|
||||
"Loop unrolling (default: off, on to enable)")
|
||||
|| !op.addStringOption('\0', "ion-instruction-reordering", "on/off",
|
||||
|
||||
Loaded 100 of 814 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user