Compare commits

..
Author SHA1 Message Date
Moonchild 44d17e4ee7 Replace strnlen with memchr. See code comment. 2026-08-15 10:19:41 +02:00
Moonchild 26cc7c3126 Revert "Refactor string utils in MFBT."
This reverts commit cea1b66ba5.
2026-08-15 09:49:46 +02:00
Moonchild cea1b66ba5 Refactor string utils in MFBT.
This adds an Apple carveout for strnlen for OS X < 10.7 and renames the
ToString.h header to StringUtils.h to better reflect the more generic
collection of functions in it, instead of a 1-file-1-method header.
2026-08-13 16:42:35 +02:00
Moonchild 948559c22c Revert "[parser] Restrict subtree root check to DOM image elements only."
As it turns out, the reported regression was actually noncompliant HTML
and the initial implementation was correct.

This reverts commit fc25f50e5f.
2026-08-11 17:39:55 +02:00
Moonchild 5d7a1ca5a9 Merge pull request 'Several Python 3-related fixes to the build system' (#3160) from FranklinDM/UXP-contrib:work_python3-fixes-build1 into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3160
2026-08-10 12:40:05 +00:00
Francis Dominic Fajardo 613443c5eb No issue - Remove old Python 2 workaround that encodes environment vars in bytes
This should fix installer packaging.
2026-08-10 19:10:18 +08:00
Basilisk-Dev d7884bb96c No issue - Fix faster build packaging on Python 3 2026-08-10 19:10:11 +08:00
Moonchild fc25f50e5f [parser] Restrict subtree root check to DOM image elements only.
This was too broadly applied, resulting in named form elements not being
accessible through the DOM tree if not in the same subtree root.
2026-08-09 18:50:50 +02:00
Moonchild b70b3a5aa1 Merge pull request 'Clean up NSIS scripting.' (#3121) from nsis-cleanup into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3121
2026-08-06 13:50:39 +00:00
Moonchild 88ae39ed04 Merge pull request 'Implement :has() CSS pseudo class' (#3152) from Basilisk-Dev/UXP-contrib:css-has into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3152
2026-08-06 06:34:14 +00:00
Basilisk-Dev d893526f82 Issue #2473 - Inline functions in CSS :has() implementation 2026-07-29 16:48:12 -04:00
Moonchild 4d5b1cc5d8 Merge branch 'nss-3.125-port' 2026-07-28 10:50:50 +02:00
Moonchild 0e521f6302 Bump NSS (UXP) patch level. 2026-07-28 10:10:27 +02:00
Moonchild 9cacfcff57 Merge remote-tracking branch 'dbsoft/flex-button-fix2' 2026-07-28 10:08:00 +02:00
John Schanck 21ad7adc90 Bug 2043887 - guard against integer overflow in CERT_Hexify. 2026-07-28 10:03:44 +02:00
Benjamin Beurdouche 9a29430751 Bug 2027353 - clamp input.len to testString size in pk11_mergeSecretKey. 2026-07-28 09:59:51 +02:00
Benjamin Beurdouche 481d4868b0 Bug 2029433 - Bounds-check wrap index in PK11_GetWrapKey to match PK11_SetWrapKey. 2026-07-28 09:57:10 +02:00
Anna Weine d148c11717 Bug 2028954 - CERT_DecodeAVAValue — Integer Overflow in Output Buffer Sizing. 2026-07-28 09:53:26 +02:00
Moonchild bc322e58f8 Bug 2027364 - Set tail pointer to null in static slot lists when deallocating. 2026-07-28 09:22:56 +02:00
John Schanck e48a6f26a5 Bug 2032110 - fix two integer overflows on LLP64 systems. 2026-07-27 23:58:14 +02:00
John Schanck d85edd01df Bug 2030102 - avoid leaving a dangling ss->sec.ci.sid on allocation failure. 2026-07-27 23:49:55 +02:00
Maurice Dauer 296379d751 Bug 2027325 - Reject empty nickname in PK11_TraverseCertsForNicknameInSlot. 2026-07-27 23:46:32 +02:00
Moonchild 7ba9fc2801 Bug 2030563 - Validate CKA_CERTIFICATE_TYPE ulValueLen in nss_cert_type_from_ck_attrib. 2026-07-27 23:22:58 +02:00
Brian Smith a9ac8b390f Issue #3153 - Make sure buttons report a definite size for flex containers.
Second try, this time ONLY do it for flex container or grids.
This should prevent layout problems in non-flex scenarios.
2026-07-27 12:36:24 -05:00
Benjamin Beurdouche 71c0f8246c Bug 2029807 - Guard padding read against empty output in SEC_PKCS7DecryptContents. 2026-07-27 19:12:42 +02:00
Benjamin Beurdouche 6cda35442f Bug 2027346 - validate encoded EC params length and tag in SECKEY_ECParamsToKeySize/BasePointOrderLen. 2026-07-27 19:05:33 +02:00
Benjamin Beurdouche 1dba423327 Bug 2027371 - rewrite labelLen bound in tls13_HkdfExpandLabelGeneral to avoid unsigned overflow. 2026-07-27 18:56:08 +02:00
Benjamin Beurdouche e2af0b28bf Bug 2027373 - guard space subtraction in ssl_CallCustomExtensionSenders. 2026-07-27 18:51:32 +02:00
Benjamin Beurdouche 5ea9b7b80a Bug 2029901 - Guard against keySize overflow in IKE PRF/PRF+ output sizing. 2026-07-27 18:44:55 +02:00
Benjamin Beurdouche b799e100ad Bug 2027362 - bound usageCount in PK11_UnwrapPrivKey to keyTemplate capacity. 2026-07-27 18:39:10 +02:00
Benjamin Beurdouche 1c500a6abb Bug 2027349 - reject empty nickname in PK11_TraverseCertsForNicknameInSlot. 2026-07-27 18:27:47 +02:00
Moonchild b909fccc92 Revert "Issue #3153 - Make sure buttons report a definite size for flex containers."
This reverts commit 3e223cbfb4.
2026-07-27 17:59:43 +02:00
Moonchild abddee43b4 Merge remote-tracking branch 'dbsoft/flex-button-fix' 2026-07-27 14:41:27 +02:00
Moonchild 49d184e550 [network] Fix CacheEntry::mDoomCallback data race. 2026-07-27 14:32:51 +02:00
Brian Smith 3e223cbfb4 Issue #3153 - Make sure buttons report a definite size for flex containers. 2026-07-27 05:38:09 -05:00
Basilisk-Dev 7341809f67 Issue #2473 - implement :has() CSS pseudo class 2026-07-26 14:35:40 -04:00
Moonchild dd9099a7d3 [layout] Fix nsFrameSetFrame primary frame handling of replicated children. 2026-07-26 19:55:49 +02:00
Moonchild 31558a1521 Merge pull request 'Issue #3061 Follow-up: Remove network tickler prefs' (#3151) from Shadow/UXP-contrib:contrib into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3151
2026-07-26 15:45:08 +00:00
Shadow 0e683bf699 Issue #3061 Follow-up: Remove network tickler prefs 2026-07-26 15:39:39 +00:00
Moonchild 6459dec5bb [DOM media] Perform more thorough checks on video regions. 2026-07-26 15:04:18 +02:00
az 0e930fba3f Bug 2045742 2026-07-26 10:08:13 +02:00
Moonchild 3b683bbe59 Merge pull request 'Align aspect-ratio to spec' (#3150) from Basilisk-Dev/UXP-contrib:css-aspect-ratio into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3150
2026-07-26 07:06:00 +00:00
Basilisk-Dev 0042e54c38 Issue #2404 - Split aspect ratio enum types into a separate header to fix compilation issues 2026-07-25 12:24:15 -04:00
Andrew McCreight e75201f7d1 Bug 2049523 - Make ToJSValue for float consistent with double. 2026-07-25 17:04:14 +02:00
Moonchild 28c47723b2 Fix comment (nit) 2026-07-25 16:12:08 +02:00
Randell Jesup 3d872ff7b0 Bug 2029734: Flush before calling sctp_process_init() 2026-07-25 14:52:29 +02:00
Moonchild beb684c29e No issue - Use strnlen instead of strnlen_s.
Apparently, GCC doesn't like the _s variants.
2026-07-25 13:08:15 +02:00
Moonchild 775588b75d [image] Make sure the image was fully and cleanly decoded before finalizing its animation state as complete. 2026-07-25 10:01:55 +02:00
Basilisk-Dev 8a2a5d837c Issue #2404 - Fix aspect ratio sizing for grid items 2026-07-25 00:58:20 -04:00
Basilisk-Dev 8add755c81 Issue #2404 - Improvements to CSS aspect-ratio spec compliance 2026-07-25 00:57:58 -04:00
Moonchild 257c64567e [parser] Associate image elements with form only if the form and the intended parent are in the same tree. 2026-07-24 21:12:04 +02:00
Moonchild d0b61d4a68 No issue - CallUpdateWindow is [[nodiscard]], but we don't need to use
the result here.
2026-07-24 19:29:00 +02:00
Moonchild 3b5f50cc35 Merge pull request 'No Issue - Remove the half-life experiment in HTTP cache' (#3149) from Shadow/UXP-contrib:master into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3149
2026-07-24 14:32:21 +00:00
Moonchild 27c6a7f477 [DOM media] Stop accepting audio samples with too low sample rates.
This sets the floor to 8kHz audio, which is generally considered to be
the lowest acceptable "phone quality"/"walkie-talkie" audio rate.
Audio outside the range will refuse to decode after this commit and give
a console error.
This avoids a whole class of potential bugs.
2026-07-24 14:52:40 +02:00
Moonchild c65f84214e [layout] Add corner-case correctness check in DottedCornerFinder::FindBestOverlap. 2026-07-24 13:17:54 +02:00
Moonchild a317c65fd7 [network] Add usrsctp TSN gap check. 2026-07-24 12:58:20 +02:00
Shadow 0ec35ce534 No Issue - Remove the half-life experiment in HTTP cache
Reviewed-on: https://repo.palemoon.org/Shadow/UXP-contrib/pulls/1
2026-07-24 09:49:48 +00:00
Moonchild bf0c040722 [js] Clamp length of regexp captures array for dollar substitution. 2026-07-22 22:37:50 +02:00
Moonchild ebadd51401 [js] Initialize BaselineFrame return value slot in OnLeaveBaselineFrame. 2026-07-22 20:26:07 +02:00
Moonchild 5ed0bf8b44 [DOM] Make parser update form owner properly 2026-07-22 17:12:32 +02:00
Moonchild f877c1dba5 [gfx] Hold a strong ref during GetFontAt lookup. 2026-07-22 10:27:53 +02:00
Moonchild 4c14f8f89b [gfx] Add bounds checks and limit reverse scanning of ligature data. 2026-07-22 09:43:54 +02:00
Moonchild c119728965 Merge pull request 'Implement CSSStyleSheet replaceSync' (#3145) from Basilisk-Dev/UXP-contrib:replacesync into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3145
2026-07-22 06:02:21 +00:00
Moonchild fa55b9c2e8 [js] Guard against OOB string sizes in internal Printf 2026-07-22 01:39:03 +02:00
Basilisk-Dev 16aba76fc5 Issue #3144 - Implement CSSStyleSheet replaceSync 2026-07-21 04:23:19 -04:00
Moonchild 995def99cd No issue - Exclude some files from the preprocessor that don't have directives in them. 2026-07-19 15:57:10 +02:00
Moonchild 923e52789c Merge pull request 'Convert std::*<T>::value/::type to _v and _t suffixes.' (#3143) from 1803 into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3143
2026-07-19 07:40:35 +00:00
Moonchild 22d945430b Issue #3061 - Remove network tickler.
Resolves #3061
2026-07-19 08:17:05 +02:00
Moonchild 65d7d93760 Issue #1803 - Convert std::*<T>::value/::type to _v and _t suffixes.
This excludes uses in third-party libs in our tree.
2026-07-15 11:20:06 +02:00
Moonchild dfd0ea4e8a Merge pull request 'implement URL.parse()' (#3141) from Basilisk-Dev/UXP-contrib:urlparse into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3141
2026-07-14 21:05:40 +00:00
Basilisk-Dev 12a49709cb Issue #3140 - implement URL.parse() 2026-07-14 16:07:45 -04:00
Moonchild 2a70c8a6f0 Merge pull request 'Implement CSS4 revert-layer keyword' (#3139) from Basilisk-Dev/UXP-contrib:revert-layer into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3139
2026-07-13 12:44:06 +00:00
Moonchild 1f639d78ca Merge pull request 'Allow Skia on Mac OS X 10.5 Leopard / big endian PPC' (#3138) from Jazzzny/UXP:skia-leopard-ppc into master
Reviewed-on: https://repo.palemoon.org/MoonchildProductions/UXP/pulls/3138
2026-07-13 12:43:15 +00:00
Basilisk-Dev 65e8245153 Issue #2829 - Regenerate devtools CSS database for revert-layer 2026-07-11 10:41:52 -04:00
Basilisk-Dev bce5dbad0b Issue #2829 - Use layer index naming for revert-layer metadata 2026-07-11 10:41:46 -04:00
Basilisk-Dev 0b25b5f8db Issue #2829 - Handle revert-layer variable fallbacks 2026-07-10 09:59:20 -04:00
Basilisk-Dev 7345874098 Issue #2829 - Implement CSS revert-layer cascade rollback 2026-07-10 09:59:14 -04:00
Basilisk-Dev edb82fd1c9 Issue #2829 - Support revert-layer in CSS-wide parsing 2026-07-10 09:59:03 -04:00
Jazzzny 35ebdcac66 Fix build issues in Skia code 2026-07-09 12:32:44 -04:00
Jazzzny a095661e86 Use Skia macros where appropriate 2026-07-08 13:43:07 -04:00
Jazzzny 205a0d66bb Add support for big endian in Skia 2026-07-08 04:38:00 -04:00
Jazzzny 54945a0808 Unblock Skia for big endian 2026-07-08 04:38:00 -04:00
Jazzzny 84c1eedac6 Fixes for Skia on 10.5 2026-07-08 04:38:00 -04:00
Moonchild ddf5d6256e Whitespace fix 2026-07-03 09:24:24 +02:00
Moonchild 0eae777fd0 Issue #3132 Follow-up: only include gtk2xtbin.h on GTK targets.
PR #3133 caused build breakage on non-gtk platforms by unconditionally
including the header (because the def defaults to true in configure)

This also reorganizes the configure script to add a check for
MOZ_WIDGET_GTK (hoisted up a little) so the new configure entry is
simply ignored on non-GTK targets.
2026-07-03 09:19:54 +02:00
Moonchild 21743e0b7c Issue #3120 - Clean up NSIS scripting.
Removes Quicklaunch shortcut code, UAC guards (always supported on target
OSes) and LSP guards (ditto).

Resolves #3120
2026-06-13 12:19:53 +02:00
234 changed files with 4088 additions and 6167 deletions

No files matched your search

+1 -1
View File
@@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Clobber for Idle GC backout
Clobber for removal of network tickler
-5
View File
@@ -88,11 +88,6 @@ 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')
+1 -1
View File
@@ -81,7 +81,7 @@ $(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/
--track install_$(subst /,_,$*).track \
$(TOPOBJDIR)/$* \
-DAB_CD=en-US \
-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/browser/locales/en-US/profile \
-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/$(MOZ_BUILD_APP)/locales/en-US/profile \
$(ACDEFINES) \
install_$(subst /,_,$*)
@@ -143,7 +143,8 @@ TooltipsOverlay.prototype = {
// Font preview tooltip
if (type === VIEW_NODE_VALUE_TYPE && prop.property === "font-family") {
let value = prop.value.toLowerCase();
if (value !== "inherit" && value !== "unset" && value !== "initial" && value !== "revert") {
if (value !== "inherit" && value !== "unset" && value !== "initial" &&
value !== "revert" && value !== "revert-layer") {
tooltipType = TOOLTIP_FONTFAMILY_TYPE;
}
}
+1 -1
View File
@@ -3,6 +3,6 @@
# 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/.
JS_PREFERENCE_PP_FILES += [
JS_PREFERENCE_FILES += [
'devtools.js',
]
+2 -1
View File
@@ -10,7 +10,8 @@ const SPECIALVALUES = new Set([
"initial",
"inherit",
"unset",
"revert"
"revert",
"revert-layer"
]);
const {getCSSLexer} = require("devtools/shared/css/lexer");
@@ -99,7 +99,14 @@ const filterList = [
];
// Valid values that shouldn't be parsed for filters.
const SPECIAL_VALUES = new Set(["none", "unset", "initial", "inherit", "revert"]);
const SPECIAL_VALUES = new Set([
"none",
"unset",
"initial",
"inherit",
"revert",
"revert-layer"
]);
/**
* A CSS Filter editor widget used to add/remove/modify
+2 -1
View File
@@ -20,7 +20,8 @@ const SPECIALVALUES = new Set([
"inherit",
"transparent",
"unset",
"revert"
"revert",
"revert-layer"
]);
/**
@@ -43,7 +43,7 @@ class MachCommands(MachCommandBase):
if not os.path.exists(generated_dir):
generated_dir = mozpath.join(self.topsrcdir, 'platform',
'devtools/shared/css/generated')
else:
if not os.path.exists(generated_dir):
raise Exception('Directory expected at %s does not exist.' % generated_dir)
self.generated_dir = generated_dir
@@ -75,6 +75,9 @@ class MachCommands(MachCommandBase):
contents = subprocess.check_output([xpcshell_path, '-g', gre_path,
'-a', browser_path, script_path],
env = sub_env)
if not isinstance(contents, str):
contents = contents.decode('utf-8')
# Extract just the output between the delimiters as the xpcshell output can
# have extra output that we don't want.
contents = contents.split('DEVTOOLS_CSS_DB_DELIMITER')[1]
@@ -88,13 +91,13 @@ class MachCommands(MachCommandBase):
destination_path = mozpath.join(self.generated_dir,
'properties-db.js')
with open(js_template_path, 'rb') as handle:
with open(js_template_path, 'r') as handle:
js_template = handle.read()
preamble = '/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */\n\n'
contents = string.Template(js_template).substitute(substitutions)
with open(destination_path, 'wb') as destination:
with open(destination_path, 'w') as destination:
destination.write(preamble + contents)
print(('The database was successfully generated at ' + destination_path))
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -52,7 +52,7 @@ static RedirEntry kRedirMap[] = {
{ "config", "chrome://global/content/config.xul", 0 },
{ "console", "chrome://global/content/console.xul", 0 },
{
"credits", "https://net.riverstonerelay.jihad-browser/credits",
"credits", "http://www.palemoon.org/Contributors.shtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::MAKE_LINKABLE
},
-28
View File
@@ -199,7 +199,6 @@
#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"
@@ -1075,33 +1074,6 @@ 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));
+1 -1
View File
@@ -274,7 +274,7 @@ public:
}
template<typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type>
typename = typename std::enable_if_t<std::is_enum_v<T>>>
EnumTable(const char* aTag, T aValue)
: tag(aTag)
, value(static_cast<int16_t>(aValue))
+1
View File
@@ -444,6 +444,7 @@ GK_ATOM(handheldFriendly, "HandheldFriendly")
GK_ATOM(handler, "handler")
GK_ATOM(handlers, "handlers")
GK_ATOM(HARD, "HARD")
GK_ATOM(hasSelectorDependency, "has-selector-dependency")
GK_ATOM(hasSameNode, "has-same-node")
GK_ATOM(hbox, "hbox")
GK_ATOM(head, "head")
+5 -33
View File
@@ -787,12 +787,6 @@ 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 :(
@@ -828,12 +822,10 @@ 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();
@@ -854,8 +846,6 @@ 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) {
@@ -2388,11 +2378,6 @@ 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;
@@ -2902,16 +2887,9 @@ nsObjectLoadingContent::GetTypeOfContent(const nsCString& aMIMEType)
#ifdef MOZ_ENABLE_NPAPI
RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
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) {
if ((caps & eSupportPlugins) &&
pluginHost &&
pluginHost->HavePluginForType(aMIMEType, nsPluginHost::eExcludeNone)) {
// ShouldPlay will handle checking for disabled plugins
return eType_Plugin;
}
@@ -3508,15 +3486,9 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp
aReason = eFallbackClickToPlay;
uint32_t enabledState = nsIPluginTag::STATE_DISABLED;
nsresult jihadStateRv = pluginHost->GetStateForType(mContentType, nsPluginHost::eExcludeNone,
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;
}
+2 -2
View File
@@ -97,7 +97,7 @@ ToJSValue(JSContext* aCx,
return true;
}
// accept floating point types
// Accept floating point types
inline bool
ToJSValue(JSContext* aCx,
float aArgument,
@@ -106,7 +106,7 @@ ToJSValue(JSContext* aCx,
// Make sure we're called in a compartment
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
aValue.setNumber(aArgument);
aValue.set(JS_NumberValue(double(aArgument)));
return true;
}
+2 -10
View File
@@ -876,20 +876,12 @@ HTMLImageElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return HTMLImageElementBinding::Wrap(aCx, this, aGivenProto);
}
#ifdef DEBUG
nsIDOMHTMLFormElement*
HTMLImageElement::GetForm() const
{
return mForm;
}
#endif
void
HTMLImageElement::SetForm(nsIDOMHTMLFormElement* aForm)
{
NS_PRECONDITION(aForm, "Don't pass null here");
NS_ASSERTION(!mForm,
"We don't support switching from one non-null form to another.");
MOZ_ASSERT(!mForm && !HasFlag(ADDED_TO_FORM),
"We don't support switching from one non-null form to another.");
mForm = static_cast<HTMLFormElement*>(aForm);
}
+2 -3
View File
@@ -216,9 +216,8 @@ public:
SetHTMLAttr(nsGkAtoms::lowsrc, aLowsrc, aError);
}
#ifdef DEBUG
nsIDOMHTMLFormElement* GetForm() const;
#endif
HTMLFormElement* GetForm() const { return mForm; };
void SetForm(nsIDOMHTMLFormElement* aForm);
void ClearForm(bool aRemoveFromForm);
+11 -4
View File
@@ -1487,8 +1487,15 @@ nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttr
}
if (w && h && *w != 0 && *h != 0) {
nsCSSValue* aspect_ratio = aData->ValueForAspectRatio();
aspect_ratio->SetFloatValue((float(*w) / float(*h)), eCSSUnit_Number);
nsCSSValue width;
width.SetFloatValue(float(*w), eCSSUnit_Number);
nsCSSValue height;
height.SetFloatValue(float(*h), eCSSUnit_Number);
nsCSSValue ratio;
ratio.SetPairValue(width, height);
nsCSSValue autoValue;
autoValue.SetAutoValue();
aData->ValueForAspectRatio()->SetPairValue(autoValue, ratio);
}
}
}
@@ -1824,8 +1831,8 @@ void
nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm)
{
NS_PRECONDITION(aForm, "Don't pass null here");
NS_ASSERTION(!mForm,
"We don't support switching from one non-null form to another.");
MOZ_ASSERT(!mForm && !HasFlag(ADDED_TO_FORM),
"We don't support switching from one non-null form to another.");
// keep a *weak* ref to the form here
mForm = static_cast<HTMLFormElement*>(aForm);
+1 -1
View File
@@ -2455,7 +2455,7 @@ ContentChild::RecvUpdateWindow(const uintptr_t& aChildId)
if (parentInstance) {
// sync! update call to the plugin instance that forces the
// plugin to paint its child window.
parentInstance->CallUpdateWindow();
Unused << parentInstance->CallUpdateWindow();
}
return true;
#else
+2 -1
View File
@@ -367,12 +367,13 @@ public:
{
}
static const uint32_t MIN_RATE = 8000;
static const uint32_t MAX_RATE = 640000;
bool IsValid() const override
{
return mChannels > 0 && mChannels <= MAX_AUDIO_CHANNELS
&& mRate > 0 && mRate <= MAX_RATE;
&& mRate >= MIN_RATE && mRate <= MAX_RATE;
}
AudioInfo* GetAsAudioInfo() override
+6
View File
@@ -179,12 +179,18 @@ IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
aFrame.width * aFrame.height != 0 &&
aPicture.width > 0 &&
aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.x >= 0 &&
aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.XMost() <= aFrame.width &&
aPicture.height > 0 &&
aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.y >= 0 &&
aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
aPicture.YMost() <= aFrame.height &&
aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
aPicture.width * aPicture.height != 0 &&
aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
+1 -1
View File
@@ -439,7 +439,7 @@ private:
Maybe<size_t> mInitOffset;
Maybe<size_t> mMediaOffset;
bool mValid = true;
char mLastInvalidBox[5];
char mLastInvalidBox[5]{};
};
public:
+14 -5
View File
@@ -20,6 +20,7 @@
#include "WebMBufferedParser.h"
#include "gfx2DGlue.h"
#include "mozilla/Atomics.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/SharedThreadPool.h"
@@ -352,19 +353,27 @@ WebMDemuxer::ReadMetadata()
mInfo.mVideo.mExtraData = new MediaByteBuffer(length);
mInfo.mVideo.mExtraData->AppendElements(data, length);
break;
}
}
default:
NS_WARNING("Unknown WebM video codec");
return NS_ERROR_FAILURE;
}
// Picture region, taking into account cropping, before scaling
// to the display size.
unsigned int cropH = params.crop_right + params.crop_left;
unsigned int cropV = params.crop_bottom + params.crop_top;
CheckedInt<uint32_t> cropH = CheckedInt<uint32_t>(params.crop_left) + params.crop_right;
CheckedInt<uint32_t> cropV = CheckedInt<uint32_t>(params.crop_top) + params.crop_bottom;
if (!cropH.isValid() || !cropV.isValid() ||
cropH.value() >= params.width || cropV.value() >= params.height) {
WEBM_DEBUG("Invalid crop values left: {} right: {} top: {} bottom: {}",
params.crop_left, params.crop_right, params.crop_top,
params.crop_bottom);
// Ignore invalid crop parameters.
continue;
}
nsIntRect pictureRect(params.crop_left,
params.crop_top,
params.width - cropH,
params.height - cropV);
params.width - cropH.value(),
params.height - cropV.value());
// If the cropping data appears invalid then use the frame data
if (pictureRect.width <= 0 ||
-160
View File
@@ -577,166 +577,6 @@ 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
+1 -15
View File
@@ -341,10 +341,6 @@ 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;
}
@@ -390,7 +386,6 @@ 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;
}
@@ -1944,16 +1939,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
}
case NPNVSupportsWindowless: {
// 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) || \
#if defined(XP_WIN) || defined(XP_MACOSX) || \
(defined(MOZ_X11) && defined(MOZ_WIDGET_GTK))
*(NPBool*)result = true;
#else
+1 -69
View File
@@ -843,9 +843,6 @@ 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;
@@ -964,12 +961,7 @@ nsPluginHost::TrySetUpPluginInstance(const nsACString &aMimeType,
#endif
RefPtr<nsNPAPIPlugin> 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);
GetPlugin(aMimeType, getter_AddRefs(plugin));
if (!plugin) {
return NS_ERROR_FAILURE;
}
@@ -995,7 +987,6 @@ 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);
@@ -4210,62 +4201,3 @@ 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
}
-7
View File
@@ -274,13 +274,6 @@ 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;
+14 -216
View File
@@ -569,75 +569,6 @@ 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;
}
@@ -2599,122 +2530,6 @@ 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;
}
@@ -3162,40 +2977,23 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
if (!mWidget) {
// native (single process)
mWidget = do_CreateInstance(kWidgetCID, &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;
}
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) {
mWidget->EnableDragDrop(true);
mWidget->Show(false);
mWidget->Enable(false);
}
mWidget->EnableDragDrop(true);
mWidget->Show(false);
mWidget->Enable(false);
}
if (mPluginFrame) {
+1 -11
View File
@@ -133,12 +133,6 @@ 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?");
@@ -238,12 +232,8 @@ BrowserStreamChild::Deliver()
if (kStreamOpen == mStreamStatus)
mStreamStatus = NPRES_DONE;
NPError dsrv = mInstance->mPluginIface
(void) 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?");
-74
View File
@@ -1,74 +0,0 @@
/* -*- 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"
-23
View File
@@ -125,29 +125,6 @@ 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
// **********************
+4 -845
View File
@@ -31,7 +31,6 @@ using mozilla::gfx::SharedDIBSurface;
#endif
#endif
#include "gfxSharedImageSurface.h"
#include <dlfcn.h> // Jihad R7: reach the plugin's own glib for npPalmEventLoopValue
#include "gfxUtils.h"
#include "gfxAlphaRecovery.h"
@@ -51,61 +50,6 @@ using namespace mozilla::gfx;
using namespace mozilla::widget;
using namespace std;
// Defined next to JihadPumpGlib, far below; declared here because DoNPP_New starts it.
static void JihadStartGlibPump(PluginInstanceChild* aInstance);
// The instance the host clock repaints, and the count of repaints the PLUGIN asked for. Both
// live here rather than on the instance because the pump is a process-wide free function.
// Cleared in ~PluginInstanceChild so the clock cannot outlive what it points at.
static PluginInstanceChild* sJihadPalmInstance = nullptr;
static uint64_t gJihadPalmDrawEvents = 0;
// Defined in JihadNPNInterpose.cpp, which deliberately includes no NPAPI headers.
extern "C" unsigned gJihadNPNInvalidateRectCalls;
// ── WHICH CALL SITE ACTUALLY DRIVES PLUGIN DRAWS (Jihad, addons R7) ──────────────────────
//
// Three sessions have now attributed the plugin's draw rate to a specific clock by READING the
// code, and every attribution was wrong. Patch 0029 finally put a counter on the request side
// and the numbers did not agree with any of them: 3988 draws against 3072 host requests, with
// the child's own timer provably stood down (one "frame clock -> HOST" transition, no fallback)
// and Flash provably not self-invalidating (gJihadNPNInvalidateRectCalls stays 0). So roughly a
// quarter of all draws come from a fourth place, and at an 8 ms tick that share DOMINATES.
//
// Every path to a draw funnels through AsyncShowPluginFrame, so tagging its call sites answers
// the question directly rather than by elimination. Reported next to `palm draw`, which is the
// count these have to add up to. Declared up here with the other file statics because the first
// tagged site is in AnswerNPP_SetWindow, well above the frame-clock block further down.
enum {
kJihadShowSetWindow = 0, // AnswerNPP_SetWindow / AsyncSetWindow, accumulated rect
kJihadShowInvalidate, // InvalidateRect reached from OUR host repaint tick
kJihadShowPluginSelf, // InvalidateRect reached from the PLUGIN's own NPN_InvalidateRect
kJihadShowRetry, // InvalidateRectDelayed's retry when ShowPluginFrame refused
kJihadShowBgUpdate, // RecvUpdateBackground, background changed
kJihadShowBgUpdateTail, // RecvUpdateBackground, unconditional tail
kJihadShowBgDestroy, // background destroyer
kJihadShowSites
};
static uint32_t gJihadShowFrom[kJihadShowSites];
static const char* const kJihadShowNames[kJihadShowSites] = {
"setwin", "hostinval", "plugininval", "retry", "bgupd", "bgtail", "bgdestroy"
};
// True only while JihadPalmRepaintTick's own InvalidateRect call is on the stack, so the two
// producers can be told apart AT the shared funnel.
//
// WHY THIS EXISTS, and it invalidates a premise the port has carried since patch 0026:
// gJihadNPNInvalidateRectCalls — the counter used to decide "the plugin never asks to be
// repainted, so the host must drive it" — is STRUCTURALLY BLIND. It lives in
// JihadNPNInterpose.cpp and only counts calls that bind the NPN_InvalidateRect SYMBOL. An
// ordinary NPAPI plugin calls through the NPNetscapeFuncs table instead, which
// PluginModuleChild fills with its own _invalidaterect (PluginModuleChild.cpp:986); that
// forwards straight to InstCast(aNPP)->InvalidateRect (:1302-1310) and never touches the
// interpose. So the counter can read 0 for the whole run while the plugin invalidates
// constantly — which is exactly what was measured: 1344 host requests against 2662 invalidates
// with the child's timer provably off (one "frame clock -> HOST", no fallback).
static bool sJihadInHostRepaint = false;
#ifdef MOZ_WIDGET_GTK
#include <gtk/gtk.h>
@@ -249,8 +193,6 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
#endif
, mAccumulatedInvalidRect(0,0,0,0)
, mIsTransparent(false)
, mJihadSentPalmGainFocus(false)
, mJihadInputEventsLogged(0)
, mSurfaceType(gfxSurfaceType::Max)
, mPendingPluginCall(false)
, mDoAlphaExtraction(false)
@@ -289,11 +231,6 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
PluginInstanceChild::~PluginInstanceChild()
{
// The host repaint clock outlives any single instance (it is bound to the process-wide
// GMainLoop), so it must not be left holding this pointer.
if (sJihadPalmInstance == this) {
sJihadPalmInstance = nullptr;
}
#if defined(OS_WIN)
NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
@@ -323,65 +260,6 @@ PluginInstanceChild::~PluginInstanceChild()
#endif
}
// ── THE webOS PLUGIN ABI (Jihad, addons R7) ───────────────────────────────────────────────
//
// None of this is in our npapi.h; it is Palm's XP_WEBOS block, and every layout below was
// checked TWICE — against WebKit/Source/WebCore/plugins/npapi.h, and against the machine code
// of the host that actually shipped on the device. That host is NOT the Qt one in our WebKit
// checkout (PluginViewQt.cpp passes a QPainter* and Flash has no Qt dependency at all); it is
// WebCore::PluginView compiled from PluginViewPalm.cpp into /usr/lib/libWebKitLuna.so, which
// is unstripped on the device and can simply be read. Offsets cited below are into that
// library. Get these wrong and the plugin reads garbage, silently.
//
// The event union itself now lives in npapi.h as the real `NPEvent` for this platform, so the
// parent (nsPluginInstanceOwner::ProcessEvent) and the child cannot drift and the type survives
// the NPRemoteEvent memcpy. What used to be here was a TRUNCATED private mirror —
// systemEvent held only `type`, which is enough for gain-focus and silently wrong for a
// spotlight event, whose rect lives in the four fields the mirror did not have.
//
// These assertions are the actual contract. Each offset was read out of the shipped host's
// machine code, and a mismatch here means the plugin reads a different field than the one
// written — a failure with no symptom other than wrong behaviour.
#if defined(XP_UNIX) && defined(MOZ_WIDGET_HEADLESS)
static_assert(sizeof(NPEvent) == 48, "webOS NPEvent is 48 bytes");
static_assert(offsetof(NPEvent, data) == 4, "eventType is a 4-byte field at 0");
// PluginView::handleMouseEvent (libWebKitLuna 0x4e9458/0x4e9460/0x4e9464) stores x, y and
// modifiers to [sp+4], [sp+8], [sp+12] with eventType at [sp].
static_assert(offsetof(NPEvent, data.penEvent.xCoord) == 4, "penEvent.xCoord @4");
static_assert(offsetof(NPEvent, data.penEvent.yCoord) == 8, "penEvent.yCoord @8");
static_assert(offsetof(NPEvent, data.penEvent.modifiers) == 12, "penEvent.modifiers @12");
// PluginView::handleKeyboardEvent (0x4e8ebc/0x4e8e80/0x4e8ef4/0x4e8f04).
static_assert(offsetof(NPEvent, data.keyEvent.chr) == 4, "keyEvent.chr @4");
static_assert(offsetof(NPEvent, data.keyEvent.modifiers) == 8, "keyEvent.modifiers @8");
static_assert(offsetof(NPEvent, data.keyEvent.rawkeyCode) == 12, "keyEvent.rawkeyCode @12");
static_assert(offsetof(NPEvent, data.keyEvent.rawModifier) == 16, "keyEvent.rawModifier @16");
// PluginView::handlePluginSpotlightStart (0x4e6cec-0x4e6d20) writes 0x100 at [sp], 11 at
// [sp+4], then left/right/top/bottom at [sp+12]/[sp+16]/[sp+20]/[sp+24] — note it never
// initialises `value` at [sp+8].
static_assert(offsetof(NPEvent, data.systemEvent.type) == 4, "systemEvent.type @4");
static_assert(offsetof(NPEvent, data.systemEvent.viewLeft) == 12, "systemEvent.viewLeft @12");
static_assert(offsetof(NPEvent, data.systemEvent.viewRight) == 16, "systemEvent.viewRight @16");
static_assert(offsetof(NPEvent, data.systemEvent.viewTop) == 20, "systemEvent.viewTop @20");
static_assert(offsetof(NPEvent, data.systemEvent.viewBottom) == 24, "systemEvent.viewBottom @24");
// PluginView::paint (0x4e7e30-0x4e7e44) stores platformContext() at frame+52 and the four
// scaled dst coords at +56/+60/+64/+68, i.e. +24/+28/+32/+36 inside the draw member.
static_assert(offsetof(NPEvent, data.drawEvent.graphicsContext) == 28, "drawEvent.gc @28");
static_assert(offsetof(NPEvent, data.drawEvent.dstLeft) == 32, "drawEvent.dstLeft @32");
#endif // XP_UNIX && MOZ_WIDGET_HEADLESS
// NPWindow.window for a webOS plugin. Palm's host never calls setwindow without one:
// PluginView::setNPWindowRect (libWebKitLuna 0x4e74e0) builds it as a STACK LOCAL, points
// m_npWindow.window at it (0x4e7570 `str sp, [r4, #520]`), calls setwindow, and immediately
// re-nulls the pointer (0x4e7614) so the dangling stack address is never left behind.
// Field offsets read straight off that function: visible is a byte at +0 (0x4e755c
// `strbne r3, [sp]`), bpp is 32 at +4 (0x4e75dc `mov ip,#32; str ip,[sp,#4]`), and scaleFactor
// is the double from ScrollView::getScale() at +8 (0x4e7588 `strd r6, [sp, #8]`).
struct JihadNpPalmWindow {
bool visible;
uint32_t bpp;
double scaleFactor;
};
NPError
PluginInstanceChild::DoNPP_New()
{
@@ -408,44 +286,6 @@ PluginInstanceChild::DoNPP_New()
return rv;
}
// WEBOS EVENT LOOP (Jihad, addons R7).
//
// Palm's own host does this right after NPP_New and before anything else
// (WebKit/Source/WebCore/plugins/qt/PluginViewQt.cpp, PluginView::platformStart):
// it hands the plugin a GMainLoop* through npPalmEventLoopValue. A webOS plugin drives
// its own frame scheduling off that loop, so a plugin that never receives one is a
// plugin that instantiates and then never asks to paint — exactly the symptom this port
// had.
//
// glib is not linked into this process; it arrives as a dependency of the plugin, so it
// can only be reached by name once the plugin is loaded — which it is by now. A plugin
// that does not know the variable just returns an error, so this is safe for any plugin.
if (mPluginIface->setvalue) {
static void* sMainLoop = nullptr;
if (!sMainLoop) {
if (void* glib = dlopen("libglib-2.0.so.0", RTLD_NOW | RTLD_GLOBAL)) {
typedef void* (*GMainLoopNewFn)(void*, int);
GMainLoopNewFn newLoop = (GMainLoopNewFn)dlsym(glib, "g_main_loop_new");
if (newLoop) {
sMainLoop = newLoop(nullptr, 1 /* is_running */);
}
}
}
if (sMainLoop) {
const uint32_t kNpPalmEventLoopValue = 10000;
NPError lerr = mPluginIface->setvalue(npp, (NPNVariable)kNpPalmEventLoopValue,
sMainLoop);
fprintf(stderr, "[jihad-npapi-child] npPalmEventLoopValue loop=%p err=%d\n",
sMainLoop, (int)lerr);
// The plugin now has a loop to hang sources off; start running it. See
// JihadStartGlibPump for why this cannot wait for the first paint.
JihadStartGlibPump(this);
} else {
fprintf(stderr, "[jihad-npapi-child] npPalmEventLoopValue: no glib main loop\n");
}
fflush(stderr);
}
Initialize();
#if defined(XP_MACOSX) && defined(__i386__)
@@ -877,8 +717,8 @@ PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
// on the chrome process. This is because our audio
// API calls should run the chrome proc, not content.
NPError rv = NPERR_GENERIC_ERROR;
PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
if (chromeInstance) {
PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
if (chromeInstance) {
rv = chromeInstance->PluginRequiresAudioDeviceChanges(this,
(NPBool)(intptr_t)aValue);
}
@@ -1124,35 +964,6 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
else
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
// 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 R7): the only field-visible evidence that host input reached the plugin.
// A closed-source plugin acknowledges nothing — Flash returns its `handled` flag before it
// even looks at the event (its NPP_HandleEvent sets the return, then dispatches), so this
// line proves DELIVERY and nothing more. Capped, because a pen-move stream would otherwise
// flood the daemon log, which is the only channel this device has.
if (mJihadInputEventsLogged < 24) {
mJihadInputEventsLogged++;
if (evcopy.eventType == npPalmKeyDownEvent ||
evcopy.eventType == npPalmKeyUpEvent ||
evcopy.eventType == npPalmKeyPressEvent) {
fprintf(stderr, "[jihad-npapi-child] palm key 0x%x chr=%d raw=%d mods=0x%x "
"handled=%d\n",
(unsigned)evcopy.eventType, (int)evcopy.data.keyEvent.chr,
(int)evcopy.data.keyEvent.rawkeyCode,
(unsigned)evcopy.data.keyEvent.modifiers, (int)*handled);
} else {
fprintf(stderr, "[jihad-npapi-child] palm event 0x%x at %d,%d mods=0x%x "
"handled=%d\n",
(unsigned)evcopy.eventType, (int)evcopy.data.penEvent.xCoord,
(int)evcopy.data.penEvent.yCoord,
(unsigned)evcopy.data.penEvent.modifiers, (int)*handled);
}
fflush(stderr);
}
#endif
#ifdef XP_MACOSX
// Release any reference counted objects created in the child process.
if (evcopy.type == NPCocoaEventKeyDown ||
@@ -1638,8 +1449,6 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
if (mPluginIface->setwindow)
(void) mPluginIface->setwindow(&mData, &mWindow);
#elif defined(MOZ_WIDGET_HEADLESS)
// Jihad headless: no windowed plugins; nothing to do.
#else
# error Implement me for your OS
#endif
@@ -3018,12 +2827,6 @@ PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
AssertPluginThread();
AutoStackHelper guard(this);
NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
// JIHAD DIAGNOSTIC (R7): does content actually reach the plugin? A plugin that was
// never handed its stream draws nothing for a completely different reason than a
// plugin that cannot draw, and on screen the two are identical (an empty box).
fprintf(stderr, "[jihad-npapi-child] NPP_NewStream mime=%s seekable=%d rv=%d stype=%d\n",
mimeType.get(), (int)seekable, (int)rv, (int)*stype);
fflush(stderr);
return rv;
}
@@ -3614,7 +3417,6 @@ PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
#endif
if (!mAccumulatedInvalidRect.IsEmpty()) {
gJihadShowFrom[kJihadShowSetWindow]++;
AsyncShowPluginFrame();
}
}
@@ -3939,467 +3741,10 @@ PluginInstanceChild::UpdateWindowAttributes(bool aForceSetWindow)
mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
if (mPluginIface->setwindow) {
// WEBOS: hand the plugin an NpPalmWindow, exactly as Palm's host does.
//
// Out of the box this embedder passes NPWindow.window = nullptr for every windowless
// plugin (DoAsyncSetWindow hard-nulls it, and the MOZ_WIDGET_HEADLESS branch of
// AnswerNPP_SetWindow is a no-op), which is correct for an X11 windowless plugin — the
// slot means "native window handle" there, and there isn't one. On webOS the same slot
// means something completely different: it carries the plugin's visibility, the
// surface depth and the page scale. A webOS plugin told window=NULL has been told
// nothing about the surface it is supposed to draw into.
//
// Scoped to the call and re-nulled afterwards, because this is a stack address and
// Palm's host is careful about exactly that (libWebKitLuna 0x4e7570 / 0x4e7614).
//
// scaleFactor is 1.0 rather than the card's fit-zoom: Palm passes ScrollView::getScale()
// because its plugin drew into the page's own scaled surface, whereas here the plugin
// owns an unscaled CSS-sized buffer and the compositor applies the zoom afterwards.
JihadNpPalmWindow palmWindow;
palmWindow.visible = true;
palmWindow.bpp = 32;
palmWindow.scaleFactor = 1.0;
void* savedWindow = mWindow.window;
mWindow.window = &palmWindow;
mPluginIface->setwindow(&mData, &mWindow);
mWindow.window = savedWindow;
static int sLoggedSetWindow = 0;
if (sLoggedSetWindow < 3) {
sLoggedSetWindow++;
fprintf(stderr, "[jihad-npapi-child] NPP_SetWindow %dx%d at %d,%d type=%d "
"palmWindow{visible=1 bpp=32 scale=1.0}\n",
(int)mWindow.width, (int)mWindow.height, (int)mWindow.x, (int)mWindow.y,
(int)mWindow.type);
fflush(stderr);
}
// WEBOS: the last thing Palm's PluginView::platformStart does is tell the plugin it has
// focus — handlePageGainFocus (libWebKitLuna 0x4e6df0) builds
// {eventType = 0x100 (npPalmSystemEvent), systemEvent.type = 3 (npPalmGainFocusEvent)}
// and dispatches it. A plugin that is never told it is focused and visible has no
// reason to start animating.
//
// Only gain-focus: Flash's own system-event dispatcher (libflashplayer.so 0x44da0)
// routes npPalmPageLoadingEvent(8), npPalmPageLoadCompleteEvent(9) and
// npPalmViewPortChangedEvent(10) to a bare `mov r0,#0; pop {pc}`, so those three are
// inert in this build and sending them would prove nothing.
//
// After setwindow, not before, and once: the plugin needs its geometry first, and
// UpdateWindowAttributes runs on every resize and every scroll.
if (mPluginIface->event && !mJihadSentPalmGainFocus) {
mJihadSentPalmGainFocus = true;
NPEvent ev;
memset(&ev, 0, sizeof(ev));
ev.eventType = npPalmSystemEvent;
ev.data.systemEvent.type = npPalmGainFocusEvent;
int16_t handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&ev));
fprintf(stderr, "[jihad-npapi-child] npPalmSystemEvent gainFocus handled=%d\n",
(int)handled);
fflush(stderr);
}
}
}
// Run any work the plugin scheduled on glib.
//
// A webOS plugin is handed a GMainLoop through npPalmEventLoopValue and hangs its timers and
// idle sources off it — but this process's main loop is chromium's MessageLoop, so unless
// glib is pumped explicitly those sources NEVER RUN. The visible symptom is a plugin that
// initialises, clears its stage, and then sits on frame zero forever: it is not stuck, it is
// simply never being given the chance to advance.
//
// Bounded so a busy plugin cannot starve the paint it is being pumped for.
static int
JihadPumpGlib(int aMaxIterations)
{
typedef int (*GMainContextIterationFn)(void*, int);
typedef int (*GMainContextPendingFn)(void*);
static bool sTried = false;
static GMainContextIterationFn sIterate = nullptr;
static GMainContextPendingFn sPending = nullptr;
if (!sTried) {
sTried = true;
if (void* glib = dlopen("libglib-2.0.so.0", RTLD_NOW | RTLD_GLOBAL)) {
sIterate = (GMainContextIterationFn)dlsym(glib, "g_main_context_iteration");
sPending = (GMainContextPendingFn)dlsym(glib, "g_main_context_pending");
}
fprintf(stderr, "[jihad-npapi-child] glib pump iterate=%p pending=%p\n",
(void*)sIterate, (void*)sPending);
fflush(stderr);
}
if (!sIterate) {
return 0;
}
int n = 0;
while (n < aMaxIterations) {
if (sPending && !sPending(nullptr)) {
break;
}
sIterate(nullptr, 0 /* may_block = FALSE */);
n++;
}
return n;
}
// ── RUN THE PLUGIN'S EVENT LOOP CONTINUOUSLY (Jihad, addons R7) ───────────────────────────
//
// Pumping glib only just before a paint deadlocks a webOS plugin, because its startup is
// asynchronous: Flash registers com.palm.flashgraphics on the LS2 bus, attaches that socket to
// the loop it was handed, and then waits on round-trips to com.palm.systemservice
// (getPreferences), com.palm.display (status) and com.palm.db (find) before it starts its frame
// clock. Until the clock runs it never calls NPN_InvalidateRect, so nothing ever asks it to
// paint — and the only pump was on the paint path. No paint, no pump; no pump, no paint.
//
// So the pump has to be independent of painting. This reposts itself on the child's own
// MessageLoop, which is the SAME thread every other NPAPI call into the plugin runs on — the
// arrangement Palm's host gave it, since BrowserServer's main loop simply WAS a GMainLoop.
// g_main_loop_run() on a dedicated thread would be the wrong shape: the plugin's timer
// callbacks would then fire off the NPAPI thread and reenter us from it.
//
// MEASURED: before the LS2 role allowed com.palm.flashgraphics, LSRegisterPalmService failed
// with -1027, nothing was ever attached to the loop, and g_main_context_pending() returned
// FALSE forever. That is fixed in packaging/gen-variant-scripts.sh; the pump only has anything
// to run because of it.
static const int kJihadGlibPumpIntervalMs = 16; // ~60Hz, the rate a frame clock wants
static const int kJihadGlibPumpMaxIters = 20; // bounded: never starve the MessageLoop
// Host-driven repaint divisor: one repaint every Nth 16 ms tick. 2 is ~30 Hz, which is what
// ordinary Flash content is authored at; 4 was ~15 Hz and was the measured ceiling on the
// WHOLE pipeline — device 2026-08-10, single instance: plugin draw 21.6-24 fps and RecvShow
// 21.3-24.2 fps while the daemon was already compositing 28.4-29.4 fps, i.e. the host was
// asking for frames more slowly than it could consume them.
//
// Env-tunable because 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. Clamped: 0 or a negative divisor
// would be a modulo-by-zero, and anything past 8 is slower than the pump itself.
// Disable the child's OWN repaint timer entirely. $JIHAD_PLUGIN_SELF_DRIVE=0.
//
// This exists to isolate ONE producer. Measured 2026-08-10, there are two: our host pull and the
// plugin invalidating itself (~26/s and ~23/s). Turning off only the daemon's pull
// (JIHAD_PLUGIN_PULL=0) does NOT isolate the plugin, because the child's host-drive lease then
// expires and this timer takes over — that run reproduces patch 0026 rather than answering the
// question, and it measured WORSE than the pull on both rate (24-29 vs 30-33 fps) and spread.
// With both switches off the plugin's own NPN_InvalidateRect is the only frame source left, which
// is the experiment that decides whether a host clock should exist here at all.
static bool JihadChildSelfDriveEnabled()
{
static int s_on = -1;
if (s_on < 0) {
const char* e = getenv("JIHAD_PLUGIN_SELF_DRIVE");
s_on = (e && *e && atoi(e) == 0) ? 0 : 1; // default ON; only an explicit 0 disables
fprintf(stderr, "[jihad-npapi-child] self-drive timer %s\n", s_on ? "enabled" : "DISABLED");
fflush(stderr);
}
return s_on != 0;
}
static int JihadRepaintEveryTicks()
{
static int sDiv = 0;
if (!sDiv) {
sDiv = 2;
const char* e = getenv("JIHAD_PLUGIN_REPAINT_DIV");
if (e && *e) {
int v = atoi(e);
if (v >= 1 && v <= 8) sDiv = v;
}
fprintf(stderr, "[jihad-npapi-child] host repaint divisor=%d (~%d Hz)\n",
sDiv, 1000 / (kJihadGlibPumpIntervalMs * sDiv));
fflush(stderr);
}
return sDiv;
}
// ── REPAINT A webOS PLUGIN FROM THE HOST'S CLOCK (Jihad, addons R7) ──────────────────────
//
// Gecko paints a windowless plugin only when something invalidates it, and for an ordinary
// NPAPI plugin that is the plugin itself calling NPN_InvalidateRect. A webOS plugin is under
// no such obligation, because the host it was written for never made it one: Palm's
// PluginView::paint (libWebKitLuna 0x4e7a3c) issues npPalmDrawEvent every time the PAGE
// paints, and BrowserServer's compositor painted continuously. Flash on that host could sit
// on its frame clock and simply expect to be asked.
//
// So this drives the repaint until the plugin proves it drives itself, then gets out of the
// way — a plugin that calls NPN_InvalidateRect gets Gecko's normal damage-driven painting and
// none of this cost. Measured: with no host clock, Flash produced exactly 3 draw events in
// 30 s and 0 invalidates.
void
PluginInstanceChild::JihadPalmRepaintTick()
{
if (!mLayersRendering || mWindow.width <= 0 || mWindow.height <= 0) {
return;
}
NPRect windowRect = { 0, 0,
uint16_t(mWindow.height), uint16_t(mWindow.width) };
sJihadInHostRepaint = true;
InvalidateRect(&windowRect);
sJihadInHostRepaint = false;
}
// ── HOST-DRIVEN FRAME CLOCK (Jihad, addons R7) ───────────────────────────────────────────
//
// When the daemon is pulling (it calls jihad_plugin_request_frame after each card frame it
// publishes), the timer above must get out of the way, or the plugin has TWO drivers and the
// extra draws are exactly the drift the pull exists to remove.
//
// The handoff is timestamp-based rather than a latch because there is no unregister: the daemon
// can stop asking at any moment — the card is backgrounded (BrowserPageGoanna::freeze returns
// early from maybePaint), the page navigates, the daemon is stopped or crashes — and a latched
// plugin would then never paint again. So host-driven is a LEASE that must be renewed. If no
// request arrives for kJihadHostDriveLeaseMs the timer resumes on its own.
//
// THE LEASE MUST EXCEED THE DAEMON'S WORST-CASE GAP BETWEEN REQUESTS, and the first version of
// this got that wrong in a way that is worth keeping written down, because the symptom looked
// like a mysterious third party rather than like a bug.
//
// The daemon does not ask again while a request is outstanding; it waits out
// kJihadPluginReqTimeoutMs (250 ms) before re-asking. The lease here was 150 ms — SHORTER — so a
// single late frame guaranteed the lease expired, the child resumed self-driving, and the two
// clocks this patch exists to collapse were both running again. Measured on device before the
// fix: the child logged HOST -> SELF -> HOST, and the invalidate counter split almost exactly in
// half, 1856 requests from the host against 1846 from the child's own timer, summing to the 3702
// total. The 23% of "unexplained" draws that sent an earlier session hunting through
// RecvUpdateBackground were this, and the background paths were measured at exactly ZERO.
//
// So the invariant, not a tuned number: lease > request timeout + one grid period + margin.
// 500 ms is 250 + 33 + slack. A card that really stopped publishing still resumes self-drawing
// in half a second, which is well inside what a resume can absorb.
static const uint32_t kJihadHostDriveLeaseMs = 500;
static uint32_t sJihadLastHostRequestMs = 0;
static bool sJihadEverHostDriven = false;
static bool JihadHostIsDrivingFrames()
{
bool driving = false;
if (sJihadEverHostDriven) {
uint32_t nowMs = uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
// Unsigned subtraction, so the 32-bit PR_IntervalNow wrap is handled without a signed
// compare that would read as "lease expired" for one interval every wrap.
driving = (nowMs - sJihadLastHostRequestMs) < kJihadHostDriveLeaseMs;
}
// LOG EVERY TRANSITION. A watchdog that silently resumes self-driving would make a BROKEN
// pull look like "no change" in the frame numbers instead of "the feature is off" — the same
// trap family as an empty log passing every grep, and a wedged plugin reading as a pacing
// regression. Both of those cost real time in this tree already; this one is cheap to close.
static bool sWas = false;
if (driving != sWas) {
sWas = driving;
fprintf(stderr, "[jihad-npapi-child] frame clock -> %s\n",
driving ? "HOST (daemon pull)" : "SELF (timer; host lease expired)");
fflush(stderr);
}
return driving;
}
bool
PluginInstanceChild::RecvJihadRequestFrame()
{
sJihadEverHostDriven = true;
sJihadLastHostRequestMs = uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
// A plugin that drives itself gets Gecko's normal damage-driven painting, exactly as the
// timer path decides below; asking such a plugin for frames would double its draw rate.
if (gJihadNPNInvalidateRectCalls != 0) {
return true;
}
static uint64_t sHostFrames = 0;
static uint32_t sReportMs = 0;
if (!(++sHostFrames % 64)) {
uint32_t nowMs = uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
if (sReportMs) {
fprintf(stderr, "[jihad-npapi-child] host-driven: 64 requests in %ums\n",
nowMs - sReportMs);
fflush(stderr);
}
sReportMs = nowMs;
}
JihadPalmRepaintTick();
return true;
}
namespace {
class JihadGlibPumpTask final : public Runnable
{
public:
NS_IMETHOD Run() override
{
int n = JihadPumpGlib(kJihadGlibPumpMaxIters);
// The pump itself keeps running at 16 ms whatever drives the frames — Flash's LS2
// socket, its timers and its audio sources all live on this GMainLoop, and starving
// them is what deadlocked startup before patch 0026. Only the REPAINT defers.
static uint64_t sRepaintTicks = 0;
if (sJihadPalmInstance && gJihadNPNInvalidateRectCalls == 0 &&
JihadChildSelfDriveEnabled() && !JihadHostIsDrivingFrames() &&
(++sRepaintTicks % JihadRepaintEveryTicks()) == 0) {
sJihadPalmInstance->JihadPalmRepaintTick();
}
// One-shot liveness, because "the plugin is idle" and "the pump died" look
// identical on screen. Report the first tick that actually ran a source.
static bool sSawFirstSource = false;
static uint64_t sTicks = 0;
sTicks++;
if (n && !sSawFirstSource) {
sSawFirstSource = true;
fprintf(stderr, "[jihad-npapi-child] glib pump: first source ran on tick %llu\n",
(unsigned long long)sTicks);
fflush(stderr);
}
MessageLoop::current()->PostDelayedTask(
RefPtr<Runnable>(new JihadGlibPumpTask()).forget(),
kJihadGlibPumpIntervalMs);
return NS_OK;
}
};
} // namespace
// Idempotent: several plugin instances share one GMainLoop, so they share one pump.
static void
JihadStartGlibPump(PluginInstanceChild* aInstance)
{
sJihadPalmInstance = aInstance;
static bool sStarted = false;
if (sStarted) {
return;
}
MessageLoop* loop = MessageLoop::current();
if (!loop) {
fprintf(stderr, "[jihad-npapi-child] glib pump: no MessageLoop, not starting\n");
fflush(stderr);
return;
}
sStarted = true;
fprintf(stderr, "[jihad-npapi-child] glib pump: starting, every %dms\n",
kJihadGlibPumpIntervalMs);
fflush(stderr);
loop->PostDelayedTask(RefPtr<Runnable>(new JihadGlibPumpTask()).forget(),
kJihadGlibPumpIntervalMs);
}
// ── PIRANHA CONTEXT FOR A WEBOS PLUGIN (Jihad, addons R7) ────────────────────────────────
//
// webOS plugins that answer npPalmUseGraphicsContext=true expect NpPalmDrawEvent to carry a
// Piranha PGContext* rather than a raster pointer. Piranha ships on the device inside
// libWebKitLuna.so (PGContext/PGSurface) and libPiranha.so; neither is linked into this
// process, so everything is resolved by dlsym on the mangled names — no webOS headers, and
// nothing breaks on a desktop build where the libraries simply are not there.
//
// Feasibility was established separately before any of this was written; see
// render/goanna/test/piranha_offscreen_probe.c, which does exactly this sequence in a bare
// process and confirms the pixels land in caller-owned memory.
//
// The context is cached per (buffer,size) because a plugin repaints constantly and both
// objects would otherwise leak once per frame.
static void*
JihadPiranhaContextFor(unsigned char* aData, int aWidth, int aHeight, int aStride)
{
// PGSurface::wrap() has no stride parameter, so it can only describe tightly packed
// rows. Refuse rather than hand Piranha a buffer it would misread into a sheared image.
if (!aData || aWidth <= 0 || aHeight <= 0 || aStride != aWidth * 4) {
static bool sWarnedStride = false;
if (!sWarnedStride) {
sWarnedStride = true;
fprintf(stderr, "[jihad-npapi-child] piranha: stride %d != %d*4, not wrapping\n",
aStride, aWidth);
fflush(stderr);
}
return nullptr;
}
typedef void* (*PGSurfaceWrapFn)(unsigned, unsigned, const unsigned char*, bool);
typedef void* (*PGContextCreateFn)(void);
typedef void (*PGContextSetSurfaceFn)(void*, void*);
static bool sTried = false;
static PGSurfaceWrapFn sWrap = nullptr;
static PGContextCreateFn sCreate = nullptr;
static PGContextSetSurfaceFn sSetSurface = nullptr;
if (!sTried) {
sTried = true;
// g_thread_init() MUST run before libWebKitLuna loads or its constructors abort with
// "GLib-ERROR **: The thread system is not yet initialized". It lives in
// libgthread-2.0.so.0, NOT libglib-2.0.so.0. Measured, not guessed.
if (void* gthread = dlopen("libgthread-2.0.so.0", RTLD_NOW | RTLD_GLOBAL)) {
typedef void (*GThreadInitFn)(void*);
if (GThreadInitFn init = (GThreadInitFn)dlsym(gthread, "g_thread_init")) {
typedef int (*GThreadGetInitFn)(void);
GThreadGetInitFn got =
(GThreadGetInitFn)dlsym(gthread, "g_thread_get_initialized");
if (!got || !got()) {
init(nullptr);
}
}
}
void* wk = dlopen("libWebKitLuna.so", RTLD_NOW | RTLD_GLOBAL);
if (!wk) {
fprintf(stderr, "[jihad-npapi-child] piranha: no libWebKitLuna (%s)\n", dlerror());
fflush(stderr);
} else {
sWrap = (PGSurfaceWrapFn)dlsym(wk, "_ZN9PGSurface4wrapEjjPKhb");
sCreate = (PGContextCreateFn)dlsym(wk, "_ZN9PGContext6createEv");
sSetSurface = (PGContextSetSurfaceFn)dlsym(wk, "_ZN9PGContext10setSurfaceEP9PGSurface");
fprintf(stderr, "[jihad-npapi-child] piranha syms wrap=%p create=%p setSurface=%p\n",
(void*)sWrap, (void*)sCreate, (void*)sSetSurface);
fflush(stderr);
}
}
if (!sWrap || !sCreate || !sSetSurface) {
return nullptr;
}
// A SET of cached contexts, not one. Palm got away with a single context because its
// buffer was one process-global screen-sized allocation that never moved; here the child
// rotates between a front and a back gfxSharedImageSurface, so a one-entry cache keyed on
// the buffer address misses on EVERY frame. That was invisible while the plugin was only
// painted twice, and turned into a PGSurface + PGContext leaked per frame the moment the
// host started driving repaints at 15Hz.
//
// Four entries: two buffers is the observed case, and a small fixed array keeps this
// allocation-free and bounded even if the surface pool grows. On overflow the oldest entry
// is dropped — leaking that one context rather than freeing an object the plugin may still
// be holding, which is the safer of the two mistakes and is bounded by the buffer count.
struct CacheEntry { unsigned char* data; int w, h; void* ctx; };
static CacheEntry sCache[4] = {};
static unsigned sCacheNext = 0;
for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sCache); ++i) {
if (sCache[i].ctx && sCache[i].data == aData &&
sCache[i].w == aWidth && sCache[i].h == aHeight) {
return sCache[i].ctx;
}
}
void* surf = sWrap((unsigned)aWidth, (unsigned)aHeight, aData, true);
void* ctx = surf ? sCreate() : nullptr;
if (ctx) {
sSetSurface(ctx, surf);
}
// Rate-limited: this is now a cache MISS log, and a miss storm is exactly the symptom
// worth seeing — but only the first few, or it floods the daemon log at frame rate.
static int sLoggedCtx = 0;
if (sLoggedCtx < 8) {
sLoggedCtx++;
fprintf(stderr, "[jihad-npapi-child] piranha ctx %dx%d buf=%p surface=%p context=%p\n",
aWidth, aHeight, aData, surf, ctx);
fflush(stderr);
}
CacheEntry& slot = sCache[sCacheNext % MOZ_ARRAY_LENGTH(sCache)];
sCacheNext++;
slot.data = aData; slot.w = aWidth; slot.h = aHeight; slot.ctx = ctx;
return ctx;
}
void
PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
gfxASurface* aSurface)
@@ -4454,187 +3799,7 @@ PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
::IntersectClipRect((HDC) mWindow.window, rect.left, rect.top, rect.right, rect.bottom);
mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
#else
// No platform surface to paint onto.
//
// This is the CLASSIC windowless paint path, and on Unix it is X11 and nothing else: the
// browser hands the plugin a Drawable and sends it an XGraphicsExposeEvent. A build with
// no X (a headless toolkit) has nothing it can hand over, and NPAPI defines no
// alternative for Unix — a plugin that wants to draw without X has to opt into a bitmap
// drawing model (NPDrawingModelAsyncBitmapSurface), which is a different path entirely
// and is fully supported here.
//
// Aborting is wrong for an embedder: the plugin process dies, the parent respawns it,
// and the whole cycle repeats for every paint, so one plugin that cannot draw becomes an
// endless crash loop. Warn once and leave the surface untouched instead — the plugin
// stays loaded and scriptable and simply shows nothing, which is a bounded failure the
// embedder can report.
// fprintf, not NS_WARNING: NS_WARNING compiles to nothing in a release build, and this is
// the ONLY signal distinguishing "the plugin drew nothing" from "the plugin was never
// asked to draw" — a distinction that is invisible on screen (both are a blank box).
// WEBOS RASTER DRAW EVENT (Jihad, addons R7).
//
// webOS plugins do not use the X11 protocol at all — they are painted by an
// npPalmDrawEvent, and NpPalmDrawEvent carries BOTH a raster destination (dstBuffer +
// dstRowBytes, "only valid in API version 1.0") and a Piranha PGContext
// ("must be used to draw in API version 2.0"). See Palm's own npapi.h, XP_WEBOS block.
//
// The device's Flash answers npPalmUseGraphicsContext = true, which per that header means
// the HOST may then pass a null raster — it is a statement about host behaviour, NOT a
// promise that the plugin refuses a non-null dstBuffer. There is no API-version
// negotiation anywhere in the contract, so the only way to find out whether a plugin
// honours the raster path is to hand it one and look. That is what this does.
//
// The surface here is already an ordinary shared-memory image (gfxSharedImageSurface,
// created a few hundred lines up), living in this process, whose pixels the parent
// already composites — so the raster the plugin needs costs nothing to produce.
if (aSurface->GetType() == gfxSurfaceType::Image) {
gfxImageSurface* img = static_cast<gfxImageSurface*>(aSurface);
uint8_t* data = img->Data();
int32_t stride = img->Stride();
if (data && stride > 0) {
// NPEvent is Palm's own event union on this platform (npapi.h); the offsets it
// depends on are asserted at the top of this file against libWebKitLuna.
// THE DRAW ALWAYS COVERS THE WHOLE SURFACE, whatever sub-rect was requested.
//
// Two things forced this. The PGContext below is built for the FULL surface
// (PGSurface::wrap takes width/height and assumes tightly packed rows), so pairing
// it with a dstBuffer offset into the middle of that surface makes the plugin's
// blit start partway in and run off the end — measured as `FAULT sig=7` (SIGBUS)
// in the plugin child the moment the host began sending partial invalidations.
// And Palm's own host never sent a partial rect either: PluginView::paint passes
// the plugin's whole frame rect, which is why a 2.0 plugin is entitled to assume
// the two agree. Redrawing the whole plugin box costs the plugin nothing it was
// not already doing every frame.
nsIntRect drawRect(0, 0, img->Width(), img->Height());
NPEvent ev;
memset(&ev, 0, sizeof(ev));
ev.eventType = npPalmDrawEvent;
// "points to top-left of area to paint"
ev.data.drawEvent.dstBuffer = data;
ev.data.drawEvent.dstRowBytes = (uint32_t)stride;
ev.data.drawEvent.srcLeft = drawRect.x;
ev.data.drawEvent.srcTop = drawRect.y;
ev.data.drawEvent.srcRight = drawRect.XMost();
ev.data.drawEvent.srcBottom = drawRect.YMost();
ev.data.drawEvent.dstLeft = ev.data.drawEvent.srcLeft;
ev.data.drawEvent.dstTop = ev.data.drawEvent.srcTop;
ev.data.drawEvent.dstRight = ev.data.drawEvent.srcRight;
ev.data.drawEvent.dstBottom = ev.data.drawEvent.srcBottom;
// API 2.0: a real Piranha PGContext bound to the very same pixels.
//
// The device's Flash answers npPalmUseGraphicsContext = true, and although it
// accepts a raster draw event (returns handled=1) it writes nothing into the
// buffer — so "handled" evidently means "consumed", not "drew". This gives it
// what it actually asked for. Proven possible by
// render/goanna/test/piranha_offscreen_probe.c: a PGContext can be created over
// wrapped memory in an ordinary process with no display and no compositor.
//
// PGSurface::wrap() takes no stride, so it assumes tightly-packed w*4 rows. That
// holds here (a 320-wide surface has stride 1280) but is asserted rather than
// assumed, because a padded stride would silently shear the image.
ev.data.drawEvent.graphicsContext = JihadPiranhaContextFor(data, img->Width(),
img->Height(), stride);
// BOTH fields stay populated, including for a plugin that answered
// npPalmUseGraphicsContext = true.
//
// npapi.h says the raster pointer "is then passed in as null" for such plugins, and
// this code used to believe it. The host that actually shipped does not: PluginView
// ::paint (libWebKitLuna 0x4e7c70 / 0x4e7c5c) stores a live dstBuffer and a live
// dstRowBytes into the event on the SAME branch that stores the PGContext — the
// graphics-context case is precisely the one where it computes
// dstBuffer = <shared buffer> + (y*screenWidth + x)*4 and
// dstRowBytes = screenWidth*4 (0x4e7e04-0x4e7e18). The header comment describes
// what a 2.0 plugin is expected to USE, not what the host is allowed to omit, and
// handing Flash a null raster is a difference from the reference host that costs
// nothing to remove.
// Let the plugin's own scheduled work run before we ask it to draw, so the
// frame it paints is current rather than whatever it had at instantiation.
JihadPumpGlib(50);
int16_t handled = mPluginIface->event
? mPluginIface->event(&mData, reinterpret_cast<void*>(&ev)) : -1;
gJihadPalmDrawEvents++;
static int sLogged = 0;
// FRAME-RATE READOUT. A static SWF and a 30fps game are indistinguishable from
// a per-event log (capped) or a screenshot (one instant), and the difference is
// the whole question for animated content. One line every 2s costs nothing and
// is the only way to see the plugin's real cadence in the field.
{
static uint32_t sDraws = 0;
static uint32_t sLastReport = 0;
sDraws++;
uint32_t nowMs = uint32_t(PR_IntervalToMilliseconds(PR_IntervalNow()));
if (!sLastReport) {
sLastReport = nowMs;
} else if (nowMs - sLastReport >= 2000) {
fprintf(stderr, "[jihad-npapi-child] palm draw: %u in %ums (%.1f fps)\n",
sDraws, nowMs - sLastReport,
sDraws * 1000.0 / double(nowMs - sLastReport));
// WHICH SITE ASKED FOR THOSE DRAWS. These are requests to draw, not draws:
// AsyncShowPluginFrame coalesces while an invalidate task is already pending,
// so the sum is an UPPER bound on sDraws and the interesting number is the
// SHARE each site holds, not the total. Printed on the same 2 s boundary so a
// reader can line them up without correlating timestamps.
{
char buf[192];
size_t n = 0;
buf[0] = '\0';
for (int i = 0; i < kJihadShowSites; ++i) {
// Clamp rather than trust the running total: snprintf returns the
// length it WANTED, so an overflow would make the next call pass a
// negative size as size_t and write off the end.
if (n < sizeof(buf) - 1) {
int w = snprintf(buf + n, sizeof(buf) - n, " %s=%u",
kJihadShowNames[i], gJihadShowFrom[i]);
if (w > 0) {
n += size_t(w);
if (n >= sizeof(buf)) n = sizeof(buf) - 1;
}
}
gJihadShowFrom[i] = 0;
}
fprintf(stderr, "[jihad-npapi-child] show req:%s\n", buf);
}
fflush(stderr);
sDraws = 0;
sLastReport = nowMs;
}
}
if (sLogged < 3) {
sLogged++;
// handled= is reported but means nothing on its own: Flash's NPP_HandleEvent
// sets its return to 1 (libflashplayer.so 0x45274) BEFORE calling the draw
// handler and returns it unconditionally, so handled=1 is emitted even when
// the handler bails without touching the surface. Only pixels are evidence.
fprintf(stderr, "[jihad-npapi-child] npPalmDrawEvent %s %d,%d %dx%d "
"stride=%d ctx=%p handled=%d\n",
ev.data.drawEvent.graphicsContext ? "pgcontext" : "raster",
drawRect.x, drawRect.y, drawRect.width, drawRect.height,
(int)stride, ev.data.drawEvent.graphicsContext, (int)handled);
fflush(stderr);
}
return;
}
}
// Nothing we can paint onto at all.
//
// Aborting is wrong for an embedder: the plugin process dies, the parent respawns it,
// and the whole cycle repeats for every paint, so one plugin that cannot draw becomes an
// endless crash loop. Warn once and leave the surface untouched instead.
// fprintf, not NS_WARNING: NS_WARNING compiles to nothing in a release build, and this is
// the ONLY signal distinguishing "the plugin drew nothing" from "the plugin was never
// asked to draw" — a distinction that is invisible on screen (both are a blank box).
static bool sWarned = false;
if (!sWarned) {
sWarned = true;
fprintf(stderr, "[jihad-npapi-child] no paintable surface for this windowless "
"plugin (type=%d); it will NOT paint.\n", (int)aSurface->GetType());
fflush(stderr);
}
NS_RUNTIMEABORT("Surface type not implemented.");
#endif
}
@@ -5099,7 +4264,6 @@ PluginInstanceChild::InvalidateRectDelayed(void)
}
if (!ShowPluginFrame()) {
gJihadShowFrom[kJihadShowRetry]++;
AsyncShowPluginFrame();
}
}
@@ -5153,8 +4317,6 @@ PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect)
mAccumulatedInvalidRect.UnionRect(r, mAccumulatedInvalidRect);
// If we are able to paint and invalidate sent, then reset
// accumulated rectangle
gJihadShowFrom[sJihadInHostRepaint ? kJihadShowInvalidate
: kJihadShowPluginSelf]++;
AsyncShowPluginFrame();
return;
}
@@ -5198,7 +4360,6 @@ PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground,
IntSize bgSize = mBackground->GetSize();
mAccumulatedInvalidRect.UnionRect(mAccumulatedInvalidRect,
nsIntRect(0, 0, bgSize.width, bgSize.height));
gJihadShowFrom[kJihadShowBgUpdate]++;
AsyncShowPluginFrame();
return true;
}
@@ -5208,7 +4369,6 @@ PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground,
// This must be asynchronous, because we may be nested within RPC messages
// which do not expect to receiving paint events.
gJihadShowFrom[kJihadShowBgUpdateTail]++;
AsyncShowPluginFrame();
return true;
@@ -5241,7 +4401,6 @@ PluginInstanceChild::RecvPPluginBackgroundDestroyerConstructor(
// NB: we don't have to XSync here because only ShowPluginFrame()
// uses mBackground, and it always XSyncs after finishing.
mBackground = nullptr;
gJihadShowFrom[kJihadShowBgDestroy]++;
AsyncShowPluginFrame();
}
@@ -5520,7 +4679,7 @@ PluginInstanceChild::Destroy()
mPendingAsyncCalls.Clear();
#ifdef MOZ_ENABLE_NPAPI_GTK2
#if defined(MOZ_WIDGET_GTK) && defined(MOZ_ENABLE_NPAPI_GTK2)
if (mWindow.type == NPWindowTypeWindow && !mXEmbed) {
xt_client_xloop_destroy();
}
+1 -21
View File
@@ -36,7 +36,7 @@
#include <map>
#ifdef MOZ_ENABLE_NPAPI_GTK2
#if defined(MOZ_WIDGET_GTK) && defined(MOZ_ENABLE_NPAPI_GTK2)
#include "gtk2xtbin.h"
#endif
@@ -141,10 +141,6 @@ 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;
@@ -645,22 +641,6 @@ 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;
+7 -224
View File
@@ -115,16 +115,6 @@ 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,
@@ -155,24 +145,10 @@ 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;
@@ -293,8 +269,6 @@ 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
@@ -795,51 +769,6 @@ 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(
@@ -935,55 +864,6 @@ 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,
@@ -997,27 +877,6 @@ 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) {
@@ -1100,75 +959,14 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
updatedRect.bottom - updatedRect.top);
surface->MarkDirty(ur);
// 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);
}
bool isPlugin = true;
RefPtr<gfx::SourceSurface> sourceSurface =
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface, isPlugin);
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), sourceSurface);
ImageContainer::NonOwningImage holder(image);
holder.mFrameID = ++mFrameID;
AutoTArray<ImageContainer::NonOwningImage,1> imageList;
imageList.AppendElement(holder);
imageList.AppendElement(
ImageContainer::NonOwningImage(image));
ImageContainer *container = GetImageContainer();
container->SetCurrentImages(imageList);
@@ -1178,17 +976,6 @@ 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)",
@@ -1514,11 +1301,7 @@ PluginInstanceParent::GetImageContainer()
return mImageContainer;
}
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.
if (IsUsingDirectDrawing()) {
mImageContainer = LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
} else {
mImageContainer = LayerManager::CreateImageContainer();
-2
View File
@@ -117,8 +117,6 @@ 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
+1 -44
View File
@@ -60,9 +60,6 @@ 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";
@@ -235,10 +232,6 @@ 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;
@@ -258,11 +251,7 @@ PluginModuleChild::InitForChrome(const std::string& aPluginFilename,
nsPluginFile pluginFile(localFile);
nsPluginInfo info = nsPluginInfo();
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)) {
if (NS_FAILED(pluginFile.GetPluginInfo(info, &mLibrary))) {
return false;
}
@@ -1854,8 +1843,6 @@ 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;
}
@@ -1895,8 +1882,6 @@ 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;
}
@@ -2632,31 +2617,3 @@ 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
+1 -190
View File
@@ -444,8 +444,6 @@ 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;
@@ -457,10 +455,7 @@ 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);
bool connected = parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000);
fprintf(stderr, "[jihad-npapi] LoadModule WaitUntilConnected(%ds) = %d\n",
(int)launchTimeoutSecs, (int)connected);
if (!connected) {
if (!parent->mSubprocess->WaitUntilConnected(launchTimeoutSecs * 1000)) {
parent->mShutdown = true;
return nullptr;
}
@@ -1139,20 +1134,9 @@ 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;
@@ -1287,172 +1271,6 @@ 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)
@@ -1478,7 +1296,6 @@ PluginModuleParent::NPP_Destroy(NPP instance,
instance->pdata = nullptr;
Unused << PluginInstanceParent::Call__delete__(parentInstance);
JihadCpuBoostRelease(instance);
return retval;
}
@@ -2475,12 +2292,6 @@ 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;
}
-5
View File
@@ -111,14 +111,9 @@ 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.
-4
View File
@@ -88,10 +88,6 @@ 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
]
+14 -5
View File
@@ -1789,17 +1789,26 @@ URL::IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
return URLWorker::IsValidURL(aGlobal, aURL, aRv);
}
bool
URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase) {
already_AddRefed<URL>
URL::Parse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase)
{
ErrorResult rv;
RefPtr<URL> parsed = URL::Constructor(aGlobal, aURL, aBase, rv);
if (rv.Failed() || !parsed) {
rv.SuppressException();
return false;
return nullptr;
}
return true;
return parsed.forget();
}
bool
URL::CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase)
{
RefPtr<URL> parsed = URL::Parse(aGlobal, aURL, aBase);
return !!parsed;
}
already_AddRefed<URLSearchParams>
+4
View File
@@ -88,6 +88,10 @@ public:
IsValidURL(const GlobalObject& aGlobal, const nsAString& aURL,
ErrorResult& aRv);
static already_AddRefed<URL>
Parse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase);
static bool
CanParse(const GlobalObject& aGlobal, const nsAString& aURL,
const Optional<nsAString>& aBase);
+28
View File
@@ -448,6 +448,34 @@
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
"URL.canParse rejects root-relative input against opaque mailto: base",
);
ok("parse" in URL, "URL.parse exists");
is(URL.parse.length, 1, "URL.parse has one required argument");
var parsed = URL.parse("/static/client/runtime.js", "x:/");
ok(parsed instanceof URL, "URL.parse returns a URL object on success");
is(parsed.href, "x:/static/client/runtime.js", "URL.parse resolves against a base");
is(URL.parse("/static/client/runtime.js"), null, "URL.parse returns null for invalid input");
is(
URL.parse("/static/client/runtime.js", "mailto:test@example.com"),
null,
"URL.parse returns null for an invalid base/input combination",
);
var conversionOrder = [];
parsed = URL.parse(
{ toString: function() { conversionOrder.push("url"); return "child"; } },
{ toString: function() { conversionOrder.push("base"); return "https://example.com/root/"; } },
);
is(conversionOrder.join(","), "url,base", "URL.parse converts its arguments in order");
is(parsed.href, "https://example.com/root/child", "URL.parse uses converted arguments");
var sentinel = {};
var thrown;
try {
URL.parse({ toString: function() { throw sentinel; } });
} catch (e) {
thrown = e;
}
is(thrown, sentinel, "URL.parse propagates argument conversion exceptions");
SimpleTest.doesThrow(() => {
new URL("/static/client/runtime.js", "mailto:test@example.com");
}, "constructor rejects root-relative input against opaque mailto: base");
+58
View File
@@ -301,6 +301,64 @@ onmessage = function () {
!URL.canParse("/static/client/runtime.js", "mailto:test@example.com"),
"URL.canParse rejects root-relative input against opaque mailto: base",
);
ok("parse" in URL, "URL.parse exists");
is(URL.parse.length, 1, "URL.parse has one required argument");
let parsed = URL.parse("/static/client/runtime.js", "x:/");
ok(parsed instanceof URL, "URL.parse returns a URL object on success");
is(
parsed.href,
"x:/static/client/runtime.js",
"URL.parse resolves against a base",
);
is(
URL.parse("/static/client/runtime.js"),
null,
"URL.parse returns null for invalid input",
);
is(
URL.parse("/static/client/runtime.js", "mailto:test@example.com"),
null,
"URL.parse returns null for an invalid base/input combination",
);
const conversionOrder = [];
parsed = URL.parse(
{
toString() {
conversionOrder.push("url");
return "child";
},
},
{
toString() {
conversionOrder.push("base");
return "https://example.com/root/";
},
},
);
is(
conversionOrder.join(","),
"url,base",
"URL.parse converts its arguments in order",
);
is(
parsed.href,
"https://example.com/root/child",
"URL.parse uses converted arguments",
);
const sentinel = {};
let thrown;
try {
URL.parse({
toString() {
throw sentinel;
},
});
} catch (e) {
thrown = e;
}
is(thrown, sentinel, "URL.parse propagates argument conversion exceptions");
let mailtoRelativeFailed = false;
try {
new URL("/static/client/runtime.js", "mailto:test@example.com");
+2
View File
@@ -25,6 +25,8 @@ interface CSSStyleSheet : StyleSheet {
unsigned long insertRule(DOMString rule, optional unsigned long index = 0);
[Throws, NeedsSubjectPrincipal]
void deleteRule(unsigned long index);
[Throws]
void replaceSync(DOMString text);
// Non-standard WebKit things, see https://github.com/w3c/csswg-drafts/pull/3900.
[Throws, NeedsSubjectPrincipal, BinaryName="cssRules"]
+1
View File
@@ -23,6 +23,7 @@ interface URL {
[Throws]
stringifier;
static URL? parse(USVString url, optional USVString base);
static boolean canParse(USVString url, optional USVString base);
[Throws]
-10
View File
@@ -5,7 +5,6 @@
// Local Includes
#include "nsDocShellTreeOwner.h"
#include "nsIBadCertListener2.h" // JIHAD: route cert errors to the embedder chrome
#include "nsWebBrowser.h"
// Helper Classes
@@ -144,15 +143,6 @@ 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);
+3 -36
View File
@@ -390,14 +390,6 @@ 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));
@@ -715,24 +707,7 @@ nsWebBrowserFind::SearchInFrame(nsPIDOMWindowOuter* aWindow, bool aWrapping,
return NS_ERROR_FAILURE;
}
// 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())) {
if (!nsContentUtils::SubjectPrincipal()->Subsumes(theDoc->NodePrincipal())) {
return NS_ERROR_DOM_PROP_ACCESS_DENIED;
}
@@ -834,11 +809,8 @@ nsWebBrowserFind::GetFrameSelection(nsPIDOMWindowOuter* aWindow)
nsCOMPtr<nsISelection> sel;
if (frame) {
frame->GetSelectionController(presContext, getter_AddRefs(selCon));
// JIHAD: same unguarded deref as above — GetSelectionController can yield null.
if (selCon) {
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(sel));
}
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(sel));
if (sel) {
int32_t count = -1;
sel->GetRangeCount(&count);
@@ -848,12 +820,7 @@ 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();
+1 -2
View File
@@ -12,6 +12,5 @@ if CONFIG['NS_PRINTING']:
DIRS += ['win']
elif toolkit == 'cocoa':
DIRS += ['mac']
elif CONFIG['MOZ_PDF_PRINTING'] or toolkit == 'headless':
# Jihad headless: reuse the gtk-free PDF printing prompt service.
elif CONFIG['MOZ_PDF_PRINTING']:
DIRS += ['unixshared']
+1 -1
View File
@@ -1374,7 +1374,7 @@ public:
static already_AddRefed<ScaledFont>
CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize);
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
#ifdef MOZ_WIDGET_GTK
static already_AddRefed<ScaledFont>
CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize);
#endif
+10
View File
@@ -1117,6 +1117,7 @@ DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
kCGImageAlphaOnly :
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
mCG = CGBitmapContextCreateWithData(mCanvasData,
mCGSize.width,
mCGSize.height,
@@ -1126,6 +1127,15 @@ DrawTargetSkia::BorrowCGContext(const DrawOptions &aOptions)
bitmapInfo,
NULL, /* Callback when released */
NULL);
#else
mCG = CGBitmapContextCreate(mCanvasData,
mCGSize.width,
mCGSize.height,
8, /* bits per component */
stride,
mColorSpace,
bitmapInfo);
#endif
if (!mCG) {
ReleaseBits(mCanvasData);
NS_WARNING("Could not create bitmap around skia data\n");
+2 -2
View File
@@ -30,7 +30,7 @@
#include "NativeFontResourceMac.h"
#endif
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
#ifdef MOZ_WIDGET_GTK
#include "ScaledFontFontconfig.h"
#endif
@@ -584,7 +584,7 @@ Factory::CreateScaledFontWithCairo(const NativeFont& aNativeFont, Float aSize, c
#endif
}
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_HEADLESS)
#ifdef MOZ_WIDGET_GTK
already_AddRefed<ScaledFont>
Factory::CreateScaledFontForFontconfigFont(cairo_scaled_font_t* aScaledFont, FcPattern* aPattern, Float aSize)
{
+1 -1
View File
@@ -115,7 +115,7 @@ ScaledFontMac::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aT
return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
}
#if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
#ifndef USE_SKIA
void
ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint)
{
+1 -1
View File
@@ -32,7 +32,7 @@ public:
virtual SkTypeface* GetSkTypeface();
#endif
virtual already_AddRefed<Path> GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget);
#if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
#ifndef USE_SKIA
virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
#endif
virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
+3 -3
View File
@@ -91,7 +91,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'windows':
'JobScheduler_posix.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
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', 'headless'):
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
DEFINES['MOZ_ENABLE_FREETYPE'] = True
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
+2 -9
View File
@@ -87,17 +87,10 @@ public:
bool
BasicPlanarYCbCrImage::CopyData(const Data& aData)
{
if (!RecyclingPlanarYCbCrImage::CopyData(aData)) {
return false;
}
RecyclingPlanarYCbCrImage::CopyData(aData);
// 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;
return false;
}
// Do some sanity checks to prevent integer overflow
+2 -3
View File
@@ -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'] or CONFIG['MOZ_WIDGET_TOOLKIT'] == 'headless':
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
UNIFIED_SOURCES += [
'skia/src/ports/SkDebug_stdio.cpp',
'skia/src/ports/SkOSFile_posix.cpp',
@@ -665,7 +665,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
'cocoa',
'gtk2',
'gtk3',
'headless',
'uikit',
}:
DEFINES['SK_FONTHOST_DOES_NOT_USE_FONTMGR'] = 1
@@ -739,7 +738,7 @@ if CONFIG['CLANG_CXX'] or CONFIG['CLANG_CL']:
'-Wno-unused-private-field',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'headless'):
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
+58 -15
View File
@@ -24,15 +24,21 @@
* Skia's 32bit backend only supports 1 sizzle order at a time (compile-time).
* This is specified by 4 defines SK_A32_SHIFT, SK_R32_SHIFT, ... for G and B.
*
* For easier compatibility with Skia's GPU backend, we further restrict these
* to either (in memory-byte-order) RGBA or BGRA. Note that this "order" does
* not directly correspond to the same shift-order, since we have to take endianess
* into account.
* For easier compatibility with Skia's GPU backend, Skia normally restricts
* these to either (in memory-byte-order) RGBA or BGRA. Mozilla's big-endian
* PowerPC port uses CoreGraphics' native ARGB pixels, so we also accept ARGB.
* Note that this "order" does not directly correspond to the same shift-order,
* since we have to take endianess into account.
*
* Here we enforce this constraint.
*/
#ifdef SK_CPU_BENDIAN
#define SK_ARGB_A32_SHIFT 24
#define SK_ARGB_R32_SHIFT 16
#define SK_ARGB_G32_SHIFT 8
#define SK_ARGB_B32_SHIFT 0
#define SK_RGBA_R32_SHIFT 24
#define SK_RGBA_G32_SHIFT 16
#define SK_RGBA_B32_SHIFT 8
@@ -43,6 +49,11 @@
#define SK_BGRA_R32_SHIFT 8
#define SK_BGRA_A32_SHIFT 0
#else
#define SK_ARGB_A32_SHIFT 0
#define SK_ARGB_R32_SHIFT 8
#define SK_ARGB_G32_SHIFT 16
#define SK_ARGB_B32_SHIFT 24
#define SK_RGBA_R32_SHIFT 0
#define SK_RGBA_G32_SHIFT 8
#define SK_RGBA_B32_SHIFT 16
@@ -54,10 +65,18 @@
#define SK_BGRA_A32_SHIFT 24
#endif
#if defined(SK_PMCOLOR_IS_RGBA) && defined(SK_PMCOLOR_IS_BGRA)
#error "can't define PMCOLOR to be RGBA and BGRA"
#if (defined(SK_PMCOLOR_IS_RGBA) && defined(SK_PMCOLOR_IS_BGRA)) || \
(defined(SK_PMCOLOR_IS_RGBA) && defined(SK_PMCOLOR_IS_ARGB)) || \
(defined(SK_PMCOLOR_IS_BGRA) && defined(SK_PMCOLOR_IS_ARGB))
#error "can't define PMCOLOR to more than one byte order"
#endif
#define LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_ARGB \
(SK_A32_SHIFT == SK_ARGB_A32_SHIFT && \
SK_R32_SHIFT == SK_ARGB_R32_SHIFT && \
SK_G32_SHIFT == SK_ARGB_G32_SHIFT && \
SK_B32_SHIFT == SK_ARGB_B32_SHIFT)
#define LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA \
(SK_A32_SHIFT == SK_RGBA_A32_SHIFT && \
SK_R32_SHIFT == SK_RGBA_R32_SHIFT && \
@@ -76,6 +95,10 @@
#define SK_G_INDEX (SK_G32_SHIFT/8)
#define SK_B_INDEX (SK_B32_SHIFT/8)
#if defined(SK_PMCOLOR_IS_ARGB) && !LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_ARGB
#error "SK_PMCOLOR_IS_ARGB does not match SK_*32_SHIFT values"
#endif
#if defined(SK_PMCOLOR_IS_RGBA) && !LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA
#error "SK_PMCOLOR_IS_RGBA does not match SK_*32_SHIFT values"
#endif
@@ -84,19 +107,22 @@
#error "SK_PMCOLOR_IS_BGRA does not match SK_*32_SHIFT values"
#endif
#if !defined(SK_PMCOLOR_IS_RGBA) && !defined(SK_PMCOLOR_IS_BGRA)
#if !defined(SK_PMCOLOR_IS_ARGB) && !defined(SK_PMCOLOR_IS_RGBA) && !defined(SK_PMCOLOR_IS_BGRA)
// deduce which to define from the _SHIFT defines
#if LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA
#if LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_ARGB
#define SK_PMCOLOR_IS_ARGB
#elif LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA
#define SK_PMCOLOR_IS_RGBA
#elif LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_BGRA
#define SK_PMCOLOR_IS_BGRA
#else
#error "need 32bit packing to be either RGBA or BGRA"
#error "need 32bit packing to be either ARGB, RGBA, or BGRA"
#endif
#endif
// hide these now that we're done
#undef LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_ARGB
#undef LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA
#undef LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_BGRA
@@ -131,19 +157,38 @@ static inline uint32_t SkPackARGB_as_BGRA(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
(g << SK_BGRA_G32_SHIFT) | (b << SK_BGRA_B32_SHIFT);
}
static inline SkPMColor SkPackARGB_as_PMColor(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
SkASSERT_IS_BYTE(a);
SkASSERT_IS_BYTE(r);
SkASSERT_IS_BYTE(g);
SkASSERT_IS_BYTE(b);
return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
(g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
}
static inline SkPMColor SkSwizzle_RGBA_to_PMColor(uint32_t c) {
#ifdef SK_PMCOLOR_IS_RGBA
return c;
#else
#elif defined(SK_PMCOLOR_IS_BGRA)
return SkSwizzle_RB(c);
#else
return SkPackARGB_as_PMColor((c >> SK_RGBA_A32_SHIFT) & 0xFF,
(c >> SK_RGBA_R32_SHIFT) & 0xFF,
(c >> SK_RGBA_G32_SHIFT) & 0xFF,
(c >> SK_RGBA_B32_SHIFT) & 0xFF);
#endif
}
static inline SkPMColor SkSwizzle_BGRA_to_PMColor(uint32_t c) {
#ifdef SK_PMCOLOR_IS_BGRA
return c;
#else
#elif defined(SK_PMCOLOR_IS_RGBA)
return SkSwizzle_RB(c);
#else
return SkPackARGB_as_PMColor((c >> SK_BGRA_A32_SHIFT) & 0xFF,
(c >> SK_BGRA_R32_SHIFT) & 0xFF,
(c >> SK_BGRA_G32_SHIFT) & 0xFF,
(c >> SK_BGRA_B32_SHIFT) & 0xFF);
#endif
}
@@ -412,8 +457,7 @@ static inline SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
SkASSERT(g <= a);
SkASSERT(b <= a);
return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
(g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
return SkPackARGB_as_PMColor(a, r, g, b);
}
static inline uint32_t SkPackPMColor_as_RGBA(SkPMColor c) {
@@ -547,8 +591,7 @@ static inline SkPMColor SkFastFourByteInterp(SkPMColor src,
* values are premultiplied in the debug version.
*/
static inline SkPMColor SkPackARGB32NoCheck(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
return (a << SK_A32_SHIFT) | (r << SK_R32_SHIFT) |
(g << SK_G32_SHIFT) | (b << SK_B32_SHIFT);
return SkPackARGB_as_PMColor(a, r, g, b);
}
static inline
+6 -1
View File
@@ -82,8 +82,13 @@ enum SkColorType {
kN32_SkColorType = kBGRA_8888_SkColorType,
#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
kN32_SkColorType = kRGBA_8888_SkColorType,
#elif defined(MOZ_SKIA) && SK_PMCOLOR_BYTE_ORDER(A,R,G,B)
// Big-endian PowerPC Mozilla surfaces use native uint32 ARGB memory.
// Moz2D's legacy A8R8G8B8_UINT32 format aliases to B8G8R8A8, so keep
// kN32 on Skia's BGRA path while the channel shifts above describe ARGB.
kN32_SkColorType = kBGRA_8888_SkColorType,
#else
#error "SK_*32_SHFIT values must correspond to BGRA or RGBA byte order"
#error "SK_*32_SHIFT values must correspond to BGRA, RGBA, or Mozilla ARGB byte order"
#endif
};
+6 -4
View File
@@ -275,17 +275,19 @@ enum GrPixelConfig {
static const int kGrPixelConfigCnt = kLast_GrPixelConfig + 1;
// Aliases for pixel configs that match skia's byte order.
#ifndef SK_CPU_LENDIAN
#error "Skia gpu currently assumes little endian"
#endif
#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig;
static const GrPixelConfig kSkiaGamma8888_GrPixelConfig = kSBGRA_8888_GrPixelConfig;
#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
static const GrPixelConfig kSkia8888_GrPixelConfig = kRGBA_8888_GrPixelConfig;
static const GrPixelConfig kSkiaGamma8888_GrPixelConfig = kSRGBA_8888_GrPixelConfig;
#elif defined(MOZ_SKIA) && SK_PMCOLOR_BYTE_ORDER(A,R,G,B)
// Mozilla's big-endian PowerPC path stores native 32-bit pixels as ARGB,
// while Moz2D routes those surfaces through Skia's BGRA color type.
static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig;
static const GrPixelConfig kSkiaGamma8888_GrPixelConfig = kSBGRA_8888_GrPixelConfig;
#else
#error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
#error "SK_*32_SHIFT values must correspond to GL_BGRA, GL_RGBA, or Mozilla ARGB format."
#endif
// Returns true if the pixel config is a GPU-specific compressed format
+4
View File
@@ -306,7 +306,11 @@ static inline SkPMColor premultiply_argb_as_bgra(U8CPU a, U8CPU r, U8CPU g, U8CP
b = SkMulDiv255Round(b, a);
}
#ifdef SK_PMCOLOR_IS_ARGB
return SkPackARGB_as_PMColor(a, r, g, b);
#else
return SkPackARGB_as_BGRA(a, r, g, b);
#endif
}
static inline bool is_rgba(SkColorType colorType) {
+6
View File
@@ -51,10 +51,16 @@ struct SkPM4f {
Sk4f to4f_pmorder() const { return swizzle_rb_if_bgra(this->to4f()); }
SkPMColor toPMColor() const {
#ifdef SK_PMCOLOR_IS_ARGB
Sk4f value = this->to4f();
Sk4b bytes = SkNx_cast<uint8_t>(value * Sk4f(255) + Sk4f(0.5f));
return SkPackARGB_as_PMColor(bytes[3], bytes[0], bytes[1], bytes[2]);
#else
Sk4f value = swizzle_rb_if_bgra(this->to4f());
SkPMColor result;
SkNx_cast<uint8_t>(value * Sk4f(255) + Sk4f(0.5f)).store(&result);
return result;
#endif
}
void toF16(uint16_t[4]) const;
+21
View File
@@ -22,29 +22,50 @@ static inline float get_alpha(const Sk4f& px) {
static inline Sk4f Sk4f_fromL32(uint32_t px) {
#ifdef SK_PMCOLOR_IS_ARGB
return Sk4f(SkGetPackedR32(px), SkGetPackedG32(px),
SkGetPackedB32(px), SkGetPackedA32(px)) * (1/255.0f);
#else
return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f);
#endif
}
static inline Sk4f Sk4f_fromS32(uint32_t px) {
#ifdef SK_PMCOLOR_IS_ARGB
return { sk_linear_from_srgb[SkGetPackedR32(px)],
sk_linear_from_srgb[SkGetPackedG32(px)],
sk_linear_from_srgb[SkGetPackedB32(px)],
(1/255.0f) * SkGetPackedA32(px) };
#else
return { sk_linear_from_srgb[(px >> 0) & 0xff],
sk_linear_from_srgb[(px >> 8) & 0xff],
sk_linear_from_srgb[(px >> 16) & 0xff],
(1/255.0f) * (px >> 24) };
#endif
}
static inline uint32_t Sk4f_toL32(const Sk4f& px) {
#ifdef SK_PMCOLOR_IS_ARGB
Sk4b bytes = SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f));
return SkPackARGB_as_PMColor(bytes[3], bytes[0], bytes[1], bytes[2]);
#else
uint32_t l32;
SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f)).store(&l32);
return l32;
#endif
}
static inline uint32_t Sk4f_toS32(const Sk4f& px) {
Sk4i rgb = sk_linear_to_srgb(px),
srgb = { rgb[0], rgb[1], rgb[2], (int)(255.0f * px[3] + 0.5f) };
#ifdef SK_PMCOLOR_IS_ARGB
return SkPackARGB_as_PMColor(srgb[3], srgb[0], srgb[1], srgb[2]);
#else
uint32_t s32;
SkNx_cast<uint8_t>(srgb).store(&s32);
return s32;
#endif
}
+3 -2
View File
@@ -121,9 +121,10 @@ SkDEBUGCODE(static const unsigned kRunRecordMagic = 0xb10bcafe;)
namespace {
struct RunRecordStorageEquivalent {
RunFont fFont;
SkPoint fOffset;
uint32_t fCount;
uint32_t fFlags;
SkPoint fOffset;
SkTextBlob::GlyphPositioning fPositioning;
bool fExtended;
SkDEBUGCODE(unsigned fMagic;)
};
}
+33 -1
View File
@@ -44,6 +44,25 @@ static Sk4x4f load_4_srgb(const void* vptr) {
Sk4x4f rgba;
#ifdef SK_PMCOLOR_IS_ARGB
rgba.r = { sk_linear_from_srgb[SkGetPackedR32(ptr[0])],
sk_linear_from_srgb[SkGetPackedR32(ptr[1])],
sk_linear_from_srgb[SkGetPackedR32(ptr[2])],
sk_linear_from_srgb[SkGetPackedR32(ptr[3])] };
rgba.g = { sk_linear_from_srgb[SkGetPackedG32(ptr[0])],
sk_linear_from_srgb[SkGetPackedG32(ptr[1])],
sk_linear_from_srgb[SkGetPackedG32(ptr[2])],
sk_linear_from_srgb[SkGetPackedG32(ptr[3])] };
rgba.b = { sk_linear_from_srgb[SkGetPackedB32(ptr[0])],
sk_linear_from_srgb[SkGetPackedB32(ptr[1])],
sk_linear_from_srgb[SkGetPackedB32(ptr[2])],
sk_linear_from_srgb[SkGetPackedB32(ptr[3])] };
rgba.a = SkNx_cast<float>(Sk4i(SkGetPackedA32(ptr[0]), SkGetPackedA32(ptr[1]),
SkGetPackedA32(ptr[2]), SkGetPackedA32(ptr[3]))) * (1/255.0f);
#else
rgba.r = { sk_linear_from_srgb[(ptr[0] >> 0) & 0xff],
sk_linear_from_srgb[(ptr[1] >> 0) & 0xff],
sk_linear_from_srgb[(ptr[2] >> 0) & 0xff],
@@ -60,15 +79,28 @@ static Sk4x4f load_4_srgb(const void* vptr) {
sk_linear_from_srgb[(ptr[3] >> 16) & 0xff] };
rgba.a = SkNx_cast<float>((Sk4i::Load(ptr) >> 24) & 0xff) * (1/255.0f);
#endif
return rgba;
}
static void store_4_srgb(void* ptr, const Sk4x4f& p) {
#ifdef SK_PMCOLOR_IS_ARGB
uint32_t* dst = (uint32_t*)ptr;
Sk4i r = sk_linear_to_srgb(p.r);
Sk4i g = sk_linear_to_srgb(p.g);
Sk4i b = sk_linear_to_srgb(p.b);
Sk4i a = Sk4f_round(255.0f*p.a);
dst[0] = SkPackARGB_as_PMColor(a[0], r[0], g[0], b[0]);
dst[1] = SkPackARGB_as_PMColor(a[1], r[1], g[1], b[1]);
dst[2] = SkPackARGB_as_PMColor(a[2], r[2], g[2], b[2]);
dst[3] = SkPackARGB_as_PMColor(a[3], r[3], g[3], b[3]);
#else
( sk_linear_to_srgb(p.r) << 0
| sk_linear_to_srgb(p.g) << 8
| sk_linear_to_srgb(p.b) << 16
| Sk4f_round(255.0f*p.a) << 24).store(ptr);
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -391,7 +423,7 @@ SkXfermode::D32Proc SkXfermode::GetD32Proc(SkXfermode* xfer, uint32_t flags) {
#include "SkColorPriv.h"
static Sk4f lcd16_to_unit_4f(uint16_t rgb) {
#ifdef SK_PMCOLOR_IS_RGBA
#if defined(SK_PMCOLOR_IS_RGBA) || defined(SK_PMCOLOR_IS_ARGB)
Sk4i rgbi = Sk4i(SkGetPackedR16(rgb), SkGetPackedG16(rgb), SkGetPackedB16(rgb), 0);
#else
Sk4i rgbi = Sk4i(SkGetPackedB16(rgb), SkGetPackedG16(rgb), SkGetPackedR16(rgb), 0);
+1 -1
View File
@@ -251,7 +251,7 @@ bool GrDrawTarget::drawBatches(GrBatchFlushState* flushState) {
if (currentRT) {
static const GrGpuCommandBuffer::LoadAndStoreInfo kBasicLoadStoreInfo
{ GrGpuCommandBuffer::LoadOp::kLoad,GrGpuCommandBuffer::StoreOp::kStore,
GrColor_ILLEGAL };
static_cast<GrColor>(GrColor_ILLEGAL) };
commandBuffer.reset(fGpu->createCommandBuffer(currentRT,
kBasicLoadStoreInfo, // Color
kBasicLoadStoreInfo)); // Stencil
+21
View File
@@ -69,11 +69,18 @@ inline Sk4px Sk4px::Wide::div255() const {
}
inline Sk4px Sk4px::alphas() const {
#ifdef SK_PMCOLOR_IS_ARGB
return Sk16b((*this)[ 0], (*this)[ 0], (*this)[ 0], (*this)[ 0],
(*this)[ 4], (*this)[ 4], (*this)[ 4], (*this)[ 4],
(*this)[ 8], (*this)[ 8], (*this)[ 8], (*this)[ 8],
(*this)[12], (*this)[12], (*this)[12], (*this)[12]);
#else
static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
return Sk16b((*this)[ 3], (*this)[ 3], (*this)[ 3], (*this)[ 3],
(*this)[ 7], (*this)[ 7], (*this)[ 7], (*this)[ 7],
(*this)[11], (*this)[11], (*this)[11], (*this)[11],
(*this)[15], (*this)[15], (*this)[15], (*this)[15]);
#endif
}
inline Sk4px Sk4px::Load4Alphas(const SkAlpha a[4]) {
@@ -91,19 +98,33 @@ inline Sk4px Sk4px::Load2Alphas(const SkAlpha a[2]) {
}
inline Sk4px Sk4px::zeroAlphas() const {
#ifdef SK_PMCOLOR_IS_ARGB
return Sk16b(0, (*this)[ 1], (*this)[ 2], (*this)[ 3],
0, (*this)[ 5], (*this)[ 6], (*this)[ 7],
0, (*this)[ 9], (*this)[10], (*this)[11],
0, (*this)[13], (*this)[14], (*this)[15]);
#else
static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
return Sk16b((*this)[ 0], (*this)[ 1], (*this)[ 2], 0,
(*this)[ 4], (*this)[ 5], (*this)[ 6], 0,
(*this)[ 8], (*this)[ 9], (*this)[10], 0,
(*this)[12], (*this)[13], (*this)[14], 0);
#endif
}
inline Sk4px Sk4px::zeroColors() const {
#ifdef SK_PMCOLOR_IS_ARGB
return Sk16b((*this)[ 0], 0,0,0,
(*this)[ 4], 0,0,0,
(*this)[ 8], 0,0,0,
(*this)[12], 0,0,0);
#else
static_assert(SK_A32_SHIFT == 24, "This method assumes little-endian.");
return Sk16b(0,0,0, (*this)[ 3],
0,0,0, (*this)[ 7],
0,0,0, (*this)[11],
0,0,0, (*this)[15]);
#endif
}
} // namespace
@@ -72,6 +72,10 @@ static void color_cube_filter_span(const SkPMColor src[],
// color is BGRA (SkColor order), dst is SkPMColor order, so may need to swap R+B.
#if defined(SK_PMCOLOR_IS_RGBA)
color = SkNx_shuffle<2,1,0,3>(color);
#elif defined(SK_PMCOLOR_IS_ARGB)
Sk4b bytes = SkNx_cast<uint8_t>(color);
dst[i] = SkPackARGB32NoCheck(a, bytes[2], bytes[1], bytes[0]);
continue;
#endif
uint8_t* dstBytes = (uint8_t*)(dst+i);
SkNx_cast<uint8_t>(color).store(dstBytes);
@@ -73,7 +73,39 @@ static CFArrayRef SkCTFontManagerCopyAvailableFontFamilyNames() {
#ifdef SK_BUILD_FOR_IOS
return CFArrayCreate(nullptr, nullptr, 0, nullptr);
#else
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
return CTFontManagerCopyAvailableFontFamilyNames();
#else
CFMutableArrayRef familyNames =
CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if (!familyNames) {
return nullptr;
}
ATSFontFamilyIterator iterator;
OSStatus status = ATSFontFamilyIteratorCreate(kATSFontContextGlobal,
nullptr,
nullptr,
kATSOptionFlagsDefaultScope,
&iterator);
if (status != noErr) {
CFRelease(familyNames);
return nullptr;
}
ATSFontFamilyRef family;
while (ATSFontFamilyIteratorNext(iterator, &family) == noErr) {
CFStringRef familyName = nullptr;
if (ATSFontFamilyGetName(family, kATSOptionFlagsDefault, &familyName) == noErr &&
familyName) {
CFArrayAppendValue(familyNames, familyName);
CFRelease(familyName);
}
}
ATSFontFamilyIteratorRelease(&iterator);
return familyNames;
#endif
#endif
}
@@ -908,6 +940,7 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
// Skia handles quantization and subpixel positioning,
// so disable quantization and enabe subpixel positioning in CG.
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
CGContextSetAllowsFontSubpixelQuantization(fCG, false);
CGContextSetShouldSubpixelQuantizeFonts(fCG, false);
@@ -916,6 +949,7 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
// then CG cannot draw the glyph in the correct location without subpixel positioning.
CGContextSetAllowsFontSubpixelPositioning(fCG, true);
CGContextSetShouldSubpixelPositionFonts(fCG, true);
#endif
CGContextSetTextDrawingMode(fCG, kCGTextFill);
@@ -971,7 +1005,13 @@ CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph&
// So always make the font transform identity and place the transform on the context.
point = CGPointApplyAffineTransform(point, context.fInvTransform);
#if defined(MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
CTFontDrawGlyphs(context.fCTFont, &glyphID, &point, 1, fCG);
#else
CGContextSetFont(fCG, CTFontCopyGraphicsFont(context.fCTFont, NULL));
CGContextSetFontSize(fCG, CTFontGetSize(context.fCTFont));
CGContextShowGlyphsAtPositions(fCG, &glyphID, &point, 1);
#endif
SkASSERT(rowBytesPtr);
*rowBytesPtr = rowBytes;
@@ -1682,6 +1722,7 @@ SkAdvancedTypefaceMetrics* SkTypeface_Mac::onGetAdvancedTypefaceMetrics(
static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
CTFontRef ctFont = typeface->fFontRef.get();
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
AutoCFRelease<CFNumberRef> fontFormatRef(
static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
if (!fontFormatRef) {
@@ -1711,6 +1752,35 @@ static SK_SFNT_ULONG get_font_type_tag(const SkTypeface_Mac* typeface) {
//Just the presence of the FontForge 'FFTM' table seems to throw it off.
return SkSFNTHeader::fontType_WindowsTrueType::TAG;
}
#else
AutoCFRelease<CFStringRef> postScriptName(CTFontCopyPostScriptName(ctFont));
if (!postScriptName) {
return 0;
}
ATSFontRef atsFont =
ATSFontFindFromPostScriptName(postScriptName, kATSOptionFlagsDefault);
if (atsFont == kInvalidFont) {
return 0;
}
SkSFNTHeader header;
ByteCount headerSize = sizeof(header);
if (ATSFontGetTableDirectory(atsFont, headerSize, &header, &headerSize) != noErr ||
headerSize < sizeof(header.fontType)) {
return 0;
}
switch (header.fontType) {
case SkSFNTHeader::fontType_OpenTypeCFF::TAG:
case SkSFNTHeader::fontType_WindowsTrueType::TAG:
case SkSFNTHeader::fontType_MacTrueType::TAG:
case SkSFNTHeader::fontType_PostScript::TAG:
return header.fontType;
default:
return SkSFNTHeader::fontType_WindowsTrueType::TAG;
}
#endif
}
SkStreamAsset* SkTypeface_Mac::onOpenStream(int* ttcIndex) const {
-7
View File
@@ -32,9 +32,6 @@
#include <gdk/gdk.h>
#include "gfxPlatformGtk.h"
#endif
#ifdef MOZ_WIDGET_HEADLESS
#include "gfxPlatformHeadless.h"
#endif
#ifdef MOZ_X11
#include "mozilla/X11Util.h"
@@ -1767,11 +1764,7 @@ 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];
+14 -1
View File
@@ -336,7 +336,20 @@ gfxGraphiteShaper::SetGlyphsFromSegment(DrawTarget *aDrawTarget,
// not a one-to-one mapping with simple metrics: use DetailedGlyph
AutoTArray<gfxShapedText::DetailedGlyph,8> details;
float clusterLoc;
for (uint32_t j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
uint32_t glyph_start = c.baseGlyph;
uint32_t glyph_end = glyph_start + c.nGlyphs;
if (glyph_end < glyph_start) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (glyph_end - glyph_start > 0xFFFF) {
return NS_ERROR_ILLEGAL_VALUE;
}
if (!details.SetCapacity(glyph_end - glyph_start, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t j = glyph_start; j < glyph_end; ++j) {
gfxShapedText::DetailedGlyph* d = details.AppendElement();
d->mGlyphID = gids[j];
d->mYOffset = roundY ? NSToIntRound(-yLocs[j]) * dev2appUnits :
-11
View File
@@ -40,8 +40,6 @@
#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)
@@ -574,8 +572,6 @@ 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)
@@ -2170,13 +2166,6 @@ 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;
}
+3 -15
View File
@@ -72,17 +72,7 @@ static cairo_user_data_key_t cairo_gdk_drawable_key;
gfxPlatformGtk::gfxPlatformGtk()
{
// 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);
}
gtk_init(nullptr, nullptr);
mMaxGenericSubstitutions = UNINITIALIZED_VALUE;
@@ -335,10 +325,8 @@ gfxPlatformGtk::GetDPI()
if (!sDPI) {
// Make sure init is run so we have a resolution
GdkScreen *screen = gdk_screen_get_default();
if (screen) { // null when headless (no X display)
gtk_settings_get_for_screen(screen);
sDPI = int32_t(round(gdk_screen_get_resolution(screen)));
}
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;
-834
View File
@@ -1,834 +0,0 @@
/* -*- 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;
}
-150
View File
@@ -1,150 +0,0 @@
/* -*- 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 */
+15 -7
View File
@@ -231,11 +231,13 @@ gfxTextRun::ComputeLigatureData(Range aPartRange,
LigatureData result;
const CompressedGlyph *charGlyphs = mCharacterGlyphs;
uint32_t i;
for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
NS_ASSERTION(i > 0, "Ligature at the start of the run??");
uint32_t i = aPartRange.start;
while (i && !charGlyphs[i].IsLigatureGroupStart()) {
--i;
}
NS_ASSERTION(charGlyphs[i].IsLigatureGroupStart(), "Ligature at run start?");
result.mRange.start = i;
for (i = aPartRange.start + 1;
i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
}
@@ -1704,21 +1706,27 @@ gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh)
RefPtr<gfxFont> font = ff.Font();
if (!font) {
gfxFontEntry* fe = mFonts[i].FontEntry();
gfxCharacterMap* unicodeRangeMap = nullptr;
RefPtr<gfxFontEntry> fe = ff.FontEntry();
if (!fe) {
return nullptr;
}
RefPtr<gfxCharacterMap> unicodeRangeMap;
if (fe->mIsUserFontContainer) {
gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
// This raw pointer is OK because fe holds a strong ref to the object.
gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe.get());
if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
ufe->CharacterInUnicodeRange(aCh) &&
!FontLoadingForFamily(ff.Family(), aCh)) {
ufe->Load();
ff.CheckState(mSkipDrawing);
}
unicodeRangeMap = ufe->GetUnicodeRangeMap();
// Update fe to refer to the actual platform font entry, rather than the
// webfont wrapper. After this, we no longer have a strong ref to ufe.
fe = ufe->GetPlatformFontEntry();
if (!fe) {
return nullptr;
}
unicodeRangeMap = ufe->GetUnicodeRangeMap();
}
font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold(),
unicodeRangeMap);
-16
View File
@@ -74,22 +74,6 @@ 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',
+7 -4
View File
@@ -1637,10 +1637,13 @@ RasterImage::NotifyDecodeComplete(const DecoderFinalStatus& aStatus,
if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
mHasBeenDecoded && mAnimationState) {
// We've finished a full decode of all animation frames and our AnimationState
// has been notified about them all, so let it know not to expect anymore.
mAnimationState->NotifyDecodeComplete();
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
// If we've successfully finished a full decode of all animation frames
// then let our animation state know it's complete and to not to expect
// any more frames.
if (aStatus.mFinished && !aStatus.mHadError) {
mAnimationState->NotifyDecodeComplete();
mAnimationState->UpdateState(mAnimationFinished, this, mSize);
}
}
// Only act on errors if we have no usable frames from the decoder.
-8
View File
@@ -6,14 +6,6 @@
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',
]
-6
View File
@@ -146,13 +146,7 @@ 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();
-135
View File
@@ -1,135 +0,0 @@
/* 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);
}
-4
View File
@@ -9,10 +9,6 @@ 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':
-117
View File
@@ -3,16 +3,10 @@
* 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
@@ -25,82 +19,6 @@ extern "C" int jihad_pmlog_sem_installed(void);
#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"
@@ -120,41 +38,6 @@ 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
-28
View File
@@ -160,13 +160,6 @@ 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;
}
@@ -669,27 +662,6 @@ 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) {
+6 -1
View File
@@ -8,8 +8,11 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/TypeTraits.h"
#include <algorithm>
#include "jscntxt.h"
#include "builtin/SelfHostingDefines.h"
#include "irregexp/RegExpParser.h"
#include "jit/InlinableNatives.h"
#include "vm/RegExpStatics.h"
@@ -1754,7 +1757,9 @@ js::RegExpGetSubstitution(JSContext* cx, HandleArrayObject matchResult, HandleLi
// Step 6.
MOZ_ASSERT(position <= string->length());
uint32_t nCaptures = matchResultLength - 1;
// String substitutions can only reference $1 through $99.
uint32_t nCaptures = std::min<uint32_t>(matchResultLength - 1,
REGEXP_MAX_SUBSTITUTION_CAPTURES);
Rooted<CapturesVector> captures(cx, CapturesVector(cx));
if (!captures.reserve(nCaptures))
return false;
+9 -1
View File
@@ -463,6 +463,12 @@ function RegExpGetComplexReplacement(result, matched, S, position,
// Step 14.k.i (reordered).
_DefineDataProperty(captures, capturesLength++, matched);
// String substitutions can only reference $1 through $99. Keep getting and
// stringifying all captures because those operations are observable.
var storedCaptures = functionalReplace ?
nCaptures :
std_Math_min(nCaptures, REGEXP_MAX_SUBSTITUTION_CAPTURES);
// Step 14.g, 14.i, 14.i.iv.
for (var n = 1; n <= nCaptures; n++) {
// Step 14.i.i.
@@ -473,7 +479,9 @@ function RegExpGetComplexReplacement(result, matched, S, position,
capN = ToString(capN);
// Step 14.i.iii.
_DefineDataProperty(captures, capturesLength++, capN);
if (n <= storedCaptures) {
_DefineDataProperty(captures, capturesLength++, capN);
}
}
// Step 14.j.
+2
View File
@@ -79,6 +79,8 @@
#define REGEXP_FLAGS_SLOT 2
#define REGEXP_MAX_SUBSTITUTION_CAPTURES 99
#define REGEXP_IGNORECASE_FLAG 0x01
#define REGEXP_GLOBAL_FLAG 0x02
#define REGEXP_MULTILINE_FLAG 0x04
+2 -2
View File
@@ -8185,7 +8185,7 @@ CodeGenerator::visitStoreElementV(LStoreElementV* lir)
template <typename T> void
CodeGenerator::emitStoreElementHoleT(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value,
static_assert(std::is_same_v<T, LStoreElementHoleT> || std::is_same_v<T, LFallibleStoreElementT>,
"emitStoreElementHoleT called with unexpected argument type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
@@ -8244,7 +8244,7 @@ CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir)
template <typename T> void
CodeGenerator::emitStoreElementHoleV(T* lir)
{
static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value,
static_assert(std::is_same_v<T, LStoreElementHoleV> || std::is_same_v<T, LFallibleStoreElementV>,
"emitStoreElementHoleV called with unexpected parameter type");
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
+3
View File
@@ -494,6 +494,9 @@ OnLeaveBaselineFrame(JSContext* cx, const JitFrameIterator& frame, jsbytecode* p
{
BaselineFrame* baselineFrame = frame.baselineFrame();
if (jit::DebugEpilogue(cx, baselineFrame, pc, frameOk)) {
if (!baselineFrame->hasReturnValue()) {
baselineFrame->setReturnValue(UndefinedValue());
}
rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
rfe->stackPointer = reinterpret_cast<uint8_t*>(baselineFrame);
-8
View File
@@ -8,14 +8,6 @@
#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 {
+11 -4
View File
@@ -312,12 +312,19 @@ cvt_s(SprintfState* ss, const char* s, int width, int prec, int flags)
s = generic_null_str(s);
// Limit string length by precision value
int slen = int(generic_strlen(s));
if (0 < prec && prec < slen)
slen = prec;
// We'd want to use strnlen() here, but it is not supported by all targets
// (Most notably old OS X), so use memchr instead.
// Replace with `size_t slen = strnlen(s, size_t(prec));` once we no longer
// need to cater to this.
const char* sptr = (const char*)memchr(s, 0, size_t(prec));
size_t slen = sptr ? sptr - s : size_t(prec);
if (slen > INT_MAX) {
return false;
}
// and away we go
return fill2(ss, s, slen, width, flags);
return fill2(ss, s, int(slen), width, flags);
}
/*
+4 -4
View File
@@ -2871,8 +2871,8 @@ static JSString* ReplaceAll(JSContext* cx, JSLinearString* str,
// Step 13.
StringBuffer result(cx);
if (std::is_same<StrChar, char16_t>::value ||
std::is_same<RepChar, char16_t>::value) {
if (std::is_same_v<StrChar, char16_t> ||
std::is_same_v<RepChar, char16_t>) {
if (!result.ensureTwoByteChars()) {
return nullptr;
}
@@ -2954,8 +2954,8 @@ static JSString* ReplaceAllInterleave(JSContext* cx, JSLinearString* str,
// Step 13.
StringBuffer result(cx);
if (std::is_same<StrChar, char16_t>::value ||
std::is_same<RepChar, char16_t>::value) {
if (std::is_same_v<StrChar, char16_t> ||
std::is_same_v<RepChar, char16_t>) {
if (!result.ensureTwoByteChars()) {
return nullptr;
}
+20 -20
View File
@@ -309,26 +309,26 @@ InflateUTF8StringToBuffer(ContextT* cx, const UTF8Chars src, CharT* dst, size_t*
while (v & (0x80 >> n))
n++;
#define INVALID(report, arg, n2) \
do { \
if (Action == CountAndReportInvalids) { \
report(cx, arg); \
return false; \
} else if (Action == AssertNoInvalids) { \
MOZ_CRASH("invalid UTF-8 string: " # report); \
} else { \
if (Action == Copy) { \
if (std::is_same<decltype(dst[0]), Latin1Char>::value) \
dst[j] = CharT(REPLACE_UTF8_LATIN1); \
else \
dst[j] = CharT(REPLACE_UTF8); \
} else { \
MOZ_ASSERT(Action == CountAndIgnoreInvalids || \
Action == FindEncoding); \
} \
n = n2; \
goto invalidMultiByteCodeUnit; \
} \
#define INVALID(report, arg, n2) \
do { \
if (Action == CountAndReportInvalids) { \
report(cx, arg); \
return false; \
} else if (Action == AssertNoInvalids) { \
MOZ_CRASH("invalid UTF-8 string: " # report); \
} else { \
if (Action == Copy) { \
if (std::is_same_v<decltype(dst[0]), Latin1Char>) \
dst[j] = CharT(REPLACE_UTF8_LATIN1); \
else \
dst[j] = CharT(REPLACE_UTF8); \
} else { \
MOZ_ASSERT(Action == CountAndIgnoreInvalids || \
Action == FindEncoding); \
} \
n = n2; \
goto invalidMultiByteCodeUnit; \
} \
} while (0)
// Check the leading byte.
+1 -1
View File
@@ -1138,7 +1138,7 @@ class StaticStrings
}
template <typename CharT,
typename = typename std::enable_if<!std::is_const<CharT>::value>::type>
typename = typename std::enable_if_t<!std::is_const_v<CharT>>>
MOZ_ALWAYS_INLINE JSAtom* lookup(CharT* chars, size_t length) {
// Collapse the remaining |CharT*| to |const CharT*| to avoid excess
// instantiations.
+2 -2
View File
@@ -677,12 +677,12 @@ class ElementSpecific
return true;
}
if (std::is_same<T, int64_t>::value) {
if (std::is_same_v<T, int64_t>) {
JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigInt64(cx, v));
return true;
}
if (std::is_same<T, uint64_t>::value) {
if (std::is_same_v<T, uint64_t>) {
JS_TRY_VAR_OR_RETURN_FALSE(cx, *result, ToBigUint64(cx, v));
return true;
}
+7
View File
@@ -411,6 +411,13 @@ DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius,
mCount = mMaxCount;
break;
}
// GetCountAndLastOverlap() failed without writing |count| and
// |actualOverlap|: the walk needed more than mMaxCount circles, i.e.
// this overlap produces too many circles. Narrow the search toward
// smaller overlaps.
upper = overlap;
overlap = (upper + lower) / 2.0f;
continue;
}
if (j == 0) {
+38
View File
@@ -13,6 +13,7 @@
#include <algorithm> // For std::max
#include "mozilla/EffectSet.h"
#include "mozilla/EventStates.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsLayoutUtils.h"
#include "AnimationCommon.h" // For GetLayerAnimationInfo
#include "FrameLayerBuilder.h"
@@ -260,6 +261,7 @@ RestyleManager::ContentStateChanged(nsIContent* aContent,
ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
PostRestyleEvent(aElement, restyleHint, changeHint);
RestyleForHasPseudoClassChange(aElement);
}
// Forwarded nsIMutationObserver method, to handle restyling.
@@ -371,6 +373,30 @@ RestyleManager::AttributeChanged(Element* aElement,
aOldValue,
rsdata);
PostRestyleEvent(aElement, rshint, hint, &rsdata);
RestyleForHasPseudoClassChange(aElement);
}
bool
RestyleManager::RestyleForHasPseudoClassChange(nsINode* aNode)
{
Element* affectedRoot = nullptr;
for (nsINode* node = aNode; node; node = node->GetParentNode()) {
if (!node->GetProperty(nsGkAtoms::hasSelectorDependency)) {
continue;
}
if (node->IsElement()) {
affectedRoot = node->AsElement();
} else if (ShadowRoot* shadow = ShadowRoot::FromNode(node)) {
affectedRoot = shadow->GetHost();
}
}
if (!affectedRoot) {
return false;
}
PostRestyleEvent(affectedRoot, eRestyle_Subtree, nsChangeHint(0));
return true;
}
/* static */ uint64_t
@@ -399,6 +425,10 @@ RestyleManager::RestyleForAppend(nsIContent* aContainer,
nsIContent* aFirstNewContent)
{
// The container cannot be a document, but might be a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
if (!aContainer->IsElement()) {
return;
}
@@ -488,6 +518,10 @@ RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
nsIContent* aChild)
{
// The container might be a document or a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
if (!aContainer->IsElement()) {
return;
}
@@ -578,6 +612,10 @@ RestyleManager::ContentRemoved(nsINode* aContainer,
nsIContent* aFollowingSibling)
{
// The container might be a document or a ShadowRoot.
if (RestyleForHasPseudoClassChange(aContainer)) {
return;
}
if (!aContainer->IsElement()) {
return;
}
+4
View File
@@ -314,6 +314,10 @@ public:
// the container is null, no work is needed.
void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
// Restyle a subtree containing any :has() anchor affected by a change at
// or below aNode. Returns true when such a restyle was posted.
bool RestyleForHasPseudoClassChange(nsINode* aNode);
// Process any pending restyles. This should be called after
// CreateNeededFrames.
// Note: It's the caller's responsibility to make sure to wrap a
+17 -8
View File
@@ -5107,19 +5107,28 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis,
styleMinBSize.GetCoordValue() == 0)) ||
styleMaxBSize.GetUnit() != eStyleUnit_None) {
AspectRatio ratio = aFrame->GetIntrinsicRatio();
AspectRatio ratio = aFrame->GetAspectRatio();
if (ratio) {
// Convert 'ratio' if necessary, so that it's storing ISize/BSize:
if (!horizontalAxis) {
ratio = ratio.Inverted();
}
AddStateBitToAncestors(aFrame,
NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
const WritingMode wm = aFrame->GetWritingMode();
LogicalSize boxSizingAdjust(wm);
boxSizingAdjust.ISize(wm) =
GetDefiniteSizeTakenByBoxSizing(boxSizing, aFrame, isInlineAxis,
aFlags & IGNORE_PADDING,
aPercentageBasis);
nscoord bSizeTakenByBoxSizing =
GetDefiniteSizeTakenByBoxSizing(boxSizing, aFrame, !isInlineAxis,
aFlags & IGNORE_PADDING,
aPercentageBasis);
boxSizingAdjust.BSize(wm) = bSizeTakenByBoxSizing;
auto iSizeFromBSize = [&](nscoord aBSize) {
LogicalAxis dependentAxis =
isInlineAxis ? eLogicalAxisInline : eLogicalAxisBlock;
return ratio.ComputeRatioDependentSize(
dependentAxis, wm, aBSize, boxSizingAdjust);
};
// NOTE: This is only the minContentSize if we've been passed MIN_INTRINSIC_ISIZE
// (which is fine, because this should only be used inside a check for that flag).
nscoord minContentSize = result;
@@ -5128,14 +5137,14 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis,
(aPercentageBasis.isNothing() &&
GetPercentBSize(styleBSize, aFrame, horizontalAxis, h))) {
h = std::max(0, h - bSizeTakenByBoxSizing);
result = ratio.ApplyTo(h);
result = iSizeFromBSize(h);
}
if (GetDefiniteSize(styleMaxBSize, aFrame, !isInlineAxis, aPercentageBasis, &h) ||
(aPercentageBasis.isNothing() &&
GetPercentBSize(styleMaxBSize, aFrame, horizontalAxis, h))) {
h = std::max(0, h - bSizeTakenByBoxSizing);
nscoord maxISize = ratio.ApplyTo(h);
nscoord maxISize = iSizeFromBSize(h);
if (maxISize < result) {
result = maxISize;
}
@@ -5148,7 +5157,7 @@ nsLayoutUtils::IntrinsicForAxis(PhysicalAxis aAxis,
(aPercentageBasis.isNothing() &&
GetPercentBSize(styleMinBSize, aFrame, horizontalAxis, h))) {
h = std::max(0, h - bSizeTakenByBoxSizing);
nscoord minISize = ratio.ApplyTo(h);
nscoord minISize = iSizeFromBSize(h);
if (minISize > result) {
result = minISize;
}
+18 -1
View File
@@ -234,7 +234,18 @@ nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
{
WritingMode wm = GetWritingMode();
LogicalSize availSize = aButtonReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_INTRINSICSIZE;
// Identify if the inner anonymous block is actually functioning as a flex or grid container.
nsIAtom* kidType = aFirstKid->GetType();
bool isFlexOrGrid = (kidType == nsGkAtoms::flexContainerFrame ||
kidType == nsGkAtoms::gridContainerFrame);
// Preserve the button's definite BSize ONLY for flex/grid children so they
// see a definite containing block main size. For standard buttons, clobber
// to intrinsic so standard vertical centering (extraSpace) calculations work.
if (!isFlexOrGrid || aButtonReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
availSize.BSize(wm) = NS_INTRINSICSIZE;
}
// shorthand for a value we need to use in a bunch of places
const LogicalMargin& clbp = aButtonReflowInput.ComputedLogicalBorderPadding();
@@ -248,6 +259,12 @@ nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
aFirstKid,
availSize);
// Apply the definite BSize directly on the inner reflow input ONLY if it's
// a flex or grid container, allowing it to be used as-is.
if (isFlexOrGrid && aButtonReflowInput.ComputedBSize() != NS_INTRINSICSIZE) {
contentsReflowInput.SetComputedBSize(aButtonReflowInput.ComputedBSize());
}
nsReflowStatus contentsReflowStatus;
ReflowOutput contentsDesiredSize(aButtonReflowInput);
childPos.B(wm) = 0; // This will be set properly later, after reflowing the
Loaded 100 of 234 files, more files were not shown because too many files have changed in this diff. Show more