Merge m-c to b2g-inbound

This commit is contained in:
Wes Kocher 2014-01-08 18:16:31 -08:00
commit eca807f202
122 changed files with 4720 additions and 3352 deletions

View File

@ -117,7 +117,6 @@ run-if = crashreporter
[browser_aboutHealthReport.js]
skip-if = os == "linux" # Bug 924307
[browser_aboutHome.js]
skip-if = os == "linux" # Bug 945667
[browser_aboutSyncProgress.js]
[browser_addKeywordSearch.js]
[browser_alltabslistener.js]

View File

@ -89,11 +89,15 @@ let gTests = [
}
},
// Disabled on Linux for intermittent issues with FHR, see Bug 945667.
{
desc: "Check that performing a search fires a search event and records to " +
"Firefox Health Report.",
setup: function () { },
run: function () {
// Skip this test on Linux.
if (navigator.platform.indexOf("Linux") == 0) { return; }
try {
let cm = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
cm.getCategoryEntry("healthreport-js-provider-default", "SearchesProvider");

View File

@ -12,6 +12,9 @@ support-files =
[browser_880164_customization_context_menus.js]
[browser_880382_drag_wide_widgets_in_panel.js]
[browser_885052_customize_mode_observers_disabed.js]
# Bug 951403 - Disabled on OSX for frequent failures
skip-if = os == "mac"
[browser_885530_showInPrivateBrowsing.js]
[browser_886323_buildArea_removable_nodes.js]
[browser_887438_currentset_shim.js]

View File

@ -5,14 +5,8 @@
ifdef ENABLE_TESTS
pp_mochitest_browser_files := \
browser_google.js \
$(NULL)
# This test depends on browser_aboutHome.js, which was disabled (bug 945667)
ifndef MOZ_WIDGET_GTK
pp_mochitest_browser_files += \
browser_google_behavior.js \
$(NULL)
endif
pp_mochitest_browser_files_PATH := $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
pp_mochitest_browser_files_FLAGS := -DMOZ_DISTRIBUTION_ID=$(MOZ_DISTRIBUTION_ID)

View File

@ -10,19 +10,18 @@ function ifWebGLSupported() {
let { gFront, $, EVENTS, ShadersListView, ShadersEditorsView } = panel.panelWin;
let reloaded = reload(target);
let firstProgram = yield once(gFront, "program-linked");
yield once(gFront, "program-linked");
yield reloaded;
let navigated = navigate(target, MULTIPLE_CONTEXTS_URL);
let [secondProgram, thirdProgram] = yield getPrograms(gFront, 2);
yield getPrograms(gFront, 2);
yield navigated;
let vsEditor = yield ShadersEditorsView._getEditor("vs");
let fsEditor = yield ShadersEditorsView._getEditor("fs");
yield navigateInHistory(target, "back", "will-navigate");
yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
yield waitForSources();
is($("#content").hidden, false,
"The tool's content should not be hidden.");
@ -37,8 +36,7 @@ function ifWebGLSupported() {
"The fragment shader editor contains the correct text.");
yield navigateInHistory(target, "forward", "will-navigate");
yield once(panel.panelWin, EVENTS.PROGRAMS_ADDED);
yield once(panel.panelWin, EVENTS.SOURCES_SHOWN);
yield waitForSources();
is($("#content").hidden, false,
"The tool's content should not be hidden.");
@ -54,4 +52,18 @@ function ifWebGLSupported() {
yield teardown(panel);
finish();
function waitForSources() {
let deferred = promise.defer();
let win = panel.panelWin;
// These events must fire in this in order and the second event fires
// synchronously after the first event, so we can't use Promise.all and the
// `once` helper.
win.once(win.EVENTS.PROGRAMS_ADDED, () => {
win.once(win.EVENTS.SOURCES_SHOWN, () => {
deferred.resolve();
});
});
return deferred.promise;
}
}

View File

@ -573,7 +573,7 @@ this.WinTaskbarJumpList =
}
break;
case "back":
case "active":
this._updateTimer();
break;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -467,6 +467,10 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
-moz-image-region: rect(18px, 630px, 36px, 612px);
}
#bookmarks-menu-button[cui-areatype="toolbar"][open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-moz-image-region: rect(36px, 630px, 54px, 612px);
}
#history-panelmenu@toolbarButtonPressed@ {
-moz-image-region: rect(18px, 180px, 36px, 162px);
}
@ -475,7 +479,6 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
-moz-image-region: rect(36px, 180px, 54px, 162px);
}
#downloads-indicator@toolbarButtonPressed@,
#downloads-button@toolbarButtonPressed@ {
-moz-image-region: rect(18px, 198px, 36px, 180px);
}
@ -671,6 +674,10 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
-moz-image-region: rect(36px, 1260px, 72px, 1224px);
}
#bookmarks-menu-button[cui-areatype="toolbar"][open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
-moz-image-region: rect(72px, 1260px, 108px, 1224px);
}
#history-panelmenu[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 360px, 36px, 324px);
}
@ -683,12 +690,10 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
-moz-image-region: rect(72px, 360px, 108px, 324px);
}
#downloads-indicator[cui-areatype="toolbar"],
#downloads-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 396px, 36px, 360px);
}
#downloads-indicator[cui-areatype="toolbar"]:hover:active:not([disabled="true"]),
#downloads-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(36px, 396px, 72px, 360px);
}
@ -941,7 +946,6 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
}
#downloads-button[cui-areatype="menu-panel"],
#downloads-indicator[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #downloads-button {
-moz-image-region: rect(0px, 512px, 64px, 448px);
}
@ -1916,6 +1920,11 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
}
#bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 18px;
height: 18px;
}
#bookmarks-menu-button[cui-areatype="toolbar"].bookmark-item > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
width: 16px;
height: 16px;
}

View File

@ -25,7 +25,6 @@ toolbarpaletteitem[place="palette"] > #history-panelmenu {
}
#downloads-button[cui-areatype="menu-panel"],
#downloads-indicator[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #downloads-button {
-moz-image-region: rect(0px, 256px, 32px, 224px);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -119,6 +119,21 @@ the build system.
There are various proposals for fixing this. See bug 795995.
Installing Python Manually
==========================
We highly recommend you use your system's package manager or a
well-supported 3rd party package manager to install Python for you. If
these are not available to you, we recommend the following tools for
installing Python:
* `buildout.python <https://github.com/collective/buildout.python>`_
* `pyenv <https://github.com/yyuu/pyenv>`_
* An official installer from http://www.python.org.
If all else fails, consider compiling Python from source manually. But this
should be viewed as the least desirable option.
Common Issues with Python
=========================

View File

@ -19,6 +19,8 @@ interface WorkerGlobalScope : EventTarget {
void close();
attribute OnErrorEventHandler onerror;
attribute EventHandler onoffline;
attribute EventHandler ononline;
// also has additional members in a partial interface
};

View File

@ -6,3 +6,4 @@ interface WorkerNavigator {
};
WorkerNavigator implements NavigatorID;
WorkerNavigator implements NavigatorOnLine;

View File

@ -17,17 +17,18 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release)
/* static */ already_AddRefed<WorkerNavigator>
WorkerNavigator::Create()
WorkerNavigator::Create(bool aOnLine)
{
RuntimeService* rts = RuntimeService::GetService();
MOZ_ASSERT(rts);
const RuntimeService::NavigatorStrings& strings =
rts->GetNavigatorStrings();
const RuntimeService::NavigatorProperties& properties =
rts->GetNavigatorProperties();
nsRefPtr<WorkerNavigator> navigator =
new WorkerNavigator(strings.mAppName, strings.mAppVersion,
strings.mPlatform, strings.mUserAgent);
new WorkerNavigator(properties.mAppName, properties.mAppVersion,
properties.mPlatform, properties.mUserAgent,
aOnLine);
return navigator.forget();
}

View File

@ -18,15 +18,18 @@ class WorkerNavigator MOZ_FINAL : public nsWrapperCache
nsString mAppVersion;
nsString mPlatform;
nsString mUserAgent;
bool mOnline;
WorkerNavigator(const nsAString& aAppName,
const nsAString& aAppVersion,
const nsAString& aPlatform,
const nsAString& aUserAgent)
const nsAString& aUserAgent,
bool aOnline)
: mAppName(aAppName)
, mAppVersion(aAppVersion)
, mPlatform(aPlatform)
, mUserAgent(aUserAgent)
, mOnline(aOnline)
{
MOZ_COUNT_CTOR(WorkerNavigator);
SetIsDOMBinding();
@ -38,7 +41,7 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerNavigator)
static already_AddRefed<WorkerNavigator>
Create();
Create(bool aOnLine);
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
@ -60,10 +63,12 @@ public:
{
aAppName = mAppName;
}
void GetAppVersion(nsString& aAppVersion) const
{
aAppVersion = mAppVersion;
}
void GetPlatform(nsString& aPlatform) const
{
aPlatform = mPlatform;
@ -76,10 +81,22 @@ public:
{
return false;
}
void GetUserAgent(nsString& aUserAgent) const
{
aUserAgent = mUserAgent;
}
bool OnLine() const
{
return mOnline;
}
// Worker thread only!
void SetOnLine(bool aOnline)
{
mOnline = aOnline;
}
};
END_WORKERS_NAMESPACE

View File

@ -1223,7 +1223,7 @@ bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false };
RuntimeService::RuntimeService()
: mMutex("RuntimeService::mMutex"), mObserved(false),
mShuttingDown(false), mNavigatorStringsLoaded(false)
mShuttingDown(false), mNavigatorPropertiesLoaded(false)
{
AssertIsOnMainThread();
NS_ASSERTION(!gRuntimeService, "More than one service!");
@ -1352,17 +1352,17 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
}
}
else {
if (!mNavigatorStringsLoaded) {
NS_GetNavigatorAppName(mNavigatorStrings.mAppName);
if (NS_FAILED(NS_GetNavigatorAppVersion(mNavigatorStrings.mAppVersion)) ||
NS_FAILED(NS_GetNavigatorPlatform(mNavigatorStrings.mPlatform)) ||
NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorStrings.mUserAgent))) {
if (!mNavigatorPropertiesLoaded) {
NS_GetNavigatorAppName(mNavigatorProperties.mAppName);
if (NS_FAILED(NS_GetNavigatorAppVersion(mNavigatorProperties.mAppVersion)) ||
NS_FAILED(NS_GetNavigatorPlatform(mNavigatorProperties.mPlatform)) ||
NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorProperties.mUserAgent))) {
JS_ReportError(aCx, "Failed to load navigator strings!");
UnregisterWorker(aCx, aWorkerPrivate);
return false;
}
mNavigatorStringsLoaded = true;
mNavigatorPropertiesLoaded = true;
}
nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
@ -1660,6 +1660,10 @@ RuntimeService::Init()
NS_WARNING("Failed to register for memory pressure notifications!");
}
if (NS_FAILED(obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) {
NS_WARNING("Failed to register for offline notification event!");
}
NS_ASSERTION(!gRuntimeServiceDuringInit, "This should be null!");
gRuntimeServiceDuringInit = this;
@ -1911,6 +1915,10 @@ RuntimeService::Cleanup()
NS_WARNING("Failed to unregister for memory pressure notifications!");
}
if (NS_FAILED(obs->RemoveObserver(this,
NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) {
NS_WARNING("Failed to unregister for offline notification event!");
}
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID);
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
mObserved = false;
@ -2288,6 +2296,12 @@ RuntimeService::CycleCollectAllWorkers()
BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false);
}
void
RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline)
{
BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline);
}
// nsISupports
NS_IMPL_ISUPPORTS1(RuntimeService, nsIObserver)
@ -2319,6 +2333,10 @@ RuntimeService::Observe(nsISupports* aSubject, const char* aTopic,
CycleCollectAllWorkers();
return NS_OK;
}
if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline());
return NS_OK;
}
NS_NOTREACHED("Unknown observer topic!");
return NS_OK;

View File

@ -100,7 +100,7 @@ private:
static bool sDefaultPreferences[WORKERPREF_COUNT];
public:
struct NavigatorStrings
struct NavigatorProperties
{
nsString mAppName;
nsString mAppVersion;
@ -109,12 +109,12 @@ public:
};
private:
NavigatorStrings mNavigatorStrings;
NavigatorProperties mNavigatorProperties;
// True when the observer service holds a reference to this object.
bool mObserved;
bool mShuttingDown;
bool mNavigatorStringsLoaded;
bool mNavigatorPropertiesLoaded;
public:
NS_DECL_ISUPPORTS
@ -150,10 +150,10 @@ public:
void
ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
const NavigatorStrings&
GetNavigatorStrings() const
const NavigatorProperties&
GetNavigatorProperties() const
{
return mNavigatorStrings;
return mNavigatorProperties;
}
void
@ -239,6 +239,9 @@ public:
void
CycleCollectAllWorkers();
void
SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline);
private:
RuntimeService();
~RuntimeService();

View File

@ -73,6 +73,7 @@
#include "File.h"
#include "MessagePort.h"
#include "Navigator.h"
#include "Principal.h"
#include "RuntimeService.h"
#include "ScriptLoader.h"
@ -1693,6 +1694,26 @@ public:
}
};
class OfflineStatusChangeRunnable : public WorkerRunnable
{
public:
OfflineStatusChangeRunnable(WorkerPrivate* aWorkerPrivate, bool aIsOffline)
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
mIsOffline(aIsOffline)
{
}
bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
aWorkerPrivate->OfflineStatusChangeEventInternal(aCx, mIsOffline);
return true;
}
private:
bool mIsOffline;
};
class WorkerJSRuntimeStats : public JS::RuntimeStats
{
const nsACString& mRtPath;
@ -3035,6 +3056,56 @@ WorkerPrivateParent<Derived>::CycleCollect(JSContext* aCx, bool aDummy)
}
}
template <class Derived>
void
WorkerPrivateParent<Derived>::OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline)
{
AssertIsOnParentThread();
nsRefPtr<OfflineStatusChangeRunnable> runnable =
new OfflineStatusChangeRunnable(ParentAsWorkerPrivate(), aIsOffline);
if (!runnable->Dispatch(aCx)) {
NS_WARNING("Failed to dispatch offline status change event!");
JS_ClearPendingException(aCx);
}
}
void
WorkerPrivate::OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline)
{
AssertIsOnWorkerThread();
for (uint32_t index = 0; index < mChildWorkers.Length(); ++index) {
mChildWorkers[index]->OfflineStatusChangeEvent(aCx, aIsOffline);
}
mOnLine = !aIsOffline;
WorkerGlobalScope* globalScope = GlobalScope();
nsRefPtr<WorkerNavigator> nav = globalScope->GetExistingNavigator();
if (nav) {
nav->SetOnLine(mOnLine);
}
nsString eventType;
if (aIsOffline) {
eventType.AssignLiteral("offline");
} else {
eventType.AssignLiteral("online");
}
nsCOMPtr<nsIDOMEvent> event;
nsresult rv =
NS_NewDOMEvent(getter_AddRefs(event), globalScope, nullptr, nullptr);
NS_ENSURE_SUCCESS_VOID(rv);
rv = event->InitEvent(eventType, false, false);
NS_ENSURE_SUCCESS_VOID(rv);
event->SetTrusted(true);
globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
}
template <class Derived>
bool
WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
@ -3554,10 +3625,12 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx,
if (aParent) {
aParent->AssertIsOnWorkerThread();
aParent->GetAllPreferences(mPreferences);
mOnLine = aParent->OnLine();
}
else {
AssertIsOnMainThread();
RuntimeService::GetDefaultPreferences(mPreferences);
mOnLine = !NS_IsOffline();
}
}

View File

@ -392,6 +392,9 @@ public:
void
CycleCollect(JSContext* aCx, bool aDummy);
void
OfflineStatusChangeEvent(JSContext* aCx, bool aIsOffline);
bool
RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
@ -743,6 +746,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
#endif
bool mPreferences[WORKERPREF_COUNT];
bool mOnLine;
protected:
~WorkerPrivate();
@ -902,6 +906,9 @@ public:
void
CycleCollectInternal(JSContext* aCx, bool aCollectChildren);
void
OfflineStatusChangeEventInternal(JSContext* aCx, bool aIsOffline);
JSContext*
GetJSContext() const
{
@ -983,6 +990,13 @@ public:
return mPreferences[WORKERPREF_PROMISE];
}
bool
OnLine() const
{
AssertIsOnWorkerThread();
return mOnLine;
}
void
StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);

View File

@ -99,7 +99,7 @@ WorkerGlobalScope::Navigator()
mWorkerPrivate->AssertIsOnWorkerThread();
if (!mNavigator) {
mNavigator = WorkerNavigator::Create();
mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine());
MOZ_ASSERT(mNavigator);
}
@ -107,6 +107,15 @@ WorkerGlobalScope::Navigator()
return navigator.forget();
}
already_AddRefed<WorkerNavigator>
WorkerGlobalScope::GetExistingNavigator() const
{
mWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<WorkerNavigator> navigator = mNavigator;
return navigator.forget();
}
void
WorkerGlobalScope::Close(JSContext* aCx)
{

View File

@ -60,8 +60,13 @@ public:
already_AddRefed<WorkerLocation>
Location();
already_AddRefed<WorkerNavigator>
Navigator();
already_AddRefed<WorkerNavigator>
GetExistingNavigator() const;
void
Close(JSContext* aCx);
@ -97,6 +102,8 @@ public:
void
Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
IMPL_EVENT_HANDLER(online)
IMPL_EVENT_HANDLER(offline)
IMPL_EVENT_HANDLER(close)
void

View File

@ -27,6 +27,9 @@ support-files =
multi_sharedWorker_sharedWorker.js
navigator_worker.js
newError_worker.js
onLine_worker.js
onLine_worker_child.js
onLine_worker_head.js
promise_worker.js
recursion_worker.js
recursiveOnerror_worker.js
@ -90,6 +93,7 @@ support-files =
[test_multi_sharedWorker_lifetimes.html]
[test_navigator.html]
[test_newError.html]
[test_onLine.html]
[test_promise.html]
[test_recursion.html]
[test_recursiveOnerror.html]

View File

@ -9,7 +9,8 @@ var supportedProps = [
"platform",
"product",
"taintEnabled",
"userAgent"
"userAgent",
"onLine"
];
for (var prop in navigator) {

View File

@ -0,0 +1,65 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
importScripts("onLine_worker_head.js");
var N_CHILDREN = 3;
var children = [];
var finishedChildrenCount = 0;
var lastTest = false;
for (var event of ["online", "offline"]) {
addEventListener(event,
makeHandler(
"addEventListener('%1', ..., false)",
event, 1, "Parent Worker"),
false);
}
onmessage = function(e) {
if (e.data === 'lastTest') {
children.forEach(function(w) {
w.postMessage({ type: 'lastTest' });
});
lastTest = true;
}
}
function setupChildren(cb) {
var readyCount = 0;
for (var i = 0; i < N_CHILDREN; ++i) {
var w = new Worker("onLine_worker_child.js");
children.push(w);
w.onerror = function(e) {
info("Error creating child " + e.message);
}
w.onmessage = function(e) {
if (e.data.type === 'ready') {
info("Got ready from child");
readyCount++;
if (readyCount === N_CHILDREN) {
cb();
}
} else if (e.data.type === 'finished') {
finishedChildrenCount++;
if (lastTest && finishedChildrenCount === N_CHILDREN) {
postMessage({ type: 'finished' });
children = [];
close();
}
} else if (e.data.type === 'ok') {
// Pass on test to page.
postMessage(e.data);
}
}
}
}
setupChildren(function() {
postMessage({ type: 'ready' });
});

View File

@ -0,0 +1,75 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function info(text) {
dump("Test for Bug 925437: worker: " + text + "\n");
}
function ok(test, message) {
postMessage({ type: 'ok', test: test, message: message });
}
/**
* Returns a handler function for an online/offline event. The returned handler
* ensures the passed event object has expected properties and that the handler
* is called at the right moment (according to the gState variable).
* @param nameTemplate The string identifying the hanlder. '%1' in that
* string will be replaced with the event name.
* @param eventName 'online' or 'offline'
* @param expectedState value of gState at the moment the handler is called.
* The handler increases gState by one before checking
* if it matches expectedState.
*/
function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) {
prefix += ": ";
return function(e) {
var name = nameTemplate.replace(/%1/, eventName);
ok(e.constructor == Event, prefix + "event should be an Event");
ok(e.type == eventName, prefix + "event type should be " + eventName);
ok(!e.bubbles, prefix + "event should not bubble");
ok(!e.cancelable, prefix + "event should not be cancelable");
ok(e.target == self, prefix + "the event target should be the worker scope");
ok(eventName == 'online' ? navigator.onLine : !navigator.onLine, prefix + "navigator.onLine " + navigator.onLine + " should reflect event " + eventName);
if (custom) {
custom();
}
}
}
var lastTest = false;
function lastTestTest() {
if (lastTest) {
postMessage({ type: 'finished' });
close();
}
}
for (var event of ["online", "offline"]) {
addEventListener(event,
makeHandler(
"addEventListener('%1', ..., false)",
event, 1, "Child Worker", lastTestTest
),
false);
}
onmessage = function(e) {
if (e.data.type === 'lastTest') {
lastTest = true;
} else if (e.data.type === 'navigatorState') {
ok(e.data.state === navigator.onLine, "Child and parent navigator state should match");
}
}
postMessage({ type: 'ready' });

View File

@ -0,0 +1,43 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function info(text) {
dump("Test for Bug 925437: worker: " + text + "\n");
}
function ok(test, message) {
postMessage({ type: 'ok', test: test, message: message });
}
/**
* Returns a handler function for an online/offline event. The returned handler
* ensures the passed event object has expected properties and that the handler
* is called at the right moment (according to the gState variable).
* @param nameTemplate The string identifying the hanlder. '%1' in that
* string will be replaced with the event name.
* @param eventName 'online' or 'offline'
* @param expectedState value of gState at the moment the handler is called.
* The handler increases gState by one before checking
* if it matches expectedState.
*/
function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) {
prefix += ": ";
return function(e) {
var name = nameTemplate.replace(/%1/, eventName);
ok(e.constructor == Event, prefix + "event should be an Event");
ok(e.type == eventName, prefix + "event type should be " + eventName);
ok(!e.bubbles, prefix + "event should not bubble");
ok(!e.cancelable, prefix + "event should not be cancelable");
ok(e.target == self, prefix + "the event target should be the worker scope");
ok(eventName == 'online' ? navigator.onLine : !navigator.onLine, prefix + "navigator.onLine " + navigator.onLine + " should reflect event " + eventName);
if (custom) {
custom();
}
}
}

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 925437: online/offline events tests.
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<head>
<title>Test for Bug 925437 (worker online/offline events)</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=925437">Mozilla Bug 925437</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
addLoadEvent(function() {
var w = new Worker("onLine_worker.js");
w.onmessage = function(e) {
if (e.data.type === 'ready') {
doTest();
} else if (e.data.type === 'ok') {
ok(e.data.test, e.data.message);
} else if (e.data.type === 'finished') {
SimpleTest.finish();
}
}
function doTest() {
var iosvc = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
.getService(SpecialPowers.Ci.nsIIOService2);
iosvc.manageOfflineStatus = false;
info("setting iosvc.offline = true");
iosvc.offline = true;
info("setting iosvc.offline = false");
iosvc.offline = false;
info("setting iosvc.offline = true");
iosvc.offline = true;
for (var i = 0; i < 10; ++i) {
iosvc.offline = !iosvc.offline;
}
info("setting iosvc.offline = false");
w.postMessage('lastTest');
iosvc.offline = false;
}
});
SimpleTest.waitForExplicitFinish();
</script>
</body>
</html>

View File

@ -1,6 +1,6 @@
This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
Current version derived from upstream changeset 6b8ddb7d599f
Current version derived from upstream changeset 1efd96aeade9
See gfx/graphite2/moz-gr-update.sh for update procedure.

View File

