зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c, a=merge
This commit is contained in:
Коммит
43c1e6ab4d
|
@ -1,13 +1,13 @@
|
|||
[
|
||||
{
|
||||
"clang_version": "r183744"
|
||||
"clang_version": "r241406"
|
||||
},
|
||||
{
|
||||
"size": 70350828,
|
||||
"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6",
|
||||
"size": 100307285,
|
||||
"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30",
|
||||
"algorithm": "sha512",
|
||||
"unpack": true,
|
||||
"filename": "clang.tar.bz2"
|
||||
"filename": "clang.tar.bz2",
|
||||
"unpack": true
|
||||
},
|
||||
{
|
||||
"size": 80458572,
|
||||
|
|
|
@ -1936,7 +1936,7 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
|
|||
message = eQueryCaretRect;
|
||||
break;
|
||||
case QUERY_TEXT_RECT:
|
||||
message = NS_QUERY_TEXT_RECT;
|
||||
message = eQueryTextRect;
|
||||
break;
|
||||
case QUERY_EDITOR_RECT:
|
||||
message = eQueryEditorRect;
|
||||
|
@ -1991,7 +1991,7 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType,
|
|||
case eQueryCaretRect:
|
||||
queryEvent.InitForQueryCaretRect(aOffset, useNativeLineBreak);
|
||||
break;
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
case eQueryTextRect:
|
||||
queryEvent.InitForQueryTextRect(aOffset, aLength, useNativeLineBreak);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -3886,7 +3886,7 @@ void
|
|||
nsDocument::DeleteShell()
|
||||
{
|
||||
mExternalResourceMap.HideViewers();
|
||||
if (IsEventHandlingEnabled()) {
|
||||
if (IsEventHandlingEnabled() && !AnimationsPaused()) {
|
||||
RevokeAnimationFrameNotifications();
|
||||
}
|
||||
if (nsPresContext* presContext = mPresShell->GetPresContext()) {
|
||||
|
@ -4632,7 +4632,7 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
|||
// our layout history state now.
|
||||
mLayoutHistoryState = GetLayoutHistoryState();
|
||||
|
||||
if (mPresShell && !EventHandlingSuppressed()) {
|
||||
if (mPresShell && !EventHandlingSuppressed() && !AnimationsPaused()) {
|
||||
RevokeAnimationFrameNotifications();
|
||||
}
|
||||
|
||||
|
@ -10318,7 +10318,8 @@ nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
|
|||
DebugOnly<FrameRequest*> request =
|
||||
mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
|
||||
NS_ASSERTION(request, "This is supposed to be infallible!");
|
||||
if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
|
||||
if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled() &&
|
||||
!AnimationsPaused()) {
|
||||
mPresShell->GetPresContext()->RefreshDriver()->
|
||||
ScheduleFrameRequestCallbacks(this);
|
||||
}
|
||||
|
|
|
@ -1449,8 +1449,8 @@ nsGlobalWindow::CleanUp()
|
|||
}
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
|
||||
DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
|
||||
DisableNetworkEvent(eNetworkUpload);
|
||||
DisableNetworkEvent(eNetworkDownload);
|
||||
#endif // MOZ_B2G
|
||||
|
||||
if (mIdleService) {
|
||||
|
@ -14601,13 +14601,13 @@ nsGlobalWindow::EnableNetworkEvent(EventMessage aEventMessage)
|
|||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case NS_NETWORK_UPLOAD_EVENT:
|
||||
case eNetworkUpload:
|
||||
if (!mNetworkUploadObserverEnabled) {
|
||||
mNetworkUploadObserverEnabled = true;
|
||||
os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC, false);
|
||||
}
|
||||
break;
|
||||
case NS_NETWORK_DOWNLOAD_EVENT:
|
||||
case eNetworkDownload:
|
||||
if (!mNetworkDownloadObserverEnabled) {
|
||||
mNetworkDownloadObserverEnabled = true;
|
||||
os->AddObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC, false);
|
||||
|
@ -14629,13 +14629,13 @@ nsGlobalWindow::DisableNetworkEvent(EventMessage aEventMessage)
|
|||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case NS_NETWORK_UPLOAD_EVENT:
|
||||
case eNetworkUpload:
|
||||
if (mNetworkUploadObserverEnabled) {
|
||||
mNetworkUploadObserverEnabled = false;
|
||||
os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_UPLOAD_TOPIC);
|
||||
}
|
||||
break;
|
||||
case NS_NETWORK_DOWNLOAD_EVENT:
|
||||
case eNetworkDownload:
|
||||
if (mNetworkDownloadObserverEnabled) {
|
||||
mNetworkDownloadObserverEnabled = false;
|
||||
os->RemoveObserver(mObserver, NS_NETWORK_ACTIVITY_BLIP_DOWNLOAD_TOPIC);
|
||||
|
|
|
@ -58,7 +58,7 @@ nsQueryContentEventResult::GetTentativeCaretOffset(uint32_t* aOffset)
|
|||
static bool IsRectEnabled(EventMessage aEventMessage)
|
||||
{
|
||||
return aEventMessage == eQueryCaretRect ||
|
||||
aEventMessage == NS_QUERY_TEXT_RECT ||
|
||||
aEventMessage == eQueryTextRect ||
|
||||
aEventMessage == eQueryEditorRect ||
|
||||
aEventMessage == eQueryCharacterAtPoint;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/BlobSet.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FetchUtil.h"
|
||||
#include "mozilla/dom/XMLHttpRequestUploadBinding.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
|
@ -1617,31 +1618,10 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
|
||||
NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Disallow HTTP/1.1 TRACE method (see bug 302489)
|
||||
// and MS IIS equivalent TRACK (see bug 381264)
|
||||
// and CONNECT
|
||||
if (inMethod.LowerCaseEqualsLiteral("trace") ||
|
||||
inMethod.LowerCaseEqualsLiteral("connect") ||
|
||||
inMethod.LowerCaseEqualsLiteral("track")) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsAutoCString method;
|
||||
// GET, POST, DELETE, HEAD, OPTIONS, PUT methods normalized to upper case
|
||||
if (inMethod.LowerCaseEqualsLiteral("get")) {
|
||||
method.AssignLiteral("GET");
|
||||
} else if (inMethod.LowerCaseEqualsLiteral("post")) {
|
||||
method.AssignLiteral("POST");
|
||||
} else if (inMethod.LowerCaseEqualsLiteral("delete")) {
|
||||
method.AssignLiteral("DELETE");
|
||||
} else if (inMethod.LowerCaseEqualsLiteral("head")) {
|
||||
method.AssignLiteral("HEAD");
|
||||
} else if (inMethod.LowerCaseEqualsLiteral("options")) {
|
||||
method.AssignLiteral("OPTIONS");
|
||||
} else if (inMethod.LowerCaseEqualsLiteral("put")) {
|
||||
method.AssignLiteral("PUT");
|
||||
} else {
|
||||
method = inMethod; // other methods are not normalized
|
||||
nsresult rv = FetchUtil::GetValidRequestMethod(inMethod, method);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// sync request is not allowed using withCredential or responseType
|
||||
|
@ -1662,7 +1642,6 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,
|
|||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
|
||||
if (mState & (XML_HTTP_REQUEST_OPENED |
|
||||
|
|
|
@ -802,7 +802,7 @@ ContentEventHandler::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
|
|||
return OnQueryTextContent(aEvent);
|
||||
case eQueryCaretRect:
|
||||
return OnQueryCaretRect(aEvent);
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
case eQueryTextRect:
|
||||
return OnQueryTextRect(aEvent);
|
||||
case eQueryEditorRect:
|
||||
return OnQueryEditorRect(aEvent);
|
||||
|
@ -1330,7 +1330,7 @@ ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
|
|||
GetLineBreakType(aEvent));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget);
|
||||
WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->widget);
|
||||
textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak);
|
||||
rv = OnQueryTextRect(&textRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
nsresult OnQueryTextContent(WidgetQueryContentEvent* aEvent);
|
||||
// eQueryCaretRect event handler
|
||||
nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent);
|
||||
// NS_QUERY_TEXT_RECT event handler
|
||||
// eQueryTextRect event handler
|
||||
nsresult OnQueryTextRect(WidgetQueryContentEvent* aEvent);
|
||||
// eQueryEditorRect event handler
|
||||
nsresult OnQueryEditorRect(WidgetQueryContentEvent* aEvent);
|
||||
|
|
|
@ -318,13 +318,13 @@ EventListenerManager::AddEventListenerInternal(
|
|||
kAllMutationBits : MutationBitForEventType(aEventMessage));
|
||||
}
|
||||
} else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
|
||||
EnableDevice(NS_DEVICE_ORIENTATION);
|
||||
EnableDevice(eDeviceOrientation);
|
||||
} else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
|
||||
EnableDevice(NS_DEVICE_PROXIMITY);
|
||||
EnableDevice(eDeviceProximity);
|
||||
} else if (aTypeAtom == nsGkAtoms::ondevicelight) {
|
||||
EnableDevice(NS_DEVICE_LIGHT);
|
||||
EnableDevice(eDeviceLight);
|
||||
} else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
|
||||
EnableDevice(NS_DEVICE_MOTION);
|
||||
EnableDevice(eDeviceMotion);
|
||||
#ifdef MOZ_B2G
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
|
@ -334,12 +334,12 @@ EventListenerManager::AddEventListenerInternal(
|
|||
} else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
|
||||
window->EnableNetworkEvent(eNetworkUpload);
|
||||
}
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
|
||||
window->EnableNetworkEvent(eNetworkDownload);
|
||||
}
|
||||
#endif // MOZ_B2G
|
||||
} else if (aTypeAtom == nsGkAtoms::ontouchstart ||
|
||||
|
@ -383,8 +383,8 @@ EventListenerManager::AddEventListenerInternal(
|
|||
window->SetHasMouseEnterLeaveEventListeners();
|
||||
}
|
||||
#ifdef MOZ_GAMEPAD
|
||||
} else if (aEventMessage >= NS_GAMEPAD_START &&
|
||||
aEventMessage <= NS_GAMEPAD_END) {
|
||||
} else if (aEventMessage >= eGamepadEventFirst &&
|
||||
aEventMessage <= eGamepadEventLast) {
|
||||
nsPIDOMWindow* window = GetInnerWindowForTarget();
|
||||
if (window) {
|
||||
window->SetHasGamepadEventListener();
|
||||
|
@ -419,11 +419,11 @@ bool
|
|||
EventListenerManager::IsDeviceType(EventMessage aEventMessage)
|
||||
{
|
||||
switch (aEventMessage) {
|
||||
case NS_DEVICE_ORIENTATION:
|
||||
case NS_DEVICE_MOTION:
|
||||
case NS_DEVICE_LIGHT:
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
case NS_USER_PROXIMITY:
|
||||
case eDeviceOrientation:
|
||||
case eDeviceMotion:
|
||||
case eDeviceLight:
|
||||
case eDeviceProximity:
|
||||
case eUserProximity:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
|
@ -440,17 +440,17 @@ EventListenerManager::EnableDevice(EventMessage aEventMessage)
|
|||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case NS_DEVICE_ORIENTATION:
|
||||
case eDeviceOrientation:
|
||||
window->EnableDeviceSensor(SENSOR_ORIENTATION);
|
||||
break;
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
case NS_USER_PROXIMITY:
|
||||
case eDeviceProximity:
|
||||
case eUserProximity:
|
||||
window->EnableDeviceSensor(SENSOR_PROXIMITY);
|
||||
break;
|
||||
case NS_DEVICE_LIGHT:
|
||||
case eDeviceLight:
|
||||
window->EnableDeviceSensor(SENSOR_LIGHT);
|
||||
break;
|
||||
case NS_DEVICE_MOTION:
|
||||
case eDeviceMotion:
|
||||
window->EnableDeviceSensor(SENSOR_ACCELERATION);
|
||||
window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
|
||||
window->EnableDeviceSensor(SENSOR_GYROSCOPE);
|
||||
|
@ -470,19 +470,19 @@ EventListenerManager::DisableDevice(EventMessage aEventMessage)
|
|||
}
|
||||
|
||||
switch (aEventMessage) {
|
||||
case NS_DEVICE_ORIENTATION:
|
||||
case eDeviceOrientation:
|
||||
window->DisableDeviceSensor(SENSOR_ORIENTATION);
|
||||
break;
|
||||
case NS_DEVICE_MOTION:
|
||||
case eDeviceMotion:
|
||||
window->DisableDeviceSensor(SENSOR_ACCELERATION);
|
||||
window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
|
||||
window->DisableDeviceSensor(SENSOR_GYROSCOPE);
|
||||
break;
|
||||
case NS_DEVICE_PROXIMITY:
|
||||
case NS_USER_PROXIMITY:
|
||||
case eDeviceProximity:
|
||||
case eUserProximity:
|
||||
window->DisableDeviceSensor(SENSOR_PROXIMITY);
|
||||
break;
|
||||
case NS_DEVICE_LIGHT:
|
||||
case eDeviceLight:
|
||||
window->DisableDeviceSensor(SENSOR_LIGHT);
|
||||
break;
|
||||
default:
|
||||
|
@ -510,9 +510,9 @@ EventListenerManager::RemoveEventListenerInternal(
|
|||
uint32_t typeCount = 0;
|
||||
bool deviceType = IsDeviceType(aEventMessage);
|
||||
#ifdef MOZ_B2G
|
||||
bool timeChangeEvent = (aEventMessage == NS_MOZ_TIME_CHANGE_EVENT);
|
||||
bool networkEvent = (aEventMessage == NS_NETWORK_UPLOAD_EVENT ||
|
||||
aEventMessage == NS_NETWORK_DOWNLOAD_EVENT);
|
||||
bool timeChangeEvent = (aEventMessage == eTimeChange);
|
||||
bool networkEvent = (aEventMessage == eNetworkUpload ||
|
||||
aEventMessage == eNetworkDownload);
|
||||
#endif // MOZ_B2G
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
|
|
|
@ -303,11 +303,11 @@ EVENT(mozfullscreenerror,
|
|||
EventNameType_HTML,
|
||||
eBasicEventClass)
|
||||
EVENT(mozpointerlockchange,
|
||||
NS_POINTERLOCKCHANGE,
|
||||
ePointerLockChange,
|
||||
EventNameType_HTML,
|
||||
eBasicEventClass)
|
||||
EVENT(mozpointerlockerror,
|
||||
NS_POINTERLOCKERROR,
|
||||
ePointerLockError,
|
||||
EventNameType_HTML,
|
||||
eBasicEventClass)
|
||||
EVENT(pointerdown,
|
||||
|
@ -469,11 +469,11 @@ FORWARDED_EVENT(scroll,
|
|||
eBasicEventClass)
|
||||
|
||||
WINDOW_EVENT(afterprint,
|
||||
NS_AFTERPRINT,
|
||||
eAfterPrint,
|
||||
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
|
||||
eBasicEventClass)
|
||||
WINDOW_EVENT(beforeprint,
|
||||
NS_BEFOREPRINT,
|
||||
eBeforePrint,
|
||||
EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly,
|
||||
eBasicEventClass)
|
||||
BEFOREUNLOAD_EVENT(beforeunload,
|
||||
|
@ -528,37 +528,37 @@ WINDOW_EVENT(unload,
|
|||
eBasicEventClass)
|
||||
|
||||
WINDOW_ONLY_EVENT(devicemotion,
|
||||
NS_DEVICE_MOTION,
|
||||
eDeviceMotion,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(deviceorientation,
|
||||
NS_DEVICE_ORIENTATION,
|
||||
eDeviceOrientation,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(deviceproximity,
|
||||
NS_DEVICE_PROXIMITY,
|
||||
eDeviceProximity,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(userproximity,
|
||||
NS_USER_PROXIMITY,
|
||||
eUserProximity,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(devicelight,
|
||||
NS_DEVICE_LIGHT,
|
||||
eDeviceLight,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
|
||||
#ifdef MOZ_B2G
|
||||
WINDOW_ONLY_EVENT(moztimechange,
|
||||
NS_MOZ_TIME_CHANGE_EVENT,
|
||||
eTimeChange,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(moznetworkupload,
|
||||
NS_NETWORK_UPLOAD_EVENT,
|
||||
eNetworkUpload,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
WINDOW_ONLY_EVENT(moznetworkdownload,
|
||||
NS_NETWORK_DOWNLOAD_EVENT,
|
||||
eNetworkDownload,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
#endif // MOZ_B2G
|
||||
|
@ -672,19 +672,19 @@ NON_IDL_EVENT(speakerforcedchange,
|
|||
// This shouldn't be used by web/xul apps. "compositionupdate" should be
|
||||
// used instead.
|
||||
NON_IDL_EVENT(text,
|
||||
NS_COMPOSITION_CHANGE,
|
||||
eCompositionChange,
|
||||
EventNameType_XUL,
|
||||
eCompositionEventClass)
|
||||
NON_IDL_EVENT(compositionstart,
|
||||
NS_COMPOSITION_START,
|
||||
eCompositionStart,
|
||||
EventNameType_XUL,
|
||||
eCompositionEventClass)
|
||||
NON_IDL_EVENT(compositionupdate,
|
||||
NS_COMPOSITION_UPDATE,
|
||||
eCompositionUpdate,
|
||||
EventNameType_XUL,
|
||||
eCompositionEventClass)
|
||||
NON_IDL_EVENT(compositionend,
|
||||
NS_COMPOSITION_END,
|
||||
eCompositionEnd,
|
||||
EventNameType_XUL,
|
||||
eCompositionEventClass)
|
||||
NON_IDL_EVENT(command,
|
||||
|
@ -732,11 +732,11 @@ NON_IDL_EVENT(draggesture,
|
|||
EventNameType_XUL,
|
||||
eDragEventClass)
|
||||
NON_IDL_EVENT(overflow,
|
||||
NS_SCROLLPORT_OVERFLOW,
|
||||
eScrollPortOverflow,
|
||||
EventNameType_XUL,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(underflow,
|
||||
NS_SCROLLPORT_UNDERFLOW,
|
||||
eScrollPortUnderflow,
|
||||
EventNameType_XUL,
|
||||
eBasicEventClass)
|
||||
|
||||
|
@ -774,34 +774,34 @@ NON_IDL_EVENT(zoom,
|
|||
// Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
|
||||
#ifndef MESSAGE_TO_EVENT
|
||||
NON_IDL_EVENT(begin,
|
||||
NS_SMIL_BEGIN,
|
||||
eSMILBeginEvent,
|
||||
EventNameType_SMIL,
|
||||
eBasicEventClass)
|
||||
#endif
|
||||
NON_IDL_EVENT(beginEvent,
|
||||
NS_SMIL_BEGIN,
|
||||
eSMILBeginEvent,
|
||||
EventNameType_None,
|
||||
eSMILTimeEventClass)
|
||||
// Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
|
||||
#ifndef MESSAGE_TO_EVENT
|
||||
NON_IDL_EVENT(end,
|
||||
NS_SMIL_END,
|
||||
eSMILEndEvent,
|
||||
EventNameType_SMIL,
|
||||
eBasicEventClass)
|
||||
#endif
|
||||
NON_IDL_EVENT(endEvent,
|
||||
NS_SMIL_END,
|
||||
eSMILEndEvent,
|
||||
EventNameType_None,
|
||||
eSMILTimeEventClass)
|
||||
// Only map the ID to the real event name when MESSAGE_TO_EVENT is defined.
|
||||
#ifndef MESSAGE_TO_EVENT
|
||||
NON_IDL_EVENT(repeat,
|
||||
NS_SMIL_REPEAT,
|
||||
eSMILRepeatEvent,
|
||||
EventNameType_SMIL,
|
||||
eBasicEventClass)
|
||||
#endif
|
||||
NON_IDL_EVENT(repeatEvent,
|
||||
NS_SMIL_REPEAT,
|
||||
eSMILRepeatEvent,
|
||||
EventNameType_None,
|
||||
eSMILTimeEventClass)
|
||||
|
||||
|
@ -811,29 +811,29 @@ NON_IDL_EVENT(MozAfterPaint,
|
|||
eBasicEventClass)
|
||||
|
||||
NON_IDL_EVENT(MozScrolledAreaChanged,
|
||||
NS_SCROLLEDAREACHANGED,
|
||||
eScrolledAreaChanged,
|
||||
EventNameType_None,
|
||||
eScrollAreaEventClass)
|
||||
|
||||
#ifdef MOZ_GAMEPAD
|
||||
NON_IDL_EVENT(gamepadbuttondown,
|
||||
NS_GAMEPAD_BUTTONDOWN,
|
||||
eGamepadButtonDown,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(gamepadbuttonup,
|
||||
NS_GAMEPAD_BUTTONUP,
|
||||
eGamepadButtonUp,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(gamepadaxismove,
|
||||
NS_GAMEPAD_AXISMOVE,
|
||||
eGamepadAxisMove,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(gamepadconnected,
|
||||
NS_GAMEPAD_CONNECTED,
|
||||
eGamepadConnected,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
NON_IDL_EVENT(gamepaddisconnected,
|
||||
NS_GAMEPAD_DISCONNECTED,
|
||||
eGamepadDisconnected,
|
||||
EventNameType_None,
|
||||
eBasicEventClass)
|
||||
#endif
|
||||
|
|
|
@ -759,7 +759,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
case eContentCommandScroll:
|
||||
DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
|
||||
break;
|
||||
case NS_COMPOSITION_START:
|
||||
case eCompositionStart:
|
||||
if (aEvent->mFlags.mIsTrusted) {
|
||||
// If the event is trusted event, set the selected text to data of
|
||||
// composition event.
|
||||
|
@ -784,7 +784,7 @@ EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
|
|||
case eQuerySelectedText:
|
||||
case eQueryTextContent:
|
||||
case eQueryCaretRect:
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
case eQueryTextRect:
|
||||
case eQueryEditorRect:
|
||||
if (!IsTargetCrossProcess(aEvent)) {
|
||||
break;
|
||||
|
@ -2563,7 +2563,7 @@ EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
|
|||
nsIFrame* targetFrame)
|
||||
{
|
||||
|
||||
NS_ASSERTION(aEvent->mMessage == NS_GESTURENOTIFY_EVENT_START,
|
||||
NS_ASSERTION(aEvent->mMessage == eGestureNotify,
|
||||
"DecideGestureEvent called with a non-gesture event");
|
||||
|
||||
/* Check the ancestor tree to decide if any frame is willing* to receive
|
||||
|
@ -3133,7 +3133,7 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
|||
}
|
||||
break;
|
||||
|
||||
case NS_GESTURENOTIFY_EVENT_START:
|
||||
case eGestureNotify:
|
||||
{
|
||||
if (nsEventStatus_eConsumeNoDefault != *aStatus) {
|
||||
DecideGestureEvent(aEvent->AsGestureNotifyEvent(), mCurrentTarget);
|
||||
|
|
|
@ -57,8 +57,8 @@ ToChar(EventMessage aEventMessage)
|
|||
return "eQueryTextContent";
|
||||
case eQueryCaretRect:
|
||||
return "eQueryCaretRect";
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
return "NS_QUERY_TEXT_RECT";
|
||||
case eQueryTextRect:
|
||||
return "eQueryTextRect";
|
||||
case eQueryEditorRect:
|
||||
return "eQueryEditorRect";
|
||||
case eQueryContentState:
|
||||
|
|
|
@ -137,18 +137,18 @@ static const char*
|
|||
GetEventMessageName(EventMessage aMessage)
|
||||
{
|
||||
switch (aMessage) {
|
||||
case NS_COMPOSITION_START:
|
||||
return "NS_COMPOSITION_START";
|
||||
case NS_COMPOSITION_END:
|
||||
return "NS_COMPOSITION_END";
|
||||
case NS_COMPOSITION_UPDATE:
|
||||
return "NS_COMPOSITION_UPDATE";
|
||||
case NS_COMPOSITION_CHANGE:
|
||||
return "NS_COMPOSITION_CHANGE";
|
||||
case NS_COMPOSITION_COMMIT_AS_IS:
|
||||
return "NS_COMPOSITION_COMMIT_AS_IS";
|
||||
case NS_COMPOSITION_COMMIT:
|
||||
return "NS_COMPOSITION_COMMIT";
|
||||
case eCompositionStart:
|
||||
return "eCompositionStart";
|
||||
case eCompositionEnd:
|
||||
return "eCompositionEnd";
|
||||
case eCompositionUpdate:
|
||||
return "eCompositionUpdate";
|
||||
case eCompositionChange:
|
||||
return "eCompositionChange";
|
||||
case eCompositionCommitAsIs:
|
||||
return "eCompositionCommitAsIs";
|
||||
case eCompositionCommit:
|
||||
return "eCompositionCommit";
|
||||
case eSetSelection:
|
||||
return "eSetSelection";
|
||||
default:
|
||||
|
@ -1147,7 +1147,7 @@ IMEStateManager::DispatchCompositionEvent(
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_UPDATE,
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionUpdate,
|
||||
"compositionupdate event shouldn't be dispatched manually");
|
||||
|
||||
EnsureTextCompositionArray();
|
||||
|
@ -1163,7 +1163,7 @@ IMEStateManager::DispatchCompositionEvent(
|
|||
MOZ_LOG(sISMLog, LogLevel::Debug,
|
||||
("ISM: IMEStateManager::DispatchCompositionEvent(), "
|
||||
"adding new TextComposition to the array"));
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage == NS_COMPOSITION_START);
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionStart);
|
||||
composition =
|
||||
new TextComposition(aPresContext, aEventTargetNode, tabParent,
|
||||
aCompositionEvent);
|
||||
|
@ -1171,7 +1171,7 @@ IMEStateManager::DispatchCompositionEvent(
|
|||
}
|
||||
#ifdef DEBUG
|
||||
else {
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage != NS_COMPOSITION_START);
|
||||
MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart);
|
||||
}
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
|
@ -1277,7 +1277,7 @@ IMEStateManager::OnCompositionEventDiscarded(
|
|||
|
||||
// Ignore compositionstart for now because sTextCompositions may not have
|
||||
// been created yet.
|
||||
if (aCompositionEvent->mMessage == NS_COMPOSITION_START) {
|
||||
if (aCompositionEvent->mMessage == eCompositionStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ TextComposition::MaybeDispatchCompositionUpdate(
|
|||
if (mLastData == aCompositionEvent->mData) {
|
||||
return true;
|
||||
}
|
||||
CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_UPDATE);
|
||||
CloneAndDispatchAs(aCompositionEvent, eCompositionUpdate);
|
||||
return IsValidStateForComposition(aCompositionEvent->widget);
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ TextComposition::CloneAndDispatchAs(
|
|||
|
||||
nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault;
|
||||
nsEventStatus* status = aStatus ? aStatus : &dummyStatus;
|
||||
if (aMessage == NS_COMPOSITION_UPDATE) {
|
||||
if (aMessage == eCompositionUpdate) {
|
||||
mLastData = compositionEvent.mData;
|
||||
}
|
||||
EventDispatcher::Dispatch(mNode, mPresContext,
|
||||
|
@ -234,12 +234,12 @@ TextComposition::DispatchCompositionEvent(
|
|||
RemoveControlCharactersFrom(aCompositionEvent->mData,
|
||||
aCompositionEvent->mRanges);
|
||||
}
|
||||
if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT_AS_IS) {
|
||||
if (aCompositionEvent->mMessage == eCompositionCommitAsIs) {
|
||||
NS_ASSERTION(!aCompositionEvent->mRanges,
|
||||
"mRanges of NS_COMPOSITION_COMMIT_AS_IS should be null");
|
||||
"mRanges of eCompositionCommitAsIs should be null");
|
||||
aCompositionEvent->mRanges = nullptr;
|
||||
NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
|
||||
"mData of NS_COMPOSITION_COMMIT_AS_IS should be empty string");
|
||||
"mData of eCompositionCommitAsIs should be empty string");
|
||||
if (mLastData == IDEOGRAPHIC_SPACE) {
|
||||
// If the last data is an ideographic space (FullWidth space), it must be
|
||||
// a placeholder character of some Chinese IME. So, committing with
|
||||
|
@ -248,9 +248,9 @@ TextComposition::DispatchCompositionEvent(
|
|||
} else {
|
||||
aCompositionEvent->mData = mLastData;
|
||||
}
|
||||
} else if (aCompositionEvent->mMessage == NS_COMPOSITION_COMMIT) {
|
||||
} else if (aCompositionEvent->mMessage == eCompositionCommit) {
|
||||
NS_ASSERTION(!aCompositionEvent->mRanges,
|
||||
"mRanges of NS_COMPOSITION_COMMIT should be null");
|
||||
"mRanges of eCompositionCommit should be null");
|
||||
aCompositionEvent->mRanges = nullptr;
|
||||
}
|
||||
|
||||
|
@ -284,10 +284,10 @@ TextComposition::DispatchCompositionEvent(
|
|||
if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) {
|
||||
nsString* committingData = nullptr;
|
||||
switch (aCompositionEvent->mMessage) {
|
||||
case NS_COMPOSITION_END:
|
||||
case NS_COMPOSITION_CHANGE:
|
||||
case NS_COMPOSITION_COMMIT_AS_IS:
|
||||
case NS_COMPOSITION_COMMIT:
|
||||
case eCompositionEnd:
|
||||
case eCompositionChange:
|
||||
case eCompositionCommitAsIs:
|
||||
case eCompositionCommit:
|
||||
committingData = &aCompositionEvent->mData;
|
||||
break;
|
||||
default:
|
||||
|
@ -309,20 +309,20 @@ TextComposition::DispatchCompositionEvent(
|
|||
bool dispatchDOMTextEvent = aCompositionEvent->CausesDOMTextEvent();
|
||||
|
||||
// When mIsComposing is false but the committing string is different from
|
||||
// the last data (E.g., previous NS_COMPOSITION_CHANGE event made the
|
||||
// the last data (E.g., previous eCompositionChange event made the
|
||||
// composition string empty or didn't have clause information), we don't
|
||||
// need to dispatch redundant DOM text event.
|
||||
if (dispatchDOMTextEvent &&
|
||||
aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE &&
|
||||
aCompositionEvent->mMessage != eCompositionChange &&
|
||||
!mIsComposing && mLastData == aCompositionEvent->mData) {
|
||||
dispatchEvent = dispatchDOMTextEvent = false;
|
||||
}
|
||||
|
||||
// widget may dispatch redundant NS_COMPOSITION_CHANGE event
|
||||
// widget may dispatch redundant eCompositionChange event
|
||||
// which modifies neither composition string, clauses nor caret
|
||||
// position. In such case, we shouldn't dispatch DOM events.
|
||||
if (dispatchDOMTextEvent &&
|
||||
aCompositionEvent->mMessage == NS_COMPOSITION_CHANGE &&
|
||||
aCompositionEvent->mMessage == eCompositionChange &&
|
||||
mLastData == aCompositionEvent->mData &&
|
||||
mRanges && aCompositionEvent->mRanges &&
|
||||
mRanges->Equals(*aCompositionEvent->mRanges)) {
|
||||
|
@ -337,13 +337,13 @@ TextComposition::DispatchCompositionEvent(
|
|||
|
||||
if (dispatchEvent) {
|
||||
// If the composition event should cause a DOM text event, we should
|
||||
// overwrite the event message as NS_COMPOSITION_CHANGE because due to
|
||||
// overwrite the event message as eCompositionChange because due to
|
||||
// the limitation of mapping between event messages and DOM event types,
|
||||
// we cannot map multiple event messages to a DOM event type.
|
||||
if (dispatchDOMTextEvent &&
|
||||
aCompositionEvent->mMessage != NS_COMPOSITION_CHANGE) {
|
||||
aCompositionEvent->mMessage != eCompositionChange) {
|
||||
aCompositionEvent->mFlags =
|
||||
CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_CHANGE,
|
||||
CloneAndDispatchAs(aCompositionEvent, eCompositionChange,
|
||||
aStatus, aCallBack);
|
||||
} else {
|
||||
EventDispatcher::Dispatch(mNode, mPresContext,
|
||||
|
@ -366,8 +366,8 @@ TextComposition::DispatchCompositionEvent(
|
|||
|
||||
if (aCompositionEvent->CausesDOMCompositionEndEvent()) {
|
||||
// Dispatch a compositionend event if it's necessary.
|
||||
if (aCompositionEvent->mMessage != NS_COMPOSITION_END) {
|
||||
CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_END);
|
||||
if (aCompositionEvent->mMessage != eCompositionEnd) {
|
||||
CloneAndDispatchAs(aCompositionEvent, eCompositionEnd);
|
||||
}
|
||||
MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?");
|
||||
MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
|
||||
|
@ -412,7 +412,7 @@ TextComposition::NotityUpdateComposition(
|
|||
// When compositon start, notify the rect of first offset character.
|
||||
// When not compositon start, notify the rect of selected composition
|
||||
// string if compositionchange event.
|
||||
if (aCompositionEvent->mMessage == NS_COMPOSITION_START) {
|
||||
if (aCompositionEvent->mMessage == eCompositionStart) {
|
||||
nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
|
||||
// Update composition start offset
|
||||
WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
|
||||
|
@ -490,10 +490,10 @@ TextComposition::RequestToCommit(nsIWidget* aWidget, bool aDiscard)
|
|||
// Otherwise, synthesize the commit in content.
|
||||
nsAutoString data(aDiscard ? EmptyString() : lastData);
|
||||
if (data == mLastData) {
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT_AS_IS, EmptyString(),
|
||||
DispatchCompositionEventRunnable(eCompositionCommitAsIs, EmptyString(),
|
||||
true);
|
||||
} else {
|
||||
DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT, data, true);
|
||||
DispatchCompositionEventRunnable(eCompositionCommit, data, true);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -611,8 +611,8 @@ TextComposition::CompositionEventDispatcher::Run()
|
|||
nsRefPtr<nsPresContext> presContext = mTextComposition->mPresContext;
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
switch (mEventMessage) {
|
||||
case NS_COMPOSITION_START: {
|
||||
WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, widget);
|
||||
case eCompositionStart: {
|
||||
WidgetCompositionEvent compStart(true, eCompositionStart, widget);
|
||||
WidgetQueryContentEvent selectedText(true, eQuerySelectedText, widget);
|
||||
ContentEventHandler handler(presContext);
|
||||
handler.OnQuerySelectedText(&selectedText);
|
||||
|
@ -625,11 +625,11 @@ TextComposition::CompositionEventDispatcher::Run()
|
|||
mIsSynthesizedEvent);
|
||||
break;
|
||||
}
|
||||
case NS_COMPOSITION_CHANGE:
|
||||
case NS_COMPOSITION_COMMIT_AS_IS:
|
||||
case NS_COMPOSITION_COMMIT: {
|
||||
case eCompositionChange:
|
||||
case eCompositionCommitAsIs:
|
||||
case eCompositionCommit: {
|
||||
WidgetCompositionEvent compEvent(true, mEventMessage, widget);
|
||||
if (mEventMessage != NS_COMPOSITION_COMMIT_AS_IS) {
|
||||
if (mEventMessage != eCompositionCommitAsIs) {
|
||||
compEvent.mData = mData;
|
||||
}
|
||||
compEvent.mFlags.mIsSynthesizedForTests =
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#include "FetchUtil.h"
|
||||
#include "nsError.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
nsresult
|
||||
FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
|
||||
{
|
||||
nsAutoCString upperCaseMethod(aMethod);
|
||||
ToUpperCase(upperCaseMethod);
|
||||
if (upperCaseMethod.EqualsLiteral("CONNECT") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACE") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACK") ||
|
||||
!NS_IsValidHTTPToken(aMethod)) {
|
||||
outMethod.SetIsVoid(true);
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (upperCaseMethod.EqualsLiteral("DELETE") ||
|
||||
upperCaseMethod.EqualsLiteral("GET") ||
|
||||
upperCaseMethod.EqualsLiteral("HEAD") ||
|
||||
upperCaseMethod.EqualsLiteral("OPTIONS") ||
|
||||
upperCaseMethod.EqualsLiteral("POST") ||
|
||||
upperCaseMethod.EqualsLiteral("PUT")) {
|
||||
outMethod = upperCaseMethod;
|
||||
}
|
||||
else {
|
||||
outMethod = aMethod; // Case unchanged for non-standard methods
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef mozilla_dom_FetchUtil_h
|
||||
#define mozilla_dom_FetchUtil_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsError.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class FetchUtil final
|
||||
{
|
||||
private:
|
||||
FetchUtil() = delete;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Sets outMethod to a valid HTTP request method string based on an input method.
|
||||
* Implements checks and normalization as specified by the Fetch specification.
|
||||
* Returns NS_ERROR_DOM_SECURITY_ERR if the method is invalid.
|
||||
* Otherwise returns NS_OK and the normalized method via outMethod.
|
||||
*/
|
||||
static nsresult
|
||||
GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/FetchUtil.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/URL.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
|
@ -298,34 +299,21 @@ Request::Constructor(const GlobalObject& aGlobal,
|
|||
// Request constructor step 14.
|
||||
if (aInit.mMethod.WasPassed()) {
|
||||
nsAutoCString method(aInit.mMethod.Value());
|
||||
nsAutoCString upperCaseMethod = method;
|
||||
ToUpperCase(upperCaseMethod);
|
||||
|
||||
// Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
|
||||
// token, since HTTP states that Method may be any of the defined values or
|
||||
// a token (extension method).
|
||||
if (upperCaseMethod.EqualsLiteral("CONNECT") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACE") ||
|
||||
upperCaseMethod.EqualsLiteral("TRACK") ||
|
||||
!NS_IsValidHTTPToken(method)) {
|
||||
nsAutoCString outMethod;
|
||||
nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_ConvertUTF8toUTF16 label(method);
|
||||
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Step 14.2
|
||||
if (upperCaseMethod.EqualsLiteral("DELETE") ||
|
||||
upperCaseMethod.EqualsLiteral("GET") ||
|
||||
upperCaseMethod.EqualsLiteral("HEAD") ||
|
||||
upperCaseMethod.EqualsLiteral("POST") ||
|
||||
upperCaseMethod.EqualsLiteral("PUT") ||
|
||||
upperCaseMethod.EqualsLiteral("OPTIONS")) {
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetMethod(upperCaseMethod);
|
||||
} else {
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetMethod(method);
|
||||
}
|
||||
request->ClearCreatedByFetchEvent();
|
||||
request->SetMethod(outMethod);
|
||||
}
|
||||
|
||||
nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
|
||||
|
|
|
@ -8,6 +8,7 @@ EXPORTS.mozilla.dom += [
|
|||
'ChannelInfo.h',
|
||||
'Fetch.h',
|
||||
'FetchDriver.h',
|
||||
'FetchUtil.h',
|
||||
'Headers.h',
|
||||
'InternalHeaders.h',
|
||||
'InternalRequest.h',
|
||||
|
@ -20,6 +21,7 @@ UNIFIED_SOURCES += [
|
|||
'ChannelInfo.cpp',
|
||||
'Fetch.cpp',
|
||||
'FetchDriver.cpp',
|
||||
'FetchUtil.cpp',
|
||||
'Headers.cpp',
|
||||
'InternalHeaders.cpp',
|
||||
'InternalRequest.cpp',
|
||||
|
|
|
@ -3034,8 +3034,8 @@ HTMLInputElement::NeedToInitializeEditorForEvent(
|
|||
case eMouseExitFromWidget:
|
||||
case eMouseOver:
|
||||
case eMouseOut:
|
||||
case NS_SCROLLPORT_UNDERFLOW:
|
||||
case NS_SCROLLPORT_OVERFLOW:
|
||||
case eScrollPortUnderflow:
|
||||
case eScrollPortOverflow:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
|
|
|
@ -17,10 +17,13 @@ interface nsIPrincipal;
|
|||
* endpoint.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(0bcac389-a3ac-44a4-97fb-b50e41a46146)]
|
||||
[scriptable, uuid(dc201064-8e5c-4a26-bd37-d1e33558a903)]
|
||||
interface nsIPushEndpointCallback : nsISupports
|
||||
{
|
||||
void onPushEndpoint(in nsresult status, in DOMString endpoint);
|
||||
void onPushEndpoint(in nsresult status,
|
||||
in DOMString endpoint,
|
||||
in uint32_t keyLen,
|
||||
[array, size_is(keyLen)] in octet key);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "mozilla/layout/RenderFrameChild.h"
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/PWebBrowserPersistDocumentChild.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
@ -599,37 +600,6 @@ TabChild::Create(nsIContentChild* aManager,
|
|||
return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
|
||||
}
|
||||
|
||||
class TabChildSetAllowedTouchBehaviorCallback : public SetAllowedTouchBehaviorCallback {
|
||||
public:
|
||||
explicit TabChildSetAllowedTouchBehaviorCallback(TabChild* aTabChild)
|
||||
: mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
|
||||
{}
|
||||
|
||||
void Run(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aFlags) const override {
|
||||
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
|
||||
static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
nsWeakPtr mTabChild;
|
||||
};
|
||||
|
||||
class TabChildContentReceivedInputBlockCallback : public ContentReceivedInputBlockCallback {
|
||||
public:
|
||||
explicit TabChildContentReceivedInputBlockCallback(TabChild* aTabChild)
|
||||
: mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
|
||||
{}
|
||||
|
||||
void Run(const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId, bool aPreventDefault) const override {
|
||||
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
|
||||
static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
|
||||
}
|
||||
}
|
||||
private:
|
||||
nsWeakPtr mTabChild;
|
||||
};
|
||||
|
||||
TabChild::TabChild(nsIContentChild* aManager,
|
||||
const TabId& aTabId,
|
||||
const TabContext& aContext,
|
||||
|
@ -649,7 +619,6 @@ TabChild::TabChild(nsIContentChild* aManager,
|
|||
, mOrientation(eScreenOrientation_PortraitPrimary)
|
||||
, mUpdateHitRegion(false)
|
||||
, mIgnoreKeyPressEvent(false)
|
||||
, mSetAllowedTouchBehaviorCallback(new TabChildSetAllowedTouchBehaviorCallback(this))
|
||||
, mHasValidInnerSize(false)
|
||||
, mDestroyed(false)
|
||||
, mUniqueId(aTabId)
|
||||
|
@ -665,6 +634,15 @@ TabChild::TabChild(nsIContentChild* aManager,
|
|||
// check the other conditions necessary for enabling APZ.
|
||||
mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
|
||||
|
||||
nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this))); // for capture by the lambda
|
||||
mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
|
||||
const nsTArray<TouchBehaviorFlags>& aFlags)
|
||||
{
|
||||
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
|
||||
static_cast<TabChild*>(tabChild.get())->SendSetAllowedTouchBehavior(aInputBlockId, aFlags);
|
||||
}
|
||||
};
|
||||
|
||||
// preloaded TabChild should not be added to child map
|
||||
if (mUniqueId) {
|
||||
MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
|
||||
|
@ -860,8 +838,17 @@ TabChild::Init()
|
|||
do_QueryInterface(window->GetChromeEventHandler());
|
||||
docShell->SetChromeEventHandler(chromeHandler);
|
||||
|
||||
mAPZEventState = new APZEventState(mPuppetWidget,
|
||||
new TabChildContentReceivedInputBlockCallback(this));
|
||||
nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this)); // for capture by the lambda
|
||||
ContentReceivedInputBlockCallback callback(
|
||||
[weakPtrThis](const ScrollableLayerGuid& aGuid,
|
||||
uint64_t aInputBlockId,
|
||||
bool aPreventDefault)
|
||||
{
|
||||
if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
|
||||
static_cast<TabChild*>(tabChild.get())->SendContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
|
||||
}
|
||||
});
|
||||
mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/layers/CompositorTypes.h"
|
||||
#include "mozilla/layers/APZCCallbackHelper.h"
|
||||
#include "nsIWebBrowserChrome3.h"
|
||||
#include "mozilla/dom/ipc/IdType.h"
|
||||
#include "AudioChannelService.h"
|
||||
|
@ -48,8 +49,6 @@ class RenderFrameChild;
|
|||
namespace layers {
|
||||
class APZEventState;
|
||||
class ImageCompositeNotification;
|
||||
struct SetTargetAPZCCallback;
|
||||
struct SetAllowedTouchBehaviorCallback;
|
||||
} // namespace layers
|
||||
|
||||
namespace widget {
|
||||
|
@ -230,7 +229,6 @@ class TabChild final : public TabChildBase,
|
|||
typedef mozilla::dom::ClonedMessageData ClonedMessageData;
|
||||
typedef mozilla::layout::RenderFrameChild RenderFrameChild;
|
||||
typedef mozilla::layers::APZEventState APZEventState;
|
||||
typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
|
||||
typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback;
|
||||
|
||||
public:
|
||||
|
@ -635,7 +633,7 @@ private:
|
|||
|
||||
bool mIgnoreKeyPressEvent;
|
||||
nsRefPtr<APZEventState> mAPZEventState;
|
||||
nsRefPtr<SetAllowedTouchBehaviorCallback> mSetAllowedTouchBehaviorCallback;
|
||||
SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
|
||||
bool mHasValidInnerSize;
|
||||
bool mDestroyed;
|
||||
// Position of tab, relative to parent widget (typically the window)
|
||||
|
|
|
@ -2369,7 +2369,7 @@ TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent)
|
|||
return true;
|
||||
}
|
||||
switch (aEvent.mMessage) {
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
case eQueryTextRect:
|
||||
case eQueryCaretRect:
|
||||
case eQueryEditorRect:
|
||||
aEvent.mReply.mRect -= GetChildProcessOffset();
|
||||
|
|
|
@ -124,9 +124,12 @@ AudioBlock::ClearDownstreamMark() {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioBlock::AssertNoLastingShares() {
|
||||
MOZ_ASSERT(!mBuffer->AsAudioBlockBuffer()->HasLastingShares());
|
||||
bool
|
||||
AudioBlock::CanWrite() {
|
||||
// If mBufferIsDownstreamRef is set then the buffer is not ours to use.
|
||||
// It may be in use by another node which is not downstream.
|
||||
return !mBufferIsDownstreamRef &&
|
||||
!mBuffer->AsAudioBlockBuffer()->HasLastingShares();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -24,9 +24,16 @@ public:
|
|||
AudioBlock() {
|
||||
mDuration = WEBAUDIO_BLOCK_SIZE;
|
||||
}
|
||||
MOZ_IMPLICIT AudioBlock(const AudioChunk& aChunk) {
|
||||
mDuration = WEBAUDIO_BLOCK_SIZE;
|
||||
operator=(aChunk);
|
||||
// No effort is made in constructors to ensure that mBufferIsDownstreamRef
|
||||
// is set because the block is expected to be a temporary and so the
|
||||
// reference will be released before the next iteration.
|
||||
// The custom copy constructor is required so as not to set
|
||||
// mBufferIsDownstreamRef without notifying AudioBlockBuffer.
|
||||
AudioBlock(const AudioBlock& aBlock) : AudioChunk(aBlock.AsAudioChunk()) {}
|
||||
explicit AudioBlock(const AudioChunk& aChunk)
|
||||
: AudioChunk(aChunk)
|
||||
{
|
||||
MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
~AudioBlock();
|
||||
|
||||
|
@ -44,7 +51,7 @@ public:
|
|||
|
||||
const AudioChunk& AsAudioChunk() const { return *this; }
|
||||
AudioChunk* AsMutableChunk() {
|
||||
void ClearDownstreamMark();
|
||||
ClearDownstreamMark();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -54,12 +61,14 @@ public:
|
|||
*/
|
||||
void AllocateChannels(uint32_t aChannelCount);
|
||||
|
||||
/**
|
||||
* ChannelFloatsForWrite() should only be used when the buffers have been
|
||||
* created with AllocateChannels().
|
||||
*/
|
||||
float* ChannelFloatsForWrite(size_t aChannel)
|
||||
{
|
||||
MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
|
||||
#if DEBUG
|
||||
AssertNoLastingShares();
|
||||
#endif
|
||||
MOZ_ASSERT(CanWrite());
|
||||
return static_cast<float*>(const_cast<void*>(mChannelData[aChannel]));
|
||||
}
|
||||
|
||||
|
@ -72,6 +81,12 @@ public:
|
|||
mBufferFormat = AUDIO_FORMAT_SILENCE;
|
||||
}
|
||||
|
||||
AudioBlock& operator=(const AudioBlock& aBlock) {
|
||||
// Instead of just copying, mBufferIsDownstreamRef must be first cleared
|
||||
// if set. It is set again for the new mBuffer if possible. This happens
|
||||
// in SetBuffer().
|
||||
return *this = aBlock.AsAudioChunk();
|
||||
}
|
||||
AudioBlock& operator=(const AudioChunk& aChunk) {
|
||||
MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE);
|
||||
SetBuffer(aChunk.mBuffer);
|
||||
|
@ -103,7 +118,7 @@ public:
|
|||
|
||||
private:
|
||||
void ClearDownstreamMark();
|
||||
void AssertNoLastingShares();
|
||||
bool CanWrite();
|
||||
|
||||
// mBufferIsDownstreamRef is set only when mBuffer references an
|
||||
// AudioBlockBuffer created in a different AudioBlock. That can happen when
|
||||
|
|
|
@ -53,6 +53,16 @@ AudioNodeStream::~AudioNodeStream()
|
|||
MOZ_COUNT_DTOR(AudioNodeStream);
|
||||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::DestroyImpl()
|
||||
{
|
||||
// These are graph thread objects, so clean up on graph thread.
|
||||
mInputChunks.Clear();
|
||||
mLastChunks.Clear();
|
||||
|
||||
ProcessedMediaStream::DestroyImpl();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioNodeStream>
|
||||
AudioNodeStream::Create(MediaStreamGraph* aGraph, AudioNodeEngine* aEngine,
|
||||
Flags aFlags)
|
||||
|
|
|
@ -169,6 +169,8 @@ public:
|
|||
|
||||
|
||||
protected:
|
||||
virtual void DestroyImpl() override;
|
||||
|
||||
void AdvanceOutputSegment();
|
||||
void FinishOutput();
|
||||
void AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk,
|
||||
|
|
|
@ -1368,7 +1368,7 @@ Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (mGlobal->IsDying()) {
|
||||
if (!mGlobal || mGlobal->IsDying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,10 +121,22 @@ Push.prototype = {
|
|||
() => {
|
||||
fn(that._scope, that._principal, {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPushEndpointCallback]),
|
||||
onPushEndpoint: function(ok, endpoint) {
|
||||
onPushEndpoint: function(ok, endpoint, keyLen, key) {
|
||||
if (ok === Cr.NS_OK) {
|
||||
if (endpoint) {
|
||||
let sub = new that._window.PushSubscription(endpoint, that._scope);
|
||||
let sub;
|
||||
if (keyLen) {
|
||||
let publicKey = new ArrayBuffer(keyLen);
|
||||
let keyView = new Uint8Array(publicKey);
|
||||
keyView.set(key);
|
||||
sub = new that._window.PushSubscription(endpoint,
|
||||
that._scope,
|
||||
publicKey);
|
||||
} else {
|
||||
sub = new that._window.PushSubscription(endpoint,
|
||||
that._scope,
|
||||
null);
|
||||
}
|
||||
sub.setPrincipal(that._principal);
|
||||
resolve(sub);
|
||||
} else {
|
||||
|
|
|
@ -100,6 +100,19 @@ PushClient.prototype = {
|
|||
}, null, principal);
|
||||
},
|
||||
|
||||
_deliverPushEndpoint: function(request, registration) {
|
||||
if (registration.p256dhKey) {
|
||||
let key = new Uint8Array(registration.p256dhKey);
|
||||
request.onPushEndpoint(Cr.NS_OK,
|
||||
registration.pushEndpoint,
|
||||
key.length,
|
||||
key);
|
||||
return;
|
||||
}
|
||||
|
||||
request.onPushEndpoint(Cr.NS_OK, registration.pushEndpoint, 0, null);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
|
||||
let json = aMessage.data;
|
||||
|
@ -112,23 +125,23 @@ PushClient.prototype = {
|
|||
debug("receiveMessage(): " + JSON.stringify(aMessage))
|
||||
switch (aMessage.name) {
|
||||
case "PushService:Register:OK":
|
||||
{
|
||||
request.onPushEndpoint(Cr.NS_OK, json.pushEndpoint);
|
||||
this._deliverPushEndpoint(request, json);
|
||||
break;
|
||||
}
|
||||
case "PushService:Register:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "");
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
case "PushService:Registration:OK":
|
||||
{
|
||||
let endpoint = "";
|
||||
if (json.registration)
|
||||
endpoint = json.registration.pushEndpoint;
|
||||
request.onPushEndpoint(Cr.NS_OK, endpoint);
|
||||
if (!json.registration) {
|
||||
request.onPushEndpoint(Cr.NS_OK, "", 0, null);
|
||||
} else {
|
||||
this._deliverPushEndpoint(request, json.registration);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "PushService:Registration:KO":
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "");
|
||||
request.onPushEndpoint(Cr.NS_ERROR_FAILURE, "", 0, null);
|
||||
break;
|
||||
case "PushService:Unregister:OK":
|
||||
if (typeof json.result !== "boolean") {
|
||||
|
|
|
@ -90,13 +90,18 @@ PushSubscription::Unsubscribe(ErrorResult& aRv)
|
|||
|
||||
PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope)
|
||||
: mGlobal(aGlobal), mEndpoint(aEndpoint), mScope(aScope)
|
||||
const nsAString& aScope,
|
||||
const nsTArray<uint8_t>& aRawP256dhKey)
|
||||
: mGlobal(aGlobal)
|
||||
, mEndpoint(aEndpoint)
|
||||
, mScope(aScope)
|
||||
, mRawP256dhKey(aRawP256dhKey)
|
||||
{
|
||||
}
|
||||
|
||||
PushSubscription::~PushSubscription()
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
JSObject*
|
||||
PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
|
@ -104,6 +109,20 @@ PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
PushSubscription::GetKey(JSContext* aCx,
|
||||
PushEncryptionKeyName aType,
|
||||
JS::MutableHandle<JSObject*> aP256dhKey)
|
||||
{
|
||||
if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
|
||||
aP256dhKey.set(ArrayBuffer::Create(aCx,
|
||||
mRawP256dhKey.Length(),
|
||||
mRawP256dhKey.Elements()));
|
||||
} else {
|
||||
aP256dhKey.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PushSubscription::SetPrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
|
@ -113,16 +132,34 @@ PushSubscription::SetPrincipal(nsIPrincipal* aPrincipal)
|
|||
|
||||
// static
|
||||
already_AddRefed<PushSubscription>
|
||||
PushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv)
|
||||
PushSubscription::Constructor(GlobalObject& aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope,
|
||||
const Nullable<ArrayBuffer>& aP256dhKey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(!aEndpoint.IsEmpty());
|
||||
MOZ_ASSERT(!aScope.IsEmpty());
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
nsRefPtr<PushSubscription> sub = new PushSubscription(global, aEndpoint, aScope);
|
||||
|
||||
nsTArray<uint8_t> rawKey;
|
||||
if (!aP256dhKey.IsNull()) {
|
||||
const ArrayBuffer& key = aP256dhKey.Value();
|
||||
key.ComputeLengthAndData();
|
||||
rawKey.SetLength(key.Length());
|
||||
rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
|
||||
}
|
||||
nsRefPtr<PushSubscription> sub = new PushSubscription(global,
|
||||
aEndpoint,
|
||||
aScope,
|
||||
rawKey);
|
||||
|
||||
return sub.forget();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mPrincipal)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
|
||||
|
@ -188,8 +225,9 @@ NS_INTERFACE_MAP_END
|
|||
// WorkerPushSubscription
|
||||
|
||||
WorkerPushSubscription::WorkerPushSubscription(const nsAString& aEndpoint,
|
||||
const nsAString& aScope)
|
||||
: mEndpoint(aEndpoint), mScope(aScope)
|
||||
const nsAString& aScope,
|
||||
const nsTArray<uint8_t>& aRawP256dhKey)
|
||||
: mEndpoint(aEndpoint), mScope(aScope), mRawP256dhKey(aRawP256dhKey)
|
||||
{
|
||||
MOZ_ASSERT(!aScope.IsEmpty());
|
||||
MOZ_ASSERT(!aEndpoint.IsEmpty());
|
||||
|
@ -206,16 +244,45 @@ WorkerPushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenP
|
|||
|
||||
// static
|
||||
already_AddRefed<WorkerPushSubscription>
|
||||
WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv)
|
||||
WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope,
|
||||
const Nullable<ArrayBuffer>& aP256dhKey,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint, aScope);
|
||||
nsTArray<uint8_t> rawKey;
|
||||
if (!aP256dhKey.IsNull()) {
|
||||
const ArrayBuffer& key = aP256dhKey.Value();
|
||||
key.ComputeLengthAndData();
|
||||
rawKey.SetLength(key.Length());
|
||||
rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
|
||||
}
|
||||
nsRefPtr<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint,
|
||||
aScope,
|
||||
rawKey);
|
||||
|
||||
return sub.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerPushSubscription::GetKey(JSContext* aCx,
|
||||
PushEncryptionKeyName aType,
|
||||
JS::MutableHandle<JSObject*> aP256dhKey)
|
||||
{
|
||||
if (aType == mozilla::dom::PushEncryptionKeyName::P256dh &&
|
||||
!mRawP256dhKey.IsEmpty()) {
|
||||
aP256dhKey.set(ArrayBuffer::Create(aCx,
|
||||
mRawP256dhKey.Length(),
|
||||
mRawP256dhKey.Elements()));
|
||||
} else {
|
||||
aP256dhKey.set(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
class UnsubscribeResultRunnable final : public WorkerRunnable
|
||||
{
|
||||
public:
|
||||
|
@ -371,6 +438,7 @@ WorkerPushSubscription::Unsubscribe(ErrorResult &aRv)
|
|||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushSubscription)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushSubscription)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushSubscription)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushSubscription)
|
||||
|
@ -397,12 +465,14 @@ public:
|
|||
GetSubscriptionResultRunnable(PromiseWorkerProxy* aProxy,
|
||||
nsresult aStatus,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope)
|
||||
const nsAString& aScope,
|
||||
const nsTArray<uint8_t>& aRawP256dhKey)
|
||||
: WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
|
||||
, mProxy(aProxy)
|
||||
, mStatus(aStatus)
|
||||
, mEndpoint(aEndpoint)
|
||||
, mScope(aScope)
|
||||
, mRawP256dhKey(aRawP256dhKey)
|
||||
{ }
|
||||
|
||||
bool
|
||||
|
@ -414,7 +484,7 @@ public:
|
|||
promise->MaybeResolve(JS::NullHandleValue);
|
||||
} else {
|
||||
nsRefPtr<WorkerPushSubscription> sub =
|
||||
new WorkerPushSubscription(mEndpoint, mScope);
|
||||
new WorkerPushSubscription(mEndpoint, mScope, mRawP256dhKey);
|
||||
promise->MaybeResolve(sub);
|
||||
}
|
||||
} else {
|
||||
|
@ -432,6 +502,7 @@ private:
|
|||
nsresult mStatus;
|
||||
nsString mEndpoint;
|
||||
nsString mScope;
|
||||
nsTArray<uint8_t> mRawP256dhKey;
|
||||
};
|
||||
|
||||
class GetSubscriptionCallback final : public nsIPushEndpointCallback
|
||||
|
@ -446,7 +517,10 @@ public:
|
|||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
|
||||
OnPushEndpoint(nsresult aStatus,
|
||||
const nsAString& aEndpoint,
|
||||
uint32_t aKeyLen,
|
||||
uint8_t* aKey) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
|
||||
|
@ -461,8 +535,15 @@ public:
|
|||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
nsTArray<uint8_t> rawP256dhKey(aKeyLen);
|
||||
rawP256dhKey.ReplaceElementsAt(0, aKeyLen, aKey, aKeyLen);
|
||||
|
||||
nsRefPtr<GetSubscriptionResultRunnable> r =
|
||||
new GetSubscriptionResultRunnable(proxy, aStatus, aEndpoint, mScope);
|
||||
new GetSubscriptionResultRunnable(proxy,
|
||||
aStatus,
|
||||
aEndpoint,
|
||||
mScope,
|
||||
rawP256dhKey);
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -502,7 +583,7 @@ public:
|
|||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
mozilla::services::GetPermissionManager();
|
||||
if (!permManager) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -515,14 +596,14 @@ public:
|
|||
&permission);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || permission != nsIPermissionManager::ALLOW_ACTION) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPushClient> client =
|
||||
do_CreateInstance("@mozilla.org/push/PushClient;1");
|
||||
if (!client) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -534,7 +615,7 @@ public:
|
|||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
|
||||
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString(), 0, nullptr);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
|
@ -44,6 +45,8 @@
|
|||
class nsIGlobalObject;
|
||||
class nsIPrincipal;
|
||||
|
||||
#include "mozilla/dom/PushSubscriptionBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -63,7 +66,8 @@ public:
|
|||
|
||||
explicit PushSubscription(nsIGlobalObject* aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope);
|
||||
const nsAString& aScope,
|
||||
const nsTArray<uint8_t>& aP256dhKey);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -80,8 +84,17 @@ public:
|
|||
aEndpoint = mEndpoint;
|
||||
}
|
||||
|
||||
void
|
||||
GetKey(JSContext* cx,
|
||||
PushEncryptionKeyName aType,
|
||||
JS::MutableHandle<JSObject*> aP256dhKey);
|
||||
|
||||
static already_AddRefed<PushSubscription>
|
||||
Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv);
|
||||
Constructor(GlobalObject& aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope,
|
||||
const Nullable<ArrayBuffer>& aP256dhKey,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SetPrincipal(nsIPrincipal* aPrincipal);
|
||||
|
@ -97,6 +110,7 @@ private:
|
|||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsString mEndpoint;
|
||||
nsString mScope;
|
||||
nsTArray<uint8_t> mRawP256dhKey;
|
||||
};
|
||||
|
||||
class PushManager final : public nsISupports
|
||||
|
@ -146,7 +160,8 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushSubscription)
|
||||
|
||||
explicit WorkerPushSubscription(const nsAString& aEndpoint,
|
||||
const nsAString& aScope);
|
||||
const nsAString& aScope,
|
||||
const nsTArray<uint8_t>& aRawP256dhKey);
|
||||
|
||||
nsIGlobalObject*
|
||||
GetParentObject() const
|
||||
|
@ -158,7 +173,11 @@ public:
|
|||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<WorkerPushSubscription>
|
||||
Constructor(GlobalObject& aGlobal, const nsAString& aEndpoint, const nsAString& aScope, ErrorResult& aRv);
|
||||
Constructor(GlobalObject& aGlobal,
|
||||
const nsAString& aEndpoint,
|
||||
const nsAString& aScope,
|
||||
const Nullable<ArrayBuffer>& aP256dhKey,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
GetEndpoint(nsAString& aEndpoint) const
|
||||
|
@ -166,6 +185,10 @@ public:
|
|||
aEndpoint = mEndpoint;
|
||||
}
|
||||
|
||||
void
|
||||
GetKey(JSContext* cx, PushEncryptionKeyName aType,
|
||||
JS::MutableHandle<JSObject*> aP256dhKey);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Unsubscribe(ErrorResult& aRv);
|
||||
|
||||
|
@ -175,6 +198,7 @@ protected:
|
|||
private:
|
||||
nsString mEndpoint;
|
||||
nsString mScope;
|
||||
nsTArray<uint8_t> mRawP256dhKey;
|
||||
};
|
||||
|
||||
class WorkerPushManager final : public nsISupports
|
||||
|
|
|
@ -719,6 +719,11 @@ this.PushService = {
|
|||
.then(record => this._notifySubscriptionChangeObservers(record));
|
||||
},
|
||||
|
||||
updateRecordAndNotifyApp: function(aKeyID, aUpdateFunc) {
|
||||
return this._db.update(aKeyID, aUpdateFunc)
|
||||
.then(record => this._notifySubscriptionChangeObservers(record));
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches an incoming message to a service worker, recalculating the
|
||||
* quota for the associated push registration. If the quota is exceeded,
|
||||
|
@ -737,7 +742,7 @@ this.PushService = {
|
|||
debug("receivedPushMessage()");
|
||||
|
||||
let shouldNotify = false;
|
||||
this.getByKeyID(keyID).then(record => {
|
||||
return this.getByKeyID(keyID).then(record => {
|
||||
if (!record) {
|
||||
throw new Error("No record for key ID " + keyID);
|
||||
}
|
||||
|
@ -761,11 +766,13 @@ this.PushService = {
|
|||
return newRecord;
|
||||
});
|
||||
}).then(record => {
|
||||
var notified = false;
|
||||
if (!record) {
|
||||
return null;
|
||||
return notified;
|
||||
}
|
||||
|
||||
if (shouldNotify) {
|
||||
this._notifyApp(record, message);
|
||||
notified = this._notifyApp(record, message);
|
||||
}
|
||||
if (record.isExpired()) {
|
||||
// Drop the registration in the background. If the user returns to the
|
||||
|
@ -775,6 +782,7 @@ this.PushService = {
|
|||
debug("receivedPushMessage: Unregister error: " + error);
|
||||
});
|
||||
}
|
||||
return notified;
|
||||
}).catch(error => {
|
||||
debug("receivedPushMessage: Error notifying app: " + error);
|
||||
});
|
||||
|
@ -785,7 +793,7 @@ this.PushService = {
|
|||
aPushRecord.originAttributes === undefined) {
|
||||
debug("notifyApp() something is undefined. Dropping notification: " +
|
||||
JSON.stringify(aPushRecord) );
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("notifyApp() " + aPushRecord.scope);
|
||||
|
@ -807,7 +815,7 @@ this.PushService = {
|
|||
// If permission has been revoked, trash the message.
|
||||
if (!aPushRecord.hasPermission()) {
|
||||
debug("Does not have permission for push.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO data.
|
||||
|
@ -818,6 +826,7 @@ this.PushService = {
|
|||
};
|
||||
|
||||
this._notifyListeners('push', data);
|
||||
return true;
|
||||
},
|
||||
|
||||
getByKeyID: function(aKeyID) {
|
||||
|
|
|
@ -19,6 +19,9 @@ Cu.import("resource://gre/modules/Timer.jsm");
|
|||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
const {PushServiceHttp2Crypto, concatArray} =
|
||||
Cu.import("resource://gre/modules/PushServiceHttp2Crypto.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["PushServiceHttp2"];
|
||||
|
||||
const prefs = new Preferences("dom.push.");
|
||||
|
@ -34,7 +37,7 @@ function debug(s) {
|
|||
}
|
||||
|
||||
const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
|
||||
const kPUSHHTTP2DB_DB_VERSION = 4; // Change this if the IndexedDB format changes
|
||||
const kPUSHHTTP2DB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
|
||||
|
||||
/**
|
||||
|
@ -114,13 +117,12 @@ PushSubscriptionListener.prototype = {
|
|||
var PushChannelListener = function(pushSubscriptionListener) {
|
||||
debug("Creating a new push channel listener.");
|
||||
this._mainListener = pushSubscriptionListener;
|
||||
this._message = [];
|
||||
this._ackUri = null;
|
||||
};
|
||||
|
||||
PushChannelListener.prototype = {
|
||||
|
||||
_message: null,
|
||||
_ackUri: null,
|
||||
|
||||
onStartRequest: function(aRequest, aContext) {
|
||||
this._ackUri = aRequest.URI.spec;
|
||||
},
|
||||
|
@ -132,15 +134,13 @@ PushChannelListener.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
let inputStream = Cc["@mozilla.org/binaryinputstream;1"]
|
||||
.createInstance(Ci.nsIBinaryInputStream);
|
||||
|
||||
inputStream.init(aStream);
|
||||
if (!this._message) {
|
||||
this._message = inputStream.read(aCount);
|
||||
} else {
|
||||
this._message.concat(inputStream.read(aCount));
|
||||
}
|
||||
inputStream.setInputStream(aStream);
|
||||
let chunk = new ArrayBuffer(aCount);
|
||||
inputStream.readArrayBuffer(aCount, chunk);
|
||||
this._message.push(chunk);
|
||||
},
|
||||
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
|
@ -148,13 +148,76 @@ PushChannelListener.prototype = {
|
|||
if (Components.isSuccessCode(aStatusCode) &&
|
||||
this._mainListener &&
|
||||
this._mainListener._pushService) {
|
||||
|
||||
var keymap = encryptKeyFieldParser(aRequest);
|
||||
if (!keymap) {
|
||||
return;
|
||||
}
|
||||
var enc = encryptFieldParser(aRequest);
|
||||
if (!enc || !enc.keyid) {
|
||||
return;
|
||||
}
|
||||
var dh = keymap[enc.keyid];
|
||||
var salt = enc.salt;
|
||||
var rs = (enc.rs)? parseInt(enc.rs, 10) : 4096;
|
||||
if (!dh || !salt || isNaN(rs) || (rs <= 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = concatArray(this._message);
|
||||
|
||||
this._mainListener._pushService._pushChannelOnStop(this._mainListener.uri,
|
||||
this._ackUri,
|
||||
this._message);
|
||||
msg,
|
||||
dh,
|
||||
salt,
|
||||
rs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var parseHeaderFieldParams = (m, v) => {
|
||||
var i = v.indexOf('=');
|
||||
if (i >= 0) {
|
||||
// A quoted string with internal quotes is invalid for all the possible
|
||||
// values of this header field.
|
||||
m[v.substring(0, i).trim()] = v.substring(i + 1).trim()
|
||||
.replace(/^"(.*)"$/, '$1');
|
||||
}
|
||||
return m;
|
||||
};
|
||||
|
||||
function encryptKeyFieldParser(aRequest) {
|
||||
try {
|
||||
var encryptKeyField = aRequest.getRequestHeader("Encryption-Key");
|
||||
|
||||
var params = encryptKeyField.split(',');
|
||||
return params.reduce((m, p) => {
|
||||
var pmap = p.split(';').reduce(parseHeaderFieldParams, {});
|
||||
if (pmap.keyid && pmap.dh) {
|
||||
m[pmap.keyid] = pmap.dh;
|
||||
}
|
||||
return m;
|
||||
}, {});
|
||||
|
||||
} catch(e) {
|
||||
// getRequestHeader can throw.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function encryptFieldParser(aRequest) {
|
||||
try {
|
||||
return aRequest.getRequestHeader("Encryption")
|
||||
.split(',', 1)[0]
|
||||
.split(';')
|
||||
.reduce(parseHeaderFieldParams, {});
|
||||
} catch(e) {
|
||||
// getRequestHeader can throw.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var PushServiceDelete = function(resolve, reject) {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
|
@ -188,9 +251,12 @@ PushServiceDelete.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
var SubscriptionListener = function(aSubInfo, aServerURI, aPushServiceHttp2) {
|
||||
var SubscriptionListener = function(aSubInfo, aResolve, aReject,
|
||||
aServerURI, aPushServiceHttp2) {
|
||||
debug("Creating a new subscription listener.");
|
||||
this._subInfo = aSubInfo;
|
||||
this._resolve = aResolve;
|
||||
this._reject = aReject;
|
||||
this._data = '';
|
||||
this._serverURI = aServerURI;
|
||||
this._service = aPushServiceHttp2;
|
||||
|
@ -221,12 +287,12 @@ SubscriptionListener.prototype = {
|
|||
|
||||
// Check if pushService is still active.
|
||||
if (!this._service.hasmainPushService()) {
|
||||
this._subInfo.reject({error: "Service deactivated"});
|
||||
this._reject({error: "Service deactivated"});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Components.isSuccessCode(aStatus)) {
|
||||
this._subInfo.reject({error: "Error status" + aStatus});
|
||||
this._reject({error: "Error status" + aStatus});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -236,15 +302,18 @@ SubscriptionListener.prototype = {
|
|||
if (this._subInfo.retries < prefs.get("http2.maxRetries")) {
|
||||
this._subInfo.retries++;
|
||||
var retryAfter = retryAfterParser(aRequest);
|
||||
setTimeout(this._service.retrySubscription.bind(this._service,
|
||||
this._subInfo),
|
||||
retryAfter);
|
||||
setTimeout(_ => this._reject(
|
||||
{
|
||||
retry: true,
|
||||
subInfo: this._subInfo
|
||||
}),
|
||||
retryAfter);
|
||||
} else {
|
||||
this._subInfo.reject({error: "Error response code: " + statusCode });
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
}
|
||||
return;
|
||||
} else if (statusCode != 201) {
|
||||
this._subInfo.reject({error: "Error response code: " + statusCode });
|
||||
this._reject({error: "Error response code: " + statusCode });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -252,7 +321,7 @@ SubscriptionListener.prototype = {
|
|||
try {
|
||||
subscriptionUri = aRequest.getResponseHeader("location");
|
||||
} catch (err) {
|
||||
this._subInfo.reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -262,27 +331,27 @@ SubscriptionListener.prototype = {
|
|||
try {
|
||||
linkList = aRequest.getResponseHeader("link");
|
||||
} catch (err) {
|
||||
this._subInfo.reject({error: "Return code 201, but the answer is bogus"});
|
||||
this._reject({error: "Return code 201, but the answer is bogus"});
|
||||
return;
|
||||
}
|
||||
|
||||
var linkParserResult = linkParser(linkList, this._serverURI);
|
||||
if (linkParserResult.error) {
|
||||
this._subInfo.reject(linkParserResult);
|
||||
this._reject(linkParserResult);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subscriptionUri) {
|
||||
this._subInfo.reject({error: "Return code 201, but the answer is bogus," +
|
||||
" missing subscriptionUri"});
|
||||
this._reject({error: "Return code 201, but the answer is bogus," +
|
||||
" missing subscriptionUri"});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let uriTry = Services.io.newURI(subscriptionUri, null, null);
|
||||
} catch (e) {
|
||||
debug("Invalid URI " + subscriptionUri);
|
||||
this._subInfo.reject({error: "Return code 201, but URI is bogus. " +
|
||||
subscriptionUri});
|
||||
this._reject({error: "Return code 201, but URI is bogus. " +
|
||||
subscriptionUri});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -295,7 +364,7 @@ SubscriptionListener.prototype = {
|
|||
quota: this._subInfo.record.maxQuota,
|
||||
});
|
||||
|
||||
this._subInfo.resolve(reply);
|
||||
this._resolve(reply);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -456,41 +525,53 @@ this.PushServiceHttp2 = {
|
|||
_subscribeResource: function(aRecord) {
|
||||
debug("subscribeResource()");
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._subscribeResourceInternal({record: aRecord,
|
||||
resolve,
|
||||
reject,
|
||||
retries: 0});
|
||||
return this._subscribeResourceInternal({
|
||||
record: aRecord,
|
||||
retries: 0
|
||||
})
|
||||
.then(result => {
|
||||
this._conns[result.subscriptionUri] = {channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
lastStartListening: 0,
|
||||
waitingForAlarm: false};
|
||||
this._listenForMsgs(result.subscriptionUri);
|
||||
return result;
|
||||
});
|
||||
.then(result =>
|
||||
PushServiceHttp2Crypto.generateKeys()
|
||||
.then(exportedKeys => {
|
||||
result.p256dhPublicKey = exportedKeys[0];
|
||||
result.p256dhPrivateKey = exportedKeys[1];
|
||||
this._conns[result.subscriptionUri] = {
|
||||
channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
lastStartListening: 0,
|
||||
waitingForAlarm: false
|
||||
};
|
||||
this._listenForMsgs(result.subscriptionUri);
|
||||
return result;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_subscribeResourceInternal: function(aSubInfo) {
|
||||
debug("subscribeResource()");
|
||||
debug("subscribeResourceInternal()");
|
||||
|
||||
var listener = new SubscriptionListener(aSubInfo,
|
||||
this._serverURI,
|
||||
this);
|
||||
return new Promise((resolve, reject) => {
|
||||
var listener = new SubscriptionListener(aSubInfo,
|
||||
resolve,
|
||||
reject,
|
||||
this._serverURI,
|
||||
this);
|
||||
|
||||
var chan = this._makeChannel(this._serverURI.spec);
|
||||
chan.requestMethod = "POST";
|
||||
try{
|
||||
chan.asyncOpen(listener, null);
|
||||
} catch(e) {
|
||||
aSubInfo.reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
},
|
||||
|
||||
retrySubscription: function(aSubInfo) {
|
||||
this._subscribeResourceInternal(aSubInfo);
|
||||
var chan = this._makeChannel(this._serverURI.spec);
|
||||
chan.requestMethod = "POST";
|
||||
try {
|
||||
chan.asyncOpen(listener, null);
|
||||
} catch(e) {
|
||||
reject({status: 0, error: "NetworkError"});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if ("retry" in err) {
|
||||
return this._subscribeResourceInternal(err.subInfo);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
_deleteResource: function(aUri) {
|
||||
|
@ -640,19 +721,51 @@ this.PushServiceHttp2 = {
|
|||
|
||||
for (let i = 0; i < aSubscriptions.length; i++) {
|
||||
let record = aSubscriptions[i];
|
||||
if (typeof this._conns[record.subscriptionUri] != "object") {
|
||||
this._conns[record.subscriptionUri] = {channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
waitingForAlarm: false};
|
||||
}
|
||||
if (!this._conns[record.subscriptionUri].conn) {
|
||||
this._conns[record.subscriptionUri].waitingForAlarm = false;
|
||||
this._listenForMsgs(record.subscriptionUri);
|
||||
if (record.p256dhPublicKey && record.p256dhPrivateKey) {
|
||||
this._startSingleConnection(record);
|
||||
} else {
|
||||
// We do not have a encryption key. so we need to generate it. This
|
||||
// is only going to happen on db upgrade from version 4 to higher.
|
||||
PushServiceHttp2Crypto.generateKeys()
|
||||
.then(exportedKeys => {
|
||||
if (this._mainPushService) {
|
||||
return this._mainPushService
|
||||
.updateRecordAndNotifyApp(record.subscriptionUri, record => {
|
||||
record.p256dhPublicKey = exportedKeys[0];
|
||||
record.p256dhPrivateKey = exportedKeys[1];
|
||||
return record;
|
||||
});
|
||||
}
|
||||
}, error => {
|
||||
record = null;
|
||||
if (this._mainPushService) {
|
||||
this._mainPushService
|
||||
.dropRegistrationAndNotifyApp(record.subscriptionUri);
|
||||
}
|
||||
})
|
||||
.then(_ => {
|
||||
if (record) {
|
||||
this._startSingleConnection(record);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_startSingleConnection: function(record) {
|
||||
debug("_startSingleConnection()");
|
||||
if (typeof this._conns[record.subscriptionUri] != "object") {
|
||||
this._conns[record.subscriptionUri] = {channel: null,
|
||||
listener: null,
|
||||
countUnableToConnect: 0,
|
||||
waitingForAlarm: false};
|
||||
}
|
||||
if (!this._conns[record.subscriptionUri].conn) {
|
||||
this._conns[record.subscriptionUri].waitingForAlarm = false;
|
||||
this._listenForMsgs(record.subscriptionUri);
|
||||
}
|
||||
},
|
||||
|
||||
// Start listening if subscriptions present.
|
||||
_startConnectionsWaitingForAlarm: function() {
|
||||
debug("startConnectionsWaitingForAlarm()");
|
||||
|
@ -756,19 +869,33 @@ this.PushServiceHttp2 = {
|
|||
}
|
||||
},
|
||||
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aMessage) {
|
||||
_pushChannelOnStop: function(aUri, aAckUri, aMessage, dh, salt, rs) {
|
||||
debug("pushChannelOnStop() ");
|
||||
|
||||
this._mainPushService.receivedPushMessage(aUri, aMessage, record => {
|
||||
// Always update the stored record.
|
||||
return record;
|
||||
this._mainPushService.getByKeyID(aUri)
|
||||
.then(aPushRecord =>
|
||||
PushServiceHttp2Crypto.decodeMsg(aMessage, aPushRecord.p256dhPrivateKey,
|
||||
dh, salt, rs)
|
||||
.then(msg => {
|
||||
var msgString = '';
|
||||
for (var i=0; i<msg.length; i++) {
|
||||
msgString += String.fromCharCode(msg[i]);
|
||||
}
|
||||
return this._mainPushService.receivedPushMessage(aUri,
|
||||
msgString,
|
||||
record => {
|
||||
// Always update the stored record.
|
||||
return record;
|
||||
});
|
||||
})
|
||||
)
|
||||
.then(_ => this._ackMsgRecv(aAckUri))
|
||||
.catch(err => {
|
||||
debug("Error receiving message: " + err);
|
||||
});
|
||||
this._ackMsgRecv(aAckUri);
|
||||
},
|
||||
|
||||
onAlarmFired: function() {
|
||||
// Conditions are arranged in decreasing specificity.
|
||||
// i.e. when _waitingForPong is true, other conditions are also true.
|
||||
this._startConnectionsWaitingForAlarm();
|
||||
},
|
||||
};
|
||||
|
@ -777,6 +904,8 @@ function PushRecordHttp2(record) {
|
|||
PushRecord.call(this, record);
|
||||
this.subscriptionUri = record.subscriptionUri;
|
||||
this.pushReceiptEndpoint = record.pushReceiptEndpoint;
|
||||
this.p256dhPublicKey = record.p256dhPublicKey;
|
||||
this.p256dhPrivateKey = record.p256dhPrivateKey;
|
||||
}
|
||||
|
||||
PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
||||
|
@ -790,11 +919,13 @@ PushRecordHttp2.prototype = Object.create(PushRecord.prototype, {
|
|||
PushRecordHttp2.prototype.toRegistration = function() {
|
||||
let registration = PushRecord.prototype.toRegistration.call(this);
|
||||
registration.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
registration.p256dhKey = this.p256dhPublicKey;
|
||||
return registration;
|
||||
};
|
||||
|
||||
PushRecordHttp2.prototype.toRegister = function() {
|
||||
let register = PushRecord.prototype.toRegister.call(this);
|
||||
register.pushReceiptEndpoint = this.pushReceiptEndpoint;
|
||||
register.p256dhKey = this.p256dhPublicKey;
|
||||
return register;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/* jshint moz: true, esnext: true */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.importGlobalProperties(['crypto']);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['PushServiceHttp2Crypto', 'concatArray'];
|
||||
|
||||
var ENCRYPT_INFO = new TextEncoder('utf-8').encode('Content-Encoding: aesgcm128');
|
||||
var NONCE_INFO = new TextEncoder('utf-8').encode('Content-Encoding: nonce');
|
||||
|
||||
function chunkArray(array, size) {
|
||||
var start = array.byteOffset || 0;
|
||||
array = array.buffer || array;
|
||||
var index = 0;
|
||||
var result = [];
|
||||
while(index + size <= array.byteLength) {
|
||||
result.push(new Uint8Array(array, start + index, size));
|
||||
index += size;
|
||||
}
|
||||
if (index < array.byteLength) {
|
||||
result.push(new Uint8Array(array, start + index));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function base64UrlDecode(s) {
|
||||
s = s.replace(/-/g, '+').replace(/_/g, '/');
|
||||
|
||||
// Replace padding if it was stripped by the sender.
|
||||
// See http://tools.ietf.org/html/rfc4648#section-4
|
||||
switch (s.length % 4) {
|
||||
case 0:
|
||||
break; // No pad chars in this case
|
||||
case 2:
|
||||
s += '==';
|
||||
break; // Two pad chars
|
||||
case 3:
|
||||
s += '=';
|
||||
break; // One pad char
|
||||
default:
|
||||
throw new Error('Illegal base64url string!');
|
||||
}
|
||||
|
||||
// With correct padding restored, apply the standard base64 decoder
|
||||
var decoded = atob(s);
|
||||
|
||||
var array = new Uint8Array(new ArrayBuffer(decoded.length));
|
||||
for (var i = 0; i < decoded.length; i++) {
|
||||
array[i] = decoded.charCodeAt(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
this.concatArray = function(arrays) {
|
||||
var size = arrays.reduce((total, a) => total + a.byteLength, 0);
|
||||
var index = 0;
|
||||
return arrays.reduce((result, a) => {
|
||||
result.set(new Uint8Array(a), index);
|
||||
index += a.byteLength;
|
||||
return result;
|
||||
}, new Uint8Array(size));
|
||||
};
|
||||
|
||||
var HMAC_SHA256 = { name: 'HMAC', hash: 'SHA-256' };
|
||||
|
||||
function hmac(key) {
|
||||
this.keyPromise = crypto.subtle.importKey('raw', key, HMAC_SHA256,
|
||||
false, ['sign']);
|
||||
}
|
||||
|
||||
hmac.prototype.hash = function(input) {
|
||||
return this.keyPromise.then(k => crypto.subtle.sign('HMAC', k, input));
|
||||
};
|
||||
|
||||
function hkdf(salt, ikm) {
|
||||
this.prkhPromise = new hmac(salt).hash(ikm)
|
||||
.then(prk => new hmac(prk));
|
||||
}
|
||||
|
||||
hkdf.prototype.generate = function(info, len) {
|
||||
var input = concatArray([info, new Uint8Array([1])]);
|
||||
return this.prkhPromise
|
||||
.then(prkh => prkh.hash(input))
|
||||
.then(h => {
|
||||
if (h.byteLength < len) {
|
||||
throw new Error('Length is too long');
|
||||
}
|
||||
return h.slice(0, len);
|
||||
});
|
||||
};
|
||||
|
||||
/* generate a 96-bit IV for use in GCM, 48-bits of which are populated */
|
||||
function generateNonce(base, index) {
|
||||
if (index >= Math.pow(2, 48)) {
|
||||
throw new Error('Error generating IV - index is too large.');
|
||||
}
|
||||
var nonce = base.slice(0, 12);
|
||||
nonce = new Uint8Array(nonce);
|
||||
for (var i = 0; i < 6; ++i) {
|
||||
nonce[nonce.byteLength - 1 - i] ^= (index / Math.pow(256, i)) & 0xff;
|
||||
}
|
||||
return nonce;
|
||||
}
|
||||
|
||||
this.PushServiceHttp2Crypto = {
|
||||
|
||||
generateKeys: function() {
|
||||
return crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256'},
|
||||
true,
|
||||
['deriveBits'])
|
||||
.then(cryptoKey =>
|
||||
Promise.all([
|
||||
crypto.subtle.exportKey('raw', cryptoKey.publicKey),
|
||||
// TODO: change this when bug 1048931 lands.
|
||||
crypto.subtle.exportKey('jwk', cryptoKey.privateKey)
|
||||
]));
|
||||
},
|
||||
|
||||
decodeMsg: function(aData, aPrivateKey, aRemotePublicKey, aSalt, aRs) {
|
||||
|
||||
if (aData.byteLength === 0) {
|
||||
// Zero length messages will be passed as null.
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// The last chunk of data must be less than aRs, if it is not return an
|
||||
// error.
|
||||
if (aData.byteLength % (aRs + 16) === 0) {
|
||||
return Promise.reject(new Error('Data truncated'));
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
crypto.subtle.importKey('raw', base64UrlDecode(aRemotePublicKey),
|
||||
{ name: 'ECDH', namedCurve: 'P-256' },
|
||||
false,
|
||||
['deriveBits']),
|
||||
crypto.subtle.importKey('jwk', aPrivateKey,
|
||||
{ name: 'ECDH', namedCurve: 'P-256' },
|
||||
false,
|
||||
['deriveBits'])
|
||||
])
|
||||
.then(keys =>
|
||||
crypto.subtle.deriveBits({ name: 'ECDH', public: keys[0] }, keys[1], 256))
|
||||
.then(rawKey => {
|
||||
var kdf = new hkdf(base64UrlDecode(aSalt), new Uint8Array(rawKey));
|
||||
return Promise.all([
|
||||
kdf.generate(ENCRYPT_INFO, 16)
|
||||
.then(gcmBits =>
|
||||
crypto.subtle.importKey('raw', gcmBits, 'AES-GCM', false,
|
||||
['decrypt'])),
|
||||
kdf.generate(NONCE_INFO, 12)
|
||||
])
|
||||
})
|
||||
.then(r =>
|
||||
// AEAD_AES_128_GCM expands ciphertext to be 16 octets longer.
|
||||
Promise.all(chunkArray(aData, aRs + 16).map((slice, index) =>
|
||||
this._decodeChunk(slice, index, r[1], r[0]))))
|
||||
.then(r => concatArray(r));
|
||||
},
|
||||
|
||||
_decodeChunk: function(aSlice, aIndex, aNonce, aKey) {
|
||||
return crypto.subtle.decrypt({name: 'AES-GCM',
|
||||
iv: generateNonce(aNonce, aIndex)
|
||||
},
|
||||
aKey, aSlice)
|
||||
.then(decoded => {
|
||||
decoded = new Uint8Array(decoded);
|
||||
if (decoded.length == 0) {
|
||||
return Promise.reject(new Error('Decoded array is too short!'));
|
||||
} else if (decoded[0] > decoded.length) {
|
||||
return Promise.reject(new Error ('Padding is wrong!'));
|
||||
} else {
|
||||
// All padded bytes must be zero except the first one.
|
||||
for (var i = 1; i <= decoded[0]; i++) {
|
||||
if (decoded[i] != 0) {
|
||||
return Promise.reject(new Error('Padding is wrong!'));
|
||||
}
|
||||
}
|
||||
return decoded.slice(decoded[0] + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
|
@ -20,6 +20,7 @@ EXTRA_JS_MODULES += [
|
|||
'PushService.jsm',
|
||||
'PushServiceChildPreload.jsm',
|
||||
'PushServiceHttp2.jsm',
|
||||
'PushServiceHttp2Crypto.jsm',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
|
|
|
@ -46,6 +46,13 @@ function run_test() {
|
|||
|
||||
add_task(function* test_pushNotifications() {
|
||||
|
||||
// /pushNotifications/subscription1 will send a message with no rs and padding
|
||||
// length 1.
|
||||
// /pushNotifications/subscription2 will send a message with no rs and padding
|
||||
// length 16.
|
||||
// /pushNotifications/subscription3 will send a message with rs equal 24 and
|
||||
// padding length 16.
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(() => {
|
||||
return db.drop().then(_ => db.close());
|
||||
|
@ -58,6 +65,16 @@ add_task(function* test_pushNotifications() {
|
|||
pushEndpoint: serverURL + '/pushEndpoint1',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint1',
|
||||
scope: 'https://example.com/page/1',
|
||||
p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
|
||||
quota: Infinity,
|
||||
}, {
|
||||
|
@ -65,6 +82,16 @@ add_task(function* test_pushNotifications() {
|
|||
pushEndpoint: serverURL + '/pushEndpoint2',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint2',
|
||||
scope: 'https://example.com/page/2',
|
||||
p256dhPublicKey: 'BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: 'lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: '-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE',
|
||||
y: '5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
|
||||
quota: Infinity,
|
||||
}, {
|
||||
|
@ -72,6 +99,16 @@ add_task(function* test_pushNotifications() {
|
|||
pushEndpoint: serverURL + '/pushEndpoint3',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint3',
|
||||
scope: 'https://example.com/page/3',
|
||||
p256dhPublicKey: 'BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: 'Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: 'EC',
|
||||
x: 'OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po',
|
||||
y: 'Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI'
|
||||
},
|
||||
originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
@ -81,9 +118,27 @@ add_task(function* test_pushNotifications() {
|
|||
}
|
||||
|
||||
let notifyPromise = Promise.all([
|
||||
promiseObserverNotification('push-notification'),
|
||||
promiseObserverNotification('push-notification'),
|
||||
promiseObserverNotification('push-notification')
|
||||
promiseObserverNotification('push-notification', function(subject, data) {
|
||||
var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
|
||||
if (notification && (data == "https://example.com/page/1")){
|
||||
equal(subject.data, "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification('push-notification', function(subject, data) {
|
||||
var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
|
||||
if (notification && (data == "https://example.com/page/2")){
|
||||
equal(subject.data, "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
}),
|
||||
promiseObserverNotification('push-notification', function(subject, data) {
|
||||
var notification = subject.QueryInterface(Ci.nsIPushObserverNotification);
|
||||
if (notification && (data == "https://example.com/page/3")){
|
||||
equal(subject.data, "Some message", "decoded message is incorrect");
|
||||
return true;
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
PushService.init({
|
||||
|
|
|
@ -68,6 +68,16 @@ add_task(function* test1() {
|
|||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
p256dhPublicKey: 'BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA',
|
||||
p256dhPrivateKey: {
|
||||
crv: 'P-256',
|
||||
d: '1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM',
|
||||
ext: true,
|
||||
key_ops: ["deriveBits"],
|
||||
kty: "EC",
|
||||
x: '8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM',
|
||||
y: '26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA'
|
||||
},
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
}];
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
'use strict';
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
const {PushDB, PushService, PushServiceHttp2} = serviceExports;
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "serverPort", function() {
|
||||
return httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
function listenHandler(metadata, response) {
|
||||
do_check_true(true, "Start listening");
|
||||
httpServer.stop(do_test_finished);
|
||||
response.setHeader("Retry-After", "10");
|
||||
response.setStatusLine(metadata.httpVersion, 500, "Retry");
|
||||
}
|
||||
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/subscriptionNoKey", listenHandler);
|
||||
httpServer.start(-1);
|
||||
|
||||
function run_test() {
|
||||
|
||||
do_get_profile();
|
||||
setPrefs({
|
||||
'http2.retryInterval': 1000,
|
||||
'http2.maxRetries': 2
|
||||
});
|
||||
disableServiceWorkerEvents(
|
||||
'https://example.com/page'
|
||||
);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test1() {
|
||||
|
||||
let db = PushServiceHttp2.newPushDB();
|
||||
do_register_cleanup(_ => {
|
||||
return db.drop().then(_ => db.close());
|
||||
});
|
||||
|
||||
do_test_pending();
|
||||
|
||||
var serverURL = "http://localhost:" + httpServer.identity.primaryPort;
|
||||
|
||||
let record = {
|
||||
subscriptionUri: serverURL + '/subscriptionNoKey',
|
||||
pushEndpoint: serverURL + '/pushEndpoint',
|
||||
pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint',
|
||||
scope: 'https://example.com/page',
|
||||
originAttributes: '',
|
||||
quota: Infinity,
|
||||
};
|
||||
|
||||
yield db.put(record);
|
||||
|
||||
let notifyPromise = promiseObserverNotification('push-subscription-change',
|
||||
_ => true);
|
||||
|
||||
PushService.init({
|
||||
serverURI: serverURL + "/subscribe",
|
||||
service: PushServiceHttp2,
|
||||
db
|
||||
});
|
||||
|
||||
yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
|
||||
'Timed out waiting for notifications');
|
||||
|
||||
let aRecord = yield db.getByKeyID(serverURL + '/subscriptionNoKey');
|
||||
ok(aRecord, 'The record should still be there');
|
||||
ok(aRecord.p256dhPublicKey, 'There should be a public key');
|
||||
ok(aRecord.p256dhPrivateKey, 'There should be a private key');
|
||||
});
|
|
@ -38,6 +38,7 @@ skip-if = toolkit == 'android'
|
|||
[test_resubscribe_5xxCode_http2.js]
|
||||
[test_resubscribe_listening_for_msg_error_http2.js]
|
||||
[test_register_5xxCode_http2.js]
|
||||
[test_updateRecordNoEncryptionKeys.js]
|
||||
[test_register_success_http2.js]
|
||||
skip-if = !hasNode
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
|
|
|
@ -191,3 +191,5 @@ skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolk
|
|||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
|
||||
[test_upgrade_insecure_cors.html]
|
||||
skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'gonk' || toolkit == 'android'
|
||||
[test_blocked_uri_in_reports.html]
|
||||
skip-if = e10s || buildapp == 'b2g' # http-on-opening-request observer not supported in child process (bug 1009632)
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1069762 - Check blocked-uri in csp-reports after redirect</title>
|
||||
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<iframe style="width:200px;height:200px;" id='cspframe'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
/* Description of the test:
|
||||
* We try to load a script from:
|
||||
* http://example.com/tests/dom/security/test/csp/file_path_matching_redirect_server.sjs
|
||||
* which gets redirected to:
|
||||
* http://test1.example.com/tests/dom/security//test/csp/file_path_matching.js
|
||||
*
|
||||
* The blocked-uri in the csp-report should be:
|
||||
* test1.example.com
|
||||
* instead of:
|
||||
* http://test1.example.com/tests/com/security/test/csp/file_path_matching.js
|
||||
*
|
||||
* see also: http://www.w3.org/TR/CSP/#violation-reports
|
||||
*
|
||||
* Note, that we reuse the test-setup from
|
||||
* test_path_matching_redirect.html
|
||||
*/
|
||||
|
||||
const reportURI = "http://mochi.test:8888/foo.sjs";
|
||||
const policy = "script-src http://example.com; report-uri " + reportURI;
|
||||
const testfile = "tests/dom/security/test/csp/file_path_matching_redirect.html";
|
||||
|
||||
// This is used to watch requests go out so we can see if the report is
|
||||
// sent correctly
|
||||
function examiner() {
|
||||
SpecialPowers.addObserver(this, "http-on-opening-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
// subject should be an nsURI
|
||||
if (!SpecialPowers.can_QI(subject))
|
||||
return;
|
||||
|
||||
if (topic === "http-on-opening-request") {
|
||||
var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIHttpChannel"), "URI.asciiSpec");
|
||||
if (asciiSpec !== reportURI) return;
|
||||
|
||||
try {
|
||||
// Verify that the report was properly formatted.
|
||||
// We'll parse the report text as JSON and verify that the properties
|
||||
// have expected values.
|
||||
var reportText = "{}";
|
||||
var uploadStream = SpecialPowers.wrap(SpecialPowers.do_QueryInterface(subject, "nsIUploadChannel")).uploadStream;
|
||||
|
||||
if (uploadStream) {
|
||||
// get the bytes from the request body
|
||||
var binstream = SpecialPowers.Cc["@mozilla.org/binaryinputstream;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsIBinaryInputStream);
|
||||
binstream.setInputStream(uploadStream);
|
||||
|
||||
var segments = [];
|
||||
for (var count = uploadStream.available(); count; count = uploadStream.available()) {
|
||||
var data = binstream.readBytes(count);
|
||||
segments.push(data);
|
||||
}
|
||||
|
||||
var reportText = segments.join("");
|
||||
// rewind stream as we are supposed to - there will be an assertion later if we don't.
|
||||
SpecialPowers.do_QueryInterface(uploadStream, "nsISeekableStream").seek(SpecialPowers.Ci.nsISeekableStream.NS_SEEK_SET, 0);
|
||||
}
|
||||
try {
|
||||
var reportObj = JSON.parse(reportText);
|
||||
}
|
||||
catch (e) {
|
||||
ok(false, "Could not parse JSON (exception: " + e + ")");
|
||||
}
|
||||
var cspReport = reportObj["csp-report"];
|
||||
// blocked-uri should only be the asciiHost instead of:
|
||||
// http://test1.example.com/tests/dom/security/test/csp/file_path_matching.js
|
||||
is(cspReport["blocked-uri"], "http://test1.example.com", "Incorrect blocked-uri");
|
||||
}
|
||||
catch (e) {
|
||||
ok(false, "Could not query report (exception: " + e + ")");
|
||||
}
|
||||
|
||||
// finish up
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
},
|
||||
|
||||
// remove the listener
|
||||
remove: function() {
|
||||
SpecialPowers.removeObserver(this, "http-on-opening-request");
|
||||
}
|
||||
}
|
||||
window.examiner = new examiner();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
var src = "file_testserver.sjs";
|
||||
// append the file that should be served
|
||||
src += "?file=" + escape(testfile);
|
||||
// append the CSP that should be used to serve the file
|
||||
src += "&csp=" + escape(policy);
|
||||
|
||||
document.getElementById("cspframe").src = src;
|
||||
}
|
||||
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -643,7 +643,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
|
|||
mClient->Activate(mCurrentInterval->Begin()->Time().GetMillis());
|
||||
}
|
||||
if (mSeekState == SEEK_NOT_SEEKING) {
|
||||
FireTimeEventAsync(NS_SMIL_BEGIN, 0);
|
||||
FireTimeEventAsync(eSMILBeginEvent, 0);
|
||||
}
|
||||
if (HasPlayed()) {
|
||||
Reset(); // Apply restart behaviour
|
||||
|
@ -679,7 +679,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
|
|||
}
|
||||
mCurrentInterval->FixEnd();
|
||||
if (mSeekState == SEEK_NOT_SEEKING) {
|
||||
FireTimeEventAsync(NS_SMIL_END, 0);
|
||||
FireTimeEventAsync(eSMILEndEvent, 0);
|
||||
}
|
||||
mCurrentRepeatIteration = 0;
|
||||
mOldIntervals.AppendElement(mCurrentInterval.forget());
|
||||
|
@ -727,7 +727,7 @@ nsSMILTimedElement::DoSampleAt(nsSMILTime aContainerTime, bool aEndOnly)
|
|||
mCurrentRepeatIteration != prevRepeatIteration &&
|
||||
mCurrentRepeatIteration &&
|
||||
mSeekState == SEEK_NOT_SEEKING) {
|
||||
FireTimeEventAsync(NS_SMIL_REPEAT,
|
||||
FireTimeEventAsync(eSMILRepeatEvent,
|
||||
static_cast<int32_t>(mCurrentRepeatIteration));
|
||||
}
|
||||
}
|
||||
|
@ -1518,14 +1518,14 @@ nsSMILTimedElement::DoPostSeek()
|
|||
case SEEK_FORWARD_FROM_ACTIVE:
|
||||
case SEEK_BACKWARD_FROM_ACTIVE:
|
||||
if (mElementState != STATE_ACTIVE) {
|
||||
FireTimeEventAsync(NS_SMIL_END, 0);
|
||||
FireTimeEventAsync(eSMILEndEvent, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SEEK_FORWARD_FROM_INACTIVE:
|
||||
case SEEK_BACKWARD_FROM_INACTIVE:
|
||||
if (mElementState == STATE_ACTIVE) {
|
||||
FireTimeEventAsync(NS_SMIL_BEGIN, 0);
|
||||
FireTimeEventAsync(eSMILBeginEvent, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -9,11 +9,17 @@
|
|||
|
||||
interface Principal;
|
||||
|
||||
enum PushEncryptionKeyName
|
||||
{
|
||||
"p256dh"
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
|
||||
ChromeConstructor(DOMString pushEndpoint, DOMString scope)]
|
||||
ChromeConstructor(DOMString pushEndpoint, DOMString scope, ArrayBuffer? key)]
|
||||
interface PushSubscription
|
||||
{
|
||||
readonly attribute USVString endpoint;
|
||||
ArrayBuffer? getKey(PushEncryptionKeyName name);
|
||||
[Throws, UseCounter]
|
||||
Promise<boolean> unsubscribe();
|
||||
jsonifier;
|
||||
|
|
|
@ -1103,11 +1103,11 @@ private:
|
|||
|
||||
class DebuggerImmediateRunnable : public WorkerRunnable
|
||||
{
|
||||
nsRefPtr<Function> mHandler;
|
||||
nsRefPtr<dom::Function> mHandler;
|
||||
|
||||
public:
|
||||
explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
Function& aHandler)
|
||||
dom::Function& aHandler)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mHandler(&aHandler)
|
||||
{ }
|
||||
|
@ -5666,7 +5666,7 @@ WorkerPrivate::PostMessageToDebugger(const nsAString& aMessage)
|
|||
}
|
||||
|
||||
void
|
||||
WorkerPrivate::SetDebuggerImmediate(JSContext* aCx, Function& aHandler,
|
||||
WorkerPrivate::SetDebuggerImmediate(JSContext* aCx, dom::Function& aHandler,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
|
@ -5904,7 +5904,7 @@ WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage,
|
|||
|
||||
int32_t
|
||||
WorkerPrivate::SetTimeout(JSContext* aCx,
|
||||
Function* aHandler,
|
||||
dom::Function* aHandler,
|
||||
const nsAString& aStringHandler,
|
||||
int32_t aTimeout,
|
||||
const Sequence<JS::Value>& aArguments,
|
||||
|
|
|
@ -254,7 +254,7 @@ nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
|
|||
// recreated with same content. Therefore, we need to forget mIMETextNode,
|
||||
// but we need to keep storing mIMETextOffset and mIMETextLength becuase
|
||||
// they are necessary to restore IME selection and replacing composing string
|
||||
// when this receives NS_COMPOSITION_CHANGE event next time.
|
||||
// when this receives eCompositionChange event next time.
|
||||
if (mIMETextNode && !mIMETextNode->IsInComposedDoc()) {
|
||||
mIMETextNode = nullptr;
|
||||
}
|
||||
|
@ -5159,11 +5159,11 @@ nsEditor::IsAcceptableInputEvent(nsIDOMEvent* aEvent)
|
|||
// If events are not created with proper event interface, their message
|
||||
// are initialized with eUnidentifiedEvent. Let's ignore such event.
|
||||
return false;
|
||||
case NS_COMPOSITION_START:
|
||||
case NS_COMPOSITION_END:
|
||||
case NS_COMPOSITION_UPDATE:
|
||||
case NS_COMPOSITION_CHANGE:
|
||||
case NS_COMPOSITION_COMMIT_AS_IS:
|
||||
case eCompositionStart:
|
||||
case eCompositionEnd:
|
||||
case eCompositionUpdate:
|
||||
case eCompositionChange:
|
||||
case eCompositionCommitAsIs:
|
||||
// Don't allow composition events whose internal event are not
|
||||
// WidgetCompositionEvent.
|
||||
widgetGUIEvent = aEvent->GetInternalNSEvent()->AsCompositionEvent();
|
||||
|
|
|
@ -460,13 +460,13 @@ nsEditorEventListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
case eBlur:
|
||||
return Blur(aEvent);
|
||||
// text
|
||||
case NS_COMPOSITION_CHANGE:
|
||||
case eCompositionChange:
|
||||
return HandleText(aEvent);
|
||||
// compositionstart
|
||||
case NS_COMPOSITION_START:
|
||||
case eCompositionStart:
|
||||
return HandleStartComposition(aEvent);
|
||||
// compositionend
|
||||
case NS_COMPOSITION_END:
|
||||
case eCompositionEnd:
|
||||
HandleEndComposition(aEvent);
|
||||
return NS_OK;
|
||||
default:
|
||||
|
|
|
@ -845,8 +845,8 @@ nsPlaintextEditor::UpdateIMEComposition(nsIDOMEvent* aDOMTextEvent)
|
|||
WidgetCompositionEvent* compositionChangeEvent =
|
||||
aDOMTextEvent->GetInternalNSEvent()->AsCompositionEvent();
|
||||
NS_ENSURE_TRUE(compositionChangeEvent, NS_ERROR_INVALID_ARG);
|
||||
MOZ_ASSERT(compositionChangeEvent->mMessage == NS_COMPOSITION_CHANGE,
|
||||
"The internal event should be NS_COMPOSITION_CHANGE");
|
||||
MOZ_ASSERT(compositionChangeEvent->mMessage == eCompositionChange,
|
||||
"The internal event should be eCompositionChange");
|
||||
|
||||
EnsureComposition(compositionChangeEvent);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
|
||||
|
||||
Current version derived from upstream changeset e6539b6769cf
|
||||
Current version derived from upstream changeset 0f9edca71849
|
||||
|
||||
See gfx/graphite2/moz-gr-update.sh for update procedure.
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#define GR2_VERSION_MAJOR 1
|
||||
#define GR2_VERSION_MINOR 3
|
||||
#define GR2_VERSION_BUGFIX 0
|
||||
#define GR2_VERSION_BUGFIX 2
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
|
|
|
@ -170,7 +170,8 @@ enum gr_bidirtl {
|
|||
/// Underlying paragraph direction is RTL
|
||||
gr_rtl = 1,
|
||||
/// Set this to not run the bidi pass internally, even if the font asks for it.
|
||||
/// This presumes that the segment is in a single direction.
|
||||
/// This presumes that the segment is in a single direction. Most of the time
|
||||
/// this bit should be set unless you know you are passing full paragraphs of text.
|
||||
gr_nobidi = 2,
|
||||
/// Disable auto mirroring for rtl text
|
||||
gr_nomirror = 4
|
||||
|
|
|
@ -747,7 +747,7 @@ void resolveWhitespace(int baseLevel, Slot *s)
|
|||
for ( ; s; s = s->prev())
|
||||
{
|
||||
int8 cls = s->getBidiClass();
|
||||
if (cls == WS || cls & WSflag)
|
||||
if (cls == WS || (cls & WSflag))
|
||||
s->setBidiLevel(baseLevel);
|
||||
else if (cls != BN)
|
||||
break;
|
||||
|
|
|
@ -74,7 +74,6 @@ add_library(graphite2 SHARED
|
|||
gr_logging.cpp
|
||||
gr_segment.cpp
|
||||
gr_slot.cpp
|
||||
Bidi.cpp
|
||||
CachedFace.cpp
|
||||
CmapCache.cpp
|
||||
Code.cpp
|
||||
|
@ -107,17 +106,14 @@ set_target_properties(graphite2 PROPERTIES PUBLIC_HEADER "${GRAPHITE_HEADERS}"
|
|||
LT_VERSION_REVISION ${GRAPHITE_API_REVISION}
|
||||
LT_VERSION_AGE ${GRAPHITE_API_AGE})
|
||||
|
||||
if (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
|
||||
set(GRAPHITE_LINK_FLAGS "-fsanitize=address")
|
||||
else (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
|
||||
set(GRAPHITE_LINK_FLAGS "")
|
||||
endif (${CMAKE_BUILD_TYPE} STREQUAL "ClangASN")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set_target_properties(graphite2 PROPERTIES
|
||||
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector -Wdouble-promotion"
|
||||
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden -fno-stack-protector"
|
||||
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
|
||||
LINKER_LANGUAGE C)
|
||||
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||
add_definitions(-Wdouble-promotion)
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX)
|
||||
if (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
|
||||
target_link_libraries(graphite2 kernel32 msvcr90 mingw32 gcc user32)
|
||||
else (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
|
||||
|
|
|
@ -38,11 +38,11 @@ const void * bmp_subtable(const Face::Table & cmap)
|
|||
{
|
||||
const void * stbl;
|
||||
if (!cmap.size()) return 0;
|
||||
if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()))
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()))
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()))
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()))
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size())))
|
||||
if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()), cmap.size())
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()), cmap.size())
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()), cmap.size())
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()), cmap.size())
|
||||
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size()), cmap.size()))
|
||||
return stbl;
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ const void * smp_subtable(const Face::Table & cmap)
|
|||
{
|
||||
const void * stbl;
|
||||
if (!cmap.size()) return 0;
|
||||
if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()))
|
||||
|| TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size())))
|
||||
if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()), cmap.size())
|
||||
|| TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size()), cmap.size()))
|
||||
return stbl;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@ struct context
|
|||
|
||||
} // end namespace
|
||||
|
||||
byte * Machine::Code::local_memory = 0;
|
||||
|
||||
class Machine::Code::decoder
|
||||
{
|
||||
|
@ -90,7 +89,8 @@ public:
|
|||
byte max_ref;
|
||||
|
||||
analysis() : slotref(0), max_ref(0) {};
|
||||
void set_ref(int index) throw();
|
||||
void set_ref(int index, bool incinsert=false) throw();
|
||||
void set_noref(int index) throw();
|
||||
void set_changed(int index) throw();
|
||||
|
||||
};
|
||||
|
@ -146,7 +146,7 @@ inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype
|
|||
|
||||
Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
|
||||
uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
|
||||
enum passtype pt, byte * & _out)
|
||||
enum passtype pt, byte * * const _out)
|
||||
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
|
||||
_constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
|
||||
{
|
||||
|
@ -162,11 +162,10 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
|
|||
assert(bytecode_end > bytecode_begin);
|
||||
const opcode_t * op_to_fn = Machine::getOpcodeTable();
|
||||
|
||||
// Allocate code and dat target buffers, these sizes are a worst case
|
||||
// Allocate code and data target buffers, these sizes are a worst case
|
||||
// estimate. Once we know their real sizes the we'll shrink them.
|
||||
if (_out) _code = reinterpret_cast<instr *>(_out);
|
||||
else _code = static_cast<instr *>(malloc((bytecode_end - bytecode_begin)
|
||||
* (sizeof(instr)+sizeof(byte))));
|
||||
if (_out) _code = reinterpret_cast<instr *>(*_out);
|
||||
else _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin)));
|
||||
_data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
|
||||
|
||||
if (!_code || !_data) {
|
||||
|
@ -220,7 +219,7 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
|
|||
memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
|
||||
size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
|
||||
if (_out)
|
||||
_out += total_sz;
|
||||
*_out += total_sz;
|
||||
else
|
||||
_code = static_cast<instr *>(realloc(_code, total_sz));
|
||||
_data = reinterpret_cast<byte *>(_code + (_instr_count+1));
|
||||
|
@ -418,9 +417,11 @@ opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
|
|||
break;
|
||||
case PUSH_IGLYPH_ATTR :// not implemented
|
||||
++_stack_depth;
|
||||
break;
|
||||
case POP_RET :
|
||||
if (--_stack_depth < 0)
|
||||
failure(underfull_stack);
|
||||
// no break
|
||||
case RET_ZERO :
|
||||
case RET_TRUE :
|
||||
break;
|
||||
|
@ -477,14 +478,23 @@ void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8 * arg)
|
|||
case PUT_GLYPH_8BIT_OBS :
|
||||
case PUT_GLYPH :
|
||||
_code._modify = true;
|
||||
_analysis.set_changed(_analysis.slotref);
|
||||
_analysis.set_changed(0);
|
||||
break;
|
||||
case ATTR_SET :
|
||||
case ATTR_ADD :
|
||||
case ATTR_SET_SLOT :
|
||||
case IATTR_SET_SLOT :
|
||||
case IATTR_SET :
|
||||
case IATTR_ADD :
|
||||
case IATTR_SUB :
|
||||
_analysis.set_noref(0);
|
||||
break;
|
||||
case NEXT :
|
||||
case COPY_NEXT :
|
||||
if (!_analysis.contexts[_analysis.slotref].flags.inserted)
|
||||
++_analysis.slotref;
|
||||
_analysis.contexts[_analysis.slotref] = context(_code._instr_count+1);
|
||||
if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
|
||||
// if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
|
||||
break;
|
||||
case INSERT :
|
||||
_analysis.contexts[_analysis.slotref].flags.inserted = true;
|
||||
|
@ -493,14 +503,15 @@ void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8 * arg)
|
|||
case PUT_SUBS_8BIT_OBS : // slotref on 1st parameter
|
||||
case PUT_SUBS :
|
||||
_code._modify = true;
|
||||
_analysis.set_changed(_analysis.slotref);
|
||||
_analysis.set_changed(0);
|
||||
// no break
|
||||
case PUT_COPY :
|
||||
{
|
||||
if (arg[0] != 0) { _analysis.set_changed(_analysis.slotref); _code._modify = true; }
|
||||
if (arg[0] != 0) { _analysis.set_changed(0); _code._modify = true; }
|
||||
if (arg[0] <= 0 && -arg[0] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
|
||||
_analysis.set_ref(_analysis.slotref + arg[0] - _analysis.contexts[_analysis.slotref].flags.inserted);
|
||||
else if (_analysis.slotref + arg[0] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[0];
|
||||
_analysis.set_ref(arg[0], true);
|
||||
else if (arg[0] > 0)
|
||||
_analysis.set_ref(arg[0], true);
|
||||
break;
|
||||
}
|
||||
case PUSH_ATT_TO_GATTR_OBS : // slotref on 2nd parameter
|
||||
|
@ -513,16 +524,18 @@ void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8 * arg)
|
|||
case PUSH_ISLOT_ATTR :
|
||||
case PUSH_FEAT :
|
||||
if (arg[1] <= 0 && -arg[1] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
|
||||
_analysis.set_ref(_analysis.slotref + arg[1] - _analysis.contexts[_analysis.slotref].flags.inserted);
|
||||
else if (_analysis.slotref + arg[1] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[1];
|
||||
_analysis.set_ref(arg[1], true);
|
||||
else if (arg[1] > 0)
|
||||
_analysis.set_ref(arg[1], true);
|
||||
break;
|
||||
case PUSH_ATT_TO_GLYPH_ATTR :
|
||||
if (_code._constraint) return;
|
||||
// no break
|
||||
case PUSH_GLYPH_ATTR :
|
||||
if (arg[2] <= 0 && -arg[2] <= _analysis.slotref - _analysis.contexts[_analysis.slotref].flags.inserted)
|
||||
_analysis.set_ref(_analysis.slotref + arg[2] - _analysis.contexts[_analysis.slotref].flags.inserted);
|
||||
else if (_analysis.slotref + arg[2] > _analysis.max_ref) _analysis.max_ref = _analysis.slotref + arg[2];
|
||||
_analysis.set_ref(arg[2], true);
|
||||
else if (arg[2] > 0)
|
||||
_analysis.set_ref(arg[2], true);
|
||||
break;
|
||||
case ASSOC : // slotrefs in varargs
|
||||
break;
|
||||
|
@ -604,6 +617,7 @@ void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end
|
|||
*tip = temp_copy;
|
||||
++code_end;
|
||||
++tempcount;
|
||||
_code._delete = true;
|
||||
}
|
||||
|
||||
_code._instr_count = code_end - code;
|
||||
|
@ -619,8 +633,13 @@ bool Machine::Code::decoder::validate_opcode(const opcode opc, const byte * cons
|
|||
return false;
|
||||
}
|
||||
const opcode_t & op = Machine::getOpcodeTable()[opc];
|
||||
if (op.param_sz == VARARGS && bc >= _max.bytecode)
|
||||
{
|
||||
failure(arguments_exhausted);
|
||||
return false;
|
||||
}
|
||||
const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
|
||||
if (bc - 1 + param_sz > _max.bytecode)
|
||||
if (bc - 1 + param_sz >= _max.bytecode)
|
||||
{
|
||||
failure(arguments_exhausted);
|
||||
return false;
|
||||
|
@ -654,16 +673,28 @@ void Machine::Code::failure(const status_t s) throw() {
|
|||
|
||||
|
||||
inline
|
||||
void Machine::Code::decoder::analysis::set_ref(const int index) throw() {
|
||||
contexts[index].flags.referenced = true;
|
||||
if (index > max_ref) max_ref = index;
|
||||
void Machine::Code::decoder::analysis::set_ref(int index, bool incinsert) throw() {
|
||||
if (incinsert && contexts[slotref].flags.inserted) --index;
|
||||
if (index + slotref < 0) return;
|
||||
contexts[index + slotref].flags.referenced = true;
|
||||
if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void Machine::Code::decoder::analysis::set_changed(const int index) throw() {
|
||||
contexts[index].flags.changed = true;
|
||||
if (index > max_ref) max_ref = index;
|
||||
void Machine::Code::decoder::analysis::set_noref(int index) throw() {
|
||||
if (contexts[slotref].flags.inserted) --index;
|
||||
if (index + slotref < 0) return;
|
||||
if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void Machine::Code::decoder::analysis::set_changed(int index) throw() {
|
||||
if (contexts[slotref].flags.inserted) --index;
|
||||
if (index + slotref < 0) return;
|
||||
contexts[index + slotref].flags.changed = true;
|
||||
if ((index > 0 || !contexts[index + slotref].flags.inserted) && index + slotref > max_ref) max_ref = index + slotref;
|
||||
}
|
||||
|
||||
|
||||
|
@ -682,10 +713,12 @@ int32 Machine::Code::run(Machine & m, slotref * & map) const
|
|||
// assert(_own);
|
||||
assert(*this); // Check we are actually runnable
|
||||
|
||||
if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context()))
|
||||
if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context())
|
||||
|| m.slotMap()[_max_ref + m.slotMap().context()] == 0)
|
||||
{
|
||||
m._status = Machine::slot_offset_out_bounds;
|
||||
return 1;
|
||||
// return m.run(_code, _data, map);
|
||||
}
|
||||
|
||||
return m.run(_code, _data, map);
|
||||
|
|
|
@ -39,7 +39,7 @@ of the License or (at your option) any later version.
|
|||
|
||||
// Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4
|
||||
// (values in font range from 0..256)
|
||||
#define SUBBOX_RND_ERR 0.016
|
||||
// #define SUBBOX_RND_ERR 0.016
|
||||
|
||||
using namespace graphite2;
|
||||
|
||||
|
@ -543,7 +543,7 @@ bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShif
|
|||
}
|
||||
}
|
||||
bool res = true;
|
||||
if (cslot && cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
|
||||
if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
|
||||
{
|
||||
// Set up the bogus slot representing the exclusion glyph.
|
||||
Slot *exclSlot = seg->newSlot();
|
||||
|
@ -925,6 +925,8 @@ bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float
|
|||
bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
|
||||
{
|
||||
int rtl = (dir & 1) * 2 - 1;
|
||||
if (!seg->getFace()->glyphs().check(slot->gid()))
|
||||
return false;
|
||||
const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
|
||||
const float sx = slot->origin().x + currShift.x;
|
||||
float x = sx + (rtl > 0 ? bb.tr.x : bb.bl.x);
|
||||
|
@ -971,7 +973,6 @@ bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift
|
|||
|
||||
|
||||
// Return the amount to kern by.
|
||||
// TODO: do we need to make use of marginMin here? Probably not.
|
||||
Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
|
||||
int dir, float margin, GR_MAYBE_UNUSED json * const dbgout)
|
||||
{
|
||||
|
|
|
@ -1,75 +1,75 @@
|
|||
/* Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
|
||||
Copyright (c) 2015, SIL International
|
||||
/* GRAPHITE2 LICENSING
|
||||
|
||||
Copyright 2015, SIL International
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should also have received a copy of the GNU Lesser General Public
|
||||
License along with this library in the file named "LICENSE".
|
||||
If not, write to the Free Software Foundation, 51 Franklin Street,
|
||||
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
|
||||
internet at http://www.fsf.org/licenses/lgpl.html.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of the
|
||||
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
|
||||
License, as published by the Free Software Foundation, either version 2
|
||||
of the License or (at your option) any later version.
|
||||
*/
|
||||
#include <cassert>
|
||||
|
||||
#include "inc/Decompressor.h"
|
||||
#include "inc/Shrinker.h"
|
||||
#include "inc/Compression.h"
|
||||
|
||||
using namespace shrinker;
|
||||
using namespace lz4;
|
||||
|
||||
namespace {
|
||||
|
||||
u8 const LONG_DIST = 0x10;
|
||||
u8 const MATCH_LEN = 0x0f;
|
||||
|
||||
template <int M>
|
||||
inline
|
||||
u32 read_literal(u8 const * &s, u8 const * const e, u32 l) {
|
||||
if (unlikely(l == M))
|
||||
if (unlikely(l == 15) && likely(s != e))
|
||||
{
|
||||
u8 b = 0;
|
||||
u8 b = 0;
|
||||
do { l += b = *s++; } while(b==0xff && s != e);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
bool read_directive(u8 const * &src, u8 const * const end, u32 & literal_len, u32 & match_len, u32 & match_dist)
|
||||
bool read_sequence(u8 const * &src, u8 const * const end, u8 const * &literal, u32 & literal_len, u32 & match_len, u32 & match_dist)
|
||||
{
|
||||
u8 const flag = *src++;
|
||||
u8 const token = *src++;
|
||||
|
||||
literal_len = read_literal<7>(src, end, flag >> 5);
|
||||
match_len = read_literal<15>(src, end, flag & MATCH_LEN);
|
||||
literal_len = read_literal(src, end, token >> 4);
|
||||
literal = src;
|
||||
src += literal_len;
|
||||
|
||||
match_dist = *src++;
|
||||
if (flag & LONG_DIST)
|
||||
match_dist |= ((*src++) << 8);
|
||||
if (unlikely(src > end - 2))
|
||||
return false;
|
||||
|
||||
return match_dist != 0xffff;
|
||||
match_dist = *src++;
|
||||
match_dist |= *src++ << 8;
|
||||
match_len = read_literal(src, end, token & 0xf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_size)
|
||||
int lz4::decompress(void const *in, size_t in_size, void *out, size_t out_size)
|
||||
{
|
||||
if (out_size <= in_size)
|
||||
return -1;
|
||||
|
||||
u8 const * src = static_cast<u8 const *>(in),
|
||||
* literal = 0,
|
||||
* const src_end = src + in_size;
|
||||
|
||||
u8 * dst = static_cast<u8*>(out),
|
||||
|
@ -78,24 +78,29 @@ int shrinker::decompress(void const *in, size_t in_size, void *out, size_t out_s
|
|||
u32 literal_len = 0,
|
||||
match_len = 0,
|
||||
match_dist = 0;
|
||||
|
||||
while (read_directive(src, src_end, literal_len, match_len, match_dist))
|
||||
|
||||
while (read_sequence(src, src_end, literal, literal_len, match_len, match_dist))
|
||||
{
|
||||
// Copy in literal
|
||||
if (unlikely(dst + literal_len + sizeof(unsigned long) > dst_end)) return -1;
|
||||
dst = memcpy_nooverlap(dst, src, literal_len);
|
||||
src += literal_len;
|
||||
|
||||
// Copy in literal. At this point the last full sequence must be at
|
||||
// least MINMATCH + 5 from the end of the output buffer.
|
||||
if (unlikely(literal + align(literal_len) > src_end
|
||||
|| dst + align(literal_len) > dst_end - MINMATCH+5))
|
||||
return -1;
|
||||
dst = overrun_copy(dst, literal, literal_len);
|
||||
|
||||
// Copy, possibly repeating, match from earlier in the
|
||||
// decoded output.
|
||||
u8 const * const pcpy = dst - match_dist - 1;
|
||||
if (unlikely(pcpy < static_cast<u8*>(out)
|
||||
|| dst + match_len + MINMATCH + sizeof(unsigned long) > dst_end)) return -1;
|
||||
dst = memcpy_(dst, pcpy, match_len + MINMATCH);
|
||||
u8 const * const pcpy = dst - match_dist;
|
||||
if (unlikely(pcpy < static_cast<u8*>(out)
|
||||
|| dst + align(match_len + MINMATCH) > dst_end))
|
||||
return -1;
|
||||
dst = copy(dst, pcpy, match_len + MINMATCH);
|
||||
}
|
||||
|
||||
if (unlikely(dst + literal_len > dst_end)) return -1;
|
||||
dst = memcpy_nooverlap_surpass(dst, src, literal_len);
|
||||
if (unlikely(literal + literal_len > src_end
|
||||
|| dst + literal_len > dst_end))
|
||||
return -1;
|
||||
dst = fast_copy(dst, literal, literal_len);
|
||||
|
||||
return dst - (u8*)out;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace
|
|||
enum compression
|
||||
{
|
||||
NONE,
|
||||
SHRINKER
|
||||
LZ4
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ bool Face::readGraphite(const Table & silf)
|
|||
Error e;
|
||||
error_context(EC_READSILF);
|
||||
const byte * p = silf;
|
||||
if (e.test(!p, E_NOSILF)) return error(e);
|
||||
if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
|
||||
|
||||
const uint32 version = be::read<uint32>(p);
|
||||
if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
|
||||
|
@ -173,6 +173,10 @@ bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
|
|||
}
|
||||
#endif
|
||||
|
||||
// if ((seg->dir() & 1) != aSilf->dir())
|
||||
// seg->reverseSlots();
|
||||
if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
|
||||
seg->doMirror(aSilf->aMirror());
|
||||
bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
|
||||
if (res)
|
||||
{
|
||||
|
@ -185,6 +189,7 @@ bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
|
|||
#if !defined GRAPHITE2_NTRACING
|
||||
if (dbgout)
|
||||
{
|
||||
seg->positionSlots(0, 0, 0, aSilf->dir());
|
||||
*dbgout << json::item
|
||||
<< json::close // Close up the passes array
|
||||
<< "output" << json::array;
|
||||
|
@ -233,7 +238,9 @@ uint16 Face::getGlyphMetric(uint16 gid, uint8 metric) const
|
|||
{
|
||||
case kgmetAscent : return m_ascent;
|
||||
case kgmetDescent : return m_descent;
|
||||
default: return glyphs().glyph(gid)->getMetric(metric);
|
||||
default:
|
||||
if (gid > glyphs().numGlyphs()) return 0;
|
||||
return glyphs().glyph(gid)->getMetric(metric);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,7 +284,6 @@ Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
|
|||
if (!TtfUtil::CheckTable(n, _p, _sz))
|
||||
{
|
||||
this->~Table(); // Make sure we release the table buffer even if the table filed it's checks
|
||||
_p = 0; _sz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -285,6 +291,15 @@ Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
|
|||
decompress();
|
||||
}
|
||||
|
||||
void Face::Table::releaseBuffers()
|
||||
{
|
||||
if (_compressed)
|
||||
free(const_cast<byte *>(_p));
|
||||
else if (_p && _f->m_ops.release_table)
|
||||
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
|
||||
_p = 0; _sz = 0;
|
||||
}
|
||||
|
||||
Face::Table & Face::Table::operator = (const Table & rhs) throw()
|
||||
{
|
||||
if (_p == rhs._p) return *this;
|
||||
|
@ -297,6 +312,8 @@ Face::Table & Face::Table::operator = (const Table & rhs) throw()
|
|||
Error Face::Table::decompress()
|
||||
{
|
||||
Error e;
|
||||
if (e.test(_sz < 2 * sizeof(uint32) + 3, E_BADSIZE))
|
||||
return e;
|
||||
byte * uncompressed_table = 0;
|
||||
size_t uncompressed_size = 0;
|
||||
|
||||
|
@ -308,25 +325,30 @@ Error Face::Table::decompress()
|
|||
switch(compression(hdr >> 27))
|
||||
{
|
||||
case NONE: return e;
|
||||
case SHRINKER:
|
||||
|
||||
case LZ4:
|
||||
{
|
||||
uncompressed_size = hdr & 0x07ffffff;
|
||||
uncompressed_table = gralloc<byte>(uncompressed_size);
|
||||
//TODO: Coverty: 1315803: FORWARD_NULL
|
||||
if (!e.test(!uncompressed_table, E_OUTOFMEM))
|
||||
e.test(shrinker::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
|
||||
//TODO: Coverty: 1315800: CHECKED_RETURN
|
||||
e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
e.error(E_BADSCHEME);
|
||||
};
|
||||
|
||||
// Check the uncompressed version number against the original.
|
||||
if (!e)
|
||||
//TODO: Coverty: 1315800: CHECKED_RETURN
|
||||
e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
|
||||
|
||||
// Tell the provider to release the compressed form since were replacing
|
||||
// it anyway.
|
||||
this->~Table();
|
||||
releaseBuffers();
|
||||
|
||||
if (e)
|
||||
{
|
||||
|
|
|
@ -131,11 +131,12 @@ bool FeatureMap::readFeats(const Face & face)
|
|||
const uint16 num_settings = be::read<uint16>(p);
|
||||
if (version >= 0x00020000)
|
||||
be::skip<uint16>(p);
|
||||
const byte * const feat_setts = feat_start + be::read<uint32>(p);
|
||||
const uint32 settings_offset = be::read<uint32>(p);
|
||||
const uint16 flags = be::read<uint16>(p),
|
||||
uiName = be::read<uint16>(p);
|
||||
|
||||
if (feat_setts + num_settings * FEATURE_SETTING_SIZE > feat_end)
|
||||
if (settings_offset > size_t(feat_end - feat_start)
|
||||
|| settings_offset + num_settings * FEATURE_SETTING_SIZE > size_t(feat_end - feat_start))
|
||||
{
|
||||
free(defVals);
|
||||
return false;
|
||||
|
@ -151,7 +152,7 @@ bool FeatureMap::readFeats(const Face & face)
|
|||
free(defVals);
|
||||
return false;
|
||||
}
|
||||
maxVal = readFeatureSettings(feat_setts, uiSet, num_settings);
|
||||
maxVal = readFeatureSettings(feat_start + settings_offset, uiSet, num_settings);
|
||||
defVals[i] = uiSet[0].value();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -83,7 +83,7 @@ const void *FileFace::get_table_fn(const void* appFaceHandle, unsigned int name,
|
|||
if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len))
|
||||
return 0;
|
||||
|
||||
if (tbl_offset + tbl_len > file_face._file_len
|
||||
if (tbl_offset > file_face._file_len || tbl_len > file_face._file_len - tbl_offset
|
||||
|| fseek(file_face._file, tbl_offset, SEEK_SET) != 0)
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace
|
|||
// This is strictly a >= operator. A true == operator could be
|
||||
// implemented that test for overlap but it would be more expensive a
|
||||
// test.
|
||||
bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e; }
|
||||
bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e - 1; }
|
||||
bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); }
|
||||
|
||||
value_type operator * () const {
|
||||
|
@ -78,7 +78,8 @@ namespace
|
|||
typedef _glat_iterator<uint16> glat2_iterator;
|
||||
}
|
||||
|
||||
const Rect GlyphCache::nullRect = Rect();
|
||||
const SlantBox SlantBox::empty = {0,0,0,0};
|
||||
|
||||
|
||||
class GlyphCache::Loader
|
||||
{
|
||||
|
@ -145,7 +146,7 @@ GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
|
|||
}
|
||||
else if (numsubs > 0)
|
||||
{
|
||||
GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + (numsubs-1) * 8 * sizeof(float));
|
||||
GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));
|
||||
GlyphBox * currbox = boxes;
|
||||
|
||||
for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)
|
||||
|
@ -277,19 +278,20 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
|
|||
// subtracting the length of the attribids array (numAttribs long if present)
|
||||
// and dividing by either 2 or 4 depending on shor or lonf format
|
||||
_long_fmt = flags & 1;
|
||||
_num_glyphs_attributes = (m_pGloc.size()
|
||||
int tmpnumgattrs = (m_pGloc.size()
|
||||
- (p - m_pGloc)
|
||||
- sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
|
||||
/ (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
|
||||
|
||||
if (version >= 0x00020000
|
||||
if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
|
||||
|| _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate?
|
||||
|| _num_glyphs_graphics > _num_glyphs_attributes)
|
||||
|| _num_glyphs_graphics > tmpnumgattrs)
|
||||
{
|
||||
_head = Face::Table();
|
||||
return;
|
||||
}
|
||||
|
||||
_num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
|
||||
p = m_pGlat;
|
||||
version = be::read<uint32>(p);
|
||||
if (version >= 0x00040000) // reject Glat tables that are too new
|
||||
|
@ -347,8 +349,12 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa
|
|||
void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());
|
||||
|
||||
if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))
|
||||
{
|
||||
if ((xMin > xMax) || (yMin > yMax))
|
||||
return 0;
|
||||
bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)),
|
||||
Position(static_cast<float>(xMax), static_cast<float>(yMax)));
|
||||
}
|
||||
}
|
||||
if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))
|
||||
advance = Position(static_cast<float>(nAdvWid), 0);
|
||||
|
@ -398,7 +404,8 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa
|
|||
else
|
||||
{
|
||||
if (gloce - glocs < 3*sizeof(uint16) // can a glyph have no attributes? why not?
|
||||
|| gloce - glocs > _num_attrs*3*sizeof(uint16))
|
||||
|| gloce - glocs > _num_attrs*3*sizeof(uint16)
|
||||
|| glocs > m_pGlat.size() - 2*sizeof(uint16))
|
||||
return 0;
|
||||
new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ of the License or (at your option) any later version.
|
|||
#include "inc/CharInfo.h"
|
||||
#include "inc/Slot.h"
|
||||
#include "inc/Main.h"
|
||||
#include <math.h>
|
||||
#include <cmath>
|
||||
|
||||
using namespace graphite2;
|
||||
|
||||
|
@ -70,6 +70,13 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
|
|||
if (width < 0 && !(silf()->flags()))
|
||||
return width;
|
||||
|
||||
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
|
||||
{
|
||||
reverseSlots();
|
||||
s = pFirst;
|
||||
pFirst = pLast;
|
||||
pLast = s;
|
||||
}
|
||||
if (!pFirst) pFirst = pSlot;
|
||||
while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
|
||||
if (!pLast) pLast = last();
|
||||
|
@ -170,7 +177,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
|
|||
}
|
||||
}
|
||||
currWidth += diff - error;
|
||||
} while (i == 0 && int(abs(error)) > 0 && tWeight);
|
||||
} while (i == 0 && int(std::abs(error)) > 0 && tWeight);
|
||||
}
|
||||
|
||||
Slot *oldFirst = m_first;
|
||||
|
@ -203,7 +210,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
|
|||
if (dbgout)
|
||||
{
|
||||
*dbgout << json::item << json::close; // Close up the passes array
|
||||
positionSlots(NULL, pSlot, pLast);
|
||||
positionSlots(NULL, pSlot, pLast, m_dir);
|
||||
Slot *lEnd = pLast->nextSibling();
|
||||
*dbgout << "output" << json::array;
|
||||
for(Slot * t = pSlot; t != lEnd; t = t->next())
|
||||
|
@ -212,7 +219,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
|
|||
}
|
||||
#endif
|
||||
|
||||
res = positionSlots(font, pSlot, pLast);
|
||||
res = positionSlots(font, pSlot, pLast, m_dir);
|
||||
|
||||
if (silf()->flags() & 1)
|
||||
{
|
||||
|
@ -221,6 +228,9 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
|
|||
}
|
||||
m_first = oldFirst;
|
||||
m_last = oldLast;
|
||||
|
||||
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
|
||||
reverseSlots();
|
||||
return res.x;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,13 @@ using namespace graphite2;
|
|||
using vm::Machine;
|
||||
typedef Machine::Code Code;
|
||||
|
||||
enum KernCollison
|
||||
{
|
||||
None = 0,
|
||||
CrossSpace = 1,
|
||||
InWord = 2,
|
||||
reserved = 3
|
||||
};
|
||||
|
||||
Pass::Pass()
|
||||
: m_silf(0),
|
||||
|
@ -53,16 +60,20 @@ Pass::Pass()
|
|||
m_states(0),
|
||||
m_codes(0),
|
||||
m_progs(0),
|
||||
m_flags(0),
|
||||
m_numCollRuns(0),
|
||||
m_kernColls(0),
|
||||
m_iMaxLoop(0),
|
||||
m_numGlyphs(0),
|
||||
m_numRules(0),
|
||||
m_numStates(0),
|
||||
m_numTransition(0),
|
||||
m_numSuccess(0),
|
||||
m_successStart(0),
|
||||
m_numColumns(0),
|
||||
m_minPreCtxt(0),
|
||||
m_maxPreCtxt(0)
|
||||
m_maxPreCtxt(0),
|
||||
m_colThreshold(0),
|
||||
m_isReverseDir(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -88,13 +99,17 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
|
|||
|
||||
if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
|
||||
// Read in basic values
|
||||
m_flags = be::read<byte>(p);
|
||||
if (e.test((m_flags & 15) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS))
|
||||
const byte flags = be::read<byte>(p);
|
||||
if (e.test((flags & 0x1f) && pt < PASS_TYPE_POSITIONING, E_BADCOLLISIONPASS))
|
||||
return face.error(e);
|
||||
m_numCollRuns = flags & 0x7;
|
||||
m_kernColls = (flags >> 3) & 0x3;
|
||||
m_isReverseDir = (flags >> 5) & 0x1;
|
||||
m_iMaxLoop = be::read<byte>(p);
|
||||
if (m_iMaxLoop < 1) m_iMaxLoop = 1;
|
||||
be::skip<byte>(p,2); // skip maxContext & maxBackup
|
||||
m_numRules = be::read<uint16>(p);
|
||||
if (e.test(!m_numRules && !(m_flags & 7), E_BADEMPTYPASS)) return face.error(e);
|
||||
if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e);
|
||||
be::skip<uint16>(p); // fsmOffset - not sure why we would want this
|
||||
const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
|
||||
* const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
|
||||
|
@ -152,7 +167,7 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
|
|||
const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
|
||||
be::skip<uint16>(p, m_numRules + 1);
|
||||
const byte * const states = p;
|
||||
if (e.test(p + 2 * m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e);
|
||||
if (e.test(p + 2u*m_numTransition*m_numColumns >= pass_end, E_BADPASSLENGTH)) return face.error(e);
|
||||
be::skip<int16>(p, m_numTransition*m_numColumns);
|
||||
be::skip<uint8>(p);
|
||||
if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
|
||||
|
@ -212,9 +227,10 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
|
|||
// Allocate pools
|
||||
m_rules = new Rule [m_numRules];
|
||||
m_codes = new Code [m_numRules*2];
|
||||
m_progs = static_cast<byte *>(malloc((ac_end - ac_data + rc_end - rc_data)
|
||||
*(sizeof(vm::instr)+sizeof(byte))));
|
||||
byte * prog_pool_free = m_progs;
|
||||
const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data);
|
||||
m_progs = gralloc<byte>(prog_pool_sz);
|
||||
byte * prog_pool_free = m_progs,
|
||||
* prog_pool_end = m_progs + prog_pool_sz;
|
||||
if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
|
||||
|
||||
Rule * r = m_rules + m_numRules - 1;
|
||||
|
@ -233,10 +249,11 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
|
|||
rc_begin = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
|
||||
|
||||
if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
|
||||
|| rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end)
|
||||
|| rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
|
||||
|| vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin) > size_t(prog_pool_end - prog_pool_free))
|
||||
return false;
|
||||
r->action = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
|
||||
r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, prog_pool_free);
|
||||
r->action = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
|
||||
r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
|
||||
|
||||
if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
|
||||
|| e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
|
||||
|
@ -245,19 +262,21 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
|
|||
return face.error(e);
|
||||
}
|
||||
|
||||
// Shrink the program pool
|
||||
ptrdiff_t const delta = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs)) - m_progs;
|
||||
if (delta)
|
||||
byte * moved_progs = static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs));
|
||||
if (e.test(!moved_progs, E_OUTOFMEM)) return face.error(e);
|
||||
|
||||
if (moved_progs != m_progs)
|
||||
{
|
||||
m_progs += delta;
|
||||
for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
|
||||
{
|
||||
c->externalProgramMoved(delta);
|
||||
c->externalProgramMoved(moved_progs - m_progs);
|
||||
}
|
||||
m_progs = moved_progs;
|
||||
}
|
||||
|
||||
// Load the rule entries map
|
||||
face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
|
||||
//TODO: Coverty: 1315804: FORWARD_NULL
|
||||
RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
|
||||
if (e.test(!re, E_OUTOFMEM)) return face.error(e);
|
||||
for (size_t n = num_entries; n; --n, ++re)
|
||||
|
@ -360,10 +379,11 @@ bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
|
|||
}
|
||||
|
||||
|
||||
bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const
|
||||
bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const
|
||||
{
|
||||
Slot *s = m.slotMap().segment.first();
|
||||
if (!s || !testPassConstraint(m)) return true;
|
||||
if (reverse) m.slotMap().segment.reverseSlots();
|
||||
if (m_numRules)
|
||||
{
|
||||
Slot *currHigh = s->next();
|
||||
|
@ -387,23 +407,25 @@ bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const
|
|||
}
|
||||
} while (s);
|
||||
}
|
||||
//TODO: Use enums for flags
|
||||
const bool collisions = m_numCollRuns || m_kernColls;
|
||||
|
||||
if (!(m_flags & 15) || !m.slotMap().segment.hasCollisionInfo())
|
||||
if (!collisions || !m.slotMap().segment.hasCollisionInfo())
|
||||
return true;
|
||||
|
||||
if (m_flags & 7)
|
||||
if (m_numCollRuns)
|
||||
{
|
||||
if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
|
||||
{
|
||||
m.slotMap().segment.positionSlots(0, 0, 0, true);
|
||||
m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true);
|
||||
// m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
|
||||
}
|
||||
if (!collisionShift(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
|
||||
if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
|
||||
return false;
|
||||
}
|
||||
if ((m_flags & 24) && !collisionKern(&m.slotMap().segment, m.slotMap().segment.dir(), fsm.dbgout))
|
||||
if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
|
||||
return false;
|
||||
if ((m_flags & 15) && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
|
||||
if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -478,8 +500,8 @@ void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) cons
|
|||
if (r != re)
|
||||
{
|
||||
const int adv = doAction(r->rule->action, slot, m);
|
||||
dumpRuleEventOutput(fsm, *r->rule, slot);
|
||||
if (r->rule->action->deletes()) fsm.slots.collectGarbage();
|
||||
dumpRuleEventOutput(fsm, m, *r->rule, slot);
|
||||
if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
|
||||
adjustSlot(adv, slot, fsm.slots);
|
||||
*fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
|
||||
<< json::close; // Close RuelEvent object
|
||||
|
@ -501,7 +523,7 @@ void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) cons
|
|||
if (r != re)
|
||||
{
|
||||
const int adv = doAction(r->rule->action, slot, m);
|
||||
if (r->rule->action->deletes()) fsm.slots.collectGarbage();
|
||||
if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
|
||||
adjustSlot(adv, slot, fsm.slots);
|
||||
return;
|
||||
}
|
||||
|
@ -533,7 +555,7 @@ void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEnt
|
|||
}
|
||||
|
||||
|
||||
void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
|
||||
void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, Machine & m, const Rule & r, Slot * const last_slot) const
|
||||
{
|
||||
*fsm.dbgout << json::item << json::flat << json::object
|
||||
<< "id" << &r - m_rules
|
||||
|
@ -551,7 +573,7 @@ void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, S
|
|||
<< json::close // close "input"
|
||||
<< "slots" << json::array;
|
||||
const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance();
|
||||
fsm.slots.segment.positionSlots(0);
|
||||
fsm.slots.segment.positionSlots(0, 0, 0, m.slotMap().dir());
|
||||
|
||||
for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next())
|
||||
*fsm.dbgout << dslot(&fsm.slots.segment, slot);
|
||||
|
@ -607,12 +629,16 @@ bool Pass::testConstraint(const Rule & r, Machine & m) const
|
|||
}
|
||||
|
||||
|
||||
void SlotMap::collectGarbage()
|
||||
void SlotMap::collectGarbage(Slot * &aSlot)
|
||||
{
|
||||
for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
|
||||
Slot *& slot = *s;
|
||||
if(slot->isDeleted() || slot->isCopied())
|
||||
{
|
||||
if (slot == aSlot)
|
||||
aSlot = slot->prev() ? slot->prev() : slot->next();
|
||||
segment.freeSlot(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -681,7 +707,6 @@ bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
|
|||
{
|
||||
ShiftCollider shiftcoll(dbgout);
|
||||
// bool isfirst = true;
|
||||
const uint8 numLoops = m_flags & 7; // number of loops permitted to fix collisions; does not include kerning
|
||||
bool hasCollisions = false;
|
||||
Slot *start = seg->first(); // turn on collision fixing for the first slot
|
||||
Slot *end = NULL;
|
||||
|
@ -690,7 +715,7 @@ bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
|
|||
#if !defined GRAPHITE2_NTRACING
|
||||
if (dbgout)
|
||||
*dbgout << "collisions" << json::array
|
||||
<< json::flat << json::object << "num-loops" << numLoops << json::close;
|
||||
<< json::flat << json::object << "num-loops" << m_numCollRuns << json::close;
|
||||
#endif
|
||||
|
||||
while (start)
|
||||
|
@ -707,7 +732,7 @@ bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
|
|||
if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
|
||||
&& !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
|
||||
return false;
|
||||
if (s != start && c->flags() & SlotCollision::COLL_END)
|
||||
if (s != start && (c->flags() & SlotCollision::COLL_END))
|
||||
{
|
||||
end = s->next();
|
||||
break;
|
||||
|
@ -720,7 +745,7 @@ bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
|
|||
#endif
|
||||
|
||||
// phase 2 : loop until happy.
|
||||
for (int i = 0; i < numLoops - 1; ++i)
|
||||
for (int i = 0; i < m_numCollRuns - 1; ++i)
|
||||
{
|
||||
if (hasCollisions || moved)
|
||||
{
|
||||
|
@ -918,7 +943,7 @@ bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
|
|||
|| (rtl ^ ignoreForKern)) // or it comes before(ltr) or after(rtl)
|
||||
&& (!isRev // if processing forwards then good to merge otherwise only:
|
||||
|| !(cNbor->flags() & SlotCollision::COLL_FIX) // merge in immovable stuff
|
||||
|| (cNbor->flags() & SlotCollision::COLL_KERN && !sameCluster) // ignore other kernable clusters
|
||||
|| ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster) // ignore other kernable clusters
|
||||
|| (cNbor->flags() & SlotCollision::COLL_ISCOL)) // test against other collided glyphs
|
||||
&& !coll.mergeSlot(seg, nbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
|
||||
return false;
|
||||
|
@ -944,7 +969,7 @@ bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
|
|||
Rect bbox;
|
||||
Position here = slotFix->origin() + shift;
|
||||
float clusterMin = here.x;
|
||||
slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, false);
|
||||
slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1002,7 +1027,7 @@ float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start
|
|||
SlotCollision *cNbor = seg->collisionInfo(nbor);
|
||||
if (bb.bl.y == 0.f && bb.tr.y == 0.f)
|
||||
{
|
||||
if ((m_flags & 24) == 16)
|
||||
if (m_kernColls == InWord)
|
||||
break;
|
||||
// Add space for a space glyph.
|
||||
currSpace += nbor->advance();
|
||||
|
|
|
@ -40,7 +40,7 @@ using namespace graphite2;
|
|||
|
||||
SegCache::SegCache(const SegCacheStore * store, const Features & feats)
|
||||
: m_prefixLength(ePrefixLength),
|
||||
m_maxCachedSegLength(eMaxSpliceSize),
|
||||
// m_maxCachedSegLength(eMaxSpliceSize),
|
||||
m_segmentCount(0),
|
||||
m_features(feats),
|
||||
m_totalAccessCount(0l), m_totalMisses(0l),
|
||||
|
@ -84,7 +84,7 @@ SegCacheEntry* SegCache::cache(SegCacheStore * store, const uint16* cmapGlyphs,
|
|||
{
|
||||
uint16 pos = 0;
|
||||
if (!length) return NULL;
|
||||
assert(length < m_maxCachedSegLength);
|
||||
// assert(length < m_maxCachedSegLength);
|
||||
SegCachePrefixArray pArray = m_prefixes;
|
||||
while (pos + 1 < m_prefixLength)
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@ of the License or (at your option) any later version.
|
|||
#include "inc/Slot.h"
|
||||
#include "inc/Main.h"
|
||||
#include "inc/CmapCache.h"
|
||||
#include "inc/Bidi.h"
|
||||
//#include "inc/Bidi.h"
|
||||
#include "inc/Collider.h"
|
||||
#include "graphite2/Segment.h"
|
||||
|
||||
|
@ -68,8 +68,10 @@ Segment::~Segment()
|
|||
{
|
||||
for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
|
||||
free(*i);
|
||||
for (AttributeRope::iterator j = m_userAttrs.begin(); j != m_userAttrs.end(); ++j)
|
||||
free(*j);
|
||||
for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
|
||||
free(*i);
|
||||
for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
|
||||
free(*i);
|
||||
delete[] m_charinfo;
|
||||
}
|
||||
|
||||
|
@ -154,6 +156,12 @@ void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
|
|||
aSlot->originate(id);
|
||||
aSlot->before(id);
|
||||
aSlot->after(id);
|
||||
// uint8 aBidi = m_silf->aBidi();
|
||||
// if (aBidi != 0xFF)
|
||||
// {
|
||||
// unsigned int bAttr = glyphAttr(gid, aBidi);
|
||||
// aSlot->setBidiClass((bAttr <= 22) * bAttr);
|
||||
// }
|
||||
if (m_last) m_last->next(aSlot);
|
||||
aSlot->prev(m_last);
|
||||
m_last = aSlot;
|
||||
|
@ -167,6 +175,9 @@ Slot *Segment::newSlot()
|
|||
{
|
||||
if (!m_freeSlots)
|
||||
{
|
||||
// check that the segment doesn't grow indefinintely
|
||||
if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
|
||||
return NULL;
|
||||
int numUser = m_silf->numUser();
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
if (m_face->logger()) ++numUser;
|
||||
|
@ -176,9 +187,8 @@ Slot *Segment::newSlot()
|
|||
if (!newSlots || !newAttrs) return NULL;
|
||||
for (size_t i = 0; i < m_bufSize; i++)
|
||||
{
|
||||
::new (newSlots + i) Slot(newAttrs + i * numUser);
|
||||
newSlots[i].next(newSlots + i + 1);
|
||||
newSlots[i].userAttrs(newAttrs + i * numUser);
|
||||
newSlots[i].setBidiClass(-1);
|
||||
}
|
||||
newSlots[m_bufSize - 1].next(NULL);
|
||||
newSlots[0].next(NULL);
|
||||
|
@ -205,7 +215,7 @@ void Segment::freeSlot(Slot *aSlot)
|
|||
aSlot->removeChild(aSlot->firstChild());
|
||||
}
|
||||
// reset the slot incase it is reused
|
||||
::new (aSlot) Slot;
|
||||
::new (aSlot) Slot(aSlot->userAttrs());
|
||||
memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
|
||||
// Update generation counter for debug
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
|
@ -309,6 +319,61 @@ void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
|
|||
}
|
||||
#endif // GRAPHITE2_NSEGCACHE
|
||||
|
||||
// reverse the slots but keep diacritics in their same position after their bases
|
||||
void Segment::reverseSlots()
|
||||
{
|
||||
m_dir = m_dir ^ 64; // invert the reverse flag
|
||||
if (m_first == m_last) return; // skip 0 or 1 glyph runs
|
||||
|
||||
Slot *t = 0;
|
||||
Slot *curr = m_first;
|
||||
Slot *tlast;
|
||||
Slot *tfirst;
|
||||
Slot *out = 0;
|
||||
|
||||
while (curr && getSlotBidiClass(curr) == 16)
|
||||
curr = curr->next();
|
||||
if (!curr) return;
|
||||
tfirst = curr->prev();
|
||||
tlast = curr;
|
||||
|
||||
while (curr)
|
||||
{
|
||||
if (getSlotBidiClass(curr) == 16)
|
||||
{
|
||||
Slot *d = curr->next();
|
||||
while (d && getSlotBidiClass(d) == 16)
|
||||
d = d->next();
|
||||
|
||||
d = d ? d->prev() : m_last;
|
||||
Slot *p = out->next(); // one after the diacritics. out can't be null
|
||||
if (p)
|
||||
p->prev(d);
|
||||
else
|
||||
tlast = d;
|
||||
t = d->next();
|
||||
d->next(p);
|
||||
curr->prev(out);
|
||||
out->next(curr);
|
||||
}
|
||||
else // will always fire first time round the loop
|
||||
{
|
||||
if (out)
|
||||
out->prev(curr);
|
||||
t = curr->next();
|
||||
curr->next(out);
|
||||
out = curr;
|
||||
}
|
||||
curr = t;
|
||||
}
|
||||
out->prev(tfirst);
|
||||
if (tfirst)
|
||||
tfirst->next(out);
|
||||
else
|
||||
m_first = out;
|
||||
m_last = tlast;
|
||||
}
|
||||
|
||||
void Segment::linkClusters(Slot *s, Slot * end)
|
||||
{
|
||||
end = end->next();
|
||||
|
@ -338,7 +403,7 @@ void Segment::linkClusters(Slot *s, Slot * end)
|
|||
}
|
||||
}
|
||||
|
||||
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isFinal)
|
||||
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
|
||||
{
|
||||
Position currpos(0., 0.);
|
||||
float clusterMin = 0.;
|
||||
|
@ -347,12 +412,12 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bo
|
|||
if (!iStart) iStart = m_first;
|
||||
if (!iEnd) iEnd = m_last;
|
||||
|
||||
if (m_dir & 1)
|
||||
if (isRtl)
|
||||
{
|
||||
for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
|
||||
{
|
||||
if (s->isBase())
|
||||
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
|
||||
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -360,7 +425,7 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bo
|
|||
for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
|
||||
{
|
||||
if (s->isBase())
|
||||
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isFinal);
|
||||
currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
|
||||
}
|
||||
}
|
||||
return currpos;
|
||||
|
@ -437,12 +502,13 @@ bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NU
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &stack);
|
||||
void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror);
|
||||
void resolveWhitespace(int baseLevel, Slot *s);
|
||||
Slot *resolveOrder(Slot * & s, const bool reordered, const int level = 0);
|
||||
|
||||
void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
|
||||
void Segment::bidiPass(int paradir, uint8 aMirror)
|
||||
{
|
||||
if (slotCount() == 0)
|
||||
return;
|
||||
|
@ -453,11 +519,8 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
|
|||
unsigned int ssize = 0;
|
||||
for (s = first(); s; s = s->next())
|
||||
{
|
||||
if (s->getBidiClass() == -1)
|
||||
{
|
||||
unsigned int bAttr = glyphAttr(s->gid(), aBidi);
|
||||
s->setBidiClass((bAttr <= 22) * bAttr);
|
||||
}
|
||||
if (getSlotBidiClass(s) < 0)
|
||||
s->setBidiClass(0);
|
||||
bmask |= (1 << s->getBidiClass());
|
||||
s->setBidiLevel(baseLevel);
|
||||
if (s->getBidiClass() == 21)
|
||||
|
@ -489,6 +552,18 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Segment::doMirror(uint16 aMirror)
|
||||
{
|
||||
Slot * s;
|
||||
for (s = m_first; s; s = s->next())
|
||||
{
|
||||
unsigned short g = glyphAttr(s->gid(), aMirror);
|
||||
if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
|
||||
s->setGlyph(this, g);
|
||||
}
|
||||
}
|
||||
|
||||
bool Segment::initCollisions()
|
||||
{
|
||||
|
|
|
@ -51,6 +51,7 @@ Silf::Silf() throw()
|
|||
m_jPass(0),
|
||||
m_bPass(0),
|
||||
m_flags(0),
|
||||
m_dir(0),
|
||||
m_aPseudo(0),
|
||||
m_aBreak(0),
|
||||
m_aUser(0),
|
||||
|
@ -58,6 +59,7 @@ Silf::Silf() throw()
|
|||
m_aMirror(0),
|
||||
m_aPassBits(0),
|
||||
m_iMaxComp(0),
|
||||
m_aCollision(0),
|
||||
m_aLig(0),
|
||||
m_numPseudo(0),
|
||||
m_nClass(0),
|
||||
|
@ -141,10 +143,10 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
|
|||
}
|
||||
|
||||
if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
|
||||
m_aLig = be::read<uint16>(p);
|
||||
m_aUser = be::read<uint8>(p);
|
||||
m_iMaxComp = be::read<uint8>(p);
|
||||
be::skip<byte>(p); // direction
|
||||
m_aLig = be::read<uint16>(p);
|
||||
m_aUser = be::read<uint8>(p);
|
||||
m_iMaxComp = be::read<uint8>(p);
|
||||
m_dir = be::read<uint8>(p) - 1;
|
||||
m_aCollision = be::read<uint8>(p);
|
||||
be::skip<byte>(p,3);
|
||||
be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
|
||||
|
@ -198,7 +200,9 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face,
|
|||
const byte * const pass_start = silf_start + be::read<uint32>(o_passes),
|
||||
* const pass_end = silf_start + be::peek<uint32>(o_passes);
|
||||
face.error_context((face.error_context() & 0xFF00) + EC_ASILF + (i << 16));
|
||||
if (e.test(pass_start > pass_end, E_BADPASSSTART) || e.test(pass_end > silf_end, E_BADPASSEND)) {
|
||||
if (e.test(pass_start > pass_end, E_BADPASSSTART)
|
||||
|| e.test(pass_start < passes_start, E_BADPASSSTART)
|
||||
|| e.test(pass_end > silf_end, E_BADPASSEND)) {
|
||||
releaseBuffers(); return face.error(e);
|
||||
}
|
||||
|
||||
|
@ -268,6 +272,9 @@ size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error
|
|||
|
||||
if (max_off == ERROROFFSET) return ERROROFFSET;
|
||||
|
||||
if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
|
||||
return ERROROFFSET;
|
||||
|
||||
// Check the linear offsets are sane, these must be monotonically increasing.
|
||||
for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
|
||||
if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
|
||||
|
@ -283,10 +290,10 @@ size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error
|
|||
for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
|
||||
{
|
||||
const uint16 * lookup = m_classData + *o;
|
||||
if (e.test(*o > max_off - 4, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
|
||||
if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
|
||||
|| e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
|
||||
|| lookup[0] > (max_off - *o - 4)/2 // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
|
||||
|| lookup[3] != lookup[0] - lookup[1], E_BADCLASSLOOKUPINFO)) // rangeShift: numIDs - searchRange
|
||||
|| lookup[0] * 2 + *o + 4 > max_off // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
|
||||
|| lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO)) // rangeShift: numIDs - searchRange
|
||||
return ERROROFFSET;
|
||||
}
|
||||
|
||||
|
@ -307,7 +314,7 @@ uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
|
|||
const uint16 * cls = m_classData + m_classOffsets[cid];
|
||||
if (cid < m_nLinear) // output class being used for input, shouldn't happen
|
||||
{
|
||||
for (unsigned int i = 0, n = m_classOffsets[cid + 1]; i < n; ++i, ++cls)
|
||||
for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
|
||||
if (*cls == gid) return i;
|
||||
return -1;
|
||||
}
|
||||
|
@ -348,7 +355,7 @@ uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
|
|||
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
|
||||
{
|
||||
assert(seg != 0);
|
||||
SlotMap map(*seg);
|
||||
SlotMap map(*seg, m_dir);
|
||||
FiniteStateMachine fsm(map, seg->getFace()->logger());
|
||||
vm::Machine m(map);
|
||||
unsigned int initSize = seg->slotCount();
|
||||
|
@ -363,7 +370,7 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
|
|||
return true;
|
||||
lastPass = m_numPasses;
|
||||
}
|
||||
if (firstPass <= lbidi && lastPass >= lbidi && dobidi)
|
||||
if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
|
||||
lastPass++;
|
||||
else
|
||||
lbidi = 0xFF;
|
||||
|
@ -379,7 +386,7 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
|
|||
*dbgout << json::item << json::object
|
||||
<< "id" << -1
|
||||
<< "slots" << json::array;
|
||||
seg->positionSlots(0);
|
||||
seg->positionSlots(0, 0, 0, m_dir);
|
||||
for(Slot * s = seg->first(); s; s = s->next())
|
||||
*dbgout << dslot(seg, s);
|
||||
*dbgout << json::close
|
||||
|
@ -387,22 +394,13 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
|
|||
<< json::close;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(seg->dir() & 2))
|
||||
seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror);
|
||||
else if (m_aMirror && (seg->dir() & 1))
|
||||
{
|
||||
Slot * s;
|
||||
for (s = seg->first(); s; s = s->next())
|
||||
{
|
||||
unsigned short g = seg->glyphAttr(s->gid(), m_aMirror);
|
||||
if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1)))
|
||||
s->setGlyph(seg, g);
|
||||
}
|
||||
}
|
||||
if (seg->currdir() != m_dir)
|
||||
seg->reverseSlots();
|
||||
if (m_aMirror && (seg->dir() & 3) == 3)
|
||||
seg->doMirror(m_aMirror);
|
||||
--i;
|
||||
lbidi = lastPass;
|
||||
--lastPass;
|
||||
lbidi = 0xFF;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -412,7 +410,7 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
|
|||
*dbgout << json::item << json::object
|
||||
<< "id" << i+1
|
||||
<< "slots" << json::array;
|
||||
seg->positionSlots(0);
|
||||
seg->positionSlots(0, 0, 0, m_dir);
|
||||
for(Slot * s = seg->first(); s; s = s->next())
|
||||
*dbgout << dslot(seg, s);
|
||||
*dbgout << json::close;
|
||||
|
@ -420,13 +418,13 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi
|
|||
#endif
|
||||
|
||||
// test whether to reorder, prepare for positioning
|
||||
if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || (m_passes[i].flags() & 7))
|
||||
&& !m_passes[i].runGraphite(m, fsm))
|
||||
bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
|
||||
if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
|
||||
&& !m_passes[i].runGraphite(m, fsm, reverse))
|
||||
return false;
|
||||
// only subsitution passes can change segment length, cached subsegments are short for their text
|
||||
if (m.status() != vm::Machine::finished
|
||||
|| (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR
|
||||
|| (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))))
|
||||
|| (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -34,15 +34,14 @@ of the License or (at your option) any later version.
|
|||
|
||||
using namespace graphite2;
|
||||
|
||||
Slot::Slot() :
|
||||
Slot::Slot(int16 *user_attrs) :
|
||||
m_next(NULL), m_prev(NULL),
|
||||
m_glyphid(0), m_realglyphid(0), m_original(0), m_before(0), m_after(0),
|
||||
m_index(0), m_parent(NULL), m_child(NULL), m_sibling(NULL),
|
||||
m_position(0, 0), m_shift(0, 0), m_advance(0, 0),
|
||||
m_attach(0, 0), m_with(0, 0), m_just(0.),
|
||||
m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0), m_justs(NULL)
|
||||
// Do not set m_userAttr since it is set *before* new is called since this
|
||||
// is used as a positional new to reset the GrSlot
|
||||
m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0),
|
||||
m_userAttr(user_attrs), m_justs(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -86,17 +85,17 @@ void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
|
|||
m_position = m_position + relpos;
|
||||
}
|
||||
|
||||
Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal)
|
||||
Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal)
|
||||
{
|
||||
SlotCollision *coll = NULL;
|
||||
if (attrLevel && m_attLevel > attrLevel) return Position(0, 0);
|
||||
float scale = font ? font->scale() : 1.0f;
|
||||
Position shift(m_shift.x * ((seg->dir() & 1) * -2 + 1) + m_just, m_shift.y);
|
||||
Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
|
||||
float tAdvance = m_advance.x + m_just;
|
||||
if (isFinal && (coll = seg->collisionInfo(this)))
|
||||
{
|
||||
const Position &collshift = coll->offset();
|
||||
if (!(coll->flags() & SlotCollision::COLL_KERN) || (seg->dir() & 1))
|
||||
if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
|
||||
shift = shift + collshift;
|
||||
}
|
||||
const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
|
||||
|
@ -134,13 +133,13 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R
|
|||
|
||||
if (m_child && m_child != this && m_child->attachedTo() == this)
|
||||
{
|
||||
Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, isFinal);
|
||||
Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal);
|
||||
if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
|
||||
}
|
||||
|
||||
if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent)
|
||||
{
|
||||
Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, isFinal);
|
||||
Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal);
|
||||
if (tRes.x > res.x) res = tRes;
|
||||
}
|
||||
|
||||
|
@ -154,12 +153,14 @@ Position Slot::finalise(const Segment *seg, const Font *font, Position & base, R
|
|||
return res;
|
||||
}
|
||||
|
||||
int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
|
||||
int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel, bool rtl)
|
||||
{
|
||||
Position base;
|
||||
if (glyph() >= seg->getFace()->glyphs().numGlyphs())
|
||||
return 0;
|
||||
Rect bbox = seg->theGlyphBBoxTemporary(glyph());
|
||||
float clusterMin = 0.;
|
||||
Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, false);
|
||||
Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
|
||||
|
||||
switch (metrics(metric))
|
||||
{
|
||||
|
@ -192,7 +193,6 @@ int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel)
|
|||
|
||||
int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
|
||||
{
|
||||
if (!this) return 0;
|
||||
if (ind == gr_slatUserDefnV1)
|
||||
{
|
||||
ind = gr_slatUserDefn;
|
||||
|
@ -220,9 +220,7 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
|
|||
case gr_slatAttLevel : return m_attLevel;
|
||||
case gr_slatBreak : return seg->charinfo(m_original)->breakWeight();
|
||||
case gr_slatCompRef : return 0;
|
||||
case gr_slatDir : if (m_bidiCls == -1)
|
||||
const_cast<Slot *>(this)->setBidiClass(int8(seg->glyphAttr(gid(), seg->silf()->aBidi())));
|
||||
return m_bidiCls;
|
||||
case gr_slatDir : return seg->dir() & 1;
|
||||
case gr_slatInsert : return isInsertBefore();
|
||||
case gr_slatPosX : return int(m_position.x); // but need to calculate it
|
||||
case gr_slatPosY : return int(m_position.y);
|
||||
|
@ -272,7 +270,6 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
|
|||
|
||||
void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, const SlotMap & map)
|
||||
{
|
||||
if (!this) return;
|
||||
if (ind == gr_slatUserDefnV1)
|
||||
{
|
||||
ind = gr_slatUserDefn;
|
||||
|
@ -299,7 +296,7 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons
|
|||
if (!other->isChildOf(this) && other->child(this))
|
||||
{
|
||||
attachTo(other);
|
||||
if (((seg->dir() & 1) != 0) ^ (idx > subindex))
|
||||
if ((map.dir() != 0) ^ (idx > subindex))
|
||||
m_with = Position(advance(), 0);
|
||||
else // normal match to previous root
|
||||
m_attach = Position(other->advance(), 0);
|
||||
|
@ -322,7 +319,7 @@ void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, cons
|
|||
seg->charinfo(m_original)->breakWeight(value);
|
||||
break;
|
||||
case gr_slatCompRef : break; // not sure what to do here
|
||||
case gr_slatDir : m_bidiCls = int8(value); break;
|
||||
case gr_slatDir : break;
|
||||
case gr_slatInsert :
|
||||
markInsertBefore(value? true : false);
|
||||
break;
|
||||
|
@ -450,6 +447,7 @@ bool Slot::removeSibling(Slot *ap)
|
|||
void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
|
||||
{
|
||||
m_glyphid = glyphid;
|
||||
m_bidiCls = -1;
|
||||
if (!theGlyph)
|
||||
{
|
||||
theGlyph = seg->getFace()->glyphs().glyphSafe(glyphid);
|
||||
|
@ -461,6 +459,8 @@ void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
|
|||
}
|
||||
}
|
||||
m_realglyphid = theGlyph->attrs()[seg->silf()->aPseudo()];
|
||||
if (m_realglyphid > seg->getFace()->glyphs().numGlyphs())
|
||||
m_realglyphid = 0;
|
||||
const GlyphFace *aGlyph = theGlyph;
|
||||
if (m_realglyphid)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,7 @@ of the License or (at your option) any later version.
|
|||
|
||||
using namespace graphite2;
|
||||
|
||||
sparse::chunk sparse::empty_chunk = {0,0};
|
||||
const sparse::chunk sparse::empty_chunk = {0,0};
|
||||
|
||||
sparse::~sparse() throw()
|
||||
{
|
||||
|
|
|
@ -62,8 +62,10 @@ Description
|
|||
***********************************************************************************************/
|
||||
namespace
|
||||
{
|
||||
#ifdef ALL_TTFUTILS
|
||||
// max number of components allowed in composite glyphs
|
||||
const int kMaxGlyphComponents = 8;
|
||||
#endif
|
||||
|
||||
template <int R, typename T>
|
||||
inline float fixed_to_float(const T f) {
|
||||
|
@ -227,7 +229,7 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
using namespace Sfnt;
|
||||
|
||||
if (pTable == 0) return false;
|
||||
if (pTable == 0 || lTableSize < 4) return false;
|
||||
|
||||
switch(TableId)
|
||||
{
|
||||
|
@ -235,6 +237,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::CharacterCodeMap * const pCmap
|
||||
= reinterpret_cast<const Sfnt::CharacterCodeMap *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::CharacterCodeMap))
|
||||
return false;
|
||||
return be::swap(pCmap->version) == 0;
|
||||
}
|
||||
|
||||
|
@ -242,6 +246,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::FontHeader * const pHead
|
||||
= reinterpret_cast<const Sfnt::FontHeader *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::FontHeader))
|
||||
return false;
|
||||
bool r = be::swap(pHead->version) == OneFix
|
||||
&& be::swap(pHead->magic_number) == FontHeader::MagicNumber
|
||||
&& be::swap(pHead->glyph_data_format)
|
||||
|
@ -258,6 +264,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::PostScriptGlyphName * const pPost
|
||||
= reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::PostScriptGlyphName))
|
||||
return false;
|
||||
const fixed format = be::swap(pPost->format);
|
||||
bool r = format == PostScriptGlyphName::Format1
|
||||
|| format == PostScriptGlyphName::Format2
|
||||
|
@ -270,6 +278,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::HorizontalHeader * pHhea =
|
||||
reinterpret_cast<const Sfnt::HorizontalHeader *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::HorizontalHeader))
|
||||
return false;
|
||||
bool r = be::swap(pHhea->version) == OneFix
|
||||
&& be::swap(pHhea->metric_data_format) == 0
|
||||
&& sizeof (Sfnt::HorizontalHeader) <= lTableSize;
|
||||
|
@ -280,6 +290,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::MaximumProfile * pMaxp =
|
||||
reinterpret_cast<const Sfnt::MaximumProfile *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::MaximumProfile))
|
||||
return false;
|
||||
bool r = be::swap(pMaxp->version) == OneFix
|
||||
&& sizeof(Sfnt::MaximumProfile) <= lTableSize;
|
||||
return r;
|
||||
|
@ -324,6 +336,8 @@ bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
|
|||
{
|
||||
const Sfnt::FontNames * pName
|
||||
= reinterpret_cast<const Sfnt::FontNames *>(pTable);
|
||||
if (lTableSize < sizeof(Sfnt::FontNames))
|
||||
return false;
|
||||
return be::swap(pName->format) == 0;
|
||||
}
|
||||
|
||||
|
@ -796,7 +810,7 @@ bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void
|
|||
size_t cLongHorMetrics = be::swap(phhea->num_long_hor_metrics);
|
||||
if (nGlyphId < cLongHorMetrics)
|
||||
{ // glyph id is acceptable
|
||||
if (nGlyphId * sizeof(Sfnt::HorizontalMetric) >= lHmtxSize) return false;
|
||||
if ((nGlyphId + 1) * sizeof(Sfnt::HorizontalMetric) > lHmtxSize) return false;
|
||||
nAdvWid = be::swap(phmtx[nGlyphId].advance_width);
|
||||
nLsb = be::swap(phmtx[nGlyphId].left_side_bearing);
|
||||
}
|
||||
|
@ -806,7 +820,7 @@ bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void
|
|||
size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics +
|
||||
sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes
|
||||
// We test like this as LsbOffset is an offset not a length.
|
||||
if (lLsbOffset > lHmtxSize - sizeof(int16))
|
||||
if (lLsbOffset >= lHmtxSize - sizeof(int16) || cLongHorMetrics == 0)
|
||||
{
|
||||
nLsb = 0;
|
||||
return false;
|
||||
|
@ -873,7 +887,7 @@ const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int
|
|||
/*----------------------------------------------------------------------------------------------
|
||||
Check the Microsoft Unicode subtable for expected values
|
||||
----------------------------------------------------------------------------------------------*/
|
||||
bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
|
||||
bool CheckCmapSubtable4(const void * pCmapSubtable4, size_t table_len /*, unsigned int maxgid*/)
|
||||
{
|
||||
if (!pCmapSubtable4) return false;
|
||||
const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
|
||||
|
@ -882,6 +896,8 @@ bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
|
|||
if (be::swap(pTable->format) != 4) return false;
|
||||
const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);
|
||||
uint16 length = be::swap(pTable4->length);
|
||||
if (length > table_len)
|
||||
return false;
|
||||
if (length < sizeof(Sfnt::CmapSubTableFormat4))
|
||||
return false;
|
||||
uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1;
|
||||
|
@ -919,7 +935,7 @@ bool CheckCmapSubtable4(const void * pCmapSubtable4 /*, unsigned int maxgid*/)
|
|||
lastend = end;
|
||||
}
|
||||
#endif
|
||||
return true;;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------------------------
|
||||
|
@ -1062,7 +1078,7 @@ unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnico
|
|||
/*----------------------------------------------------------------------------------------------
|
||||
Check the Microsoft UCS-4 subtable for expected values.
|
||||
----------------------------------------------------------------------------------------------*/
|
||||
bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, unsigned int maxgid*/)
|
||||
bool CheckCmapSubtable12(const void *pCmapSubtable12, size_t table_len /*, unsigned int maxgid*/)
|
||||
{
|
||||
if (!pCmapSubtable12) return false;
|
||||
const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);
|
||||
|
@ -1070,6 +1086,8 @@ bool CheckCmapSubtable12(const void *pCmapSubtable12 /*, unsigned int maxgid*/)
|
|||
return false;
|
||||
const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmapSubtable12);
|
||||
uint32 length = be::swap(pTable12->length);
|
||||
if (length > table_len)
|
||||
return false;
|
||||
if (length < sizeof(Sfnt::CmapSubTableFormat12))
|
||||
return false;
|
||||
uint32 num_groups = be::swap(pTable12->num_groups);
|
||||
|
@ -1221,7 +1239,7 @@ size_t LocaLookup(gid16 nGlyphId,
|
|||
void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen)
|
||||
{
|
||||
const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf);
|
||||
if (nGlyfOffset == size_t(-1) || nGlyfOffset == size_t(-2) || nGlyfOffset >= nTableLen)
|
||||
if (nGlyfOffset + pByte < pByte || nGlyfOffset + sizeof(Sfnt::Glyph) >= nTableLen)
|
||||
return NULL;
|
||||
return const_cast<uint8 *>(pByte + nGlyfOffset);
|
||||
}
|
||||
|
@ -1833,7 +1851,6 @@ bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca
|
|||
This range is parallel to the prgnX & prgnY
|
||||
Return true if successful, false otherwise. On false, all points may be INT_MIN
|
||||
False may indicate a white space glyph, a multi-level composite, or a corrupt font
|
||||
// TODO: doesn't support composite glyphs whose components are themselves components
|
||||
It's not clear from the TTF spec when the transforms should be applied. Should the
|
||||
transform be done before or after attachment point calcs? (current code - before)
|
||||
Should the transform be applied to other offsets? (currently - no; however commented
|
||||
|
|
|
@ -70,6 +70,7 @@ struct regbank {
|
|||
SlotMap & smap;
|
||||
slotref * const map_base;
|
||||
const instr * & ip;
|
||||
uint8 direction;
|
||||
int8 flags;
|
||||
};
|
||||
|
||||
|
@ -86,6 +87,7 @@ namespace {
|
|||
#define map reg.map
|
||||
#define mapb reg.map_base
|
||||
#define flags reg.flags
|
||||
#define dir reg.direction
|
||||
|
||||
#include "inc/opcodes.h"
|
||||
|
||||
|
@ -96,6 +98,7 @@ namespace {
|
|||
#undef map
|
||||
#undef mapb
|
||||
#undef flags
|
||||
#undef dir
|
||||
}
|
||||
|
||||
Machine::stack_t Machine::run(const instr * program,
|
||||
|
@ -110,7 +113,7 @@ Machine::stack_t Machine::run(const instr * program,
|
|||
const byte * dp = data;
|
||||
stack_t * sp = _stack + Machine::STACK_GUARD,
|
||||
* const sb = sp;
|
||||
regbank reg = {*map, map, _map, _map.begin()+_map.context(), ip, 0};
|
||||
regbank reg = {*map, map, _map, _map.begin()+_map.context(), ip, _map.dir(), 0};
|
||||
|
||||
// Run the program
|
||||
while ((reinterpret_cast<ip_t>(*++ip))(dp, sp, sb, reg)) {}
|
||||
|
|
|
@ -61,6 +61,7 @@ const void * direct_run(const bool get_table_mode,
|
|||
const byte * data,
|
||||
Machine::stack_t * stack,
|
||||
slotref * & __map,
|
||||
uint8 _dir,
|
||||
SlotMap * __smap=0)
|
||||
{
|
||||
// We need to define and return to opcode table from within this function
|
||||
|
@ -79,6 +80,7 @@ const void * direct_run(const bool get_table_mode,
|
|||
slotref is = *__map,
|
||||
* map = __map,
|
||||
* const mapb = smap.begin()+smap.context();
|
||||
uint8 dir = _dir;
|
||||
int8 flags = 0;
|
||||
|
||||
// start the program
|
||||
|
@ -109,7 +111,7 @@ Machine::stack_t Machine::run(const instr * program,
|
|||
assert(program != 0);
|
||||
|
||||
const stack_t *sp = static_cast<const stack_t *>(
|
||||
direct_run(false, program, data, _stack, is, &_map));
|
||||
direct_run(false, program, data, _stack, is, _map.dir(), &_map));
|
||||
const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
|
||||
check_final_stack(sp);
|
||||
return ret;
|
||||
|
|
|
@ -47,7 +47,6 @@ $(_NS)_SOURCES = \
|
|||
$($(_NS)_BASE)/src/gr_segment.cpp \
|
||||
$($(_NS)_BASE)/src/gr_slot.cpp \
|
||||
$($(_NS)_BASE)/src/json.cpp \
|
||||
$($(_NS)_BASE)/src/Bidi.cpp \
|
||||
$($(_NS)_BASE)/src/CachedFace.cpp \
|
||||
$($(_NS)_BASE)/src/CmapCache.cpp \
|
||||
$($(_NS)_BASE)/src/Code.cpp \
|
||||
|
@ -78,13 +77,12 @@ $(_NS)_PRIVATE_HEADERS = \
|
|||
$($(_NS)_BASE)/src/inc/bits.h \
|
||||
$($(_NS)_BASE)/src/inc/debug.h \
|
||||
$($(_NS)_BASE)/src/inc/json.h \
|
||||
$($(_NS)_BASE)/src/inc/locale2lcid.h \
|
||||
$($(_NS)_BASE)/src/inc/Bidi.h \
|
||||
$($(_NS)_BASE)/src/inc/CachedFace.h \
|
||||
$($(_NS)_BASE)/src/inc/CharInfo.h \
|
||||
$($(_NS)_BASE)/src/inc/CmapCache.h \
|
||||
$($(_NS)_BASE)/src/inc/Code.h \
|
||||
$($(_NS)_BASE)/src/inc/Collider.h \
|
||||
$($(_NS)_BASE)/src/inc/Compression.h \
|
||||
$($(_NS)_BASE)/src/inc/Decompressor.h \
|
||||
$($(_NS)_BASE)/src/inc/Endian.h \
|
||||
$($(_NS)_BASE)/src/inc/Error.h \
|
||||
|
@ -110,7 +108,6 @@ $(_NS)_PRIVATE_HEADERS = \
|
|||
$($(_NS)_BASE)/src/inc/SegCacheEntry.h \
|
||||
$($(_NS)_BASE)/src/inc/SegCacheStore.h \
|
||||
$($(_NS)_BASE)/src/inc/Segment.h \
|
||||
$($(_NS)_BASE)/src/inc/Shrinker.h \
|
||||
$($(_NS)_BASE)/src/inc/Silf.h \
|
||||
$($(_NS)_BASE)/src/inc/Slot.h \
|
||||
$($(_NS)_BASE)/src/inc/Sparse.h \
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace
|
|||
delete pRes;
|
||||
return NULL;
|
||||
}
|
||||
pRes->finalise(font);
|
||||
pRes->finalise(font, true);
|
||||
|
||||
return static_cast<gr_segment*>(pRes);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,6 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
static byte * local_memory;
|
||||
class decoder;
|
||||
|
||||
instr * _code;
|
||||
|
@ -87,22 +86,24 @@ private:
|
|||
void failure(const status_t) throw();
|
||||
|
||||
public:
|
||||
static size_t estimateCodeDataOut(size_t num_bytecodes);
|
||||
|
||||
Code() throw();
|
||||
Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
|
||||
uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
|
||||
enum passtype pt, byte * & _out = local_memory);
|
||||
enum passtype pt, byte * * const _out = 0);
|
||||
Code(const Machine::Code &) throw();
|
||||
~Code() throw();
|
||||
|
||||
Code & operator=(const Code &rhs) throw();
|
||||
operator bool () const throw();
|
||||
status_t status() const throw();
|
||||
bool constraint() const throw();
|
||||
size_t dataSize() const throw();
|
||||
size_t instructionCount() const throw();
|
||||
bool immutable() const throw();
|
||||
bool deletes() const throw();
|
||||
size_t maxRef() const throw();
|
||||
operator bool () const throw() { return _code && status() == loaded; }
|
||||
status_t status() const throw() { return _status; }
|
||||
bool constraint() const throw() { return _constraint; }
|
||||
size_t dataSize() const throw() { return _data_size; }
|
||||
size_t instructionCount() const throw() { return _instr_count; }
|
||||
bool immutable() const throw() { return !(_delete || _modify); }
|
||||
bool deletes() const throw() { return _delete; }
|
||||
size_t maxRef() const throw() { return _max_ref; }
|
||||
void externalProgramMoved(ptrdiff_t) throw();
|
||||
|
||||
int32 run(Machine &m, slotref * & map) const;
|
||||
|
@ -110,10 +111,16 @@ public:
|
|||
CLASS_NEW_DELETE;
|
||||
};
|
||||
|
||||
inline
|
||||
size_t Machine::Code::estimateCodeDataOut(size_t n_bc)
|
||||
{
|
||||
return n_bc * (sizeof(instr)+sizeof(byte));
|
||||
}
|
||||
|
||||
|
||||
inline Machine::Code::Code() throw()
|
||||
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
|
||||
_status(loaded), _constraint(false), _modify(false),_delete(false),
|
||||
_status(loaded), _constraint(false), _modify(false), _delete(false),
|
||||
_own(false)
|
||||
{
|
||||
}
|
||||
|
@ -149,41 +156,6 @@ inline Machine::Code & Machine::Code::operator=(const Machine::Code &rhs) throw(
|
|||
return *this;
|
||||
}
|
||||
|
||||
inline Machine::Code::operator bool () const throw () {
|
||||
return _code && status() == loaded;
|
||||
}
|
||||
|
||||
inline Machine::Code::status_t Machine::Code::status() const throw() {
|
||||
return _status;
|
||||
}
|
||||
|
||||
inline bool Machine::Code::constraint() const throw() {
|
||||
return _constraint;
|
||||
}
|
||||
|
||||
inline size_t Machine::Code::dataSize() const throw() {
|
||||
return _data_size;
|
||||
}
|
||||
|
||||
inline size_t Machine::Code::instructionCount() const throw() {
|
||||
return _instr_count;
|
||||
}
|
||||
|
||||
inline bool Machine::Code::immutable() const throw()
|
||||
{
|
||||
return !(_delete || _modify);
|
||||
}
|
||||
|
||||
inline bool Machine::Code::deletes() const throw()
|
||||
{
|
||||
return _delete;
|
||||
}
|
||||
|
||||
inline size_t Machine::Code::maxRef() const throw()
|
||||
{
|
||||
return _max_ref;
|
||||
}
|
||||
|
||||
inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw()
|
||||
{
|
||||
if (_code && !_own)
|
||||
|
|
|
@ -28,7 +28,7 @@ of the License or (at your option) any later version.
|
|||
|
||||
#include <utility>
|
||||
#include "inc/List.h"
|
||||
#include "inc/Slot.h"
|
||||
//#include "inc/Slot.h"
|
||||
#include "inc/Position.h"
|
||||
#include "inc/Intervals.h"
|
||||
#include "inc/debug.h"
|
||||
|
@ -118,8 +118,8 @@ private:
|
|||
|
||||
}; // end of class SlotColllision
|
||||
|
||||
class BBox;
|
||||
class SlantBox;
|
||||
struct BBox;
|
||||
struct SlantBox;
|
||||
|
||||
class ShiftCollider
|
||||
{
|
||||
|
@ -128,13 +128,7 @@ public:
|
|||
typedef Vector<fpair> vfpairs;
|
||||
typedef vfpairs::iterator ivfpairs;
|
||||
|
||||
ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
|
||||
{
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
for (int i = 0; i < 4; ++i)
|
||||
_ranges[i].setdebug(dbgout);
|
||||
#endif
|
||||
}
|
||||
ShiftCollider(json *dbgout);
|
||||
~ShiftCollider() throw() { };
|
||||
|
||||
bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint,
|
||||
|
@ -176,10 +170,25 @@ protected:
|
|||
|
||||
}; // end of class ShiftCollider
|
||||
|
||||
inline
|
||||
ShiftCollider::ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
|
||||
: _target(0),
|
||||
_margin(0.0),
|
||||
_marginWt(0.0),
|
||||
_seqClass(0),
|
||||
_seqProxClass(0),
|
||||
_seqOrder(0)
|
||||
{
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
for (int i = 0; i < 4; ++i)
|
||||
_ranges[i].setdebug(dbgout);
|
||||
#endif
|
||||
}
|
||||
|
||||
class KernCollider
|
||||
{
|
||||
public:
|
||||
KernCollider(GR_MAYBE_UNUSED json *dbg) : _miny(-1e38f), _maxy(1e38f) { };
|
||||
KernCollider(json *dbg);
|
||||
~KernCollider() throw() { };
|
||||
bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin,
|
||||
const Position &currShift, const Position &offsetPrev, int dir,
|
||||
|
@ -213,8 +222,24 @@ private:
|
|||
|
||||
|
||||
inline
|
||||
float sqr(float x) { return x * x; }
|
||||
float sqr(float x) {
|
||||
return x * x;
|
||||
}
|
||||
|
||||
inline
|
||||
KernCollider::KernCollider(GR_MAYBE_UNUSED json *dbg)
|
||||
: _target(0),
|
||||
_margin(0.0f),
|
||||
_miny(-1e38f),
|
||||
_maxy(1e38f),
|
||||
_sliceWidth(0.0f),
|
||||
_mingap(0.0f),
|
||||
_xbound(0.0)
|
||||
{
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
_seg = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
}; // end of namespace graphite2
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/* GRAPHITE2 LICENSING
|
||||
|
||||
Copyright 2015, SIL International
|
||||
All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should also have received a copy of the GNU Lesser General Public
|
||||
License along with this library in the file named "LICENSE".
|
||||
If not, write to the Free Software Foundation, 51 Franklin Street,
|
||||
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
|
||||
internet at http://www.fsf.org/licenses/lgpl.html.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of the
|
||||
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
|
||||
License, as published by the Free Software Foundation, either version 2
|
||||
of the License or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#if ((defined GCC_VERSION && GCC_VERSION >= 302) || (defined __INTEL_COMPILER && __INTEL_COMPILER >= 800) || defined(__clang__))
|
||||
#define expect(expr,value) (__builtin_expect ((expr),(value)) )
|
||||
#else
|
||||
#define expect(expr,value) (expr)
|
||||
#endif
|
||||
|
||||
#define likely(expr) expect((expr) != 0, 1)
|
||||
#define unlikely(expr) expect((expr) != 0, 0)
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned __int8 u8;
|
||||
typedef unsigned __int16 u16;
|
||||
typedef unsigned __int32 u32;
|
||||
typedef unsigned __int64 u64;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
#endif
|
||||
|
||||
ptrdiff_t const MINMATCH = 4;
|
||||
|
||||
template<int S>
|
||||
inline
|
||||
void unaligned_copy(void * d, void const * s) {
|
||||
::memcpy(d, s, S);
|
||||
}
|
||||
|
||||
inline
|
||||
size_t align(size_t p) {
|
||||
return (p + sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1);
|
||||
}
|
||||
|
||||
inline
|
||||
u8 * overrun_copy(u8 * d, u8 const * s, size_t n) {
|
||||
size_t const WS = sizeof(unsigned long);
|
||||
u8 const * e = s + n;
|
||||
do
|
||||
{
|
||||
unaligned_copy<WS>(d, s);
|
||||
d += WS;
|
||||
s += WS;
|
||||
}
|
||||
while (s < e);
|
||||
d-=(s-e);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
u8 * fast_copy(u8 * d, u8 const * s, size_t n) {
|
||||
size_t const WS = sizeof(unsigned long);
|
||||
size_t wn = n/WS;
|
||||
while (wn--)
|
||||
{
|
||||
unaligned_copy<WS>(d, s);
|
||||
d += WS;
|
||||
s += WS;
|
||||
}
|
||||
n &= WS-1;
|
||||
while (n--) {*d++ = *s++; }
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
u8 * copy(u8 * d, u8 const * s, size_t n) {
|
||||
if (likely(d>s+sizeof(unsigned long)))
|
||||
return overrun_copy(d,s,n);
|
||||
else
|
||||
while (n--) *d++ = *s++;
|
||||
return d;
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
|
|
@ -1,51 +1,39 @@
|
|||
/* Copyright (c) 2012, Siyuan Fu <fusiyuan2010@gmail.com>
|
||||
Copyright (c) 2015, SIL International
|
||||
/* GRAPHITE2 LICENSING
|
||||
|
||||
Copyright 2015, SIL International
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
This library is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation; either version 2.1 of License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should also have received a copy of the GNU Lesser General Public
|
||||
License along with this library in the file named "LICENSE".
|
||||
If not, write to the Free Software Foundation, 51 Franklin Street,
|
||||
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
|
||||
internet at http://www.fsf.org/licenses/lgpl.html.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of the
|
||||
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
|
||||
License, as published by the Free Software Foundation, either version 2
|
||||
of the License or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace shrinker
|
||||
namespace lz4
|
||||
{
|
||||
|
||||
// return value is either decompressed size of -1
|
||||
int decompress(void const *in, size_t in_size, void *out, size_t out_size);
|
||||
/*
|
||||
in: inbuf --- compressed data
|
||||
out: outbuf --- decompressed data to place in
|
||||
size: decompressed(original) data size should be
|
||||
|
||||
return value:
|
||||
positive integer means decompress success and it's the sizeof decompressed data,
|
||||
which should be equal to size.
|
||||
or -1 means decompress failed
|
||||
*/
|
||||
|
||||
} // end of namespace shrinker
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ class FileFace;
|
|||
class GlyphCache;
|
||||
class NameTable;
|
||||
class json;
|
||||
class Font;
|
||||
|
||||
|
||||
using TtfUtil::Tag;
|
||||
|
@ -174,6 +175,8 @@ class Face::Table
|
|||
|
||||
Error decompress();
|
||||
|
||||
void releaseBuffers();
|
||||
|
||||
public:
|
||||
Table() throw();
|
||||
Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw();
|
||||
|
@ -202,10 +205,7 @@ Face::Table::Table(const Table & rhs) throw()
|
|||
inline
|
||||
Face::Table::~Table() throw()
|
||||
{
|
||||
if (_compressed)
|
||||
free(const_cast<byte *>(_p));
|
||||
else if (_p && _f->m_ops.release_table)
|
||||
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
|
||||
releaseBuffers();
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
@ -56,7 +56,7 @@ class FeatureRef
|
|||
static const uint8 SIZEOF_CHUNK = sizeof(chunk_t)*8;
|
||||
|
||||
public:
|
||||
FeatureRef() : m_nameValues(0) {}
|
||||
FeatureRef();
|
||||
FeatureRef(const Face & face, unsigned short & bits_offset, uint32 max_val,
|
||||
uint32 name, uint16 uiName, uint16 flags,
|
||||
FeatureSetting *settings, uint16 num_set) throw();
|
||||
|
@ -99,6 +99,16 @@ private: //unimplemented
|
|||
};
|
||||
|
||||
|
||||
inline
|
||||
FeatureRef::FeatureRef()
|
||||
: m_pFace(0), m_nameValues(0),
|
||||
m_mask(0), m_max(0), m_id(0),
|
||||
m_nameid(0), m_flags(0), m_numSet(0),
|
||||
m_bits(0), m_index(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class NameAndFeatureRef
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -39,10 +39,11 @@ class FeatureVal;
|
|||
class Segment;
|
||||
|
||||
|
||||
class SlantBox
|
||||
struct SlantBox
|
||||
{
|
||||
public:
|
||||
SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
|
||||
static const SlantBox empty;
|
||||
|
||||
// SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
|
||||
float width() const { return sa - si; }
|
||||
float height() const { return da - di; }
|
||||
float si; // min
|
||||
|
@ -51,11 +52,9 @@ public:
|
|||
float da; // max
|
||||
};
|
||||
|
||||
static SlantBox nullSlant(0, 0, 0, 0);
|
||||
|
||||
class BBox
|
||||
struct BBox
|
||||
{
|
||||
public:
|
||||
BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
|
||||
float width() const { return xa - xi; }
|
||||
float height() const { return ya - yi; }
|
||||
|
@ -65,7 +64,6 @@ public:
|
|||
float ya; // max
|
||||
};
|
||||
|
||||
static BBox nullBBox(0, 0, 0, 0);
|
||||
|
||||
class GlyphBox
|
||||
{
|
||||
|
@ -95,8 +93,6 @@ class GlyphCache
|
|||
GlyphCache(const GlyphCache&);
|
||||
GlyphCache& operator=(const GlyphCache&);
|
||||
|
||||
static const Rect nullRect;
|
||||
|
||||
public:
|
||||
GlyphCache(const Face & face, const uint32 face_options);
|
||||
~GlyphCache();
|
||||
|
@ -110,7 +106,7 @@ public:
|
|||
float getBoundingMetric(unsigned short glyphid, uint8 metric) const;
|
||||
uint8 numSubBounds(unsigned short glyphid) const;
|
||||
float getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const;
|
||||
const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : nullRect; }
|
||||
const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; }
|
||||
const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
|
||||
const BBox & getBoundingBBox(unsigned short glyphid) const;
|
||||
const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
|
||||
|
@ -120,6 +116,7 @@ public:
|
|||
CLASS_NEW_DELETE;
|
||||
|
||||
private:
|
||||
const Rect _empty_slant_box;
|
||||
const Loader * _glyph_loader;
|
||||
const GlyphFace * * _glyphs;
|
||||
GlyphBox * * _boxes;
|
||||
|
@ -149,7 +146,7 @@ unsigned short GlyphCache::unitsPerEm() const throw()
|
|||
inline
|
||||
bool GlyphCache::check(unsigned short glyphid) const
|
||||
{
|
||||
return glyphid < _num_glyphs;
|
||||
return _boxes && glyphid < _num_glyphs;
|
||||
}
|
||||
|
||||
inline
|
||||
|
@ -177,7 +174,7 @@ float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const
|
|||
|
||||
inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
|
||||
{
|
||||
return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : nullSlant;
|
||||
return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty;
|
||||
}
|
||||
|
||||
inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
|
||||
|
@ -207,14 +204,12 @@ float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, u
|
|||
inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const
|
||||
{
|
||||
GlyphBox *b = _boxes[glyphid];
|
||||
// if (b == NULL || subindex >= b->num()) return nullSlant;
|
||||
return *(SlantBox *)(b->subs() + 2 * subindex + 1);
|
||||
}
|
||||
|
||||
inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const
|
||||
{
|
||||
GlyphBox *b = _boxes[glyphid];
|
||||
// if (b == NULL || subindex >= b->num()) return nullBBox;
|
||||
return *(BBox *)(b->subs() + 2 * subindex);
|
||||
}
|
||||
|
||||
|
|
|
@ -144,6 +144,9 @@ inline
|
|||
Zones::Zones()
|
||||
: _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
|
||||
{
|
||||
#if !defined GRAPHITE2_NTRACING
|
||||
_dbg = 0;
|
||||
#endif
|
||||
_exclusions.reserve(8);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ void Vector<T>::reserve(size_t n)
|
|||
{
|
||||
const ptrdiff_t sz = size();
|
||||
m_first = static_cast<T*>(realloc(m_first, n*sizeof(T)));
|
||||
if (!m_first) std::abort();
|
||||
m_last = m_first + sz;
|
||||
m_end = m_first + n;
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ template <typename T> T * gralloc(size_t n)
|
|||
#ifdef GRAPHITE2_TELEMETRY
|
||||
telemetry::count_bytes(sizeof(T) * n);
|
||||
#endif
|
||||
return reinterpret_cast<T*>(malloc(sizeof(T) * n));
|
||||
return static_cast<T*>(malloc(sizeof(T) * n));
|
||||
}
|
||||
|
||||
template <typename T> T * grzeroalloc(size_t n)
|
||||
|
@ -93,7 +93,7 @@ template <typename T> T * grzeroalloc(size_t n)
|
|||
#ifdef GRAPHITE2_TELEMETRY
|
||||
telemetry::count_bytes(sizeof(T) * n);
|
||||
#endif
|
||||
return reinterpret_cast<T*>(calloc(n, sizeof(T)));
|
||||
return static_cast<T*>(calloc(n, sizeof(T)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -128,5 +128,5 @@ inline T max(const T a, const T b)
|
|||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4800)
|
||||
#pragma warning(once: 4355)
|
||||
#pragma warning(disable: 4355)
|
||||
#endif
|
||||
|
|
|
@ -53,9 +53,10 @@ public:
|
|||
|
||||
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face,
|
||||
enum passtype pt, uint32 version, Error &e);
|
||||
bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
|
||||
bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const;
|
||||
void init(Silf *silf) { m_silf = silf; }
|
||||
byte flags() const { return m_flags; }
|
||||
byte collisionLoops() const { return m_numCollRuns; }
|
||||
bool reverseDir() const { return m_isReverseDir; }
|
||||
|
||||
CLASS_NEW_DELETE
|
||||
private:
|
||||
|
@ -73,7 +74,7 @@ private:
|
|||
uint16 glyphToCol(const uint16 gid) const;
|
||||
bool runFSM(FiniteStateMachine & fsm, Slot * slot) const;
|
||||
void dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;
|
||||
void dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const;
|
||||
void dumpRuleEventOutput(const FiniteStateMachine & fsm, vm::Machine & m, const Rule & r, Slot * os) const;
|
||||
void adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
|
||||
bool collisionShift(Segment *seg, int dir, json * const dbgout) const;
|
||||
bool collisionKern(Segment *seg, int dir, json * const dbgout) const;
|
||||
|
@ -93,7 +94,8 @@ private:
|
|||
vm::Machine::Code * m_codes;
|
||||
byte * m_progs;
|
||||
|
||||
byte m_flags;
|
||||
byte m_numCollRuns;
|
||||
byte m_kernColls;
|
||||
byte m_iMaxLoop;
|
||||
uint16 m_numGlyphs;
|
||||
uint16 m_numRules;
|
||||
|
@ -105,6 +107,7 @@ private:
|
|||
byte m_minPreCtxt;
|
||||
byte m_maxPreCtxt;
|
||||
byte m_colThreshold;
|
||||
bool m_isReverseDir;
|
||||
vm::Machine::Code m_cPConstraint;
|
||||
|
||||
private: //defensive
|
||||
|
|
|
@ -41,8 +41,8 @@ struct Rule {
|
|||
uint16 rule_idx;
|
||||
#endif
|
||||
|
||||
Rule() : constraint(0), action(0), sort(0), preContext(0) {}
|
||||
~Rule();
|
||||
Rule();
|
||||
~Rule() {}
|
||||
|
||||
CLASS_NEW_DELETE;
|
||||
|
||||
|
@ -51,8 +51,16 @@ private:
|
|||
Rule & operator = (const Rule &);
|
||||
};
|
||||
|
||||
inline Rule::~Rule()
|
||||
inline
|
||||
Rule::Rule()
|
||||
: constraint(0),
|
||||
action(0),
|
||||
sort(0),
|
||||
preContext(0)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
rule_idx = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -94,7 +102,7 @@ class SlotMap
|
|||
{
|
||||
public:
|
||||
enum {MAX_SLOTS=64};
|
||||
SlotMap(Segment & seg);
|
||||
SlotMap(Segment & seg, uint8 direction);
|
||||
|
||||
Slot * * begin();
|
||||
Slot * * end();
|
||||
|
@ -105,19 +113,22 @@ public:
|
|||
Slot * const & operator[](int n) const;
|
||||
Slot * & operator [] (int);
|
||||
void pushSlot(Slot * const slot);
|
||||
void collectGarbage();
|
||||
void collectGarbage(Slot *& aSlot);
|
||||
|
||||
Slot * highwater() { return m_highwater; }
|
||||
void highwater(Slot *s) { m_highwater = s; m_highpassed = false; }
|
||||
bool highpassed() const { return m_highpassed; }
|
||||
void highpassed(bool v) { m_highpassed = v; }
|
||||
|
||||
uint8 dir() const { return m_dir; }
|
||||
|
||||
Segment & segment;
|
||||
private:
|
||||
Slot * m_slot_map[MAX_SLOTS+1];
|
||||
unsigned short m_size;
|
||||
unsigned short m_precontext;
|
||||
Slot * m_highwater;
|
||||
uint8 m_dir;
|
||||
bool m_highpassed;
|
||||
};
|
||||
|
||||
|
@ -231,8 +242,8 @@ void FiniteStateMachine::Rules::accumulate_rules(const State &state)
|
|||
}
|
||||
|
||||
inline
|
||||
SlotMap::SlotMap(Segment & seg)
|
||||
: segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_highpassed(false)
|
||||
SlotMap::SlotMap(Segment & seg, uint8 direction)
|
||||
: segment(seg), m_size(0), m_precontext(0), m_highwater(0), m_dir(direction), m_highpassed(false)
|
||||
{
|
||||
m_slot_map[0] = 0;
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@ private:
|
|||
unsigned long long minAccessCount, unsigned long long oldAccessTime);
|
||||
|
||||
uint16 m_prefixLength;
|
||||
uint16 m_maxCachedSegLength;
|
||||
// uint16 m_maxCachedSegLength;
|
||||
size_t m_segmentCount;
|
||||
SegCachePrefixArray m_prefixes;
|
||||
Features m_features;
|
||||
|
|
|
@ -39,7 +39,7 @@ of the License or (at your option) any later version.
|
|||
#include "inc/Slot.h"
|
||||
#include "inc/Position.h"
|
||||
#include "inc/List.h"
|
||||
#include "inc/Bidi.h"
|
||||
//#include "inc/Bidi.h"
|
||||
#include "inc/Collider.h"
|
||||
|
||||
#define MAX_SEG_GROWTH_FACTOR 256
|
||||
|
@ -48,7 +48,7 @@ namespace graphite2 {
|
|||
|
||||
typedef Vector<Features> FeatureList;
|
||||
typedef Vector<Slot *> SlotRope;
|
||||
typedef Vector<int16 *> AttributeRope;
|
||||
typedef Vector<int16 *> AttributeRope;
|
||||
typedef Vector<SlotJustify *> JustifyRope;
|
||||
|
||||
#ifndef GRAPHITE2_NSEGCACHE
|
||||
|
@ -101,7 +101,6 @@ public:
|
|||
unsigned int charInfoCount() const { return m_numCharinfo; }
|
||||
const CharInfo *charinfo(unsigned int index) const { return index < m_numCharinfo ? m_charinfo + index : NULL; }
|
||||
CharInfo *charinfo(unsigned int index) { return index < m_numCharinfo ? m_charinfo + index : NULL; }
|
||||
int8 dir() const { return m_dir; }
|
||||
|
||||
Segment(unsigned int numchars, const Face* face, uint32 script, int dir);
|
||||
~Segment();
|
||||
|
@ -124,7 +123,7 @@ public:
|
|||
void freeSlot(Slot *);
|
||||
SlotJustify *newJustify();
|
||||
void freeJustify(SlotJustify *aJustify);
|
||||
Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isFinal = true);
|
||||
Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isRtl = false, bool isFinal = true);
|
||||
void associateChars(int offset, int num);
|
||||
void linkClusters(Slot *first, Slot *last);
|
||||
uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); }
|
||||
|
@ -138,11 +137,13 @@ public:
|
|||
if (val > pFR->maxVal()) val = pFR->maxVal();
|
||||
pFR->applyValToFeature(val, m_feats[index]);
|
||||
} }
|
||||
int8 dir() const { return m_dir; }
|
||||
void dir(int8 val) { m_dir = val; }
|
||||
bool currdir() const { return ((m_dir >> 6) ^ m_dir) & 1; }
|
||||
unsigned int passBits() const { return m_passBits; }
|
||||
void mergePassBits(const unsigned int val) { m_passBits &= val; }
|
||||
int16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
|
||||
int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const;
|
||||
int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const;
|
||||
float glyphAdvance(uint16 gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; }
|
||||
const Rect &theGlyphBBoxTemporary(uint16 gid) const { return m_face->glyphs().glyph(gid)->theBBox(); } //warning value may become invalid when another glyph is accessed
|
||||
Slot *findRoot(Slot *is) const { return is->attachedTo() ? findRoot(is->attachedTo()) : is; }
|
||||
|
@ -150,10 +151,13 @@ public:
|
|||
int defaultOriginal() const { return m_defaultOriginal; }
|
||||
const Face * getFace() const { return m_face; }
|
||||
const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; }
|
||||
void bidiPass(uint8 aBidi, int paradir, uint8 aMirror);
|
||||
void bidiPass(int paradir, uint8 aMirror);
|
||||
int8 getSlotBidiClass(Slot *s) const;
|
||||
void doMirror(uint16 aMirror);
|
||||
Slot *addLineEnd(Slot *nSlot);
|
||||
void delLineEnd(Slot *s);
|
||||
bool hasJustification() const { return m_justifies.size() != 0; }
|
||||
void reverseSlots();
|
||||
|
||||
bool isWhitespace(const int cid) const;
|
||||
bool hasCollisionInfo() const { return m_collisions != 0; }
|
||||
|
@ -163,7 +167,7 @@ public:
|
|||
|
||||
public: //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
|
||||
bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
|
||||
void finalise(const Font *font);
|
||||
void finalise(const Font *font, bool reverse=false);
|
||||
float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
|
||||
bool initCollisions();
|
||||
|
||||
|
@ -190,24 +194,34 @@ private:
|
|||
uint8 m_flags; // General purpose flags
|
||||
};
|
||||
|
||||
|
||||
inline
|
||||
int8 Segment::getSlotBidiClass(Slot *s) const
|
||||
{
|
||||
int8 res = s->getBidiClass();
|
||||
if (res != -1) return res;
|
||||
res = glyphAttr(s->gid(), m_silf->aBidi());
|
||||
s->setBidiClass(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
inline
|
||||
void Segment::finalise(const Font *font)
|
||||
void Segment::finalise(const Font *font, bool reverse)
|
||||
{
|
||||
if (!m_first) return;
|
||||
|
||||
m_advance = positionSlots(font);
|
||||
m_advance = positionSlots(font, m_first, m_last, m_silf->dir(), true);
|
||||
//associateChars(0, m_numCharinfo);
|
||||
if (reverse && currdir() != (m_dir & 1))
|
||||
reverseSlots();
|
||||
linkClusters(m_first, m_last);
|
||||
}
|
||||
|
||||
inline
|
||||
int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel) const {
|
||||
int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const {
|
||||
if (attrLevel > 0)
|
||||
{
|
||||
Slot *is = findRoot(iSlot);
|
||||
return is->clusterMetric(this, metric, attrLevel);
|
||||
return is->clusterMetric(this, metric, attrLevel, rtl);
|
||||
}
|
||||
else
|
||||
return m_face->getGlyphMetric(iSlot->gid(), metric);
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
uint8 maxCompPerLig() const { return m_iMaxComp; }
|
||||
uint16 numClasses() const { return m_nClass; }
|
||||
byte flags() const { return m_flags; }
|
||||
byte dir() const { return m_dir; }
|
||||
uint8 numJustLevels() const { return m_numJusts; }
|
||||
Justinfo *justAttrs() const { return m_justs; }
|
||||
uint16 endLineGlyphid() const { return m_gEndLine; }
|
||||
|
@ -113,7 +114,7 @@ private:
|
|||
uint8 m_numPasses;
|
||||
uint8 m_numJusts;
|
||||
uint8 m_sPass, m_pPass, m_jPass, m_bPass,
|
||||
m_flags;
|
||||
m_flags, m_dir;
|
||||
|
||||
uint8 m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
|
||||
m_iMaxComp, m_aCollision;
|
||||
|
|
|
@ -32,15 +32,13 @@ of the License or (at your option) any later version.
|
|||
#include "inc/Font.h"
|
||||
#include "inc/Position.h"
|
||||
|
||||
|
||||
|
||||
namespace graphite2 {
|
||||
|
||||
typedef gr_attrCode attrCode;
|
||||
|
||||
class GlyphFace;
|
||||
class Segment;
|
||||
class SegCacheEntry;
|
||||
class Segment;
|
||||
|
||||
struct SlotJustify
|
||||
{
|
||||
|
@ -82,7 +80,7 @@ public:
|
|||
uint32 index() const { return m_index; }
|
||||
void index(uint32 val) { m_index = val; }
|
||||
|
||||
Slot();
|
||||
Slot(int16 *m_userAttr = NULL);
|
||||
void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars);
|
||||
Slot *next() const { return m_next; }
|
||||
void next(Slot *s) { m_next = s; }
|
||||
|
@ -99,7 +97,7 @@ public:
|
|||
void after(int ind) { m_after = ind; }
|
||||
bool isBase() const { return (!m_parent); }
|
||||
void update(int numSlots, int numCharInfo, Position &relpos);
|
||||
Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool isFinal);
|
||||
Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal);
|
||||
bool isDeleted() const { return (m_flags & DELETED) ? true : false; }
|
||||
void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; }
|
||||
bool isCopied() const { return (m_flags & COPIED) ? true : false; }
|
||||
|
@ -109,6 +107,7 @@ public:
|
|||
bool isInsertBefore() const { return !(m_flags & INSERTED); }
|
||||
uint8 getBidiLevel() const { return m_bidiLevel; }
|
||||
void setBidiLevel(uint8 level) { m_bidiLevel = level; }
|
||||
int8 getBidiClass(const Segment *seg);
|
||||
int8 getBidiClass() const { return m_bidiCls; }
|
||||
void setBidiClass(int8 cls) { m_bidiCls = cls; }
|
||||
int16 *userAttrs() const { return m_userAttr; }
|
||||
|
@ -130,7 +129,7 @@ public:
|
|||
bool sibling(Slot *ap);
|
||||
bool removeChild(Slot *ap);
|
||||
bool removeSibling(Slot *ap);
|
||||
int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel);
|
||||
int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel, bool rtl);
|
||||
void positionShift(Position a) { m_position += a; }
|
||||
void floodShift(Position adj);
|
||||
float just() const { return m_just; }
|
||||
|
|
|
@ -56,7 +56,7 @@ private:
|
|||
key_type offset;
|
||||
};
|
||||
|
||||
static chunk empty_chunk;
|
||||
static const chunk empty_chunk;
|
||||
sparse(const sparse &);
|
||||
sparse & operator = (const sparse &);
|
||||
|
||||
|
@ -88,7 +88,7 @@ private:
|
|||
inline
|
||||
sparse::sparse() throw() : m_nchunks(0)
|
||||
{
|
||||
m_array.map = &empty_chunk;
|
||||
m_array.map = const_cast<graphite2::sparse::chunk *>(&empty_chunk);
|
||||
}
|
||||
|
||||
|
||||
|
@ -113,7 +113,7 @@ sparse::sparse(I attr, const I last)
|
|||
}
|
||||
if (m_nchunks == 0)
|
||||
{
|
||||
m_array.map=&empty_chunk;
|
||||
m_array.map=const_cast<graphite2::sparse::chunk *>(&empty_chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,11 +137,11 @@ public:
|
|||
////////////////////////////////// cmap lookup tools
|
||||
const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3,
|
||||
int nEncodingId = 1, size_t length = 0);
|
||||
bool CheckCmapSubtable4(const void * pCmap31 /*, unsigned int maxgid*/);
|
||||
bool CheckCmapSubtable4(const void * pCmap31, size_t table_len /*, unsigned int maxgid*/);
|
||||
gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0);
|
||||
unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId,
|
||||
int * pRangeKey = 0);
|
||||
bool CheckCmapSubtable12(const void *pCmap310 /*, unsigned int maxgid*/);
|
||||
bool CheckCmapSubtable12(const void *pCmap310, size_t table_len /*, unsigned int maxgid*/);
|
||||
gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0);
|
||||
unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId,
|
||||
int * pRangeKey = 0);
|
||||
|
|
|
@ -61,6 +61,7 @@ of the License or (at your option) any later version.
|
|||
// isl = The last positioned slot
|
||||
// ip = The current instruction pointer
|
||||
// endPos = Position of advance of last cluster
|
||||
// dir = writing system directionality of the font
|
||||
|
||||
|
||||
// #define NOT_IMPLEMENTED assert(false)
|
||||
|
@ -313,6 +314,8 @@ STARTOP(insert)
|
|||
{
|
||||
newSlot->originate(seg.defaultOriginal());
|
||||
}
|
||||
if (is == smap.highwater())
|
||||
smap.highpassed(false);
|
||||
is = newSlot;
|
||||
seg.extendLength(1);
|
||||
if (map != &smap[-1])
|
||||
|
@ -389,7 +392,7 @@ STARTOP(attr_add)
|
|||
const int val = int(pop());
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
int res = is->getAttr(&seg, slat, 0);
|
||||
|
@ -402,7 +405,7 @@ STARTOP(attr_sub)
|
|||
const int val = int(pop());
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
int res = is->getAttr(&seg, slat, 0);
|
||||
|
@ -431,7 +434,7 @@ STARTOP(push_slot_attr)
|
|||
const int slot_ref = int8(param[1]);
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
slotref slot = slotat(slot_ref);
|
||||
|
@ -458,7 +461,7 @@ STARTOP(push_glyph_metric)
|
|||
const signed int attr_level = uint8(param[2]);
|
||||
slotref slot = slotat(slot_ref);
|
||||
if (slot)
|
||||
push(seg.getGlyphMetric(slot, glyph_attr, attr_level));
|
||||
push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir));
|
||||
ENDOP
|
||||
|
||||
STARTOP(push_feat)
|
||||
|
@ -496,7 +499,7 @@ STARTOP(push_att_to_glyph_metric)
|
|||
{
|
||||
slotref att = slot->attachedTo();
|
||||
if (att) slot = att;
|
||||
push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level)));
|
||||
push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)));
|
||||
}
|
||||
ENDOP
|
||||
|
||||
|
@ -507,7 +510,7 @@ STARTOP(push_islot_attr)
|
|||
idx = uint8(param[2]);
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
slotref slot = slotat(slot_ref);
|
||||
|
@ -552,7 +555,7 @@ STARTOP(iattr_add)
|
|||
const int val = int(pop());
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
int res = is->getAttr(&seg, slat, idx);
|
||||
|
@ -566,7 +569,7 @@ STARTOP(iattr_sub)
|
|||
const int val = int(pop());
|
||||
if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
|
||||
{
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1));
|
||||
seg.positionSlots(0, *smap.begin(), *(smap.end()-1), dir);
|
||||
flags |= POSITIONED;
|
||||
}
|
||||
int res = is->getAttr(&seg, slat, idx);
|
||||
|
|
|
@ -5,6 +5,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
|||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="user-scalable=no">
|
||||
<title>Test for Bug 982141, helper page</title>
|
||||
<script type="application/javascript" src="apz_test_utils.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
@ -87,7 +88,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=982141
|
|||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="overflow: hidden;"><!-- Make sure the root frame is not scrollable -->
|
||||
<body style="overflow: hidden;"><!-- This combined with the user-scalable=no ensures the root frame is not scrollable -->
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982141">Mozilla Bug 982141</a>
|
||||
<!-- A scrollable subframe, with enough content to make it have a nonzero scroll range -->
|
||||
<div style="height: 50px; width: 50px; overflow: scroll">
|
||||
|
|
|
@ -787,13 +787,13 @@ APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
|
|||
nsIWidget* aWidget,
|
||||
const WidgetTouchEvent& aEvent,
|
||||
uint64_t aInputBlockId,
|
||||
const nsRefPtr<SetAllowedTouchBehaviorCallback>& aCallback)
|
||||
const SetAllowedTouchBehaviorCallback& aCallback)
|
||||
{
|
||||
nsTArray<TouchBehaviorFlags> flags;
|
||||
for (uint32_t i = 0; i < aEvent.touches.Length(); i++) {
|
||||
flags.AppendElement(widget::ContentHelper::GetAllowedTouchBehavior(aWidget, aEvent.touches[i]->mRefPoint));
|
||||
}
|
||||
aCallback->Run(aInputBlockId, flags);
|
||||
aCallback(aInputBlockId, flags);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "FrameMetrics.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/layers/APZUtils.h"
|
||||
#include "nsIDOMWindowUtils.h"
|
||||
|
||||
|
@ -22,15 +23,8 @@ template<class T> class nsRefPtr;
|
|||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/* A base class for callbacks to be passed to
|
||||
* APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification. */
|
||||
struct SetAllowedTouchBehaviorCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(SetAllowedTouchBehaviorCallback)
|
||||
virtual void Run(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aFlags) const = 0;
|
||||
protected:
|
||||
virtual ~SetAllowedTouchBehaviorCallback() {}
|
||||
};
|
||||
typedef Function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
|
||||
SetAllowedTouchBehaviorCallback;
|
||||
|
||||
/* This class contains some helper methods that facilitate implementing the
|
||||
GeckoContentController callback interface required by the AsyncPanZoomController.
|
||||
|
@ -162,7 +156,7 @@ public:
|
|||
static void SendSetAllowedTouchBehaviorNotification(nsIWidget* aWidget,
|
||||
const WidgetTouchEvent& aEvent,
|
||||
uint64_t aInputBlockId,
|
||||
const nsRefPtr<SetAllowedTouchBehaviorCallback>& aCallback);
|
||||
const SetAllowedTouchBehaviorCallback& aCallback);
|
||||
|
||||
/* Notify content of a mouse scroll testing event. */
|
||||
static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "APZCCallbackHelper.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TouchEvents.h"
|
||||
#include "mozilla/layers/APZCCallbackHelper.h"
|
||||
|
@ -91,10 +92,10 @@ static int32_t sActiveDurationMs = 10;
|
|||
static bool sActiveDurationMsSet = false;
|
||||
|
||||
APZEventState::APZEventState(nsIWidget* aWidget,
|
||||
const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback)
|
||||
ContentReceivedInputBlockCallback&& aCallback)
|
||||
: mWidget(nullptr) // initialized in constructor body
|
||||
, mActiveElementManager(new ActiveElementManager())
|
||||
, mContentReceivedInputBlockCallback(aCallback)
|
||||
, mContentReceivedInputBlockCallback(Move(aCallback))
|
||||
, mPendingTouchPreventedResponse(false)
|
||||
, mPendingTouchPreventedBlockId(0)
|
||||
, mEndTouchIsClick(false)
|
||||
|
@ -244,7 +245,7 @@ APZEventState::ProcessLongTap(const nsCOMPtr<nsIPresShell>& aPresShell,
|
|||
APZES_LOG("MOZLONGTAP event handled: %d\n", eventHandled);
|
||||
}
|
||||
|
||||
mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, eventHandled);
|
||||
mContentReceivedInputBlockCallback(aGuid, aInputBlockId, eventHandled);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -266,13 +267,13 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
|
|||
if (mPendingTouchPreventedResponse) {
|
||||
// We can enter here if we get two TOUCH_STARTs in a row and didn't
|
||||
// respond to the first one. Respond to it now.
|
||||
mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
|
||||
mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
|
||||
mPendingTouchPreventedBlockId, false);
|
||||
sentContentResponse = true;
|
||||
mPendingTouchPreventedResponse = false;
|
||||
}
|
||||
if (isTouchPrevented) {
|
||||
mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, isTouchPrevented);
|
||||
mContentReceivedInputBlockCallback(aGuid, aInputBlockId, isTouchPrevented);
|
||||
sentContentResponse = true;
|
||||
} else {
|
||||
mPendingTouchPreventedResponse = true;
|
||||
|
@ -328,7 +329,7 @@ APZEventState::ProcessWheelEvent(const WidgetWheelEvent& aEvent,
|
|||
// scroll by setting defaultPrevented to true.
|
||||
bool defaultPrevented =
|
||||
aEvent.mFlags.mDefaultPrevented || aEvent.TriggersSwipe();
|
||||
mContentReceivedInputBlockCallback->Run(aGuid, aInputBlockId, defaultPrevented);
|
||||
mContentReceivedInputBlockCallback(aGuid, aInputBlockId, defaultPrevented);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -408,7 +409,7 @@ bool
|
|||
APZEventState::SendPendingTouchPreventedResponse(bool aPreventDefault)
|
||||
{
|
||||
if (mPendingTouchPreventedResponse) {
|
||||
mContentReceivedInputBlockCallback->Run(mPendingTouchPreventedGuid,
|
||||
mContentReceivedInputBlockCallback(mPendingTouchPreventedGuid,
|
||||
mPendingTouchPreventedBlockId, aPreventDefault);
|
||||
mPendingTouchPreventedResponse = false;
|
||||
return true;
|
||||
|
|
|
@ -11,11 +11,12 @@
|
|||
#include "FrameMetrics.h" // for ScrollableLayerGuid
|
||||
#include "Units.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/Function.h"
|
||||
#include "mozilla/layers/GeckoContentController.h" // for APZStateChange
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h" // for NS_INLINE_DECL_REFCOUNTING
|
||||
#include "nsIWeakReferenceUtils.h" // for nsWeakPtr
|
||||
#include "mozilla/nsRefPtr.h"
|
||||
|
||||
template <class> class nsCOMPtr;
|
||||
class nsIDocument;
|
||||
|
@ -27,15 +28,10 @@ namespace layers {
|
|||
|
||||
class ActiveElementManager;
|
||||
|
||||
struct ContentReceivedInputBlockCallback {
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ContentReceivedInputBlockCallback);
|
||||
virtual void Run(const ScrollableLayerGuid& aGuid,
|
||||
uint64_t aInputBlockId,
|
||||
bool aPreventDefault) const = 0;
|
||||
protected:
|
||||
virtual ~ContentReceivedInputBlockCallback() {}
|
||||
};
|
||||
typedef Function<void(const ScrollableLayerGuid&,
|
||||
uint64_t /* input block id */,
|
||||
bool /* prevent default */)>
|
||||
ContentReceivedInputBlockCallback;
|
||||
|
||||
/**
|
||||
* A content-side component that keeps track of state for handling APZ
|
||||
|
@ -46,7 +42,7 @@ class APZEventState {
|
|||
typedef FrameMetrics::ViewID ViewID;
|
||||
public:
|
||||
APZEventState(nsIWidget* aWidget,
|
||||
const nsRefPtr<ContentReceivedInputBlockCallback>& aCallback);
|
||||
ContentReceivedInputBlockCallback&& aCallback);
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(APZEventState);
|
||||
|
||||
|
@ -76,7 +72,7 @@ private:
|
|||
private:
|
||||
nsWeakPtr mWidget;
|
||||
nsRefPtr<ActiveElementManager> mActiveElementManager;
|
||||
nsRefPtr<ContentReceivedInputBlockCallback> mContentReceivedInputBlockCallback;
|
||||
ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
|
||||
bool mPendingTouchPreventedResponse;
|
||||
ScrollableLayerGuid mPendingTouchPreventedGuid;
|
||||
uint64_t mPendingTouchPreventedBlockId;
|
||||
|
|
|
@ -585,21 +585,45 @@ AdjustForClip(const Matrix4x4& asyncTransform, Layer* aLayer)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
ExpandRootClipRect(Layer* aLayer, const ScreenMargin& aFixedLayerMargins)
|
||||
{
|
||||
// For Fennec we want to expand the root scrollable layer clip rect based on
|
||||
// the fixed position margins. In particular, we want this while the dynamic
|
||||
// toolbar is in the process of sliding offscreen and the area of the
|
||||
// LayerView visible to the user is larger than the viewport size that Gecko
|
||||
// knows about (and therefore larger than the clip rect). We could also just
|
||||
// clear the clip rect on aLayer entirely but this seems more precise.
|
||||
Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
|
||||
if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
// We should never enter here on anything other than Fennec, since
|
||||
// aFixedLayerMargins should be empty everywhere else.
|
||||
MOZ_ASSERT(false);
|
||||
#endif
|
||||
ParentLayerRect rect(rootClipRect.value());
|
||||
rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
|
||||
PixelCastJustification::ScreenIsParentLayerForRoot));
|
||||
aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
||||
AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer,
|
||||
bool* aOutFoundRoot)
|
||||
{
|
||||
bool appliedTransform = false;
|
||||
for (Layer* child = aLayer->GetFirstChild();
|
||||
child; child = child->GetNextSibling()) {
|
||||
appliedTransform |=
|
||||
ApplyAsyncContentTransformToTree(child);
|
||||
ApplyAsyncContentTransformToTree(child, aOutFoundRoot);
|
||||
}
|
||||
|
||||
Matrix4x4 oldTransform = aLayer->GetTransform();
|
||||
|
||||
Matrix4x4 combinedAsyncTransform;
|
||||
bool hasAsyncTransform = false;
|
||||
ScreenMargin fixedLayerMargins(0, 0, 0, 0);
|
||||
ScreenMargin fixedLayerMargins;
|
||||
|
||||
// Each layer has multiple clips. Its local clip, which must move with async
|
||||
// transforms, and its scrollframe clips, which are the clips between each
|
||||
|
@ -642,34 +666,43 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
|||
}
|
||||
|
||||
const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
|
||||
// TODO: When we enable APZ on Fennec, we'll need to call SyncFrameMetrics here.
|
||||
// When doing so, it might be useful to look at how it was called here before
|
||||
// bug 1036967 removed the (dead) call.
|
||||
|
||||
#if defined(MOZ_ANDROID_APZ)
|
||||
bool rootContentLayer = metrics.IsRootContent();
|
||||
#ifdef MOZ_B2GDROID
|
||||
// B2GDroid is a special snowflake since it doesn't seem to have any root
|
||||
// content document. However we still need to send a setFirstPaintViewport
|
||||
// message, so we use the root of the layer tree as the root content layer
|
||||
// instead. For the most part this should work fine; the Java code will just
|
||||
// think the root layer is the "main" content, which in a manner of speaking,
|
||||
// it is.
|
||||
rootContentLayer = (aLayer->GetParent() == nullptr);
|
||||
#endif // MOZ_B2GDROID
|
||||
if (rootContentLayer) {
|
||||
if (mIsFirstPaint) {
|
||||
// If we find a metrics which is the root content doc, use that. If not, use
|
||||
// the root layer. Since this function recurses on children first we should
|
||||
// only end up using the root layer if the entire tree was devoid of a
|
||||
// root content metrics. This is a temporary solution; in the long term we
|
||||
// should not need the root content metrics at all. See bug 1201529 comment
|
||||
// 6 for details.
|
||||
if (!(*aOutFoundRoot)) {
|
||||
*aOutFoundRoot = metrics.IsRootContent() || /* RCD */
|
||||
(aLayer->GetParent() == nullptr && /* rootmost metrics */
|
||||
i + 1 >= aLayer->GetFrameMetricsCount());
|
||||
if (*aOutFoundRoot) {
|
||||
CSSToLayerScale geckoZoom = metrics.LayersPixelsPerCSSPixel().ToScaleFactor();
|
||||
LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
|
||||
mContentRect = metrics.GetScrollableRect();
|
||||
SetFirstPaintViewport(scrollOffsetLayerPixels,
|
||||
geckoZoom,
|
||||
mContentRect);
|
||||
if (mIsFirstPaint) {
|
||||
LayerIntPoint scrollOffsetLayerPixels = RoundedToInt(metrics.GetScrollOffset() * geckoZoom);
|
||||
mContentRect = metrics.GetScrollableRect();
|
||||
SetFirstPaintViewport(scrollOffsetLayerPixels,
|
||||
geckoZoom,
|
||||
mContentRect);
|
||||
} else {
|
||||
// Compute the painted displayport in document-relative CSS pixels.
|
||||
CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ?
|
||||
metrics.GetDisplayPort() :
|
||||
metrics.GetCriticalDisplayPort());
|
||||
displayPort += metrics.GetScrollOffset();
|
||||
SyncFrameMetrics(scrollOffset,
|
||||
geckoZoom * asyncTransformWithoutOverscroll.mScale,
|
||||
metrics.GetScrollableRect(), displayPort, geckoZoom, mLayersUpdated,
|
||||
mPaintSyncId, fixedLayerMargins);
|
||||
}
|
||||
mIsFirstPaint = false;
|
||||
mLayersUpdated = false;
|
||||
mPaintSyncId = 0;
|
||||
}
|
||||
mIsFirstPaint = false;
|
||||
mLayersUpdated = false;
|
||||
}
|
||||
#endif // MOZ_ANDROID_APZ
|
||||
#endif
|
||||
|
||||
// Transform the current local clip by this APZC's async transform. If we're
|
||||
// using containerful scrolling, then the clip is not part of the scrolled
|
||||
|
@ -745,6 +778,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer)
|
|||
appliedTransform = true;
|
||||
}
|
||||
|
||||
ExpandRootClipRect(aLayer, fixedLayerMargins);
|
||||
|
||||
if (aLayer->GetScrollbarDirection() != Layer::NONE) {
|
||||
ApplyAsyncTransformToScrollbar(aLayer);
|
||||
}
|
||||
|
@ -1103,24 +1138,7 @@ AsyncCompositionManager::TransformScrollableLayer(Layer* aLayer)
|
|||
AlignFixedAndStickyLayers(aLayer, aLayer, metrics.GetScrollId(), oldTransform,
|
||||
aLayer->GetLocalTransform(), fixedLayerMargins);
|
||||
|
||||
// For Fennec we want to expand the root scrollable layer clip rect based on
|
||||
// the fixed position margins. In particular, we want this while the dynamic
|
||||
// toolbar is in the process of sliding offscreen and the area of the
|
||||
// LayerView visible to the user is larger than the viewport size that Gecko
|
||||
// knows about (and therefore larger than the clip rect). We could also just
|
||||
// clear the clip rect on aLayer entirely but this seems more precise.
|
||||
Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
|
||||
if (rootClipRect && fixedLayerMargins != ScreenMargin()) {
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
// We should never enter here on anything other than Fennec, since
|
||||
// fixedLayerMargins should be empty everywhere else.
|
||||
MOZ_ASSERT(false);
|
||||
#endif
|
||||
ParentLayerRect rect(rootClipRect.value());
|
||||
rect.Deflate(ViewAs<ParentLayerPixel>(fixedLayerMargins,
|
||||
PixelCastJustification::ScreenIsParentLayerForRoot));
|
||||
aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
|
||||
}
|
||||
ExpandRootClipRect(aLayer, fixedLayerMargins);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1160,7 +1178,12 @@ AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
|
|||
// its own platform-specific async rendering that is done partially
|
||||
// in Gecko and partially in Java.
|
||||
wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), aCurrentFrame);
|
||||
if (!ApplyAsyncContentTransformToTree(root)) {
|
||||
bool foundRoot = false;
|
||||
if (ApplyAsyncContentTransformToTree(root, &foundRoot)) {
|
||||
#if defined(MOZ_ANDROID_APZ)
|
||||
MOZ_ASSERT(foundRoot);
|
||||
#endif
|
||||
} else {
|
||||
nsAutoTArray<Layer*,1> scrollableLayers;
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
mLayerManager->GetRootScrollableLayers(scrollableLayers);
|
||||
|
@ -1229,18 +1252,18 @@ AsyncCompositionManager::SyncViewportInfo(const LayerIntRect& aDisplayPort,
|
|||
|
||||
void
|
||||
AsyncCompositionManager::SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
|
||||
float aZoom,
|
||||
const CSSToParentLayerScale& aZoom,
|
||||
const CSSRect& aCssPageRect,
|
||||
bool aLayersUpdated,
|
||||
const CSSRect& aDisplayPort,
|
||||
const CSSToLayerScale& aDisplayResolution,
|
||||
bool aIsFirstPaint,
|
||||
const CSSToLayerScale& aPaintedResolution,
|
||||
bool aLayersUpdated,
|
||||
int32_t aPaintSyncId,
|
||||
ScreenMargin& aFixedLayerMargins)
|
||||
{
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
AndroidBridge::Bridge()->SyncFrameMetrics(aScrollOffset, aZoom, aCssPageRect,
|
||||
aLayersUpdated, aDisplayPort,
|
||||
aDisplayResolution, aIsFirstPaint,
|
||||
aDisplayPort, aPaintedResolution,
|
||||
aLayersUpdated, aPaintSyncId,
|
||||
aFixedLayerMargins);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -128,8 +128,11 @@ public:
|
|||
private:
|
||||
void TransformScrollableLayer(Layer* aLayer);
|
||||
// Return true if an AsyncPanZoomController content transform was
|
||||
// applied for |aLayer|.
|
||||
bool ApplyAsyncContentTransformToTree(Layer* aLayer);
|
||||
// applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
|
||||
// one of the metrics on one of the layers was determined to be the "root"
|
||||
// and its state was synced to the Java front-end. |aOutFoundRoot| must be
|
||||
// non-null.
|
||||
bool ApplyAsyncContentTransformToTree(Layer* aLayer, bool* aOutFoundRoot);
|
||||
/**
|
||||
* Update the shadow transform for aLayer assuming that is a scrollbar,
|
||||
* so that it stays in sync with the content that is being scrolled by APZ.
|
||||
|
@ -148,12 +151,12 @@ private:
|
|||
CSSToParentLayerScale& aScale,
|
||||
ScreenMargin& aFixedLayerMargins);
|
||||
void SyncFrameMetrics(const ParentLayerPoint& aScrollOffset,
|
||||
float aZoom,
|
||||
const CSSToParentLayerScale& aZoom,
|
||||
const CSSRect& aCssPageRect,
|
||||
bool aLayersUpdated,
|
||||
const CSSRect& aDisplayPort,
|
||||
const CSSToLayerScale& aDisplayResolution,
|
||||
bool aIsFirstPaint,
|
||||
const CSSToLayerScale& aPaintedResolution,
|
||||
bool aLayersUpdated,
|
||||
int32_t aPaintSyncId,
|
||||
ScreenMargin& aFixedLayerMargins);
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
-->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
width="100%" height="100%">
|
||||
<!-- There is a bug with MultiTiledContentClient that causes improper painting;
|
||||
bug 1204076 is on file for this issue. As a temporary workaround, we set
|
||||
user-scalable=no here so that WantAsyncZoom() returns false and we don't
|
||||
use a MultiTiledContentClient. Once bug 1204076 is fixed, we can remove this. -->
|
||||
<meta xmlns="http://www.w3.org/1999/xhtml" name="viewport" content="user-scalable=no"/>
|
||||
|
||||
<title>Testcase for small circles</title>
|
||||
<!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1143303 -->
|
||||
|
|
До Ширина: | Высота: | Размер: 770 B После Ширина: | Высота: | Размер: 1.2 KiB |
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче