mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
edefedeca8
@ -3,12 +3,8 @@
|
||||
// Send these sites a custom user-agent. Bugs to remove each override after
|
||||
// evangelism are included.
|
||||
{
|
||||
// bug 826335, globo.com
|
||||
"globo.com": "\\(Mobile#(Android; Mobile",
|
||||
// bug 826347, msn.com
|
||||
"msn.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
|
||||
// bug 826348, linkedin.com
|
||||
"linkedin.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
|
||||
// bug 826353, itau.com.br
|
||||
"itau.com.br": "\\(Mobile#(Android; Mobile",
|
||||
// bug 826510, r7.com
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
@ -129,7 +129,7 @@
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
|
||||
<!-- dolphin specific things -->
|
||||
<project name="device/sprd" path="device/sprd" revision="b007fe5fb2c214bd71133378248113e6e74ce4d9"/>
|
||||
<project name="device/sprd" path="device/sprd" revision="14267a1ca51dcefe56d031245c6252c9d7402ce5"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
|
||||
<project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
|
||||
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f11d3c6500659e3232fbe6fe7ea0204c40ab7fdd"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f11d3c6500659e3232fbe6fe7ea0204c40ab7fdd"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "3d66911e8f085901b6302ec2b40bc89bc5322db4",
|
||||
"revision": "24f27472ae6ddb6c819227cb3b7d398a6925ef86",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f11d3c6500659e3232fbe6fe7ea0204c40ab7fdd"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ca6e91e09ef3ab417a0f6b6d6668d43597d85700"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c5b03a9b40a37947d44a71eccd9017e76632f796"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="8aee09c106f479f36c57b2a29af72d455e359211"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -281,9 +281,9 @@ let MozLoopPushHandler = {
|
||||
}
|
||||
|
||||
let pushServerURLFetchError = () => {
|
||||
console.warn("MozLoopPushHandler - Could not retrieve push server URL from Loop server; using default");
|
||||
this.pushServerUri = Services.prefs.getCharPref("services.push.serverURL");
|
||||
performOpen();
|
||||
console.warn("MozLoopPushHandler - Could not retrieve push server URL from Loop server, will retry");
|
||||
this._retryOperation(() => this._openSocket());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pushServerUri) {
|
||||
@ -303,6 +303,7 @@ let MozLoopPushHandler = {
|
||||
}
|
||||
if (pushServerConfig.pushServerURI) {
|
||||
this.pushServerUri = pushServerConfig.pushServerURI;
|
||||
this._retryEnd();
|
||||
performOpen();
|
||||
} else {
|
||||
console.warn("MozLoopPushHandler - push server URL config lacks pushServerURI parameter");
|
||||
|
@ -65,6 +65,7 @@ function setup_params(request, response) {
|
||||
if (request.method == "DELETE") {
|
||||
setSharedState("/fxa-oauth/params", "");
|
||||
setSharedState("/registration", "");
|
||||
setSharedState("/fxa-oauth/token", "");
|
||||
response.write("Params deleted");
|
||||
return;
|
||||
}
|
||||
@ -166,13 +167,14 @@ function token(request, response) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tokenData = {
|
||||
access_token: payload.code + "_access_token",
|
||||
scope: "profile",
|
||||
token_type: "bearer",
|
||||
};
|
||||
let tokenData = JSON.stringify(
|
||||
{access_token: payload.code + "_access_token",
|
||||
scope: "profile",
|
||||
token_type: "bearer"},
|
||||
null, 2);
|
||||
setSharedState("/fxa-oauth/token", tokenData);
|
||||
response.setHeader("Content-Type", "application/json; charset=utf-8", false);
|
||||
response.write(JSON.stringify(tokenData, null, 2));
|
||||
response.write(tokenData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,17 +196,27 @@ function profile(request, response) {
|
||||
* Mock Loop registration endpoint. Hawk Authorization headers are expected only for FxA sessions.
|
||||
*/
|
||||
function registration(request, response) {
|
||||
let isFxARequest = function(payload) {
|
||||
return (payload.simplePushURL == "https://localhost/pushUrl/fxa" ||
|
||||
payload.simplePushURLs.calls == "https://localhost/pushUrl/fxa-calls" ||
|
||||
payload.simplePushURLs.rooms == "https://localhost/pushUrl/fxa-rooms");
|
||||
};
|
||||
|
||||
let body = NetUtil.readInputStreamToString(request.bodyInputStream,
|
||||
request.bodyInputStream.available());
|
||||
let payload = JSON.parse(body);
|
||||
if ((payload.simplePushURL == "https://localhost/pushUrl/fxa" ||
|
||||
payload.simplePushURLs.calls == "https://localhost/pushUrl/fxa-calls" ||
|
||||
payload.simplePushURLs.rooms == "https://localhost/pushUrl/fxa-rooms") &&
|
||||
(!request.hasHeader("Authorization") ||
|
||||
!request.getHeader("Authorization").startsWith("Hawk"))) {
|
||||
response.setStatusLine(request.httpVersion, 401, "Missing Hawk");
|
||||
response.write("401 Missing Hawk Authorization header");
|
||||
return;
|
||||
if (isFxARequest(payload)) {
|
||||
if (!request.hasHeader("Authorization") ||
|
||||
!request.getHeader("Authorization").startsWith("Hawk")) {
|
||||
response.setStatusLine(request.httpVersion, 401, "Missing Hawk");
|
||||
response.write("401 Missing Hawk Authorization header");
|
||||
return;
|
||||
}
|
||||
if (!getSharedState("/fxa-oauth/token")) {
|
||||
response.setStatusLine(request.httpVersion, 409, "Wrong State");
|
||||
response.write("409 complete OAuth before attempting registration");
|
||||
return;
|
||||
}
|
||||
}
|
||||
setSharedState("/registration", body);
|
||||
}
|
||||
|
@ -3,15 +3,54 @@
|
||||
{
|
||||
let dummyCallback = () => {};
|
||||
let mockWebSocket = new MockWebSocketChannel();
|
||||
let pushServerRequestCount = 0;
|
||||
|
||||
add_test(function test_initalize_offline() {
|
||||
Services.io.offline = true;
|
||||
do_check_false(MozLoopPushHandler.initialize());
|
||||
Services.io.offline = false;
|
||||
MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_initalize() {
|
||||
loopServer.registerPathHandler("/push-server-config", (request, response) => {
|
||||
// The PushHandler should retry the request for the push-server-config for
|
||||
// each of these cases without throwing an error.
|
||||
let n = 0;
|
||||
switch (++pushServerRequestCount) {
|
||||
case ++n:
|
||||
// Non-200 response
|
||||
response.setStatusLine(null, 500, "Retry");
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
break;
|
||||
case ++n:
|
||||
// missing parameter
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify({pushServerURI: null}));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
break;
|
||||
case ++n:
|
||||
// json parse error
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
break;
|
||||
case ++n:
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
|
||||
run_next_test();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
do_check_true(MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket}));
|
||||
});
|
||||
|
||||
add_test(function test_initalize_missing_chanid() {
|
||||
Assert.throws(() => {MozLoopPushHandler.register(null, dummyCallback, dummyCallback)});
|
||||
run_next_test();
|
||||
@ -104,11 +143,13 @@
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
|
||||
Services.prefs.setCharPref("loop.server", kLoopServerUrl);
|
||||
Services.prefs.setIntPref("loop.retry_delay.start", 10); // 10 ms
|
||||
Services.prefs.setIntPref("loop.retry_delay.limit", 20); // 20 ms
|
||||
|
||||
do_register_cleanup(function() {
|
||||
Services.prefs.setCharPref("loop.server", kLoopServerUrl);
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ function checkState(tab) {
|
||||
// deserialized in the content scope. And in this case, since RegExps are
|
||||
// not currently Xrayable (see bug 1014991), trying to pull |obj3| (a RegExp)
|
||||
// off of an Xrayed Object won't work. So we need to waive.
|
||||
runInContent(tab.linkedBrowser, function(win, state) {
|
||||
return Cu.waiveXrays(state).obj3.toString();
|
||||
}, aEvent.state).then(function(stateStr) {
|
||||
runInContent(tab.linkedBrowser, function(win, event) {
|
||||
return Cu.waiveXrays(event.state).obj3.toString();
|
||||
}, aEvent).then(function(stateStr) {
|
||||
is(stateStr, '/^a$/', "second popstate object.");
|
||||
|
||||
// Make sure that the new-elem node is present in the document. If it's
|
||||
|
@ -233,7 +233,7 @@ function finishTests() {
|
||||
|
||||
// Sends a click event on the passed DOM node in an async manner
|
||||
function click(node) {
|
||||
node.scrollIntoView()
|
||||
node.scrollIntoView();
|
||||
executeSoon(() => EventUtils.synthesizeMouseAtCenter(node, {}, gPanelWindow));
|
||||
}
|
||||
|
||||
@ -483,8 +483,35 @@ function selectTreeItem(ids) {
|
||||
// Expand tree as some/all items could be collapsed leading to click on an
|
||||
// incorrect tree item
|
||||
gUI.tree.expandAll();
|
||||
click(gPanelWindow.document.querySelector("[data-id='" + JSON.stringify(ids) +
|
||||
"'] > .tree-widget-item"));
|
||||
let target = gPanelWindow.document.querySelector(
|
||||
"[data-id='" + JSON.stringify(ids) + "'] > .tree-widget-item");
|
||||
|
||||
// Look for an animating list
|
||||
//
|
||||
// If the list is still expanding we won't be able to click the requested
|
||||
// item. We detect if a list (or one of its ancestor lists) is animating
|
||||
// by checking if max-height is set. If the animation effect in
|
||||
// widgets.inc.css changes this will likely break but until the Web
|
||||
// Animations API is available (specifically, bug 1074630 and bug 1112422)
|
||||
// there is no reliable way to detect if animations are running, particularly
|
||||
// in the face of interactions that can cancel an animation without triggering
|
||||
// an animationend event.
|
||||
let animatingList = target.nextElementSibling;
|
||||
while (animatingList) {
|
||||
if (window.getComputedStyle(animatingList).maxHeight != "none") {
|
||||
break;
|
||||
}
|
||||
animatingList = animatingList.parentNode.closest(".tree-widget-item + ul");
|
||||
}
|
||||
|
||||
if (animatingList) {
|
||||
animatingList.addEventListener("animationend", function animationend() {
|
||||
animatingList.removeEventListener("animationend", animationend);
|
||||
click(target);
|
||||
});
|
||||
} else {
|
||||
click(target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +161,6 @@ http://tracking.example.org:80
|
||||
|
||||
# Bug 483437, 484111
|
||||
https://www.bank1.com:443 privileged,cert=escapeattack1
|
||||
https://www.bank2.com:443 privileged,cert=escapeattack2
|
||||
|
||||
#
|
||||
# CONNECT for redirproxy results in a 302 redirect to
|
||||
|
@ -133,9 +133,7 @@ AnimationPlayer::SetSource(Animation* aSource)
|
||||
void
|
||||
AnimationPlayer::Tick()
|
||||
{
|
||||
if (mSource) {
|
||||
mSource->SetParentTime(GetCurrentTime());
|
||||
}
|
||||
UpdateSourceContent();
|
||||
}
|
||||
|
||||
void
|
||||
@ -275,6 +273,14 @@ AnimationPlayer::DoPause()
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::UpdateSourceContent()
|
||||
{
|
||||
if (mSource) {
|
||||
mSource->SetParentTime(GetCurrentTime());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::FlushStyle() const
|
||||
{
|
||||
|
@ -21,6 +21,12 @@
|
||||
#undef CurrentTime
|
||||
#endif
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount().
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
struct JSContext;
|
||||
class nsCSSPropertySet;
|
||||
class nsIDocument;
|
||||
@ -131,6 +137,7 @@ protected:
|
||||
void DoPlay();
|
||||
void DoPause();
|
||||
|
||||
void UpdateSourceContent();
|
||||
void FlushStyle() const;
|
||||
void PostUpdate();
|
||||
// Remove this player from the pending player tracker and resets mIsPending
|
||||
|
@ -38,34 +38,104 @@ AnimationTimeline::GetCurrentTimeAsDouble() const
|
||||
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
|
||||
}
|
||||
|
||||
void
|
||||
AnimationTimeline::FastForward(const TimeStamp& aTimeStamp)
|
||||
{
|
||||
// If we have already been fast-forwarded to an equally or more
|
||||
// recent time, ignore this call.
|
||||
if (!mFastForwardTime.IsNull() && aTimeStamp <= mFastForwardTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the refresh driver is under test control then its values have little
|
||||
// connection to TimeStamp values and it doesn't make sense to fast-forward
|
||||
// the timeline to a TimeStamp value.
|
||||
//
|
||||
// Furthermore, when the refresh driver is under test control,
|
||||
// nsDOMWindowUtils::AdvanceTimeAndRefresh automatically starts any
|
||||
// pending animation players so we don't need to fast-forward the timeline
|
||||
// anyway.
|
||||
nsRefreshDriver* refreshDriver = GetRefreshDriver();
|
||||
if (refreshDriver && refreshDriver->IsTestControllingRefreshesEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bug 1113413: If the refresh driver has just been restored from test
|
||||
// control it's possible that aTimeStamp could be before the most recent
|
||||
// refresh.
|
||||
if (refreshDriver &&
|
||||
aTimeStamp < refreshDriver->MostRecentRefresh()) {
|
||||
mFastForwardTime = refreshDriver->MostRecentRefresh();
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: For all animations attached to this timeline, we should mark
|
||||
// their target elements as needing restyling. Otherwise, tasks that run
|
||||
// in between now and the next refresh driver tick might see inconsistencies
|
||||
// between the timing of an animation and the computed style of its target.
|
||||
|
||||
mFastForwardTime = aTimeStamp;
|
||||
}
|
||||
|
||||
TimeStamp
|
||||
AnimationTimeline::GetCurrentTimeStamp() const
|
||||
{
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
TimeStamp result = mLastCurrentTime;
|
||||
nsRefreshDriver* refreshDriver = GetRefreshDriver();
|
||||
TimeStamp refreshTime = refreshDriver
|
||||
? refreshDriver->MostRecentRefresh()
|
||||
: TimeStamp();
|
||||
|
||||
// If we've never been sampled, initialize the current time to the timeline's
|
||||
// zero time since that is the time we'll use if we don't have a refresh
|
||||
// driver.
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
TimeStamp result = !refreshTime.IsNull()
|
||||
? refreshTime
|
||||
: mLastRefreshDriverTime;
|
||||
|
||||
// If we don't have a refresh driver and we've never had one use the
|
||||
// timeline's zero time.
|
||||
if (result.IsNull()) {
|
||||
nsRefPtr<nsDOMNavigationTiming> timing = mDocument->GetNavigationTiming();
|
||||
if (!timing) {
|
||||
return result;
|
||||
if (timing) {
|
||||
result = timing->GetNavigationStartTimeStamp();
|
||||
// Also, let this time represent the current refresh time. This way
|
||||
// we'll save it as the last refresh time and skip looking up
|
||||
// navigation timing each time.
|
||||
refreshTime = result;
|
||||
}
|
||||
result = timing->GetNavigationStartTimeStamp();
|
||||
}
|
||||
|
||||
nsRefreshDriver* refreshDriver = GetRefreshDriver();
|
||||
if (!refreshDriver) {
|
||||
return result;
|
||||
// The timeline may have been fast-forwarded to account for animations
|
||||
// that begin playing between ticks of the refresh driver. If so, we should
|
||||
// use the fast-forward time unless we've already gone past that time.
|
||||
//
|
||||
// (If the refresh driver were ever to go backwards then we would need to
|
||||
// ignore the fast-forward time in that case to prevent the timeline getting
|
||||
// "stuck" until the refresh driver caught up. However, the only time the
|
||||
// refresh driver goes backwards is when it is restored from test control
|
||||
// and FastForward makes sure we don't set the fast foward time when we
|
||||
// are under test control.)
|
||||
MOZ_ASSERT(refreshTime.IsNull() || mLastRefreshDriverTime.IsNull() ||
|
||||
refreshTime >= mLastRefreshDriverTime ||
|
||||
mFastForwardTime.IsNull(),
|
||||
"The refresh driver time should not go backwards when the"
|
||||
" fast-forward time is set");
|
||||
|
||||
// We need to check if mFastForwardTime is ahead of the refresh driver
|
||||
// time. This is because mFastForwardTime can still be set after the next
|
||||
// refresh driver tick since we don't clear mFastForwardTime on a call to
|
||||
// Tick() as we aren't currently guaranteed to get only one call to Tick()
|
||||
// per refresh-driver tick.
|
||||
if (result.IsNull() ||
|
||||
(!mFastForwardTime.IsNull() && mFastForwardTime > result)) {
|
||||
result = mFastForwardTime;
|
||||
} else {
|
||||
// Make sure we continue to ignore the fast-forward time.
|
||||
mFastForwardTime = TimeStamp();
|
||||
}
|
||||
|
||||
if (!refreshTime.IsNull()) {
|
||||
mLastRefreshDriverTime = refreshTime;
|
||||
}
|
||||
|
||||
result = refreshDriver->MostRecentRefresh();
|
||||
// FIXME: We would like to assert that:
|
||||
// mLastCurrentTime.IsNull() || result >= mLastCurrentTime
|
||||
// but due to bug 1043078 this will not be the case when the refresh driver
|
||||
// is restored from test control.
|
||||
mLastCurrentTime = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -50,16 +50,36 @@ public:
|
||||
Nullable<TimeDuration> ToTimelineTime(const TimeStamp& aTimeStamp) const;
|
||||
TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const;
|
||||
|
||||
// Force the timeline to advance to |aTimeStamp|.
|
||||
//
|
||||
// Normally the timeline uses the refresh driver time but when we have
|
||||
// animations that are timed from when their first frame is rendered we need
|
||||
// to bring the timeline forward to that moment. If we don't, calling
|
||||
// IsRunning() will incorrectly return false (because GetCurrentTime() will
|
||||
// return a negative time) until the next refresh driver tick causes the
|
||||
// timeline to catch up.
|
||||
//
|
||||
// |aTimeStamp| must be greater or equal to the current refresh driver
|
||||
// time for the document with which this timeline is associated unless the
|
||||
// refresh driver is under test control, in which case this method will
|
||||
// be a no-op.
|
||||
void FastForward(const TimeStamp& aTimeStamp);
|
||||
|
||||
nsRefreshDriver* GetRefreshDriver() const;
|
||||
|
||||
protected:
|
||||
TimeStamp GetCurrentTimeStamp() const;
|
||||
nsRefreshDriver* GetRefreshDriver() const;
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
||||
// Store the most recently returned value of current time. This is used
|
||||
// in cases where we don't have a refresh driver (e.g. because we are in
|
||||
// a display:none iframe).
|
||||
mutable TimeStamp mLastCurrentTime;
|
||||
// The most recently used refresh driver time. This is used in cases where
|
||||
// we don't have a refresh driver (e.g. because we are in a display:none
|
||||
// iframe).
|
||||
mutable TimeStamp mLastRefreshDriverTime;
|
||||
|
||||
// The time to which the timeline has been forced-to in order to account for
|
||||
// animations that are started in-between frames.
|
||||
mutable TimeStamp mFastForwardTime;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -5,11 +5,15 @@
|
||||
|
||||
#include "PendingPlayerTracker.h"
|
||||
|
||||
#include "mozilla/dom/AnimationTimeline.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(PendingPlayerTracker, mPlayPendingSet)
|
||||
NS_IMPL_CYCLE_COLLECTION(PendingPlayerTracker, mPlayPendingSet, mDocument)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingPlayerTracker, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingPlayerTracker, Release)
|
||||
@ -18,6 +22,11 @@ void
|
||||
PendingPlayerTracker::AddPlayPending(dom::AnimationPlayer& aPlayer)
|
||||
{
|
||||
mPlayPendingSet.PutEntry(&aPlayer);
|
||||
|
||||
// Schedule a paint. Otherwise animations that don't trigger a paint by
|
||||
// themselves (e.g. CSS animations with an empty keyframes rule) won't
|
||||
// start until something else paints.
|
||||
EnsurePaintIsScheduled();
|
||||
}
|
||||
|
||||
void
|
||||
@ -32,4 +41,56 @@ PendingPlayerTracker::IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const
|
||||
return mPlayPendingSet.Contains(const_cast<dom::AnimationPlayer*>(&aPlayer));
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
StartPlayerAtTime(nsRefPtrHashKey<dom::AnimationPlayer>* aKey,
|
||||
void* aReadyTime)
|
||||
{
|
||||
dom::AnimationPlayer* player = aKey->GetKey();
|
||||
|
||||
// For animations that are waiting until their first frame has rendered
|
||||
// before starting, we record the moment when they finish painting
|
||||
// as the "ready time" and make any pending layer animations start at
|
||||
// that time.
|
||||
//
|
||||
// Here we fast-forward the player's timeline to the same "ready time" and
|
||||
// then tell the player to start at the timeline's current time.
|
||||
//
|
||||
// Redundant calls to FastForward with the same ready time are ignored by
|
||||
// AnimationTimeline.
|
||||
dom::AnimationTimeline* timeline = player->Timeline();
|
||||
timeline->FastForward(*static_cast<const TimeStamp*>(aReadyTime));
|
||||
|
||||
player->StartNow();
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
PendingPlayerTracker::StartPendingPlayers(const TimeStamp& aReadyTime)
|
||||
{
|
||||
mPlayPendingSet.EnumerateEntries(StartPlayerAtTime,
|
||||
const_cast<TimeStamp*>(&aReadyTime));
|
||||
mPlayPendingSet.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
PendingPlayerTracker::EnsurePaintIsScheduled()
|
||||
{
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = mDocument->GetShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIFrame* rootFrame = presShell->GetRootFrame();
|
||||
if (!rootFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
rootFrame->SchedulePaint();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -8,13 +8,20 @@
|
||||
|
||||
#include "mozilla/dom/AnimationPlayer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
class nsIFrame;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PendingPlayerTracker MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
explicit PendingPlayerTracker(nsIDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
{ }
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingPlayerTracker)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingPlayerTracker)
|
||||
|
||||
@ -22,13 +29,21 @@ public:
|
||||
void RemovePlayPending(dom::AnimationPlayer& aPlayer);
|
||||
bool IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const;
|
||||
|
||||
// Causes any pending players to resume at |aReadyTime| by first
|
||||
// fast-forwarding their timeline to the corresponding time.
|
||||
void StartPendingPlayers(const TimeStamp& aReadyTime);
|
||||
bool HasPendingPlayers() const { return mPlayPendingSet.Count() > 0; }
|
||||
|
||||
private:
|
||||
~PendingPlayerTracker() { }
|
||||
|
||||
void EnsurePaintIsScheduled();
|
||||
|
||||
typedef nsTHashtable<nsRefPtrHashKey<dom::AnimationPlayer>>
|
||||
AnimationPlayerSet;
|
||||
|
||||
AnimationPlayerSet mPlayPendingSet;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "nsFrameManager.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "mozilla/dom/Touch.h"
|
||||
#include "mozilla/PendingPlayerTracker.h"
|
||||
#include "nsIObjectLoadingContent.h"
|
||||
#include "nsFrame.h"
|
||||
#include "mozilla/layers/ShadowLayers.h"
|
||||
@ -2654,6 +2655,23 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds)
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsRefreshDriver* driver = GetPresContext()->RefreshDriver();
|
||||
|
||||
// Before we advance the time, we should trigger any animations that are
|
||||
// waiting to start. This is because there are many tests that call this
|
||||
// which expect animations to start immediately. Ideally, we should make
|
||||
// all these tests do an asynchronous wait on the corresponding animation
|
||||
// player's 'ready' promise before continuing. Then we could remove the
|
||||
// special handling here and the code path followed when testing would
|
||||
// more closely match the code path during regular operation. Filed as
|
||||
// bug 1112957.
|
||||
nsCOMPtr<nsIDocument> doc = GetDocument();
|
||||
if (doc) {
|
||||
PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
|
||||
if (tracker) {
|
||||
tracker->StartPendingPlayers(driver->MostRecentRefresh());
|
||||
}
|
||||
}
|
||||
|
||||
driver->AdvanceTimeAndRefresh(aMilliseconds);
|
||||
|
||||
RefPtr<LayerTransactionChild> transaction = GetLayerTransaction();
|
||||
|
@ -7397,7 +7397,7 @@ PendingPlayerTracker*
|
||||
nsDocument::GetOrCreatePendingPlayerTracker()
|
||||
{
|
||||
if (!mPendingPlayerTracker) {
|
||||
mPendingPlayerTracker = new PendingPlayerTracker();
|
||||
mPendingPlayerTracker = new PendingPlayerTracker(this);
|
||||
}
|
||||
|
||||
return mPendingPlayerTracker;
|
||||
|
@ -1614,11 +1614,6 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
||||
return true;
|
||||
}
|
||||
|
||||
nsRefPtr<SelectionCarets> selectionCarets = presShell->GetSelectionCarets();
|
||||
if (selectionCarets) {
|
||||
selectionCarets->NotifyBlur();
|
||||
}
|
||||
|
||||
bool clearFirstBlurEvent = false;
|
||||
if (!mFirstBlurEvent) {
|
||||
mFirstBlurEvent = content;
|
||||
@ -1688,8 +1683,14 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
||||
|
||||
// if we are leaving the document or the window was lowered, make the caret
|
||||
// invisible.
|
||||
if (aIsLeavingDocument || !mActiveWindow)
|
||||
if (aIsLeavingDocument || !mActiveWindow) {
|
||||
SetCaretVisible(presShell, false, nullptr);
|
||||
nsRefPtr<SelectionCarets> selectionCarets = presShell->GetSelectionCarets();
|
||||
if (selectionCarets) {
|
||||
selectionCarets->NotifyBlur();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// at this point, it is expected that this window will be still be
|
||||
// focused, but the focused content will be null, as it was cleared before
|
||||
|
@ -197,24 +197,6 @@
|
||||
let savedElement = null;
|
||||
function recvDomTest(message) {
|
||||
savedElement = message.objects.element;
|
||||
|
||||
// Test to ensure that we don't pass CPOWs to C++-implemented interfaces.
|
||||
// See bug 1072980.
|
||||
if (test_state == "remote") {
|
||||
let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"]
|
||||
.createInstance(Components.interfaces.inIDeepTreeWalker);
|
||||
const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT;
|
||||
walker.showAnonymousContent = true;
|
||||
walker.showSubDocuments = false;
|
||||
|
||||
try {
|
||||
walker.init(savedElement, SHOW_ELEMENT);
|
||||
ok(false, "expected exception passing CPOW to C++");
|
||||
} catch (e) {
|
||||
is(e.result, Components.results.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,
|
||||
"got exception when passing CPOW to C++");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recvDomTestAfterGC(message) {
|
||||
|
@ -1,12 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
window.addEventListener("focus", function() { window.close(); }, false);
|
||||
function done() {
|
||||
window.focus();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="done();">
|
||||
</body>
|
||||
</html>
|
@ -5,7 +5,6 @@ support-files =
|
||||
bug299673.js
|
||||
bug322588-popup.html
|
||||
bug426082.html
|
||||
bug457672.html
|
||||
bug656379-1.html
|
||||
error_event_worker.js
|
||||
empty.js
|
||||
@ -53,7 +52,7 @@ skip-if = buildapp == 'mulet'
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_bug456273.html]
|
||||
[test_bug457672.html]
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #CRASH_DUMP, RANDOM
|
||||
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #CRASH_DUMP, RANDOM
|
||||
[test_bug489671.html]
|
||||
[test_bug493251.html]
|
||||
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
|
||||
|
@ -48,7 +48,8 @@ function startTest() {
|
||||
// Note, focus/blur don't bubble
|
||||
window.addEventListener("focus", listener, false);
|
||||
window.addEventListener("blur", listener, false);
|
||||
window.open("bug457672.html", "", "");
|
||||
var subwin = window.open("about:blank", "", "");
|
||||
subwin.addEventListener("focus", function(e) { subwin.close(); }, false);
|
||||
}
|
||||
|
||||
addLoadEvent(startTest);
|
||||
|
@ -7287,7 +7287,8 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
nsTArray<nsFilePickerFilter> filters;
|
||||
nsString allExtensionsList;
|
||||
|
||||
bool allFiltersAreValid = true;
|
||||
bool allMimeTypeFiltersAreValid = true;
|
||||
bool atLeastOneFileExtensionFilter = false;
|
||||
|
||||
// Retrieve all filters
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
@ -7314,6 +7315,10 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
filterMask = nsIFilePicker::filterVideo;
|
||||
filterBundle->GetStringFromName(MOZ_UTF16("videoFilter"),
|
||||
getter_Copies(extensionListStr));
|
||||
} else if (token.First() == '.') {
|
||||
extensionListStr = NS_LITERAL_STRING("*") + token;
|
||||
filterName = extensionListStr + NS_LITERAL_STRING("; ");
|
||||
atLeastOneFileExtensionFilter = true;
|
||||
} else {
|
||||
//... if no image/audio/video filter is found, check mime types filters
|
||||
nsCOMPtr<nsIMIMEInfo> mimeInfo;
|
||||
@ -7322,7 +7327,7 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
EmptyCString(), // No extension
|
||||
getter_AddRefs(mimeInfo))) ||
|
||||
!mimeInfo) {
|
||||
allFiltersAreValid = false;
|
||||
allMimeTypeFiltersAreValid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -7355,7 +7360,7 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
|
||||
if (!filterMask && (extensionListStr.IsEmpty() || filterName.IsEmpty())) {
|
||||
// No valid filter found
|
||||
allFiltersAreValid = false;
|
||||
allMimeTypeFiltersAreValid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -7377,6 +7382,28 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove similar filters
|
||||
// Iterate over a copy, as we might modify the original filters list
|
||||
nsTArray<nsFilePickerFilter> filtersCopy;
|
||||
filtersCopy = filters;
|
||||
for (uint32_t i = 0; i < filtersCopy.Length(); ++i) {
|
||||
const nsFilePickerFilter& filterToCheck = filtersCopy[i];
|
||||
if (filterToCheck.mFilterMask) {
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < filtersCopy.Length(); ++j) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
if (FindInReadable(filterToCheck.mFilter, filtersCopy[j].mFilter)) {
|
||||
// We already have a similar, less restrictive filter (i.e.
|
||||
// filterToCheck extensionList is just a subset of another filter
|
||||
// extension list): remove this one
|
||||
filters.RemoveElement(filterToCheck);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add "All Supported Types" filter
|
||||
if (filters.Length() > 1) {
|
||||
nsXPIDLString title;
|
||||
@ -7395,9 +7422,8 @@ HTMLInputElement::SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker)
|
||||
}
|
||||
}
|
||||
|
||||
// If all filters are known/valid, select the first filter as default;
|
||||
// otherwise filterAll will remain the default filter
|
||||
if (filters.Length() >= 1 && allFiltersAreValid) {
|
||||
if (filters.Length() >= 1 &&
|
||||
(allMimeTypeFiltersAreValid || atLeastOneFileExtensionFilter)) {
|
||||
// |filterAll| will always use index=0 so we need to set index=1 as the
|
||||
// current filter.
|
||||
filePicker->SetFilterIndex(1);
|
||||
|
@ -320,12 +320,20 @@ public:
|
||||
*
|
||||
* @note You should not call this function if the element has no @accept.
|
||||
* @note "All Files" filter is always set, no matter if there is a valid
|
||||
* filter specifed or not.
|
||||
* @note If there is only one valid filter that is audio or video or image,
|
||||
* it will be selected as the default filter. Otherwise "All files" remains
|
||||
* the default filter.
|
||||
* filter specified or not.
|
||||
* @note If more than one valid filter is found, the "All Supported Types"
|
||||
* filter is added, which is the concatenation of all valid filters.
|
||||
* @note Duplicate filters and similar filters (i.e. filters whose file
|
||||
* extensions already exist in another filter) are ignored.
|
||||
* @note "All Files" filter will be selected by default if unknown mime types
|
||||
* have been specified and no file extension filter has been specified.
|
||||
* Otherwise, specified filter or "All Supported Types" filter will be
|
||||
* selected by default.
|
||||
* The logic behind is that having unknown mime type means we might restrict
|
||||
* user's input too much, as some filters will be missing.
|
||||
* However, if author has also specified some file extension filters, it's
|
||||
* likely those are fallback for the unusual mime type we haven't been able
|
||||
* to resolve; so it's better to select author specified filters in that case.
|
||||
*/
|
||||
void SetFilePickerFiltersFromAccept(nsIFilePicker* filePicker);
|
||||
|
||||
|
@ -101,6 +101,7 @@ static PRLogModuleInfo* gMediaElementEventsLog;
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
@ -1369,7 +1370,7 @@ HTMLMediaElement::Seek(double aTime,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// aTime should be non-NaN.
|
||||
MOZ_ASSERT(aTime == aTime);
|
||||
MOZ_ASSERT(!mozilla::IsNaN(aTime));
|
||||
|
||||
StopSuspendingAfterFirstFrame();
|
||||
|
||||
@ -1493,7 +1494,7 @@ HTMLMediaElement::Seek(double aTime,
|
||||
NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
|
||||
{
|
||||
// Detect for a NaN and invalid values.
|
||||
if (aCurrentTime != aCurrentTime) {
|
||||
if (mozilla::IsNaN(aCurrentTime)) {
|
||||
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: bad time", this, aCurrentTime));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -32,6 +32,11 @@ function ok(condition, msg) {
|
||||
// test failures, so work around it on Linux until we can get a proper fix.
|
||||
const workAroundFullscreenTransition = navigator.userAgent.indexOf("Linux") != -1;
|
||||
|
||||
if (workAroundFullscreenTransition) {
|
||||
SimpleTest.requestFlakyTimeout("We need to wait an arbitrary and non-zero " +
|
||||
"amount of time in case of the Linux specific workaround to avoid busy-waiting.");
|
||||
}
|
||||
|
||||
// Adds a listener that will be called once a fullscreen transition
|
||||
// is complete. When type==='enter', callback is called when we've
|
||||
// received a fullscreenchange event, and the fullscreen transition is
|
||||
|
@ -11,6 +11,7 @@
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377624">Mozilla Bug 377624</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=565274">Mozilla Bug 565274</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=701353">Mozilla Bug 701353</a>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=826176">Mozilla Bug 826176</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id='a' type='file' accept="image/*">
|
||||
@ -27,7 +28,12 @@
|
||||
<input id='l' type="file" accept="image/*,image/gif,image/png">
|
||||
<input id='m' type="file" accept="image/gif,image/gif">
|
||||
<input id='n' type="file" accept="">
|
||||
<input id='o' type="file" accept=".test">
|
||||
<input id='p' type="file" accept="image/gif,.csv">
|
||||
<input id='q' type="file" accept="image/gif,.gif">
|
||||
<input id='z' type='file' accept="i/am,a,pathological,;,,,,test/case">
|
||||
<input id='mix-ref' type="file" accept="image/jpeg">
|
||||
<input id='mix' type="file" accept="image/jpeg,.jpg">
|
||||
<input id='hidden' hidden type='file'>
|
||||
<input id='untrusted-click' type='file'>
|
||||
<input id='prevent-default' type='file'>
|
||||
@ -82,7 +88,13 @@ var testData = [["a", 1, MockFilePicker.filterImages, 1],
|
||||
["l", 4, imageExtensionList + "; " + "*.gif; *.png", 1],
|
||||
["m", 1, "*.gif", 1],
|
||||
["n", 0, undefined, 0],
|
||||
["o", 1, "*.test", 1],
|
||||
["p", 3, "*.gif; *.csv", 1],
|
||||
["q", 1, "*.gif", 1],
|
||||
["z", 0, undefined, 0],
|
||||
// Note: mix and mix-ref tests extension lists are checked differently: see SimpleTest.executeSoon below
|
||||
["mix-ref", undefined, undefined, undefined],
|
||||
["mix", 1, undefined, 1],
|
||||
["hidden", 0, undefined, 0],
|
||||
["untrusted-click", 0, undefined, 0],
|
||||
["prevent-default", 0, undefined, 0, true],
|
||||
@ -105,6 +117,7 @@ var currentTest = 0;
|
||||
var filterAllAdded;
|
||||
var filters;
|
||||
var filterIndex;
|
||||
var mixRefExtensionList;
|
||||
|
||||
// disable popups to make sure that the popup blocker does not interfere
|
||||
// with manually opened file pickers.
|
||||
@ -192,12 +205,31 @@ function runTests() {
|
||||
"File picker show method should have been called (" + testName + ")");
|
||||
ok(filterAllAdded,
|
||||
"filterAll is missing (" + testName + ")");
|
||||
is(filters.length, testData[currentTest][1],
|
||||
"appendFilters not called as often as expected (" + testName + ")");
|
||||
is(filters[0], testData[currentTest][2],
|
||||
"Correct filters should have been added (" + testName + ")");
|
||||
is(filterIndex, testData[currentTest][3],
|
||||
"File picker should show the correct filter index (" + testName + ")");
|
||||
if (testName == "mix-ref") {
|
||||
// Used only for reference for next test: nothing to be tested here
|
||||
mixRefExtensionList = filters[0];
|
||||
if (mixRefExtensionList == undefined) {
|
||||
mixRefExtensionList = "";
|
||||
}
|
||||
} else {
|
||||
if (testName == "mix") {
|
||||
// Mixing mime type and file extension filters ("image/jpeg" and
|
||||
// ".jpg" here) shouldn't restrict the list but only extend it, if file
|
||||
// extension filter isn't a duplicate
|
||||
ok(filters[0].contains(mixRefExtensionList),
|
||||
"Mixing mime types and file extension filters shouldn't restrict extension list: " +
|
||||
mixRefExtensionList + " | " + filters[0]);
|
||||
ok(filters[0].contains("*.jpg"),
|
||||
"Filter should contain '.jpg' extension", filters[0]);
|
||||
} else {
|
||||
is(filters[0], testData[currentTest][2],
|
||||
"Correct filters should have been added (" + testName + ")");
|
||||
is(filters.length, testData[currentTest][1],
|
||||
"appendFilters not called as often as expected (" + testName + ")");
|
||||
}
|
||||
is(filterIndex, testData[currentTest][3],
|
||||
"File picker should show the correct filter index (" + testName + ")");
|
||||
}
|
||||
|
||||
if (++currentTest == testData.length) {
|
||||
MockFilePicker.cleanup();
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include "MediaQueue.h"
|
||||
#include "AudioCompactor.h"
|
||||
|
||||
#include "mozilla/TypedEnum.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
@ -22,6 +24,17 @@ class TimeRanges;
|
||||
class MediaDecoderReader;
|
||||
class SharedDecoderManager;
|
||||
|
||||
struct WaitForDataRejectValue {
|
||||
enum Reason {
|
||||
SHUTDOWN
|
||||
};
|
||||
|
||||
WaitForDataRejectValue(MediaData::Type aType, Reason aReason)
|
||||
:mType(aType), mReason(aReason) {}
|
||||
MediaData::Type mType;
|
||||
Reason mReason;
|
||||
};
|
||||
|
||||
// Encapsulates the decoding and reading of media data. Reading can either
|
||||
// synchronous and done on the calling "decode" thread, or asynchronous and
|
||||
// performed on a background thread, with the result being returned by
|
||||
@ -40,6 +53,7 @@ public:
|
||||
typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason> AudioDataPromise;
|
||||
typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason> VideoDataPromise;
|
||||
typedef MediaPromise<bool, nsresult> SeekPromise;
|
||||
typedef MediaPromise<MediaData::Type, WaitForDataRejectValue> WaitForDataPromise;
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
|
||||
|
||||
@ -113,6 +127,12 @@ public:
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
|
||||
|
||||
// By default, the state machine polls the reader once per second when it's
|
||||
// in buffering mode. Some readers support a promise-based mechanism by which
|
||||
// they notify the state machine when the data arrives.
|
||||
virtual bool IsWaitForDataSupported() { return false; }
|
||||
virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
|
||||
|
||||
virtual bool HasAudio() = 0;
|
||||
virtual bool HasVideo() = 0;
|
||||
|
||||
@ -175,10 +195,11 @@ public:
|
||||
|
||||
virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio);
|
||||
|
||||
// Wait this number of seconds when buffering, then leave and play
|
||||
// as best as we can if the required amount of data hasn't been
|
||||
// retrieved.
|
||||
virtual uint32_t GetBufferingWait() { return 30; }
|
||||
// The MediaDecoderStateMachine uses various heuristics that assume that
|
||||
// raw media data is arriving sequentially from a network channel. This
|
||||
// makes sense in the <video src="foo"> case, but not for more advanced use
|
||||
// cases like MSE.
|
||||
virtual bool UseBufferingHeuristics() { return true; }
|
||||
|
||||
// Returns the number of bytes of memory allocated by structures/frames in
|
||||
// the video queue.
|
||||
|
@ -109,6 +109,13 @@ const int64_t NO_VIDEO_AMPLE_AUDIO_DIVISOR = 8;
|
||||
// which is at or after the current playback position.
|
||||
static const uint32_t LOW_VIDEO_FRAMES = 1;
|
||||
|
||||
// Threshold in usecs that used to check if we are low on decoded video.
|
||||
// If the last video frame's end time |mDecodedVideoEndTime| doesn't exceed
|
||||
// |clock time + LOW_VIDEO_THRESHOLD_USECS*mPlaybackRate| calculation in
|
||||
// Advanceframe(), we are low on decoded video frames and trying to skip to next
|
||||
// keyframe.
|
||||
static const int32_t LOW_VIDEO_THRESHOLD_USECS = 16000;
|
||||
|
||||
// Arbitrary "frame duration" when playing only audio.
|
||||
static const int AUDIO_DURATION_USECS = 40000;
|
||||
|
||||
@ -189,14 +196,15 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mAudioEndTime(-1),
|
||||
mDecodedAudioEndTime(-1),
|
||||
mVideoFrameEndTime(-1),
|
||||
mDecodedVideoEndTime(-1),
|
||||
mVolume(1.0),
|
||||
mPlaybackRate(1.0),
|
||||
mPreservesPitch(true),
|
||||
mAmpleVideoFrames(2),
|
||||
mLowAudioThresholdUsecs(LOW_AUDIO_USECS),
|
||||
mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
|
||||
mAudioRequestPending(false),
|
||||
mVideoRequestPending(false),
|
||||
mAudioRequestStatus(RequestStatus::Idle),
|
||||
mVideoRequestStatus(RequestStatus::Idle),
|
||||
mAudioCaptured(false),
|
||||
mPositionChangeQueued(false),
|
||||
mAudioCompleted(false),
|
||||
@ -221,7 +229,7 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
||||
mAmpleVideoFrames =
|
||||
std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
|
||||
|
||||
mBufferingWait = mScheduler->IsRealTime() ? 0 : mReader->GetBufferingWait();
|
||||
mBufferingWait = mScheduler->IsRealTime() ? 0 : 30;
|
||||
mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS;
|
||||
|
||||
mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2;
|
||||
@ -567,6 +575,47 @@ MediaDecoderStateMachine::NeedToDecodeVideo()
|
||||
(!mMinimizePreroll && !HaveEnoughDecodedVideo()));
|
||||
}
|
||||
|
||||
bool
|
||||
MediaDecoderStateMachine::NeedToSkipToNextKeyframe()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(mState == DECODER_STATE_DECODING ||
|
||||
mState == DECODER_STATE_BUFFERING ||
|
||||
mState == DECODER_STATE_SEEKING);
|
||||
|
||||
// We are in seeking or buffering states, don't skip frame.
|
||||
if (!IsVideoDecoding() || mState == DECODER_STATE_BUFFERING ||
|
||||
mState == DECODER_STATE_SEEKING) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't skip frame for video-only decoded stream because the clock time of
|
||||
// the stream relies on the video frame.
|
||||
if (mDecoder->GetDecodedStream() && !HasAudio()) {
|
||||
DECODER_LOG("Video-only decoded stream, set skipToNextKeyFrame to false");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We'll skip the video decode to the nearest keyframe if we're low on
|
||||
// audio, or if we're low on video, provided we're not running low on
|
||||
// data to decode. If we're running low on downloaded data to decode,
|
||||
// we won't start keyframe skipping, as we'll be pausing playback to buffer
|
||||
// soon anyway and we'll want to be able to display frames immediately
|
||||
// after buffering finishes.
|
||||
bool isLowOnDecodedAudio = !mIsAudioPrerolling && IsAudioDecoding() &&
|
||||
(GetDecodedAudioDuration() <
|
||||
mLowAudioThresholdUsecs * mPlaybackRate);
|
||||
bool isLowOnDecodedVideo = !mIsVideoPrerolling &&
|
||||
(mDecodedVideoEndTime - GetClock() <
|
||||
LOW_VIDEO_THRESHOLD_USECS * mPlaybackRate);
|
||||
if ((isLowOnDecodedAudio || isLowOnDecodedVideo) && !HasLowUndecodedData()) {
|
||||
DECODER_LOG("Skipping video decode to the next keyframe");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::DecodeVideo()
|
||||
{
|
||||
@ -579,7 +628,7 @@ MediaDecoderStateMachine::DecodeVideo()
|
||||
if (mState != DECODER_STATE_DECODING &&
|
||||
mState != DECODER_STATE_BUFFERING &&
|
||||
mState != DECODER_STATE_SEEKING) {
|
||||
mVideoRequestPending = false;
|
||||
mVideoRequestStatus = RequestStatus::Idle;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
return;
|
||||
}
|
||||
@ -594,27 +643,8 @@ MediaDecoderStateMachine::DecodeVideo()
|
||||
mIsVideoPrerolling = false;
|
||||
}
|
||||
|
||||
// We'll skip the video decode to the nearest keyframe if we're low on
|
||||
// audio, or if we're low on video, provided we're not running low on
|
||||
// data to decode. If we're running low on downloaded data to decode,
|
||||
// we won't start keyframe skipping, as we'll be pausing playback to buffer
|
||||
// soon anyway and we'll want to be able to display frames immediately
|
||||
// after buffering finishes.
|
||||
if (mState == DECODER_STATE_DECODING &&
|
||||
IsVideoDecoding() &&
|
||||
((!mIsAudioPrerolling && IsAudioDecoding() &&
|
||||
GetDecodedAudioDuration() < mLowAudioThresholdUsecs * mPlaybackRate) ||
|
||||
(!mIsVideoPrerolling && IsVideoDecoding() &&
|
||||
// don't skip frame when |clock time| <= |mVideoFrameEndTime| for
|
||||
// we are still in the safe range without underrunning video frames
|
||||
GetClock() > mVideoFrameEndTime &&
|
||||
(static_cast<uint32_t>(VideoQueue().GetSize())
|
||||
< LOW_VIDEO_FRAMES * mPlaybackRate))) &&
|
||||
!HasLowUndecodedData())
|
||||
{
|
||||
skipToNextKeyFrame = true;
|
||||
DECODER_LOG("Skipping video decode to the next keyframe");
|
||||
}
|
||||
skipToNextKeyFrame = NeedToSkipToNextKeyframe();
|
||||
|
||||
currentTime = mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime();
|
||||
|
||||
// Time the video decode, so that if it's slow, we can increase our low
|
||||
@ -658,7 +688,7 @@ MediaDecoderStateMachine::DecodeAudio()
|
||||
if (mState != DECODER_STATE_DECODING &&
|
||||
mState != DECODER_STATE_BUFFERING &&
|
||||
mState != DECODER_STATE_SEEKING) {
|
||||
mAudioRequestPending = false;
|
||||
mAudioRequestStatus = RequestStatus::Idle;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
mon.NotifyAll();
|
||||
return;
|
||||
@ -713,7 +743,7 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
nsRefPtr<AudioData> audio(aAudioSample);
|
||||
MOZ_ASSERT(audio);
|
||||
mAudioRequestPending = false;
|
||||
mAudioRequestStatus = RequestStatus::Idle;
|
||||
mDecodedAudioEndTime = audio->GetEndTime();
|
||||
|
||||
SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
|
||||
@ -729,8 +759,10 @@ MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
|
||||
}
|
||||
|
||||
case DECODER_STATE_BUFFERING:
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Schedule the state machine and then fall through.
|
||||
ScheduleStateMachine();
|
||||
case DECODER_STATE_DECODING: {
|
||||
// In buffering and decoding state, we simply enqueue samples.
|
||||
Push(audio);
|
||||
return;
|
||||
}
|
||||
@ -825,11 +857,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
||||
MOZ_ASSERT_IF(!isAudio, aType == MediaData::VIDEO_DATA);
|
||||
|
||||
// This callback means that the pending request is dead.
|
||||
if (isAudio) {
|
||||
mAudioRequestPending = false;
|
||||
} else {
|
||||
mVideoRequestPending = false;
|
||||
}
|
||||
RequestStatusRef(aType) = RequestStatus::Idle;
|
||||
|
||||
// If this is a decode error, delegate to the generic error path.
|
||||
if (aReason == MediaDecoderReader::DECODE_ERROR) {
|
||||
@ -837,15 +865,15 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
||||
return;
|
||||
}
|
||||
|
||||
// If the decoder is waiting for data, we need to make sure that the requests
|
||||
// are cleared, which happened above. Additionally, if we're out of decoded
|
||||
// samples, we need to switch to buffering mode.
|
||||
// If the decoder is waiting for data, we tell it to call us back when the
|
||||
// data arrives.
|
||||
if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
|
||||
bool outOfSamples = isAudio ? !AudioQueue().GetSize() : !VideoQueue().GetSize();
|
||||
if (outOfSamples) {
|
||||
StartBuffering();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
|
||||
"Readers that send WAITING_FOR_DATA need to implement WaitForData");
|
||||
RequestStatusRef(aType) = RequestStatus::Waiting;
|
||||
mReader->WaitForData(aType)->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnWaitForDataResolved,
|
||||
&MediaDecoderStateMachine::OnWaitForDataRejected);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -930,7 +958,8 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
nsRefPtr<VideoData> video(aVideoSample);
|
||||
mVideoRequestPending = false;
|
||||
mVideoRequestStatus = RequestStatus::Idle;
|
||||
mDecodedVideoEndTime = video ? video->GetEndTime() : mDecodedVideoEndTime;
|
||||
|
||||
SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
|
||||
(video ? video->mTime : -1),
|
||||
@ -945,6 +974,9 @@ MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
|
||||
}
|
||||
|
||||
case DECODER_STATE_BUFFERING:
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Schedule the state machine and then fall through.
|
||||
ScheduleStateMachine();
|
||||
case DECODER_STATE_DECODING: {
|
||||
Push(video);
|
||||
// If the requested video sample was slow to arrive, increase the
|
||||
@ -1505,6 +1537,7 @@ void MediaDecoderStateMachine::ResetPlayback()
|
||||
MOZ_ASSERT(!mAudioSink);
|
||||
|
||||
mVideoFrameEndTime = -1;
|
||||
mDecodedVideoEndTime = -1;
|
||||
mAudioStartTime = -1;
|
||||
mAudioEndTime = -1;
|
||||
mDecodedAudioEndTime = -1;
|
||||
@ -1745,9 +1778,9 @@ MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
|
||||
!needToDecodeVideo &&
|
||||
!IsPlaying();
|
||||
|
||||
SAMPLE_LOG("DispatchDecodeTasksIfNeeded needAudio=%d dispAudio=%d needVideo=%d dispVid=%d needIdle=%d",
|
||||
needToDecodeAudio, mAudioRequestPending,
|
||||
needToDecodeVideo, mVideoRequestPending,
|
||||
SAMPLE_LOG("DispatchDecodeTasksIfNeeded needAudio=%d audioStatus=%d needVideo=%d videoStatus=%d needIdle=%d",
|
||||
needToDecodeAudio, mAudioRequestStatus,
|
||||
needToDecodeVideo, mVideoRequestStatus,
|
||||
needIdle);
|
||||
|
||||
if (needToDecodeAudio) {
|
||||
@ -1817,8 +1850,8 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
||||
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
|
||||
SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d dispatched=%d",
|
||||
IsAudioDecoding(), mAudioRequestPending);
|
||||
SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%d",
|
||||
IsAudioDecoding(), mAudioRequestStatus);
|
||||
|
||||
if (mState >= DECODER_STATE_COMPLETED) {
|
||||
return NS_OK;
|
||||
@ -1826,12 +1859,12 @@ MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
|
||||
|
||||
MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
|
||||
|
||||
if (IsAudioDecoding() && !mAudioRequestPending && !mWaitingForDecoderSeek) {
|
||||
if (IsAudioDecoding() && mAudioRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
|
||||
RefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeAudio));
|
||||
nsresult rv = DecodeTaskQueue()->Dispatch(task);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mAudioRequestPending = true;
|
||||
mAudioRequestStatus = RequestStatus::Pending;
|
||||
} else {
|
||||
DECODER_WARN("Failed to dispatch task to decode audio");
|
||||
}
|
||||
@ -1859,8 +1892,8 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d dispatched=%d",
|
||||
IsVideoDecoding(), mVideoRequestPending);
|
||||
SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%d",
|
||||
IsVideoDecoding(), mVideoRequestStatus);
|
||||
|
||||
NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
|
||||
"Should be on state machine or decode thread.");
|
||||
@ -1871,12 +1904,12 @@ MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
|
||||
|
||||
MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
|
||||
|
||||
if (IsVideoDecoding() && !mVideoRequestPending && !mWaitingForDecoderSeek) {
|
||||
if (IsVideoDecoding() && mVideoRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
|
||||
RefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeVideo));
|
||||
nsresult rv = DecodeTaskQueue()->Dispatch(task);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mVideoRequestPending = true;
|
||||
mVideoRequestStatus = RequestStatus::Pending;
|
||||
} else {
|
||||
DECODER_WARN("Failed to dispatch task to decode video");
|
||||
}
|
||||
@ -1930,6 +1963,7 @@ int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
|
||||
bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs)
|
||||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
MOZ_ASSERT(mReader->UseBufferingHeuristics());
|
||||
// We consider ourselves low on decoded data if we're low on audio,
|
||||
// provided we've not decoded to the end of the audio stream, or
|
||||
// if we're low on video frames, provided
|
||||
@ -2626,28 +2660,41 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
|
||||
|
||||
// We will remain in the buffering state if we've not decoded enough
|
||||
// data to begin playback, or if we've not downloaded a reasonable
|
||||
// amount of data inside our buffering time.
|
||||
TimeDuration elapsed = now - mBufferingStart;
|
||||
bool isLiveStream = resource->GetLength() == -1;
|
||||
if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
|
||||
elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
|
||||
(mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
|
||||
: HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
|
||||
mDecoder->IsExpectingMoreData())
|
||||
{
|
||||
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
|
||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
||||
(mQuickBuffering ? "(quick exit)" : ""));
|
||||
ScheduleStateMachine(USECS_PER_S);
|
||||
// With buffering heuristics we will remain in the buffering state if
|
||||
// we've not decoded enough data to begin playback, or if we've not
|
||||
// downloaded a reasonable amount of data inside our buffering time.
|
||||
if (mReader->UseBufferingHeuristics()) {
|
||||
TimeDuration elapsed = now - mBufferingStart;
|
||||
bool isLiveStream = resource->GetLength() == -1;
|
||||
if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
|
||||
elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
|
||||
(mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
|
||||
: HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
|
||||
mDecoder->IsExpectingMoreData())
|
||||
{
|
||||
DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
|
||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
||||
(mQuickBuffering ? "(quick exit)" : ""));
|
||||
ScheduleStateMachine(USECS_PER_S);
|
||||
return NS_OK;
|
||||
}
|
||||
} else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
|
||||
MOZ_ASSERT(mReader->IsWaitForDataSupported(),
|
||||
"Don't yet have a strategy for non-heuristic + non-WaitForData");
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
MOZ_ASSERT_IF(OutOfDecodedAudio(), mAudioRequestStatus != RequestStatus::Idle);
|
||||
MOZ_ASSERT_IF(OutOfDecodedVideo(), mVideoRequestStatus != RequestStatus::Idle);
|
||||
DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
|
||||
"mAudioStatus: %d, outOfVideo: %d, mVideoStatus: %d",
|
||||
OutOfDecodedAudio(), mAudioRequestStatus,
|
||||
OutOfDecodedVideo(), mVideoRequestStatus);
|
||||
return NS_OK;
|
||||
} else {
|
||||
DECODER_LOG("Changed state from BUFFERING to DECODING");
|
||||
DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
|
||||
StartDecoding();
|
||||
}
|
||||
|
||||
DECODER_LOG("Changed state from BUFFERING to DECODING");
|
||||
DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
|
||||
StartDecoding();
|
||||
|
||||
// Notify to allow blocked decoder thread to continue
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
UpdateReadyState();
|
||||
@ -2747,8 +2794,8 @@ MediaDecoderStateMachine::FlushDecoding()
|
||||
// These flags will be reset when the decoded data returned in OnAudioDecoded()
|
||||
// and OnVideoDecoded(). Because the decode tasks are flushed, these flags need
|
||||
// to be reset here.
|
||||
mAudioRequestPending = false;
|
||||
mVideoRequestPending = false;
|
||||
mAudioRequestStatus = RequestStatus::Idle;
|
||||
mVideoRequestStatus = RequestStatus::Idle;
|
||||
|
||||
// We must reset playback so that all references to frames queued
|
||||
// in the state machine are dropped, else subsequent calls to Shutdown()
|
||||
@ -2904,9 +2951,16 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
||||
// If we don't, switch to buffering mode.
|
||||
if (mState == DECODER_STATE_DECODING &&
|
||||
mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
|
||||
HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
|
||||
mDecoder->IsExpectingMoreData()) {
|
||||
if (JustExitedQuickBuffering() || HasLowUndecodedData()) {
|
||||
bool shouldBuffer;
|
||||
if (mReader->UseBufferingHeuristics()) {
|
||||
shouldBuffer = HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
|
||||
(JustExitedQuickBuffering() || HasLowUndecodedData());
|
||||
} else {
|
||||
MOZ_ASSERT(mReader->IsWaitForDataSupported());
|
||||
shouldBuffer = OutOfDecodedAudio() || OutOfDecodedVideo();
|
||||
}
|
||||
if (shouldBuffer) {
|
||||
if (currentFrame) {
|
||||
VideoQueue().PushFront(currentFrame);
|
||||
}
|
||||
|
@ -389,6 +389,23 @@ public:
|
||||
void OnSeekCompleted();
|
||||
void OnSeekFailed(nsresult aResult);
|
||||
|
||||
void OnWaitForDataResolved(MediaData::Type aType)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (RequestStatusRef(aType) == RequestStatus::Waiting) {
|
||||
RequestStatusRef(aType) = RequestStatus::Idle;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void OnWaitForDataRejected(WaitForDataRejectValue aRejection)
|
||||
{
|
||||
MOZ_ASSERT(aRejection.mReason == WaitForDataRejectValue::SHUTDOWN);
|
||||
if (RequestStatusRef(aRejection.mType) == RequestStatus::Waiting) {
|
||||
RequestStatusRef(aRejection.mType) = RequestStatus::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AcquireMonitorAndInvokeDecodeError();
|
||||
|
||||
@ -462,8 +479,23 @@ protected:
|
||||
|
||||
// Returns true if we've got less than aAudioUsecs microseconds of decoded
|
||||
// and playable data. The decoder monitor must be held.
|
||||
//
|
||||
// May not be invoked when mReader->UseBufferingHeuristics() is false.
|
||||
bool HasLowDecodedData(int64_t aAudioUsecs);
|
||||
|
||||
bool OutOfDecodedAudio()
|
||||
{
|
||||
return IsAudioDecoding() && !AudioQueue().IsFinished() && AudioQueue().GetSize() == 0;
|
||||
}
|
||||
|
||||
bool OutOfDecodedVideo()
|
||||
{
|
||||
// In buffering mode, we keep the last already-played frame in the queue.
|
||||
int emptyVideoSize = mState == DECODER_STATE_BUFFERING ? 1 : 0;
|
||||
return IsVideoDecoding() && !VideoQueue().IsFinished() && VideoQueue().GetSize() <= emptyVideoSize;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if we're running low on data which is not yet decoded.
|
||||
// The decoder monitor must be held.
|
||||
bool HasLowUndecodedData();
|
||||
@ -710,6 +742,10 @@ protected:
|
||||
// DECODER_STATE_WAIT_FOR_RESOURCES.
|
||||
void DoNotifyWaitingForResourcesStatusChanged();
|
||||
|
||||
// Return true if the video decoder's decode speed can not catch up the
|
||||
// play time.
|
||||
bool NeedToSkipToNextKeyframe();
|
||||
|
||||
// The decoder object that created this state machine. The state machine
|
||||
// holds a strong reference to the decoder to ensure that the decoder stays
|
||||
// alive once media element has started the decoder shutdown process, and has
|
||||
@ -843,6 +879,10 @@ protected:
|
||||
// in microseconds. Accessed from the state machine thread.
|
||||
int64_t mVideoFrameEndTime;
|
||||
|
||||
// The end time of the last decoded video frame. Used to check if we are low
|
||||
// on decoded video data.
|
||||
int64_t mDecodedVideoEndTime;
|
||||
|
||||
// Volume of playback. 0.0 = muted. 1.0 = full volume. Read/Written
|
||||
// from the state machine and main threads. Synchronised via decoder
|
||||
// monitor.
|
||||
@ -911,11 +951,22 @@ protected:
|
||||
bool mIsAudioPrerolling;
|
||||
bool mIsVideoPrerolling;
|
||||
|
||||
MOZ_BEGIN_NESTED_ENUM_CLASS(RequestStatus)
|
||||
Idle,
|
||||
Pending,
|
||||
Waiting
|
||||
MOZ_END_NESTED_ENUM_CLASS(RequestStatus)
|
||||
|
||||
// True when we have dispatched a task to the decode task queue to request
|
||||
// decoded audio/video, and/or we are waiting for the requested sample to be
|
||||
// returned by callback from the Reader.
|
||||
bool mAudioRequestPending;
|
||||
bool mVideoRequestPending;
|
||||
RequestStatus mAudioRequestStatus;
|
||||
RequestStatus mVideoRequestStatus;
|
||||
|
||||
RequestStatus& RequestStatusRef(MediaData::Type aType)
|
||||
{
|
||||
return aType == MediaData::AUDIO_DATA ? mAudioRequestStatus : mVideoRequestStatus;
|
||||
}
|
||||
|
||||
// True if we shouldn't play our audio (but still write it to any capturing
|
||||
// streams). When this is true, mStopAudioThread is always true and
|
||||
|
@ -61,7 +61,6 @@ public:
|
||||
: mCreationSite(aCreationSite)
|
||||
, mMutex("MediaPromise Mutex")
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaPromise);
|
||||
PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
|
||||
}
|
||||
|
||||
@ -99,14 +98,10 @@ protected:
|
||||
public:
|
||||
ResolveRunnable(ThenValueBase* aThenValue, ResolveValueType aResolveValue)
|
||||
: mThenValue(aThenValue)
|
||||
, mResolveValue(aResolveValue)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ResolveRunnable);
|
||||
}
|
||||
, mResolveValue(aResolveValue) {}
|
||||
|
||||
~ResolveRunnable()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ResolveRunnable);
|
||||
MOZ_ASSERT(!mThenValue);
|
||||
}
|
||||
|
||||
@ -130,14 +125,10 @@ protected:
|
||||
public:
|
||||
RejectRunnable(ThenValueBase* aThenValue, RejectValueType aRejectValue)
|
||||
: mThenValue(aThenValue)
|
||||
, mRejectValue(aRejectValue)
|
||||
{
|
||||
MOZ_COUNT_CTOR(RejectRunnable);
|
||||
}
|
||||
, mRejectValue(aRejectValue) {}
|
||||
|
||||
~RejectRunnable()
|
||||
{
|
||||
MOZ_COUNT_DTOR(RejectRunnable);
|
||||
MOZ_ASSERT(!mThenValue);
|
||||
}
|
||||
|
||||
@ -327,7 +318,6 @@ protected:
|
||||
|
||||
~MediaPromise()
|
||||
{
|
||||
MOZ_COUNT_DTOR(MediaPromise);
|
||||
PROMISE_LOG("MediaPromise::~MediaPromise [this=%p]", this);
|
||||
MOZ_ASSERT(!IsPending());
|
||||
MOZ_ASSERT(mThenValues.IsEmpty());
|
||||
|
@ -157,7 +157,7 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
|
||||
PlatformDecoderModule::Init();
|
||||
mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource()));
|
||||
mDemuxer = new MP4Demuxer(new MP4Stream(mDecoder->GetResource(), &mIndexMonitor), &mIndexMonitor);
|
||||
|
||||
InitLayersBackendType();
|
||||
|
||||
@ -294,11 +294,10 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
|
||||
MetadataTags** aTags)
|
||||
{
|
||||
if (!mDemuxerInitialized) {
|
||||
bool ok = mDemuxer->Init();
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
bool ok = mDemuxer->Init();
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
|
||||
mIndexReady = true;
|
||||
}
|
||||
|
||||
@ -632,6 +631,7 @@ MP4Reader::ReturnOutput(MediaData* aData, TrackType aTrack)
|
||||
MP4Sample*
|
||||
MP4Reader::PopSample(TrackType aTrack)
|
||||
{
|
||||
MonitorAutoLock mon(mIndexMonitor);
|
||||
switch (aTrack) {
|
||||
case kAudio:
|
||||
return mDemuxer->DemuxAudioSample();
|
||||
|
@ -9,7 +9,9 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MP4Stream::MP4Stream(MediaResource* aResource) : mResource(aResource)
|
||||
MP4Stream::MP4Stream(MediaResource* aResource, Monitor* aDemuxerMonitor)
|
||||
: mResource(aResource)
|
||||
, mDemuxerMonitor(aDemuxerMonitor)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MP4Stream);
|
||||
MOZ_ASSERT(aResource);
|
||||
@ -24,6 +26,12 @@ bool
|
||||
MP4Stream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
|
||||
size_t* aBytesRead)
|
||||
{
|
||||
// The read call can acquire various monitors, including both the decoder
|
||||
// monitor and gMediaCache's monitor. So we need to unlock ours to avoid
|
||||
// deadlock.
|
||||
mDemuxerMonitor->AssertCurrentThreadOwns();
|
||||
MonitorAutoUnlock unlock(*mDemuxerMonitor);
|
||||
|
||||
uint32_t sum = 0;
|
||||
uint32_t bytesRead = 0;
|
||||
do {
|
||||
@ -44,6 +52,12 @@ bool
|
||||
MP4Stream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
|
||||
size_t* aBytesRead)
|
||||
{
|
||||
// The read call can acquire various monitors, including both the decoder
|
||||
// monitor and gMediaCache's monitor. So we need to unlock ours to avoid
|
||||
// deadlock.
|
||||
mDemuxerMonitor->AssertCurrentThreadOwns();
|
||||
MonitorAutoUnlock unlock(*mDemuxerMonitor);
|
||||
|
||||
nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer),
|
||||
aOffset, aCount);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -9,13 +9,15 @@
|
||||
|
||||
#include "mp4_demuxer/mp4_demuxer.h"
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaResource;
|
||||
|
||||
class MP4Stream : public mp4_demuxer::Stream {
|
||||
public:
|
||||
explicit MP4Stream(MediaResource* aResource);
|
||||
explicit MP4Stream(MediaResource* aResource, Monitor* aDemuxerMonitor);
|
||||
virtual ~MP4Stream();
|
||||
virtual bool ReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
|
||||
size_t* aBytesRead) MOZ_OVERRIDE;
|
||||
@ -25,6 +27,7 @@ public:
|
||||
|
||||
private:
|
||||
nsRefPtr<MediaResource> mResource;
|
||||
Monitor* mDemuxerMonitor;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -18,11 +18,13 @@ public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MP4DemuxerBinding);
|
||||
|
||||
nsRefPtr<MockMediaResource> resource;
|
||||
Monitor mMonitor;
|
||||
nsAutoPtr<MP4Demuxer> demuxer;
|
||||
|
||||
explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4")
|
||||
: resource(new MockMediaResource(aFileName))
|
||||
, demuxer(new MP4Demuxer(new MP4Stream(resource)))
|
||||
, mMonitor("TestMP4Demuxer monitor")
|
||||
, demuxer(new MP4Demuxer(new MP4Stream(resource, &mMonitor), &mMonitor))
|
||||
{
|
||||
EXPECT_EQ(NS_OK, resource->Open(nullptr));
|
||||
}
|
||||
@ -36,6 +38,7 @@ private:
|
||||
TEST(MP4Demuxer, Seek)
|
||||
{
|
||||
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding();
|
||||
MonitorAutoLock mon(b->mMonitor);
|
||||
MP4Demuxer* d = b->demuxer;
|
||||
|
||||
EXPECT_TRUE(d->Init());
|
||||
@ -87,6 +90,7 @@ ToCryptoString(CryptoSample& aCrypto)
|
||||
TEST(MP4Demuxer, CENC)
|
||||
{
|
||||
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding("short-cenc.mp4");
|
||||
MonitorAutoLock mon(b->mMonitor);
|
||||
MP4Demuxer* d = b->demuxer;
|
||||
|
||||
EXPECT_TRUE(d->Init());
|
||||
@ -147,6 +151,7 @@ TEST(MP4Demuxer, CENC)
|
||||
TEST(MP4Demuxer, CENCFrag)
|
||||
{
|
||||
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding("gizmo-frag.mp4");
|
||||
MonitorAutoLock mon(b->mMonitor);
|
||||
MP4Demuxer* d = b->demuxer;
|
||||
|
||||
EXPECT_TRUE(d->Init());
|
||||
|
@ -203,7 +203,7 @@ private:
|
||||
|
||||
class MP4ContainerParser : public ContainerParser {
|
||||
public:
|
||||
MP4ContainerParser() {}
|
||||
MP4ContainerParser() :mMonitor("MP4ContainerParser Index Monitor") {}
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
@ -244,10 +244,12 @@ public:
|
||||
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
|
||||
// but mParser requires us to hold a monitor.
|
||||
bool initSegment = IsInitSegmentPresent(aData, aLength);
|
||||
if (initSegment) {
|
||||
mStream = new mp4_demuxer::BufferStream();
|
||||
mParser = new mp4_demuxer::MoofParser(mStream, 0);
|
||||
mParser = new mp4_demuxer::MoofParser(mStream, 0, &mMonitor);
|
||||
} else if (!mStream || !mParser) {
|
||||
return false;
|
||||
}
|
||||
@ -291,6 +293,7 @@ public:
|
||||
private:
|
||||
nsRefPtr<mp4_demuxer::BufferStream> mStream;
|
||||
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
|
||||
Monitor mMonitor;
|
||||
};
|
||||
|
||||
/*static*/ ContainerParser*
|
||||
|
@ -66,8 +66,7 @@ MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
SetStateMachineParameters();
|
||||
|
||||
return NS_OK;
|
||||
return ScheduleStateMachineThread();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -345,6 +345,9 @@ MediaSourceReader::ContinueShutdown()
|
||||
MOZ_ASSERT(mAudioPromise.IsEmpty());
|
||||
MOZ_ASSERT(mVideoPromise.IsEmpty());
|
||||
|
||||
mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
|
||||
mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
|
||||
|
||||
MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
|
||||
}
|
||||
|
||||
@ -391,6 +394,15 @@ MediaSourceReader::SelectReader(int64_t aTarget,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
|
||||
{
|
||||
TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
|
||||
MOZ_ASSERT(trackBuffer);
|
||||
nsRefPtr<MediaDecoderReader> reader = SelectReader(aTarget, trackBuffer->Decoders());
|
||||
return !!reader;
|
||||
}
|
||||
|
||||
bool
|
||||
MediaSourceReader::SwitchAudioReader(int64_t aTarget)
|
||||
{
|
||||
@ -720,17 +732,42 @@ MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::WaitForDataPromise>
|
||||
MediaSourceReader::WaitForData(MediaData::Type aType)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
|
||||
MaybeNotifyHaveData();
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceReader::MaybeNotifyHaveData()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
bool haveAudio = false, haveVideo = false;
|
||||
if (!mAudioIsSeeking && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
|
||||
haveAudio = true;
|
||||
WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
|
||||
}
|
||||
if (!mVideoIsSeeking && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) {
|
||||
haveVideo = true;
|
||||
WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
|
||||
}
|
||||
MSE_DEBUG("MediaSourceReader(%p)::MaybeNotifyHaveData haveAudio=%d, haveVideo=%d", this,
|
||||
haveAudio, haveVideo);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata tracks=%u/%u audio=%p video=%p",
|
||||
this, mEssentialTrackBuffers.Length(), mTrackBuffers.Length(),
|
||||
mAudioTrack.get(), mVideoTrack.get());
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
mEssentialTrackBuffers.Clear();
|
||||
}
|
||||
mEssentialTrackBuffers.Clear();
|
||||
if (!mAudioTrack && !mVideoTrack) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata missing track: mAudioTrack=%p mVideoTrack=%p",
|
||||
this, mAudioTrack.get(), mVideoTrack.get());
|
||||
|
@ -61,6 +61,10 @@ public:
|
||||
void OnSeekCompleted();
|
||||
void OnSeekFailed(nsresult aResult);
|
||||
|
||||
virtual bool IsWaitForDataSupported() MOZ_OVERRIDE { return true; }
|
||||
virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) MOZ_OVERRIDE;
|
||||
void MaybeNotifyHaveData();
|
||||
|
||||
bool HasVideo() MOZ_OVERRIDE
|
||||
{
|
||||
return mInfo.HasVideo();
|
||||
@ -78,11 +82,11 @@ public:
|
||||
// as chrome/blink and assumes that we always start at t=0.
|
||||
virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio) MOZ_OVERRIDE { return 0; }
|
||||
|
||||
// Buffering waits (in which we decline to present decoded frames because we
|
||||
// "don't have enough") don't really make sense for MSE. The delay is
|
||||
// essentially a streaming heuristic, but JS is supposed to take care of that
|
||||
// in the MSE world. Avoid injecting inexplicable delays.
|
||||
virtual uint32_t GetBufferingWait() { return 0; }
|
||||
// Buffering heuristics don't make sense for MSE, because the arrival of data
|
||||
// is at least partly controlled by javascript, and javascript does not expect
|
||||
// us to sit on unplayed data just because it may not be enough to play
|
||||
// through.
|
||||
bool UseBufferingHeuristics() MOZ_OVERRIDE { return false; }
|
||||
|
||||
bool IsMediaSeekable() { return true; }
|
||||
|
||||
@ -132,6 +136,7 @@ private:
|
||||
// available in the range requested by aTarget.
|
||||
already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
|
||||
const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
|
||||
bool HaveData(int64_t aTarget, MediaData::Type aType);
|
||||
|
||||
void AttemptSeek();
|
||||
void FinalizeSeek();
|
||||
@ -148,6 +153,13 @@ private:
|
||||
MediaPromiseHolder<AudioDataPromise> mAudioPromise;
|
||||
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
|
||||
|
||||
MediaPromiseHolder<WaitForDataPromise> mAudioWaitPromise;
|
||||
MediaPromiseHolder<WaitForDataPromise> mVideoWaitPromise;
|
||||
MediaPromiseHolder<WaitForDataPromise>& WaitPromise(MediaData::Type aType)
|
||||
{
|
||||
return aType == MediaData::AUDIO_DATA ? mAudioWaitPromise : mVideoWaitPromise;
|
||||
}
|
||||
|
||||
#ifdef MOZ_EME
|
||||
nsRefPtr<CDMProxy> mCDMProxy;
|
||||
#endif
|
||||
|
@ -183,9 +183,9 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Schedule the state machine thread to ensure playback starts if required
|
||||
// when data is appended.
|
||||
mParentDecoder->ScheduleStateMachineThread();
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -440,6 +440,11 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
RemoveDecoder(aDecoder);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
|
||||
}
|
||||
|
||||
|
@ -2491,26 +2491,75 @@ this.GECKO_NETWORK_STATE_CONNECTED = 1;
|
||||
this.GECKO_NETWORK_STATE_DISCONNECTING = 2;
|
||||
this.GECKO_NETWORK_STATE_DISCONNECTED = 3;
|
||||
|
||||
// 3GPP 24.008 Annex H.
|
||||
this.CALL_FAIL_UNOBTAINABLE_NUMBER = 1;
|
||||
this.CALL_FAIL_NO_ROUTE_TO_DESTINATION = 3;
|
||||
this.CALL_FAIL_CHANNEL_UNACCEPTABLE = 6;
|
||||
this.CALL_FAIL_OPERATOR_DETERMINED_BARRING = 8;
|
||||
this.CALL_FAIL_NORMAL = 16;
|
||||
this.CALL_FAIL_BUSY = 17;
|
||||
this.CALL_FAIL_NO_USER_RESPONDING = 18;
|
||||
this.CALL_FAIL_USER_ALERTING = 19;
|
||||
this.CALL_FAIL_CALL_REJECTED = 21;
|
||||
this.CALL_FAIL_NUMBER_CHANGED = 22;
|
||||
this.CALL_FAIL_CALL_REJECTED_DESTINATION_FEATURE = 24;
|
||||
this.CALL_FAIL_CALL_PRE_EMPTION = 25;
|
||||
this.CALL_FAIL_DEST_OUT_OF_ORDER = 27;
|
||||
this.CALL_FAIL_INVALID_FORMAT = 28;
|
||||
this.CALL_FAIL_FACILITY_REJECTED = 29;
|
||||
this.CALL_FAIL_RESPONSE_TO_STATUS_ENQUIRY = 30;
|
||||
this.CALL_FAIL_NORMAL_UNSPECIFIED = 31;
|
||||
this.CALL_FAIL_CONGESTION = 34;
|
||||
this.CALL_FAIL_NETWORK_OUT_OF_ORDER = 38;
|
||||
this.CALL_FAIL_NETWORK_TEMP_FAILURE = 41;
|
||||
this.CALL_FAIL_SWITCHING_EQUIP_CONGESTION = 42;
|
||||
this.CALL_FAIL_ACCESS_INFO_DISCARDED = 43;
|
||||
this.CALL_FAIL_REQUESTED_CHANNEL_NOT_AVAILABLE = 44;
|
||||
this.CALL_FAIL_RESOURCE_UNAVAILABLE = 47;
|
||||
this.CALL_FAIL_QOS_UNAVAILABLE = 49;
|
||||
this.CALL_FAIL_REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
|
||||
this.CALL_FAIL_INCOMING_CALLS_BARRED_WITHIN_CUG = 55;
|
||||
this.CALL_FAIL_BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
|
||||
this.CALL_FAIL_BEARER_CAPABILITY_NOT_AVAILABLE = 58;
|
||||
this.CALL_FAIL_SERVICE_NOT_AVAILABLE = 63;
|
||||
this.CALL_FAIL_BEARER_NOT_IMPLEMENTED = 65;
|
||||
this.CALL_FAIL_ACM_LIMIT_EXCEEDED = 68;
|
||||
this.CALL_FAIL_REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
|
||||
this.CALL_FAIL_UNRESTRICTED_BEARER_NOT_AVAILABLE = 70;
|
||||
this.CALL_FAIL_SERVICE_NOT_IMPLEMENTED = 79;
|
||||
this.CALL_FAIL_INVALID_TRANSACTION_ID = 81;
|
||||
this.CALL_FAIL_USER_NOT_CUG_MEMBER = 87;
|
||||
this.CALL_FAIL_INCOMPATIBLE_DESTINATION = 88;
|
||||
this.CALL_FAIL_INVALID_TRANSIT_NETWORK_SELECTION = 91;
|
||||
this.CALL_FAIL_SEMANTICALLY_INCORRECT_MESSAGE = 95;
|
||||
this.CALL_FAIL_INVALID_MANDATORY_INFO = 96;
|
||||
this.CALL_FAIL_MESSAGE_TYPE_NOT_IMPLEMENTED = 97;
|
||||
this.CALL_FAIL_MESSAGE_TYPE_INCOMPATIBLE_PROTOCOL_STATE = 98;
|
||||
this.CALL_FAIL_INFO_ELEMENT_NOT_IMPLEMENTED = 99;
|
||||
this.CALL_FAIL_CONDITIONAL_IE_ERROR = 100;
|
||||
this.CALL_FAIL_MESSAGE_INCOMPABITLE_PROTOCOL_STATE = 101;
|
||||
this.CALL_FAIL_RECOVERY_ON_TIMER_EXPIRY = 102;
|
||||
this.CALL_FAIL_PROTOCOL_ERROR = 111;
|
||||
this.CALL_FAIL_INTERWORKING = 127;
|
||||
// AOSP ril.h
|
||||
this.CALL_FAIL_CALL_BARRED = 240;
|
||||
this.CALL_FAIL_FDN_BLOCKED = 241;
|
||||
this.CALL_FAIL_IMSI_UNKNOWN_IN_VLR = 242;
|
||||
this.CALL_FAIL_IMEI_NOT_ACCEPTED = 243;
|
||||
this.CALL_FAIL_DIAL_MODIFIED_TO_USSD = 244; // STK Call Control
|
||||
this.CALL_FAIL_DIAL_MODIFIED_TO_SS = 245;
|
||||
this.CALL_FAIL_DIAL_MODIFIED_TO_DIAL = 246;
|
||||
this.CALL_FAIL_CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
|
||||
this.CALL_FAIL_CDMA_DROP = 1001;
|
||||
this.CALL_FAIL_CDMA_INTERCEPT = 1002;
|
||||
this.CALL_FAIL_CDMA_REORDER = 1003;
|
||||
this.CALL_FAIL_CDMA_SO_REJECT = 1004;
|
||||
this.CALL_FAIL_CDMA_RETRY_ORDER = 1005;
|
||||
this.CALL_FAIL_CDMA_ACCESS_FAILURE = 1006;
|
||||
this.CALL_FAIL_CDMA_PREEMPTED = 1007;
|
||||
this.CALL_FAIL_CDMA_NOT_EMERGENCY = 1008; // For non-emergency number dialed
|
||||
// during emergency callback mode
|
||||
this.CALL_FAIL_CDMA_ACCESS_BLOCKED = 1009;
|
||||
this.CALL_FAIL_ERROR_UNSPECIFIED = 0xffff;
|
||||
|
||||
// See nsIMobileConnection::MOBILE_RADIO_STATE_*
|
||||
@ -2666,50 +2715,139 @@ NETWORK_CREG_TO_GECKO_MOBILE_CONNECTION_STATE[NETWORK_CREG_STATE_DENIED_EMERGENC
|
||||
NETWORK_CREG_TO_GECKO_MOBILE_CONNECTION_STATE[NETWORK_CREG_STATE_UNKNOWN_EMERGENCY_CALLS] = GECKO_MOBILE_CONNECTION_STATE_UNKNOWN;
|
||||
|
||||
|
||||
this.GECKO_CALL_ERROR_BAD_NUMBER = "BadNumberError";
|
||||
this.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING = "NormalCallClearingError";
|
||||
this.GECKO_CALL_ERROR_BUSY = "BusyError";
|
||||
this.GECKO_CALL_ERROR_NO_USER_RESPONDING = "NoUserRespondingError";
|
||||
this.GECKO_CALL_ERROR_USER_ALERTING = "UserAlertingNoAnswerError";
|
||||
this.GECKO_CALL_ERROR_REJECTED = "CallRejectedError";
|
||||
this.GECKO_CALL_ERROR_NUMBER_CHANGED = "NumberChangedError";
|
||||
this.GECKO_CALL_ERROR_PRE_EMPTION = "PreEmptionError";
|
||||
this.GECKO_CALL_ERROR_DEST_OUT_OF_ORDER = "DestinationOutOfOrderError";
|
||||
this.GECKO_CALL_ERROR_INVALID_NUMBER_FORMAT = "InvalidNumberFormatError";
|
||||
this.GECKO_CALL_ERROR_FACILITY_REJECTED = "FacilityRejectedError";
|
||||
this.GECKO_CALL_ERROR_CONGESTION = "CongestionError";
|
||||
this.GECKO_CALL_ERROR_NETWORK_OUT_OF_ORDER = "NetworkOutOfOrderError";
|
||||
this.GECKO_CALL_ERROR_NETWORK_TEMP_FAILURE = "NetworkTempFailureError";
|
||||
this.GECKO_CALL_ERROR_INCOMING_CALL_EXCEEDED = "IncomingCallExceededError";
|
||||
this.GECKO_CALL_ERROR_BARRED = "BarredError";
|
||||
this.GECKO_CALL_ERROR_FDN_BLOCKED = "FDNBlockedError";
|
||||
this.GECKO_CALL_ERROR_SUBSCRIBER_UNKNOWN = "SubscriberUnknownError";
|
||||
this.GECKO_CALL_ERROR_DEVICE_NOT_ACCEPTED = "DeviceNotAcceptedError";
|
||||
this.GECKO_CALL_ERROR_BAD_NUMBER = "BadNumberError";
|
||||
this.GECKO_CALL_ERROR_NO_ROUTE_TO_DESTINATION = "NoRouteToDestinationError";
|
||||
this.GECKO_CALL_ERROR_CHANNEL_UNACCEPTABLE = "ChannelUnacceptableError";
|
||||
this.GECKO_CALL_ERROR_OPERATOR_DETERMINED_BARRING = "OperatorDeterminedBarringError";
|
||||
this.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING = "NormalCallClearingError";
|
||||
this.GECKO_CALL_ERROR_BUSY = "BusyError";
|
||||
this.GECKO_CALL_ERROR_NO_USER_RESPONDING = "NoUserRespondingError";
|
||||
this.GECKO_CALL_ERROR_USER_ALERTING = "UserAlertingNoAnswerError";
|
||||
this.GECKO_CALL_ERROR_REJECTED = "CallRejectedError";
|
||||
this.GECKO_CALL_ERROR_NUMBER_CHANGED = "NumberChangedError";
|
||||
this.GECKO_CALL_ERROR_REJECTED_DETINATION_FEATURE = "CallRejectedDestinationFeature";
|
||||
this.GECKO_CALL_ERROR_PRE_EMPTION = "PreEmptionError";
|
||||
this.GECKO_CALL_ERROR_DEST_OUT_OF_ORDER = "DestinationOutOfOrderError";
|
||||
this.GECKO_CALL_ERROR_INVALID_NUMBER_FORMAT = "InvalidNumberFormatError";
|
||||
this.GECKO_CALL_ERROR_FACILITY_REJECTED = "FacilityRejectedError";
|
||||
this.GECKO_CALL_ERROR_RESPONSE_TO_STATUS_ENQUIRY = "ResponseToStatusEnquiryError";
|
||||
this.GECKO_CALL_ERROR_CONGESTION = "CongestionError";
|
||||
this.GECKO_CALL_ERROR_NETWORK_OUT_OF_ORDER = "NetworkOutOfOrderError";
|
||||
this.GECKO_CALL_ERROR_NETWORK_TEMP_FAILURE = "NetworkTempFailureError";
|
||||
this.GECKO_CALL_ERROR_SWITCHING_EQUIP_CONGESTION = "SwitchingEquipCongestionError";
|
||||
this.GECKO_CALL_ERROR_ACCESS_INFO_DISCARDED = "AccessInfoDiscardedError";
|
||||
this.GECKO_CALL_ERROR_REQUESTED_CHANNEL_NOT_AVAILABLE = "RequestedChannelNotAvailableError";
|
||||
this.GECKO_CALL_ERROR_RESOURCE_UNAVAILABLE = "ResourceUnavailableError";
|
||||
this.GECKO_CALL_ERROR_QOS_UNAVAILABLE = "QosUnavailableError";
|
||||
this.GECKO_CALL_ERROR_REQUESTED_FACILITY_NOT_SUBSCRIBED = "RequestedFacilityNotSubscribedError";
|
||||
this.GECKO_CALL_ERROR_INCOMING_CALLS_BARRED_WITHIN_CUG = "IncomingCallsBarredWithinCugError";
|
||||
this.GECKO_CALL_ERROR_BEARER_CAPABILITY_NOT_AUTHORIZED = "BearerCapabilityNotAuthorizedError";
|
||||
this.GECKO_CALL_ERROR_BEARER_CAPABILITY_NOT_AVAILABLE = "BearerCapabilityNotAvailableError";
|
||||
this.GECKO_CALL_ERROR_BEARER_NOT_IMPLEMENTED = "BearerNotImplementedError";
|
||||
this.GECKO_CALL_ERROR_SERVICE_NOT_AVAILABLE = "ServiceNotAvailableError";
|
||||
this.GECKO_CALL_ERROR_INCOMING_CALL_EXCEEDED = "IncomingCallExceededError";
|
||||
this.GECKO_CALL_ERROR_REQUESTED_FACILITY_NOT_IMPLEMENTED = "RequestedFacilityNotImplementedError";
|
||||
this.GECKO_CALL_ERROR_UNRESTRICTED_BEARER_NOT_AVAILABLE = "UnrestrictedBearerNotAvailableError";
|
||||
this.GECKO_CALL_ERROR_SERVICE_NOT_IMPLEMENTED = "ServiceNotImplementedError";
|
||||
this.GECKO_CALL_ERROR_INVALID_TRANSACTION_ID = "InvalidTransactionIdError";
|
||||
this.GECKO_CALL_ERROR_USER_NOT_CUG_MEMBER = "NotCugMemberError";
|
||||
this.GECKO_CALL_ERROR_INCOMPATIBLE_DESTINATION = "IncompatibleDestinationError";
|
||||
this.GECKO_CALL_ERROR_INVALID_TRANSIT_NETWORK_SELECTION = "InvalidTransitNetworkSelectionError";
|
||||
this.GECKO_CALL_ERROR_SEMANTICALLY_INCORRECT_MESSAGE = "SemanticallyIncorrectMessageError";
|
||||
this.GECKO_CALL_ERROR_INVALID_MANDATORY_INFO = "InvalidMandatoryInfoError";
|
||||
this.GECKO_CALL_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED = "MessageTypeNotImplementedError";
|
||||
this.GECKO_CALL_ERROR_MESSAGE_TYPE_INCOMPATIBLE_PROTOCOL_STATE = "MessageTypeIncompatibleProtocolStateError";
|
||||
this.GECKO_CALL_ERROR_INFO_ELEMENT_NOT_IMPLEMENTED = "InfoElementNotImplementedError";
|
||||
this.GECKO_CALL_ERROR_CONDITIONAL_IE = "ConditionalIeError";
|
||||
this.GECKO_CALL_ERROR_MESSAGE_INCOMPATIBLE_PROTOCOL_STATE = "MessageIncompatibleProtocolStateError";
|
||||
this.GECKO_CALL_ERROR_RECOVERY_ON_TIMER_EXPIRY = "RecoveryOnTimerExpiryError";
|
||||
this.GECKO_CALL_ERROR_PROTOCOL = "ProtocolError";
|
||||
this.GECKO_CALL_ERROR_INTERWORKING = "InterworkingError";
|
||||
this.GECKO_CALL_ERROR_BARRED = "BarredError";
|
||||
this.GECKO_CALL_ERROR_FDN_BLOCKED = "FDNBlockedError";
|
||||
this.GECKO_CALL_ERROR_SUBSCRIBER_UNKNOWN = "SubscriberUnknownError";
|
||||
this.GECKO_CALL_ERROR_DEVICE_NOT_ACCEPTED = "DeviceNotAcceptedError";
|
||||
this.GECKO_CALL_ERROR_MODIFIED_TO_DIAL_FAILED = "ModifiedDialError";
|
||||
this.GECKO_CALL_ERROR_UNSPECIFIED = "UnspecifiedError";
|
||||
this.GECKO_CALL_ERROR_CDMA_LOCKED_UNTIL_POWER_CYCLE = "CdmaLockedUntilPowerCycleError";
|
||||
this.GECKO_CALL_ERROR_CDMA_DROP = "CdmaDropError";
|
||||
this.GECKO_CALL_ERROR_CDMA_INTERCEPT = "CdmaInterceptError";
|
||||
this.GECKO_CALL_ERROR_CDMA_REORDER = "CdmaReorderError";
|
||||
this.GECKO_CALL_ERROR_CDMA_SO_REJECT = "CdmaSoRejectError";
|
||||
this.GECKO_CALL_ERROR_CDMA_RETRY_ORDER = "CdmaRetryOrderError";
|
||||
this.GECKO_CALL_ERROR_CDMA_ACCESS_FAILURE = "CdmaAcessError";
|
||||
this.GECKO_CALL_ERROR_CDMA_PREEMPTED = "CdmaPreemptedError";
|
||||
this.GECKO_CALL_ERROR_CDMA_NOT_EMERGENCY = "CdmaNotEmergencyError";
|
||||
this.GECKO_CALL_ERROR_CDMA_ACCESS_BLOCKED = "CdmaAccessBlockedError";
|
||||
this.GECKO_CALL_ERROR_UNSPECIFIED = "UnspecifiedError";
|
||||
|
||||
this.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR = {};
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_UNOBTAINABLE_NUMBER] = GECKO_CALL_ERROR_BAD_NUMBER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NORMAL] = GECKO_CALL_ERROR_NORMAL_CALL_CLEARING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_BUSY] = GECKO_CALL_ERROR_BUSY;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NO_USER_RESPONDING] = GECKO_CALL_ERROR_NO_USER_RESPONDING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_USER_ALERTING] = GECKO_CALL_ERROR_USER_ALERTING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_REJECTED] = GECKO_CALL_ERROR_REJECTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NUMBER_CHANGED] = GECKO_CALL_ERROR_NUMBER_CHANGED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_PRE_EMPTION] = GECKO_CALL_ERROR_PRE_EMPTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_DEST_OUT_OF_ORDER] = GECKO_CALL_ERROR_DEST_OUT_OF_ORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INVALID_FORMAT] = GECKO_CALL_ERROR_INVALID_NUMBER_FORMAT;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_FACILITY_REJECTED] = GECKO_CALL_ERROR_FACILITY_REJECTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CONGESTION] = GECKO_CALL_ERROR_CONGESTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NETWORK_OUT_OF_ORDER] = GECKO_CALL_ERROR_NETWORK_OUT_OF_ORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NETWORK_TEMP_FAILURE] = GECKO_CALL_ERROR_NETWORK_TEMP_FAILURE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ACM_LIMIT_EXCEEDED] = GECKO_CALL_ERROR_INCOMING_CALL_EXCEEDED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_BARRED] = GECKO_CALL_ERROR_BARRED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_FDN_BLOCKED] = GECKO_CALL_ERROR_FDN_BLOCKED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMSI_UNKNOWN_IN_VLR] = GECKO_CALL_ERROR_SUBSCRIBER_UNKNOWN;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMEI_NOT_ACCEPTED] = GECKO_CALL_ERROR_DEVICE_NOT_ACCEPTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_UNOBTAINABLE_NUMBER] = GECKO_CALL_ERROR_BAD_NUMBER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NO_ROUTE_TO_DESTINATION] = GECKO_CALL_ERROR_NO_ROUTE_TO_DESTINATION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CHANNEL_UNACCEPTABLE] = GECKO_CALL_ERROR_CHANNEL_UNACCEPTABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_OPERATOR_DETERMINED_BARRING] = GECKO_CALL_ERROR_OPERATOR_DETERMINED_BARRING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NORMAL] = GECKO_CALL_ERROR_NORMAL_CALL_CLEARING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_BUSY] = GECKO_CALL_ERROR_BUSY;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NO_USER_RESPONDING] = GECKO_CALL_ERROR_NO_USER_RESPONDING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_USER_ALERTING] = GECKO_CALL_ERROR_USER_ALERTING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_REJECTED] = GECKO_CALL_ERROR_REJECTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NUMBER_CHANGED] = GECKO_CALL_ERROR_NUMBER_CHANGED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_REJECTED_DESTINATION_FEATURE] = GECKO_CALL_ERROR_REJECTED_DETINATION_FEATURE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_PRE_EMPTION] = GECKO_CALL_ERROR_PRE_EMPTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_DEST_OUT_OF_ORDER] = GECKO_CALL_ERROR_DEST_OUT_OF_ORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INVALID_FORMAT] = GECKO_CALL_ERROR_INVALID_NUMBER_FORMAT;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_FACILITY_REJECTED] = GECKO_CALL_ERROR_FACILITY_REJECTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_RESPONSE_TO_STATUS_ENQUIRY] = GECKO_CALL_ERROR_RESPONSE_TO_STATUS_ENQUIRY;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NORMAL_UNSPECIFIED] = GECKO_CALL_ERROR_NORMAL_CALL_CLEARING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CONGESTION] = GECKO_CALL_ERROR_CONGESTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NETWORK_OUT_OF_ORDER] = GECKO_CALL_ERROR_NETWORK_OUT_OF_ORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_NETWORK_TEMP_FAILURE] = GECKO_CALL_ERROR_NETWORK_TEMP_FAILURE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_SWITCHING_EQUIP_CONGESTION] = GECKO_CALL_ERROR_SWITCHING_EQUIP_CONGESTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ACCESS_INFO_DISCARDED] = GECKO_CALL_ERROR_ACCESS_INFO_DISCARDED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_REQUESTED_CHANNEL_NOT_AVAILABLE] = GECKO_CALL_ERROR_REQUESTED_CHANNEL_NOT_AVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_RESOURCE_UNAVAILABLE] = GECKO_CALL_ERROR_RESOURCE_UNAVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_QOS_UNAVAILABLE] = GECKO_CALL_ERROR_QOS_UNAVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_REQUESTED_FACILITY_NOT_SUBSCRIBED] = GECKO_CALL_ERROR_REQUESTED_FACILITY_NOT_SUBSCRIBED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INCOMING_CALLS_BARRED_WITHIN_CUG] = GECKO_CALL_ERROR_INCOMING_CALLS_BARRED_WITHIN_CUG;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_BEARER_CAPABILITY_NOT_AUTHORIZED] = GECKO_CALL_ERROR_BEARER_CAPABILITY_NOT_AUTHORIZED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_BEARER_CAPABILITY_NOT_AVAILABLE] = GECKO_CALL_ERROR_BEARER_CAPABILITY_NOT_AVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_SERVICE_NOT_AVAILABLE] = GECKO_CALL_ERROR_SERVICE_NOT_AVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_BEARER_NOT_IMPLEMENTED] = GECKO_CALL_ERROR_BEARER_NOT_IMPLEMENTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ACM_LIMIT_EXCEEDED] = GECKO_CALL_ERROR_INCOMING_CALL_EXCEEDED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_REQUESTED_FACILITY_NOT_IMPLEMENTED] = GECKO_CALL_ERROR_REQUESTED_FACILITY_NOT_IMPLEMENTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_UNRESTRICTED_BEARER_NOT_AVAILABLE] = GECKO_CALL_ERROR_UNRESTRICTED_BEARER_NOT_AVAILABLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_SERVICE_NOT_IMPLEMENTED] = GECKO_CALL_ERROR_SERVICE_NOT_IMPLEMENTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INVALID_TRANSACTION_ID] = GECKO_CALL_ERROR_INVALID_TRANSACTION_ID;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_USER_NOT_CUG_MEMBER] = GECKO_CALL_ERROR_USER_NOT_CUG_MEMBER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INCOMPATIBLE_DESTINATION] = GECKO_CALL_ERROR_INCOMPATIBLE_DESTINATION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INVALID_TRANSIT_NETWORK_SELECTION] = GECKO_CALL_ERROR_INVALID_TRANSIT_NETWORK_SELECTION;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_SEMANTICALLY_INCORRECT_MESSAGE] = GECKO_CALL_ERROR_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INVALID_MANDATORY_INFO] = GECKO_CALL_ERROR_INVALID_MANDATORY_INFO;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_MESSAGE_TYPE_NOT_IMPLEMENTED] = GECKO_CALL_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_MESSAGE_TYPE_INCOMPATIBLE_PROTOCOL_STATE] = GECKO_CALL_ERROR_MESSAGE_TYPE_INCOMPATIBLE_PROTOCOL_STATE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INFO_ELEMENT_NOT_IMPLEMENTED] = GECKO_CALL_ERROR_INFO_ELEMENT_NOT_IMPLEMENTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CONDITIONAL_IE_ERROR] = GECKO_CALL_ERROR_CONDITIONAL_IE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_MESSAGE_INCOMPABITLE_PROTOCOL_STATE] = GECKO_CALL_ERROR_MESSAGE_INCOMPATIBLE_PROTOCOL_STATE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_RECOVERY_ON_TIMER_EXPIRY] = GECKO_CALL_ERROR_RECOVERY_ON_TIMER_EXPIRY;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_PROTOCOL_ERROR] = GECKO_CALL_ERROR_PROTOCOL;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_INTERWORKING] = GECKO_CALL_ERROR_INTERWORKING;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CALL_BARRED] = GECKO_CALL_ERROR_BARRED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_FDN_BLOCKED] = GECKO_CALL_ERROR_FDN_BLOCKED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMSI_UNKNOWN_IN_VLR] = GECKO_CALL_ERROR_SUBSCRIBER_UNKNOWN;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_IMEI_NOT_ACCEPTED] = GECKO_CALL_ERROR_DEVICE_NOT_ACCEPTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_DIAL_MODIFIED_TO_USSD] = GECKO_CALL_ERROR_MODIFIED_TO_DIAL_FAILED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_DIAL_MODIFIED_TO_SS] = GECKO_CALL_ERROR_MODIFIED_TO_DIAL_FAILED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_DIAL_MODIFIED_TO_DIAL] = GECKO_CALL_ERROR_MODIFIED_TO_DIAL_FAILED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ERROR_UNSPECIFIED] = GECKO_CALL_ERROR_UNSPECIFIED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_LOCKED_UNTIL_POWER_CYCLE] = GECKO_CALL_ERROR_CDMA_LOCKED_UNTIL_POWER_CYCLE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_DROP] = GECKO_CALL_ERROR_CDMA_DROP;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_INTERCEPT] = GECKO_CALL_ERROR_CDMA_INTERCEPT;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_REORDER] = GECKO_CALL_ERROR_CDMA_REORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_SO_REJECT] = GECKO_CALL_ERROR_CDMA_SO_REJECT;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_RETRY_ORDER] = GECKO_CALL_ERROR_CDMA_RETRY_ORDER;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_ACCESS_FAILURE] = GECKO_CALL_ERROR_CDMA_ACCESS_FAILURE;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_PREEMPTED] = GECKO_CALL_ERROR_CDMA_PREEMPTED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_NOT_EMERGENCY] = GECKO_CALL_ERROR_CDMA_NOT_EMERGENCY;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_CDMA_ACCESS_BLOCKED] = GECKO_CALL_ERROR_CDMA_ACCESS_BLOCKED;
|
||||
RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[CALL_FAIL_ERROR_UNSPECIFIED] = GECKO_CALL_ERROR_UNSPECIFIED;
|
||||
|
||||
this.GECKO_DATACALL_ERROR_OPERATOR_BARRED = "OperatorBarredError";
|
||||
this.GECKO_DATACALL_ERROR_INSUFFICIENT_RESOURCES = "InsufficientResourcesError";
|
||||
|
@ -3894,7 +3894,8 @@ RilObject.prototype = {
|
||||
* Helpers for processing call state changes.
|
||||
*/
|
||||
_processCalls: function(newCalls, failCause) {
|
||||
if (DEBUG) this.context.debug("_processCalls: " + JSON.stringify(newCalls));
|
||||
if (DEBUG) this.context.debug("_processCalls: " + JSON.stringify(newCalls) +
|
||||
" failCause: " + failCause);
|
||||
|
||||
// Let's get the failCause first if there are removed calls. Otherwise, we
|
||||
// need to trigger another async request when removing call and it cause
|
||||
@ -5455,7 +5456,16 @@ RilObject.prototype[REQUEST_UDUB] = function REQUEST_UDUB(length, options) {
|
||||
RilObject.prototype[REQUEST_LAST_CALL_FAIL_CAUSE] = function REQUEST_LAST_CALL_FAIL_CAUSE(length, options) {
|
||||
let Buf = this.context.Buf;
|
||||
let num = length ? Buf.readInt32() : 0;
|
||||
let failCause = num ? RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[Buf.readInt32()] : null;
|
||||
let failCause = null;
|
||||
|
||||
if (num) {
|
||||
let causeNum = Buf.readInt32();
|
||||
// To make _processCalls work as design, failCause couldn't be "undefined."
|
||||
// See Bug 1112550 for details.
|
||||
failCause = RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[causeNum] || null;
|
||||
}
|
||||
if (DEBUG) this.context.debug("Last call fail cause: " + failCause);
|
||||
|
||||
if (options.callback) {
|
||||
options.callback(failCause);
|
||||
}
|
||||
|
@ -8,7 +8,9 @@
|
||||
ChromeOnly]
|
||||
interface ActivityRequestHandler
|
||||
{
|
||||
[UnsafeInPrerendering]
|
||||
void postResult(any result);
|
||||
[UnsafeInPrerendering]
|
||||
void postError(DOMString error);
|
||||
[Pure, Cached, Frozen]
|
||||
readonly attribute ActivityOptions source;
|
||||
|
@ -12,6 +12,8 @@
|
||||
Pref="dom.mozAlarms.enabled"]
|
||||
interface AlarmsManager {
|
||||
DOMRequest getAll();
|
||||
[UnsafeInPrerendering]
|
||||
DOMRequest add(any date, DOMString respectTimezone, optional any data);
|
||||
[UnsafeInPrerendering]
|
||||
void remove(unsigned long id);
|
||||
};
|
||||
|
@ -20,10 +20,10 @@ interface AudioBufferSourceNode : AudioNode {
|
||||
attribute double loopStart;
|
||||
attribute double loopEnd;
|
||||
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void start(optional double when = 0, optional double grainOffset = 0,
|
||||
optional double grainDuration);
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void stop(optional double when = 0);
|
||||
|
||||
attribute EventHandler onended;
|
||||
|
@ -45,9 +45,9 @@ interface AudioContext : EventTarget {
|
||||
StereoPannerNode createStereoPanner();
|
||||
[NewObject]
|
||||
AnalyserNode createAnalyser();
|
||||
[NewObject, Throws]
|
||||
[NewObject, Throws, UnsafeInPrerendering]
|
||||
MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
|
||||
[NewObject, Throws]
|
||||
[NewObject, Throws, UnsafeInPrerendering]
|
||||
MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream);
|
||||
[NewObject]
|
||||
GainNode createGain();
|
||||
|
@ -33,10 +33,12 @@ interface DOMDownloadManager : EventTarget {
|
||||
|
||||
// Removes one download from the downloads set. Returns a promise resolved
|
||||
// with the finalized download.
|
||||
[UnsafeInPrerendering]
|
||||
Promise<DOMDownload> remove(DOMDownload download);
|
||||
|
||||
// Removes all the completed downloads from the set. Returns an
|
||||
// array of the completed downloads that were removed.
|
||||
[UnsafeInPrerendering]
|
||||
Promise<sequence<DOMDownload>> clearAllDone();
|
||||
|
||||
// Fires when a new download starts.
|
||||
@ -85,10 +87,12 @@ interface DOMDownload : EventTarget {
|
||||
readonly attribute DOMError? error;
|
||||
|
||||
// Pauses the download.
|
||||
[UnsafeInPrerendering]
|
||||
Promise<DOMDownload> pause();
|
||||
|
||||
// Resumes the download. This resolves only once the download has
|
||||
// succeeded.
|
||||
[UnsafeInPrerendering]
|
||||
Promise<DOMDownload> resume();
|
||||
|
||||
// This event is triggered anytime a property of the object changes:
|
||||
|
@ -96,7 +96,7 @@ interface Element : Node {
|
||||
boolean mozMatchesSelector(DOMString selector);
|
||||
|
||||
// Pointer events methods.
|
||||
[Throws, Pref="dom.w3c_pointer_events.enabled"]
|
||||
[Throws, Pref="dom.w3c_pointer_events.enabled", UnsafeInPrerendering]
|
||||
void setPointerCapture(long pointerId);
|
||||
|
||||
[Throws, Pref="dom.w3c_pointer_events.enabled"]
|
||||
@ -128,6 +128,7 @@ interface Element : Node {
|
||||
*
|
||||
* @see <https://wiki.mozilla.org/index.php?title=Gecko:FullScreenAPI>
|
||||
*/
|
||||
[UnsafeInPrerendering]
|
||||
void mozRequestFullScreen(optional RequestFullscreenOptions fsOptions);
|
||||
|
||||
/**
|
||||
@ -136,6 +137,7 @@ interface Element : Node {
|
||||
*
|
||||
* @see <http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html>
|
||||
*/
|
||||
[UnsafeInPrerendering]
|
||||
void mozRequestPointerLock();
|
||||
|
||||
// Obsolete methods.
|
||||
|
@ -7,12 +7,12 @@
|
||||
[JSImplementation="@mozilla.org/sidebar;1"]
|
||||
interface External
|
||||
{
|
||||
void AddSearchProvider(DOMString aDescriptionURL);
|
||||
[UnsafeInPrerendering] void AddSearchProvider(DOMString aDescriptionURL);
|
||||
unsigned long IsSearchProviderInstalled(DOMString aSearchURL);
|
||||
};
|
||||
|
||||
// Mozilla extension
|
||||
partial interface External {
|
||||
void addSearchEngine(DOMString engineURL, DOMString iconURL,
|
||||
DOMString suggestedTitle, DOMString suggestedCategory);
|
||||
[UnsafeInPrerendering] void addSearchEngine(DOMString engineURL, DOMString iconURL,
|
||||
DOMString suggestedTitle, DOMString suggestedCategory);
|
||||
};
|
||||
|
@ -110,9 +110,9 @@ partial interface HTMLMediaElement {
|
||||
[Func="IsChromeOrXBL"] attribute boolean mozIsCasting;
|
||||
|
||||
// Mozilla extension: stream capture
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
MediaStream mozCaptureStream();
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
MediaStream mozCaptureStreamUntilEnded();
|
||||
readonly attribute boolean mozAudioCaptured;
|
||||
|
||||
|
@ -254,7 +254,7 @@ partial interface Navigator {
|
||||
|
||||
// nsIDOMNavigatorDesktopNotification
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="notification.feature.enabled"]
|
||||
[Throws, Pref="notification.feature.enabled", UnsafeInPrerendering]
|
||||
readonly attribute DesktopNotificationCenter mozNotification;
|
||||
};
|
||||
|
||||
@ -273,7 +273,7 @@ partial interface Navigator {
|
||||
|
||||
// nsIDOMNavigatorCamera
|
||||
partial interface Navigator {
|
||||
[Throws, Func="Navigator::HasCameraSupport"]
|
||||
[Throws, Func="Navigator::HasCameraSupport", UnsafeInPrerendering]
|
||||
readonly attribute CameraManager mozCameras;
|
||||
};
|
||||
|
||||
@ -289,27 +289,27 @@ partial interface Navigator {
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.mobileconnection.enabled", CheckPermissions="mobileconnection mobilenetwork"]
|
||||
[Throws, Pref="dom.mobileconnection.enabled", CheckPermissions="mobileconnection mobilenetwork", UnsafeInPrerendering]
|
||||
readonly attribute MozMobileConnectionArray mozMobileConnections;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.cellbroadcast.enabled", CheckPermissions="cellbroadcast"]
|
||||
[Throws, Pref="dom.cellbroadcast.enabled", CheckPermissions="cellbroadcast", UnsafeInPrerendering]
|
||||
readonly attribute MozCellBroadcast mozCellBroadcast;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.voicemail.enabled", CheckPermissions="voicemail"]
|
||||
[Throws, Pref="dom.voicemail.enabled", CheckPermissions="voicemail", UnsafeInPrerendering]
|
||||
readonly attribute MozVoicemail mozVoicemail;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.icc.enabled", CheckPermissions="mobileconnection"]
|
||||
[Throws, Pref="dom.icc.enabled", CheckPermissions="mobileconnection", UnsafeInPrerendering]
|
||||
readonly attribute MozIccManager? mozIccManager;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[Throws, Pref="dom.telephony.enabled", CheckPermissions="telephony"]
|
||||
[Throws, Pref="dom.telephony.enabled", CheckPermissions="telephony", UnsafeInPrerendering]
|
||||
readonly attribute Telephony? mozTelephony;
|
||||
};
|
||||
#endif // MOZ_B2G_RIL
|
||||
@ -329,14 +329,14 @@ partial interface Navigator {
|
||||
|
||||
#ifdef MOZ_B2G_BT
|
||||
partial interface Navigator {
|
||||
[Throws, CheckPermissions="bluetooth"]
|
||||
[Throws, CheckPermissions="bluetooth", UnsafeInPrerendering]
|
||||
readonly attribute BluetoothManager mozBluetooth;
|
||||
};
|
||||
#endif // MOZ_B2G_BT
|
||||
|
||||
#ifdef MOZ_B2G_FM
|
||||
partial interface Navigator {
|
||||
[Throws, CheckPermissions="fmradio"]
|
||||
[Throws, CheckPermissions="fmradio", UnsafeInPrerendering]
|
||||
readonly attribute FMRadio mozFMRadio;
|
||||
};
|
||||
#endif // MOZ_B2G_FM
|
||||
@ -366,7 +366,7 @@ partial interface Navigator {
|
||||
readonly attribute MediaDevices mediaDevices;
|
||||
|
||||
// Deprecated. Use mediaDevices.getUserMedia instead.
|
||||
[Throws, Func="Navigator::HasUserMediaSupport"]
|
||||
[Throws, Func="Navigator::HasUserMediaSupport", UnsafeInPrerendering]
|
||||
void mozGetUserMedia(MediaStreamConstraints constraints,
|
||||
NavigatorUserMediaSuccessCallback successCallback,
|
||||
NavigatorUserMediaErrorCallback errorCallback);
|
||||
|
@ -12,7 +12,8 @@
|
||||
*/
|
||||
|
||||
[Pref="dom.webnotifications.enabled",
|
||||
Constructor(DOMString title, optional NotificationOptions options)]
|
||||
Constructor(DOMString title, optional NotificationOptions options),
|
||||
UnsafeInPrerendering]
|
||||
interface Notification : EventTarget {
|
||||
[GetterThrows]
|
||||
static readonly attribute NotificationPermission permission;
|
||||
|
@ -26,9 +26,9 @@ interface OscillatorNode : AudioNode {
|
||||
readonly attribute AudioParam frequency; // in Hertz
|
||||
readonly attribute AudioParam detune; // in Cents
|
||||
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void start(optional double when = 0);
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void stop(optional double when = 0);
|
||||
void setPeriodicWave(PeriodicWave periodicWave);
|
||||
|
||||
|
@ -102,8 +102,11 @@ interface mozRTCPeerConnection : EventTarget {
|
||||
attribute DOMString id;
|
||||
|
||||
RTCConfiguration getConfiguration ();
|
||||
[UnsafeInPrerendering]
|
||||
sequence<MediaStream> getLocalStreams ();
|
||||
[UnsafeInPrerendering]
|
||||
sequence<MediaStream> getRemoteStreams ();
|
||||
[UnsafeInPrerendering]
|
||||
MediaStream? getStreamById (DOMString streamId);
|
||||
void addStream (MediaStream stream);
|
||||
void removeStream (MediaStream stream);
|
||||
|
@ -40,13 +40,14 @@ interface Screen : EventTarget {
|
||||
/**
|
||||
* Lock screen orientation to the specified type.
|
||||
*/
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
boolean mozLockOrientation(DOMString orientation);
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
boolean mozLockOrientation(sequence<DOMString> orientation);
|
||||
|
||||
/**
|
||||
* Unlock the screen orientation.
|
||||
*/
|
||||
[UnsafeInPrerendering]
|
||||
void mozUnlockOrientation();
|
||||
};
|
||||
|
@ -28,7 +28,7 @@ interface SpeechRecognition : EventTarget {
|
||||
attribute DOMString serviceURI;
|
||||
|
||||
// methods to drive the speech interaction
|
||||
[Throws]
|
||||
[Throws, UnsafeInPrerendering]
|
||||
void start(optional MediaStream stream);
|
||||
void stop();
|
||||
void abort();
|
||||
|
@ -16,9 +16,11 @@ interface SpeechSynthesis {
|
||||
readonly attribute boolean speaking;
|
||||
readonly attribute boolean paused;
|
||||
|
||||
[UnsafeInPrerendering]
|
||||
void speak(SpeechSynthesisUtterance utterance);
|
||||
void cancel();
|
||||
void pause();
|
||||
[UnsafeInPrerendering]
|
||||
void resume();
|
||||
sequence<SpeechSynthesisVoice> getVoices();
|
||||
};
|
||||
|
@ -234,8 +234,8 @@ hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS)
|
||||
$(AM_V_GEN) $(GLIB_MKENUMS) \
|
||||
--identifier-prefix hb_ --symbol-prefix hb_gobject \
|
||||
--template $^ | \
|
||||
sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@.tmp" && \
|
||||
mv "$@.tmp" "$@" || ( $(RM) "@.tmp" && false )
|
||||
sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \
|
||||
|| ($(RM) "$@"; false)
|
||||
endif
|
||||
EXTRA_DIST += \
|
||||
harfbuzz-gobject.pc.in \
|
||||
@ -251,8 +251,8 @@ EXTRA_DIST += \
|
||||
-e 's@%libdir%@$(libdir)@g' \
|
||||
-e 's@%includedir%@$(includedir)@g' \
|
||||
-e 's@%VERSION%@$(VERSION)@g' \
|
||||
"$<" \
|
||||
> "$@.tmp" && mv "$@.tmp" "$@" || ( $(RM) "$@.tmp"; false )
|
||||
"$<" > "$@" \
|
||||
|| ($(RM) "$@"; false)
|
||||
|
||||
CLEANFILES += $(pkgconfig_DATA)
|
||||
|
||||
@ -265,8 +265,9 @@ harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
|
||||
sed -e 's/ (.*//' | \
|
||||
LANG=C sort; \
|
||||
echo LIBRARY libharfbuzz-$(HB_VERSION_MAJOR).dll; \
|
||||
) >"$@.tmp"
|
||||
@ ! grep -q hb_ERROR "$@.tmp" && mv "$@.tmp" "$@" || ($(RM) "$@"; false)
|
||||
) >"$@"
|
||||
@ ! grep -q hb_ERROR "$@" \
|
||||
|| ($(RM) "$@"; false)
|
||||
|
||||
|
||||
GENERATORS = \
|
||||
@ -278,26 +279,25 @@ EXTRA_DIST += $(GENERATORS)
|
||||
unicode-tables: arabic-table indic-table
|
||||
|
||||
indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
|
||||
$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc.tmp && \
|
||||
mv hb-ot-shape-complex-indic-table.cc.tmp $(srcdir)/hb-ot-shape-complex-indic-table.cc || \
|
||||
($(RM) hb-ot-shape-complex-indic-table.cc.tmp; false)
|
||||
$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc \
|
||||
|| ($(RM) hb-ot-shape-complex-indic-table.cc; false)
|
||||
|
||||
arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt
|
||||
$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \
|
||||
mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \
|
||||
($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false)
|
||||
$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh \
|
||||
|| ($(RM) hb-ot-shape-complex-arabic-table.hh; false)
|
||||
|
||||
built-sources: $(BUILT_SOURCES)
|
||||
|
||||
.PHONY: unicode-tables arabic-table indic-table built-sources
|
||||
|
||||
BUILT_SOURCES += \
|
||||
hb-buffer-deserialize-json.hh \
|
||||
hb-buffer-deserialize-text.hh \
|
||||
hb-ot-shape-complex-indic-machine.hh \
|
||||
hb-ot-shape-complex-myanmar-machine.hh \
|
||||
hb-ot-shape-complex-sea-machine.hh \
|
||||
RAGEL_GENERATED = \
|
||||
$(srcdir)/hb-buffer-deserialize-json.hh \
|
||||
$(srcdir)/hb-buffer-deserialize-text.hh \
|
||||
$(srcdir)/hb-ot-shape-complex-indic-machine.hh \
|
||||
$(srcdir)/hb-ot-shape-complex-myanmar-machine.hh \
|
||||
$(srcdir)/hb-ot-shape-complex-sea-machine.hh \
|
||||
$(NULL)
|
||||
BUILT_SOURCES += $(RAGEL_GENERATED)
|
||||
EXTRA_DIST += \
|
||||
hb-buffer-deserialize-json.rl \
|
||||
hb-buffer-deserialize-text.rl \
|
||||
@ -305,9 +305,10 @@ EXTRA_DIST += \
|
||||
hb-ot-shape-complex-myanmar-machine.rl \
|
||||
hb-ot-shape-complex-sea-machine.rl \
|
||||
$(NULL)
|
||||
.rl.hh:
|
||||
$(AM_V_GEN)$(RAGEL) -e -F1 -o "$@.tmp" "$<" && \
|
||||
mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
|
||||
MAINTAINERCLEANFILES += $(RAGEL_GENERATED)
|
||||
$(srcdir)/%.hh: $(srcdir)/%.rl
|
||||
$(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \
|
||||
|| ($(RM) "$@"; false)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
main \
|
||||
|
@ -17,14 +17,14 @@ fi
|
||||
tested=false
|
||||
for suffix in so dylib; do
|
||||
so=.libs/libharfbuzz.$suffix
|
||||
if test -f "$so"; then
|
||||
echo "Checking that we are not linking to libstdc++"
|
||||
if ldd $so | grep 'libstdc[+][+]'; then
|
||||
echo "Ouch, linked to libstdc++"
|
||||
stat=1
|
||||
fi
|
||||
tested=true
|
||||
if ! test -f "$so"; then continue; fi
|
||||
|
||||
echo "Checking that we are not linking to libstdc++"
|
||||
if ldd $so | grep 'libstdc[+][+]'; then
|
||||
echo "Ouch, linked to libstdc++"
|
||||
stat=1
|
||||
fi
|
||||
tested=true
|
||||
done
|
||||
if ! $tested; then
|
||||
echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
|
||||
|
@ -22,8 +22,8 @@ fi
|
||||
|
||||
echo "Checking that no object file has static initializers"
|
||||
for obj in $OBJS; do
|
||||
if objdump -t "$obj" | grep '[.]ctors'; then
|
||||
echo "Ouch, $obj has static initializers"
|
||||
if objdump -t "$obj" | grep '[.][cd]tors' | grep -v '\<00*\>'; then
|
||||
echo "Ouch, $obj has static initializers/finalizers"
|
||||
stat=1
|
||||
fi
|
||||
done
|
||||
|
@ -16,11 +16,17 @@ fi
|
||||
|
||||
echo "Checking that we are not exposing internal symbols"
|
||||
tested=false
|
||||
for so in `ls .libs/lib*.so .libs/lib*.dylib 2>/dev/null` ; do
|
||||
for suffix in so dylib; do
|
||||
so=.libs/libharfbuzz.$suffix
|
||||
if ! test -f "$so"; then continue; fi
|
||||
|
||||
EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3`"
|
||||
EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' _fini\>\| _init\>\| _fdata\>\| _ftext\>\| _fbss\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>\| __gcov_flush\>\| llvm_' | cut -d' ' -f3`"
|
||||
|
||||
prefix=`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'`
|
||||
|
||||
# On mac, C symbols are prefixed with _
|
||||
if test $suffix = dylib; then prefix="_$prefix"; fi
|
||||
|
||||
echo "Processing $so"
|
||||
if echo "$EXPORTED_SYMBOLS" | grep -v "^${prefix}_"; then
|
||||
echo "Ouch, internal symbols exposed"
|
||||
|
@ -102,7 +102,10 @@ hb_blob_create (const char *data,
|
||||
{
|
||||
hb_blob_t *blob;
|
||||
|
||||
if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
|
||||
if (!length ||
|
||||
length >= 1u << 31 ||
|
||||
data + length < data /* overflows */ ||
|
||||
!(blob = hb_object_create<hb_blob_t> ())) {
|
||||
if (destroy)
|
||||
destroy (user_data);
|
||||
return hb_blob_get_empty ();
|
||||
|
@ -117,8 +117,8 @@ _hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
|
||||
|
||||
const char *tok = NULL;
|
||||
int cs;
|
||||
hb_glyph_info_t info;
|
||||
hb_glyph_position_t pos;
|
||||
hb_glyph_info_t info = {0};
|
||||
hb_glyph_position_t pos = {0};
|
||||
%%{
|
||||
write init;
|
||||
write exec;
|
||||
|
@ -48,15 +48,13 @@ struct hb_buffer_t {
|
||||
ASSERT_POD ();
|
||||
|
||||
/* Information about how the text in the buffer should be treated */
|
||||
|
||||
hb_unicode_funcs_t *unicode; /* Unicode functions */
|
||||
hb_segment_properties_t props; /* Script, language, direction */
|
||||
hb_buffer_flags_t flags; /* BOT / EOT / etc. */
|
||||
hb_codepoint_t replacement; /* U+FFFD or something else. */
|
||||
|
||||
/* Buffer contents */
|
||||
|
||||
hb_buffer_content_type_t content_type;
|
||||
hb_segment_properties_t props; /* Script, language, direction */
|
||||
|
||||
bool in_error; /* Allocation failed */
|
||||
bool have_output; /* Whether we have an output buffer going on */
|
||||
@ -183,6 +181,9 @@ struct hb_buffer_t {
|
||||
inline bool ensure (unsigned int size)
|
||||
{ return likely (!size || size < allocated) ? true : enlarge (size); }
|
||||
|
||||
inline bool ensure_inplace (unsigned int size)
|
||||
{ return likely (!size || size < allocated); }
|
||||
|
||||
HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
|
||||
HB_INTERNAL bool shift_forward (unsigned int count);
|
||||
|
||||
|
@ -178,6 +178,7 @@ hb_buffer_t::reset (void)
|
||||
|
||||
hb_unicode_funcs_destroy (unicode);
|
||||
unicode = hb_unicode_funcs_get_default ();
|
||||
flags = HB_BUFFER_FLAG_DEFAULT;
|
||||
replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
|
||||
|
||||
clear ();
|
||||
@ -191,7 +192,6 @@ hb_buffer_t::clear (void)
|
||||
|
||||
hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
|
||||
props = default_props;
|
||||
flags = HB_BUFFER_FLAG_DEFAULT;
|
||||
|
||||
content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
|
||||
in_error = false;
|
||||
@ -702,11 +702,11 @@ hb_buffer_get_empty (void)
|
||||
HB_OBJECT_HEADER_STATIC,
|
||||
|
||||
const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
|
||||
HB_SEGMENT_PROPERTIES_DEFAULT,
|
||||
HB_BUFFER_FLAG_DEFAULT,
|
||||
HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
|
||||
|
||||
HB_BUFFER_CONTENT_TYPE_INVALID,
|
||||
HB_SEGMENT_PROPERTIES_DEFAULT,
|
||||
true, /* in_error */
|
||||
true, /* have_output */
|
||||
true /* have_positions */
|
||||
|
@ -234,7 +234,7 @@ struct hb_language_item_t {
|
||||
|
||||
static hb_language_item_t *langs;
|
||||
|
||||
#ifdef HAVE_ATEXIT
|
||||
#ifdef HB_USE_ATEXIT
|
||||
static inline
|
||||
void free_langs (void)
|
||||
{
|
||||
@ -269,7 +269,7 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATEXIT
|
||||
#ifdef HB_USE_ATEXIT
|
||||
if (!first_lang)
|
||||
atexit (free_langs); /* First person registers atexit() callback. */
|
||||
#endif
|
||||
|
@ -123,12 +123,13 @@ hb_direction_from_string (const char *str, int len);
|
||||
const char *
|
||||
hb_direction_to_string (hb_direction_t direction);
|
||||
|
||||
#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4)
|
||||
/* Direction must be valid for the following */
|
||||
#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4)
|
||||
#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6)
|
||||
#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4)
|
||||
#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5)
|
||||
#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4)
|
||||
#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1)) /* Direction must be valid */
|
||||
#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1))
|
||||
|
||||
|
||||
/* hb_language_t */
|
||||
|
@ -27,6 +27,7 @@
|
||||
*/
|
||||
|
||||
#define HB_SHAPER coretext
|
||||
#define hb_coretext_shaper_face_data_t CGFont
|
||||
#include "hb-shaper-impl-private.hh"
|
||||
|
||||
#include "hb-coretext.h"
|
||||
@ -77,10 +78,6 @@ HB_SHAPER_DATA_ENSURE_DECLARE(coretext, font)
|
||||
* shaper face data
|
||||
*/
|
||||
|
||||
struct hb_coretext_shaper_face_data_t {
|
||||
CGFontRef cg_font;
|
||||
};
|
||||
|
||||
static void
|
||||
release_data (void *info, const void *data, size_t size)
|
||||
{
|
||||
@ -93,13 +90,11 @@ release_data (void *info, const void *data, size_t size)
|
||||
hb_coretext_shaper_face_data_t *
|
||||
_hb_coretext_shaper_face_data_create (hb_face_t *face)
|
||||
{
|
||||
hb_coretext_shaper_face_data_t *data = (hb_coretext_shaper_face_data_t *) calloc (1, sizeof (hb_coretext_shaper_face_data_t));
|
||||
if (unlikely (!data))
|
||||
return NULL;
|
||||
hb_coretext_shaper_face_data_t *data = NULL;
|
||||
|
||||
if (face->destroy == (hb_destroy_func_t) CGFontRelease)
|
||||
{
|
||||
data->cg_font = CGFontRetain ((CGFontRef) face->user_data);
|
||||
data = CGFontRetain ((CGFontRef) face->user_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -110,14 +105,15 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face)
|
||||
DEBUG_MSG (CORETEXT, face, "Face has empty blob");
|
||||
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data);
|
||||
data->cg_font = CGFontCreateWithDataProvider (provider);
|
||||
CGDataProviderRelease (provider);
|
||||
if (likely (provider))
|
||||
{
|
||||
data = CGFontCreateWithDataProvider (provider);
|
||||
CGDataProviderRelease (provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely (!data->cg_font)) {
|
||||
if (unlikely (!data)) {
|
||||
DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed");
|
||||
free (data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -126,8 +122,7 @@ _hb_coretext_shaper_face_data_create (hb_face_t *face)
|
||||
void
|
||||
_hb_coretext_shaper_face_data_destroy (hb_coretext_shaper_face_data_t *data)
|
||||
{
|
||||
CFRelease (data->cg_font);
|
||||
free (data);
|
||||
CFRelease (data);
|
||||
}
|
||||
|
||||
CGFontRef
|
||||
@ -135,7 +130,7 @@ hb_coretext_face_get_cg_font (hb_face_t *face)
|
||||
{
|
||||
if (unlikely (!hb_coretext_shaper_face_data_ensure (face))) return NULL;
|
||||
hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
|
||||
return face_data->cg_font;
|
||||
return face_data;
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +154,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
|
||||
hb_face_t *face = font->face;
|
||||
hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
|
||||
|
||||
data->ct_font = CTFontCreateWithGraphicsFont (face_data->cg_font, font->y_scale, NULL, NULL);
|
||||
data->ct_font = CTFontCreateWithGraphicsFont (face_data, font->y_scale, NULL, NULL);
|
||||
if (unlikely (!data->ct_font)) {
|
||||
DEBUG_MSG (CORETEXT, font, "Font CTFontCreateWithGraphicsFont() failed");
|
||||
free (data);
|
||||
@ -332,7 +327,7 @@ struct range_record_t {
|
||||
#define kUpperCaseType 38
|
||||
|
||||
/* Table data courtesy of Apple. */
|
||||
struct feature_mapping_t {
|
||||
static const struct feature_mapping_t {
|
||||
FourCharCode otFeatureTag;
|
||||
uint16_t aatFeatureType;
|
||||
uint16_t selectorToEnable;
|
||||
@ -436,12 +431,29 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
||||
hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
|
||||
hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
|
||||
|
||||
/* Attach marks to their bases, to match the 'ot' shaper.
|
||||
* Adapted from hb-ot-shape:hb_form_clusters().
|
||||
* Note that this only makes us be closer to the 'ot' shaper,
|
||||
* but by no means the same. For example, if there's
|
||||
* B1 M1 B2 M2, and B1-B2 form a ligature, M2's cluster will
|
||||
* continue pointing to B2 even though B2 was merged into B1's
|
||||
* cluster... */
|
||||
{
|
||||
hb_unicode_funcs_t *unicode = buffer->unicode;
|
||||
unsigned int count = buffer->len;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
for (unsigned int i = 1; i < count; i++)
|
||||
if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint)))
|
||||
buffer->merge_clusters (i - 1, i + 1);
|
||||
}
|
||||
|
||||
hb_auto_array_t<feature_record_t> feature_records;
|
||||
hb_auto_array_t<range_record_t> range_records;
|
||||
|
||||
/*
|
||||
* Set up features.
|
||||
* (copied + modified from code from hb-uniscribe.cc)
|
||||
*/
|
||||
hb_auto_array_t<feature_record_t> feature_records;
|
||||
hb_auto_array_t<range_record_t> range_records;
|
||||
if (num_features)
|
||||
{
|
||||
/* Sort features by start/end events. */
|
||||
@ -549,7 +561,6 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
||||
CFRelease (attributes);
|
||||
|
||||
range->font = CTFontCreateCopyWithAttributes (font_data->ct_font, 0.0, NULL, font_desc);
|
||||
|
||||
CFRelease (font_desc);
|
||||
}
|
||||
else
|
||||
@ -584,32 +595,26 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
||||
num_features = 0;
|
||||
}
|
||||
|
||||
#define FAIL(...) \
|
||||
HB_STMT_START { \
|
||||
DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
|
||||
return false; \
|
||||
} HB_STMT_END;
|
||||
|
||||
unsigned int scratch_size;
|
||||
hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
|
||||
|
||||
#define ALLOCATE_ARRAY(Type, name, len) \
|
||||
#define ALLOCATE_ARRAY(Type, name, len, on_no_room) \
|
||||
Type *name = (Type *) scratch; \
|
||||
{ \
|
||||
unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
|
||||
assert (_consumed <= scratch_size); \
|
||||
if (unlikely (_consumed > scratch_size)) \
|
||||
{ \
|
||||
on_no_room; \
|
||||
assert (0); \
|
||||
} \
|
||||
scratch += _consumed; \
|
||||
scratch_size -= _consumed; \
|
||||
}
|
||||
|
||||
#define utf16_index() var1.u32
|
||||
|
||||
ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2);
|
||||
|
||||
ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2, /*nothing*/);
|
||||
unsigned int chars_len = 0;
|
||||
for (unsigned int i = 0; i < buffer->len; i++) {
|
||||
hb_codepoint_t c = buffer->info[i].codepoint;
|
||||
buffer->info[i].utf16_index() = chars_len;
|
||||
if (likely (c <= 0xFFFFu))
|
||||
pchars[chars_len++] = c;
|
||||
else if (unlikely (c > 0x10FFFFu))
|
||||
@ -620,232 +625,439 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
||||
}
|
||||
}
|
||||
|
||||
#undef utf16_index
|
||||
|
||||
CFStringRef string_ref = CFStringCreateWithCharactersNoCopy (NULL,
|
||||
pchars, chars_len,
|
||||
kCFAllocatorNull);
|
||||
|
||||
CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
|
||||
CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
|
||||
kCTFontAttributeName, font_data->ct_font);
|
||||
|
||||
if (num_features)
|
||||
ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, /*nothing*/);
|
||||
chars_len = 0;
|
||||
for (unsigned int i = 0; i < buffer->len; i++)
|
||||
{
|
||||
ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len);
|
||||
|
||||
/* Need log_clusters to assign features. */
|
||||
chars_len = 0;
|
||||
for (unsigned int i = 0; i < buffer->len; i++)
|
||||
{
|
||||
hb_codepoint_t c = buffer->info[i].codepoint;
|
||||
unsigned int cluster = buffer->info[i].cluster;
|
||||
log_clusters[chars_len++] = cluster;
|
||||
if (hb_in_range (c, 0x10000u, 0x10FFFFu))
|
||||
log_clusters[chars_len++] = cluster; /* Surrogates. */
|
||||
}
|
||||
|
||||
unsigned int start = 0;
|
||||
range_record_t *last_range = &range_records[0];
|
||||
for (unsigned int k = 0; k < chars_len; k++)
|
||||
{
|
||||
range_record_t *range = last_range;
|
||||
while (log_clusters[k] < range->index_first)
|
||||
range--;
|
||||
while (log_clusters[k] > range->index_last)
|
||||
range++;
|
||||
if (range != last_range)
|
||||
{
|
||||
if (last_range->font)
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
|
||||
kCTFontAttributeName, last_range->font);
|
||||
|
||||
start = k;
|
||||
}
|
||||
|
||||
last_range = range;
|
||||
}
|
||||
if (start != chars_len && last_range->font)
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start - 1),
|
||||
kCTFontAttributeName, last_range->font);
|
||||
|
||||
for (unsigned int i = 0; i < range_records.len; i++)
|
||||
if (range_records[i].font)
|
||||
CFRelease (range_records[i].font);
|
||||
hb_codepoint_t c = buffer->info[i].codepoint;
|
||||
unsigned int cluster = buffer->info[i].cluster;
|
||||
log_clusters[chars_len++] = cluster;
|
||||
if (hb_in_range (c, 0x10000u, 0x10FFFFu))
|
||||
log_clusters[chars_len++] = cluster; /* Surrogates. */
|
||||
}
|
||||
|
||||
CTLineRef line = CTLineCreateWithAttributedString (attr_string);
|
||||
CFRelease (attr_string);
|
||||
#define FAIL(...) \
|
||||
HB_STMT_START { \
|
||||
DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
|
||||
ret = false; \
|
||||
goto fail; \
|
||||
} HB_STMT_END;
|
||||
|
||||
CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
|
||||
unsigned int num_runs = CFArrayGetCount (glyph_runs);
|
||||
bool ret = true;
|
||||
CFStringRef string_ref = NULL;
|
||||
CTLineRef line = NULL;
|
||||
|
||||
buffer->len = 0;
|
||||
|
||||
const CFRange range_all = CFRangeMake (0, 0);
|
||||
|
||||
for (unsigned int i = 0; i < num_runs; i++)
|
||||
if (0)
|
||||
{
|
||||
CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i);
|
||||
resize_and_retry:
|
||||
DEBUG_MSG (CORETEXT, buffer, "Buffer resize");
|
||||
/* string_ref uses the scratch-buffer for backing store, and line references
|
||||
* string_ref (via attr_string). We must release those before resizing buffer. */
|
||||
assert (string_ref);
|
||||
assert (line);
|
||||
CFRelease (string_ref);
|
||||
CFRelease (line);
|
||||
string_ref = NULL;
|
||||
line = NULL;
|
||||
|
||||
/* CoreText does automatic font fallback (AKA "cascading") for characters
|
||||
* not supported by the requested font, and provides no way to turn it off,
|
||||
* so we detect if the returned run uses a font other than the requested
|
||||
* one and fill in the buffer with .notdef glyphs instead of random glyph
|
||||
* indices from a different font.
|
||||
*/
|
||||
CFDictionaryRef attributes = CTRunGetAttributes (run);
|
||||
CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
|
||||
CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
|
||||
if (!CFEqual (run_cg_font, face_data->cg_font))
|
||||
{
|
||||
CFRelease (run_cg_font);
|
||||
/* Get previous start-of-scratch-area, that we use later for readjusting
|
||||
* our existing scratch arrays. */
|
||||
unsigned int old_scratch_used;
|
||||
hb_buffer_t::scratch_buffer_t *old_scratch;
|
||||
old_scratch = buffer->get_scratch_buffer (&old_scratch_used);
|
||||
old_scratch_used = scratch - old_scratch;
|
||||
|
||||
CFRange range = CTRunGetStringRange (run);
|
||||
buffer->ensure (buffer->len + range.length);
|
||||
if (buffer->in_error)
|
||||
FAIL ("Buffer resize failed");
|
||||
hb_glyph_info_t *info = buffer->info + buffer->len;
|
||||
|
||||
CGGlyph notdef = 0;
|
||||
double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, ¬def, NULL, 1);
|
||||
|
||||
for (CFIndex j = range.location; j < range.location + range.length; j++)
|
||||
{
|
||||
UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
|
||||
if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
|
||||
{
|
||||
ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
|
||||
if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
|
||||
/* This is the second of a surrogate pair. Don't need .notdef
|
||||
* for this one. */
|
||||
continue;
|
||||
}
|
||||
|
||||
info->codepoint = notdef;
|
||||
/* TODO We have to fixup clusters later. See vis_clusters in
|
||||
* hb-uniscribe.cc for example. */
|
||||
info->cluster = j;
|
||||
|
||||
info->mask = advance;
|
||||
info->var1.u32 = 0;
|
||||
info->var2.u32 = 0;
|
||||
|
||||
info++;
|
||||
buffer->len++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
CFRelease (run_cg_font);
|
||||
|
||||
unsigned int num_glyphs = CTRunGetGlyphCount (run);
|
||||
if (num_glyphs == 0)
|
||||
continue;
|
||||
|
||||
buffer->ensure (buffer->len + num_glyphs);
|
||||
if (unlikely (!buffer->ensure (buffer->allocated * 2)))
|
||||
FAIL ("Buffer resize failed");
|
||||
|
||||
/* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the
|
||||
* cleanest way to do without completely restructuring the rest of this shaper. */
|
||||
scratch = buffer->get_scratch_buffer (&scratch_size);
|
||||
pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch)));
|
||||
log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch)));
|
||||
scratch += old_scratch_used;
|
||||
scratch_size -= old_scratch_used;
|
||||
}
|
||||
retry:
|
||||
{
|
||||
string_ref = CFStringCreateWithCharactersNoCopy (NULL,
|
||||
pchars, chars_len,
|
||||
kCFAllocatorNull);
|
||||
if (unlikely (!string_ref))
|
||||
FAIL ("CFStringCreateWithCharactersNoCopy failed");
|
||||
|
||||
/* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always
|
||||
* succeed, and so copying data to our own buffer will be rare. */
|
||||
/* Create an attributed string, populate it, and create a line from it, then release attributed string. */
|
||||
{
|
||||
CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault,
|
||||
chars_len);
|
||||
if (unlikely (!attr_string))
|
||||
FAIL ("CFAttributedStringCreateMutable failed");
|
||||
CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
|
||||
if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
|
||||
{
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
|
||||
kCTVerticalFormsAttributeName, kCFBooleanTrue);
|
||||
}
|
||||
|
||||
const CGGlyph* glyphs = CTRunGetGlyphsPtr (run);
|
||||
if (!glyphs) {
|
||||
ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs);
|
||||
CTRunGetGlyphs (run, range_all, glyph_buf);
|
||||
glyphs = glyph_buf;
|
||||
if (buffer->props.language)
|
||||
{
|
||||
/* What's the iOS equivalent of this check?
|
||||
* The symbols was introduced in iOS 7.0.
|
||||
* At any rate, our fallback is safe and works fine. */
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1090
|
||||
# define kCTLanguageAttributeName CFSTR ("NSLanguage")
|
||||
#endif
|
||||
CFStringRef lang = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault,
|
||||
hb_language_to_string (buffer->props.language),
|
||||
kCFStringEncodingUTF8,
|
||||
kCFAllocatorNull);
|
||||
if (unlikely (!lang))
|
||||
FAIL ("CFStringCreateWithCStringNoCopy failed");
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
|
||||
kCTLanguageAttributeName, lang);
|
||||
CFRelease (lang);
|
||||
}
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
|
||||
kCTFontAttributeName, font_data->ct_font);
|
||||
|
||||
if (num_features)
|
||||
{
|
||||
unsigned int start = 0;
|
||||
range_record_t *last_range = &range_records[0];
|
||||
for (unsigned int k = 0; k < chars_len; k++)
|
||||
{
|
||||
range_record_t *range = last_range;
|
||||
while (log_clusters[k] < range->index_first)
|
||||
range--;
|
||||
while (log_clusters[k] > range->index_last)
|
||||
range++;
|
||||
if (range != last_range)
|
||||
{
|
||||
if (last_range->font)
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start),
|
||||
kCTFontAttributeName, last_range->font);
|
||||
|
||||
start = k;
|
||||
}
|
||||
|
||||
last_range = range;
|
||||
}
|
||||
if (start != chars_len && last_range->font)
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
|
||||
kCTFontAttributeName, last_range->font);
|
||||
}
|
||||
|
||||
int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
|
||||
CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
|
||||
CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
|
||||
(const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
|
||||
(const void **) &level_number,
|
||||
1,
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
if (unlikely (!options))
|
||||
FAIL ("CFDictionaryCreate failed");
|
||||
|
||||
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
|
||||
CFRelease (options);
|
||||
CFRelease (attr_string);
|
||||
if (unlikely (!typesetter))
|
||||
FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
|
||||
|
||||
line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0));
|
||||
CFRelease (typesetter);
|
||||
if (unlikely (!line))
|
||||
FAIL ("CTTypesetterCreateLine failed");
|
||||
}
|
||||
|
||||
const CGPoint* positions = CTRunGetPositionsPtr (run);
|
||||
if (!positions) {
|
||||
ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs);
|
||||
CTRunGetPositions (run, range_all, position_buf);
|
||||
positions = position_buf;
|
||||
}
|
||||
CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
|
||||
unsigned int num_runs = CFArrayGetCount (glyph_runs);
|
||||
DEBUG_MSG (CORETEXT, NULL, "Num runs: %d", num_runs);
|
||||
|
||||
const CFIndex* string_indices = CTRunGetStringIndicesPtr (run);
|
||||
if (!string_indices) {
|
||||
ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs);
|
||||
CTRunGetStringIndices (run, range_all, index_buf);
|
||||
string_indices = index_buf;
|
||||
}
|
||||
buffer->len = 0;
|
||||
uint32_t status_and = ~0, status_or = 0;
|
||||
|
||||
const CFRange range_all = CFRangeMake (0, 0);
|
||||
|
||||
for (unsigned int i = 0; i < num_runs; i++)
|
||||
{
|
||||
CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i));
|
||||
CTRunStatus run_status = CTRunGetStatus (run);
|
||||
status_or |= run_status;
|
||||
status_and &= run_status;
|
||||
DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
|
||||
|
||||
/* CoreText does automatic font fallback (AKA "cascading") for characters
|
||||
* not supported by the requested font, and provides no way to turn it off,
|
||||
* so we must detect if the returned run uses a font other than the requested
|
||||
* one and fill in the buffer with .notdef glyphs instead of random glyph
|
||||
* indices from a different font.
|
||||
*/
|
||||
CFDictionaryRef attributes = CTRunGetAttributes (run);
|
||||
CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
|
||||
if (!CFEqual (run_ct_font, font_data->ct_font))
|
||||
{
|
||||
/* The run doesn't use our main font instance. We have to figure out
|
||||
* whether font fallback happened, or this is just CoreText giving us
|
||||
* another CTFont using the same underlying CGFont. CoreText seems
|
||||
* to do that in a variety of situations, one of which being vertical
|
||||
* text, but also perhaps for caching reasons.
|
||||
*
|
||||
* First, see if it uses any of our subfonts created to set font features...
|
||||
*
|
||||
* Next, compare the CGFont to the one we used to create our fonts.
|
||||
* Even this doesn't work all the time.
|
||||
*
|
||||
* Finally, we compare PS names, which I don't think are unique...
|
||||
*
|
||||
* Looks like if we really want to be sure here we have to modify the
|
||||
* font to change the name table, similar to what we do in the uniscribe
|
||||
* backend.
|
||||
*
|
||||
* However, even that wouldn't work if we were passed in the CGFont to
|
||||
* begin with.
|
||||
*
|
||||
* Webkit uses a slightly different approach: it installs LastResort
|
||||
* as fallback chain, and then checks PS name of used font against
|
||||
* LastResort. That one is safe for any font except for LastResort,
|
||||
* as opposed to ours, which can fail if we are using any uninstalled
|
||||
* font that has the same name as an installed font.
|
||||
*
|
||||
* See: http://github.com/behdad/harfbuzz/pull/36
|
||||
*/
|
||||
bool matched = false;
|
||||
for (unsigned int i = 0; i < range_records.len; i++)
|
||||
if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font))
|
||||
{
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if (!matched)
|
||||
{
|
||||
CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
|
||||
if (run_cg_font)
|
||||
{
|
||||
matched = CFEqual (run_cg_font, face_data);
|
||||
CFRelease (run_cg_font);
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
{
|
||||
CFStringRef font_ps_name = CTFontCopyName (font_data->ct_font, kCTFontPostScriptNameKey);
|
||||
CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey);
|
||||
CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0);
|
||||
CFRelease (run_ps_name);
|
||||
CFRelease (font_ps_name);
|
||||
if (result == kCFCompareEqualTo)
|
||||
matched = true;
|
||||
}
|
||||
if (!matched)
|
||||
{
|
||||
CFRange range = CTRunGetStringRange (run);
|
||||
DEBUG_MSG (CORETEXT, run, "Run used fallback font: %ld..%ld",
|
||||
range.location, range.location + range.length);
|
||||
if (!buffer->ensure_inplace (buffer->len + range.length))
|
||||
goto resize_and_retry;
|
||||
hb_glyph_info_t *info = buffer->info + buffer->len;
|
||||
|
||||
CGGlyph notdef = 0;
|
||||
double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, ¬def, NULL, 1);
|
||||
|
||||
unsigned int old_len = buffer->len;
|
||||
for (CFIndex j = range.location; j < range.location + range.length; j++)
|
||||
{
|
||||
UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
|
||||
if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j)
|
||||
{
|
||||
ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
|
||||
if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu))
|
||||
/* This is the second of a surrogate pair. Don't need .notdef
|
||||
* for this one. */
|
||||
continue;
|
||||
}
|
||||
|
||||
info->codepoint = notdef;
|
||||
info->cluster = log_clusters[j];
|
||||
|
||||
info->mask = advance;
|
||||
info->var1.u32 = 0;
|
||||
info->var2.u32 = 0;
|
||||
|
||||
info++;
|
||||
buffer->len++;
|
||||
}
|
||||
if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
|
||||
buffer->reverse_range (old_len, buffer->len);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int num_glyphs = CTRunGetGlyphCount (run);
|
||||
if (num_glyphs == 0)
|
||||
continue;
|
||||
|
||||
if (!buffer->ensure_inplace (buffer->len + num_glyphs))
|
||||
goto resize_and_retry;
|
||||
|
||||
hb_glyph_info_t *run_info = buffer->info + buffer->len;
|
||||
|
||||
/* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always
|
||||
* succeed, and so copying data to our own buffer will be rare. Reports
|
||||
* have it that this changed in OS X 10.10 Yosemite, and NULL is returned
|
||||
* frequently. At any rate, we can test that codepath by setting USE_PTR
|
||||
* to false. */
|
||||
|
||||
#define USE_PTR true
|
||||
|
||||
#define SCRATCH_SAVE() \
|
||||
unsigned int scratch_size_saved = scratch_size; \
|
||||
hb_buffer_t::scratch_buffer_t *scratch_saved = scratch
|
||||
|
||||
#define SCRATCH_RESTORE() \
|
||||
scratch_size = scratch_size_saved; \
|
||||
scratch = scratch_saved;
|
||||
|
||||
{
|
||||
SCRATCH_SAVE();
|
||||
const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : NULL;
|
||||
if (!glyphs) {
|
||||
ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry);
|
||||
CTRunGetGlyphs (run, range_all, glyph_buf);
|
||||
glyphs = glyph_buf;
|
||||
}
|
||||
const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : NULL;
|
||||
if (!string_indices) {
|
||||
ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry);
|
||||
CTRunGetStringIndices (run, range_all, index_buf);
|
||||
string_indices = index_buf;
|
||||
}
|
||||
hb_glyph_info_t *info = run_info;
|
||||
for (unsigned int j = 0; j < num_glyphs; j++)
|
||||
{
|
||||
info->codepoint = glyphs[j];
|
||||
info->cluster = log_clusters[string_indices[j]];
|
||||
info++;
|
||||
}
|
||||
SCRATCH_RESTORE();
|
||||
}
|
||||
{
|
||||
SCRATCH_SAVE();
|
||||
const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : NULL;
|
||||
if (!positions) {
|
||||
ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry);
|
||||
CTRunGetPositions (run, range_all, position_buf);
|
||||
positions = position_buf;
|
||||
}
|
||||
double run_advance = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
|
||||
DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
|
||||
hb_glyph_info_t *info = run_info;
|
||||
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
|
||||
{
|
||||
for (unsigned int j = 0; j < num_glyphs; j++)
|
||||
{
|
||||
double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_advance) - positions[j].x;
|
||||
info->mask = advance;
|
||||
info->var1.u32 = positions[0].x; /* Yes, zero. */
|
||||
info->var2.u32 = positions[j].y;
|
||||
info++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
run_advance = -run_advance;
|
||||
for (unsigned int j = 0; j < num_glyphs; j++)
|
||||
{
|
||||
double advance = (j + 1 < num_glyphs ? positions[j + 1].y : positions[0].y + run_advance) - positions[j].y;
|
||||
info->mask = advance;
|
||||
info->var1.u32 = positions[j].x;
|
||||
info->var2.u32 = positions[0].y; /* Yes, zero. */
|
||||
info++;
|
||||
}
|
||||
}
|
||||
SCRATCH_RESTORE();
|
||||
}
|
||||
#undef SCRATCH_RESTORE
|
||||
#undef SCRATCH_SAVE
|
||||
#undef USE_PTR
|
||||
#undef ALLOCATE_ARRAY
|
||||
|
||||
double run_width = CTRunGetTypographicBounds (run, range_all, NULL, NULL, NULL);
|
||||
|
||||
for (unsigned int j = 0; j < num_glyphs; j++) {
|
||||
double advance = (j + 1 < num_glyphs ? positions[j + 1].x : positions[0].x + run_width) - positions[j].x;
|
||||
|
||||
hb_glyph_info_t *info = &buffer->info[buffer->len];
|
||||
|
||||
info->codepoint = glyphs[j];
|
||||
info->cluster = string_indices[j];
|
||||
|
||||
/* Currently, we do all x-positioning by setting the advance, we never use x-offset. */
|
||||
info->mask = advance;
|
||||
info->var1.u32 = 0;
|
||||
info->var2.u32 = positions[j].y;
|
||||
|
||||
buffer->len++;
|
||||
buffer->len += num_glyphs;
|
||||
}
|
||||
}
|
||||
|
||||
buffer->clear_positions ();
|
||||
/* Make sure all runs had the expected direction. */
|
||||
bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
|
||||
assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
|
||||
assert (bool (status_or & kCTRunStatusRightToLeft) == backward);
|
||||
|
||||
unsigned int count = buffer->len;
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
hb_glyph_info_t *info = &buffer->info[i];
|
||||
hb_glyph_position_t *pos = &buffer->pos[i];
|
||||
buffer->clear_positions ();
|
||||
|
||||
/* TODO vertical */
|
||||
pos->x_advance = info->mask;
|
||||
pos->x_offset = info->var1.u32;
|
||||
pos->y_offset = info->var2.u32;
|
||||
}
|
||||
|
||||
/* Fix up clusters so that we never return out-of-order indices;
|
||||
* if core text has reordered glyphs, we'll merge them to the
|
||||
* beginning of the reordered cluster.
|
||||
*
|
||||
* This does *not* mean we'll form the same clusters as Uniscribe
|
||||
* or the native OT backend, only that the cluster indices will be
|
||||
* monotonic in the output buffer. */
|
||||
if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
|
||||
unsigned int prev_cluster = 0;
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
unsigned int curr_cluster = buffer->info[i].cluster;
|
||||
if (curr_cluster < prev_cluster) {
|
||||
for (unsigned int j = i; j > 0; j--) {
|
||||
if (buffer->info[j - 1].cluster > curr_cluster)
|
||||
buffer->info[j - 1].cluster = curr_cluster;
|
||||
else
|
||||
break;
|
||||
}
|
||||
unsigned int count = buffer->len;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
hb_glyph_position_t *pos = buffer->pos;
|
||||
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
pos->x_advance = info->mask;
|
||||
pos->x_offset = info->var1.u32;
|
||||
pos->y_offset = info->var2.u32;
|
||||
info++, pos++;
|
||||
}
|
||||
prev_cluster = curr_cluster;
|
||||
}
|
||||
} else {
|
||||
unsigned int prev_cluster = (unsigned int)-1;
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
unsigned int curr_cluster = buffer->info[i].cluster;
|
||||
if (curr_cluster > prev_cluster) {
|
||||
for (unsigned int j = i; j > 0; j--) {
|
||||
if (buffer->info[j - 1].cluster < curr_cluster)
|
||||
buffer->info[j - 1].cluster = curr_cluster;
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
pos->y_advance = info->mask;
|
||||
pos->x_offset = info->var1.u32;
|
||||
pos->y_offset = info->var2.u32;
|
||||
info++, pos++;
|
||||
}
|
||||
|
||||
/* Fix up clusters so that we never return out-of-order indices;
|
||||
* if core text has reordered glyphs, we'll merge them to the
|
||||
* beginning of the reordered cluster. CoreText is nice enough
|
||||
* to tell us whenever it has produced nonmonotonic results...
|
||||
* Note that we assume the input clusters were nonmonotonic to
|
||||
* begin with.
|
||||
*
|
||||
* This does *not* mean we'll form the same clusters as Uniscribe
|
||||
* or the native OT backend, only that the cluster indices will be
|
||||
* monotonic in the output buffer. */
|
||||
if (count > 1 && (status_or & kCTRunStatusNonMonotonic))
|
||||
{
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
|
||||
{
|
||||
unsigned int cluster = info[count - 1].cluster;
|
||||
for (unsigned int i = count - 1; i > 0; i--)
|
||||
{
|
||||
cluster = MIN (cluster, info[i - 1].cluster);
|
||||
info[i - 1].cluster = cluster;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int cluster = info[0].cluster;
|
||||
for (unsigned int i = 1; i < count; i++)
|
||||
{
|
||||
cluster = MIN (cluster, info[i].cluster);
|
||||
info[i].cluster = cluster;
|
||||
}
|
||||
}
|
||||
prev_cluster = curr_cluster;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease (string_ref);
|
||||
CFRelease (line);
|
||||
#undef FAIL
|
||||
|
||||
return true;
|
||||
fail:
|
||||
if (string_ref)
|
||||
CFRelease (string_ref);
|
||||
if (line)
|
||||
CFRelease (line);
|
||||
|
||||
for (unsigned int i = 0; i < range_records.len; i++)
|
||||
if (range_records[i].font)
|
||||
CFRelease (range_records[i].font);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,7 +66,7 @@ struct hb_face_t {
|
||||
{
|
||||
hb_blob_t *blob;
|
||||
|
||||
if (unlikely (!this || !reference_table_func))
|
||||
if (unlikely (!reference_table_func))
|
||||
return hb_blob_get_empty ();
|
||||
|
||||
blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
|
||||
|
@ -136,7 +136,7 @@ hb_ft_get_glyph_v_origin (hb_font_t *font HB_UNUSED,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
FT_Face ft_face = (FT_Face) font_data;
|
||||
int load_flags = FT_LOAD_DEFAULT;
|
||||
int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
|
||||
|
||||
if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
|
||||
return false;
|
||||
@ -185,7 +185,7 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
FT_Face ft_face = (FT_Face) font_data;
|
||||
int load_flags = FT_LOAD_DEFAULT;
|
||||
int load_flags = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
|
||||
|
||||
if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
|
||||
return false;
|
||||
@ -455,7 +455,7 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATEXIT
|
||||
#ifdef HB_USE_ATEXIT
|
||||
atexit (free_ft_library); /* First person registers atexit() callback. */
|
||||
#endif
|
||||
}
|
||||
|
@ -274,8 +274,8 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan,
|
||||
while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
|
||||
DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
|
||||
{
|
||||
buffer->ensure (buffer->allocated * 2);
|
||||
if (unlikely (buffer->in_error)) {
|
||||
if (unlikely (!buffer->ensure (buffer->allocated * 2)))
|
||||
{
|
||||
if (feats) gr_featureval_destroy (feats);
|
||||
gr_seg_destroy (seg);
|
||||
return false;
|
||||
|
@ -46,7 +46,7 @@
|
||||
|
||||
#include <windows.h>
|
||||
typedef CRITICAL_SECTION hb_mutex_impl_t;
|
||||
#define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 }
|
||||
#define HB_MUTEX_IMPL_INIT {0}
|
||||
#define hb_mutex_impl_init(M) InitializeCriticalSection (M)
|
||||
#define hb_mutex_impl_lock(M) EnterCriticalSection (M)
|
||||
#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M)
|
||||
|
@ -68,8 +68,6 @@ struct hb_reference_count_t
|
||||
#define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT}
|
||||
struct hb_user_data_array_t
|
||||
{
|
||||
/* TODO Add tracing. */
|
||||
|
||||
struct hb_user_data_item_t {
|
||||
hb_user_data_key_t *key;
|
||||
void *data;
|
||||
@ -106,69 +104,6 @@ struct hb_object_header_t
|
||||
|
||||
#define HB_OBJECT_HEADER_STATIC {HB_REFERENCE_COUNT_INVALID, HB_USER_DATA_ARRAY_INIT}
|
||||
|
||||
static inline void *create (unsigned int size) {
|
||||
hb_object_header_t *obj = (hb_object_header_t *) calloc (1, size);
|
||||
|
||||
if (likely (obj))
|
||||
obj->init ();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline void init (void) {
|
||||
ref_count.init (1);
|
||||
user_data.init ();
|
||||
}
|
||||
|
||||
inline bool is_inert (void) const {
|
||||
return unlikely (ref_count.is_invalid ());
|
||||
}
|
||||
|
||||
inline void reference (void) {
|
||||
if (unlikely (!this || this->is_inert ()))
|
||||
return;
|
||||
ref_count.inc ();
|
||||
}
|
||||
|
||||
inline bool destroy (void) {
|
||||
if (unlikely (!this || this->is_inert ()))
|
||||
return false;
|
||||
if (ref_count.dec () != 1)
|
||||
return false;
|
||||
|
||||
ref_count.finish (); /* Do this before user_data */
|
||||
user_data.finish ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool set_user_data (hb_user_data_key_t *key,
|
||||
void * data,
|
||||
hb_destroy_func_t destroy_func,
|
||||
hb_bool_t replace) {
|
||||
if (unlikely (!this || this->is_inert ()))
|
||||
return false;
|
||||
|
||||
return user_data.set (key, data, destroy_func, replace);
|
||||
}
|
||||
|
||||
inline void *get_user_data (hb_user_data_key_t *key) {
|
||||
if (unlikely (!this || this->is_inert ()))
|
||||
return NULL;
|
||||
|
||||
return user_data.get (key);
|
||||
}
|
||||
|
||||
inline void trace (const char *function) const {
|
||||
if (unlikely (!this)) return;
|
||||
/* TODO We cannot use DEBUG_MSG_FUNC here since that one currently only
|
||||
* prints the class name and throws away the template info. */
|
||||
DEBUG_MSG (OBJECT, (void *) this,
|
||||
"%s refcount=%d",
|
||||
function,
|
||||
this ? ref_count.ref_count : 0);
|
||||
}
|
||||
|
||||
private:
|
||||
ASSERT_POD ();
|
||||
};
|
||||
@ -179,32 +114,56 @@ struct hb_object_header_t
|
||||
template <typename Type>
|
||||
static inline void hb_object_trace (const Type *obj, const char *function)
|
||||
{
|
||||
obj->header.trace (function);
|
||||
DEBUG_MSG (OBJECT, (void *) obj,
|
||||
"%s refcount=%d",
|
||||
function,
|
||||
obj ? obj->header.ref_count.ref_count : 0);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static inline Type *hb_object_create (void)
|
||||
{
|
||||
Type *obj = (Type *) hb_object_header_t::create (sizeof (Type));
|
||||
Type *obj = (Type *) calloc (1, sizeof (Type));
|
||||
|
||||
if (unlikely (!obj))
|
||||
return obj;
|
||||
|
||||
hb_object_init (obj);
|
||||
hb_object_trace (obj, HB_FUNC);
|
||||
return obj;
|
||||
}
|
||||
template <typename Type>
|
||||
static inline void hb_object_init (Type *obj)
|
||||
{
|
||||
obj->header.ref_count.init (1);
|
||||
obj->header.user_data.init ();
|
||||
}
|
||||
template <typename Type>
|
||||
static inline bool hb_object_is_inert (const Type *obj)
|
||||
{
|
||||
return unlikely (obj->header.is_inert ());
|
||||
return unlikely (obj->header.ref_count.is_invalid ());
|
||||
}
|
||||
template <typename Type>
|
||||
static inline Type *hb_object_reference (Type *obj)
|
||||
{
|
||||
hb_object_trace (obj, HB_FUNC);
|
||||
obj->header.reference ();
|
||||
if (unlikely (!obj || hb_object_is_inert (obj)))
|
||||
return obj;
|
||||
obj->header.ref_count.inc ();
|
||||
return obj;
|
||||
}
|
||||
template <typename Type>
|
||||
static inline bool hb_object_destroy (Type *obj)
|
||||
{
|
||||
hb_object_trace (obj, HB_FUNC);
|
||||
return obj->header.destroy ();
|
||||
if (unlikely (!obj || hb_object_is_inert (obj)))
|
||||
return false;
|
||||
if (obj->header.ref_count.dec () != 1)
|
||||
return false;
|
||||
|
||||
obj->header.ref_count.finish (); /* Do this before user_data */
|
||||
obj->header.user_data.finish ();
|
||||
return true;
|
||||
}
|
||||
template <typename Type>
|
||||
static inline bool hb_object_set_user_data (Type *obj,
|
||||
@ -213,14 +172,18 @@ static inline bool hb_object_set_user_data (Type *obj,
|
||||
hb_destroy_func_t destroy,
|
||||
hb_bool_t replace)
|
||||
{
|
||||
return obj->header.set_user_data (key, data, destroy, replace);
|
||||
if (unlikely (!obj || hb_object_is_inert (obj)))
|
||||
return false;
|
||||
return obj->header.user_data.set (key, data, destroy, replace);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static inline void *hb_object_get_user_data (Type *obj,
|
||||
hb_user_data_key_t *key)
|
||||
{
|
||||
return obj->header.get_user_data (key);
|
||||
if (unlikely (!obj || hb_object_is_inert (obj)))
|
||||
return NULL;
|
||||
return obj->header.user_data.get (key);
|
||||
}
|
||||
|
||||
|
||||
|
@ -197,6 +197,8 @@ struct TTCHeader
|
||||
|
||||
struct OpenTypeFontFile
|
||||
{
|
||||
static const hb_tag_t tableTag = HB_TAG ('_','_','_','_'); /* Sanitizer needs this. */
|
||||
|
||||
static const hb_tag_t CFFTag = HB_TAG ('O','T','T','O'); /* OpenType with Postscript outlines */
|
||||
static const hb_tag_t TrueTypeTag = HB_TAG ( 0 , 1 , 0 , 0 ); /* OpenType with TrueType outlines */
|
||||
static const hb_tag_t TTCTag = HB_TAG ('t','t','c','f'); /* TrueType Collection */
|
||||
|
@ -194,10 +194,11 @@ struct hb_sanitize_context_t
|
||||
{
|
||||
this->start = hb_blob_get_data (this->blob, NULL);
|
||||
this->end = this->start + hb_blob_get_length (this->blob);
|
||||
assert (this->start <= this->end); /* Must not overflow. */
|
||||
this->edit_count = 0;
|
||||
this->debug_depth = 0;
|
||||
|
||||
DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
|
||||
DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
|
||||
"start [%p..%p] (%lu bytes)",
|
||||
this->start, this->end,
|
||||
(unsigned long) (this->end - this->start));
|
||||
@ -205,7 +206,7 @@ struct hb_sanitize_context_t
|
||||
|
||||
inline void end_processing (void)
|
||||
{
|
||||
DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
|
||||
DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1,
|
||||
"end [%p..%p] %u edit requests",
|
||||
this->start, this->end, this->edit_count);
|
||||
|
||||
@ -217,28 +218,31 @@ struct hb_sanitize_context_t
|
||||
inline bool check_range (const void *base, unsigned int len) const
|
||||
{
|
||||
const char *p = (const char *) base;
|
||||
bool ok = this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len;
|
||||
|
||||
hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
|
||||
(&this->debug_depth, "SANITIZE", this->blob, NULL,
|
||||
"check_range [%p..%p] (%d bytes) in [%p..%p]",
|
||||
DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
|
||||
"check_range [%p..%p] (%d bytes) in [%p..%p] -> %s",
|
||||
p, p + len, len,
|
||||
this->start, this->end);
|
||||
this->start, this->end,
|
||||
ok ? "OK" : "OUT-OF-RANGE");
|
||||
|
||||
return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
|
||||
return likely (ok);
|
||||
}
|
||||
|
||||
inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
|
||||
{
|
||||
const char *p = (const char *) base;
|
||||
bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
|
||||
unsigned int array_size = record_size * len;
|
||||
bool ok = !overflows && this->check_range (base, array_size);
|
||||
|
||||
hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
|
||||
(&this->debug_depth, "SANITIZE", this->blob, NULL,
|
||||
"check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
|
||||
p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
|
||||
this->start, this->end);
|
||||
DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
|
||||
"check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s",
|
||||
p, p + (record_size * len), record_size, len, (unsigned int) array_size,
|
||||
this->start, this->end,
|
||||
overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE");
|
||||
|
||||
return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
|
||||
return likely (ok);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
@ -255,15 +259,14 @@ struct hb_sanitize_context_t
|
||||
const char *p = (const char *) base;
|
||||
this->edit_count++;
|
||||
|
||||
hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace
|
||||
(&this->debug_depth, "SANITIZE", this->blob, NULL,
|
||||
DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
|
||||
"may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
|
||||
this->edit_count,
|
||||
p, p + len, len,
|
||||
this->start, this->end,
|
||||
this->writable ? "GRANTED" : "DENIED");
|
||||
|
||||
return TRACE_RETURN (this->writable);
|
||||
return this->writable;
|
||||
}
|
||||
|
||||
template <typename Type, typename ValueType>
|
||||
@ -297,7 +300,7 @@ struct Sanitizer
|
||||
c->init (blob);
|
||||
|
||||
retry:
|
||||
DEBUG_MSG_FUNC (SANITIZE, blob, "start");
|
||||
DEBUG_MSG_FUNC (SANITIZE, c->start, "start");
|
||||
|
||||
c->start_processing ();
|
||||
|
||||
@ -311,13 +314,13 @@ struct Sanitizer
|
||||
sane = t->sanitize (c);
|
||||
if (sane) {
|
||||
if (c->edit_count) {
|
||||
DEBUG_MSG_FUNC (SANITIZE, blob, "passed first round with %d edits; going for second round", c->edit_count);
|
||||
DEBUG_MSG_FUNC (SANITIZE, c->start, "passed first round with %d edits; going for second round", c->edit_count);
|
||||
|
||||
/* sanitize again to ensure no toe-stepping */
|
||||
c->edit_count = 0;
|
||||
sane = t->sanitize (c);
|
||||
if (c->edit_count) {
|
||||
DEBUG_MSG_FUNC (SANITIZE, blob, "requested %d edits in second round; FAILLING", c->edit_count);
|
||||
DEBUG_MSG_FUNC (SANITIZE, c->start, "requested %d edits in second round; FAILLING", c->edit_count);
|
||||
sane = false;
|
||||
}
|
||||
}
|
||||
@ -330,7 +333,7 @@ struct Sanitizer
|
||||
if (c->start) {
|
||||
c->writable = true;
|
||||
/* ok, we made it writable by relocating. try again */
|
||||
DEBUG_MSG_FUNC (SANITIZE, blob, "retry");
|
||||
DEBUG_MSG_FUNC (SANITIZE, c->start, "retry");
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
@ -338,7 +341,7 @@ struct Sanitizer
|
||||
|
||||
c->end_processing ();
|
||||
|
||||
DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
|
||||
DEBUG_MSG_FUNC (SANITIZE, c->start, sane ? "PASSED" : "FAILED");
|
||||
if (sane)
|
||||
return blob;
|
||||
else {
|
||||
@ -533,32 +536,77 @@ template <typename Type>
|
||||
struct BEInt<Type, 2>
|
||||
{
|
||||
public:
|
||||
inline void set (Type i) { hb_be_uint16_put (v,i); }
|
||||
inline operator Type (void) const { return hb_be_uint16_get (v); }
|
||||
inline bool operator == (const BEInt<Type, 2>& o) const { return hb_be_uint16_eq (v, o.v); }
|
||||
inline void set (Type V)
|
||||
{
|
||||
v[0] = (V >> 8) & 0xFF;
|
||||
v[1] = (V ) & 0xFF;
|
||||
}
|
||||
inline operator Type (void) const
|
||||
{
|
||||
return (v[0] << 8)
|
||||
+ (v[1] );
|
||||
}
|
||||
inline bool operator == (const BEInt<Type, 2>& o) const
|
||||
{
|
||||
return v[0] == o.v[0]
|
||||
&& v[1] == o.v[1];
|
||||
}
|
||||
inline bool operator != (const BEInt<Type, 2>& o) const { return !(*this == o); }
|
||||
private: uint8_t v[2];
|
||||
};
|
||||
template <typename Type>
|
||||
struct BEInt<Type, 4>
|
||||
{
|
||||
public:
|
||||
inline void set (Type i) { hb_be_uint32_put (v,i); }
|
||||
inline operator Type (void) const { return hb_be_uint32_get (v); }
|
||||
inline bool operator == (const BEInt<Type, 4>& o) const { return hb_be_uint32_eq (v, o.v); }
|
||||
inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
|
||||
private: uint8_t v[4];
|
||||
};
|
||||
template <typename Type>
|
||||
struct BEInt<Type, 3>
|
||||
{
|
||||
public:
|
||||
inline void set (Type i) { hb_be_uint24_put (v,i); }
|
||||
inline operator Type (void) const { return hb_be_uint24_get (v); }
|
||||
inline bool operator == (const BEInt<Type, 3>& o) const { return hb_be_uint24_eq (v, o.v); }
|
||||
inline void set (Type V)
|
||||
{
|
||||
v[0] = (V >> 16) & 0xFF;
|
||||
v[1] = (V >> 8) & 0xFF;
|
||||
v[2] = (V ) & 0xFF;
|
||||
}
|
||||
inline operator Type (void) const
|
||||
{
|
||||
return (v[0] << 16)
|
||||
+ (v[1] << 8)
|
||||
+ (v[2] );
|
||||
}
|
||||
inline bool operator == (const BEInt<Type, 3>& o) const
|
||||
{
|
||||
return v[0] == o.v[0]
|
||||
&& v[1] == o.v[1]
|
||||
&& v[2] == o.v[2];
|
||||
}
|
||||
inline bool operator != (const BEInt<Type, 3>& o) const { return !(*this == o); }
|
||||
private: uint8_t v[3];
|
||||
};
|
||||
template <typename Type>
|
||||
struct BEInt<Type, 4>
|
||||
{
|
||||
public:
|
||||
inline void set (Type V)
|
||||
{
|
||||
v[0] = (V >> 24) & 0xFF;
|
||||
v[1] = (V >> 16) & 0xFF;
|
||||
v[2] = (V >> 8) & 0xFF;
|
||||
v[3] = (V ) & 0xFF;
|
||||
}
|
||||
inline operator Type (void) const
|
||||
{
|
||||
return (v[0] << 24)
|
||||
+ (v[1] << 16)
|
||||
+ (v[2] << 8)
|
||||
+ (v[3] );
|
||||
}
|
||||
inline bool operator == (const BEInt<Type, 4>& o) const
|
||||
{
|
||||
return v[0] == o.v[0]
|
||||
&& v[1] == o.v[1]
|
||||
&& v[2] == o.v[2]
|
||||
&& v[3] == o.v[3];
|
||||
}
|
||||
inline bool operator != (const BEInt<Type, 4>& o) const { return !(*this == o); }
|
||||
private: uint8_t v[4];
|
||||
};
|
||||
|
||||
/* Integer types in big-endian order and no alignment requirement */
|
||||
template <typename Type, unsigned int Size>
|
||||
|
@ -35,17 +35,128 @@
|
||||
#include "hb-ot-hmtx-table.hh"
|
||||
|
||||
|
||||
struct hb_ot_face_metrics_accelerator_t
|
||||
{
|
||||
unsigned int num_metrics;
|
||||
unsigned int num_advances;
|
||||
unsigned int default_advance;
|
||||
const OT::_mtx *table;
|
||||
hb_blob_t *blob;
|
||||
|
||||
inline void init (hb_face_t *face,
|
||||
hb_tag_t _hea_tag, hb_tag_t _mtx_tag,
|
||||
unsigned int default_advance)
|
||||
{
|
||||
this->default_advance = default_advance;
|
||||
this->num_metrics = face->get_num_glyphs ();
|
||||
|
||||
hb_blob_t *_hea_blob = OT::Sanitizer<OT::_hea>::sanitize (face->reference_table (_hea_tag));
|
||||
const OT::_hea *_hea = OT::Sanitizer<OT::_hea>::lock_instance (_hea_blob);
|
||||
this->num_advances = _hea->numberOfLongMetrics;
|
||||
hb_blob_destroy (_hea_blob);
|
||||
|
||||
this->blob = OT::Sanitizer<OT::_mtx>::sanitize (face->reference_table (_mtx_tag));
|
||||
if (unlikely (!this->num_advances ||
|
||||
2 * (this->num_advances + this->num_metrics) < hb_blob_get_length (this->blob)))
|
||||
{
|
||||
this->num_metrics = this->num_advances = 0;
|
||||
hb_blob_destroy (this->blob);
|
||||
this->blob = hb_blob_get_empty ();
|
||||
}
|
||||
this->table = OT::Sanitizer<OT::_mtx>::lock_instance (this->blob);
|
||||
}
|
||||
|
||||
inline void fini (void)
|
||||
{
|
||||
hb_blob_destroy (this->blob);
|
||||
}
|
||||
|
||||
inline unsigned int get_advance (hb_codepoint_t glyph) const
|
||||
{
|
||||
if (unlikely (glyph >= this->num_metrics))
|
||||
{
|
||||
/* If this->num_metrics is zero, it means we don't have the metrics table
|
||||
* for this direction: return one EM. Otherwise, it means that the glyph
|
||||
* index is out of bound: return zero. */
|
||||
if (this->num_metrics)
|
||||
return 0;
|
||||
else
|
||||
return this->default_advance;
|
||||
}
|
||||
|
||||
if (glyph >= this->num_advances)
|
||||
glyph = this->num_advances - 1;
|
||||
|
||||
return this->table->longMetric[glyph].advance;
|
||||
}
|
||||
};
|
||||
|
||||
struct hb_ot_face_cmap_accelerator_t
|
||||
{
|
||||
const OT::CmapSubtable *table;
|
||||
const OT::CmapSubtable *uvs_table;
|
||||
hb_blob_t *blob;
|
||||
|
||||
inline void init (hb_face_t *face)
|
||||
{
|
||||
this->blob = OT::Sanitizer<OT::cmap>::sanitize (face->reference_table (HB_OT_TAG_cmap));
|
||||
const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (this->blob);
|
||||
const OT::CmapSubtable *subtable = NULL;
|
||||
const OT::CmapSubtable *subtable_uvs = NULL;
|
||||
|
||||
/* 32-bit subtables. */
|
||||
if (!subtable) subtable = cmap->find_subtable (3, 10);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 6);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 4);
|
||||
/* 16-bit subtables. */
|
||||
if (!subtable) subtable = cmap->find_subtable (3, 1);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 3);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 2);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 1);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 0);
|
||||
/* Meh. */
|
||||
if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
|
||||
|
||||
/* UVS subtable. */
|
||||
if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5);
|
||||
/* Meh. */
|
||||
if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable);
|
||||
|
||||
this->table = subtable;
|
||||
this->uvs_table = subtable_uvs;
|
||||
}
|
||||
|
||||
inline void fini (void)
|
||||
{
|
||||
hb_blob_destroy (this->blob);
|
||||
}
|
||||
|
||||
inline bool get_glyph (hb_codepoint_t unicode,
|
||||
hb_codepoint_t variation_selector,
|
||||
hb_codepoint_t *glyph) const
|
||||
{
|
||||
if (unlikely (variation_selector))
|
||||
{
|
||||
switch (this->uvs_table->get_glyph_variant (unicode,
|
||||
variation_selector,
|
||||
glyph))
|
||||
{
|
||||
case OT::GLYPH_VARIANT_NOT_FOUND: return false;
|
||||
case OT::GLYPH_VARIANT_FOUND: return true;
|
||||
case OT::GLYPH_VARIANT_USE_DEFAULT: break;
|
||||
}
|
||||
}
|
||||
|
||||
return this->table->get_glyph (unicode, glyph);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct hb_ot_font_t
|
||||
{
|
||||
unsigned int num_glyphs;
|
||||
unsigned int num_hmetrics;
|
||||
const OT::hmtx *hmtx;
|
||||
hb_blob_t *hmtx_blob;
|
||||
|
||||
const OT::CmapSubtable *cmap;
|
||||
const OT::CmapSubtable *cmap_uvs;
|
||||
hb_blob_t *cmap_blob;
|
||||
hb_ot_face_cmap_accelerator_t cmap;
|
||||
hb_ot_face_metrics_accelerator_t h_metrics;
|
||||
hb_ot_face_metrics_accelerator_t v_metrics;
|
||||
};
|
||||
|
||||
|
||||
@ -53,50 +164,16 @@ static hb_ot_font_t *
|
||||
_hb_ot_font_create (hb_font_t *font)
|
||||
{
|
||||
hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t));
|
||||
hb_face_t *face = font->face;
|
||||
|
||||
if (unlikely (!ot_font))
|
||||
return NULL;
|
||||
|
||||
ot_font->num_glyphs = font->face->get_num_glyphs ();
|
||||
unsigned int upem = face->get_upem ();
|
||||
|
||||
{
|
||||
hb_blob_t *hhea_blob = OT::Sanitizer<OT::hhea>::sanitize (font->face->reference_table (HB_OT_TAG_hhea));
|
||||
const OT::hhea *hhea = OT::Sanitizer<OT::hhea>::lock_instance (hhea_blob);
|
||||
ot_font->num_hmetrics = hhea->numberOfHMetrics;
|
||||
hb_blob_destroy (hhea_blob);
|
||||
}
|
||||
ot_font->hmtx_blob = OT::Sanitizer<OT::hmtx>::sanitize (font->face->reference_table (HB_OT_TAG_hmtx));
|
||||
if (unlikely (!ot_font->num_hmetrics ||
|
||||
2 * (ot_font->num_hmetrics + ot_font->num_glyphs) < hb_blob_get_length (ot_font->hmtx_blob)))
|
||||
{
|
||||
hb_blob_destroy (ot_font->hmtx_blob);
|
||||
free (ot_font);
|
||||
return NULL;
|
||||
}
|
||||
ot_font->hmtx = OT::Sanitizer<OT::hmtx>::lock_instance (ot_font->hmtx_blob);
|
||||
|
||||
ot_font->cmap_blob = OT::Sanitizer<OT::cmap>::sanitize (font->face->reference_table (HB_OT_TAG_cmap));
|
||||
const OT::cmap *cmap = OT::Sanitizer<OT::cmap>::lock_instance (ot_font->cmap_blob);
|
||||
const OT::CmapSubtable *subtable = NULL;
|
||||
const OT::CmapSubtable *subtable_uvs = NULL;
|
||||
|
||||
/* 32-bit subtables. */
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 6);
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 4);
|
||||
if (!subtable) subtable = cmap->find_subtable (3, 10);
|
||||
/* 16-bit subtables. */
|
||||
if (!subtable) subtable = cmap->find_subtable (0, 3);
|
||||
if (!subtable) subtable = cmap->find_subtable (3, 1);
|
||||
/* Meh. */
|
||||
if (!subtable) subtable = &OT::Null(OT::CmapSubtable);
|
||||
|
||||
/* UVS subtable. */
|
||||
if (!subtable_uvs) subtable_uvs = cmap->find_subtable (0, 5);
|
||||
/* Meh. */
|
||||
if (!subtable_uvs) subtable_uvs = &OT::Null(OT::CmapSubtable);
|
||||
|
||||
ot_font->cmap = subtable;
|
||||
ot_font->cmap_uvs = subtable_uvs;
|
||||
ot_font->cmap.init (face);
|
||||
ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1);
|
||||
ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, upem); /* TODO Can we do this lazily? */
|
||||
|
||||
return ot_font;
|
||||
}
|
||||
@ -104,8 +181,9 @@ _hb_ot_font_create (hb_font_t *font)
|
||||
static void
|
||||
_hb_ot_font_destroy (hb_ot_font_t *ot_font)
|
||||
{
|
||||
hb_blob_destroy (ot_font->cmap_blob);
|
||||
hb_blob_destroy (ot_font->hmtx_blob);
|
||||
ot_font->cmap.fini ();
|
||||
ot_font->h_metrics.fini ();
|
||||
ot_font->v_metrics.fini ();
|
||||
|
||||
free (ot_font);
|
||||
}
|
||||
@ -121,20 +199,7 @@ hb_ot_get_glyph (hb_font_t *font HB_UNUSED,
|
||||
|
||||
{
|
||||
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
|
||||
|
||||
if (unlikely (variation_selector))
|
||||
{
|
||||
switch (ot_font->cmap_uvs->get_glyph_variant (unicode,
|
||||
variation_selector,
|
||||
glyph))
|
||||
{
|
||||
case OT::GLYPH_VARIANT_NOT_FOUND: return false;
|
||||
case OT::GLYPH_VARIANT_FOUND: return true;
|
||||
case OT::GLYPH_VARIANT_USE_DEFAULT: break;
|
||||
}
|
||||
}
|
||||
|
||||
return ot_font->cmap->get_glyph (unicode, glyph);
|
||||
return ot_font->cmap.get_glyph (unicode, variation_selector, glyph);
|
||||
}
|
||||
|
||||
static hb_position_t
|
||||
@ -144,14 +209,7 @@ hb_ot_get_glyph_h_advance (hb_font_t *font HB_UNUSED,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
|
||||
|
||||
if (unlikely (glyph >= ot_font->num_glyphs))
|
||||
return 0; /* Maybe better to return notdef's advance instead? */
|
||||
|
||||
if (glyph >= ot_font->num_hmetrics)
|
||||
glyph = ot_font->num_hmetrics - 1;
|
||||
|
||||
return font->em_scale_x (ot_font->hmtx->longHorMetric[glyph].advanceWidth);
|
||||
return font->em_scale_x (ot_font->h_metrics.get_advance (glyph));
|
||||
}
|
||||
|
||||
static hb_position_t
|
||||
@ -160,8 +218,8 @@ hb_ot_get_glyph_v_advance (hb_font_t *font HB_UNUSED,
|
||||
hb_codepoint_t glyph,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
|
||||
return font->em_scale_y (-ot_font->v_metrics.get_advance (glyph));
|
||||
}
|
||||
|
||||
static hb_bool_t
|
||||
@ -206,6 +264,7 @@ hb_ot_get_glyph_v_kerning (hb_font_t *font HB_UNUSED,
|
||||
hb_codepoint_t bottom_glyph HB_UNUSED,
|
||||
void *user_data HB_UNUSED)
|
||||
{
|
||||
/* OpenType doesn't have vertical-kerning other than GPOS. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -35,14 +35,19 @@ namespace OT {
|
||||
|
||||
/*
|
||||
* hhea -- The Horizontal Header Table
|
||||
* vhea -- The Vertical Header Table
|
||||
*/
|
||||
|
||||
#define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
|
||||
#define HB_OT_TAG_vhea HB_TAG('v','h','e','a')
|
||||
|
||||
|
||||
struct hhea
|
||||
struct _hea
|
||||
{
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_hhea;
|
||||
static const hb_tag_t tableTag = HB_TAG('_','h','e','a');
|
||||
|
||||
static const hb_tag_t hheaTag = HB_OT_TAG_hhea;
|
||||
static const hb_tag_t vheaTag = HB_OT_TAG_vhea;
|
||||
|
||||
inline bool sanitize (hb_sanitize_context_t *c) {
|
||||
TRACE_SANITIZE (this);
|
||||
@ -51,45 +56,45 @@ struct hhea
|
||||
|
||||
public:
|
||||
FixedVersion version; /* 0x00010000u for version 1.0. */
|
||||
FWORD ascender; /* Typographic ascent. <a
|
||||
* href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
|
||||
* (Distance from baseline of highest
|
||||
* ascender)</a> */
|
||||
FWORD descender; /* Typographic descent. <a
|
||||
* href="http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html">
|
||||
* (Distance from baseline of lowest
|
||||
* descender)</a> */
|
||||
FWORD lineGap; /* Typographic line gap. Negative
|
||||
* LineGap values are treated as zero
|
||||
* in Windows 3.1, System 6, and
|
||||
* System 7. */
|
||||
UFWORD advanceWidthMax; /* Maximum advance width value in
|
||||
* 'hmtx' table. */
|
||||
FWORD minLeftSideBearing; /* Minimum left sidebearing value in
|
||||
* 'hmtx' table. */
|
||||
FWORD minRightSideBearing; /* Minimum right sidebearing value;
|
||||
FWORD ascender; /* Typographic ascent. */
|
||||
FWORD descender; /* Typographic descent. */
|
||||
FWORD lineGap; /* Typographic line gap. */
|
||||
UFWORD advanceMax; /* Maximum advance width/height value in
|
||||
* metrics table. */
|
||||
FWORD minLeadingBearing; /* Minimum left/top sidebearing value in
|
||||
* metrics table. */
|
||||
FWORD minTrailingBearing; /* Minimum right/bottom sidebearing value;
|
||||
* calculated as Min(aw - lsb -
|
||||
* (xMax - xMin)). */
|
||||
FWORD xMaxExtent; /* Max(lsb + (xMax - xMin)). */
|
||||
* (xMax - xMin)) for horizontal. */
|
||||
FWORD maxExtent; /* horizontal: Max(lsb + (xMax - xMin)),
|
||||
* vertical: minLeadingBearing+(yMax-yMin). */
|
||||
SHORT caretSlopeRise; /* Used to calculate the slope of the
|
||||
* cursor (rise/run); 1 for vertical. */
|
||||
SHORT caretSlopeRun; /* 0 for vertical. */
|
||||
* cursor (rise/run); 1 for vertical caret,
|
||||
* 0 for horizontal.*/
|
||||
SHORT caretSlopeRun; /* 0 for vertical caret, 1 for horizontal. */
|
||||
SHORT caretOffset; /* The amount by which a slanted
|
||||
* highlight on a glyph needs
|
||||
* to be shifted to produce the
|
||||
* best appearance. Set to 0 for
|
||||
* non--slanted fonts */
|
||||
SHORT reserved1; /* set to 0 */
|
||||
SHORT reserved2; /* set to 0 */
|
||||
SHORT reserved3; /* set to 0 */
|
||||
SHORT reserved4; /* set to 0 */
|
||||
* non-slanted fonts. */
|
||||
SHORT reserved1; /* Set to 0. */
|
||||
SHORT reserved2; /* Set to 0. */
|
||||
SHORT reserved3; /* Set to 0. */
|
||||
SHORT reserved4; /* Set to 0. */
|
||||
SHORT metricDataFormat; /* 0 for current format. */
|
||||
USHORT numberOfHMetrics; /* Number of hMetric entries in 'hmtx'
|
||||
* table */
|
||||
USHORT numberOfLongMetrics; /* Number of LongMetric entries in metric
|
||||
* table. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (36);
|
||||
};
|
||||
|
||||
struct hhea : _hea {
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_hhea;
|
||||
};
|
||||
struct vhea : _hea {
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_vhea;
|
||||
};
|
||||
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
@ -35,22 +35,27 @@ namespace OT {
|
||||
|
||||
/*
|
||||
* hmtx -- The Horizontal Metrics Table
|
||||
* vmtx -- The Vertical Metrics Table
|
||||
*/
|
||||
|
||||
#define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
|
||||
#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
|
||||
|
||||
|
||||
struct LongHorMetric
|
||||
struct LongMetric
|
||||
{
|
||||
USHORT advanceWidth;
|
||||
SHORT lsb;
|
||||
USHORT advance; /* Advance width/height. */
|
||||
SHORT lsb; /* Leading (left/top) side bearing. */
|
||||
public:
|
||||
DEFINE_SIZE_STATIC (4);
|
||||
};
|
||||
|
||||
struct hmtx
|
||||
struct _mtx
|
||||
{
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_hmtx;
|
||||
static const hb_tag_t tableTag = HB_TAG('_','m','t','x');
|
||||
|
||||
static const hb_tag_t hmtxTag = HB_OT_TAG_hmtx;
|
||||
static const hb_tag_t vmtxTag = HB_OT_TAG_vmtx;
|
||||
|
||||
inline bool sanitize (hb_sanitize_context_t *c) {
|
||||
TRACE_SANITIZE (this);
|
||||
@ -60,7 +65,7 @@ struct hmtx
|
||||
}
|
||||
|
||||
public:
|
||||
LongHorMetric longHorMetric[VAR]; /* Paired advance width and left side
|
||||
LongMetric longMetric[VAR]; /* Paired advance width and leading
|
||||
* bearing values for each glyph. The
|
||||
* value numOfHMetrics comes from
|
||||
* the 'hhea' table. If the font is
|
||||
@ -68,23 +73,29 @@ struct hmtx
|
||||
* be in the array, but that entry is
|
||||
* required. The last entry applies to
|
||||
* all subsequent glyphs. */
|
||||
SHORT leftSideBearingX[VAR]; /* Here the advanceWidth is assumed
|
||||
* to be the same as the advanceWidth
|
||||
SHORT leadingBearingX[VAR]; /* Here the advance is assumed
|
||||
* to be the same as the advance
|
||||
* for the last entry above. The
|
||||
* number of entries in this array is
|
||||
* derived from numGlyphs (from 'maxp'
|
||||
* table) minus numberOfHMetrics. This
|
||||
* generally is used with a run of
|
||||
* monospaced glyphs (e.g., Kanji
|
||||
* table) minus numberOfLongMetrics.
|
||||
* This generally is used with a run
|
||||
* of monospaced glyphs (e.g., Kanji
|
||||
* fonts or Courier fonts). Only one
|
||||
* run is allowed and it must be at
|
||||
* the end. This allows a monospaced
|
||||
* font to vary the left side bearing
|
||||
* font to vary the side bearing
|
||||
* values for each glyph. */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX);
|
||||
DEFINE_SIZE_ARRAY2 (0, longMetric, leadingBearingX);
|
||||
};
|
||||
|
||||
struct hmtx : _mtx {
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_hmtx;
|
||||
};
|
||||
struct vmtx : _mtx {
|
||||
static const hb_tag_t tableTag = HB_OT_TAG_vmtx;
|
||||
};
|
||||
|
||||
} /* namespace OT */
|
||||
|
||||
|
@ -345,8 +345,8 @@ struct AnchorMatrix
|
||||
inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
|
||||
*found = false;
|
||||
if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
|
||||
*found = !matrix[row * cols + col].is_null ();
|
||||
return this+matrix[row * cols + col];
|
||||
*found = !matrixZ[row * cols + col].is_null ();
|
||||
return this+matrixZ[row * cols + col];
|
||||
}
|
||||
|
||||
inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
|
||||
@ -354,19 +354,19 @@ struct AnchorMatrix
|
||||
if (!c->check_struct (this)) return TRACE_RETURN (false);
|
||||
if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
|
||||
unsigned int count = rows * cols;
|
||||
if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
|
||||
if (!c->check_array (matrixZ, matrixZ[0].static_size, count)) return TRACE_RETURN (false);
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
|
||||
if (!matrixZ[i].sanitize (c, this)) return TRACE_RETURN (false);
|
||||
return TRACE_RETURN (true);
|
||||
}
|
||||
|
||||
USHORT rows; /* Number of rows */
|
||||
protected:
|
||||
OffsetTo<Anchor>
|
||||
matrix[VAR]; /* Matrix of offsets to Anchor tables--
|
||||
matrixZ[VAR]; /* Matrix of offsets to Anchor tables--
|
||||
* from beginning of AnchorMatrix table */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2, matrix);
|
||||
DEFINE_SIZE_ARRAY (2, matrixZ);
|
||||
};
|
||||
|
||||
|
||||
@ -530,7 +530,7 @@ struct SinglePos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
case 2: return TRACE_RETURN (c->dispatch (u.format2));
|
||||
@ -583,7 +583,7 @@ struct PairSet
|
||||
unsigned int len2 = valueFormats[1].get_len ();
|
||||
unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
|
||||
|
||||
const PairValueRecord *record = CastP<PairValueRecord> (array);
|
||||
const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
|
||||
unsigned int count = len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
@ -602,7 +602,7 @@ struct PairSet
|
||||
unsigned int len2 = valueFormats[1].get_len ();
|
||||
unsigned int record_size = USHORT::static_size * (1 + len1 + len2);
|
||||
|
||||
const PairValueRecord *record = CastP<PairValueRecord> (array);
|
||||
const PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
|
||||
unsigned int count = len;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
@ -634,20 +634,20 @@ struct PairSet
|
||||
inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
|
||||
TRACE_SANITIZE (this);
|
||||
if (!(c->check_struct (this)
|
||||
&& c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
|
||||
&& c->check_array (arrayZ, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
|
||||
|
||||
unsigned int count = len;
|
||||
PairValueRecord *record = CastP<PairValueRecord> (array);
|
||||
PairValueRecord *record = CastP<PairValueRecord> (arrayZ);
|
||||
return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
|
||||
&& closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
|
||||
}
|
||||
|
||||
protected:
|
||||
USHORT len; /* Number of PairValueRecords */
|
||||
USHORT array[VAR]; /* Array of PairValueRecords--ordered
|
||||
USHORT arrayZ[VAR]; /* Array of PairValueRecords--ordered
|
||||
* by GlyphID of the second glyph */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY (2, array);
|
||||
DEFINE_SIZE_ARRAY (2, arrayZ);
|
||||
};
|
||||
|
||||
struct PairPosFormat1
|
||||
@ -822,7 +822,7 @@ struct PairPos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
case 2: return TRACE_RETURN (c->dispatch (u.format2));
|
||||
@ -989,7 +989,7 @@ struct CursivePos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1088,7 +1088,7 @@ struct MarkBasePos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1209,7 +1209,7 @@ struct MarkLigPos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1328,7 +1328,7 @@ struct MarkMarkPos
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1387,7 +1387,7 @@ struct PosLookupSubTable
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
switch (lookup_type) {
|
||||
case Single: return TRACE_RETURN (u.single.dispatch (c));
|
||||
case Pair: return TRACE_RETURN (u.pair.dispatch (c));
|
||||
@ -1488,8 +1488,8 @@ struct PosLookup : Lookup
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
unsigned int lookup_type = get_type ();
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
unsigned int count = get_subtable_count ();
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
|
||||
|
@ -200,7 +200,7 @@ struct SingleSubst
|
||||
TRACE_SERIALIZE (this);
|
||||
if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
|
||||
unsigned int format = 2;
|
||||
int delta;
|
||||
int delta = 0;
|
||||
if (num_glyphs) {
|
||||
format = 1;
|
||||
/* TODO(serialize) check for wrap-around */
|
||||
@ -222,7 +222,7 @@ struct SingleSubst
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
case 2: return TRACE_RETURN (c->dispatch (u.format2));
|
||||
@ -422,7 +422,7 @@ struct MultipleSubst
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -573,7 +573,7 @@ struct AlternateSubst
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -889,7 +889,7 @@ struct LigatureSubst
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1053,7 +1053,7 @@ struct ReverseChainSingleSubst
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
default:return TRACE_RETURN (c->default_return_value ());
|
||||
@ -1100,7 +1100,7 @@ struct SubstLookupSubTable
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
switch (lookup_type) {
|
||||
case Single: return TRACE_RETURN (u.single.dispatch (c));
|
||||
case Multiple: return TRACE_RETURN (u.multiple.dispatch (c));
|
||||
@ -1275,8 +1275,8 @@ struct SubstLookup : Lookup
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
unsigned int lookup_type = get_type ();
|
||||
TRACE_DISPATCH (this, lookup_type);
|
||||
unsigned int count = get_subtable_count ();
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
|
||||
|
@ -38,10 +38,10 @@ namespace OT {
|
||||
|
||||
|
||||
|
||||
#define TRACE_DISPATCH(this) \
|
||||
#define TRACE_DISPATCH(this, format) \
|
||||
hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
|
||||
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
|
||||
"");
|
||||
"format %d", (int) format);
|
||||
|
||||
#ifndef HB_DEBUG_CLOSURE
|
||||
#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
|
||||
@ -168,6 +168,10 @@ struct hb_collect_glyphs_context_t
|
||||
if (output == hb_set_get_empty ())
|
||||
return HB_VOID;
|
||||
|
||||
/* Return if new lookup was recursed to before. */
|
||||
if (recursed_lookups.has (lookup_index))
|
||||
return HB_VOID;
|
||||
|
||||
hb_set_t *old_before = before;
|
||||
hb_set_t *old_input = input;
|
||||
hb_set_t *old_after = after;
|
||||
@ -181,6 +185,8 @@ struct hb_collect_glyphs_context_t
|
||||
input = old_input;
|
||||
after = old_after;
|
||||
|
||||
recursed_lookups.add (lookup_index);
|
||||
|
||||
return HB_VOID;
|
||||
}
|
||||
|
||||
@ -190,6 +196,7 @@ struct hb_collect_glyphs_context_t
|
||||
hb_set_t *after;
|
||||
hb_set_t *output;
|
||||
recurse_func_t recurse_func;
|
||||
hb_set_t recursed_lookups;
|
||||
unsigned int nesting_level_left;
|
||||
unsigned int debug_depth;
|
||||
|
||||
@ -205,18 +212,30 @@ struct hb_collect_glyphs_context_t
|
||||
after (glyphs_after ? glyphs_after : hb_set_get_empty ()),
|
||||
output (glyphs_output ? glyphs_output : hb_set_get_empty ()),
|
||||
recurse_func (NULL),
|
||||
recursed_lookups (),
|
||||
nesting_level_left (nesting_level_left_),
|
||||
debug_depth (0) {}
|
||||
debug_depth (0)
|
||||
{
|
||||
recursed_lookups.init ();
|
||||
}
|
||||
~hb_collect_glyphs_context_t (void)
|
||||
{
|
||||
recursed_lookups.fini ();
|
||||
}
|
||||
|
||||
void set_recurse_func (recurse_func_t func) { recurse_func = func; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifndef HB_DEBUG_GET_COVERAGE
|
||||
#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
|
||||
#endif
|
||||
|
||||
struct hb_get_coverage_context_t
|
||||
{
|
||||
inline const char *get_name (void) { return "GET_COVERAGE"; }
|
||||
static const unsigned int max_debug_depth = 0;
|
||||
static const unsigned int max_debug_depth = HB_DEBUG_GET_COVERAGE;
|
||||
typedef const Coverage &return_t;
|
||||
template <typename T>
|
||||
inline return_t dispatch (const T &obj) { return obj.get_coverage (); }
|
||||
@ -1117,9 +1136,9 @@ struct Rule
|
||||
inline void closure (hb_closure_context_t *c, ContextClosureLookupContext &lookup_context) const
|
||||
{
|
||||
TRACE_CLOSURE (this);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
context_closure_lookup (c,
|
||||
inputCount, input,
|
||||
inputCount, inputZ,
|
||||
lookupCount, lookupRecord,
|
||||
lookup_context);
|
||||
}
|
||||
@ -1127,9 +1146,9 @@ struct Rule
|
||||
inline void collect_glyphs (hb_collect_glyphs_context_t *c, ContextCollectGlyphsLookupContext &lookup_context) const
|
||||
{
|
||||
TRACE_COLLECT_GLYPHS (this);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
context_collect_glyphs_lookup (c,
|
||||
inputCount, input,
|
||||
inputCount, inputZ,
|
||||
lookupCount, lookupRecord,
|
||||
lookup_context);
|
||||
}
|
||||
@ -1137,15 +1156,15 @@ struct Rule
|
||||
inline bool would_apply (hb_would_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
|
||||
{
|
||||
TRACE_WOULD_APPLY (this);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
return TRACE_RETURN (context_would_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
return TRACE_RETURN (context_would_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
|
||||
}
|
||||
|
||||
inline bool apply (hb_apply_context_t *c, ContextApplyLookupContext &lookup_context) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (inputZ, inputZ[0].static_size * (inputCount ? inputCount - 1 : 0));
|
||||
return TRACE_RETURN (context_apply_lookup (c, inputCount, inputZ, lookupCount, lookupRecord, lookup_context));
|
||||
}
|
||||
|
||||
public:
|
||||
@ -1153,8 +1172,8 @@ struct Rule
|
||||
TRACE_SANITIZE (this);
|
||||
return inputCount.sanitize (c)
|
||||
&& lookupCount.sanitize (c)
|
||||
&& c->check_range (input,
|
||||
input[0].static_size * inputCount
|
||||
&& c->check_range (inputZ,
|
||||
inputZ[0].static_size * inputCount
|
||||
+ lookupRecordX[0].static_size * lookupCount);
|
||||
}
|
||||
|
||||
@ -1163,12 +1182,12 @@ struct Rule
|
||||
* glyph sequence--includes the first
|
||||
* glyph */
|
||||
USHORT lookupCount; /* Number of LookupRecords */
|
||||
USHORT input[VAR]; /* Array of match inputs--start with
|
||||
USHORT inputZ[VAR]; /* Array of match inputs--start with
|
||||
* second glyph */
|
||||
LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in
|
||||
* design order */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
|
||||
DEFINE_SIZE_ARRAY2 (4, inputZ, lookupRecordX);
|
||||
};
|
||||
|
||||
struct RuleSet
|
||||
@ -1413,16 +1432,16 @@ struct ContextFormat3
|
||||
inline void closure (hb_closure_context_t *c) const
|
||||
{
|
||||
TRACE_CLOSURE (this);
|
||||
if (!(this+coverage[0]).intersects (c->glyphs))
|
||||
if (!(this+coverageZ[0]).intersects (c->glyphs))
|
||||
return;
|
||||
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
|
||||
struct ContextClosureLookupContext lookup_context = {
|
||||
{intersects_coverage},
|
||||
this
|
||||
};
|
||||
context_closure_lookup (c,
|
||||
glyphCount, (const USHORT *) (coverage + 1),
|
||||
glyphCount, (const USHORT *) (coverageZ + 1),
|
||||
lookupCount, lookupRecord,
|
||||
lookup_context);
|
||||
}
|
||||
@ -1430,16 +1449,16 @@ struct ContextFormat3
|
||||
inline void collect_glyphs (hb_collect_glyphs_context_t *c) const
|
||||
{
|
||||
TRACE_COLLECT_GLYPHS (this);
|
||||
(this+coverage[0]).add_coverage (c->input);
|
||||
(this+coverageZ[0]).add_coverage (c->input);
|
||||
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
|
||||
struct ContextCollectGlyphsLookupContext lookup_context = {
|
||||
{collect_coverage},
|
||||
this
|
||||
};
|
||||
|
||||
context_collect_glyphs_lookup (c,
|
||||
glyphCount, (const USHORT *) (coverage + 1),
|
||||
glyphCount, (const USHORT *) (coverageZ + 1),
|
||||
lookupCount, lookupRecord,
|
||||
lookup_context);
|
||||
}
|
||||
@ -1448,41 +1467,42 @@ struct ContextFormat3
|
||||
{
|
||||
TRACE_WOULD_APPLY (this);
|
||||
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
|
||||
struct ContextApplyLookupContext lookup_context = {
|
||||
{match_coverage},
|
||||
this
|
||||
};
|
||||
return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
|
||||
return TRACE_RETURN (context_would_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
|
||||
}
|
||||
|
||||
inline const Coverage &get_coverage (void) const
|
||||
{
|
||||
return this+coverage[0];
|
||||
return this+coverageZ[0];
|
||||
}
|
||||
|
||||
inline bool apply (hb_apply_context_t *c) const
|
||||
{
|
||||
TRACE_APPLY (this);
|
||||
unsigned int index = (this+coverage[0]).get_coverage (c->buffer->cur().codepoint);
|
||||
unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
|
||||
if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
|
||||
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
|
||||
const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * glyphCount);
|
||||
struct ContextApplyLookupContext lookup_context = {
|
||||
{match_coverage},
|
||||
this
|
||||
};
|
||||
return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
|
||||
return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverageZ + 1), lookupCount, lookupRecord, lookup_context));
|
||||
}
|
||||
|
||||
inline bool sanitize (hb_sanitize_context_t *c) {
|
||||
TRACE_SANITIZE (this);
|
||||
if (!c->check_struct (this)) return TRACE_RETURN (false);
|
||||
unsigned int count = glyphCount;
|
||||
if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
|
||||
if (!count) return TRACE_RETURN (false); /* We want to access coverageZ[0] freely. */
|
||||
if (!c->check_array (coverageZ, coverageZ[0].static_size, count)) return TRACE_RETURN (false);
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
|
||||
LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
|
||||
if (!coverageZ[i].sanitize (c, this)) return TRACE_RETURN (false);
|
||||
LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverageZ, coverageZ[0].static_size * count);
|
||||
return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
|
||||
}
|
||||
|
||||
@ -1492,12 +1512,12 @@ struct ContextFormat3
|
||||
* sequence */
|
||||
USHORT lookupCount; /* Number of LookupRecords */
|
||||
OffsetTo<Coverage>
|
||||
coverage[VAR]; /* Array of offsets to Coverage
|
||||
coverageZ[VAR]; /* Array of offsets to Coverage
|
||||
* table in glyph sequence order */
|
||||
LookupRecord lookupRecordX[VAR]; /* Array of LookupRecords--in
|
||||
* design order */
|
||||
public:
|
||||
DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
|
||||
DEFINE_SIZE_ARRAY2 (6, coverageZ, lookupRecordX);
|
||||
};
|
||||
|
||||
struct Context
|
||||
@ -1505,7 +1525,7 @@ struct Context
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
case 2: return TRACE_RETURN (c->dispatch (u.format2));
|
||||
@ -2090,6 +2110,7 @@ struct ChainContextFormat3
|
||||
if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
|
||||
OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
|
||||
if (!input.sanitize (c, this)) return TRACE_RETURN (false);
|
||||
if (!input.len) return TRACE_RETURN (false); /* To be consistent with Context. */
|
||||
OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
|
||||
if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
|
||||
ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
|
||||
@ -2122,7 +2143,7 @@ struct ChainContext
|
||||
template <typename context_t>
|
||||
inline typename context_t::return_t dispatch (context_t *c) const
|
||||
{
|
||||
TRACE_DISPATCH (this);
|
||||
TRACE_DISPATCH (this, u.format);
|
||||
switch (u.format) {
|
||||
case 1: return TRACE_RETURN (c->dispatch (u.format1));
|
||||
case 2: return TRACE_RETURN (c->dispatch (u.format2));
|
||||
|
@ -207,7 +207,7 @@ struct arabic_fallback_plan_t
|
||||
|
||||
static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
|
||||
|
||||
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_WITH_WIN1256)
|
||||
#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_NO_WIN1256)
|
||||
#define HB_WITH_WIN1256
|
||||
#endif
|
||||
|
||||
|
@ -313,8 +313,11 @@ OT_TABLE_END
|
||||
/*
|
||||
* Include a second time to get the table data...
|
||||
*/
|
||||
#if 0
|
||||
#include "hb-private.hh" /* Make check-includes.sh happy. */
|
||||
#endif
|
||||
#ifdef OT_MEASURE
|
||||
#include __FILE__
|
||||
#include "hb-ot-shape-complex-arabic-win1256.hh"
|
||||
#endif
|
||||
|
||||
#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "hb-private.hh"
|
||||
|
||||
|
||||
#line 36 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 36 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
static const unsigned char _indic_syllable_machine_trans_keys[] = {
|
||||
1u, 16u, 13u, 13u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u,
|
||||
5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 4u, 4u, 6u, 6u,
|
||||
@ -1550,7 +1550,7 @@ find_syllables (hb_buffer_t *buffer)
|
||||
int cs;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
||||
#line 1554 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 1554 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
{
|
||||
cs = indic_syllable_machine_start;
|
||||
ts = 0;
|
||||
@ -1567,7 +1567,7 @@ find_syllables (hb_buffer_t *buffer)
|
||||
unsigned int last = 0;
|
||||
unsigned int syllable_serial = 1;
|
||||
|
||||
#line 1571 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 1571 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
{
|
||||
int _slen;
|
||||
int _trans;
|
||||
@ -1581,7 +1581,7 @@ _resume:
|
||||
#line 1 "NONE"
|
||||
{ts = p;}
|
||||
break;
|
||||
#line 1585 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 1585 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
}
|
||||
|
||||
_keys = _indic_syllable_machine_trans_keys + (cs<<1);
|
||||
@ -1700,7 +1700,7 @@ _eof_trans:
|
||||
#line 93 "../../src/hb-ot-shape-complex-indic-machine.rl"
|
||||
{act = 6;}
|
||||
break;
|
||||
#line 1704 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 1704 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
}
|
||||
|
||||
_again:
|
||||
@ -1709,7 +1709,7 @@ _again:
|
||||
#line 1 "NONE"
|
||||
{ts = 0;}
|
||||
break;
|
||||
#line 1713 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
#line 1713 "hb-ot-shape-complex-indic-machine.hh.tmp"
|
||||
}
|
||||
|
||||
if ( ++p != pe )
|
||||
|
@ -69,7 +69,7 @@ cn = c.ZWJ?.n?;
|
||||
forced_rakar = ZWJ H ZWJ Ra;
|
||||
symbol = Symbol.N?;
|
||||
matra_group = z{0,3}.M.N?.(H | forced_rakar)?;
|
||||
syllable_tail = (SM.SM?.ZWNJ?)? A{0,3}? VD{0,2};
|
||||
syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}? VD{0,2};
|
||||
place_holder = PLACEHOLDER | DOTTEDCIRCLE;
|
||||
halant_group = (z?.h.(ZWJ.N?)?);
|
||||
final_halant_group = halant_group | h.ZWNJ;
|
||||
|
@ -96,6 +96,8 @@
|
||||
|
||||
#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
|
||||
#define snprintf _snprintf
|
||||
/* Windows CE only has _strdup, while rest of Windows has both. */
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -126,10 +128,47 @@
|
||||
# ifndef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x0600
|
||||
# endif
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# define STRICT
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
# endif
|
||||
# ifndef STRICT
|
||||
# define STRICT 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
/* Some things not defined on Windows CE. */
|
||||
#define MemoryBarrier()
|
||||
#define getenv(Name) NULL
|
||||
#define setlocale(Category, Locale) "C"
|
||||
static int errno = 0; /* Use something better? */
|
||||
#endif
|
||||
|
||||
#if HAVE_ATEXIT
|
||||
/* atexit() is only safe to be called from shared libraries on certain
|
||||
* platforms. Whitelist.
|
||||
* https://bugs.freedesktop.org/show_bug.cgi?id=82246 */
|
||||
# if defined(__linux) && defined(__GLIBC_PREREQ)
|
||||
# if __GLIBC_PREREQ(2,3)
|
||||
/* From atexit() manpage, it's safe with glibc 2.2.3 on Linux. */
|
||||
# define HB_USE_ATEXIT 1
|
||||
# endif
|
||||
# elif defined(_MSC_VER) || defined(__MINGW32__)
|
||||
/* For MSVC:
|
||||
* http://msdn.microsoft.com/en-ca/library/tze57ck3.aspx
|
||||
* http://msdn.microsoft.com/en-ca/library/zk17ww08.aspx
|
||||
* mingw32 headers say atexit is safe to use in shared libraries.
|
||||
*/
|
||||
# define HB_USE_ATEXIT 1
|
||||
# elif defined(__ANDROID__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
|
||||
/* This was fixed in Android NKD r8 or r8b:
|
||||
* https://code.google.com/p/android/issues/detail?id=6455
|
||||
* which introduced GCC 4.6:
|
||||
* https://developer.android.com/tools/sdk/ndk/index.html
|
||||
*/
|
||||
# define HB_USE_ATEXIT 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Basics */
|
||||
|
||||
@ -500,47 +539,6 @@ struct hb_lockable_set_t
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* Big-endian handling */
|
||||
|
||||
static inline uint16_t hb_be_uint16 (const uint16_t v)
|
||||
{
|
||||
const uint8_t *V = (const uint8_t *) &v;
|
||||
return (V[0] << 8) | V[1];
|
||||
}
|
||||
|
||||
static inline uint16_t hb_uint16_swap (const uint16_t v)
|
||||
{
|
||||
return (v >> 8) | (v << 8);
|
||||
}
|
||||
|
||||
static inline uint32_t hb_uint32_swap (const uint32_t v)
|
||||
{
|
||||
return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16);
|
||||
}
|
||||
|
||||
/* Note, of the following macros, uint16_get is the one called many many times.
|
||||
* If there is any optimizations to be done, it's in that macro. However, I
|
||||
* already confirmed that on my T400 ThinkPad at least, using bswap_16(), which
|
||||
* results in a single ror instruction, does NOT speed this up. In fact, it
|
||||
* resulted in a minor slowdown. At any rate, note that v may not be correctly
|
||||
* aligned, so I think the current implementation is optimal.
|
||||
*/
|
||||
|
||||
#define hb_be_uint16_put(v,V) HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
|
||||
#define hb_be_uint16_get(v) (uint16_t) ((v[0] << 8) + v[1])
|
||||
#define hb_be_uint16_eq(a,b) (a[0] == b[0] && a[1] == b[1])
|
||||
|
||||
#define hb_be_uint32_put(v,V) HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END
|
||||
#define hb_be_uint32_get(v) (uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
|
||||
#define hb_be_uint32_eq(a,b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
|
||||
|
||||
#define hb_be_uint24_put(v,V) HB_STMT_START { v[0] = (V>>16); v[1] = (V>>8); v[2] = (V); } HB_STMT_END
|
||||
#define hb_be_uint24_get(v) (uint32_t) ((v[0] << 16) + (v[1] << 8) + v[2])
|
||||
#define hb_be_uint24_eq(a,b) (a[0] == b[0] && a[1] == b[1] && a[2] == b[2])
|
||||
|
||||
|
||||
/* ASCII tag/character handling */
|
||||
|
||||
static inline bool ISALPHA (unsigned char c)
|
||||
@ -585,6 +583,15 @@ _hb_debug (unsigned int level,
|
||||
#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
|
||||
#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0))
|
||||
|
||||
template <int max_level> static inline void
|
||||
_hb_debug_msg_va (const char *what,
|
||||
const void *obj,
|
||||
const char *func,
|
||||
bool indented,
|
||||
unsigned int level,
|
||||
int level_dir,
|
||||
const char *message,
|
||||
va_list ap) HB_PRINTF_FUNC(7, 0);
|
||||
template <int max_level> static inline void
|
||||
_hb_debug_msg_va (const char *what,
|
||||
const void *obj,
|
||||
@ -708,7 +715,9 @@ _hb_debug_msg<0> (const char *what HB_UNUSED,
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
struct hb_printer_t {};
|
||||
struct hb_printer_t {
|
||||
const char *print (const T&) { return "something"; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hb_printer_t<bool> {
|
||||
@ -815,7 +824,9 @@ hb_in_range (T u, T lo, T hi)
|
||||
* to generate a warning than unused variables. */
|
||||
ASSERT_STATIC (sizeof (hb_assert_unsigned_t<T>) >= 0);
|
||||
|
||||
return (u - lo) <= (hi - lo);
|
||||
/* The casts below are important as if T is smaller than int,
|
||||
* the subtract results will become a signed int! */
|
||||
return (T)(u - lo) <= (T)(hi - lo);
|
||||
}
|
||||
|
||||
template <typename T> static inline bool
|
||||
@ -839,7 +850,7 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
|
||||
#define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
|
||||
|
||||
|
||||
template <typename T, typename T2> inline void
|
||||
template <typename T, typename T2> static inline void
|
||||
hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
|
||||
{
|
||||
if (unlikely (!len))
|
||||
@ -872,7 +883,7 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *),
|
||||
} while (k);
|
||||
}
|
||||
|
||||
template <typename T> inline void
|
||||
template <typename T> static inline void
|
||||
hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
|
||||
{
|
||||
hb_bubble_sort (array, len, compar, (int *) NULL);
|
||||
@ -901,12 +912,12 @@ hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *o
|
||||
|
||||
struct hb_options_t
|
||||
{
|
||||
int initialized : 1;
|
||||
int uniscribe_bug_compatible : 1;
|
||||
unsigned int initialized : 1;
|
||||
unsigned int uniscribe_bug_compatible : 1;
|
||||
};
|
||||
|
||||
union hb_options_union_t {
|
||||
int i;
|
||||
unsigned int i;
|
||||
hb_options_t opts;
|
||||
};
|
||||
ASSERT_STATIC (sizeof (int) == sizeof (hb_options_union_t));
|
||||
|
@ -150,7 +150,7 @@ struct hb_set_t
|
||||
bool in_error;
|
||||
|
||||
inline void init (void) {
|
||||
header.init ();
|
||||
hb_object_init (this);
|
||||
clear ();
|
||||
}
|
||||
inline void fini (void) {
|
||||
|
@ -29,6 +29,12 @@
|
||||
#include "hb-font-private.hh"
|
||||
#include "hb-buffer-private.hh"
|
||||
|
||||
|
||||
#ifndef HB_DEBUG_SHAPE_PLAN
|
||||
#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
|
||||
#endif
|
||||
|
||||
|
||||
#define HB_SHAPER_IMPLEMENT(shaper) \
|
||||
HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
|
||||
HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
|
||||
@ -42,6 +48,11 @@ hb_shape_plan_plan (hb_shape_plan_t *shape_plan,
|
||||
unsigned int num_user_features,
|
||||
const char * const *shaper_list)
|
||||
{
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
|
||||
"num_features=%d shaper_list=%p",
|
||||
num_user_features,
|
||||
shaper_list);
|
||||
|
||||
const hb_shaper_pair_t *shapers = _hb_shapers_get ();
|
||||
|
||||
#define HB_SHAPER_PLAN(shaper) \
|
||||
@ -104,6 +115,12 @@ hb_shape_plan_create (hb_face_t *face,
|
||||
unsigned int num_user_features,
|
||||
const char * const *shaper_list)
|
||||
{
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
|
||||
"face=%p num_features=%d shaper_list=%p",
|
||||
face,
|
||||
num_user_features,
|
||||
shaper_list);
|
||||
|
||||
hb_shape_plan_t *shape_plan;
|
||||
hb_feature_t *features = NULL;
|
||||
|
||||
@ -271,6 +288,11 @@ hb_shape_plan_execute (hb_shape_plan_t *shape_plan,
|
||||
const hb_feature_t *features,
|
||||
unsigned int num_features)
|
||||
{
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
|
||||
"num_features=%d shaper_func=%p",
|
||||
num_features,
|
||||
shape_plan->shaper_func);
|
||||
|
||||
if (unlikely (hb_object_is_inert (shape_plan) ||
|
||||
hb_object_is_inert (font) ||
|
||||
hb_object_is_inert (buffer)))
|
||||
@ -383,6 +405,12 @@ hb_shape_plan_create_cached (hb_face_t *face,
|
||||
unsigned int num_user_features,
|
||||
const char * const *shaper_list)
|
||||
{
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
|
||||
"face=%p num_features=%d shaper_list=%p",
|
||||
face,
|
||||
num_user_features,
|
||||
shaper_list);
|
||||
|
||||
hb_shape_plan_proposal_t proposal = {
|
||||
*props,
|
||||
shaper_list,
|
||||
@ -392,25 +420,22 @@ hb_shape_plan_create_cached (hb_face_t *face,
|
||||
};
|
||||
|
||||
if (shaper_list) {
|
||||
/* Choose shaper. Adapted from hb_shape_plan_plan(). */
|
||||
#define HB_SHAPER_PLAN(shaper) \
|
||||
HB_STMT_START { \
|
||||
if (hb_##shaper##_shaper_face_data_ensure (face)) \
|
||||
proposal.shaper_func = _hb_##shaper##_shape; \
|
||||
} HB_STMT_END
|
||||
|
||||
/* Choose shaper. Adapted from hb_shape_plan_plan().
|
||||
* Must choose shaper exactly the same way as that function. */
|
||||
for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
|
||||
if (0)
|
||||
;
|
||||
#define HB_SHAPER_IMPLEMENT(shaper) \
|
||||
else if (0 == strcmp (*shaper_item, #shaper)) \
|
||||
HB_SHAPER_PLAN (shaper);
|
||||
else if (0 == strcmp (*shaper_item, #shaper) && \
|
||||
hb_##shaper##_shaper_face_data_ensure (face)) \
|
||||
{ \
|
||||
proposal.shaper_func = _hb_##shaper##_shape; \
|
||||
break; \
|
||||
}
|
||||
#include "hb-shaper-list.hh"
|
||||
#undef HB_SHAPER_IMPLEMENT
|
||||
|
||||
#undef HB_SHAPER_PLAN
|
||||
|
||||
if (unlikely (!proposal.shaper_list))
|
||||
if (unlikely (!proposal.shaper_func))
|
||||
return hb_shape_plan_get_empty ();
|
||||
}
|
||||
|
||||
@ -419,7 +444,10 @@ retry:
|
||||
hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
|
||||
for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
|
||||
if (hb_shape_plan_matches (node->shape_plan, &proposal))
|
||||
{
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
|
||||
return hb_shape_plan_reference (node->shape_plan);
|
||||
}
|
||||
|
||||
/* Not found. */
|
||||
|
||||
@ -442,6 +470,7 @@ retry:
|
||||
free (node);
|
||||
goto retry;
|
||||
}
|
||||
DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
|
||||
|
||||
return hb_shape_plan_reference (shape_plan);
|
||||
}
|
||||
|
@ -320,7 +320,7 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATEXIT
|
||||
#ifdef HB_USE_ATEXIT
|
||||
atexit (free_static_shaper_list); /* First person registers atexit() callback. */
|
||||
#endif
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ATEXIT
|
||||
#ifdef HB_USE_ATEXIT
|
||||
atexit (free_static_shapers); /* First person registers atexit() callback. */
|
||||
#endif
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user