@ -30,7 +30,7 @@
#define GR2_VERSION_MAJOR 1
#define GR2_VERSION_MINOR 2
#define GR2_VERSION_BUGFIX 3
#define GR2_VERSION_BUGFIX 4
#ifdef __cplusplus
extern "C"
@ -296,7 +296,7 @@ typedef struct gr_font_ops gr_font_ops;
* @param appFontHandle font specific information that must stay alive as long
* as the font does
* @param font_ops pointer font specific callback structure for hinted metrics.
* Must stay alive for the duration of the call.
* Need only stay alive for the duration of the call.
* @param face the face this font corresponds to. Must stay alive as long as
* the font does.
*/
@ -310,7 +310,6 @@ GR2_API gr_font* gr_make_font_with_ops(float ppm, const void* appFontHandle, con
* @param appFontHandle font specific information that must stay alive as long
* as the font does
* @param getAdvance callback function reference that returns horizontal advance in pixels for a glyph.
* Must stay alive for the duration of the call.
* @param face the face this font corresponds to. Must stay alive as long as
* the font does.
*/

View File

@ -125,6 +125,8 @@ enum gr_attrCode {
gr_slatSegSplit = gr_slatJStretch + 29,
/// User defined attribute, see subattr for user attr number
gr_slatUserDefn,
/// Bidi level
gr_slatBidiLevel,
/// not implemented
gr_slatMax,
@ -327,8 +329,8 @@ GR2_API const gr_slot* gr_slot_first_attachment(const gr_slot* p);
*
* This returns the next slot in the singly linked list of slots attached to this
* slot's parent. If there are no more such slots, NULL is returned. If there is
* no parent, i.e. the passed slot is a base, then the next base in graphical order
* (ltr even for rtl text) is returned.
* no parent, i.e. the passed slot is a cluster base, then the next cluster base
* in graphical order (ltr, even for rtl text) is returned.
*
* if gr_slot_next_sibling_attachment(p) != NULL then gr_slot_attached_to(gr_slot_next_sibling_attachment(p)) == gr_slot_attached_to(p).
*/
@ -398,7 +400,13 @@ GR2_API unsigned int gr_slot_index(const gr_slot* p/*not NULL*/);
*/
GR2_API int gr_slot_attr(const gr_slot* p/*not NULL*/, const gr_segment* pSeg/*not NULL*/, enum gr_attrCode index, gr_uint8 subindex); //tbd - do we need to expose this?
/** Returns whether text may be inserted before this glyph [check this isn't inverted] **/
/** Returns whether text may be inserted before this glyph.
*
* This indicates whether a cursor can be put before this slot. It applies to
* base glyphs that have no parent as well as attached glyphs that have the
* .insert attribute explicitly set to true. This is the primary mechanism
* for identifying contiguous sequences of base plus diacritics.
*/
GR2_API int gr_slot_can_insert_before(const gr_slot* p);
/** Returns the original gr_char_info index this slot refers to.

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
@ -27,6 +27,7 @@ of the License or (at your option) any later version.
#include "inc/Main.h"
#include "inc/Slot.h"
#include "inc/Segment.h"
#include "inc/Bidi.h"
using namespace graphite2;
@ -50,106 +51,273 @@ enum DirCode { // Hungarian: dirc
RLE = 14, // RTL embedding
PDF = 15, // pop directional format
NSM = 16, // non-space mark
LRI = 17, // LRI isolate
RLI = 18, // RLI isolate
FSI = 19, // FSI isolate
PDI = 20, // pop isolate
OPP = 21, // opening paired parenthesis
CPP = 22, // closing paired parenthesis
ON = N
};
enum DirMask {
Nmask = 1,
Lmask = 2,
Rmask = 4,
ALmask = 8,
ENmask = 0x10,
ESmask = 0x20,
ETmask = 0x40,
ANmask = 0x80,
CSmask = 0x100,
WSmask = 0x200,
BNmask = 0x400,
LROmask = 0x800,
RLOmask = 0x1000,
LREmask = 0x2000,
RLEmask = 0x4000,
PDFmask = 0x8000,
NSMmask = 0x10000
WSflag = (1 << 7), // keep track of WS for eos handling
WSMask = ~(1 << 7)
};
unsigned int bidi_class_map[] = { 0, 1, 2, 5, 4, 8, 9, 3, 7, 0, 0, 0, 0, 0, 0, 0, 6 };
// Algorithms based on Unicode reference standard code. Thanks Asmus Freitag.
#define MAX_LEVEL 61
inline uint8 BaseClass(Slot *s) { return s->getBidiClass() & WSMask; }
Slot *resolveExplicit(int level, int dir, Slot *s, int nNest = 0)
unsigned int bidi_class_map[] = { 0, 1, 2, 5, 4, 8, 9, 3, 7, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 };
// Algorithms based on Unicode reference standard code. Thanks Asmus Freitag.
void resolveWeak(Slot *start, int sos, int eos);
void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos);
void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack);
inline int calc_base_level(Slot *s)
{
int nLastValid = nNest;
Slot *res = NULL;
for ( ; s && !res; s = s->next())
int count = 0;
for ( ; s; s = s->next())
{
int cls = s->getBidiClass();
if (count)
{
switch(cls)
{
case LRO:
case LRE:
nNest++;
if (level & 1)
s->setBidiLevel(level + 1);
else
s->setBidiLevel(level + 2);
if (s->getBidiLevel() > MAX_LEVEL)
s->setBidiLevel(level);
else
{
s = resolveExplicit(s->getBidiLevel(), (cls == LRE ? N : L), s->next(), nNest);
nNest--;
if (s) continue; else break;
}
cls = BN;
s->setBidiClass(cls);
case LRI :
case RLI :
case FSI :
++count;
break;
case RLO:
case RLE:
nNest++;
if (level & 1)
s->setBidiLevel(level + 2);
else
s->setBidiLevel(level + 1);
if (s->getBidiLevel() > MAX_LEVEL)
s->setBidiLevel(level);
case PDI :
--count;
}
}
else
{
s = resolveExplicit(s->getBidiLevel(), (cls == RLE ? N : R), s->next(), nNest);
nNest--;
if (s) continue; else break;
switch(cls)
{
case L :
return 0;
case R :
case AL :
return 1;
case LRI :
case RLI :
case FSI :
++count;
}
cls = BN;
s->setBidiClass(cls);
}
}
return 0;
}
// inline or not?
void do_resolves(Slot *start, int level, int sos, int eos, int &bmask, Segment *seg, uint8 aMirror, BracketPairStack &stack)
{
if (bmask & 0x1F1178)
resolveWeak(start, sos, eos);
if (bmask & 0x200000)
processParens(start, seg, aMirror, level, stack);
if (bmask & 0x7E0361)
resolveNeutrals(start, level, sos, eos);
bmask = 0;
}
enum maxs
{
MAX_LEVEL = 125,
};
// returns where we are up to in processing
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 &bstack)
{
int bmask = 0;
Slot *s = start;
Slot *slast = start;
Slot *scurr = 0;
Slot *stemp;
int lnextLevel = nextLevel;
int newLevel;
int empty = 1;
for ( ; s; s = s ? s->next() : s)
{
int cls = s->getBidiClass();
bmask |= (1 << cls);
s->setBidiLevel(level);
// we keep s->prev() pointing backwards for PDI repeating
switch (cls)
{
case BN :
if (slast == s) slast = s->next(); // ignore if at front of text
continue;
case LRE :
case LRO :
case RLE :
case RLO :
switch (cls)
{
case LRE :
case LRO :
newLevel = level + (level & 1 ? 1 : 2);
break;
case RLE :
case RLO :
newLevel = level + (level & 1 ? 2 : 1);
break;
}
s->setBidiClass(BN);
if (isolerr || newLevel > MAX_LEVEL || embederr)
{
if (!isolerr) ++embederr;
break;
}
stemp = scurr;
if (scurr)
scurr->prev(0); // don't include control in string
lnextLevel = newLevel;
scurr = s;
s->setBidiLevel(newLevel); // to make it vanish
// recurse for the new subsequence. A sequence only contains text at the same level
s = process_bidi(s->next(), newLevel, level, lnextLevel, cls < LRE, 0, cisol, isolerr, embederr, 0, seg, aMirror, bstack);
// s points at PDF or end of sequence
// try to keep extending the run and not process it until we have to
if (lnextLevel != level || !s) // if the subsequence really had something in it, or we are at the end of the run
{
if (slast != scurr) // process the run now, don't try to extend it
{
// process text preceeding embedding
do_resolves(slast, level, (prelevel > level ? prelevel : level) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack);
empty = 0;
nextLevel = level;
}
else if (lnextLevel != level) // the subsequence had something
{
empty = 0; // so we aren't empty either
nextLevel = lnextLevel; // but since we really are empty, pass back our level from the subsequence
}
if (s) // if still more to process
{
prelevel = lnextLevel; // future text starts out with sos of the higher subsequence
lnextLevel = level; // and eos is our level
}
slast = s ? s->next() : s;
}
else if (stemp)
stemp->prev(s);
break;
case PDF :
cls = BN;
s->setBidiClass(cls);
if (nNest)
s->setBidiClass(BN);
s->prev(0); // unstitch us since we skip final stitching code when we return
if (isol || isolerr || init) // boundary error conditions
break;
if (embederr)
{
if (nLastValid < nNest)
--nNest;
else
res = s;
}
--embederr;
break;
}
if (slast != s)
{
scurr->prev(0); // if slast, then scurr. Terminate before here
do_resolves(slast, level, level & 1, level & 1, bmask, seg, aMirror, bstack);
empty = 0;
}
if (empty)
{
nextLevel = prelevel; // no contents? set our level to that of parent
s->setBidiLevel(prelevel);
}
return s;
if (dir != N)
cls = dir;
case FSI :
case LRI :
case RLI :
switch (cls)
{
case FSI :
if (calc_base_level(s->next()))
newLevel = level + (level & 1 ? 2 : 1);
else
newLevel = level + (level & 1 ? 1 : 2);
break;
case LRI :
newLevel = level + (level & 1 ? 1 : 2);
break;
case RLI :
newLevel = level + (level & 1 ? 2 : 1);
break;
}
if (newLevel > MAX_LEVEL || isolerr)
{
++isolerr;
s->setBidiClass(ON | WSflag);
break;
}
++cisol;
if (scurr) scurr->prev(s);
scurr = s; // include FSI
lnextLevel = newLevel;
// recurse for the new sub sequence
s = process_bidi(s->next(), newLevel, newLevel, lnextLevel, 0, 1, cisol, isolerr, embederr, 0, seg, aMirror, bstack);
// s points at PDI
if (s)
{
s->setBidiLevel(level);
if (s->getBidiClass() != BN)
s->setBidiClass(cls);
bmask |= 1 << BaseClass(s); // include the PDI in the mask
s->setBidiLevel(level); // reset its level to our level
}
else
lnextLevel = level;
break;
case PDI :
if (isolerr)
{
--isolerr;
s->setBidiClass(ON | WSflag);
break;
}
return res;
if (init || !cisol)
{
s->setBidiClass(ON | WSflag);
break;
}
embederr = 0;
if (!isol) // we are in an embedded subsequence, we have to return through all those
{
if (empty) // if empty, reset the level to tell embedded parent
nextLevel = prelevel;
return s->prev(); // keep working up the stack pointing at this PDI until we get to an isolate entry
}
else // we are terminating an isolate sequence
{
if (slast != s) // process any remaining content in this subseqence
{
scurr->prev(0);
do_resolves(slast, level, prelevel & 1, level & 1, bmask, seg, aMirror, bstack);
}
--cisol; // pop the isol sequence from the stack
return s;
}
default :
if (dirover)
s->setBidiClass((level & 1 ? R : L) | (WSflag * (cls == WS)));
}
if (s) s->prev(0); // unstitch us
if (scurr) // stitch in text for processing
scurr->prev(s);
scurr = s; // add us to text to process
}
if (slast != s)
{
do_resolves(slast, level, (level > prelevel ? level : prelevel) & 1, lnextLevel & 1, bmask, seg, aMirror, bstack);
empty = 0;
}
if (empty || isol)
nextLevel = prelevel;
return s;
}
// === RESOLVE WEAK TYPES ================================================
@ -183,30 +351,6 @@ enum bidi_state // possible states
let, // ET following le
} ;
enum bidi_state_mask
{
xamask = 1,
xrmask = 2,
xlmask = 4,
aomask = 8,
romask = 0x10,
lomask = 0x20,
rtmask = 0x40,
ltmask = 0x80,
cnmask = 0x100,
ramask = 0x200,
remask = 0x400,
lamask = 0x800,
lemask = 0x1000,
acmask = 0x2000,
rcmask = 0x4000,
rsmask = 0x8000,
lcmask = 0x10000,
lsmask = 0x20000,
retmask = 0x40000,
letmask = 0x80000
};
const bidi_state stateWeak[][10] =
{
// N, L, R, AN, EN, AL,NSM, CS, ES, ET,
@ -300,66 +444,6 @@ const bidi_action actionWeak[][10] =
inline uint8 GetDeferredType(bidi_action a) { return (a >> 4) & 0xF; }
inline uint8 GetResolvedType(bidi_action a) { return a & 0xF; }
inline DirCode EmbeddingDirection(int l) { return l & 1 ? R : L; }
inline bool IsDeferredState(bidi_state a) { return 0 != ((1 << a) & (rtmask | ltmask | acmask | rcmask | rsmask | lcmask | lsmask)); }
inline bool IsModifiedClass(DirCode a) { return 0 != ((1 << a) & (ALmask | NSMmask | ESmask | CSmask | ETmask | ENmask)); }
void SetDeferredRunClass(Slot *s, Slot *sRun, int nval)
{
if (!sRun || s == sRun) return;
for (Slot *p = s->prev(); p != sRun; p = p->prev())
p->setBidiClass(nval);
}
void resolveWeak(int baseLevel, Slot *s)
{
int state = (baseLevel & 1) ? xr : xl;
int cls;
int level = baseLevel;
Slot *sRun = NULL;
Slot *sLast = s;
for ( ; s; s = s->next())
{
sLast = s;
cls = s->getBidiClass();
if (cls == BN)
{
s->setBidiLevel(level);
if (!s->next() && level != baseLevel)
s->setBidiClass(EmbeddingDirection(level));
else if (s->next() && level != s->next()->getBidiLevel() && s->next()->getBidiClass() != BN)
{
int newLevel = s->next()->getBidiLevel();
if (level > newLevel)
newLevel = level;
s->setBidiLevel(newLevel);
s->setBidiClass(EmbeddingDirection(newLevel));
level = s->next()->getBidiLevel();
}
else
continue;
}
bidi_action action = actionWeak[state][bidi_class_map[cls]];
int clsRun = GetDeferredType(action);
if (clsRun != XX)
{
SetDeferredRunClass(s, sRun, clsRun);
sRun = NULL;
}
int clsNew = GetResolvedType(action);
if (clsNew != XX)
s->setBidiClass(clsNew);
if (!sRun && (IX & action))
sRun = s->prev();
state = stateWeak[state][bidi_class_map[cls]];
}
cls = EmbeddingDirection(level);
int clsRun = GetDeferredType(actionWeak[state][bidi_class_map[cls]]);
if (clsRun != XX)
SetDeferredRunClass(sLast, sRun, clsRun);
}
// Neutrals
enum neutral_action
@ -373,6 +457,151 @@ enum neutral_action
LnL = (1<<4)+L, // set run and EN to L
};
// ->prev() here means ->next()
void SetDeferredRunClass(Slot *s, Slot *sRun, int nval)
{
if (!sRun || s == sRun) return;
for (Slot *p = sRun; p != s; p = p->prev())
if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag);
else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag));
}
void SetThisDeferredRunClass(Slot *s, Slot *sRun, int nval)
{
if (!sRun) return;
for (Slot *p = sRun, *e = s->prev(); p != e; p = p->prev())
if (p->getBidiClass() == WS) p->setBidiClass(nval | WSflag);
else if (BaseClass(p) != BN) p->setBidiClass(nval | (p->getBidiClass() & WSflag));
}
void resolveWeak(Slot *start, int sos, int eos)
{
int state = (sos & 1) ? xr : xl;
int cls;
Slot *s = start;
Slot *sRun = NULL;
Slot *sLast = s;
for ( ; s; s = s->prev())
{
sLast = s;
cls = BaseClass(s);
switch (cls)
{
case BN :
if (s == start) start = s->prev(); // skip initial BNs for NSM resolving
continue;
case LRI :
case RLI :
case FSI :
case PDI :
{
Slot *snext = s->prev();
if (snext && snext->getBidiClass() == NSM)
snext->setBidiClass(ON);
s->setBidiClass(ON | WSflag);
}
break;
case NSM :
if (s == start)
{
cls = EmbeddingDirection(sos);
s->setBidiClass(cls);
}
break;
}
bidi_action action = actionWeak[state][bidi_class_map[cls]];
int clsRun = GetDeferredType(action);
if (clsRun != XX)
{
SetDeferredRunClass(s, sRun, clsRun);
sRun = NULL;
}
int clsNew = GetResolvedType(action);
if (clsNew != XX)
s->setBidiClass(clsNew);
if (!sRun && (IX & action))
sRun = s;
state = stateWeak[state][bidi_class_map[cls]];
}
cls = EmbeddingDirection(eos);
int clsRun = GetDeferredType(actionWeak[state][bidi_class_map[cls]]);
if (clsRun != XX)
SetThisDeferredRunClass(sLast, sRun, clsRun);
}
void processParens(Slot *s, Segment *seg, uint8 aMirror, int level, BracketPairStack &stack)
{
uint8 mask = 0;
int8 lastDir = -1;
BracketPair *p;
for ( ; s; s = s->prev()) // walk the sequence
{
uint16 ogid = seg->glyphAttr(s->gid(), aMirror);
int cls = BaseClass(s);
switch(cls)
{
case OPP :
stack.orin(mask);
stack.push(ogid, s, lastDir, lastDir != CPP);
mask = 0;
lastDir = OPP;
break;
case CPP :
stack.orin(mask);
p = stack.scan(s->gid());
if (!p) break;
mask = 0;
stack.close(p, s);
lastDir = CPP;
break;
case L :
lastDir = L;
mask |= 1;
break;
case R :
case AL :
case AN :
case EN :
lastDir = R;
mask |= 2;
}
}
for (p = stack.start(); p; p =p->next()) // walk the stack
{
if (p->close() && p->mask())
{
int dir = (level & 1) + 1;
if (p->mask() & dir)
{ }
else if (p->mask() & (1 << (~level & 1))) // if inside has strong other embedding
{
int ldir = p->before();
if ((p->before() == OPP || p->before() == CPP) && p->prev())
{
for (BracketPair *q = p->prev(); q; q = q->prev())
{
ldir = q->open()->getBidiClass();
if (ldir < 3) break;
ldir = q->before();
if (ldir < 3) break;
}
if (ldir > 2) ldir = 0;
}
if (ldir > 0 && (ldir - 1) != (level & 1)) // is dir given opp. to level dir (ldir == R or L)
dir = (~level & 1) + 1;
}
p->open()->setBidiClass(dir);
p->close()->setBidiClass(dir);
}
}
stack.clear();
}
int GetDeferredNeutrals(int action, int level)
{
action = (action >> 4) & 0xF;
@ -384,11 +613,7 @@ int GetDeferredNeutrals(int action, int level)
int GetResolvedNeutrals(int action)
{
action = action & 0xF;
if (action == In)
return 0;
else
return action;
return action & 0xF;
}
// state values
@ -403,12 +628,11 @@ enum neutral_state
na, // N preceeded by a
} ;
const uint8 neutral_class_map[] = { 0, 1, 2, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0 };
const uint8 neutral_class_map[] = { 0, 1, 2, 0, 4, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const int actionNeutrals[][5] =
{
// N, L, R, AN, EN, = cls
// state =
// cls= N, L, R, AN, EN, state =
{ In, 0, 0, 0, 0, }, // r right
{ In, 0, 0, 0, L, }, // l left
@ -421,8 +645,7 @@ const int actionNeutrals[][5] =
const int stateNeutrals[][5] =
{
// N, L, R, AN, EN = cls
// state =
// cls= N, L, R, AN, EN state =
{ rn, l, r, r, r, }, // r right
{ ln, l, r, a, l, }, // l left
@ -433,25 +656,29 @@ const int stateNeutrals[][5] =
{ na, l, r, a, l, }, // na N preceded by la
} ;
void resolveNeutrals(int baseLevel, Slot *s)
void resolveNeutrals(Slot *s, int baseLevel, int sos, int eos)
{
int state = baseLevel ? r : l;
int state = (sos & 1) ? r : l;
int cls;
Slot *sRun = NULL;
Slot *sLast = s;
int level = baseLevel;
for ( ; s; s = s->next())
for ( ; s; s = s->prev())
{
sLast = s;
cls = s->getBidiClass();
if (cls == BN)
cls = BaseClass(s);
switch (cls)
{
if (sRun)
sRun = sRun->prev();
case BN :
continue;
case LRI :
case RLI :
case FSI :
s->setBidiClass(BN | WSflag);
continue;
}
default :
int action = actionNeutrals[state][neutral_class_map[cls]];
int clsRun = GetDeferredNeutrals(action, level);
if (clsRun != N)
@ -463,20 +690,19 @@ void resolveNeutrals(int baseLevel, Slot *s)
if (clsNew != N)
s->setBidiClass(clsNew);
if (!sRun && (action & In))
sRun = s->prev();
sRun = s;
state = stateNeutrals[state][neutral_class_map[cls]];
level = s->getBidiLevel();
}
cls = EmbeddingDirection(level);
}
cls = EmbeddingDirection(eos);
int clsRun = GetDeferredNeutrals(actionNeutrals[state][neutral_class_map[cls]], level);
if (clsRun != N)
SetDeferredRunClass(sLast, sRun, clsRun);
SetThisDeferredRunClass(sLast, sRun, clsRun);
}
const int addLevel[][4] =
{
// L, R, AN, EN = cls
// level =
// cls = L, R, AN, EN level =
/* even */ { 0, 1, 2, 2, }, // EVEN
/* odd */ { 1, 0, 1, 1, }, // ODD
@ -485,18 +711,21 @@ const int addLevel[][4] =
void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror)
{
bool rtl = seg->dir() & 1;
int level = rtl;
Slot *slast = 0;
for ( ; s; s = s->next())
{
int cls = s->getBidiClass();
if (cls == BN)
continue;
else if (cls == AN)
cls = AL;
int cls = BaseClass(s);
s->prev(slast); // restitch the prev() side of the doubly linked list
slast = s;
if (cls == AN)
cls = AL; // use AL value as the index for AN, no property change
if (cls < 5 && cls > 0)
{
int level = s->getBidiLevel();
level = s->getBidiLevel();
level += addLevel[level & 1][cls - 1];
s->setBidiLevel(level);
}
if (aMirror)
{
int hasChar = seg->glyphAttr(s->gid(), aMirror + 1);
@ -509,21 +738,24 @@ void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror)
}
}
}
}
void resolveWhitespace(int baseLevel, Segment *seg, uint8 aBidi, Slot *s)
void resolveWhitespace(int baseLevel, Slot *s)
{
for ( ; s; s = s->prev())
{
int cls = seg->glyphAttr(s->gid(), aBidi);
if (cls == WS)
int8 cls = s->getBidiClass();
if (cls == WS || cls & WSflag)
s->setBidiLevel(baseLevel);
else
else if (cls != BN)
break;
}
}
/*
Stitch two spans together to make another span (with ends tied together).
If the level is odd then swap the order of the two spans
*/
inline
Slot * join(int level, Slot * a, Slot * b)
{
@ -535,14 +767,19 @@ Slot * join(int level, Slot * a, Slot * b)
return a;
}
/*
Given the first slot in a run of slots with the same bidi level, turn the run
into it's own little doubly linked list ring (a span) with the two ends joined together.
If the run is rtl then reverse its direction.
Returns the first slot after the span
*/
Slot * span(Slot * & cs, const bool rtl)
{
Slot * r = cs, * re = cs; cs = cs->next();
if (rtl)
{
Slot * t = r->next(); r->next(r->prev()); r->prev(t);
for (int l = r->getBidiLevel(); cs && l == cs->getBidiLevel(); cs = cs->prev())
for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->prev())
{
re = cs;
t = cs->next(); cs->next(cs->prev()); cs->prev(t);
@ -553,7 +790,7 @@ Slot * span(Slot * & cs, const bool rtl)
}
else
{
for (int l = r->getBidiLevel(); cs && l == cs->getBidiLevel(); cs = cs->next())
for (int l = r->getBidiLevel(); cs && (l == cs->getBidiLevel() || cs->getBidiClass() == BN); cs = cs->next())
re = cs;
r->prev(re);
re->next(r);
@ -562,17 +799,25 @@ Slot * span(Slot * & cs, const bool rtl)
return r;
}
inline int getlevel(const Slot *cs, const int level)
{
while (cs && cs->getBidiClass() == BN)
{ cs = cs->next(); }
if (cs)
return cs->getBidiLevel();
else
return level;
}
Slot *resolveOrder(Slot * & cs, const bool reordered, const int level)
{
Slot * r = 0;
int ls;
while (cs && level <= (ls = cs->getBidiLevel() - reordered))
while (cs && level <= (ls = getlevel(cs, level) - reordered))
{
r = join(level, r, level >= ls
? span(cs, level & 1)
: resolveOrder(cs, reordered, level+1));
r = join(level, r, level < ls
? resolveOrder(/* updates */cs, reordered, level+1) // find span of heighest level
: span(/* updates */cs, level & 1));
}
return r;
}

View File

@ -104,15 +104,25 @@ 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"
LINK_FLAGS "-nodefaultlibs"
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
LINKER_LANGUAGE C)
if (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
target_link_libraries(graphite2 kernel32 msvcr90 mingw32 gcc user32)
else (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
if (GRAPHITE2_ASAN)
target_link_libraries(graphite2 c gcc_s)
else (GRAPHITE2_ASAN)
target_link_libraries(graphite2 c gcc)
endif (GRAPHITE2_ASAN)
include(Graphite)
nolib_test(stdc++ $<TARGET_SONAME_FILE:graphite2>)
endif (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")

View File

@ -103,8 +103,8 @@ bool CachedFace::runGraphite(Segment *seg, const Silf *pSilf) const
pSilf->runGraphite(seg, pSilf->substitutionPass(), pSilf->numPasses());
if (length < eMaxSpliceSize)
{
seg->associateChars();
entry = segCache->cache(m_cacheStore, cmapGlyphs, length, seg, subSegStart);
seg->associateChars(subSegStart, length);
segCache->cache(m_cacheStore, cmapGlyphs, length, seg, subSegStart);
}
seg->removeScope(scopeState);
}

View File

@ -215,7 +215,10 @@ Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte
_data = static_cast<byte *>(realloc(_data, _data_size*sizeof(byte)));
if (!_code)
{
failure(alloc_failed);
return;
}
// Make this RET_ZERO, we should never reach this but just in case ...
_code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];

View File

@ -36,7 +36,7 @@ of the License or (at your option) any later version.
#include "inc/SegCacheStore.h"
#include "inc/Segment.h"
#include "inc/NameTable.h"
#include "inc/Error.h"
using namespace graphite2;
@ -47,6 +47,7 @@ Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
m_cmap(NULL),
m_pNames(NULL),
m_logger(NULL),
m_error(0), m_errcntxt(0),
m_silfs(NULL),
m_numSilf(0),
m_ascent(0),
@ -78,20 +79,24 @@ float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid)
bool Face::readGlyphs(uint32 faceOptions)
{
Error e;
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _glyph_cat(tele.glyph);
#endif
error_context(EC_READGLYPHS);
if (faceOptions & gr_face_cacheCmap)
m_cmap = new CachedCmap(*this);
else
m_cmap = new DirectCmap(*this);
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
if (!m_pGlyphFaceCache
|| m_pGlyphFaceCache->numGlyphs() == 0
|| m_pGlyphFaceCache->unitsPerEm() == 0
|| !m_cmap || !*m_cmap)
return false;
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM)
|| e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
{
return error(e);
}
if (faceOptions & gr_face_preloadGlyphs)
nameTable(); // preload the name table along with the glyphs.
@ -104,11 +109,13 @@ bool Face::readGraphite(const Table & silf)
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _silf_cat(tele.silf);
#endif
Error e;
error_context(EC_READSILF);
const byte * p = silf;
if (!p) return false;
if (e.test(!p, E_NOSILF)) return error(e);
const uint32 version = be::read<uint32>(p);
if (version < 0x00020000) return false;
if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
if (version >= 0x00030000)
be::skip<uint32>(p); // compilerVersion
m_numSilf = be::read<uint16>(p);
@ -118,10 +125,11 @@ bool Face::readGraphite(const Table & silf)
m_silfs = new Silf[m_numSilf];
for (int i = 0; i < m_numSilf; i++)
{
error_context(EC_ASILF + (i << 8));
const uint32 offset = be::read<uint32>(p),
next = i == m_numSilf - 1 ? silf.size() : be::peek<uint32>(p);
if (next > silf.size() || offset >= next)
return false;
if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
return error(e);
if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
return false;
@ -150,9 +158,9 @@ bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
}
#endif
bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass());
bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true);
if (res)
res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses());
res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
#if !defined GRAPHITE2_NTRACING
if (dbgout)

View File

@ -122,6 +122,7 @@ bool FeatureMap::readFeats(const Face & face)
m_feats = new FeatureRef [m_numFeats];
uint16 * const defVals = gralloc<uint16>(m_numFeats);
if (!defVals || !m_feats) return false;
unsigned short bits = 0; //to cause overflow on first Feature
for (int i = 0, ie = m_numFeats; i != ie; i++)
@ -145,6 +146,11 @@ bool FeatureMap::readFeats(const Face & face)
if (num_settings != 0)
{
uiSet = gralloc<FeatureSetting>(num_settings);
if (!uiSet)
{
free(defVals);
return false;
}
maxVal = readFeatureSettings(feat_setts, uiSet, num_settings);
defVals[i] = uiSet[0].value();
}
@ -161,6 +167,11 @@ bool FeatureMap::readFeats(const Face & face)
}
m_defaultFeatures = new Features(bits/(sizeof(uint32)*8) + 1, *this);
m_pNamedFeats = new NameAndFeatureRef[m_numFeats];
if (!m_defaultFeatures || !m_pNamedFeats)
{
free(defVals);
return false;
}
for (int i = 0; i < m_numFeats; ++i)
{
m_feats[i].applyValToFeature(defVals[i], *m_defaultFeatures);
@ -204,6 +215,7 @@ bool SillMap::readSill(const Face & face)
uint16 offset = be::read<uint16>(p);
if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false;
Features* feats = new Features(*m_FeatureMap.m_defaultFeatures);
if (!feats) return false;
const byte *pLSet = sill + offset;
// Apply langauge specific settings

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -37,7 +37,7 @@ Font::Font(float ppm, const Face & f, const void * appFontHandle, const gr_font_
m_hinted(appFontHandle && ops && (ops->glyph_advance_x || ops->glyph_advance_y))
{
memset(&m_ops, 0, sizeof m_ops);
if (m_hinted)
if (m_hinted && ops)
memcpy(&m_ops, ops, min(sizeof m_ops, ops->size));
else
m_ops.glyph_advance_x = &Face::default_glyph_advance;

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
@ -36,45 +36,45 @@ using namespace graphite2;
namespace
{
class glat_iterator : public std::iterator<std::input_iterator_tag, std::pair<sparse::key_type, sparse::mapped_type> >
// Iterator over version 1 or 2 glat entries which consist of a series of
// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+
// v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN |
// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+
// variable length structures.
template<typename W>
class _glat_iterator : public std::iterator<std::input_iterator_tag, std::pair<sparse::key_type, sparse::mapped_type> >
{
unsigned short key() const { return be::peek<W>(_e) + _n; }
unsigned int run() const { return be::peek<W>(_e+sizeof(W)); }
void advance_entry() { _n = 0; _e = _v; be::skip<W>(_v,2); }
public:
glat_iterator(const void * glat=0) : _p(reinterpret_cast<const byte *>(glat)), _n(0) {}
_glat_iterator(const void * glat=0) : _e(reinterpret_cast<const byte *>(glat)), _v(_e+2*sizeof(W)), _n(0) {}
glat_iterator & operator ++ () { ++_v.first; --_n; _p += sizeof(uint16); if (_n == -1) { _p -= 2; _v.first = *_p++; _n = *_p++; } return *this; }
glat_iterator operator ++ (int) { glat_iterator tmp(*this); operator++(); return tmp; }
_glat_iterator<W> & operator ++ () {
++_n; be::skip<uint16>(_v);
if (_n == run()) advance_entry();
return *this;
}
_glat_iterator<W> operator ++ (int) { _glat_iterator<W> tmp(*this); operator++(); return tmp; }
bool operator == (const glat_iterator & rhs) { return _p >= rhs._p || _p + _n*sizeof(uint16) > rhs._p; }
bool operator != (const glat_iterator & rhs) { return !operator==(rhs); }
// 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 !operator==(rhs); }
value_type operator * () const {
if (_n==0) { _v.first = *_p++; _n = *_p++; }
_v.second = be::peek<uint16>(_p);
return _v;
return value_type(key(), be::peek<uint16>(_v));
}
const value_type * operator ->() const { operator * (); return &_v; }
protected:
mutable const byte * _p;
mutable value_type _v;
mutable int _n;
const byte * _e, * _v;
ptrdiff_t _n;
};
class glat2_iterator : public glat_iterator
{
public:
glat2_iterator(const void * glat) : glat_iterator(glat) {}
glat2_iterator & operator ++ () { ++_v.first; --_n; _p += sizeof(uint16); if (_n == -1) { _p -= sizeof(uint16)*2; _v.first = be::read<uint16>(_p); _n = be::read<uint16>(_p); } return *this; }
glat2_iterator operator ++ (int) { glat2_iterator tmp(*this); operator++(); return tmp; }
value_type operator * () const {
if (_n==0) { _v.first = be::read<uint16>(_p); _n = be::read<uint16>(_p); }
_v.second = be::peek<uint16>(_p);
return _v;
}
const value_type * operator ->() const { operator * (); return &_v; }
};
typedef _glat_iterator<uint8> glat_iterator;
typedef _glat_iterator<uint16> glat2_iterator;
}
@ -203,7 +203,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
_num_glyphs_graphics = TtfUtil::GlyphCount(maxp);
// This will fail if the number of glyphs is wildly out of range.
if (TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1))
if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-1))
{
_head = Face::Table();
return;
@ -244,7 +244,7 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
inline
GlyphCache::Loader::operator bool () const throw()
{
return _head && _hhea && _hmtx && _glyf && _loca;
return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca));
}
inline
@ -272,17 +272,21 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa
if (glyphid < _num_glyphs_graphics)
{
int nLsb, xMin, yMin, xMax, yMax;
int nLsb;
unsigned int nAdvWid;
if (_glyf)
{
int xMin, yMin, xMax, yMax;
size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head);
void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());
if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))
advance = Position(static_cast<float>(nAdvWid), 0);
if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))
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);
}
if (glyphid < _num_glyphs_attributes)
{
@ -329,7 +333,7 @@ const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFa
new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
}
if (glyph.attrs().capacity() > _num_attrs)
if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)
return 0;
}

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
@ -117,6 +117,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
}
JustifyTotal *stats = new JustifyTotal[numLevels];
if (!stats) return -1.0;
for (s = pFirst; s != end; s = s->nextSibling())
{
float w = s->origin().x / scale + s->advance() - base;
@ -179,6 +180,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
{
m_first = pSlot = addLineEnd(pSlot);
m_last = pLast = addLineEnd(end);
if (!m_first || !m_last) return -1.0;
}
else
{
@ -226,6 +228,7 @@ float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUS
Slot *Segment::addLineEnd(Slot *nSlot)
{
Slot *eSlot = newSlot();
if (!eSlot) return NULL;
const uint16 gid = silf()->endLineGlyphid();
const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
eSlot->setGlyph(this, gid, theGlyph);

View File

@ -145,6 +145,12 @@ void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint
}
utf16Length >>= 1; // in utf16 units
utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length);
if (!utf16Name)
{
languageId = 0;
length = 0;
return NULL;
}
const uint8* pName = m_nameData + offset;
for (size_t i = 0; i < utf16Length; i++)
{
@ -155,11 +161,19 @@ void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint
case gr_utf8:
{
utf8::codeunit_t* uniBuffer = gralloc<utf8::codeunit_t>(3 * utf16Length + 1);
if (!uniBuffer)
{
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
utf8::iterator d = uniBuffer;
for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
*d = *s;
length = d - uniBuffer;
uniBuffer[length] = 0;
free(utf16Name);
return uniBuffer;
}
case gr_utf16:
@ -168,14 +182,24 @@ void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint
case gr_utf32:
{
utf32::codeunit_t * uniBuffer = gralloc<utf32::codeunit_t>(utf16Length + 1);
if (!uniBuffer)
{
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
utf32::iterator d = uniBuffer;
for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
*d = *s;
length = d - uniBuffer;
uniBuffer[length] = 0;
free(utf16Name);
return uniBuffer;
}
}
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}

View File

@ -34,6 +34,7 @@ of the License or (at your option) any later version.
#include "inc/Segment.h"
#include "inc/Code.h"
#include "inc/Rule.h"
#include "inc/Error.h"
using namespace graphite2;
using vm::Machine;
@ -72,13 +73,13 @@ Pass::~Pass()
delete [] m_rules;
}
bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED const Face & face)
bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base, GR_MAYBE_UNUSED Face & face, Error &e)
{
const byte * p = pass_start,
* const pass_end = p + pass_length;
size_t numRanges;
if (pass_length < 40) return false;
if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
// Read in basic values
m_flags = be::read<byte>(p);
m_iMaxLoop = be::read<byte>(p);
@ -97,14 +98,14 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
assert(p - pass_start == 40);
// Perform some sanity checks.
if ( m_numTransition > m_numStates
|| m_numSuccess > m_numStates
|| m_numSuccess + m_numTransition < m_numStates
|| numRanges == 0)
return false;
if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
|| e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
|| e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
|| e.test(numRanges == 0, E_NORANGES))
return face.error(e);
m_successStart = m_numStates - m_numSuccess;
if (p + numRanges * 6 - 4 > pass_end) return false;
if (e.test(p + numRanges * 6 - 4 > pass_end, E_BADPASSLENGTH)) return face.error(e);
m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
// Calculate the start of various arrays.
const byte * const ranges = p;
@ -113,17 +114,17 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
be::skip<uint16>(p, m_numSuccess + 1);
// More sanity checks
if (reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
|| p > pass_end)
return false;
if (e.test(reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
|| p > pass_end, E_BADRULEMAPLEN))
return face.error(e);
const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
const byte * const rule_map = p;
be::skip<uint16>(p, numEntries);
if (p + 2*sizeof(uint8) > pass_end) return false;
if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e);
m_minPreCtxt = be::read<uint8>(p);
m_maxPreCtxt = be::read<uint8>(p);
if (m_minPreCtxt > m_maxPreCtxt) return false;
if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e);
const byte * const start_states = p;
be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1);
const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p);
@ -132,7 +133,7 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
be::skip<byte>(p, m_numRules);
be::skip<byte>(p); // skip reserved byte
if (p + sizeof(uint16) > pass_end) return false;
if (e.test(p + sizeof(uint16) > pass_end, E_BADCTXTLENS)) return face.error(e);
const size_t pass_constraint_len = be::read<uint16>(p);
const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
be::skip<uint16>(p, m_numRules + 1);
@ -141,31 +142,35 @@ bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t su
const byte * const states = p;
be::skip<int16>(p, m_numTransition*m_numColumns);
be::skip<byte>(p); // skip reserved byte
if (p != pcCode || p >= pass_end) return false;
if (e.test(p != pcCode, E_BADPASSCCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
be::skip<byte>(p, pass_constraint_len);
if (p != rcCode || p >= pass_end
|| size_t(rcCode - pcCode) != pass_constraint_len) return false;
if (e.test(p != rcCode, E_BADRULECCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)
|| e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
if (p != aCode || p >= pass_end) return false;
if (e.test(p != aCode, E_BADACTIONCODEPTR) || e.test(p >= pass_end, E_BADPASSLENGTH)) return face.error(e);
be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
// We should be at the end or within the pass
if (p > pass_end) return false;
if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e);
// Load the pass constraint if there is one.
if (pass_constraint_len)
{
face.error_context(face.error_context() + 1);
m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len,
precontext[0], be::peek<uint16>(sort_keys), *m_silf, face);
if (!m_cPConstraint) return false;
if (e.test(!m_cPConstraint, E_OUTOFMEM)
|| e.test(m_cPConstraint.status(), m_cPConstraint.status() + E_CODEFAILURE))
return face.error(e);
face.error_context(face.error_context() - 1);
}
if (!readRanges(ranges, numRanges)) return false;
if (!readRanges(ranges, numRanges, e)) return face.error(e);
if (!readRules(rule_map, numEntries, precontext, sort_keys,
o_constraint, rcCode, o_actions, aCode, face)) return false;
o_constraint, rcCode, o_actions, aCode, face, e)) return false;
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _states_cat(face.tele.states);
#endif
return readStates(start_states, states, o_rule_map, face);
return readStates(start_states, states, o_rule_map, face, e);
}
@ -173,12 +178,12 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
const byte *precontext, const uint16 * sort_key,
const uint16 * o_constraint, const byte *rc_data,
const uint16 * o_action, const byte * ac_data,
const Face & face)
Face & face, Error &e)
{
const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
if (!(m_rules = new Rule [m_numRules])) return false;
if (e.test(!(m_rules = new Rule [m_numRules]), E_OUTOFMEM)) return face.error(e);
precontext += m_numRules;
sort_key += m_numRules;
o_constraint += m_numRules;
@ -191,6 +196,7 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
Rule * r = m_rules + m_numRules - 1;
for (size_t n = m_numRules; n; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
{
face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + ((n - 1) << 24));
r->preContext = *--precontext;
r->sort = be::peek<uint16>(--sort_key);
#ifndef NDEBUG
@ -208,19 +214,21 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
r->action = new vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face);
r->constraint = new vm::Machine::Code(true, rc_begin, rc_end, r->preContext, r->sort, *m_silf, face);
if (!r->action || !r->constraint
|| r->action->status() != Code::loaded
|| r->constraint->status() != Code::loaded
|| !r->constraint->immutable())
return false;
if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
|| e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
|| e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
|| e.test(!r->constraint->immutable(), E_MUTABLECCODE))
return face.error(e);
}
// Load the rule entries map
face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
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)
{
const ptrdiff_t rn = be::read<uint16>(rule_map);
if (rn >= m_numRules) return false;
if (e.test(rn >= m_numRules, E_BADRULENUM)) return face.error(e);
re->rule = m_rules + rn;
}
@ -230,7 +238,7 @@ bool Pass::readRules(const byte * rule_map, const size_t num_entries,
static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 :
(*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); }
bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED const Face & face)
bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _states_cat(face.tele.starts);
@ -245,13 +253,17 @@ bool Pass::readStates(const byte * starts, const byte *states, const byte * o_ru
#endif
m_transitions = gralloc<uint16>(m_numTransition * m_numColumns);
if (!m_startStates || !m_states || !m_transitions) return false;
if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e);
// load start states
for (uint16 * s = m_startStates,
* const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
{
*s = be::read<uint16>(starts);
if (*s >= m_numStates) return false; // true;
if (e.test(*s >= m_numStates, E_BADSTATE))
{
face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + ((s - m_startStates) << 24));
return face.error(e); // true;
}
}
// load state transition table.
@ -259,7 +271,11 @@ bool Pass::readStates(const byte * starts, const byte *states, const byte * o_ru
* const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t)
{
*t = be::read<uint16>(states);
if (*t >= m_numStates) return false;
if (e.test(*t >= m_numStates, E_BADSTATE))
{
face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + (((t - m_transitions) / m_numColumns) << 24));
return face.error(e);
}
}
State * s = m_states,
@ -270,8 +286,11 @@ bool Pass::readStates(const byte * starts, const byte *states, const byte * o_ru
RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map),
* const end = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map);
if (begin >= rule_map_end || end > rule_map_end || begin > end)
return false;
if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING))
{
face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + (n << 24));
return face.error(e);
}
s->rules = begin;
s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
begin + FiniteStateMachine::MAX_RULES;
@ -281,9 +300,10 @@ bool Pass::readStates(const byte * starts, const byte *states, const byte * o_ru
return true;
}
bool Pass::readRanges(const byte * ranges, size_t num_ranges)
bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
{
m_cols = gralloc<uint16>(m_numGlyphs);
if (e.test(!m_cols, E_OUTOFMEM)) return false;
memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16));
for (size_t n = num_ranges; n; --n)
{
@ -291,14 +311,14 @@ bool Pass::readRanges(const byte * ranges, size_t num_ranges)
* ci_end = m_cols + be::read<uint16>(ranges) + 1,
col = be::read<uint16>(ranges);
if (ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns)
if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE))
return false;
// A glyph must only belong to one column at a time
while (ci != ci_end && *ci == 0xffff)
*ci++ = col;
if (ci != ci_end)
if (e.test(ci != ci_end, E_BADRANGE))
return false;
}
return true;

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -41,12 +41,13 @@ SegCacheEntry::SegCacheEntry(const uint16* cmapGlyphs, size_t length, Segment *
m_attr(NULL), m_justs(NULL),
m_accessCount(0), m_lastAccess(cacheTime)
{
if (m_unicode)
for (uint16 i = 0; i < length; i++)
{
m_unicode[i] = cmapGlyphs[i];
}
const size_t glyphCount = seg->slotCount(),
sizeof_sjust = SlotJustify::size_of(seg->silf()->numJustLevels());
if (!glyphCount) return;
size_t num_justs = 0,
justs_pos = 0;
if (seg->hasJustification())
@ -61,6 +62,7 @@ SegCacheEntry::SegCacheEntry(const uint16* cmapGlyphs, size_t length, Segment *
const Slot * slot = seg->first();
m_glyph = new Slot[glyphCount];
m_attr = gralloc<int16>(glyphCount * seg->numAttrs());
if (!m_glyph || (!m_attr && seg->numAttrs())) return;
m_glyphLength = glyphCount;
Slot * slotCopy = m_glyph;
m_glyph->prev(NULL);
@ -70,7 +72,7 @@ SegCacheEntry::SegCacheEntry(const uint16* cmapGlyphs, size_t length, Segment *
{
slotCopy->userAttrs(m_attr + pos * seg->numAttrs());
slotCopy->m_justs = m_justs ? reinterpret_cast<SlotJustify *>(m_justs + justs_pos++ * sizeof_sjust) : 0;
slotCopy->set(*slot, -static_cast<int32>(charOffset), seg->numAttrs(), seg->silf()->numJustLevels());
slotCopy->set(*slot, -static_cast<int32>(charOffset), seg->numAttrs(), seg->silf()->numJustLevels(), length);
slotCopy->index(pos);
if (slot->firstChild())
slotCopy->m_child = m_glyph + slot->firstChild()->index();

View File

@ -36,6 +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 "graphite2/Segment.h"
@ -106,7 +107,7 @@ void Segment::removeScope(SegmentScopeState & state)
m_defaultOriginal = 0;
}
#if 0
void Segment::append(const Segment &other)
{
Rect bbox = other.m_bbox + m_advance;
@ -131,12 +132,14 @@ void Segment::append(const Segment &other)
m_bbox = m_bbox.widen(bbox);
m_passBits &= other.passBits();
}
#endif
#endif // GRAPHITE2_NSEGCACHE
void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
{
Slot *aSlot = newSlot();
if (!aSlot) return;
m_charinfo[id].init(cid);
m_charinfo[id].feats(iFeats);
m_charinfo[id].base(coffset);
@ -167,14 +170,15 @@ Slot *Segment::newSlot()
#endif
Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
int16 *newAttrs = grzeroalloc<int16>(numUser * m_bufSize);
newSlots[0].userAttrs(newAttrs);
for (size_t i = 1; i < m_bufSize - 1; i++)
if (!newSlots || !newAttrs) return NULL;
for (size_t i = 0; i < m_bufSize; i++)
{
newSlots[i].next(newSlots + i + 1);
newSlots[i].userAttrs(newAttrs + i * numUser);
newSlots[i].setBidiClass(-1);
}
newSlots[m_bufSize - 1].userAttrs(newAttrs + (m_bufSize - 1) * numUser);
newSlots[m_bufSize - 1].next(NULL);
newSlots[0].next(NULL);
m_slots.push_back(newSlots);
m_userAttrs.push_back(newAttrs);
m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
@ -219,6 +223,7 @@ SlotJustify *Segment::newJustify()
{
const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
if (!justs) return NULL;
for (int i = m_bufSize - 2; i >= 0; --i)
{
SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
@ -248,6 +253,7 @@ void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
Slot * endSlot, const Slot * srcSlot,
const size_t numGlyphs)
{
size_t numChars = length;
extendLength(numGlyphs - length);
// remove any extra
if (numGlyphs < length)
@ -268,6 +274,7 @@ void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
while (numGlyphs > length)
{
Slot * extra = newSlot();
if (!extra) return;
extra->prev(endSlot);
extra->next(endSlot->next());
endSlot->next(extra);
@ -282,16 +289,16 @@ void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
endSlot = endSlot->next();
assert(numGlyphs == length);
assert(offset + numChars <= m_numCharinfo);
Slot * indexmap[eMaxSpliceSize*3];
assert(numGlyphs < sizeof indexmap/sizeof *indexmap);
Slot * slot = startSlot;
for (uint16 i=0; i < numGlyphs; slot = slot->next(), ++i)
indexmap[i] = slot;
slot = startSlot;
for (slot = startSlot; slot != endSlot; slot = slot->next(), srcSlot = srcSlot->next())
{
slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels());
slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars);
if (srcSlot->attachedTo()) slot->attachTo(indexmap[srcSlot->attachedTo()->index()]);
if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()];
if (srcSlot->firstChild()) slot->m_child = indexmap[srcSlot->firstChild()->index()];
@ -331,8 +338,8 @@ void Segment::linkClusters(Slot *s, Slot * end)
Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
{
Position currpos(0., 0.);
Rect bbox;
float clusterMin = 0.;
Rect bbox;
if (!iStart) iStart = m_first;
if (!iEnd) iEnd = m_last;
@ -357,21 +364,40 @@ Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
}
void Segment::associateChars()
void Segment::associateChars(int offset, int numChars)
{
int i = 0;
int i = 0, j = 0;
CharInfo *c, *cend;
for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
{
c->before(-1);
c->after(-1);
}
for (Slot * s = m_first; s; s->index(i++), s = s->next())
{
int j = s->before();
j = s->before();
if (j < 0) continue;
for (const int after = s->after(); j <= after; ++j)
{
CharInfo & c = *charinfo(j);
if (c.before() == -1 || i < c.before()) c.before(i);
if (c.after() < i) c.after(i);
c = charinfo(j);
if (c->before() == -1 || i < c->before()) c->before(i);
if (c->after() < i) c->after(i);
}
}
for (Slot *s = m_first; s; s = s->next())
{
int a;
for (a = s->after() + 1; a < offset + numChars && charinfo(a)->after() < 0; ++a)
{ charinfo(a)->after(s->index()); }
--a;
s->after(a);
for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
{ charinfo(a)->before(s->index()); }
++a;
s->before(a);
}
}
@ -392,17 +418,20 @@ inline void process_utf_data(Segment & seg, const Face & face, const int fid, ut
}
void Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
{
assert(face);
assert(pFeats);
if (!m_charinfo) return false;
// utf iterator is self recovering so we don't care about the error state of the iterator.
switch (enc)
{
case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
}
return true;
}
void Segment::prepare_pos(const Font * /*font*/)
@ -410,11 +439,9 @@ void Segment::prepare_pos(const Font * /*font*/)
// copy key changeable metrics into slot (if any);
}
Slot *resolveExplicit(int level, int dir, Slot *s, int nNest = 0);
void resolveWeak(int baseLevel, Slot *s);
void resolveNeutrals(int baseLevel, Slot *s);
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, Segment *seg, uint8 aBidi, Slot *s);
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)
@ -425,27 +452,36 @@ void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
Slot *s;
int baseLevel = paradir ? 1 : 0;
unsigned int bmask = 0;
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 <= 16) * bAttr);
s->setBidiClass((bAttr <= 22) * bAttr);
}
bmask |= (1 << s->getBidiClass());
s->setBidiLevel(baseLevel);
if (glyphAttr(s->gid(), aMirror) && s->getBidiClass() == 21)
++ssize;
}
if (bmask & (paradir ? 0x92 : 0x9C))
BracketPairStack bstack(ssize);
if (bmask & (paradir ? 0x2E7892 : 0x2E789C))
{
if (bmask & 0xF800)
resolveExplicit(baseLevel, 0, first(), 0);
if (bmask & 0x10178)
resolveWeak(baseLevel, first());
if (bmask & 0x361)
resolveNeutrals(baseLevel, first());
// O(8N) algorithm, with no working data beyond what is needed for processParens
int nextLevel = paradir;
int e, i, c;
process_bidi(first(), baseLevel, paradir, nextLevel, 0, 0, c = 0, i = 0, e = 0, 1, this, aMirror, bstack);
resolveImplicit(first(), this, aMirror);
resolveWhitespace(baseLevel, this, aBidi, last());
resolveWhitespace(baseLevel, last());
s = resolveOrder(s = first(), baseLevel != 0);
if (s)
{
first(s); last(s->prev());
s->prev()->next(0); s->prev(0);
}
}
else if (!(dir() & 4) && baseLevel && aMirror)
{
for (s = first(); s; s = s->next())

View File

@ -31,6 +31,7 @@ of the License or (at your option) any later version.
#include "inc/Silf.h"
#include "inc/Segment.h"
#include "inc/Rule.h"
#include "inc/Error.h"
using namespace graphite2;
@ -86,18 +87,19 @@ void Silf::releaseBuffers() throw()
}
bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, const Face& face, uint32 version)
bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
{
const byte * p = silf_start,
* const silf_end = p + lSilf;
Error e;
if (version >= 0x00030000)
{
if (lSilf < 28) { releaseBuffers(); return false; }
if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
be::skip<int32>(p); // ruleVersion
be::skip<uint16>(p,2); // passOffset & pseudosOffset
}
else if (lSilf < 20) { releaseBuffers(); return false; }
else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
const uint16 maxGlyph = be::read<uint16>(p);
m_silfinfo.extra_ascent = be::read<uint16>(p);
m_silfinfo.extra_descent = be::read<uint16>(p);
@ -116,45 +118,59 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, const Face&
// Read Justification levels.
m_numJusts = be::read<uint8>(p);
if (maxGlyph >= face.glyphs().numGlyphs()
|| p + m_numJusts * 8 >= silf_end) { releaseBuffers(); return false; }
if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
|| e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
{
releaseBuffers(); return face.error(e);
}
if (m_numJusts)
{
m_justs = gralloc<Justinfo>(m_numJusts);
if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
for (uint8 i = 0; i < m_numJusts; i++)
{
::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
be::skip<byte>(p,8);
}
}
if (p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end) { releaseBuffers(); return false; }
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,5); // direction and 4 reserved bytes
be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
be::skip<byte>(p); // reserved
if (p >= silf_end) { releaseBuffers(); return false; }
if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
be::skip<uint32>(p, be::read<uint8>(p)); // don't use scriptTag array.
if (p + sizeof(uint16) + sizeof(uint32) >= silf_end) { releaseBuffers(); return false; }
if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
m_gEndLine = be::read<uint16>(p); // lbGID
const byte * o_passes = p,
* const passes_start = silf_start + be::read<uint32>(p);
const size_t num_attrs = face.glyphs().numAttrs();
if (m_aPseudo >= num_attrs
|| m_aBreak >= num_attrs
|| m_aBidi >= num_attrs
|| m_aMirror>= num_attrs
|| m_numPasses > 128 || passes_start >= silf_end
|| m_pPass < m_sPass || m_pPass > m_numPasses || m_sPass > m_numPasses
|| m_jPass < m_pPass || m_jPass > m_numPasses
|| (m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses))
|| m_aLig > 127) { releaseBuffers(); return false; }
if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO)
|| e.test(m_aBreak >= num_attrs, E_BADABREAK)
|| e.test(m_aBidi >= num_attrs, E_BADABIDI)
|| e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
|| e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= silf_end, E_BADPASSESSTART)
|| e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
|| e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
|| e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
|| e.test(m_aLig > 127, E_BADALIG))
{
releaseBuffers();
return face.error(e);
}
be::skip<uint32>(p, m_numPasses);
if (p + sizeof(uint16) >= passes_start) { releaseBuffers(); return false; }
if (e.test(p + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
m_numPseudo = be::read<uint16>(p);
be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
if (p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start) {
releaseBuffers(); return false;
if (e.test(p + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO))
{
releaseBuffers(); return face.error(e);
}
m_pseudos = new Pseudo[m_numPseudo];
for (int i = 0; i < m_numPseudo; i++)
@ -163,20 +179,21 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, const Face&
m_pseudos[i].gid = be::read<uint16>(p);
}
const size_t clen = readClassMap(p, passes_start - p, version);
if (clen == ERROROFFSET || p + clen > passes_start) { releaseBuffers(); return false; }
const size_t clen = readClassMap(p, passes_start - p, version, e);
if (e || e.test(p + clen > passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
m_passes = new Pass[m_numPasses];
for (size_t i = 0; i < m_numPasses; ++i)
{
const byte * const pass_start = silf_start + be::read<uint32>(o_passes),
* const pass_end = silf_start + be::peek<uint32>(o_passes);
if (pass_start > pass_end || pass_end > silf_end) {
releaseBuffers(); return false;
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)) {
releaseBuffers(); return face.error(e);
}
m_passes[i].init(this);
if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face))
if (!m_passes[i].readPass(pass_start, pass_end - pass_start, pass_start - silf_start, face, e))
{
releaseBuffers();
return false;
@ -192,54 +209,56 @@ bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, const Face&
return true;
}
template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len)
template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
{
const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
const size_t max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16);
// Check that the last+1 offset is less than or equal to the class map length.
if (be::peek<T>(p) != cls_off || max_off > (data_len - cls_off)/sizeof(uint16))
if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
|| e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET))
return ERROROFFSET;
// Read in all the offsets.
m_classOffsets = gralloc<uint32>(m_nClass+1);
if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
{
*o = (be::read<T>(p) - cls_off)/sizeof(uint16);
if (*o > max_off)
if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
return ERROROFFSET;
}
return max_off;
}
size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version)
size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e)
{
if (data_len < sizeof(uint16)*2) return ERROROFFSET;
if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET;
m_nClass = be::read<uint16>(p);
m_nLinear = be::read<uint16>(p);
// Check that numLinear < numClass,
// that there is at least enough data for numClasses offsets.
if (m_nLinear > m_nClass
|| (m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16))> (data_len - 4))
if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
|| e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG))
return ERROROFFSET;
uint32 max_off;
if (version >= 0x00040000)
max_off = readClassOffsets<uint32>(p, data_len);
max_off = readClassOffsets<uint32>(p, data_len, e);
else
max_off = readClassOffsets<uint16>(p, data_len);
max_off = readClassOffsets<uint16>(p, data_len, e);
if (max_off == ERROROFFSET) 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 (o[0] > o[1])
if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
return ERROROFFSET;
// Fortunately the class data is all uint16s so we can decode these now
m_classData = gralloc<uint16>(max_off);
if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
*d = be::read<uint16>(p);
@ -247,10 +266,10 @@ size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version)
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 (*o > max_off - 4 // LookupClass doesn't stretch over max_off
|| lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
if (e.test(*o > max_off - 4, 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]) // rangeShift: numIDs - searchRange
|| lookup[3] != lookup[0] - lookup[1], E_BADCLASSLOOKUPINFO)) // rangeShift: numIDs - searchRange
return ERROROFFSET;
}
@ -309,28 +328,33 @@ uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
}
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass) const
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
{
assert(seg != 0);
SlotMap map(*seg);
FiniteStateMachine fsm(map, seg->getFace()->logger());
vm::Machine m(map);
unsigned int initSize = seg->slotCount();
uint8 lbidi = m_bPass;
#if !defined GRAPHITE2_NTRACING
json * const dbgout = seg->getFace()->logger();
#endif
if (lastPass == 0)
{
if (firstPass == lastPass)
if (firstPass == lastPass && lbidi == 0xFF)
return true;
lastPass = m_numPasses;
}
if (firstPass <= lbidi && lastPass >= lbidi && dobidi)
lastPass++;
else
lbidi = 0xFF;
for (size_t i = firstPass; i < lastPass; ++i)
{
// bidi and mirroring
if (i == m_bPass)
if (i == lbidi)
{
#if !defined GRAPHITE2_NTRACING
if (dbgout)
@ -359,6 +383,10 @@ bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass) const
s->setGlyph(seg, g);
}
}
--i;
--lastPass;
lbidi = 0xFF;
continue;
}
#if !defined GRAPHITE2_NTRACING

View File

@ -37,22 +37,28 @@ Slot::Slot() :
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(-1, -1),
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(0), m_bidiLevel(0), m_justs(NULL)
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
{
}
// take care, this does not copy any of the GrSlot pointer fields
void Slot::set(const Slot & orig, int charOffset, size_t numUserAttr, size_t justLevels)
void Slot::set(const Slot & orig, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars)
{
// leave m_next and m_prev unchanged
m_glyphid = orig.m_glyphid;
m_realglyphid = orig.m_realglyphid;
m_original = orig.m_original + charOffset;
if (charOffset + int(orig.m_before) < 0)
m_before = 0;
else
m_before = orig.m_before + charOffset;
if (charOffset <= 0 && orig.m_after + charOffset >= numChars)
m_after = numChars - 1;
else
m_after = orig.m_after + charOffset;
m_parent = NULL;
m_child = NULL;
@ -67,14 +73,10 @@ void Slot::set(const Slot & orig, int charOffset, size_t numUserAttr, size_t jus
m_bidiCls = orig.m_bidiCls;
m_bidiLevel = orig.m_bidiLevel;
if (m_userAttr && orig.m_userAttr)
{
memcpy(m_userAttr, orig.m_userAttr, numUserAttr * sizeof(*m_userAttr));
}
if (m_justs && orig.m_justs)
{
memcpy(m_justs, orig.m_justs, SlotJustify::size_of(justLevels));
}
}
void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
{
@ -208,7 +210,9 @@ 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 : return seg->dir();
case gr_slatDir : if (m_bidiCls == -1)
const_cast<Slot *>(this)->setBidiClass(seg->glyphAttr(gid(), seg->silf()->aBidi()));
return m_bidiCls;
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);
@ -216,9 +220,10 @@ int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
case gr_slatShiftY : return int(m_shift.y);
case gr_slatMeasureSol: return -1; // err what's this?
case gr_slatMeasureEol: return -1;
case gr_slatJWidth: return m_just;
case gr_slatJWidth: return int(m_just);
case gr_slatUserDefn : return m_userAttr[subindex];
case gr_slatSegSplit : return seg->charinfo(m_original)->flags() & 3;
case gr_slatBidiLevel: return m_bidiLevel;
default : return 0;
}
}
@ -275,7 +280,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 : break; // read only
case gr_slatDir : m_bidiCls = value; break;
case gr_slatInsert :
markInsertBefore(value? true : false);
break;
@ -319,6 +324,7 @@ void Slot::setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value)
if (!m_justs)
{
SlotJustify *j = seg->newJustify();
if (!j) return;
j->LoadSlot(this, seg);
m_justs = j;
}

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
@ -30,9 +30,11 @@ of the License or (at your option) any later version.
using namespace graphite2;
sparse::chunk sparse::empty_chunk = {0,0};
sparse::~sparse() throw()
{
if (m_array.map == &empty_chunk) return;
free(m_array.values);
}

View File

@ -911,7 +911,6 @@ gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId,
{
pMid = &(pTable->end_code[rangeKey]);
chEnd = be::peek<uint16>(pMid);
n = rangeKey;
}
else
{

View File

@ -1,6 +1,6 @@
# GRAPHITE2 LICENSING
#
# Copyright 2010, SIL International
# Copyright 2011, SIL International
# All rights reserved.
#
# This library is free software; you can redistribute it and/or modify
@ -79,6 +79,7 @@ $(_NS)_PRIVATE_HEADERS = \
$($(_NS)_BASE)/src/inc/CmapCache.h \
$($(_NS)_BASE)/src/inc/Code.h \
$($(_NS)_BASE)/src/inc/Endian.h \
$($(_NS)_BASE)/src/inc/Error.h \
$($(_NS)_BASE)/src/inc/Face.h \
$($(_NS)_BASE)/src/inc/FeatureMap.h \
$($(_NS)_BASE)/src/inc/FeatureVal.h \

View File

@ -31,9 +31,14 @@ of the License or (at your option) any later version.
#include "inc/CachedFace.h"
#include "inc/CmapCache.h"
#include "inc/Silf.h"
#include "inc/json.h"
using namespace graphite2;
#if !defined GRAPHITE2_NTRACING
extern json *global_log;
#endif
namespace
{
bool load_face(Face & face, unsigned int options)
@ -49,8 +54,27 @@ namespace
if (!face.readGlyphs(options))
return false;
return silf ? face.readFeatures() && face.readGraphite(silf)
: options & gr_face_dumbRendering;
if (silf)
{
if (!face.readFeatures() || !face.readGraphite(silf))
{
#if !defined GRAPHITE2_NTRACING
if (global_log)
{
*global_log << json::object
<< "type" << "fontload"
<< "failure" << face.error()
<< "context" << face.error_context()
<< json::close;
}
#endif
return false;
}
else
return true;
}
else
return options & gr_face_dumbRendering;
}
}
@ -130,7 +154,7 @@ uint32 zeropad(const uint32 x)
gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 langname/*0 means clone default*/) //clones the features. if none for language, clones the default
{
assert(pFace);
zeropad(langname);
langname = zeropad(langname);
return static_cast<gr_feature_val *>(pFace->theSill().cloneFeatures(langname));
}
@ -138,7 +162,7 @@ gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 lang
const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, gr_uint32 featId) //When finished with the FeatureRef, call destroy_FeatureRef
{
assert(pFace);
zeropad(featId);
featId = zeropad(featId);
const FeatureRef* pRef = pFace->featureById(featId);
return static_cast<const gr_feature_ref*>(pRef);
}

View File

@ -31,6 +31,7 @@ of the License or (at your option) any later version.
#include "inc/CharInfo.h"
#include "inc/Slot.h"
#include "inc/Segment.h"
#include "inc/json.h"
#if defined _WIN32
#include "windows.h"
@ -38,12 +39,15 @@ of the License or (at your option) any later version.
using namespace graphite2;
#if !defined GRAPHITE2_NTRACING
json *global_log = NULL;
#endif
extern "C" {
bool gr_start_logging(gr_face * face, const char *log_path)
bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path)
{
if (!face || !log_path) return false;
if (!log_path) return false;
#if !defined GRAPHITE2_NTRACING
gr_stop_logging(face);
@ -52,6 +56,7 @@ bool gr_start_logging(gr_face * face, const char *log_path)
if (n == 0 || n > MAX_PATH - 12) return false;
LPWSTR wlog_path = gralloc<WCHAR>(n);
if (!wlog_path) return false;
FILE *log = 0;
if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n))
log = _wfopen(wlog_path, L"wt");
@ -62,6 +67,8 @@ bool gr_start_logging(gr_face * face, const char *log_path)
#endif // _WIN32
if (!log) return false;
if (face)
{
face->setLogger(log);
if (!face->logger()) return false;
@ -69,6 +76,12 @@ bool gr_start_logging(gr_face * face, const char *log_path)
#ifdef GRAPHITE2_TELEMETRY
*face->logger() << face->tele;
#endif
}
else
{
global_log = new json(log);
*global_log << json::array;
}
return true;
#else // GRAPHITE2_NTRACING
@ -93,17 +106,21 @@ bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */)
//#endif
}
void gr_stop_logging(gr_face * face)
void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face)
{
if (!face) return;
#if !defined GRAPHITE2_NTRACING
if (face->logger())
if (face && face->logger())
{
FILE * log = face->logger()->stream();
face->setLogger(0);
fclose(log);
}
else if (!face && global_log)
{
FILE * log = global_log->stream();
delete global_log;
fclose(log);
}
#endif
}

View File

@ -42,8 +42,8 @@ namespace
// if (!font) return NULL;
Segment* pRes=new Segment(nChars, face, script, dir);
pRes->read_text(face, pFeats, enc, pStart, nChars);
if (!pRes->runGraphite())
if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite())
{
delete pRes;
return NULL;

View File

@ -0,0 +1,126 @@
/* GRAPHITE2 LICENSING
Copyright 2013, 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
namespace graphite2
{
class BracketPair
{
public:
BracketPair(uint16 g, Slot *s, uint8 b, BracketPair *p, BracketPair *l) : _open(s), _close(0), _parent(p), _next(0), _prev(l), _gid(g), _mask(0), _before(b) {};
uint16 gid() const { return _gid; }
Slot *open() const { return _open; }
Slot *close() const { return _close; }
uint8 mask() const { return _mask; }
int8 before() const { return _before; }
BracketPair *parent() const { return _parent; }
void close(Slot *s) { _close = s; }
BracketPair *next() const { return _next; }
BracketPair *prev() const { return _prev; }
void next(BracketPair *n) { _next = n; }
void orin(uint8 m) { _mask |= m; }
private:
Slot * _open; // Slot of opening paren
Slot * _close; // Slot of closing paren
BracketPair * _parent; // pair this pair is in or NULL
BracketPair * _next; // next pair along the string
BracketPair * _prev; // pair that closed last before we opened
uint16 _gid; // gid of closing paren
uint8 _mask; // bitmap (2 bits) of directions within the pair
int8 _before; // most recent strong type (L, R, OPP, CPP)
};
class BracketPairStack
{
public:
BracketPairStack(int s) : _stack(grzeroalloc<BracketPair>(s)), _ip(_stack - 1), _top(0), _last(0), _lastclose(0), _size(s) {}
~BracketPairStack() { free(_stack); }
public:
BracketPair *scan(uint16 gid);
void close(BracketPair *tos, Slot *s);
BracketPair *push(uint16 gid, Slot *pos, uint8 before, int prevopen);
void orin(uint8 mask);
void clear() { _ip = _stack - 1; _top = 0; _last = 0; _lastclose = 0; }
int size() const { return _size; }
BracketPair *start() const { return _stack; }
CLASS_NEW_DELETE
private:
BracketPair *_stack; // start of storage
BracketPair *_ip; // where to add the next pair
BracketPair *_top; // current parent
BracketPair *_last; // end of next() chain
BracketPair *_lastclose; // last pair to close
int _size; // capacity
};
inline BracketPair *BracketPairStack::scan(uint16 gid)
{
BracketPair *res = _top;
while (res >= _stack)
{
if (res->gid() == gid)
return res;
res = res->parent();
}
return 0;
}
inline void BracketPairStack::close(BracketPair *tos, Slot *s)
{
for ( ; _last && _last != tos && !_last->close(); _last = _last->parent())
{ }
tos->close(s);
_last->next(NULL);
_lastclose = tos;
_top = tos->parent();
}
inline BracketPair *BracketPairStack::push(uint16 gid, Slot *pos, uint8 before, int prevopen)
{
if (++_ip - _stack < _size && _stack)
{
::new (_ip) BracketPair(gid, pos, before, _top, prevopen ? _last : _lastclose);
if (_last) _last->next(_ip);
_last = _ip;
}
_top = _ip;
return _ip;
}
inline void BracketPairStack::orin(uint8 mask)
{
BracketPair *t = _top;
for ( ; t; t = t->parent())
t->orin(mask);
}
}

View File

@ -34,7 +34,7 @@ class CharInfo
{
public:
CharInfo() : m_char(0), m_before(-1), m_after(0), m_base(0), m_featureid(0), m_break(0), m_flags(0) {}
CharInfo() : m_char(0), m_before(-1), m_after(-1), m_base(0), m_featureid(0), m_break(0), m_flags(0) {}
void init(int cid) { m_char = cid; }
unsigned int unicodeChar() const { return m_char; }
void feats(int offset) { m_featureid = offset; }

View File

@ -1,6 +1,7 @@
/*-----------------------------------------------------------------------------
Copyright (C) 2011 SIL International
Responsibility: Tim Eves
/* GRAPHITE2 LICENSING
Copyright 2011, 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
@ -22,17 +23,21 @@ 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.
*/
/*
Description:
A set of fast template based decoders for decoding values of any C integer
type up to long int size laid out with most significant byte first or least
significant byte first (aka big endian or little endian). These are CPU
byte order agnostic and will function the same regardless of the CPUs native
byte order.
Being template based means if the either le or be class is not used then
template code of unused functions will not be instantiated by the compiler
and thus shouldn't cause any overhead.
-----------------------------------------------------------------------------*/
*/
#include <cstddef>
#pragma once

View File

@ -0,0 +1,127 @@
/* GRAPHITE2 LICENSING
Copyright 2013, 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
// numbers are explicitly assigned for future proofing
namespace graphite2
{
class Error
{
public:
Error() : _e(0) {};
operator bool() { return (_e != 0); }
int error() { return _e; }
void error(int e) { _e = e; }
bool test(bool pr, int err) { return (_e = int(pr) * err); }
private:
int _e;
};
enum errcontext {
EC_READGLYPHS = 1, // while reading glyphs
EC_READSILF = 2, // in Silf table
EC_ASILF = 3, // in Silf %d
EC_APASS = 4, // in Silf %d, pass %d
EC_PASSCCODE = 5, // in pass constraint code for Silf %d, pass %d
EC_ARULE = 6, // in Silf %d, pass %d, rule %d
EC_ASTARTS = 7, // in Silf %d, pass %d, start state %d
EC_ATRANS = 8, // in Silf %d, pass %d, fsm state %d
EC_ARULEMAP = 9 // in Silf %d, pass %d, state %d
};
enum errors {
E_OUTOFMEM = 1, // Out of memory
E_NOGLYPHS = 2, // There are no glyphs in the font
E_BADUPEM = 3, // The units per em for the font is bad (0)
E_BADCMAP = 4, // The font does not contain any useful cmaps
E_NOSILF = 5, // Missing Silf table
E_TOOOLD = 6, // Silf table version is too old
E_BADSIZE = 7, // context object has the wrong structural size
// Silf Subtable Errors take a Silf subtable number * 256 in the context
E_BADMAXGLYPH = 8, // Silf max glyph id is too high
E_BADNUMJUSTS = 9, // Number of Silf justification blocks is too high
E_BADENDJUSTS = 10, // Silf justification blocks take too much of the Silf table space
E_BADCRITFEATURES = 11, // Critical features section in a Silf table is too big
E_BADSCRIPTTAGS = 12, // Silf script tags area is too big
E_BADAPSEUDO = 13, // The pseudo glyph attribute number is too high
E_BADABREAK = 14, // The linebreak glyph attribute number is too high
E_BADABIDI = 15, // The bidi glyph attribute number is too high
E_BADAMIRROR = 16, // The mirrored glyph attribute number is too high
E_BADNUMPASSES = 17, // The number of passes is > 128
E_BADPASSESSTART = 18, // The Silf table is too small to hold any passes
E_BADPASSBOUND = 19, // The positioning pass number is too low or the substitution pass number is too high
E_BADPPASS = 20, // The positioning pass number is too high
E_BADSPASS = 21, // the substitution pass number is too high
E_BADJPASSBOUND = 22, // the justification pass must be higher than the positioning pass
E_BADJPASS = 23, // the justification pass is too high
E_BADALIG = 24, // the number of initial ligature component glyph attributes is too high
E_BADBPASS = 25, // the bidi pass number is specified and is either too high or too low
E_BADNUMPSEUDO = 26, // The number of pseudo glyphs is too high
E_BADCLASSSIZE = 27, // The size of the classes block is bad
E_TOOMANYLINEAR = 28, // The number of linear classes in the silf table is too high
E_CLASSESTOOBIG = 29, // There are too many classes for the space allocated in the Silf subtable
E_MISALIGNEDCLASSES = 30, // The class offsets in the class table don't line up with the number of classes
E_HIGHCLASSOFFSET = 31, // The class offsets point out of the class table
E_BADCLASSOFFSET = 32, // A class offset is less than one following it
E_BADCLASSLOOKUPINFO = 33, // The search header info for a non-linear class has wrong values in it
// Pass subtable errors. Context has pass number * 65536
E_BADPASSSTART = 34, // The start offset for a particular pass is bad
E_BADPASSEND = 35, // The end offset for a particular pass is bad
E_BADPASSLENGTH = 36, // The length of the pass is too small
E_BADNUMTRANS = 37, // The number of transition states in the fsm is bad
E_BADNUMSUCCESS = 38, // The number of success states in the fsm is bad
E_BADNUMSTATES = 39, // The number of states in the fsm is bad
E_NORANGES = 40, // There are no columns in the fsm
E_BADRULEMAPLEN = 41, // The size of the success state to rule mapping is bad
E_BADCTXTLENBOUNDS = 42, // The precontext maximum is greater than its minimum
E_BADCTXTLENS = 43, // The lists of rule lengths or pre context lengths is bad
E_BADPASSCCODEPTR = 44, // The pass constraint code position does not align with where the forward reference says it should be
E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be
E_BADCCODELEN = 46, // Bad rule/pass constraint code length
E_BADACTIONCODEPTR = 47, // The action code position does not align with where the forward reference says it should be
E_MUTABLECCODE = 48, // Constraint code edits slots. It shouldn't.
E_BADSTATE = 49, // Bad state transition referencing an illegal state
E_BADRULEMAPPING = 50, // The structure of the rule mapping is bad
E_BADRANGE = 51, // Bad column range structure including a glyph in more than one column
E_BADRULENUM = 52, // A reference to a rule is out of range (too high)
// Code errors
E_CODEFAILURE = 60, // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
E_CODEALLOC = 61, // Out of memory
E_INVALIDOPCODE = 62, // Invalid op code
E_UNIMPOPCODE = 63, // Unimplemented op code encountered
E_OUTOFRANGECODE = 64, // Code argument out of range
E_BADJUMPCODE = 65, // Code jumps past end of op codes
E_CODEBADARGS = 66, // Code arguments exhausted
E_CODENORETURN = 67, // Missing return type op code at end of code
E_CODENESTEDCTXT = 68 // Nested context encountered in code
};
}

View File

@ -34,6 +34,7 @@ of the License or (at your option) any later version.
#include "inc/FeatureMap.h"
#include "inc/TtfUtil.h"
#include "inc/Silf.h"
#include "inc/Error.h"
namespace graphite2 {
@ -88,6 +89,12 @@ public:
uint16 getGlyphMetric(uint16 gid, uint8 metric) const;
uint16 findPseudo(uint32 uid) const;
// Errors
unsigned int error() const { return m_error; }
bool error(Error e) { m_error = e.error(); return false; }
unsigned int error_context() const { return m_error; }
void error_context(unsigned int errcntxt) { m_errcntxt = errcntxt; }
CLASS_NEW_DELETE;
private:
SillMap m_Sill;
@ -98,6 +105,8 @@ private:
mutable Cmap * m_cmap; // cmap cache if available
mutable NameTable * m_pNames;
mutable json * m_logger;
unsigned int m_error;
unsigned int m_errcntxt;
protected:
Silf * m_silfs; // silf subtables.
uint16 m_numSilf; // num silf subtables in the silf table

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -44,7 +44,7 @@ typedef gr_int16 int16;
typedef gr_int32 int32;
typedef size_t uintptr;
#if GRAPHITE2_TELEMETRY
#ifdef GRAPHITE2_TELEMETRY
struct telemetry
{
class category;
@ -82,7 +82,7 @@ struct telemetry {};
template <typename T> T * gralloc(size_t n)
{
#if GRAPHITE2_TELEMETRY
#ifdef GRAPHITE2_TELEMETRY
telemetry::count_bytes(sizeof(T) * n);
#endif
return reinterpret_cast<T*>(malloc(sizeof(T) * n));
@ -90,7 +90,7 @@ template <typename T> T * gralloc(size_t n)
template <typename T> T * grzeroalloc(size_t n)
{
#if GRAPHITE2_TELEMETRY
#ifdef GRAPHITE2_TELEMETRY
telemetry::count_bytes(sizeof(T) * n);
#endif
return reinterpret_cast<T*>(calloc(n, sizeof(T)));

View File

@ -38,6 +38,7 @@ struct Rule;
struct RuleEntry;
struct State;
class FiniteStateMachine;
class Error;
class Pass
{
@ -45,7 +46,7 @@ public:
Pass();
~Pass();
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, const Face & face);
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face, Error &e);
void runGraphite(vm::Machine & m, FiniteStateMachine & fsm) const;
void init(Silf *silf) { m_silf = silf; }
byte spaceContextuals() const { return (m_flags & 0x0E) >> 1; }
@ -60,9 +61,9 @@ private:
const byte *precontext, const uint16 * sort_key,
const uint16 * o_constraint, const byte *constraint_data,
const uint16 * o_action, const byte * action_data,
const Face &);
bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, const Face &);
bool readRanges(const byte * ranges, size_t num_ranges);
Face &, Error &e);
bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e);
bool readRanges(const byte * ranges, size_t num_ranges, Error &e);
uint16 glyphToCol(const uint16 gid) const;
bool runFSM(FiniteStateMachine & fsm, Slot * slot) const;
void dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -93,9 +93,7 @@ public:
if (m_entryCounts[length-1] + 1u > listSize)
{
if (m_entryCounts[length-1] == 0)
{
listSize = 1;
}
else
{
// the problem comes when you get incremental numeric ids in a large doc
@ -105,10 +103,8 @@ public:
}
newEntries = gralloc<SegCacheEntry>(listSize);
if (!newEntries)
{
return NULL;
}
}
uint16 insertPos = 0;
if (m_entryCounts[length-1] > 0)

View File

@ -39,6 +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"
#define MAX_SEG_GROWTH_FACTOR 256
@ -116,7 +117,7 @@ public:
SlotJustify *newJustify();
void freeJustify(SlotJustify *aJustify);
Position positionSlots(const Font *font, Slot *first=0, Slot *last=0);
void associateChars();
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); }
uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); }
@ -144,13 +145,12 @@ public:
CLASS_NEW_DELETE
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);
void read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
void prepare_pos(const Font *font);
void finalise(const Font *font);
float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
private:
Rect m_bbox; // ink box of the segment
Position m_advance; // whole segment advance
SlotRope m_slots; // Vector of slot buffers
AttributeRope m_userAttrs; // Vector of userAttrs buffers
@ -179,7 +179,7 @@ void Segment::finalise(const Font *font)
if (!m_first) return;
m_advance = positionSlots(font);
associateChars();
associateChars(0, m_numCharinfo);
linkClusters(m_first, m_last);
}

View File

@ -36,6 +36,7 @@ class Face;
class Segment;
class FeatureVal;
class VMScratch;
class Error;
class Pseudo
{
@ -73,8 +74,8 @@ public:
Silf() throw();
~Silf() throw();
bool readGraphite(const byte * const pSilf, size_t lSilf, const Face &face, uint32 version);
bool runGraphite(Segment *seg, uint8 firstPass=0, uint8 lastPass=0) const;
bool readGraphite(const byte * const pSilf, size_t lSilf, Face &face, uint32 version);
bool runGraphite(Segment *seg, uint8 firstPass=0, uint8 lastPass=0, int dobidi = 0) const;
uint16 findClassIndex(uint16 cid, uint16 gid) const;
uint16 getClassGlyph(uint16 cid, unsigned int index) const;
uint16 findPseudo(uint32 uid) const;
@ -83,6 +84,7 @@ public:
uint8 aBreak() const { return m_aBreak; }
uint8 aMirror() const {return m_aMirror; }
uint8 aPassBits() const { return m_aPassBits; }
uint8 aBidi() const { return m_aBidi; }
uint8 substitutionPass() const { return m_sPass; }
uint8 positionPass() const { return m_pPass; }
uint8 justificationPass() const { return m_jPass; }
@ -99,8 +101,8 @@ public:
CLASS_NEW_DELETE;
private:
size_t readClassMap(const byte *p, size_t data_len, uint32 version);
template<typename T> inline uint32 readClassOffsets(const byte *&p, size_t data_len);
size_t readClassMap(const byte *p, size_t data_len, uint32 version, Error &e);
template<typename T> inline uint32 readClassOffsets(const byte *&p, size_t data_len, Error &e);
Pass * m_passes;
Pseudo * m_pseudos;

View File

@ -82,7 +82,7 @@ public:
void index(uint32 val) { m_index = val; }
Slot();
void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels);
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; }
Slot *prev() const { return m_prev; }
@ -108,8 +108,8 @@ public:
bool isInsertBefore() const { return !(m_flags & INSERTED); }
uint8 getBidiLevel() const { return m_bidiLevel; }
void setBidiLevel(uint8 level) { m_bidiLevel = level; }
uint8 getBidiClass() const { return m_bidiCls; }
void setBidiClass(uint8 cls) { m_bidiCls = cls; }
int8 getBidiClass() const { return m_bidiCls; }
void setBidiClass(int8 cls) { m_bidiCls = cls; }
int16 *userAttrs() const { return m_userAttr; }
void userAttrs(int16 *p) { m_userAttr = p; }
void markInsertBefore(bool state) { if (!state) m_flags |= INSERTED; else m_flags &= ~INSERTED; }
@ -155,7 +155,7 @@ private:
float m_just; // Justification inserted space
uint8 m_flags; // holds bit flags
byte m_attLevel; // attachment level
byte m_bidiCls; // bidirectional class
int8 m_bidiCls; // bidirectional class
byte m_bidiLevel; // bidirectional level
int16 *m_userAttr; // pointer to user attributes
SlotJustify *m_justs; // pointer to justification parameters

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
@ -56,6 +56,7 @@ private:
key_type offset;
};
static chunk empty_chunk;
sparse(const sparse &);
sparse & operator = (const sparse &);
@ -87,7 +88,7 @@ private:
inline
sparse::sparse() throw() : m_nchunks(0)
{
m_array.map = 0;
m_array.map = &empty_chunk;
}
@ -99,12 +100,22 @@ sparse::sparse(I attr, const I last)
// Find the maximum extent of the key space.
size_t n_values=0;
long lastkey = -1;
for (I i = attr; i != last; ++i, ++n_values)
{
const key_type k = (*i).first / SIZEOF_CHUNK;
const typename std::iterator_traits<I>::value_type v = *i;
if (v.second == 0) { --n_values; continue; }
if (v.first <= lastkey) { m_nchunks = 0; return; }
lastkey = v.first;
const key_type k = v.first / SIZEOF_CHUNK;
if (k >= m_nchunks) m_nchunks = k+1;
}
if (m_nchunks == 0) return;
if (m_nchunks == 0)
{
m_array.map=&empty_chunk;
return;
}
m_array.values = grzeroalloc<mapped_type>((m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)
/ sizeof(mapped_type)
@ -122,6 +133,8 @@ sparse::sparse(I attr, const I last)
for (; attr != last; ++attr, ++vi)
{
const typename std::iterator_traits<I>::value_type v = *attr;
if (v.second == 0) { --vi; continue; }
chunk * const ci_ = m_array.map + v.first/SIZEOF_CHUNK;
if (ci != ci_)

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -273,6 +273,11 @@ public:
while (old[len]) len++;
len += 2;
mLangLookup[a][b] = gralloc<const IsoLangEntry *>(len);
if (!mLangLookup[a][b])
{
mLangLookup[a][b] = old;
continue;
}
mLangLookup[a][b][--len] = NULL;
mLangLookup[a][b][--len] = &LANG_ENTRIES[i];
while (--len >= 0)
@ -285,6 +290,7 @@ public:
else
{
mLangLookup[a][b] = gralloc<const IsoLangEntry *>(2);
if (!mLangLookup[a][b]) continue;
mLangLookup[a][b][1] = NULL;
mLangLookup[a][b][0] = &LANG_ENTRIES[i];
}
@ -393,7 +399,7 @@ public:
++i;
continue;
}
if (strcmp(mLangLookup[a][b][i]->maCountry, region) == 0)
if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0))
{
langId = mLangLookup[a][b][i]->mnLang;
break;

View File

@ -263,6 +263,7 @@ ENDOP
STARTOP(insert)
Slot *newSlot = seg.newSlot();
if (!newSlot) DIE;
Slot *iss = is;
while (iss && iss->isDeleted()) iss = iss->next();
if (!iss)
@ -635,6 +636,7 @@ ENDOP
STARTOP(temp_copy)
slotref newSlot = seg.newSlot();
if (!newSlot) DIE;
int16 *tempUserAttrs = newSlot->userAttrs();
memcpy(newSlot, is, sizeof(Slot));
memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));

View File

@ -1,6 +1,6 @@
/* GRAPHITE2 LICENSING
Copyright 2010, SIL International
Copyright 2011, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify

View File

@ -4324,7 +4324,7 @@ if test -n "$ENABLE_INTL_API" -a -z "$MOZ_NATIVE_ICU"; then
CC="$HOST_CC" CXX="$HOST_CXX" LD="$HOST_LD" \
CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS" \
CPPFLAGS="$ICU_CPPFLAGS" \
CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMZIE_FLAGS" \
CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS" \
LDFLAGS="$HOST_LDFLAGS" \
$SHELL $abs_srcdir/../../intl/icu/source/runConfigureICU \
$HOST_ICU_BUILD_OPTS \
@ -4364,11 +4364,16 @@ if test -n "$ENABLE_INTL_API" -a -z "$MOZ_NATIVE_ICU"; then
if test -n "$MOZ_DEBUG" -o "MOZ_DEBUG_SYMBOLS"; then
ICU_CFLAGS="$ICU_CFLAGS $MOZ_DEBUG_FLAGS"
ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_DEBUG_FLAGS"
if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin"; then
# Bug 951758: Cross-OSX builds have issues with -g because it
# tries to run dsymutil
ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g||g'`
ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g||g'`
if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin" \
-a "$HOST_OS_ARCH" != "Darwin"
then
# Bug 951758: Cross-OSX builds with non-Darwin hosts have issues
# with -g and friends (like -gdwarf and -gfull) because they try
# to run dsymutil
changequote(,)
ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g[^ \t]*||g'`
ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g[^ \t]*||g'`
changequote([,])
fi
ICU_LDFLAGS="$MOZ_DEBUG_LDFLAGS"

View File

@ -306,7 +306,7 @@ class AsmJSModule
const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
};
#if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
// Function information to add to the VTune JIT profiler following linking.
struct ProfiledFunction
{
@ -360,7 +360,7 @@ class AsmJSModule
typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector;
typedef Vector<jit::AsmJSHeapAccess, 0, SystemAllocPolicy> HeapAccessVector;
typedef Vector<jit::IonScriptCounts *, 0, SystemAllocPolicy> FunctionCountsVector;
#if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
typedef Vector<ProfiledFunction, 0, SystemAllocPolicy> ProfiledFunctionVector;
#endif
#if defined(JS_ION_PERF)
@ -376,7 +376,7 @@ class AsmJSModule
ExitVector exits_;
ExportedFunctionVector exports_;
HeapAccessVector heapAccesses_;
#if defined(MOZ_VTUNE) or defined(JS_ION_PERF)
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
ProfiledFunctionVector profiledFunctions_;
#endif
#if defined(JS_ION_PERF)

View File

@ -688,7 +688,7 @@ HandleException(ResumeFromException *rfe)
void
HandleParallelFailure(ResumeFromException *rfe)
{
ForkJoinSlice *slice = ForkJoinSlice::Current();
ForkJoinSlice *slice = ForkJoinSlice::current();
IonFrameIterator iter(slice->perThreadData->ionTop, ParallelExecution);
parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");

View File

@ -23,7 +23,7 @@ using parallel::SpewBailoutIR;
ForkJoinSlice *
jit::ForkJoinSlicePar()
{
return ForkJoinSlice::Current();
return ForkJoinSlice::current();
}
// NewGCThingPar() is called in place of NewGCThing() when executing
@ -32,7 +32,7 @@ jit::ForkJoinSlicePar()
JSObject *
jit::NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind)
{
JS_ASSERT(ForkJoinSlice::Current() == slice);
JS_ASSERT(ForkJoinSlice::current() == slice);
uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind);
return gc::NewGCThing<JSObject, NoGC>(slice, allocKind, thingSize, gc::DefaultHeap);
}
@ -42,7 +42,7 @@ jit::NewGCThingPar(ForkJoinSlice *slice, gc::AllocKind allocKind)
bool
jit::IsThreadLocalObject(ForkJoinSlice *slice, JSObject *object)
{
JS_ASSERT(ForkJoinSlice::Current() == slice);
JS_ASSERT(ForkJoinSlice::current() == slice);
return slice->isThreadLocal(object);
}
@ -87,7 +87,7 @@ jit::TraceLIR(IonLIRTraceData *current)
if (current->execModeInt == 0)
cached = &seqTraceData;
else
cached = &ForkJoinSlice::Current()->traceData;
cached = &ForkJoinSlice::current()->traceData;
if (current->blockIndex == 0xDEADBEEF) {
if (current->execModeInt == 0)
@ -106,7 +106,7 @@ jit::TraceLIR(IonLIRTraceData *current)
bool
jit::CheckOverRecursedPar(ForkJoinSlice *slice)
{
JS_ASSERT(ForkJoinSlice::Current() == slice);
JS_ASSERT(ForkJoinSlice::current() == slice);
int stackDummy_;
// When an interrupt is triggered, the main thread stack limit is
@ -135,7 +135,7 @@ jit::CheckOverRecursedPar(ForkJoinSlice *slice)
bool
jit::CheckInterruptPar(ForkJoinSlice *slice)
{
JS_ASSERT(ForkJoinSlice::Current() == slice);
JS_ASSERT(ForkJoinSlice::current() == slice);
bool result = slice->check();
if (!result) {
// Do not set the cause here. Either it was set by this
@ -481,7 +481,7 @@ jit::AbortPar(ParallelBailoutCause cause, JSScript *outermostScript, JSScript *c
JS_ASSERT(currentScript != nullptr);
JS_ASSERT(outermostScript->hasParallelIonScript());
ForkJoinSlice *slice = ForkJoinSlice::Current();
ForkJoinSlice *slice = ForkJoinSlice::current();
JS_ASSERT(slice->bailoutRecord->depth == 0);
slice->bailoutRecord->setCause(cause, outermostScript, currentScript, bytecode);
@ -500,7 +500,7 @@ jit::PropagateAbortPar(JSScript *outermostScript, JSScript *currentScript)
outermostScript->parallelIonScript()->setHasUncompiledCallTarget();
ForkJoinSlice *slice = ForkJoinSlice::Current();
ForkJoinSlice *slice = ForkJoinSlice::current();
if (currentScript)
slice->bailoutRecord->addTrace(currentScript, nullptr);
}

View File

@ -591,7 +591,7 @@ JS_Init(void)
return false;
#endif
if (!ForkJoinSlice::InitializeTLS())
if (!ForkJoinSlice::initialize())
return false;
#if EXPOSE_INTL_API

View File

@ -2113,7 +2113,7 @@ js::TriggerGC(JSRuntime *rt, JS::gcreason::Reason reason)
{
/* Wait till end of parallel section to trigger GC. */
if (InParallelSection()) {
ForkJoinSlice::Current()->requestGC(reason);
ForkJoinSlice::current()->requestGC(reason);
return true;
}
@ -2140,7 +2140,7 @@ js::TriggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
* are stopped to trigger GC.
*/
if (InParallelSection()) {
ForkJoinSlice::Current()->requestZoneGC(zone, reason);
ForkJoinSlice::current()->requestZoneGC(zone, reason);
return true;
}

View File

@ -420,15 +420,15 @@ NewGCThing(ThreadSafeContext *cx, AllocKind kind, size_t thingSize, InitialHeap
if (cx->isJSContext()) {
JSContext *ncx = cx->asJSContext();
#ifdef JS_GC_ZEAL
#if defined(JS_GC_ZEAL) || defined(DEBUG)
JSRuntime *rt = ncx->runtime();
#endif
JS_ASSERT_IF(rt->isAtomsCompartment(ncx->compartment()),
kind == FINALIZE_STRING ||
kind == FINALIZE_SHORT_STRING ||
kind == FINALIZE_JITCODE);
JS_ASSERT(!rt->isHeapBusy());
JS_ASSERT(!rt->noGCOrAllocationCheck);
#endif
/* For testing out of memory conditions */
JS_OOM_POSSIBLY_FAIL_REPORT(ncx);

View File

@ -108,12 +108,6 @@ static size_t gMaxStackSize = 2 * 128 * sizeof(size_t) * 1024;
static size_t gMaxStackSize = 128 * sizeof(size_t) * 1024;
#endif
#ifdef JS_THREADSAFE
static unsigned gStackBaseThreadIndex;
#else
static uintptr_t gStackBase;
#endif
/*
* Limit the timeout to 30 minutes to prevent an overflow on platfoms
* that represent the time internally in microseconds using 32-bit int.
@ -5721,7 +5715,6 @@ main(int argc, char **argv, char **envp)
sArgc = argc;
sArgv = argv;
int stackDummy;
JSRuntime *rt;
JSContext *cx;
int result;
@ -5739,15 +5732,6 @@ main(int argc, char **argv, char **envp)
setlocale(LC_ALL, "");
#endif
#ifdef JS_THREADSAFE
if (PR_FAILURE == PR_NewThreadPrivateIndex(&gStackBaseThreadIndex, nullptr) ||
PR_FAILURE == PR_SetThreadPrivate(gStackBaseThreadIndex, &stackDummy)) {
return 1;
}
#else
gStackBase = (uintptr_t) &stackDummy;
#endif
#ifdef XP_OS2
/* these streams are normally line buffered on OS/2 and need a \n, *
* so we need to unbuffer then to get a reasonable prompt */

View File

@ -6,6 +6,8 @@
#include "vm/ForkJoin.h"
#include "mozilla/ThreadLocal.h"
#include "jscntxt.h"
#include "jslock.h"
#include "jsprf.h"
@ -28,6 +30,8 @@ using namespace js;
using namespace js::parallel;
using namespace js::jit;
using mozilla::ThreadLocal;
///////////////////////////////////////////////////////////////////////////
// Degenerate configurations
//
@ -70,12 +74,6 @@ ForkJoinSlice::isMainThread() const
return true;
}
bool
ForkJoinSlice::InitializeTLS()
{
return true;
}
JSRuntime *
ForkJoinSlice::runtime()
{
@ -174,6 +172,18 @@ ExecuteSequentially(JSContext *cx, HandleValue funVal, bool *complete)
return true;
}
ThreadLocal<ForkJoinSlice*> ForkJoinSlice::tlsForkJoinSlice;
/* static */ bool
ForkJoinSlice::initialize()
{
if (!tlsForkJoinSlice.initialized()) {
if (!tlsForkJoinSlice.init())
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////////
// Parallel configurations
//
@ -220,9 +230,6 @@ enum ForkJoinMode {
NumForkJoinModes
};
unsigned ForkJoinSlice::ThreadPrivateIndex;
bool ForkJoinSlice::TLSInitialized;
class ParallelDo
{
public:
@ -445,11 +452,11 @@ class AutoSetForkJoinSlice
{
public:
AutoSetForkJoinSlice(ForkJoinSlice *threadCx) {
PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, threadCx);
ForkJoinSlice::tlsForkJoinSlice.set(threadCx);
}
~AutoSetForkJoinSlice() {
PR_SetThreadPrivate(ForkJoinSlice::ThreadPrivateIndex, nullptr);
ForkJoinSlice::tlsForkJoinSlice.set(nullptr);
}
};
@ -1199,7 +1206,7 @@ js::ParallelDo::parallelExecution(ExecutionStatus *status)
// Recursive use of the ThreadPool is not supported. Right now we
// cannot get here because parallel code cannot invoke native
// functions such as ForkJoin().
JS_ASSERT(ForkJoinSlice::Current() == nullptr);
JS_ASSERT(ForkJoinSlice::current() == nullptr);
ForkJoinActivation activation(cx_);
@ -1763,17 +1770,6 @@ ForkJoinSlice::check()
return true;
}
bool
ForkJoinSlice::InitializeTLS()
{
if (!TLSInitialized) {
if (PR_NewThreadPrivateIndex(&ThreadPrivateIndex, nullptr) != PR_SUCCESS)
return false;
TLSInitialized = true;
}
return true;
}
void
ForkJoinSlice::requestGC(JS::gcreason::Reason reason)
{
@ -1984,7 +1980,7 @@ class ParallelSpewer
// doesn't get interrupted when running with multiple threads.
char buf[BufferSize];
if (ForkJoinSlice *slice = ForkJoinSlice::Current()) {
if (ForkJoinSlice *slice = ForkJoinSlice::current()) {
JS_snprintf(buf, BufferSize, "[%sParallel:%u%s] ",
sliceColor(slice->sliceId), slice->sliceId, reset());
} else {
@ -2210,7 +2206,7 @@ parallel::SpewBailoutIR(IonLIRTraceData *data)
bool
js::InExclusiveParallelSection()
{
return InParallelSection() && ForkJoinSlice::Current()->hasAcquiredContext();
return InParallelSection() && ForkJoinSlice::current()->hasAcquiredContext();
}
bool

View File

@ -7,6 +7,8 @@
#ifndef vm_ForkJoin_h
#define vm_ForkJoin_h
#include "mozilla/ThreadLocal.h"
#include "jscntxt.h"
#include "gc/GCInternals.h"
@ -372,20 +374,17 @@ class ForkJoinSlice : public ThreadSafeContext
bool hasAcquiredContext() const;
// Check the current state of parallel execution.
static inline ForkJoinSlice *Current();
static inline ForkJoinSlice *current();
// Initializes the thread-local state.
static bool InitializeTLS();
static bool initialize();
private:
friend class AutoRendezvous;
friend class AutoSetForkJoinSlice;
#if defined(JS_THREADSAFE) && defined(JS_ION)
// Initialized by InitializeTLS()
static unsigned ThreadPrivateIndex;
static bool TLSInitialized;
#endif
// Initialized by initialize()
static mozilla::ThreadLocal<ForkJoinSlice*> tlsForkJoinSlice;
ForkJoinShared *const shared;
@ -432,17 +431,6 @@ class LockedJSContext
JSContext *operator->() { return cx_; }
};
static inline bool
InParallelSection()
{
#ifdef JS_THREADSAFE
ForkJoinSlice *current = ForkJoinSlice::Current();
return current != nullptr;
#else
return false;
#endif
}
bool InExclusiveParallelSection();
bool ParallelTestsShouldPass(JSContext *cx);
@ -511,13 +499,19 @@ static inline void SpewBailoutIR(IonLIRTraceData *data) { }
} // namespace js
/* static */ inline js::ForkJoinSlice *
js::ForkJoinSlice::Current()
js::ForkJoinSlice::current()
{
#if defined(JS_THREADSAFE) && defined(JS_ION)
return (ForkJoinSlice*) PR_GetThreadPrivate(ThreadPrivateIndex);
#else
return nullptr;
#endif
return tlsForkJoinSlice.get();
}
namespace js {
static inline bool
InParallelSection()
{
return ForkJoinSlice::current() != nullptr;
}
} // namespace js
#endif /* vm_ForkJoin_h */

View File

@ -1500,6 +1500,118 @@ class EqHelper<true> {
}
};
// The helper function for {ASSERT|EXPECT}_NE.
template <typename T1, typename T2>
AssertionResult CmpHelperNE(const char* expected_expression,
const char* actual_expression,
const T1& expected,
const T2& actual) {
#ifdef _MSC_VER
# pragma warning(push) // Saves the current warning state.
# pragma warning(disable:4389) // Temporarily disables warning on
// signed/unsigned mismatch.
#endif
if (expected != actual) {
return AssertionSuccess();
}
#ifdef _MSC_VER
# pragma warning(pop) // Restores the warning state.
#endif
return NeFailure(expected_expression,
actual_expression,
FormatForComparisonFailureMessage(expected, actual),
FormatForComparisonFailureMessage(actual, expected),
false);
}
// With this overloaded version, we allow anonymous enums to be used
// in {ASSERT|EXPECT}_NE when compiled with gcc 4, as anonymous enums
// can be implicitly cast to BiggestInt.
GTEST_API_ AssertionResult CmpHelperNE(const char* expected_expression,
const char* actual_expression,
BiggestInt expected,
BiggestInt actual);
// The helper class for {ASSERT|EXPECT}_NE. The template argument
// lhs_is_null_literal is true iff the first argument to ASSERT_NE()
// is a null pointer literal. The following default implementation is
// for lhs_is_null_literal being false.
template <bool lhs_is_null_literal>
class NeHelper {
public:
// This templatized version is for the general case.
template <typename T1, typename T2>
static AssertionResult Compare(const char* expected_expression,
const char* actual_expression,
const T1& expected,
const T2& actual) {
return CmpHelperNE(expected_expression, actual_expression, expected,
actual);
}
// With this overloaded version, we allow anonymous enums to be used
// in {ASSERT|EXPECT}_NE when compiled with gcc 4, as anonymous
// enums can be implicitly cast to BiggestInt.
//
// Even though its body looks the same as the above version, we
// cannot merge the two, as it will make anonymous enums unhappy.
static AssertionResult Compare(const char* expected_expression,
const char* actual_expression,
BiggestInt expected,
BiggestInt actual) {
return CmpHelperNE(expected_expression, actual_expression, expected,
actual);
}
};
// This specialization is used when the first argument to ASSERT_NE()
// is a null pointer literal, like NULL, false, or 0.
template <>
class NeHelper<true> {
public:
// We define two overloaded versions of Compare(). The first
// version will be picked when the second argument to ASSERT_NE() is
// NOT a pointer, e.g. ASSERT_NE(0, AnIntFunction()) or
// EXPECT_NE(false, a_bool).
template <typename T1, typename T2>
static AssertionResult Compare(
const char* expected_expression,
const char* actual_expression,
const T1& expected,
const T2& actual,
// The following line prevents this overload from being considered if T2
// is not a pointer type. We need this because ASSERT_NE(NULL, my_ptr)
// expands to Compare("", "", NULL, my_ptr), which requires a conversion
// to match the Secret* in the other overload, which would otherwise make
// this template match better.
typename EnableIf<!is_pointer<T2>::value>::type* = 0) {
return CmpHelperNE(expected_expression, actual_expression, expected,
actual);
}
// This version will be picked when the second argument to ASSERT_NE() is a
// pointer, e.g. ASSERT_NE(NULL, a_pointer).
template <typename T>
static AssertionResult Compare(
const char* expected_expression,
const char* actual_expression,
// We used to have a second template parameter instead of Secret*. That
// template parameter would deduce to 'long', making this a better match
// than the first overload even without the first overload's EnableIf.
// Unfortunately, gcc with -Wconversion-null warns when "passing NULL to
// non-pointer argument" (even a deduced integral argument), so the old
// implementation caused warnings in user code.
Secret* /* expected (NULL) */,
T* actual) {
// We already know that 'expected' is a null pointer.
return CmpHelperNE(expected_expression, actual_expression,
static_cast<T*>(NULL), actual);
}
};
// A macro for implementing the helper functions needed to implement
// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste
// of similar code.
@ -1528,8 +1640,6 @@ GTEST_API_ AssertionResult CmpHelper##op_name(\
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
// Implements the helper function for {ASSERT|EXPECT}_NE
GTEST_IMPL_CMP_HELPER_(NE, !=);
// Implements the helper function for {ASSERT|EXPECT}_LE
GTEST_IMPL_CMP_HELPER_(LE, <=);
// Implements the helper function for {ASSERT|EXPECT}_LT
@ -1928,8 +2038,10 @@ class TestWithParam : public Test, public WithParamInterface<T> {
EXPECT_PRED_FORMAT2(::testing::internal:: \
EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
expected, actual)
#define EXPECT_NE(expected, actual) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual)
#define EXPECT_NE(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal:: \
NeHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
val1, val2)
#define EXPECT_LE(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
#define EXPECT_LT(val1, val2) \
@ -1944,7 +2056,9 @@ class TestWithParam : public Test, public WithParamInterface<T> {
EqHelper<GTEST_IS_NULL_LITERAL_(expected)>::Compare, \
expected, actual)
#define GTEST_ASSERT_NE(val1, val2) \
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
ASSERT_PRED_FORMAT2(::testing::internal:: \
NeHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
val1, val2)
#define GTEST_ASSERT_LE(val1, val2) \
ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
#define GTEST_ASSERT_LT(val1, val2) \

View File

@ -216,6 +216,27 @@ GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
const String& actual_value,
bool ignoring_case);
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_NE, EXPECT_NE, etc) failure.
//
// The first four parameters are the expressions used in the assertion
// and their values, as strings. For example, for ASSERT_NE(foo, bar)
// where foo is 5 and bar is 6, we have:
//
// expected_expression: "foo"
// actual_expression: "bar"
// expected_value: "5"
// actual_value: "6"
//
// The ignoring_case parameter is true iff the assertion is a
// *_STRCASENE*. When it's true, the string " (ignoring case)" will
// be inserted into the message.
GTEST_API_ AssertionResult NeFailure(const char* expected_expression,
const char* actual_expression,
const String& expected_value,
const String& actual_value,
bool ignoring_case);
// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
GTEST_API_ String GetBoolAssertionFailureMessage(
const AssertionResult& assertion_result,

View File

@ -1007,6 +1007,43 @@ AssertionResult EqFailure(const char* expected_expression,
return AssertionFailure() << msg;
}
// Constructs and returns the message for an equality assertion
// (e.g. ASSERT_NE, EXPECT_NE, etc) failure.
//
// The first four parameters are the expressions used in the assertion
// and their values, as strings. For example, for ASSERT_NE(foo, bar)
// where foo is 5 and bar is 6, we have:
//
// expected_expression: "foo"
// actual_expression: "bar"
// expected_value: "5"
// actual_value: "6"
//
// The ignoring_case parameter is true iff the assertion is a
// *_STRCASENE*. When it's true, the string " (ignoring case)" will
// be inserted into the message.
AssertionResult NeFailure(const char* expected_expression,
const char* actual_expression,
const String& expected_value,
const String& actual_value,
bool ignoring_case) {
Message msg;
msg << "Value of: " << actual_expression;
if (actual_value != actual_expression) {
msg << "\n Actual: " << actual_value;
}
msg << "\nExpected: " << expected_expression;
if (ignoring_case) {
msg << " (ignoring case)";
}
if (expected_value != expected_expression) {
msg << "\nWhich is: " << expected_value;
}
return AssertionFailure() << msg;
}
// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
String GetBoolAssertionFailureMessage(const AssertionResult& assertion_result,
const char* expression_text,

View File

@ -1631,9 +1631,9 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void onLocaleReady(final String locale) {
super.onLocaleReady(locale);
if (isHomePagerVisible()) {
// Blow it away and rebuild it with the right strings.
mHomePager.redisplay(getSupportLoaderManager(), getSupportFragmentManager());
if (mHomePager != null) {
mHomePager.invalidate(getSupportLoaderManager(), getSupportFragmentManager());
}
if (mMenu != null) {
@ -2016,8 +2016,11 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void openOptionsMenu() {
if (!hasTabsSideBar() && areTabsShown())
// Disable menu access in edge cases only accessible to hardware menu buttons.
if ((!hasTabsSideBar() && areTabsShown()) ||
mBrowserToolbar.isEditing()) {
return;
}
// Scroll custom menu to the top
if (mMenuPanel != null)

View File

@ -124,6 +124,9 @@ class TextSelection extends Layer implements GeckoEventListener {
if (mActionModeTimerTask != null)
mActionModeTimerTask.cancel();
showActionMode(message.getJSONArray("actions"));
if (handles.length() > 1)
GeckoAppShell.performHapticFeedback(true);
} else if (event.equals("TextSelection:Update")) {
if (mActionModeTimerTask != null)
mActionModeTimerTask.cancel();

View File

@ -47,6 +47,9 @@ public class HomePager extends ViewPager {
private String mInitialPageId;
// Whether or not we need to restart the loader when we show the HomePager.
private boolean mRestartLoader;
// This is mostly used by UI tests to easily fetch
// specific list views at runtime.
static final String LIST_TAG_HISTORY = "history";
@ -154,7 +157,20 @@ public class HomePager extends ViewPager {
super.addView(child, index, params);
}
public void redisplay(LoaderManager lm, FragmentManager fm) {
/**
* Invalidates the current configuration, redisplaying the HomePager if necessary.
*/
public void invalidate(LoaderManager lm, FragmentManager fm) {
// We need to restart the loader to load the new strings.
mRestartLoader = true;
// If the HomePager is currently visible, redisplay it with the new strings.
if (isVisible()) {
redisplay(lm, fm);
}
}
private void redisplay(LoaderManager lm, FragmentManager fm) {
final HomeAdapter adapter = (HomeAdapter) getAdapter();
// If mInitialPageId is non-null, this means the HomePager hasn't
@ -193,8 +209,13 @@ public class HomePager extends ViewPager {
// list of pages in place.
mTabStrip.setVisibility(View.INVISIBLE);
// Load list of pages from configuration
// Load list of pages from configuration. Restart the loader if necessary.
if (mRestartLoader) {
lm.restartLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
mRestartLoader = false;
} else {
lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks);
}
if (shouldAnimate) {
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {

Some files were not shown because too many files have changed in this diff Show More