Merge mozilla-central to autoland r=merge on a CLOSED TREE

This commit is contained in:
Dorel Luca 2017-11-29 12:31:25 +02:00
commit 79499f4044
915 changed files with 22956 additions and 15246 deletions

View File

@ -224,12 +224,14 @@
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpToConsole = true;
gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose,stack");
var gQueue = null;
function doTest()
{
SimpleTest.requestCompleteLog();
// Load documents into tabs and wait for docLoadComplete events caused by these
// documents load before we start the test.
gQueue = new eventQueue();

File diff suppressed because it is too large Load Diff

View File

@ -995,7 +995,7 @@ var RefreshBlocker = {
let URI = Services.io.newURI(data.URI);
refreshURI.forceRefreshURI(URI, data.delay, true);
refreshURI.forceRefreshURI(URI, null, data.delay, true);
}
},

View File

@ -20,7 +20,7 @@ async function attemptFakeRefresh(browser, expectRefresh) {
await ContentTask.spawn(browser, expectRefresh, async function(contentExpectRefresh) {
let URI = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
let refresher = docShell.QueryInterface(Ci.nsIRefreshURI);
refresher.refreshURI(URI, 0, false, true);
refresher.refreshURI(URI, null, 0, false, true);
Assert.equal(refresher.refreshPending, contentExpectRefresh,
"Got the right refreshPending state");

View File

@ -194,7 +194,7 @@ this.windows = class extends ExtensionAPI {
"XULFrameLoaderCreated", onXULFrameLoaderCreated);
}
}
if (createData.titlePreface) {
if (createData.titlePreface !== null) {
win.setTitlePreface(createData.titlePreface);
}
return win.convert({populate: true});
@ -225,7 +225,7 @@ this.windows = class extends ExtensionAPI {
win.updateGeometry(updateInfo);
if (updateInfo.titlePreface) {
if (updateInfo.titlePreface !== null) {
win.setTitlePreface(updateInfo.titlePreface);
win.window.gBrowser.updateTitlebar();
}

View File

@ -160,6 +160,41 @@ add_task(async function testWindowTitle() {
};
await updateWindow({titlePreface: PREFACE2}, apiWin, expected);
// Create a window with a preface.
apiWin = await createApiWin({url: START_URL, titlePreface: PREFACE1});
realWin = windowTracker.getWindow(apiWin.id);
// Update the titlePreface of the window with an empty string.
expected = {
before: {
preface: PREFACE1,
text: START_TITLE,
},
after: {
preface: "",
text: START_TITLE,
},
};
await updateWindow({titlePreface: ""}, apiWin, expected);
ok(!realWin.document.title.startsWith(expected.before.preface), "Updated window has the expected empty title preface.");
// Create a window with a preface.
apiWin = await createApiWin({url: START_URL, titlePreface: PREFACE1});
realWin = windowTracker.getWindow(apiWin.id);
// Update the window without a titlePreface.
expected = {
before: {
preface: PREFACE1,
text: START_TITLE,
},
after: {
preface: PREFACE1,
text: START_TITLE,
},
};
await updateWindow({}, apiWin, expected);
await extension.unload();
});

View File

@ -289,7 +289,6 @@ Inspector.prototype = {
this._defaultNode = null;
this.selection.setNodeFront(null);
this._destroyMarkup();
this.isDirty = false;
this._pendingSelection = null;
},
@ -367,15 +366,6 @@ Inspector.prototype = {
this._target = value;
},
/**
* Indicate that a tool has modified the state of the page. Used to
* decide whether to show the "are you sure you want to navigate"
* notification.
*/
markDirty: function () {
this.isDirty = true;
},
/**
* Hooks the searchbar to show result and auto completion suggestions.
*/
@ -910,7 +900,6 @@ Inspector.prototype = {
this._defaultNode = null;
this.selection.setNodeFront(null);
this._destroyMarkup();
this.isDirty = false;
let onNodeSelected = defaultNode => {
// Cancel this promise resolution as a new one had

View File

@ -46,7 +46,6 @@ function Rule(elementStyle, options) {
this.isUnmatched = options.isUnmatched || false;
this.inherited = options.inherited || null;
this.keyframes = options.keyframes || null;
this._modificationDepth = 0;
if (this.domRule && this.domRule.mediaText) {
this.mediaText = this.domRule.mediaText;

View File

@ -1588,12 +1588,10 @@ function RuleViewTool(inspector, window) {
this.refresh = this.refresh.bind(this);
this.onMutations = this.onMutations.bind(this);
this.onPanelSelected = this.onPanelSelected.bind(this);
this.onPropertyChanged = this.onPropertyChanged.bind(this);
this.onResized = this.onResized.bind(this);
this.onSelected = this.onSelected.bind(this);
this.onViewRefreshed = this.onViewRefreshed.bind(this);
this.view.on("ruleview-changed", this.onPropertyChanged);
this.view.on("ruleview-refreshed", this.onViewRefreshed);
this.inspector.selection.on("detached-front", this.onSelected);
@ -1666,10 +1664,6 @@ RuleViewTool.prototype = {
}
},
onPropertyChanged: function () {
this.inspector.markDirty();
},
onViewRefreshed: function () {
this.inspector.emit("rule-view-refreshed");
},
@ -1708,7 +1702,6 @@ RuleViewTool.prototype = {
this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
}
this.view.off("ruleview-changed", this.onPropertyChanged);
this.view.off("ruleview-refreshed", this.onViewRefreshed);
this.view.destroy();

View File

@ -103,27 +103,6 @@ function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
this.StyleEditorUI = StyleEditorUI;
StyleEditorUI.prototype = {
/**
* Get whether any of the editors have unsaved changes.
*
* @return boolean
*/
get isDirty() {
if (this._markedDirty === true) {
return true;
}
return this.editors.some((editor) => {
return editor.sourceEditor && !editor.sourceEditor.isClean();
});
},
/*
* Mark the style editor as having or not having unsaved changes.
*/
set isDirty(value) {
this._markedDirty = value;
},
/*
* Index of selected stylesheet in document.styleSheets
*/

View File

@ -3398,7 +3398,8 @@ nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal)
{
// If there is an existing document then there is no need to create
// a client for a future initial about:blank document.
if (mScriptGlobal && mScriptGlobal->GetExtantDoc()) {
if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
MOZ_DIAGNOSTIC_ASSERT(
mScriptGlobal->GetCurrentInnerWindowInternal()->GetClientInfo().isSome());
MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
@ -6911,7 +6912,8 @@ nsDocShell::ScrollByPages(int32_t aNumPages)
//*****************************************************************************
NS_IMETHODIMP
nsDocShell::RefreshURI(nsIURI* aURI, int32_t aDelay, bool aRepeat,
nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
int32_t aDelay, bool aRepeat,
bool aMetaRefresh)
{
NS_ENSURE_ARG(aURI);
@ -6940,7 +6942,7 @@ nsDocShell::RefreshURI(nsIURI* aURI, int32_t aDelay, bool aRepeat,
}
nsCOMPtr<nsITimerCallback> refreshTimer =
new nsRefreshTimer(this, aURI, aDelay, aRepeat, aMetaRefresh);
new nsRefreshTimer(this, aURI, aPrincipal, aDelay, aRepeat, aMetaRefresh);
uint32_t busyFlags = 0;
GetBusyFlags(&busyFlags);
@ -6971,6 +6973,7 @@ nsDocShell::RefreshURI(nsIURI* aURI, int32_t aDelay, bool aRepeat,
nsresult
nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
nsIPrincipal* aPrincipal,
int32_t aDelay,
bool aMetaRefresh,
nsITimer* aTimer)
@ -6991,11 +6994,11 @@ nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
}
}
return ForceRefreshURI(aURI, aDelay, aMetaRefresh);
return ForceRefreshURI(aURI, aPrincipal, aDelay, aMetaRefresh);
}
NS_IMETHODIMP
nsDocShell::ForceRefreshURI(nsIURI* aURI, int32_t aDelay, bool aMetaRefresh)
nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, int32_t aDelay, bool aMetaRefresh)
{
NS_ENSURE_ARG(aURI);
@ -7043,6 +7046,13 @@ nsDocShell::ForceRefreshURI(nsIURI* aURI, int32_t aDelay, bool aMetaRefresh)
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadRefresh);
}
// If the principal is null, the refresh will have a triggeringPrincipal
// derived from the referrer URI, or will be set to the system principal
// if there is no refererrer. See LoadURI()
if (aPrincipal) {
loadInfo->SetTriggeringPrincipal(aPrincipal);
}
/*
* LoadURI(...) will cancel all refresh timers... This causes the
* Timer and its refreshData instance to be released...
@ -7283,7 +7293,7 @@ nsDocShell::SetupRefreshURIFromHeader(nsIURI* aBaseURI,
return NS_ERROR_FAILURE;
}
rv = RefreshURI(uri, seconds * 1000, false, true);
rv = RefreshURI(uri, aPrincipal, seconds * 1000, false, true);
}
}
}
@ -7801,6 +7811,11 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
return NS_ERROR_NULL_POINTER;
}
// Make sure to discard the initial client if we never created the initial
// about:blank document. Do this before possibly returning from the method
// due to an error.
mInitialClientSource.reset();
nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
if (reporter) {
nsCOMPtr<nsILoadGroup> loadGroup;
@ -7836,10 +7851,6 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
// Timing is picked up by the window, we don't need it anymore
mTiming = nullptr;
// Make sure to discard the initial client if we never created the initial
// about:blank document.
mInitialClientSource.reset();
// clean up reload state for meta charset
if (eCharsetReloadRequested == mCharsetReloadState) {
mCharsetReloadState = eCharsetReloadStopOrigional;
@ -11835,6 +11846,11 @@ nsDocShell::DoChannelLoad(nsIChannel* aChannel,
openFlags |= nsIURILoader::DONT_RETARGET;
}
// If anything fails here, make sure to clear our initial ClientSource.
auto cleanupInitialClient = MakeScopeExit([&] {
mInitialClientSource.reset();
});
nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
@ -11860,6 +11876,9 @@ nsDocShell::DoChannelLoad(nsIChannel* aChannel,
// collector slice
nsJSContext::MaybeRunNextCollectorSlice(this, JS::gcreason::DOCSHELL);
// Success. Keep the initial ClientSource if it exists.
cleanupInitialClient.release();
return NS_OK;
}
@ -13898,9 +13917,12 @@ nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
return NS_OK;
}
nsRefreshTimer::nsRefreshTimer(nsDocShell* aDocShell, nsIURI* aURI,
nsRefreshTimer::nsRefreshTimer(nsDocShell* aDocShell,
nsIURI* aURI,
nsIPrincipal* aPrincipal,
int32_t aDelay, bool aRepeat, bool aMetaRefresh)
: mDocShell(aDocShell), mURI(aURI), mDelay(aDelay), mRepeat(aRepeat),
: mDocShell(aDocShell), mURI(aURI), mPrincipal(aPrincipal),
mDelay(aDelay), mRepeat(aRepeat),
mMetaRefresh(aMetaRefresh)
{
}
@ -13927,7 +13949,7 @@ nsRefreshTimer::Notify(nsITimer* aTimer)
// Get the delay count to determine load type
uint32_t delay = 0;
aTimer->GetDelay(&delay);
mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
mDocShell->ForceRefreshURIFromTimer(mURI, mPrincipal, delay, mMetaRefresh, aTimer);
}
return NS_OK;
}

View File

@ -117,8 +117,12 @@ class nsRefreshTimer : public nsITimerCallback
, public nsINamed
{
public:
nsRefreshTimer(nsDocShell* aDocShell, nsIURI* aURI, int32_t aDelay,
bool aRepeat, bool aMetaRefresh);
nsRefreshTimer(nsDocShell* aDocShell,
nsIURI* aURI,
nsIPrincipal* aPrincipal,
int32_t aDelay,
bool aRepeat,
bool aMetaRefresh);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
@ -128,6 +132,7 @@ public:
RefPtr<nsDocShell> mDocShell;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mPrincipal;
int32_t mDelay;
bool mRepeat;
bool mMetaRefresh;
@ -262,7 +267,8 @@ public:
// ForceRefreshURI method on nsIRefreshURI, but makes sure to take
// the timer involved out of mRefreshURIList if it's there.
// aTimer must not be null.
nsresult ForceRefreshURIFromTimer(nsIURI* aURI, int32_t aDelay,
nsresult ForceRefreshURIFromTimer(nsIURI* aURI, nsIPrincipal* aPrincipal,
int32_t aDelay,
bool aMetaRefresh, nsITimer* aTimer);
friend class OnLinkClickEvent;

View File

@ -18,23 +18,33 @@ interface nsIRefreshURI : nsISupports {
* queued and executed when the current load finishes.
*
* @param aUri The uri to refresh.
* @param aPrincipal The triggeringPrincipal for the refresh load
* May be null, in which case a principal will be built based on the
* referrer URI of the previous docshell load, or will use the system
* principal when there is no referrer.
* @param aMillis The number of milliseconds to wait.
* @param aRepeat Flag to indicate if the uri is to be
* repeatedly refreshed every aMillis milliseconds.
* @param aMetaRefresh Flag to indicate if this is a Meta refresh.
*/
void refreshURI(in nsIURI aURI, in long aMillis, in boolean aRepeat,
void refreshURI(in nsIURI aURI, in nsIPrincipal aPrincipal,
in long aMillis, in boolean aRepeat,
in boolean aMetaRefresh);
/**
* Loads a URI immediately as if it were a refresh.
*
* @param aURI The URI to refresh.
* @param aPrincipal The triggeringPrincipal for the refresh load
* May be null, in which case a principal will be built based on the
* referrer URI of the previous docshell load, or will use the system
* principal when there is no referrer.
* @param aMillis The number of milliseconds by which this refresh would
* be delayed if it were not being forced.
* @param aMetaRefresh Flag to indicate if this is a meta refresh.
*/
void forceRefreshURI(in nsIURI aURI, in long aMillis, in boolean aMetaRefresh);
void forceRefreshURI(in nsIURI aURI, in nsIPrincipal aPrincipal,
in long aMillis, in boolean aMetaRefresh);
/**
* Checks the passed in channel to see if there is a refresh header,
@ -57,10 +67,15 @@ interface nsIRefreshURI : nsISupports {
* the current page finishes loading.
*
* @param aBaseURI base URI to resolve refresh uri with.
* @param principal the associated principal
* @param aPrincipal The triggeringPrincipal for the refresh load
* May be null, in which case a principal will be built based on the
* referrer URI of the previous docshell load, or will use the system
* principal when there is no referrer.
* @param aHeader The meta refresh header string.
*/
void setupRefreshURIFromHeader(in nsIURI aBaseURI, in nsIPrincipal principal, in ACString aHeader);
void setupRefreshURIFromHeader(in nsIURI aBaseURI,
in nsIPrincipal principal,
in ACString aHeader);
/**
* Cancels all timer loads.

View File

@ -419,7 +419,7 @@ class nsTextNodeDirectionalityMap
nsTextNodeDirectionalityMap* map =
reinterpret_cast<nsTextNodeDirectionalityMap * >(aPropertyValue);
map->EnsureMapIsClear(textNode);
map->EnsureMapIsClear();
delete map;
}
@ -548,11 +548,11 @@ private:
return OpRemove;
}
static nsCheapSetOperator ClearEntry(nsPtrHashKey<Element>* aEntry, void* aData)
static nsCheapSetOperator TakeEntries(nsPtrHashKey<Element>* aEntry, void* aData)
{
Element* rootNode = aEntry->GetKey();
rootNode->ClearHasDirAutoSet();
rootNode->DeleteProperty(nsGkAtoms::dirAutoSetBy);
AutoTArray<Element*, 8>* entries =
static_cast<AutoTArray<Element*, 8>*>(aData);
entries->AppendElement(aEntry->GetKey());
return OpRemove;
}
@ -568,12 +568,15 @@ public:
mElements.EnumerateEntries(ResetNodeDirection, &data);
}
void EnsureMapIsClear(nsINode* aTextNode)
void EnsureMapIsClear()
{
AutoRestore<Element*> restore(mElementToBeRemoved);
DebugOnly<uint32_t> clearedEntries =
mElements.EnumerateEntries(ClearEntry, aTextNode);
MOZ_ASSERT(clearedEntries == 0, "Map should be empty already");
AutoTArray<Element*, 8> entries;
mElements.EnumerateEntries(TakeEntries, &entries);
for (Element* el : entries) {
el->ClearHasDirAutoSet();
el->DeleteProperty(nsGkAtoms::dirAutoSetBy);
}
}
static void RemoveElementFromMap(nsTextNode* aTextNode, Element* aElement)
@ -613,7 +616,7 @@ public:
static void EnsureMapIsClearFor(nsINode* aTextNode)
{
if (aTextNode->HasTextNodeDirectionalityMap()) {
GetDirectionalityMap(aTextNode)->EnsureMapIsClear(aTextNode);
GetDirectionalityMap(aTextNode)->EnsureMapIsClear();
}
}
};

View File

@ -31,13 +31,21 @@ IdleRequest::~IdleRequest()
{
}
NS_IMPL_CYCLE_COLLECTION(IdleRequest, mCallback)
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
if (tmp->isInList()) {
tmp->remove();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(IdleRequest, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(IdleRequest, Release)
void
IdleRequest::SetTimeoutHandle(int32_t aHandle)

View File

@ -9,6 +9,7 @@
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
@ -23,8 +24,7 @@ namespace dom {
class IdleRequestCallback;
class IdleRequest final : public nsISupports,
public LinkedListElement<IdleRequest>
class IdleRequest final : public LinkedListElement<RefPtr<IdleRequest>>
{
public:
IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle);
@ -42,9 +42,8 @@ public:
return mHandle;
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequest)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(IdleRequest)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(IdleRequest)
private:
~IdleRequest();

View File

@ -29,7 +29,9 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Timeout)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Timeout)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptHandler)
tmp->remove();
if (tmp->isInList()) {
tmp->remove();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Timeout)

View File

@ -686,14 +686,6 @@ nsGlobalWindowInner::ResumeIdleRequests()
ScheduleIdleRequestDispatch();
}
void
nsGlobalWindowInner::InsertIdleCallback(IdleRequest* aRequest)
{
AssertIsOnMainThread();
mIdleRequestCallbacks.insertBack(aRequest);
aRequest->AddRef();
}
void
nsGlobalWindowInner::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
{
@ -705,7 +697,6 @@ nsGlobalWindowInner::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
}
aRequest->removeFrom(mIdleRequestCallbacks);
aRequest->Release();
}
nsresult
@ -823,8 +814,7 @@ nsGlobalWindowInner::RequestIdleCallback(JSContext* aCx,
request->SetTimeoutHandle(timeoutHandle);
}
// mIdleRequestCallbacks now owns request
InsertIdleCallback(request);
mIdleRequestCallbacks.insertBack(request);
if (!IsSuspended()) {
ScheduleIdleRequestDispatch();
@ -1546,6 +1536,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)
}
// Here the Timeouts list would've been unlinked, but we rely on
// that Timeout objects have been traced and will remove themselves
// while unlinking.
tmp->UpdateTopInnerWindow();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTopInnerWindow)
@ -1593,7 +1587,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
tmp->UnlinkHostObjectURIs();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
tmp->DisableIdleCallbackRequests();
// Here the IdleRequest list would've been unlinked, but we rely on
// that IdleRequest objects have been traced and will remove
// themselves while unlinking.
tmp->mClientSource.reset();
@ -1749,6 +1746,14 @@ nsGlobalWindowInner::EnsureClientSource()
nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
// Take the initial client source from the docshell immediately. Even if we
// don't end up using it here we should consume it.
UniquePtr<ClientSource> initialClientSource;
nsIDocShell* docshell = GetDocShell();
if (docshell) {
initialClientSource = docshell->TakeInitialClientSource();
}
// Try to get the reserved client from the LoadInfo. A Client is
// reserved at the start of the channel load if there is not an
// initial about:blank document that will be reused. It is also
@ -1769,12 +1774,9 @@ nsGlobalWindowInner::EnsureClientSource()
// and it created an initial Client as a placeholder for the document.
// In this case we want to inherit this placeholder Client here.
if (!mClientSource) {
nsIDocShell* docshell = GetDocShell();
if (docshell) {
mClientSource = docshell->TakeInitialClientSource();
if (mClientSource) {
newClientSource = true;
}
mClientSource = Move(initialClientSource);
if (mClientSource) {
newClientSource = true;
}
}

View File

@ -1253,9 +1253,7 @@ public:
void SuspendIdleRequests();
void ResumeIdleRequests();
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
void InsertIdleCallback(mozilla::dom::IdleRequest* aRequest);
typedef mozilla::LinkedList<RefPtr<mozilla::dom::IdleRequest>> IdleRequests;
void RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest);
protected:

View File

@ -1062,9 +1062,6 @@ public:
virtual mozilla::AbstractThread*
AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
protected:
bool mFullScreen : 1;
bool mFullscreenMode : 1;

View File

@ -1092,7 +1092,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
//////
// Initial setup.
MakeContextCurrent();
gl->mImplicitMakeCurrent = true;
gl->fViewport(0, 0, mWidth, mHeight);
mViewportX = mViewportY = 0;
@ -1966,12 +1966,6 @@ WebGLContext::ForceRestoreContext()
EnqueueUpdateContextLossStatus();
}
void
WebGLContext::MakeContextCurrent() const
{
gl->MakeCurrent();
}
already_AddRefed<mozilla::gfx::SourceSurface>
WebGLContext::GetSurfaceSnapshot(gfxAlphaType* const out_alphaType)
{

View File

@ -1698,7 +1698,7 @@ protected:
void Invalidate();
void DestroyResourcesAndContext();
void MakeContextCurrent() const;
void MakeContextCurrent() const { } // MakeCurrent is implicit now.
// helpers

View File

@ -717,12 +717,16 @@ private:
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
// Check whether the integer addition would overflow.
if (std::numeric_limits<CryptoBuffer::size_type>::max() - 16 < mData.Length()) {
return NS_ERROR_DOM_DATA_ERR;
}
// Initialize the output buffer (enough space for padding / a full tag)
uint32_t dataLen = mData.Length();
uint32_t maxLen = dataLen + 16;
if (!mResult.SetLength(maxLen, fallible)) {
if (!mResult.SetLength(mData.Length() + 16, fallible)) {
return NS_ERROR_DOM_UNKNOWN_ERR;
}
uint32_t outLen = 0;
// Perform the encryption/decryption

View File

@ -7,6 +7,7 @@
#include "nsHostObjectProtocolHandler.h"
#include "DOMMediaStream.h"
#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Exceptions.h"
@ -823,23 +824,35 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
return NS_ERROR_DOM_BAD_URI;
}
#ifdef DEBUG
DataInfo* info = GetDataInfoFromURI(uri);
// Info can be null, in case this blob URL has been revoked already.
if (info) {
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
nsCOMPtr<nsIPrincipal> principal;
uriPrinc->GetPrincipal(getter_AddRefs(principal));
MOZ_ASSERT(info->mPrincipal == principal, "Wrong principal!");
nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
if (!uriPrinc) {
return NS_ERROR_DOM_BAD_URI;
}
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = uriPrinc->GetPrincipal(getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG
// Info can be null, in case this blob URL has been revoked already.
DataInfo* info = GetDataInfoFromURI(uri);
MOZ_ASSERT_IF(info, info->mPrincipal == principal);
#endif
ErrorResult rv;
// We want to be sure that we stop the creation of the channel if the blob URL
// is copy-and-pasted on a different context (ex. private browsing or
// containers).
if (aLoadInfo &&
!ChromeUtils::IsOriginAttributesEqualIgnoringFPD(aLoadInfo->GetOriginAttributes(),
BasePrincipal::Cast(principal)->OriginAttributesRef())) {
return NS_ERROR_DOM_BAD_URI;
}
ErrorResult error;
nsCOMPtr<nsIInputStream> stream;
blobImpl->CreateInputStream(getter_AddRefs(stream), rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
blobImpl->CreateInputStream(getter_AddRefs(stream), error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
nsAutoString contentType;
@ -852,8 +865,8 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
NS_ConvertUTF16toUTF8(contentType),
EmptyCString(), // aContentCharset
aLoadInfo);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (blobImpl->IsFile()) {
@ -862,9 +875,9 @@ nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
channel->SetContentDispositionFilename(filename);
}
uint64_t size = blobImpl->GetSize(rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
uint64_t size = blobImpl->GetSize(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
channel->SetOriginalURI(uri);

View File

@ -2367,7 +2367,7 @@ ContentChild::AddRemoteAlertObserver(const nsString& aData,
}
mozilla::ipc::IPCResult
ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
ContentChild::RecvPreferenceUpdate(const Pref& aPref)
{
Preferences::SetPreference(aPref);
return IPC_OK();

View File

@ -365,7 +365,7 @@ public:
// auto remove when alertfinished is received.
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
virtual mozilla::ipc::IPCResult RecvPreferenceUpdate(const PrefSetting& aPref) override;
virtual mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref) override;
virtual mozilla::ipc::IPCResult RecvVarUpdate(const GfxVarUpdate& pref) override;
virtual mozilla::ipc::IPCResult RecvDataStoragePut(const nsString& aFilename,

View File

@ -2796,7 +2796,7 @@ ContentParent::Observe(nsISupports* aSubject,
// We know prefs are ASCII here.
NS_LossyConvertUTF16toASCII strData(aData);
PrefSetting pref(strData, null_t(), null_t());
Pref pref(strData, null_t(), null_t());
Preferences::GetPreference(&pref);
if (!SendPreferenceUpdate(pref)) {
return NS_ERROR_NOT_AVAILABLE;

View File

@ -115,7 +115,7 @@ ContentProcess::Init(int aArgc, char* aArgv[])
#endif
char* schedulerPrefs = nullptr;
InfallibleTArray<PrefSetting> prefsArray;
InfallibleTArray<Pref> prefsArray;
for (int idx = aArgc; idx > 0; idx--) {
if (!aArgv[idx]) {
continue;
@ -156,7 +156,7 @@ ContentProcess::Init(int aArgc, char* aArgv[])
MaybePrefValue value(PrefValue(static_cast<int32_t>(strtol(str, &str, 10))));
MOZ_ASSERT(str[0] == '|');
str++;
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
Pref pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
}
SET_PREF_PHASE(END_INIT_PREFS);
@ -171,7 +171,7 @@ ContentProcess::Init(int aArgc, char* aArgv[])
MaybePrefValue value(PrefValue(!!strtol(str, &str, 10)));
MOZ_ASSERT(str[0] == '|');
str++;
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
Pref pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
}
SET_PREF_PHASE(END_INIT_PREFS);
@ -187,7 +187,7 @@ ContentProcess::Init(int aArgc, char* aArgv[])
MOZ_ASSERT(str[0] == ';');
str++;
MaybePrefValue value(PrefValue(nsCString(str, length)));
PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
Pref pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue());
prefsArray.AppendElement(pref);
str += length + 1;
MOZ_ASSERT(*(str - 1) == '|');

View File

@ -154,7 +154,7 @@ union MaybePrefValue {
null_t;
};
struct PrefSetting {
struct Pref {
nsCString name;
MaybePrefValue defaultValue;
MaybePrefValue userValue;
@ -270,7 +270,7 @@ struct XPCOMInitData
ClipboardCapabilities clipboardCaps;
DomainPolicyClone domainPolicy;
OptionalURIParams userContentSheetURL;
PrefSetting[] prefs;
Pref[] prefs;
GfxVarUpdate[] gfxNonDefaultVarUpdates;
ContentDeviceData contentDeviceData;
GfxInfoFeatureStatus[] gfxFeatureStatus;
@ -424,7 +424,7 @@ child:
async NotifyVisited(URIParams uri);
async PreferenceUpdate(PrefSetting pref);
async PreferenceUpdate(Pref pref);
async VarUpdate(GfxVarUpdate var);
async DataStoragePut(nsString aFilename, DataStorageItem aItem);

View File

@ -300,6 +300,14 @@ LazyLogModule gMediaTimerLog("MediaTimer");
constexpr TimeUnit MediaDecoder::DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED;
void
MediaDecoder::InitStatics()
{
MOZ_ASSERT(NS_IsMainThread());
// Eagerly init gMediaDecoderLog to work around bug 1415441.
MOZ_LOG(gMediaDecoderLog, LogLevel::Info, ("MediaDecoder::InitStatics"));
}
NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
void

View File

@ -100,6 +100,9 @@ public:
PLAY_STATE_SHUTDOWN
};
// Must be called exactly once, on the main thread, during startup.
static void InitStatics();
explicit MediaDecoder(MediaDecoderInit& aInit);
// Returns the container content type of the resource.

View File

@ -986,9 +986,7 @@ public:
void HandleEndOfAudio() override
{
MOZ_ASSERT(!mDoneAudioSeeking);
AudioQueue().Finish();
mDoneAudioSeeking = true;
HandleEndOfAudioInternal();
MaybeFinishSeek();
}
@ -1006,14 +1004,7 @@ public:
void HandleEndOfVideo() override
{
MOZ_ASSERT(!mDoneVideoSeeking);
if (mFirstVideoFrameAfterSeek) {
// Hit the end of stream. Move mFirstVideoFrameAfterSeek into
// mSeekedVideoData so we have something to display after seeking.
mMaster->PushVideo(mFirstVideoFrameAfterSeek);
}
VideoQueue().Finish();
mDoneVideoSeeking = true;
HandleEndOfVideoInternal();
MaybeFinishSeek();
}
@ -1143,8 +1134,13 @@ protected:
}
if (aReject.mError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
HandleEndOfAudio();
HandleEndOfVideo();
if (!mDoneAudioSeeking) {
HandleEndOfAudioInternal();
}
if (!mDoneVideoSeeking) {
HandleEndOfVideoInternal();
}
MaybeFinishSeek();
return;
}
@ -1296,6 +1292,25 @@ protected:
return NS_OK;
}
void HandleEndOfAudioInternal()
{
MOZ_ASSERT(!mDoneAudioSeeking);
AudioQueue().Finish();
mDoneAudioSeeking = true;
}
void HandleEndOfVideoInternal()
{
MOZ_ASSERT(!mDoneVideoSeeking);
if (mFirstVideoFrameAfterSeek) {
// Hit the end of stream. Move mFirstVideoFrameAfterSeek into
// mSeekedVideoData so we have something to display after seeking.
mMaster->PushVideo(mFirstVideoFrameAfterSeek);
}
VideoQueue().Finish();
mDoneVideoSeeking = true;
}
void MaybeFinishSeek()
{
if (mDoneAudioSeeking && mDoneVideoSeeking) {

View File

@ -5,13 +5,21 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/U2F.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "nsContentUtils.h"
#include "nsICryptoHash.h"
#include "nsIEffectiveTLDService.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsURLParsers.h"
#include "U2FManager.h"
#include "U2FTransactionChild.h"
#include "U2FUtil.h"
#include "hasht.h"
using namespace mozilla::ipc;
// Forward decl because of nsHTMLDocument.h's complex dependency on /layout/style
class nsHTMLDocument {
@ -25,12 +33,14 @@ namespace dom {
static mozilla::LazyLogModule gU2FLog("u2fmanager");
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
NS_NAMED_LITERAL_STRING(kFinishEnrollment, "navigator.id.finishEnrollment");
NS_NAMED_LITERAL_STRING(kGetAssertion, "navigator.id.getAssertion");
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
@ -38,6 +48,23 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
/***********************************************************************
* Utility Functions
**********************************************************************/
static ErrorCode
ConvertNSResultToErrorCode(const nsresult& aError)
{
if (aError == NS_ERROR_DOM_TIMEOUT_ERR) {
return ErrorCode::TIMEOUT;
}
/* Emitted by U2F{Soft,HID}TokenManager when we really mean ineligible */
if (aError == NS_ERROR_DOM_NOT_ALLOWED_ERR) {
return ErrorCode::DEVICE_INELIGIBLE;
}
return ErrorCode::OTHER_ERROR;
}
static uint32_t
AdjustedTimeoutMillis(const Optional<Nullable<int32_t>>& opt_aSeconds)
{
@ -173,6 +200,59 @@ EvaluateAppID(nsPIDOMWindowInner* aParent, const nsString& aOrigin,
return ErrorCode::BAD_REQUEST;
}
static nsresult
BuildTransactionHashes(const nsCString& aRpId,
const nsCString& aClientDataJSON,
/* out */ CryptoBuffer& aRpIdHash,
/* out */ CryptoBuffer& aClientDataHash)
{
nsresult srv;
nsCOMPtr<nsICryptoHash> hashService =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
if (NS_FAILED(srv)) {
return srv;
}
if (!aRpIdHash.SetLength(SHA256_LENGTH, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
srv = HashCString(hashService, aRpId, aRpIdHash);
if (NS_WARN_IF(NS_FAILED(srv))) {
return NS_ERROR_FAILURE;
}
if (!aClientDataHash.SetLength(SHA256_LENGTH, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
srv = HashCString(hashService, aClientDataJSON, aClientDataHash);
if (NS_WARN_IF(NS_FAILED(srv))) {
return NS_ERROR_FAILURE;
}
if (MOZ_LOG_TEST(gU2FLog, LogLevel::Debug)) {
nsString base64;
Unused << NS_WARN_IF(NS_FAILED(aRpIdHash.ToJwkBase64(base64)));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::RpID: %s", aRpId.get()));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Rp ID Hash (base64): %s",
NS_ConvertUTF16toUTF8(base64).get()));
Unused << NS_WARN_IF(NS_FAILED(aClientDataHash.ToJwkBase64(base64)));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Client Data JSON: %s", aClientDataJSON.get()));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Client Data Hash (base64): %s",
NS_ConvertUTF16toUTF8(base64).get()));
}
return NS_OK;
}
template<typename T, typename C>
static void
ExecuteCallback(T& aResp, Maybe<nsMainThreadPtrHandle<C>>& aCb)
@ -194,14 +274,30 @@ ExecuteCallback(T& aResp, Maybe<nsMainThreadPtrHandle<C>>& aCb)
error.SuppressException(); // Useful exceptions already emitted
}
/***********************************************************************
* U2F JavaScript API Implementation
**********************************************************************/
U2F::U2F(nsPIDOMWindowInner* aParent)
: mParent(aParent)
{
MOZ_ASSERT(NS_IsMainThread());
}
U2F::~U2F()
{
mPromiseHolder.DisconnectIfExists();
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome()) {
RejectTransaction(NS_ERROR_ABORT);
}
if (mChild) {
RefPtr<U2FTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
mRegisterCallback.reset();
mSignCallback.reset();
}
@ -210,7 +306,6 @@ void
U2F::Init(ErrorResult& aRv)
{
MOZ_ASSERT(mParent);
MOZ_ASSERT(!mEventTarget);
nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
MOZ_ASSERT(doc);
@ -229,9 +324,6 @@ U2F::Init(ErrorResult& aRv)
aRv.Throw(NS_ERROR_FAILURE);
return;
}
mEventTarget = doc->EventTargetFor(TaskCategory::Other);
MOZ_ASSERT(mEventTarget);
}
/* virtual */ JSObject*
@ -250,7 +342,9 @@ U2F::Register(const nsAString& aAppId,
{
MOZ_ASSERT(NS_IsMainThread());
Cancel();
if (mTransaction.isSome()) {
CancelTransaction(NS_ERROR_ABORT);
}
MOZ_ASSERT(mRegisterCallback.isNothing());
mRegisterCallback = Some(nsMainThreadPtrHandle<U2FRegisterCallback>(
@ -302,38 +396,83 @@ U2F::Register(const nsAString& aAppId,
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
excludeList);
auto& localReqHolder = mPromiseHolder;
auto& localCb = mRegisterCallback;
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
RefPtr<U2FPromise> p = mgr->Register(mParent, cAppId,
NS_ConvertUTF16toUTF8(clientDataJSON),
adjustedTimeoutMillis, excludeList);
p->Then(mEventTarget, "dom::U2F::Register::Promise::Resolve",
[&localCb, &localReqHolder](nsString aResponse) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2F::Register::Promise::Resolve, response was %s",
NS_ConvertUTF16toUTF8(aResponse).get()));
RegisterResponse response;
response.Init(aResponse);
auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
// U2F could be reentered from microtask-checkpoint while calling
// ExecuteCallback(), so we should mark Complete() earlier.
localReqHolder.Complete();
ExecuteCallback(response, localCb);
},
[&localCb, &localReqHolder](ErrorCode aErrorCode) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2F::Register::Promise::Reject, response was %d",
static_cast<uint32_t>(aErrorCode)));
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
CryptoBuffer rpIdHash, clientDataHash;
if (NS_FAILED(BuildTransactionHashes(cAppId, clientData,
rpIdHash, clientDataHash))) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
ExecuteCallback(response, mRegisterCallback);
return;
}
// U2F could be reentered from microtask-checkpoint while calling
// ExecuteCallback(), so we should mark Complete() earlier.
localReqHolder.Complete();
ExecuteCallback(response, localCb);
})
->Track(mPromiseHolder);
if (!MaybeCreateBackgroundActor()) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
ExecuteCallback(response, mRegisterCallback);
return;
}
ListenForVisibilityEvents();
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
WebAuthnTransactionInfo info(rpIdHash,
clientDataHash,
adjustedTimeoutMillis,
excludeList,
extensions);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(clientData));
mChild->SendRequestRegister(mTransaction.ref().mId, info);
}
void
U2F::FinishRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer regBuf;
if (NS_WARN_IF(!regBuf.Assign(aRegBuffer))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
nsString clientDataBase64;
nsString registrationDataBase64;
nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
nsresult rvRegistrationData = regBuf.ToJwkBase64(registrationDataBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
// Assemble a response object to return
RegisterResponse response;
response.mVersion.Construct(kRequiredU2FVersion);
response.mClientData.Construct(clientDataBase64);
response.mRegistrationData.Construct(registrationDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
ExecuteCallback(response, mRegisterCallback);
ClearTransaction();
}
void
@ -346,7 +485,9 @@ U2F::Sign(const nsAString& aAppId,
{
MOZ_ASSERT(NS_IsMainThread());
Cancel();
if (mTransaction.isSome()) {
CancelTransaction(NS_ERROR_ABORT);
}
MOZ_ASSERT(mSignCallback.isNothing());
mSignCallback = Some(nsMainThreadPtrHandle<U2FSignCallback>(
@ -383,65 +524,244 @@ U2F::Sign(const nsAString& aAppId,
nsTArray<WebAuthnScopedCredentialDescriptor> permittedList;
RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
permittedList);
auto& localReqHolder = mPromiseHolder;
auto& localCb = mSignCallback;
RefPtr<U2FManager> mgr = U2FManager::GetOrCreate();
RefPtr<U2FPromise> p = mgr->Sign(mParent, cAppId,
NS_ConvertUTF16toUTF8(clientDataJSON),
adjustedTimeoutMillis, permittedList);
p->Then(mEventTarget, "dom::U2F::Sign::Promise::Resolve",
[&localCb, &localReqHolder](nsString aResponse) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2F::Sign::Promise::Resolve, response was %s",
NS_ConvertUTF16toUTF8(aResponse).get()));
SignResponse response;
response.Init(aResponse);
// U2F could be reentered from microtask-checkpoint while calling
// ExecuteCallback(), so we should mark Complete() earlier.
localReqHolder.Complete();
ExecuteCallback(response, localCb);
},
[&localCb, &localReqHolder](ErrorCode aErrorCode) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2F::Sign::Promise::Reject, response was %d",
static_cast<uint32_t>(aErrorCode)));
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
auto clientData = NS_ConvertUTF16toUTF8(clientDataJSON);
// U2F could be reentered from microtask-checkpoint while calling
// ExecuteCallback(), so we should mark Complete() earlier.
localReqHolder.Complete();
ExecuteCallback(response, localCb);
})
->Track(mPromiseHolder);
CryptoBuffer rpIdHash, clientDataHash;
if (NS_FAILED(BuildTransactionHashes(cAppId, clientData,
rpIdHash, clientDataHash))) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
ExecuteCallback(response, mSignCallback);
return;
}
if (!MaybeCreateBackgroundActor()) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
ExecuteCallback(response, mSignCallback);
return;
}
ListenForVisibilityEvents();
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
WebAuthnTransactionInfo info(rpIdHash,
clientDataHash,
adjustedTimeoutMillis,
permittedList,
extensions);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(clientData));
mChild->SendRequestSign(mTransaction.ref().mId, info);
}
void
U2F::Cancel()
U2F::FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
const ErrorCode errorCode = ErrorCode::OTHER_ERROR;
if (mRegisterCallback.isSome()) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
ExecuteCallback(response, mRegisterCallback);
// Check for a valid transaction.
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
if (mSignCallback.isSome()) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(errorCode));
ExecuteCallback(response, mSignCallback);
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
RefPtr<U2FManager> mgr = U2FManager::Get();
if (mgr) {
mgr->MaybeCancelTransaction(NS_ERROR_DOM_OPERATION_ERR);
CryptoBuffer credBuf;
if (NS_WARN_IF(!credBuf.Assign(aCredentialId))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
mPromiseHolder.DisconnectIfExists();
CryptoBuffer sigBuf;
if (NS_WARN_IF(!sigBuf.Assign(aSigBuffer))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
// Assemble a response object to return
nsString clientDataBase64;
nsString signatureDataBase64;
nsString keyHandleBase64;
nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
nsresult rvSignatureData = sigBuf.ToJwkBase64(signatureDataBase64);
nsresult rvKeyHandle = credBuf.ToJwkBase64(keyHandleBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvSignatureData) ||
NS_WARN_IF(NS_FAILED(rvKeyHandle)))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
SignResponse response;
response.mKeyHandle.Construct(keyHandleBase64);
response.mClientData.Construct(clientDataBase64);
response.mSignatureData.Construct(signatureDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
ExecuteCallback(response, mSignCallback);
ClearTransaction();
}
void
U2F::ClearTransaction()
{
if (!NS_WARN_IF(mTransaction.isNothing())) {
StopListeningForVisibilityEvents();
}
mTransaction.reset();
}
void
U2F::RejectTransaction(const nsresult& aError)
{
if (!NS_WARN_IF(mTransaction.isNothing())) {
ErrorCode code = ConvertNSResultToErrorCode(aError);
if (mRegisterCallback.isSome()) {
RegisterResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(code));
ExecuteCallback(response, mRegisterCallback);
}
if (mSignCallback.isSome()) {
SignResponse response;
response.mErrorCode.Construct(static_cast<uint32_t>(code));
ExecuteCallback(response, mSignCallback);
}
}
ClearTransaction();
}
void
U2F::CancelTransaction(const nsresult& aError)
{
if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
mChild->SendRequestCancel(mTransaction.ref().mId);
}
RejectTransaction(aError);
}
void
U2F::RequestAborted(const uint64_t& aTransactionId, const nsresult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
RejectTransaction(aError);
}
}
/***********************************************************************
* Event Handling
**********************************************************************/
void
U2F::ListenForVisibilityEvents()
{
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, this,
/* use capture */ true,
/* wants untrusted */ false);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
void
U2F::StopListeningForVisibilityEvents()
{
nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, this,
/* use capture */ true);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
NS_IMETHODIMP
U2F::HandleEvent(nsIDOMEvent* aEvent)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aEvent);
nsAutoString type;
aEvent->GetType(type);
if (!type.Equals(kVisibilityChange)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
if (NS_WARN_IF(!doc)) {
return NS_ERROR_FAILURE;
}
if (doc->Hidden()) {
MOZ_LOG(gU2FLog, LogLevel::Debug,
("Visibility change: U2F window is hidden, cancelling job."));
CancelTransaction(NS_ERROR_ABORT);
}
return NS_OK;
}
/***********************************************************************
* IPC Protocol Implementation
**********************************************************************/
bool
U2F::MaybeCreateBackgroundActor()
{
MOZ_ASSERT(NS_IsMainThread());
if (mChild) {
return true;
}
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!actorChild)) {
return false;
}
RefPtr<U2FTransactionChild> mgr(new U2FTransactionChild(this));
PWebAuthnTransactionChild* constructedMgr =
actorChild->SendPWebAuthnTransactionConstructor(mgr);
if (NS_WARN_IF(!constructedMgr)) {
return false;
}
MOZ_ASSERT(constructedMgr == mgr);
mChild = mgr.forget();
return true;
}
void
U2F::ActorDestroyed()
{
MOZ_ASSERT(NS_IsMainThread());
mChild = nullptr;
}
} // namespace dom

View File

@ -17,12 +17,14 @@
#include "nsProxyRelease.h"
#include "nsWrapperCache.h"
#include "U2FAuthenticator.h"
#include "nsIDOMEventListener.h"
class nsISerialEventTarget;
namespace mozilla {
namespace dom {
class U2FTransactionChild;
class U2FRegisterCallback;
class U2FSignCallback;
@ -30,11 +32,38 @@ class U2FSignCallback;
struct RegisterRequest;
struct RegisteredKey;
// The U2F Class is used by the JS engine to initiate U2F operations.
class U2F final : public nsISupports
class U2FTransaction
{
public:
explicit U2FTransaction(const nsCString& aClientData)
: mClientData(aClientData)
, mId(NextId())
{
MOZ_ASSERT(mId > 0);
}
// Client data used to assemble reply objects.
nsCString mClientData;
// Unique transaction id.
uint64_t mId;
private:
// Generates a unique id for new transactions. This doesn't have to be unique
// forever, it's sufficient to differentiate between temporally close
// transactions, where messages can intersect. Can overflow.
static uint64_t NextId() {
static uint64_t id = 0;
return ++id;
}
};
class U2F final : public nsIDOMEventListener
, public nsWrapperCache
{
public:
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(U2F)
@ -68,18 +97,48 @@ public:
const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
ErrorResult& aRv);
private:
void
Cancel();
FinishRegister(const uint64_t& aTransactionId, nsTArray<uint8_t>& aRegBuffer);
void
FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer);
void
RequestAborted(const uint64_t& aTransactionId, const nsresult& aError);
void ActorDestroyed();
private:
~U2F();
// Visibility event handling.
void ListenForVisibilityEvents();
void StopListeningForVisibilityEvents();
// Clears all information we have about the current transaction.
void ClearTransaction();
// Rejects the current transaction and calls ClearTransaction().
void RejectTransaction(const nsresult& aError);
// Cancels the current transaction (by sending a Cancel message to the
// parent) and rejects it by calling RejectTransaction().
void CancelTransaction(const nsresult& aError);
bool MaybeCreateBackgroundActor();
nsString mOrigin;
nsCOMPtr<nsPIDOMWindowInner> mParent;
nsCOMPtr<nsISerialEventTarget> mEventTarget;
// U2F API callbacks.
Maybe<nsMainThreadPtrHandle<U2FRegisterCallback>> mRegisterCallback;
Maybe<nsMainThreadPtrHandle<U2FSignCallback>> mSignCallback;
MozPromiseRequestHolder<U2FPromise> mPromiseHolder;
~U2F();
// IPC Channel to the parent process.
RefPtr<U2FTransactionChild> mChild;
// The current transaction, if any.
Maybe<U2FTransaction> mTransaction;
};
} // namespace dom

View File

@ -1,490 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "hasht.h"
#include "nsICryptoHash.h"
#include "nsNetCID.h"
#include "U2FManager.h"
#include "U2FTransactionChild.h"
#include "U2FUtil.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/BackgroundChild.h"
using namespace mozilla::ipc;
namespace mozilla {
namespace dom {
/***********************************************************************
* Statics
**********************************************************************/
namespace {
StaticRefPtr<U2FManager> gU2FManager;
static mozilla::LazyLogModule gU2FManagerLog("u2fmanager");
}
NS_NAMED_LITERAL_STRING(kVisibilityChange, "visibilitychange");
NS_IMPL_ISUPPORTS(U2FManager, nsIDOMEventListener);
/***********************************************************************
* Utility Functions
**********************************************************************/
static void
ListenForVisibilityEvents(nsPIDOMWindowInner* aParent,
U2FManager* aListener)
{
MOZ_ASSERT(aParent);
MOZ_ASSERT(aListener);
nsCOMPtr<nsIDocument> doc = aParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->AddSystemEventListener(kVisibilityChange, aListener,
/* use capture */ true,
/* wants untrusted */ false);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
static void
StopListeningForVisibilityEvents(nsPIDOMWindowInner* aParent,
U2FManager* aListener)
{
MOZ_ASSERT(aParent);
MOZ_ASSERT(aListener);
nsCOMPtr<nsIDocument> doc = aParent->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
return;
}
nsresult rv = doc->RemoveSystemEventListener(kVisibilityChange, aListener,
/* use capture */ true);
Unused << NS_WARN_IF(NS_FAILED(rv));
}
static ErrorCode
ConvertNSResultToErrorCode(const nsresult& aError)
{
if (aError == NS_ERROR_DOM_TIMEOUT_ERR) {
return ErrorCode::TIMEOUT;
}
/* Emitted by U2F{Soft,HID}TokenManager when we really mean ineligible */
if (aError == NS_ERROR_DOM_NOT_ALLOWED_ERR) {
return ErrorCode::DEVICE_INELIGIBLE;
}
return ErrorCode::OTHER_ERROR;
}
/***********************************************************************
* U2FManager Implementation
**********************************************************************/
U2FManager::U2FManager()
{
MOZ_ASSERT(NS_IsMainThread());
}
void
U2FManager::ClearTransaction()
{
if (!NS_WARN_IF(mTransaction.isNothing())) {
StopListeningForVisibilityEvents(mTransaction.ref().mParent, this);
}
mTransaction.reset();
}
void
U2FManager::RejectTransaction(const nsresult& aError)
{
if (!NS_WARN_IF(mTransaction.isNothing())) {
ErrorCode code = ConvertNSResultToErrorCode(aError);
mTransaction.ref().mPromise.Reject(code, __func__);
}
ClearTransaction();
}
void
U2FManager::CancelTransaction(const nsresult& aError)
{
if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
mChild->SendRequestCancel(mTransaction.ref().mId);
}
RejectTransaction(aError);
}
U2FManager::~U2FManager()
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome()) {
RejectTransaction(NS_ERROR_ABORT);
}
if (mChild) {
RefPtr<U2FTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
}
bool
U2FManager::MaybeCreateBackgroundActor()
{
MOZ_ASSERT(NS_IsMainThread());
if (mChild) {
return true;
}
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
if (NS_WARN_IF(!actorChild)) {
return false;
}
RefPtr<U2FTransactionChild> mgr(new U2FTransactionChild());
PWebAuthnTransactionChild* constructedMgr =
actorChild->SendPWebAuthnTransactionConstructor(mgr);
if (NS_WARN_IF(!constructedMgr)) {
return false;
}
MOZ_ASSERT(constructedMgr == mgr);
mChild = mgr.forget();
return true;
}
//static
U2FManager*
U2FManager::GetOrCreate()
{
MOZ_ASSERT(NS_IsMainThread());
if (gU2FManager) {
return gU2FManager;
}
gU2FManager = new U2FManager();
ClearOnShutdown(&gU2FManager);
return gU2FManager;
}
//static
U2FManager*
U2FManager::Get()
{
MOZ_ASSERT(NS_IsMainThread());
return gU2FManager;
}
//static
nsresult
U2FManager::BuildTransactionHashes(const nsCString& aRpId,
const nsCString& aClientDataJSON,
/* out */ CryptoBuffer& aRpIdHash,
/* out */ CryptoBuffer& aClientDataHash)
{
nsresult srv;
nsCOMPtr<nsICryptoHash> hashService =
do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv);
if (NS_FAILED(srv)) {
return srv;
}
if (!aRpIdHash.SetLength(SHA256_LENGTH, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
srv = HashCString(hashService, aRpId, aRpIdHash);
if (NS_WARN_IF(NS_FAILED(srv))) {
return NS_ERROR_FAILURE;
}
if (!aClientDataHash.SetLength(SHA256_LENGTH, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
srv = HashCString(hashService, aClientDataJSON, aClientDataHash);
if (NS_WARN_IF(NS_FAILED(srv))) {
return NS_ERROR_FAILURE;
}
if (MOZ_LOG_TEST(gU2FLog, LogLevel::Debug)) {
nsString base64;
Unused << NS_WARN_IF(NS_FAILED(aRpIdHash.ToJwkBase64(base64)));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::RpID: %s", aRpId.get()));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Rp ID Hash (base64): %s",
NS_ConvertUTF16toUTF8(base64).get()));
Unused << NS_WARN_IF(NS_FAILED(aClientDataHash.ToJwkBase64(base64)));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Client Data JSON: %s", aClientDataJSON.get()));
MOZ_LOG(gU2FLog, LogLevel::Debug,
("dom::U2FManager::Client Data Hash (base64): %s",
NS_ConvertUTF16toUTF8(base64).get()));
}
return NS_OK;
}
already_AddRefed<U2FPromise>
U2FManager::Register(nsPIDOMWindowInner* aParent, const nsCString& aRpId,
const nsCString& aClientDataJSON,
const uint32_t& aTimeoutMillis,
const nsTArray<WebAuthnScopedCredentialDescriptor>& aExcludeList)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aParent);
if (mTransaction.isSome()) {
CancelTransaction(NS_ERROR_ABORT);
}
CryptoBuffer rpIdHash, clientDataHash;
if (NS_FAILED(BuildTransactionHashes(aRpId, aClientDataJSON,
rpIdHash, clientDataHash))) {
return U2FPromise::CreateAndReject(ErrorCode::OTHER_ERROR, __func__).forget();
}
if (!MaybeCreateBackgroundActor()) {
return U2FPromise::CreateAndReject(ErrorCode::OTHER_ERROR, __func__).forget();
}
ListenForVisibilityEvents(aParent, this);
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
WebAuthnTransactionInfo info(rpIdHash,
clientDataHash,
aTimeoutMillis,
aExcludeList,
extensions);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(aParent, Move(info), aClientDataJSON));
mChild->SendRequestRegister(mTransaction.ref().mId, mTransaction.ref().mInfo);
return mTransaction.ref().mPromise.Ensure(__func__);
}
already_AddRefed<U2FPromise>
U2FManager::Sign(nsPIDOMWindowInner* aParent,
const nsCString& aRpId,
const nsCString& aClientDataJSON,
const uint32_t& aTimeoutMillis,
const nsTArray<WebAuthnScopedCredentialDescriptor>& aAllowList)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aParent);
if (mTransaction.isSome()) {
CancelTransaction(NS_ERROR_ABORT);
}
CryptoBuffer rpIdHash, clientDataHash;
if (NS_FAILED(BuildTransactionHashes(aRpId, aClientDataJSON,
rpIdHash, clientDataHash))) {
return U2FPromise::CreateAndReject(ErrorCode::OTHER_ERROR, __func__).forget();
}
if (!MaybeCreateBackgroundActor()) {
return U2FPromise::CreateAndReject(ErrorCode::OTHER_ERROR, __func__).forget();
}
ListenForVisibilityEvents(aParent, this);
// Always blank for U2F
nsTArray<WebAuthnExtension> extensions;
WebAuthnTransactionInfo info(rpIdHash,
clientDataHash,
aTimeoutMillis,
aAllowList,
extensions);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(aParent, Move(info), aClientDataJSON));
mChild->SendRequestSign(mTransaction.ref().mId, mTransaction.ref().mInfo);
return mTransaction.ref().mPromise.Ensure(__func__);
}
void
U2FManager::FinishRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer regBuf;
if (NS_WARN_IF(!regBuf.Assign(aRegBuffer))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
nsString clientDataBase64;
nsString registrationDataBase64;
nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
nsresult rvRegistrationData = regBuf.ToJwkBase64(registrationDataBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
// Assemble a response object to return
RegisterResponse response;
response.mVersion.Construct(kRequiredU2FVersion);
response.mClientData.Construct(clientDataBase64);
response.mRegistrationData.Construct(registrationDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
nsString responseStr;
if (NS_WARN_IF(!response.ToJSON(responseStr))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
mTransaction.ref().mPromise.Resolve(responseStr, __func__);
ClearTransaction();
}
void
U2FManager::FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
CryptoBuffer clientDataBuf;
if (NS_WARN_IF(!clientDataBuf.Assign(mTransaction.ref().mClientData))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer credBuf;
if (NS_WARN_IF(!credBuf.Assign(aCredentialId))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
CryptoBuffer sigBuf;
if (NS_WARN_IF(!sigBuf.Assign(aSigBuffer))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
// Assemble a response object to return
nsString clientDataBase64;
nsString signatureDataBase64;
nsString keyHandleBase64;
nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
nsresult rvSignatureData = sigBuf.ToJwkBase64(signatureDataBase64);
nsresult rvKeyHandle = credBuf.ToJwkBase64(keyHandleBase64);
if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
NS_WARN_IF(NS_FAILED(rvSignatureData) ||
NS_WARN_IF(NS_FAILED(rvKeyHandle)))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
SignResponse response;
response.mKeyHandle.Construct(keyHandleBase64);
response.mClientData.Construct(clientDataBase64);
response.mSignatureData.Construct(signatureDataBase64);
response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
nsString responseStr;
if (NS_WARN_IF(!response.ToJSON(responseStr))) {
RejectTransaction(NS_ERROR_ABORT);
return;
}
mTransaction.ref().mPromise.Resolve(responseStr, __func__);
ClearTransaction();
}
void
U2FManager::RequestAborted(const uint64_t& aTransactionId,
const nsresult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
RejectTransaction(aError);
}
}
NS_IMETHODIMP
U2FManager::HandleEvent(nsIDOMEvent* aEvent)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aEvent);
nsAutoString type;
aEvent->GetType(type);
if (!type.Equals(kVisibilityChange)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
if (NS_WARN_IF(!doc)) {
return NS_ERROR_FAILURE;
}
if (doc->Hidden()) {
MOZ_LOG(gU2FManagerLog, LogLevel::Debug,
("Visibility change: U2F window is hidden, cancelling job."));
CancelTransaction(NS_ERROR_ABORT);
}
return NS_OK;
}
void
U2FManager::ActorDestroyed()
{
MOZ_ASSERT(NS_IsMainThread());
mChild = nullptr;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,161 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_U2FManager_h
#define mozilla_dom_U2FManager_h
#include "U2FAuthenticator.h"
#include "mozilla/MozPromise.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/PWebAuthnTransaction.h"
#include "nsIDOMEventListener.h"
/*
* Content process manager for the U2F protocol. Created on calls to the
* U2F DOM object, this manager handles establishing IPC channels
* for U2F transactions, as well as keeping track of MozPromise objects
* representing transactions in flight.
*
* The U2F spec (http://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915.zip)
* allows for two different types of transactions: registration and signing.
* When either of these is requested via the DOM API, the following steps are
* executed in the U2FManager:
*
* - Validation of the request. Return a failed promise to the caller if request
* does not have correct parameters.
*
* - If request is valid, open a new IPC channel for running the transaction. If
* another transaction is already running in this content process, cancel it.
* Return a pending promise to the caller.
*
* - Send transaction information to parent process (by running the Start*
* functions of U2FManager). Assuming another transaction is currently in
* flight in another content process, parent will handle canceling it.
*
* - On return of successful transaction information from parent process, turn
* information into DOM object format required by spec, and resolve promise
* (by running the Finish* functions of U2FManager). On cancellation request
* from parent, reject promise with corresponding error code. Either
* outcome will also close the IPC channel.
*
*/
namespace mozilla {
namespace dom {
class ArrayBufferViewOrArrayBuffer;
class OwningArrayBufferViewOrArrayBuffer;
class Promise;
class U2FTransactionChild;
class U2FTransactionInfo;
class U2FTransaction
{
public:
U2FTransaction(nsPIDOMWindowInner* aParent,
const WebAuthnTransactionInfo&& aInfo,
const nsCString& aClientData)
: mParent(aParent)
, mInfo(aInfo)
, mClientData(aClientData)
, mId(NextId())
{
MOZ_ASSERT(mId > 0);
}
// Parent of the context we're running the transaction in.
nsCOMPtr<nsPIDOMWindowInner> mParent;
// JS Promise representing the transaction status.
MozPromiseHolder<U2FPromise> mPromise;
// Holds the parameters of the current transaction, as we need them both
// before the transaction request is sent, and on successful return.
WebAuthnTransactionInfo mInfo;
// Client data used to assemble reply objects.
nsCString mClientData;
// Unique transaction id.
uint64_t mId;
private:
// Generates a unique id for new transactions. This doesn't have to be unique
// forever, it's sufficient to differentiate between temporally close
// transactions, where messages can intersect. Can overflow.
static uint64_t NextId() {
static uint64_t id = 0;
return ++id;
}
};
class U2FManager final : public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
static U2FManager* GetOrCreate();
static U2FManager* Get();
already_AddRefed<U2FPromise> Register(nsPIDOMWindowInner* aParent,
const nsCString& aRpId,
const nsCString& aClientDataJSON,
const uint32_t& aTimeoutMillis,
const nsTArray<WebAuthnScopedCredentialDescriptor>& aExcludeList);
already_AddRefed<U2FPromise> Sign(nsPIDOMWindowInner* aParent,
const nsCString& aRpId,
const nsCString& aClientDataJSON,
const uint32_t& aTimeoutMillis,
const nsTArray<WebAuthnScopedCredentialDescriptor>& aKeyList);
void FinishRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer);
void FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer);
void RequestAborted(const uint64_t& aTransactionId, const nsresult& aError);
// XXX This is exposed only until we fix bug 1410346.
void MaybeCancelTransaction(const nsresult& aError) {
if (mTransaction.isSome()) {
CancelTransaction(NS_ERROR_ABORT);
}
}
void ActorDestroyed();
private:
U2FManager();
virtual ~U2FManager();
static nsresult
BuildTransactionHashes(const nsCString& aRpId,
const nsCString& aClientDataJSON,
/* out */ CryptoBuffer& aRpIdHash,
/* out */ CryptoBuffer& aClientDataHash);
// Clears all information we have about the current transaction.
void ClearTransaction();
// Rejects the current transaction and calls ClearTransaction().
void RejectTransaction(const nsresult& aError);
// Cancels the current transaction (by sending a Cancel message to the
// parent) and rejects it by calling RejectTransaction().
void CancelTransaction(const nsresult& aError);
bool MaybeCreateBackgroundActor();
// IPC Channel to the parent process.
RefPtr<U2FTransactionChild> mChild;
// The current transaction, if any.
Maybe<U2FTransaction> mTransaction;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_U2FManager_h

View File

@ -13,9 +13,7 @@ mozilla::ipc::IPCResult
U2FTransactionChild::RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishRegister(aTransactionId, aRegBuffer);
mU2F->FinishRegister(aTransactionId, aRegBuffer);
return IPC_OK();
}
@ -24,9 +22,7 @@ U2FTransactionChild::RecvConfirmSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishSign(aTransactionId, aCredentialId, aBuffer);
mU2F->FinishSign(aTransactionId, aCredentialId, aBuffer);
return IPC_OK();
}
@ -34,20 +30,14 @@ mozilla::ipc::IPCResult
U2FTransactionChild::RecvAbort(const uint64_t& aTransactionId,
const nsresult& aError)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->RequestAborted(aTransactionId, aError);
mU2F->RequestAborted(aTransactionId, aError);
return IPC_OK();
}
void
U2FTransactionChild::ActorDestroy(ActorDestroyReason why)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
// This could happen after the U2FManager has been shut down.
if (mgr) {
mgr->ActorDestroyed();
}
mU2F->ActorDestroyed();
}
} // namespace dom

View File

@ -21,6 +21,10 @@ namespace dom {
class U2FTransactionChild final : public WebAuthnTransactionChildBase
{
public:
explicit U2FTransactionChild(U2F* aU2F) : mU2F(aU2F) {
MOZ_ASSERT(mU2F);
}
mozilla::ipc::IPCResult
RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer) override;
@ -34,6 +38,10 @@ public:
RecvAbort(const uint64_t& aTransactionId, const nsresult& aError) override;
void ActorDestroy(ActorDestroyReason why) override;
private:
// ~U2F() will destroy child actors.
U2F* mU2F;
};
}

View File

@ -15,7 +15,6 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
'U2F.cpp',
'U2FManager.cpp',
'U2FTransactionChild.cpp',
'U2FTransactionParent.cpp',
]
@ -34,3 +33,4 @@ LOCAL_INCLUDES += [
]
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
tab_u2f_result.html
skip-if = !e10s
[browser_abort_visibility.js]

View File

@ -0,0 +1,117 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const TEST_URL = "https://example.com/browser/dom/u2f/tests/browser/tab_u2f_result.html";
function bytesToBase64(u8a){
let CHUNK_SZ = 0x8000;
let c = [];
for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return window.btoa(c.join(""));
}
function bytesToBase64UrlSafe(buf) {
return bytesToBase64(buf)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
async function assertStatus(tab, expected) {
let actual = await ContentTask.spawn(tab.linkedBrowser, null, async function () {
return content.document.getElementById("status").value;
});
is(actual, expected, "u2f request " + expected);
}
async function waitForStatus(tab, expected) {
await ContentTask.spawn(tab.linkedBrowser, [expected], async function (expected) {
return ContentTaskUtils.waitForCondition(() => {
return content.document.getElementById("status").value == expected;
});
});
await assertStatus(tab, expected);
}
function startMakeCredentialRequest(tab) {
let challenge = crypto.getRandomValues(new Uint8Array(16));
challenge = bytesToBase64UrlSafe(challenge);
return ContentTask.spawn(tab.linkedBrowser, [challenge], async function ([challenge]) {
let appId = content.location.origin;
let request = {version: "U2F_V2", challenge};
let status = content.document.getElementById("status");
content.u2f.register(appId, [request], [], result => {
status.value = result.errorCode ? "aborted" : completed;
});
status.value = "pending";
});
}
function startGetAssertionRequest(tab) {
let challenge = crypto.getRandomValues(new Uint8Array(16));
challenge = bytesToBase64UrlSafe(challenge);
let keyHandle = crypto.getRandomValues(new Uint8Array(16));
keyHandle = bytesToBase64UrlSafe(keyHandle);
return ContentTask.spawn(tab.linkedBrowser, [challenge, keyHandle], async function ([challenge, keyHandle]) {
let appId = content.location.origin;
let key = {version: "U2F_V2", keyHandle};
let status = content.document.getElementById("status");
content.u2f.sign(appId, challenge, [key], result => {
status.value = result.errorCode ? "aborted" : completed;
});
status.value = "pending";
});
}
// Test that MakeCredential() and GetAssertion() requests
// are aborted when the current tab loses its focus.
add_task(async function test_abort() {
// Enable the USB token.
Services.prefs.setBoolPref("security.webauth.u2f", true);
Services.prefs.setBoolPref("security.webauth.webauthn_enable_softtoken", false);
Services.prefs.setBoolPref("security.webauth.webauthn_enable_usbtoken", true);
// Create a new tab for the MakeCredential() request.
let tab_create = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
// Start the request.
await startMakeCredentialRequest(tab_create);
await assertStatus(tab_create, "pending");
// Open another tab and switch to it. The first will lose focus.
let tab_get = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
await waitForStatus(tab_create, "aborted");
// Start a GetAssertion() request in the second tab.
await startGetAssertionRequest(tab_get);
await assertStatus(tab_get, "pending");
// Switch back to the first tab, the get() request is aborted.
await BrowserTestUtils.switchTab(gBrowser, tab_create);
await waitForStatus(tab_get, "aborted");
// Close tabs.
await BrowserTestUtils.removeTab(tab_create);
await BrowserTestUtils.removeTab(tab_get);
// Cleanup.
Services.prefs.clearUserPref("security.webauth.u2f");
Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
});

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Generic U2F Test Result Page</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<h1>Generic U2F Test Result Page</h1>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420906">Mozilla Bug 1420906</a>
<input type="text" id="status" value="init" />
</body>
</html>

View File

@ -0,0 +1,85 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for overriding U2F requests</title>
<script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<h1>Test for overriding U2F requests</h1>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420906">Mozilla Bug 1420906</a>
<script class="testbody" type="text/javascript">
"use strict";
// Last request status.
let status = "";
// Start a new MakeCredential() request.
async function requestMakeCredential(status_value) {
let appId = window.location.origin;
let challenge = crypto.getRandomValues(new Uint8Array(16));
let request = {
version: "U2F_V2",
challenge: bytesToBase64UrlSafe(challenge),
};
u2f.register(appId, [request], [], result => {
local_ok(result.errorCode, "request aborted");
status = status_value;
});
// Wait a tick to let the statemachine start.
await Promise.resolve();
}
// Start a new GetAssertion() request.
async function requestGetAssertion(status_value) {
let appId = window.location.origin;
let challenge = crypto.getRandomValues(new Uint8Array(16));
let keyHandle = crypto.getRandomValues(new Uint8Array(16));
let key = {
version: "U2F_V2",
keyHandle: bytesToBase64UrlSafe(keyHandle)
};
u2f.sign(appId, bytesToBase64UrlSafe(challenge), [key], result => {
local_ok(result.errorCode, "request aborted");
status = status_value;
});
// Wait a tick to let the statemachine start.
await Promise.resolve();
}
// Test that .create() and .get() requests override any pending requests.
(async function () {
// Request a new credential.
await requestMakeCredential("aborted1");
// Request another credential, the first request will abort.
await requestMakeCredential("aborted2");
local_is(status, "aborted1", "first request aborted");
// Request an assertion, the second request will abort.
await requestGetAssertion("aborted3");
local_is(status, "aborted2", "second request aborted");
// Request another assertion, the third request will abort.
await requestGetAssertion("aborted4");
local_is(status, "aborted3", "third request aborted");
// Request another credential, the fourth request will abort.
await requestMakeCredential("aborted5");
local_is(status, "aborted4", "fourth request aborted");
local_finished();
})();
</script>
</body>
</html>

View File

@ -5,6 +5,7 @@ support-files =
frame_appid_facet_subdomain.html
frame_multiple_keys.html
frame_no_token.html
frame_override_request.html
frame_register.html
frame_register_sign.html
frame_utils.js
@ -28,3 +29,4 @@ skip-if = !e10s
scheme = http
[test_appid_facet_subdomain.html]
[test_multiple_keys.html]
[test_override_request.html]

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<head>
<title>Test for overriding U2F requests</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="frame_utils.js"></script>
<script type="text/javascript" src="u2futil.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<h1>Test for overriding U2F requests</h1>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1420906">Mozilla Bug 1420906</a>
<iframe id="testing_frame"></iframe>
<script class="testbody" type="text/javascript">
"use strict";
SimpleTest.waitForExplicitFinish();
// Enable USB tokens.
SpecialPowers.pushPrefEnv({"set": [
["security.webauth.u2f", true],
["security.webauth.webauthn_enable_softtoken", false],
["security.webauth.webauthn_enable_usbtoken", true],
]}, () => {
addEventListener("message", handleEventMessage);
document.getElementById("testing_frame").src = "https://example.com/tests/dom/u2f/tests/frame_override_request.html";
});
</script>
</body>
</html>

View File

@ -73,6 +73,9 @@ static egl::Stream::GLTextureDescription getGLDescFromTex(ID3D11Texture2D* tex,
case DXGI_FORMAT_R8G8B8A8_UNORM:
ret.internalFormat = GL_RGBA8;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
ret.internalFormat = GL_BGRA8_EXT;
break;
default:
*out_error = "Unsupported format";
@ -119,7 +122,7 @@ egl::Error StreamProducerNV12::validateD3DNV12Texture(void *pointer, const egl::
{
return egl::EglBadParameter() << errorText;
}
return egl::NoError();
}

View File

@ -213,7 +213,13 @@ EGLint SwapChain11::resetOffscreenColorBuffer(const gl::Context *context,
ID3D11Resource *tempResource11;
HRESULT result = device->OpenSharedResource(mShareHandle, __uuidof(ID3D11Resource),
(void **)&tempResource11);
ASSERT(SUCCEEDED(result));
if (FAILED(result))
{
ERR() << "Could not open shared handle. " << gl::FmtHR(result);
release();
return EGL_BAD_SURFACE;
}
mOffscreenTexture.set(d3d11::DynamicCastComObject<ID3D11Texture2D>(tempResource11),
backbufferFormatInfo);

View File

@ -744,7 +744,7 @@ Error ValidateCreateContext(Display *display, Config *configuration, gl::Context
{
return EglBadConfig();
}
if (!(configuration->conformant & EGL_OPENGL_ES3_BIT_KHR))
if (!(configuration->renderableType & EGL_OPENGL_ES3_BIT_KHR))
{
return EglBadConfig();
}

View File

@ -60,11 +60,7 @@ namespace gl {
using namespace mozilla::gfx;
using namespace mozilla::layers;
#ifdef MOZ_GL_DEBUG
unsigned GLContext::sCurrentGLContextTLS = -1;
#endif
MOZ_THREAD_LOCAL(GLContext*) GLContext::sCurrentContext;
MOZ_THREAD_LOCAL(uintptr_t) GLContext::sCurrentContext;
// If adding defines, don't forget to undefine symbols. See #undef block below.
#define CORE_SYMBOL(x) { (PRFuncPtr*) &mSymbols.f##x, { #x, nullptr } }
@ -267,7 +263,8 @@ ChooseDebugFlags(CreateContextFlags createFlags)
GLContext::GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
GLContext* sharedContext, bool isOffscreen, bool useTLSIsCurrent)
: mIsOffscreen(isOffscreen),
: mImplicitMakeCurrent(false),
mIsOffscreen(isOffscreen),
mContextLost(false),
mUseTLSIsCurrent(ShouldUseTLSIsCurrent(useTLSIsCurrent)),
mVersion(0),
@ -299,7 +296,7 @@ GLContext::GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
mMaxViewportDims[1] = 0;
mOwningThreadId = PlatformThread::CurrentId();
MOZ_ALWAYS_TRUE( sCurrentContext.init() );
sCurrentContext.set(nullptr);
sCurrentContext.set(0);
}
GLContext::~GLContext() {
@ -3028,37 +3025,31 @@ GetBytesPerTexel(GLenum format, GLenum type)
return 0;
}
bool GLContext::MakeCurrent(bool aForce)
bool
GLContext::MakeCurrent(bool aForce) const
{
if (IsDestroyed())
if (MOZ_UNLIKELY( IsDestroyed() ))
return false;
#ifdef MOZ_GL_DEBUG
PR_SetThreadPrivate(sCurrentGLContextTLS, this);
// XXX this assertion is disabled because it's triggering on Mac;
// we need to figure out why and reenable it.
#if 0
// IsOwningThreadCurrent is a bit of a misnomer;
// the "owning thread" is the creation thread,
// and the only thread that can own this. We don't
// support contexts used on multiple threads.
NS_ASSERTION(IsOwningThreadCurrent(),
"MakeCurrent() called on different thread than this context was created on!");
#endif
#endif
if (mUseTLSIsCurrent && !aForce && sCurrentContext.get() == this) {
MOZ_ASSERT(IsCurrent());
return true;
if (MOZ_LIKELY( !aForce )) {
bool isCurrent;
if (mUseTLSIsCurrent) {
isCurrent = (sCurrentContext.get() == reinterpret_cast<uintptr_t>(this));
} else {
isCurrent = IsCurrentImpl();
}
if (MOZ_LIKELY( isCurrent )) {
MOZ_ASSERT(IsCurrentImpl());
return true;
}
}
if (!MakeCurrentImpl(aForce))
if (!MakeCurrentImpl())
return false;
if (mUseTLSIsCurrent) {
sCurrentContext.set(this);
sCurrentContext.set(reinterpret_cast<uintptr_t>(this));
}
return true;
}
@ -3073,5 +3064,56 @@ GLContext::ResetSyncCallCount(const char* resetReason) const
mSyncGLCallCount = 0;
}
// --
void
GLContext::BeforeGLCall_Debug(const char* const funcName) const
{
MOZ_ASSERT(mDebugFlags);
FlushErrors();
if (mDebugFlags & DebugFlagTrace) {
printf_stderr("[gl:%p] > %s\n", this, funcName);
}
}
void
GLContext::AfterGLCall_Debug(const char* const funcName) const
{
MOZ_ASSERT(mDebugFlags);
// calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
// the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
// tend to be meaningless
mSymbols.fFinish();
GLenum err = FlushErrors();
if (mDebugFlags & DebugFlagTrace) {
printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
GLErrorToString(err), err);
}
if (err != LOCAL_GL_NO_ERROR &&
!mLocalErrorScopeStack.size())
{
printf_stderr("[gl:%p] %s: Generated unexpected %s error."
" (0x%04x)\n", this, funcName,
GLErrorToString(err), err);
if (mDebugFlags & DebugFlagAbortOnError) {
MOZ_CRASH("Unexpected error with MOZ_GL_DEBUG_ABORT_ON_ERROR. (Run"
" with MOZ_GL_DEBUG_ABORT_ON_ERROR=0 to disable)");
}
}
}
/*static*/ void
GLContext::OnImplicitMakeCurrentFailure(const char* const funcName)
{
gfxCriticalError() << "Ignoring call to " << funcName << " with failed"
<< " mImplicitMakeCurrent.";
}
} /* namespace gl */
} /* namespace mozilla */

View File

@ -196,7 +196,9 @@ class GLContext
{
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
static MOZ_THREAD_LOCAL(GLContext*) sCurrentContext;
static MOZ_THREAD_LOCAL(uintptr_t) sCurrentContext;
bool mImplicitMakeCurrent;
// -----------------------------------------------------------------------------
// basic getters
@ -296,7 +298,17 @@ public:
virtual GLContextType GetContextType() const = 0;
virtual bool IsCurrent() = 0;
virtual bool IsCurrentImpl() const = 0;
virtual bool MakeCurrentImpl() const = 0;
bool IsCurrent() const {
if (mImplicitMakeCurrent)
return MakeCurrent();
return IsCurrentImpl();
}
bool MakeCurrent(bool aForce = false) const;
/**
* Get the default framebuffer for this context.
@ -307,7 +319,7 @@ public:
protected:
bool mIsOffscreen;
bool mContextLost;
mutable bool mContextLost;
const bool mUseTLSIsCurrent;
/**
@ -540,13 +552,13 @@ public:
}
private:
GLenum mTopError;
mutable GLenum mTopError;
GLenum RawGetError() {
GLenum RawGetError() const {
return mSymbols.fGetError();
}
GLenum RawGetErrorAndClear() {
GLenum RawGetErrorAndClear() const {
GLenum err = RawGetError();
if (err)
@ -555,26 +567,17 @@ private:
return err;
}
public:
GLenum FlushErrors() {
GLenum FlushErrors() const {
GLenum err = RawGetErrorAndClear();
if (!mTopError)
mTopError = err;
return err;
}
// We smash all errors together, so you never have to loop on this. We
// guarantee that immediately after this call, there are no errors left.
GLenum fGetError() {
FlushErrors();
GLenum err = mTopError;
mTopError = LOCAL_GL_NO_ERROR;
return err;
}
////////////////////////////////////
// Use this safer option.
public:
class LocalErrorScope;
private:
@ -652,11 +655,6 @@ private:
// MOZ_GL_DEBUG implementation
private:
#undef BEFORE_GL_CALL
#undef AFTER_GL_CALL
#ifdef MOZ_GL_DEBUG
#ifndef MOZ_FUNCTION_NAME
# ifdef __GNUC__
# define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
@ -667,51 +665,45 @@ private:
# endif
#endif
void BeforeGLCall(const char* funcName) {
MOZ_ASSERT(IsCurrent());
#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#define ANDROID_ONLY_PROFILER_LABEL AUTO_PROFILER_LABEL(__func__, GRAPHICS);
#else
#define ANDROID_ONLY_PROFILER_LABEL
#endif
if (mDebugFlags) {
FlushErrors();
#define BEFORE_GL_CALL \
ANDROID_ONLY_PROFILER_LABEL \
if (MOZ_LIKELY( BeforeGLCall(MOZ_FUNCTION_NAME) )) { \
do { } while (0)
if (mDebugFlags & DebugFlagTrace) {
printf_stderr("[gl:%p] > %s\n", this, funcName);
}
#define AFTER_GL_CALL \
AfterGLCall(MOZ_FUNCTION_NAME); \
} \
do { } while (0)
GLContext* tlsContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
if (this != tlsContext) {
printf_stderr("Fatal: %s called on non-current context %p. The"
" current context for this thread is %p.\n",
funcName, this, tlsContext);
MOZ_CRASH("GFX: GLContext is not current.");
void BeforeGLCall_Debug(const char* funcName) const;
void AfterGLCall_Debug(const char* funcName) const;
static void OnImplicitMakeCurrentFailure(const char* funcName);
bool BeforeGLCall(const char* const funcName) const {
if (mImplicitMakeCurrent) {
if (MOZ_UNLIKELY( !MakeCurrent() )) {
OnImplicitMakeCurrentFailure(funcName);
return false;
}
}
MOZ_ASSERT(IsCurrentImpl());
if (mDebugFlags) {
BeforeGLCall_Debug(funcName);
}
return true;
}
void AfterGLCall(const char* funcName) {
void AfterGLCall(const char* const funcName) const {
if (mDebugFlags) {
// calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
// the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
// tend to be meaningless
mSymbols.fFinish();
GLenum err = FlushErrors();
if (mDebugFlags & DebugFlagTrace) {
printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
GLErrorToString(err), err);
}
if (err != LOCAL_GL_NO_ERROR &&
!mLocalErrorScopeStack.size())
{
printf_stderr("[gl:%p] %s: Generated unexpected %s error."
" (0x%04x)\n", this, funcName,
GLErrorToString(err), err);
if (mDebugFlags & DebugFlagAbortOnError) {
MOZ_CRASH("Unexpected error with MOZ_GL_DEBUG_ABORT_ON_ERROR. (Run"
" with MOZ_GL_DEBUG_ABORT_ON_ERROR=0 to disable)");
}
}
AfterGLCall_Debug(funcName);
}
}
@ -725,22 +717,7 @@ private:
static void AssertNotPassingStackBufferToTheGL(const void* ptr);
#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#define BEFORE_GL_CALL \
AUTO_PROFILER_LABEL(__func__, GRAPHICS);\
BeforeGLCall(MOZ_FUNCTION_NAME)
#else
#define BEFORE_GL_CALL \
do { \
BeforeGLCall(MOZ_FUNCTION_NAME); \
} while (0)
#endif
#define AFTER_GL_CALL \
do { \
AfterGLCall(MOZ_FUNCTION_NAME); \
} while (0)
#ifdef MOZ_GL_DEBUG
#define TRACKING_CONTEXT(a) \
do { \
@ -749,20 +726,6 @@ private:
#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) AssertNotPassingStackBufferToTheGL(ptr)
#else // ifdef MOZ_GL_DEBUG
#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#define BEFORE_GL_CALL AUTO_PROFILER_LABEL(__func__, GRAPHICS)
#else
#define BEFORE_GL_CALL do { } while (0)
#endif
#define AFTER_GL_CALL do { } while (0)
#define TRACKING_CONTEXT(a) do {} while (0)
#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) do {} while (0)
#endif // ifdef MOZ_GL_DEBUG
#define ASSERT_SYMBOL_PRESENT(func) \
do {\
MOZ_ASSERT(strstr(MOZ_FUNCTION_NAME, #func) != nullptr, "Mismatched symbol check.");\
@ -772,6 +735,15 @@ private:
}\
} while (0)
#else // ifdef MOZ_GL_DEBUG
#define TRACKING_CONTEXT(a) do {} while (0)
#define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) do {} while (0)
#define ASSERT_SYMBOL_PRESENT(func) do {} while (0)
#endif // ifdef MOZ_GL_DEBUG
// Do whatever setup is necessary to draw to our offscreen FBO, if it's
// bound.
void BeforeGLDrawCall() { }
@ -802,6 +774,19 @@ public:
// -----------------------------------------------------------------------------
// GL official entry points
public:
// We smash all errors together, so you never have to loop on this. We
// guarantee that immediately after this call, there are no errors left.
GLenum fGetError() {
GLenum err = LOCAL_GL_CONTEXT_LOST;
BEFORE_GL_CALL;
FlushErrors();
err = mTopError;
mTopError = LOCAL_GL_NO_ERROR;
AFTER_GL_CALL;
return err;
}
void fActiveTexture(GLenum texture) {
BEFORE_GL_CALL;
@ -1182,8 +1167,9 @@ public:
}
GLint fGetAttribLocation(GLuint program, const GLchar* name) {
GLint retval = 0;
BEFORE_GL_CALL;
GLint retval = mSymbols.fGetAttribLocation(program, name);
retval = mSymbols.fGetAttribLocation(program, name);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -1235,9 +1221,10 @@ public:
}
GLuint fGetDebugMessageLog(GLuint count, GLsizei bufsize, GLenum* sources, GLenum* types, GLuint* ids, GLenum* severities, GLsizei* lengths, GLchar* messageLog) {
GLuint ret = 0;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fGetDebugMessageLog);
GLuint ret = mSymbols.fGetDebugMessageLog(count, bufsize, sources, types, ids, severities, lengths, messageLog);
ret = mSymbols.fGetDebugMessageLog(count, bufsize, sources, types, ids, severities, lengths, messageLog);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -1306,8 +1293,9 @@ public:
}
const GLubyte* fGetString(GLenum name) {
const GLubyte* result = nullptr;
BEFORE_GL_CALL;
const GLubyte* result = mSymbols.fGetString(name);
result = mSymbols.fGetString(name);
OnSyncCall();
AFTER_GL_CALL;
return result;
@ -1367,8 +1355,9 @@ public:
}
GLint fGetUniformLocation (GLint programObj, const GLchar* name) {
GLint retval = 0;
BEFORE_GL_CALL;
GLint retval = mSymbols.fGetUniformLocation(programObj, name);
retval = mSymbols.fGetUniformLocation(programObj, name);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -1402,16 +1391,18 @@ public:
}
realGLboolean fIsBuffer(GLuint buffer) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsBuffer(buffer);
retval = mSymbols.fIsBuffer(buffer);
OnSyncCall();
AFTER_GL_CALL;
return retval;
}
realGLboolean fIsEnabled(GLenum capability) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsEnabled(capability);
retval = mSymbols.fIsEnabled(capability);
AFTER_GL_CALL;
return retval;
}
@ -1433,22 +1424,25 @@ public:
}
realGLboolean fIsProgram(GLuint program) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsProgram(program);
retval = mSymbols.fIsProgram(program);
AFTER_GL_CALL;
return retval;
}
realGLboolean fIsShader(GLuint shader) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsShader(shader);
retval = mSymbols.fIsShader(shader);
AFTER_GL_CALL;
return retval;
}
realGLboolean fIsTexture(GLuint texture) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsTexture(texture);
retval = mSymbols.fIsTexture(texture);
AFTER_GL_CALL;
return retval;
}
@ -1888,6 +1882,23 @@ public:
AFTER_GL_CALL;
}
void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
if (mViewportRect[0] == x &&
mViewportRect[1] == y &&
mViewportRect[2] == width &&
mViewportRect[3] == height)
{
return;
}
mViewportRect[0] = x;
mViewportRect[1] = y;
mViewportRect[2] = width;
mViewportRect[3] = height;
BEFORE_GL_CALL;
mSymbols.fViewport(x, y, width, height);
AFTER_GL_CALL;
}
void fCompileShader(GLuint shader) {
BEFORE_GL_CALL;
mSymbols.fCompileShader(shader);
@ -1978,8 +1989,9 @@ public:
}
GLenum fCheckFramebufferStatus(GLenum target) {
GLenum retval = 0;
BEFORE_GL_CALL;
GLenum retval = mSymbols.fCheckFramebufferStatus(target);
retval = mSymbols.fCheckFramebufferStatus(target);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -2022,8 +2034,9 @@ public:
}
realGLboolean fIsFramebuffer (GLuint framebuffer) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsFramebuffer(framebuffer);
retval = mSymbols.fIsFramebuffer(framebuffer);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -2031,8 +2044,9 @@ public:
public:
realGLboolean fIsRenderbuffer (GLuint renderbuffer) {
realGLboolean retval = false;
BEFORE_GL_CALL;
realGLboolean retval = mSymbols.fIsRenderbuffer(renderbuffer);
retval = mSymbols.fIsRenderbuffer(renderbuffer);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -2099,18 +2113,20 @@ public:
}
void* fMapBuffer(GLenum target, GLenum access) {
void* ret = nullptr;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fMapBuffer);
void* ret = mSymbols.fMapBuffer(target, access);
ret = mSymbols.fMapBuffer(target, access);
OnSyncCall();
AFTER_GL_CALL;
return ret;
}
realGLboolean fUnmapBuffer(GLenum target) {
realGLboolean ret = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fUnmapBuffer);
realGLboolean ret = mSymbols.fUnmapBuffer(target);
ret = mSymbols.fUnmapBuffer(target);
AFTER_GL_CALL;
return ret;
}
@ -2118,15 +2134,17 @@ public:
private:
GLuint raw_fCreateProgram() {
GLuint ret = 0;
BEFORE_GL_CALL;
GLuint ret = mSymbols.fCreateProgram();
ret = mSymbols.fCreateProgram();
AFTER_GL_CALL;
return ret;
}
GLuint raw_fCreateShader(GLenum t) {
GLuint ret = 0;
BEFORE_GL_CALL;
GLuint ret = mSymbols.fCreateShader(t);
ret = mSymbols.fCreateShader(t);
AFTER_GL_CALL;
return ret;
}
@ -2259,9 +2277,10 @@ public:
}
GLenum fGetGraphicsResetStatus() {
GLenum ret = 0;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fGetGraphicsResetStatus);
GLenum ret = mSymbols.fGetGraphicsResetStatus();
ret = mSymbols.fGetGraphicsResetStatus();
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2272,18 +2291,20 @@ public:
// Extension ARB_sync (GL)
public:
GLsync fFenceSync(GLenum condition, GLbitfield flags) {
GLsync ret = 0;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fFenceSync);
GLsync ret = mSymbols.fFenceSync(condition, flags);
ret = mSymbols.fFenceSync(condition, flags);
OnSyncCall();
AFTER_GL_CALL;
return ret;
}
realGLboolean fIsSync(GLsync sync) {
realGLboolean ret = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fIsSync);
realGLboolean ret = mSymbols.fIsSync(sync);
ret = mSymbols.fIsSync(sync);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2297,9 +2318,10 @@ public:
}
GLenum fClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
GLenum ret = 0;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fClientWaitSync);
GLenum ret = mSymbols.fClientWaitSync(sync, flags, timeout);
ret = mSymbols.fClientWaitSync(sync, flags, timeout);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2577,9 +2599,10 @@ public:
GLint fGetFragDataLocation(GLuint program, const GLchar* name)
{
GLint result = 0;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fGetFragDataLocation);
GLint result = mSymbols.fGetFragDataLocation(program, name);
result = mSymbols.fGetFragDataLocation(program, name);
OnSyncCall();
AFTER_GL_CALL;
return result;
@ -2674,9 +2697,10 @@ public:
}
realGLboolean fIsQuery(GLuint query) {
realGLboolean retval = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fIsQuery);
realGLboolean retval = mSymbols.fIsQuery(query);
retval = mSymbols.fIsQuery(query);
OnSyncCall();
AFTER_GL_CALL;
return retval;
@ -2766,9 +2790,10 @@ public:
realGLboolean fIsTransformFeedback(GLuint id)
{
realGLboolean result = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fIsTransformFeedback);
realGLboolean result = mSymbols.fIsTransformFeedback(id);
result = mSymbols.fIsTransformFeedback(id);
OnSyncCall();
AFTER_GL_CALL;
return result;
@ -2877,9 +2902,10 @@ public:
realGLboolean fIsVertexArray(GLuint array)
{
realGLboolean ret = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fIsVertexArray);
realGLboolean ret = mSymbols.fIsVertexArray(array);
ret = mSymbols.fIsVertexArray(array);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2914,9 +2940,10 @@ public:
realGLboolean fTestFence(GLuint fence)
{
realGLboolean ret = false;
ASSERT_SYMBOL_PRESENT(fTestFence);
BEFORE_GL_CALL;
realGLboolean ret = mSymbols.fTestFence(fence);
ret = mSymbols.fTestFence(fence);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2933,9 +2960,10 @@ public:
realGLboolean fIsFence(GLuint fence)
{
realGLboolean ret = false;
ASSERT_SYMBOL_PRESENT(fIsFence);
BEFORE_GL_CALL;
realGLboolean ret = mSymbols.fIsFence(fence);
ret = mSymbols.fIsFence(fence);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -2980,9 +3008,10 @@ public:
void* fMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
GLbitfield access)
{
void* data = nullptr;
ASSERT_SYMBOL_PRESENT(fMapBufferRange);
BEFORE_GL_CALL;
void* data = mSymbols.fMapBufferRange(target, offset, length, access);
data = mSymbols.fMapBufferRange(target, offset, length, access);
OnSyncCall();
AFTER_GL_CALL;
return data;
@ -3017,9 +3046,10 @@ public:
realGLboolean fIsSampler(GLuint sampler)
{
realGLboolean result = false;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fIsSampler);
realGLboolean result = mSymbols.fIsSampler(sampler);
result = mSymbols.fIsSampler(sampler);
OnSyncCall();
AFTER_GL_CALL;
return result;
@ -3106,9 +3136,10 @@ public:
}
GLuint fGetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName) {
GLuint result = 0;
ASSERT_SYMBOL_PRESENT(fGetUniformBlockIndex);
BEFORE_GL_CALL;
GLuint result = mSymbols.fGetUniformBlockIndex(program, uniformBlockName);
result = mSymbols.fGetUniformBlockIndex(program, uniformBlockName);
OnSyncCall();
AFTER_GL_CALL;
return result;
@ -3235,9 +3266,10 @@ public:
// GL3+, ES3+
const GLubyte* fGetStringi(GLenum name, GLuint index) {
const GLubyte* ret = nullptr;
BEFORE_GL_CALL;
ASSERT_SYMBOL_PRESENT(fGetStringi);
const GLubyte* ret = mSymbols.fGetStringi(name, index);
ret = mSymbols.fGetStringi(name, index);
OnSyncCall();
AFTER_GL_CALL;
return ret;
@ -3263,6 +3295,12 @@ public:
AFTER_GL_CALL;
}
#undef BEFORE_GL_CALL
#undef AFTER_GL_CALL
#undef ASSERT_SYMBOL_PRESENT
// #undef TRACKING_CONTEXT // Needed in GLContext.cpp
#undef ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL
// -----------------------------------------------------------------------------
// Constructor
protected:
@ -3285,24 +3323,14 @@ public:
protected:
typedef gfx::SurfaceFormat SurfaceFormat;
virtual bool MakeCurrentImpl(bool aForce) = 0;
public:
#ifdef MOZ_GL_DEBUG
static void StaticInit() {
PR_NewThreadPrivateIndex(&sCurrentGLContextTLS, nullptr);
}
#endif
bool MakeCurrent(bool aForce = false);
virtual bool Init() = 0;
virtual bool SetupLookupFunction() = 0;
virtual void ReleaseSurface() {}
bool IsDestroyed() {
bool IsDestroyed() const {
// MarkDestroyed will mark all these as null.
return mSymbols.fUseProgram == nullptr;
}
@ -3437,15 +3465,6 @@ protected:
GLContextSymbols mSymbols;
#ifdef MOZ_GL_DEBUG
// Non-zero debug flags will check that we don't send call
// to a GLContext that isn't current on the current
// thread.
// Store the current context when binding to thread local
// storage to support debug flags on an arbitrary thread.
static unsigned sCurrentGLContextTLS;
#endif
UniquePtr<GLBlitHelper> mBlitHelper;
UniquePtr<GLReadTexImageHelper> mReadTexImageHelper;
@ -3608,25 +3627,6 @@ public:
return mMaxSamples;
}
void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
if (mViewportRect[0] == x &&
mViewportRect[1] == y &&
mViewportRect[2] == width &&
mViewportRect[3] == height)
{
return;
}
mViewportRect[0] = x;
mViewportRect[1] = y;
mViewportRect[2] = width;
mViewportRect[3] = height;
BEFORE_GL_CALL;
mSymbols.fViewport(x, y, width, height);
AFTER_GL_CALL;
}
#undef ASSERT_SYMBOL_PRESENT
#ifdef MOZ_GL_DEBUG
void CreatedProgram(GLContext* aOrigin, GLuint aName);
void CreatedShader(GLContext* aOrigin, GLuint aName);

View File

@ -45,9 +45,9 @@ public:
NSOpenGLContext* GetNSOpenGLContext() const { return mContext; }
CGLContextObj GetCGLContext() const;
virtual bool MakeCurrentImpl(bool aForce) override;
virtual bool MakeCurrentImpl() const override;
virtual bool IsCurrent() override;
virtual bool IsCurrentImpl() const override;
virtual GLenum GetPreferredARGB32Format() const override;

View File

@ -43,9 +43,9 @@ public:
EAGLContext* GetEAGLContext() const { return mContext; }
virtual bool MakeCurrentImpl(bool aForce) override;
virtual bool MakeCurrentImpl() const override;
virtual bool IsCurrent() override;
virtual bool IsCurrentImpl() const override;
virtual bool SetupLookupFunction() override;

View File

@ -73,9 +73,9 @@ public:
return mSurfaceOverride;
}
virtual bool MakeCurrentImpl(bool aForce) override;
virtual bool MakeCurrentImpl() const override;
virtual bool IsCurrent() override;
virtual bool IsCurrentImpl() const override;
virtual bool RenewSurface(widget::CompositorWidget* aWidget) override;

View File

@ -46,9 +46,9 @@ public:
bool Init() override;
virtual bool MakeCurrentImpl(bool aForce) override;
virtual bool MakeCurrentImpl() const override;
virtual bool IsCurrent() override;
virtual bool IsCurrentImpl() const override;
virtual bool SetupLookupFunction() override;

View File

@ -110,15 +110,11 @@ GLContextCGL::GetCGLContext() const
}
bool
GLContextCGL::MakeCurrentImpl(bool aForce)
GLContextCGL::MakeCurrentImpl() const
{
if (!aForce && [NSOpenGLContext currentContext] == mContext) {
return true;
}
if (mContext) {
[mContext makeCurrentContext];
MOZ_ASSERT(IsCurrent());
MOZ_ASSERT(IsCurrentImpl());
// Use non-blocking swap in "ASAP mode".
// ASAP mode means that rendering is iterated as fast as possible.
// ASAP mode is entered when layout.frame_rate=0 (requires restart).
@ -132,7 +128,8 @@ GLContextCGL::MakeCurrentImpl(bool aForce)
}
bool
GLContextCGL::IsCurrent() {
GLContextCGL::IsCurrentImpl() const
{
return [NSOpenGLContext currentContext] == mContext;
}

View File

@ -113,12 +113,8 @@ GLContextEAGL::RecreateRB()
}
bool
GLContextEAGL::MakeCurrentImpl(bool aForce)
GLContextEAGL::MakeCurrentImpl() const
{
if (!aForce && [EAGLContext currentContext] == mContext) {
return true;
}
if (mContext) {
if(![EAGLContext setCurrentContext:mContext]) {
return false;
@ -128,7 +124,8 @@ GLContextEAGL::MakeCurrentImpl(bool aForce)
}
bool
GLContextEAGL::IsCurrent() {
GLContextEAGL::IsCurrentImpl() const
{
return [EAGLContext currentContext] == mContext;
}

View File

@ -356,34 +356,22 @@ GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) {
}
bool
GLContextEGL::MakeCurrentImpl(bool aForce) {
bool succeeded = true;
// Assume that EGL has the same problem as WGL does,
// where MakeCurrent with an already-current context is
// still expensive.
bool needsMakeCurrent = (aForce || sEGLLibrary.fGetCurrentContext() != mContext);
if (needsMakeCurrent) {
EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE
? mSurfaceOverride
: mSurface;
if (surface == EGL_NO_SURFACE) {
return false;
}
succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(),
surface, surface,
mContext);
if (!succeeded) {
int eglError = sEGLLibrary.fGetError();
if (eglError == LOCAL_EGL_CONTEXT_LOST) {
mContextLost = true;
NS_WARNING("EGL context has been lost.");
} else {
NS_WARNING("Failed to make GL context current!");
GLContextEGL::MakeCurrentImpl() const
{
const EGLSurface surface = (mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride
: mSurface;
const bool succeeded = sEGLLibrary.fMakeCurrent(EGL_DISPLAY(), surface, surface,
mContext);
if (!succeeded) {
const auto eglError = sEGLLibrary.fGetError();
if (eglError == LOCAL_EGL_CONTEXT_LOST) {
mContextLost = true;
NS_WARNING("EGL context has been lost.");
} else {
NS_WARNING("Failed to make GL context current!");
#ifdef DEBUG
printf_stderr("EGL Error: 0x%04x\n", eglError);
printf_stderr("EGL Error: 0x%04x\n", eglError);
#endif
}
}
}
@ -391,7 +379,8 @@ GLContextEGL::MakeCurrentImpl(bool aForce) {
}
bool
GLContextEGL::IsCurrent() {
GLContextEGL::IsCurrentImpl() const
{
return sEGLLibrary.fGetCurrentContext() == mContext;
}

View File

@ -606,40 +606,30 @@ GLContextGLX::Init()
}
bool
GLContextGLX::MakeCurrentImpl(bool aForce)
GLContextGLX::MakeCurrentImpl() const
{
bool succeeded = true;
// With the ATI FGLRX driver, glxMakeCurrent is very slow even when the context doesn't change.
// (This is not the case with other drivers such as NVIDIA).
// So avoid calling it more than necessary. Since GLX documentation says that:
// "glXGetCurrentContext returns client-side information.
// It does not make a round trip to the server."
// I assume that it's not worth using our own TLS slot here.
if (aForce || mGLX->fGetCurrentContext() != mContext) {
if (mGLX->IsMesa()) {
// Read into the event queue to ensure that Mesa receives a
// DRI2InvalidateBuffers event before drawing. See bug 1280653.
Unused << XPending(mDisplay);
}
succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
NS_ASSERTION(succeeded, "Failed to make GL context current!");
if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
// Many GLX implementations default to blocking until the next
// VBlank when calling glXSwapBuffers. We want to run unthrottled
// in ASAP mode. See bug 1280744.
const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0);
mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1);
}
if (mGLX->IsMesa()) {
// Read into the event queue to ensure that Mesa receives a
// DRI2InvalidateBuffers event before drawing. See bug 1280653.
Unused << XPending(mDisplay);
}
const bool succeeded = mGLX->fMakeCurrent(mDisplay, mDrawable, mContext);
NS_ASSERTION(succeeded, "Failed to make GL context current!");
if (!IsOffscreen() && mGLX->SupportsSwapControl()) {
// Many GLX implementations default to blocking until the next
// VBlank when calling glXSwapBuffers. We want to run unthrottled
// in ASAP mode. See bug 1280744.
const bool isASAP = (gfxPrefs::LayoutFrameRate() == 0);
mGLX->fSwapInterval(mDisplay, mDrawable, isASAP ? 0 : 1);
}
return succeeded;
}
bool
GLContextGLX::IsCurrent() {
GLContextGLX::IsCurrentImpl() const
{
return mGLX->fGetCurrentContext() == mContext;
}

View File

@ -323,24 +323,15 @@ GLContextWGL::Init()
}
bool
GLContextWGL::MakeCurrentImpl(bool aForce)
GLContextWGL::MakeCurrentImpl() const
{
BOOL succeeded = true;
// wglGetCurrentContext seems to just pull the HGLRC out
// of its TLS slot, so no need to do our own tls slot.
// You would think that wglMakeCurrent would avoid doing
// work if mContext was already current, but not so much..
if (aForce || sWGLLib.mSymbols.fGetCurrentContext() != mContext) {
succeeded = sWGLLib.mSymbols.fMakeCurrent(mDC, mContext);
NS_ASSERTION(succeeded, "Failed to make GL context current!");
}
const bool succeeded = sWGLLib.mSymbols.fMakeCurrent(mDC, mContext);
NS_ASSERTION(succeeded, "Failed to make GL context current!");
return succeeded;
}
bool
GLContextWGL::IsCurrent()
GLContextWGL::IsCurrentImpl() const
{
return sWGLLib.mSymbols.fGetCurrentContext() == mContext;
}

View File

@ -45,9 +45,9 @@ public:
bool Init() override;
virtual bool MakeCurrentImpl(bool aForce) override;
virtual bool MakeCurrentImpl() const override;
virtual bool IsCurrent() override;
virtual bool IsCurrentImpl() const override;
void SetIsDoubleBuffered(bool aIsDB);

View File

@ -53,6 +53,11 @@
#define LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE 0x320C
#define LOCAL_EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE 0x320F
// EGL_ANGLE_experimental_present_path
#define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE 0x33A4
#define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE 0x33A9
#define LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE 0x33AA
// EGL_ANGLE_direct3d_display
#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)

View File

@ -146,6 +146,35 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl, void* displayType)
return display;
}
static EGLDisplay
GetAndInitDisplayForWebRender(GLLibraryEGL& egl, void* displayType)
{
const EGLint attrib_list[] = { LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
LOCAL_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
LOCAL_EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
LOCAL_EGL_NONE };
EGLDisplay display = egl.fGetPlatformDisplayEXT(LOCAL_EGL_PLATFORM_ANGLE_ANGLE,
displayType,
attrib_list);
if (display == EGL_NO_DISPLAY) {
const EGLint err = egl.fGetError();
if (err != LOCAL_EGL_SUCCESS) {
gfxCriticalError() << "Unexpected GL error: " << gfx::hexa(err);
MOZ_CRASH("GFX: Unexpected GL error.");
}
return EGL_NO_DISPLAY;
}
if (!egl.fInitialize(display, nullptr, nullptr))
return EGL_NO_DISPLAY;
return display;
}
static bool
IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
nsACString* const out_failureId)
@ -241,7 +270,7 @@ GetAndInitDisplayForAccelANGLE(GLLibraryEGL& egl, nsACString* const out_failureI
EGLDisplay ret = 0;
if (wr::RenderThread::IsInRenderThread()) {
return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
return GetAndInitDisplayForWebRender(egl, EGL_DEFAULT_DISPLAY);
}
FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);

View File

@ -1,3 +1,23 @@
Overview of changes leading to 1.7.1
Tuesday, November 14, 2017
====================================
- Fix atexit object destruction regression.
- Fix minor integer-overflow.
Overview of changes leading to 1.7.0
Monday, November 13, 2017
====================================
- Minor Indic fixes.
- Implement kerning and glyph names in hb-ot-font.
- Various DSO optimization re .data and .bss sizes.
- Make C++11 optional; build fixes.
- Mark all other backends "unsafe-to-break".
- Graphite fix.
Overview of changes leading to 1.6.3
Thursday, October 26th, 2017
====================================
@ -809,7 +829,7 @@ Wednesday, July 16, 2014
U+FFFD REPLACEMENT CHARACTER now.
- With all changes in this release, the buffer will contain fully
valid Unicode after hb_buffer_add_utf8/16/32 no matter how
broken the input is. This can be overriden though. See below.
broken the input is. This can be overridden though. See below.
- Fix Mongolian Variation Selectors for fonts without GDEF.
- Fix minor invalid buffer access.
- Accept zh-Hant and zh-Hans language tags. hb_ot_tag_to_language()

View File

@ -1,5 +1,6 @@
[![Build Status](https://travis-ci.org/behdad/harfbuzz.svg)](https://travis-ci.org/behdad/harfbuzz)
[![Build Status](https://ci.appveyor.com/api/projects/status/4oaq58ns2h0m2soa?svg=true)](https://ci.appveyor.com/project/behdad/harfbuzz)
[![CircleCI](https://circleci.com/gh/behdad/harfbuzz.svg?style=svg)](https://circleci.com/gh/behdad/harfbuzz)
[![Coverage Status](https://img.shields.io/coveralls/behdad/harfbuzz.svg)](https://coveralls.io/r/behdad/harfbuzz)
[ABI Tracker](http://abi-laboratory.pro/tracker/timeline/harfbuzz/)

View File

@ -1,18 +1,18 @@
gfx/harfbuzz status as of 2017-10-26:
gfx/harfbuzz status as of 2017-11-14:
This directory contains the harfbuzz source from the 'master' branch of
This directory contains the HarfBuzz source from the 'master' branch of
https://github.com/behdad/harfbuzz.
Current version: 1.6.3
Current version: 1.7.1
UPDATING:
Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
repository. It is created at build time by the harfbuzz build system;
repository. It is created at build time by the HarfBuzz build system;
but as we don't use that build system in mozilla, it is necessary to refresh
this file when updating harfbuzz, and check it into the mozilla tree.
this file when updating HarfBuzz, and check it into the mozilla tree.
The normal approach to updating harfbuzz, therefore, is to pull the latest HB
The normal approach to updating HarfBuzz, therefore, is to pull the latest HB
source into a scratch directory and do a local build; then copy the original
sources AND the generated header mentioned above from the build directory into
the mozilla tree.

View File

@ -1,6 +1,6 @@
AC_PREREQ([2.64])
AC_INIT([HarfBuzz],
[1.6.3],
[1.7.1],
[https://github.com/behdad/harfbuzz/issues/new],
[harfbuzz],
[http://harfbuzz.org/])
@ -23,6 +23,7 @@ AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(11, noext, optional)
AC_SYS_LARGEFILE
PKG_PROG_PKG_CONFIG([0.20])
AM_MISSING_PROG([RAGEL], [ragel])
@ -80,9 +81,6 @@ if test "x$GCC" = "xyes"; then
# Make symbols link locally
LDFLAGS="$LDFLAGS -Bsymbolic-functions"
# Choose C++ version
CXXFLAGS="$CXXFLAGS -std=c++11"
# Make sure we don't link to libstdc++
CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions"
@ -167,7 +165,7 @@ dnl ===========================================================================
AC_ARG_WITH(gobject,
[AS_HELP_STRING([--with-gobject=@<:@yes/no/auto@:>@],
[Use gobject @<:@default=auto@:>@])],,
[Use gobject @<:@default=no@:>@])],,
[with_gobject=no])
have_gobject=false
if test "x$with_gobject" = "xyes" -o "x$with_gobject" = "xauto"; then

View File

@ -48,7 +48,7 @@ GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk
#
# This file knows how to handle autoconf, automake, libtool, gtk-doc,
# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata,
# appstream.
# appstream, hotdoc.
#
# This makefile provides the following targets:
#
@ -86,6 +86,7 @@ GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \
ar-lib \
compile \
config.guess \
config.rpath \
config.sub \
depcomp \
install-sh \
@ -120,6 +121,47 @@ GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \
lt~obsolete.m4 \
; do echo "$$MACRO_DIR/$$x"; done; \
fi`
#
# Modules that use gettext and use AC_CONFIG_MACRO_DIR() may also include this,
# though it's harmless to include regardless.
GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \
`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \
for x in \
codeset.m4 \
extern-inline.m4 \
fcntl-o.m4 \
gettext.m4 \
glibc2.m4 \
glibc21.m4 \
iconv.m4 \
intdiv0.m4 \
intl.m4 \
intldir.m4 \
intlmacosx.m4 \
intmax.m4 \
inttypes-pri.m4 \
inttypes_h.m4 \
lcmessage.m4 \
lib-ld.m4 \
lib-link.m4 \
lib-prefix.m4 \
lock.m4 \
longlong.m4 \
nls.m4 \
po.m4 \
printf-posix.m4 \
progtest.m4 \
size_max.m4 \
stdint_h.m4 \
threadlib.m4 \
uintmax_t.m4 \
visibility.m4 \
wchar_t.m4 \
wint_t.m4 \
xsize.m4 \
; do echo "$$MACRO_DIR/$$x"; done; \
fi`
@ -208,6 +250,15 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
"*/*.omf.out" \
; do echo /$$x; done; \
fi; \
if test "x$(HOTDOC)" = x; then :; else \
$(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \
echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \
echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \
) \
for x in \
.hotdoc.d \
; do echo "/$$x"; done; \
fi; \
if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
for lc in $(HELP_LINGUAS); do \
for x in \
@ -235,6 +286,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
fi; \
if test -f $(srcdir)/po/Makefile.in.in; then \
for x in \
ABOUT-NLS \
po/Makefile.in.in \
po/Makefile.in.in~ \
po/Makefile.in \
@ -243,6 +295,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
po/POTFILES \
po/Rules-quot \
po/stamp-it \
po/stamp-po \
po/.intltool-merge-cache \
"po/*.gmo" \
"po/*.header" \
@ -274,7 +327,7 @@ $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
if test "x$(am__dirstamp)" = x; then :; else \
echo "$(am__dirstamp)"; \
fi; \
if test "x$(LTCOMPILE)" = x -a "x$(LTCXXCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
for x in \
"*.lo" \
".libs" "_libs" \

View File

@ -34,7 +34,7 @@ grep -v 'hb-private[.]hh:' |
grep . >&2 && stat=1
echo 'Checking that there is no #include <hb.*.h>'
echo 'Checking that there is no #include <hb-*.h>'
for x in $HBHEADERS $HBSOURCES; do
test -f "$srcdir/$x" && x="$srcdir/$x"
grep '#.*\<include\>.*<.*hb' "$x" /dev/null >&2 && stat=1

View File

@ -5,7 +5,7 @@ includedir=/usr/local/include
Name: harfbuzz
Description: HarfBuzz text shaping library ICU integration
Version: 1.6.3
Version: 1.7.1
Requires: harfbuzz
Requires.private: icu-uc

View File

@ -5,7 +5,7 @@ includedir=/usr/local/include
Name: harfbuzz
Description: HarfBuzz text shaping library
Version: 1.6.3
Version: 1.7.1
Libs: -L${libdir} -lharfbuzz
Libs.private: -lm

View File

@ -89,9 +89,9 @@ typedef int32_t hb_atomic_int_impl_t;
#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
#else
#if __ppc64__ || __x86_64__ || __aarch64__
#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (void *) (O), (int64_t) (void *) (N), (int64_t*) (P))
#else
#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (void *) (O), (int32_t) (void *) (N), (int32_t*) (P))
#endif
#endif

View File

@ -30,6 +30,7 @@
#endif
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-object-private.hh"
@ -44,12 +45,6 @@
#include <errno.h>
#ifndef HB_DEBUG_BLOB
#define HB_DEBUG_BLOB (HB_DEBUG+0)
#endif
struct hb_blob_t {
hb_object_header_t header;
ASSERT_POD ();

View File

@ -305,16 +305,16 @@ struct hb_buffer_t {
info.cluster = cluster;
}
int
inline int
_unsafe_to_break_find_min_cluster (const hb_glyph_info_t *info,
unsigned int start, unsigned int end,
unsigned int cluster) const
{
for (unsigned int i = start; i < end; i++)
cluster = MIN (cluster, info[i].cluster);
cluster = MIN<unsigned int> (cluster, info[i].cluster);
return cluster;
}
void
inline void
_unsafe_to_break_set_mask (hb_glyph_info_t *info,
unsigned int start, unsigned int end,
unsigned int cluster)
@ -326,6 +326,19 @@ struct hb_buffer_t {
info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
}
}
inline void
unsafe_to_break_all (void)
{
for (unsigned int i = 0; i < len; i++)
info[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
}
inline void
safe_to_break_all (void)
{
for (unsigned int i = 0; i < len; i++)
info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
}
};

View File

@ -554,7 +554,7 @@ hb_buffer_t::merge_clusters_impl (unsigned int start,
unsigned int cluster = info[start].cluster;
for (unsigned int i = start + 1; i < end; i++)
cluster = MIN (cluster, info[i].cluster);
cluster = MIN<unsigned int> (cluster, info[i].cluster);
/* Extend end */
while (end < len && info[end - 1].cluster == info[end].cluster)
@ -585,7 +585,7 @@ hb_buffer_t::merge_out_clusters (unsigned int start,
unsigned int cluster = out_info[start].cluster;
for (unsigned int i = start + 1; i < end; i++)
cluster = MIN (cluster, out_info[i].cluster);
cluster = MIN<unsigned int> (cluster, out_info[i].cluster);
/* Extend start */
while (start && out_info[start - 1].cluster == out_info[start].cluster)

View File

@ -85,7 +85,7 @@ hb_tag_from_string (const char *str, int len)
for (; i < 4; i++)
tag[i] = ' ';
return HB_TAG_CHAR4 (tag);
return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
}
/**
@ -699,34 +699,43 @@ parse_uint32 (const char **pp, const char *end, uint32_t *pv)
#if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
#define USE_XLOCALE 1
#define HB_LOCALE_T locale_t
#define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
#define HB_FREE_LOCALE(loc) freelocale (loc)
#elif defined(_MSC_VER)
#define USE_XLOCALE 1
#define HB_LOCALE_T _locale_t
#define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
#define HB_FREE_LOCALE(loc) _free_locale (loc)
#define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
#endif
#ifdef USE_XLOCALE
static locale_t C_locale;
static HB_LOCALE_T C_locale;
#ifdef HB_USE_ATEXIT
static void
free_C_locale (void)
{
if (C_locale)
freelocale (C_locale);
HB_FREE_LOCALE (C_locale);
}
#endif
static locale_t
static HB_LOCALE_T
get_C_locale (void)
{
retry:
locale_t C = (locale_t) hb_atomic_ptr_get (&C_locale);
HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
if (unlikely (!C))
{
C = newlocale (LC_ALL_MASK, "C", nullptr);
C = HB_CREATE_LOCALE ("C");
if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
{
freelocale (C_locale);
HB_FREE_LOCALE (C_locale);
goto retry;
}

View File

@ -27,16 +27,14 @@
*/
#define HB_SHAPER coretext
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-shaper-impl-private.hh"
#include "hb-coretext.h"
#include <math.h>
#ifndef HB_DEBUG_CORETEXT
#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
#endif
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
@ -351,7 +349,9 @@ struct active_feature_t {
feature_record_t rec;
unsigned int order;
static int cmp (const active_feature_t *a, const active_feature_t *b) {
static int cmp (const void *pa, const void *pb) {
const active_feature_t *a = (const active_feature_t *) pa;
const active_feature_t *b = (const active_feature_t *) pb;
return a->rec.feature < b->rec.feature ? -1 : a->rec.feature > b->rec.feature ? 1 :
a->order < b->order ? -1 : a->order > b->order ? 1 :
a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 :
@ -367,7 +367,9 @@ struct feature_event_t {
bool start;
active_feature_t feature;
static int cmp (const feature_event_t *a, const feature_event_t *b) {
static int cmp (const void *pa, const void *pb) {
const feature_event_t *a = (const feature_event_t *) pa;
const feature_event_t *b = (const feature_event_t *) pb;
return a->index < b->index ? -1 : a->index > b->index ? 1 :
a->start < b->start ? -1 : a->start > b->start ? 1 :
active_feature_t::cmp (&a->feature, &b->feature);
@ -1260,6 +1262,8 @@ resize_and_retry:
}
}
buffer->unsafe_to_break_all ();
#undef FAIL
fail:

View File

@ -0,0 +1,419 @@
/*
* Copyright © 2017 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_DEBUG_HH
#define HB_DEBUG_HH
#include "hb-private.hh"
#ifndef HB_DEBUG
#define HB_DEBUG 0
#endif
static inline bool
_hb_debug (unsigned int level,
unsigned int max_level)
{
return level < max_level;
}
#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0))
static inline void
_hb_print_func (const char *func)
{
if (func)
{
unsigned int func_len = strlen (func);
/* Skip "static" */
if (0 == strncmp (func, "static ", 7))
func += 7;
/* Skip "typename" */
if (0 == strncmp (func, "typename ", 9))
func += 9;
/* Skip return type */
const char *space = strchr (func, ' ');
if (space)
func = space + 1;
/* Skip parameter list */
const char *paren = strchr (func, '(');
if (paren)
func_len = paren - func;
fprintf (stderr, "%.*s", func_len, func);
}
}
template <int max_level> static inline void
_hb_debug_msg_va (const char *what,
const void *obj,
const char *func,
bool indented,
unsigned int level,
int level_dir,
const char *message,
va_list ap) HB_PRINTF_FUNC(7, 0);
template <int max_level> static inline void
_hb_debug_msg_va (const char *what,
const void *obj,
const char *func,
bool indented,
unsigned int level,
int level_dir,
const char *message,
va_list ap)
{
if (!_hb_debug (level, max_level))
return;
fprintf (stderr, "%-10s", what ? what : "");
if (obj)
fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
else
fprintf (stderr, " %*s ", (unsigned int) (2 * sizeof (void *)), "");
if (indented) {
#define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */
#define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
#define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */
#define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */
#define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */
static const char bars[] =
VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR
VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR;
fprintf (stderr, "%2u %s" VRBAR "%s",
level,
bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars) - 1, (unsigned int) (sizeof (VBAR) - 1) * level),
level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR);
} else
fprintf (stderr, " " VRBAR LBAR);
_hb_print_func (func);
if (message)
{
fprintf (stderr, ": ");
vfprintf (stderr, message, ap);
}
fprintf (stderr, "\n");
}
template <> inline void
_hb_debug_msg_va<0> (const char *what HB_UNUSED,
const void *obj HB_UNUSED,
const char *func HB_UNUSED,
bool indented HB_UNUSED,
unsigned int level HB_UNUSED,
int level_dir HB_UNUSED,
const char *message HB_UNUSED,
va_list ap HB_UNUSED) {}
template <int max_level> static inline void
_hb_debug_msg (const char *what,
const void *obj,
const char *func,
bool indented,
unsigned int level,
int level_dir,
const char *message,
...) HB_PRINTF_FUNC(7, 8);
template <int max_level> static inline void
_hb_debug_msg (const char *what,
const void *obj,
const char *func,
bool indented,
unsigned int level,
int level_dir,
const char *message,
...)
{
va_list ap;
va_start (ap, message);
_hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
va_end (ap);
}
template <> inline void
_hb_debug_msg<0> (const char *what HB_UNUSED,
const void *obj HB_UNUSED,
const char *func HB_UNUSED,
bool indented HB_UNUSED,
unsigned int level HB_UNUSED,
int level_dir HB_UNUSED,
const char *message HB_UNUSED,
...) HB_PRINTF_FUNC(7, 8);
template <> inline void
_hb_debug_msg<0> (const char *what HB_UNUSED,
const void *obj HB_UNUSED,
const char *func HB_UNUSED,
bool indented HB_UNUSED,
unsigned int level HB_UNUSED,
int level_dir HB_UNUSED,
const char *message HB_UNUSED,
...) {}
#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__)
#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__)
/*
* Printer
*/
template <typename T>
struct hb_printer_t {
const char *print (const T&) { return "something"; }
};
template <>
struct hb_printer_t<bool> {
const char *print (bool v) { return v ? "true" : "false"; }
};
template <>
struct hb_printer_t<hb_void_t> {
const char *print (hb_void_t) { return ""; }
};
/*
* Trace
*/
template <typename T>
static inline void _hb_warn_no_return (bool returned)
{
if (unlikely (!returned)) {
fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n");
}
}
template <>
/*static*/ inline void _hb_warn_no_return<hb_void_t> (bool returned HB_UNUSED)
{}
template <int max_level, typename ret_t>
struct hb_auto_trace_t {
explicit inline hb_auto_trace_t (unsigned int *plevel_,
const char *what_,
const void *obj_,
const char *func,
const char *message,
...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
{
if (plevel) ++*plevel;
va_list ap;
va_start (ap, message);
_hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap);
va_end (ap);
}
inline ~hb_auto_trace_t (void)
{
_hb_warn_no_return<ret_t> (returned);
if (!returned) {
_hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " ");
}
if (plevel) --*plevel;
}
inline ret_t ret (ret_t v, unsigned int line = 0)
{
if (unlikely (returned)) {
fprintf (stderr, "OUCH, double calls to return_trace(). This is a bug, please report.\n");
return v;
}
_hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1,
"return %s (line %d)",
hb_printer_t<ret_t>().print (v), line);
if (plevel) --*plevel;
plevel = nullptr;
returned = true;
return v;
}
private:
unsigned int *plevel;
const char *what;
const void *obj;
bool returned;
};
template <typename ret_t> /* Make sure we don't use hb_auto_trace_t when not tracing. */
struct hb_auto_trace_t<0, ret_t>;
/* For disabled tracing; optimize out everything.
* https://github.com/behdad/harfbuzz/pull/605 */
template <typename ret_t>
struct hb_no_trace_t {
inline ret_t ret (ret_t v, unsigned int line HB_UNUSED = 0) { return v; }
};
#define return_trace(RET) return trace.ret (RET, __LINE__)
/*
* Instances.
*/
#ifndef HB_DEBUG_ARABIC
#define HB_DEBUG_ARABIC (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_BLOB
#define HB_DEBUG_BLOB (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_CORETEXT
#define HB_DEBUG_CORETEXT (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_DIRECTWRITE
#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_FT
#define HB_DEBUG_FT (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_GET_COVERAGE
#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_OBJECT
#define HB_DEBUG_OBJECT (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_SHAPE_PLAN
#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
#endif
#ifndef HB_DEBUG_UNISCRIBE
#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
#endif
/*
* With tracing.
*/
#ifndef HB_DEBUG_APPLY
#define HB_DEBUG_APPLY (HB_DEBUG+0)
#endif
#if HB_DEBUG_APPLY
#define TRACE_APPLY(this) \
hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"idx %d gid %u lookup %d", \
c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index)
#else
#define TRACE_APPLY(this) hb_no_trace_t<bool> trace
#endif
#ifndef HB_DEBUG_CLOSURE
#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
#endif
#if HB_DEBUG_CLOSURE
#define TRACE_CLOSURE(this) \
hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"")
#else
#define TRACE_CLOSURE(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED
#endif
#ifndef HB_DEBUG_COLLECT_GLYPHS
#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
#endif
#if HB_DEBUG_COLLECT_GLYPHS
#define TRACE_COLLECT_GLYPHS(this) \
hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"")
#else
#define TRACE_COLLECT_GLYPHS(this) hb_no_trace_t<hb_void_t> trace HB_UNUSED
#endif
#ifndef HB_DEBUG_SANITIZE
#define HB_DEBUG_SANITIZE (HB_DEBUG+0)
#endif
#if HB_DEBUG_SANITIZE
#define TRACE_SANITIZE(this) \
hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"");
#else
#define TRACE_SANITIZE(this) hb_no_trace_t<bool> trace
#endif
#ifndef HB_DEBUG_SERIALIZE
#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
#endif
#if HB_DEBUG_SERIALIZE
#define TRACE_SERIALIZE(this) \
hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
"");
#else
#define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace
#endif
#ifndef HB_DEBUG_WOULD_APPLY
#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
#endif
#if HB_DEBUG_WOULD_APPLY
#define TRACE_WOULD_APPLY(this) \
hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"%d glyphs", c->len);
#else
#define TRACE_WOULD_APPLY(this) hb_no_trace_t<bool> trace
#endif
#ifndef HB_DEBUG_DISPATCH
#define HB_DEBUG_DISPATCH ( \
HB_DEBUG_APPLY + \
HB_DEBUG_CLOSURE + \
HB_DEBUG_COLLECT_GLYPHS + \
HB_DEBUG_SANITIZE + \
HB_DEBUG_SERIALIZE + \
HB_DEBUG_WOULD_APPLY + \
0)
#endif
#if HB_DEBUG_DISPATCH
#define TRACE_DISPATCH(this, format) \
hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"format %d", (int) format);
#else
#define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
#endif
#endif /* HB_DEBUG_HH */

View File

@ -22,6 +22,8 @@
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include "hb-private.hh"
#include "hb-debug.hh"
#define HB_SHAPER directwrite
#include "hb-shaper-impl-private.hh"
@ -30,10 +32,6 @@
#include "hb-directwrite.h"
#ifndef HB_DEBUG_DIRECTWRITE
#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0)
#endif
HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, face)
HB_SHAPER_DATA_ENSURE_DEFINE(directwrite, font)
@ -929,8 +927,7 @@ hb_directwrite_shape_experimental_width(hb_font_t *font,
hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
features, num_features, width);
if (res)
buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
buffer->unsafe_to_break_all ();
return res;
}

View File

@ -0,0 +1,161 @@
/*
* Copyright © 2017 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_DSALGS_HH
#define HB_DSALGS_HH
#include "hb-private.hh"
static inline void *
hb_bsearch_r (const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *_key, const void *_item, void *_arg),
void *arg)
{
int min = 0, max = (int) nmemb - 1;
while (min <= max)
{
int mid = (min + max) / 2;
const void *p = (const void *) (((const char *) base) + (mid * size));
int c = compar (key, p, arg);
if (c < 0)
max = mid - 1;
else if (c > 0)
min = mid + 1;
else
return (void *) p;
}
return NULL;
}
/* From https://github.com/noporpoise/sort_r */
/* Isaac Turner 29 April 2014 Public Domain */
/*
hb_sort_r function to be exported.
Parameters:
base is the array to be sorted
nel is the number of elements in the array
width is the size in bytes of each element of the array
compar is the comparison function
arg is a pointer to be passed to the comparison function
void hb_sort_r(void *base, size_t nel, size_t width,
int (*compar)(const void *_a, const void *_b, void *_arg),
void *arg);
*/
/* swap a, b iff a>b */
/* __restrict is same as restrict but better support on old machines */
static int sort_r_cmpswap(char *__restrict a, char *__restrict b, size_t w,
int (*compar)(const void *_a, const void *_b,
void *_arg),
void *arg)
{
char tmp, *end = a+w;
if(compar(a, b, arg) > 0) {
for(; a < end; a++, b++) { tmp = *a; *a = *b; *b = tmp; }
return 1;
}
return 0;
}
/* Note: quicksort is not stable, equivalent values may be swapped */
static inline void sort_r_simple(void *base, size_t nel, size_t w,
int (*compar)(const void *_a, const void *_b,
void *_arg),
void *arg)
{
char *b = (char *)base, *end = b + nel*w;
if(nel < 7) {
/* Insertion sort for arbitrarily small inputs */
char *pi, *pj;
for(pi = b+w; pi < end; pi += w) {
for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,arg); pj -= w) {}
}
}
else
{
/* nel > 6; Quicksort */
/* Use median of first, middle and last items as pivot */
char *x, *y, *xend, ch;
char *pl, *pr;
char *last = b+w*(nel-1), *tmp;
char *l[3];
l[0] = b;
l[1] = b+w*(nel/2);
l[2] = last;
if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; }
if(compar(l[1],l[2],arg) > 0) {
tmp=l[1]; l[1]=l[2]; l[2]=tmp; /* swap(l[1],l[2]) */
if(compar(l[0],l[1],arg) > 0) { tmp=l[0]; l[0]=l[1]; l[1]=tmp; }
}
/* swap l[id], l[2] to put pivot as last element */
for(x = l[1], y = last, xend = x+w; x<xend; x++, y++) {
ch = *x; *x = *y; *y = ch;
}
pl = b;
pr = last;
while(pl < pr) {
for(; pl < pr; pl += w) {
if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
pr -= w; /* pivot now at pl */
break;
}
}
for(; pl < pr; pr -= w) {
if(sort_r_cmpswap(pl, pr, w, compar, arg)) {
pl += w; /* pivot now at pr */
break;
}
}
}
sort_r_simple(b, (pl-b)/w, w, compar, arg);
sort_r_simple(pl+w, (end-(pl+w))/w, w, compar, arg);
}
}
static inline void hb_sort_r(void *base, size_t nel, size_t width,
int (*compar)(const void *_a, const void *_b, void *_arg),
void *arg)
{
sort_r_simple(base, nel, width, compar, arg);
}
#endif /* HB_DSALGS_HH */

View File

@ -143,5 +143,7 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
if (HB_DIRECTION_IS_BACKWARD (direction))
hb_buffer_reverse (buffer);
buffer->safe_to_break_all ();
return true;
}

View File

@ -28,6 +28,7 @@
*/
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-ft.h"
@ -38,12 +39,6 @@
#include FT_TRUETYPE_TABLES_H
#ifndef HB_DEBUG_FT
#define HB_DEBUG_FT (HB_DEBUG+0)
#endif
/* TODO:
*
* In general, this file does a fine job of what it's supposed to do.

View File

@ -364,22 +364,44 @@ hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
return utf8_decomposed_len;
}
static hb_unicode_funcs_t *static_glib_funcs = nullptr;
#ifdef HB_USE_ATEXIT
static
void free_static_glib_funcs (void)
{
hb_unicode_funcs_destroy (static_glib_funcs);
}
#endif
hb_unicode_funcs_t *
hb_glib_get_unicode_funcs (void)
{
static const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
HB_OBJECT_HEADER_STATIC,
retry:
hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_glib_funcs);
nullptr, /* parent */
true, /* immutable */
{
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_glib_unicode_##name,
if (unlikely (!funcs))
{
funcs = hb_unicode_funcs_create (nullptr);
#define HB_UNICODE_FUNC_IMPLEMENT(name) \
hb_unicode_funcs_set_##name##_func (funcs, hb_glib_unicode_##name, nullptr, nullptr);
HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
hb_unicode_funcs_make_immutable (funcs);
if (!hb_atomic_ptr_cmpexch (&static_glib_funcs, nullptr, funcs)) {
hb_unicode_funcs_destroy (funcs);
goto retry;
}
#ifdef HB_USE_ATEXIT
atexit (free_static_glib_funcs); /* First person registers atexit() callback. */
#endif
};
return const_cast<hb_unicode_funcs_t *> (&_hb_glib_unicode_funcs);
return hb_unicode_funcs_reference (funcs);
}
#if GLIB_CHECK_VERSION(2,31,10)

View File

@ -307,6 +307,8 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan,
curradv = gr_slot_origin_X(gr_seg_first_slot(seg));
clusters[0].advance = gr_seg_advance_X(seg) - curradv;
}
else
clusters[0].advance = 0;
for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
{
unsigned int before = gr_slot_before (is);
@ -332,7 +334,10 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan,
if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
c->advance = curradv - gr_slot_origin_X(is);
else
clusters[ci].advance = gr_slot_origin_X(is) - curradv;
{
c->advance = 0;
clusters[ci].advance += gr_slot_origin_X(is) - curradv;
}
ci++;
curradv = gr_slot_origin_X(is);
}
@ -345,7 +350,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan,
if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction))
clusters[ci].advance += curradv;
else
clusters[ci].advance = gr_seg_advance_X(seg) - curradv;
clusters[ci].advance += gr_seg_advance_X(seg) - curradv;
ci++;
for (unsigned int i = 0; i < ci; ++i)
@ -411,5 +416,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan,
if (feats) gr_featureval_destroy (feats);
gr_seg_destroy (seg);
buffer->unsafe_to_break_all ();
return true;
}

View File

@ -345,27 +345,50 @@ hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
}
static hb_unicode_funcs_t *static_icu_funcs = nullptr;
#ifdef HB_USE_ATEXIT
static
void free_static_icu_funcs (void)
{
hb_unicode_funcs_destroy (static_icu_funcs);
}
#endif
hb_unicode_funcs_t *
hb_icu_get_unicode_funcs (void)
{
static const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
HB_OBJECT_HEADER_STATIC,
retry:
hb_unicode_funcs_t *funcs = (hb_unicode_funcs_t *) hb_atomic_ptr_get (&static_icu_funcs);
nullptr, /* parent */
true, /* immutable */
{
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_icu_unicode_##name,
if (unlikely (!funcs))
{
#if U_ICU_VERSION_MAJOR_NUM >= 49
if (!hb_atomic_ptr_get (&normalizer)) {
UErrorCode icu_err = U_ZERO_ERROR;
/* We ignore failure in getNFCInstace(). */
(void) hb_atomic_ptr_cmpexch (&normalizer, nullptr, unorm2_getNFCInstance (&icu_err));
}
#endif
funcs = hb_unicode_funcs_create (nullptr);
#define HB_UNICODE_FUNC_IMPLEMENT(name) \
hb_unicode_funcs_set_##name##_func (funcs, hb_icu_unicode_##name, nullptr, nullptr);
HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
#undef HB_UNICODE_FUNC_IMPLEMENT
hb_unicode_funcs_make_immutable (funcs);
if (!hb_atomic_ptr_cmpexch (&static_icu_funcs, nullptr, funcs)) {
hb_unicode_funcs_destroy (funcs);
goto retry;
}
#ifdef HB_USE_ATEXIT
atexit (free_static_icu_funcs); /* First person registers atexit() callback. */
#endif
};
#if U_ICU_VERSION_MAJOR_NUM >= 49
if (!hb_atomic_ptr_get (&normalizer)) {
UErrorCode icu_err = U_ZERO_ERROR;
/* We ignore failure in getNFCInstace(). */
(void) hb_atomic_ptr_cmpexch (&normalizer, nullptr, unorm2_getNFCInstance (&icu_err));
}
#endif
return const_cast<hb_unicode_funcs_t *> (&_hb_icu_unicode_funcs);
return hb_unicode_funcs_reference (funcs);
}

View File

@ -33,18 +33,12 @@
#define HB_OBJECT_PRIVATE_HH
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-atomic-private.hh"
#include "hb-mutex-private.hh"
/* Debug */
#ifndef HB_DEBUG_OBJECT
#define HB_DEBUG_OBJECT (HB_DEBUG+0)
#endif
/* reference_count */
#define HB_REFERENCE_COUNT_INERT_VALUE -1

View File

@ -53,6 +53,9 @@ struct TTCHeader;
typedef struct TableRecord
{
int cmp (Tag t) const
{ return t.cmp (tag); }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -73,10 +76,9 @@ typedef struct OffsetTable
friend struct OpenTypeFontFile;
inline unsigned int get_table_count (void) const
{ return numTables; }
{ return tables.len; }
inline const TableRecord& get_table (unsigned int i) const
{
if (unlikely (i >= numTables)) return Null(TableRecord);
return tables[i];
}
inline unsigned int get_table_tags (unsigned int start_offset,
@ -85,33 +87,28 @@ typedef struct OffsetTable
{
if (table_count)
{
if (start_offset >= numTables)
if (start_offset >= tables.len)
*table_count = 0;
else
*table_count = MIN (*table_count, numTables - start_offset);
*table_count = MIN<unsigned int> (*table_count, tables.len - start_offset);
const TableRecord *sub_tables = tables + start_offset;
const TableRecord *sub_tables = tables.array + start_offset;
unsigned int count = *table_count;
for (unsigned int i = 0; i < count; i++)
table_tags[i] = sub_tables[i].tag;
}
return numTables;
return tables.len;
}
inline bool find_table_index (hb_tag_t tag, unsigned int *table_index) const
{
Tag t;
t.set (tag);
unsigned int count = numTables;
for (unsigned int i = 0; i < count; i++)
{
if (t == tables[i].tag)
{
if (table_index) *table_index = i;
return true;
}
}
if (table_index) *table_index = Index::NOT_FOUND_INDEX;
return false;
/* Linear-search for small tables to work around fonts with unsorted
* table list. */
int i = tables.len < 64 ? tables.lsearch (t) : tables.bsearch (t);
if (table_index)
*table_index = i == -1 ? Index::NOT_FOUND_INDEX : (unsigned int) i;
return i != -1;
}
inline const TableRecord& get_table_by_tag (hb_tag_t tag) const
{
@ -124,16 +121,13 @@ typedef struct OffsetTable
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
return_trace (c->check_struct (this) && tables.sanitize (c));
}
protected:
Tag sfnt_version; /* '\0\001\0\00' if TrueType / 'OTTO' if CFF */
USHORT numTables; /* Number of tables. */
USHORT searchRangeZ; /* (Maximum power of 2 <= numTables) x 16 */
USHORT entrySelectorZ; /* Log2(maximum power of 2 <= numTables). */
USHORT rangeShiftZ; /* NumTables x 16-searchRange. */
TableRecord tables[VAR]; /* TableRecord entries. numTables items */
BinSearchArrayOf<TableRecord>
tables;
public:
DEFINE_SIZE_ARRAY (12, tables);
} OpenTypeFontFace;

View File

@ -30,6 +30,7 @@
#define HB_OPEN_TYPE_PRIVATE_HH
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-face-private.hh"
@ -130,14 +131,16 @@ static inline Type& StructAfter(TObject &X)
*/
/* Global nul-content Null pool. Enlarge as necessary. */
/* TODO This really should be a extern HB_INTERNAL and defined somewhere... */
static const void *_NullPool[(256+8) / sizeof (void *)];
#define HB_NULL_POOL_SIZE 264
static_assert (HB_NULL_POOL_SIZE % sizeof (void *) == 0, "Align HB_NULL_POOL_SIZE.");
extern HB_INTERNAL const void * const _hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)];
/* Generic nul-content Null objects. */
template <typename Type>
static inline const Type& Null (void) {
static_assert ((sizeof (Type) <= sizeof (_NullPool)), "");
return *CastP<Type> (_NullPool);
static_assert (sizeof (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE.");
return *CastP<Type> (_hb_NullPool);
}
/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
@ -172,16 +175,6 @@ struct hb_dispatch_context_t
* Sanitize
*/
#ifndef HB_DEBUG_SANITIZE
#define HB_DEBUG_SANITIZE (HB_DEBUG+0)
#endif
#define TRACE_SANITIZE(this) \
hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"");
/* This limits sanitizing time on really broken fonts. */
#ifndef HB_SANITIZE_MAX_EDITS
#define HB_SANITIZE_MAX_EDITS 32
@ -385,16 +378,6 @@ struct Sanitizer
* Serialize
*/
#ifndef HB_DEBUG_SERIALIZE
#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
#endif
#define TRACE_SERIALIZE(this) \
hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \
(&c->debug_depth, "SERIALIZE", c, HB_FUNC, \
"");
struct hb_serialize_context_t
{
@ -632,10 +615,11 @@ struct IntType
inline bool operator == (const IntType<Type,Size> &o) const { return (Type) v == (Type) o.v; }
inline bool operator != (const IntType<Type,Size> &o) const { return !(*this == o); }
static inline int cmp (const IntType<Type,Size> *a, const IntType<Type,Size> *b) { return b->cmp (*a); }
inline int cmp (Type a) const
template <typename Type2>
inline int cmp (Type2 a) const
{
Type b = v;
if (sizeof (Type) < sizeof (int))
if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int))
return (int) a - (int) b;
else
return a < b ? -1 : a == b ? 0 : +1;
@ -651,9 +635,9 @@ struct IntType
DEFINE_SIZE_STATIC (Size);
};
typedef IntType<int8_t , 1> CHAR; /* 8-bit signed integer. */
typedef IntType<uint8_t , 1> BYTE; /* 8-bit unsigned integer. */
typedef IntType<int8_t , 1> INT8; /* 8-bit signed integer. */
typedef IntType<int8_t, 1> CHAR; /* 8-bit signed integer. */
typedef IntType<uint8_t, 1> BYTE; /* 8-bit unsigned integer. */
typedef IntType<int8_t, 1> INT8; /* 8-bit signed integer. */
typedef IntType<uint16_t, 2> USHORT; /* 16-bit unsigned integer. */
typedef IntType<int16_t, 2> SHORT; /* 16-bit signed integer. */
typedef IntType<uint32_t, 4> ULONG; /* 32-bit unsigned integer. */
@ -713,10 +697,7 @@ struct Tag : ULONG
DEFINE_NULL_DATA (Tag, " ");
/* Glyph index number, same as uint16 (length = 16 bits) */
struct GlyphID : USHORT {
static inline int cmp (const GlyphID *a, const GlyphID *b) { return b->USHORT::cmp (*a); }
inline int cmp (hb_codepoint_t a) const { return (int) a - (int) *this; }
};
typedef USHORT GlyphID;
/* Script/language-system/feature index */
struct Index : USHORT {
@ -943,7 +924,7 @@ struct ArrayOf
inline bool sanitize_shallow (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && c->check_array (array, Type::static_size, len));
return_trace (len.sanitize (c) && c->check_array (array, Type::static_size, len));
}
public:
@ -1009,12 +990,6 @@ struct HeadlessArrayOf
return_trace (true);
}
inline bool sanitize_shallow (hb_sanitize_context_t *c) const
{
return c->check_struct (this)
&& c->check_array (this, Type::static_size, len);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -1032,6 +1007,15 @@ struct HeadlessArrayOf
return_trace (true);
}
private:
inline bool sanitize_shallow (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (len.sanitize (c) &&
(!len || c->check_array (array, Type::static_size, len - 1)));
}
public:
LenType len;
Type array[VAR];
public:
@ -1039,7 +1023,9 @@ struct HeadlessArrayOf
};
/* An array with sorted elements. Supports binary searching. */
/*
* An array with sorted elements. Supports binary searching.
*/
template <typename Type, typename LenType=USHORT>
struct SortedArrayOf : ArrayOf<Type, LenType>
{
@ -1064,6 +1050,33 @@ struct SortedArrayOf : ArrayOf<Type, LenType>
}
};
/*
* Binary-search arrays
*/
struct BinSearchHeader
{
inline operator uint32_t (void) const { return len; }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
protected:
USHORT len;
USHORT searchRangeZ;
USHORT entrySelectorZ;
USHORT rangeShiftZ;
public:
DEFINE_SIZE_STATIC (8);
};
template <typename Type>
struct BinSearchArrayOf : SortedArrayOf<Type, BinSearchHeader> {};
/* Lazy struct and blob loaders. */

View File

@ -36,9 +36,10 @@
#include "hb-ot-head-table.hh"
#include "hb-ot-hhea-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-kern-table.hh"
#include "hb-ot-os2-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-ot-post-table.hh"
#include "hb-ot-var-hvar-table.hh"
struct hb_ot_face_metrics_accelerator_t
@ -304,25 +305,24 @@ struct hb_ot_face_cbdt_accelerator_t
struct hb_ot_face_post_accelerator_t
{
hb_blob_t *post_blob;
unsigned int post_len;
const OT::post *post;
OT::post::accelerator_t accel;
inline void init (hb_face_t *face)
{
this->post_blob = OT::Sanitizer<OT::post>::sanitize (face->reference_table (HB_OT_TAG_post));
this->post = OT::Sanitizer<OT::post>::lock_instance (this->post_blob);
this->post_len = hb_blob_get_length (this->post_blob);
hb_blob_t *blob = this->post_blob = OT::Sanitizer<OT::post>::sanitize (face->reference_table (HB_OT_TAG_post));
accel.init (OT::Sanitizer<OT::post>::lock_instance (blob), hb_blob_get_length (blob));
}
inline void fini (void)
{
accel.fini ();
hb_blob_destroy (this->post_blob);
}
inline bool get_glyph_name (hb_codepoint_t glyph,
char *name, unsigned int size) const
{
return this->post->get_glyph_name (glyph, name, size, this->post_len);
return this->accel.get_glyph_name (glyph, name, size);
}
inline bool get_glyph_from_name (const char *name, int len,
@ -331,10 +331,31 @@ struct hb_ot_face_post_accelerator_t
if (unlikely (!len))
return false;
return this->post->get_glyph_from_name (name, len, glyph, this->post_len);
return this->accel.get_glyph_from_name (name, len, glyph);
}
};
struct hb_ot_face_kern_accelerator_t
{
hb_blob_t *kern_blob;
OT::kern::accelerator_t accel;
inline void init (hb_face_t *face)
{
hb_blob_t *blob = this->kern_blob = OT::Sanitizer<OT::kern>::sanitize (face->reference_table (HB_OT_TAG_kern));
accel.init (OT::Sanitizer<OT::kern>::lock_instance (blob), hb_blob_get_length (blob));
}
inline void fini (void)
{
accel.fini ();
hb_blob_destroy (this->kern_blob);
}
inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ return accel.get_h_kerning (left, right); }
};
typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
hb_codepoint_t codepoint,
hb_codepoint_t *glyph);
@ -471,6 +492,7 @@ struct hb_ot_font_t
OT::hb_lazy_loader_t<hb_ot_face_glyf_accelerator_t> glyf;
OT::hb_lazy_loader_t<hb_ot_face_cbdt_accelerator_t> cbdt;
OT::hb_lazy_loader_t<hb_ot_face_post_accelerator_t> post;
OT::hb_lazy_loader_t<hb_ot_face_kern_accelerator_t> kern;
};
@ -489,6 +511,7 @@ _hb_ot_font_create (hb_face_t *face)
ot_font->glyf.init (face);
ot_font->cbdt.init (face);
ot_font->post.init (face);
ot_font->kern.init (face);
return ot_font;
}
@ -504,6 +527,7 @@ _hb_ot_font_destroy (void *data)
ot_font->glyf.fini ();
ot_font->cbdt.fini ();
ot_font->post.fini ();
ot_font->kern.fini ();
free (ot_font);
}
@ -553,6 +577,17 @@ hb_ot_get_glyph_v_advance (hb_font_t *font,
return font->em_scale_y (-(int) ot_font->v_metrics.get_advance (glyph, font));
}
static hb_position_t
hb_ot_get_glyph_h_kerning (hb_font_t *font,
void *font_data,
hb_codepoint_t left_glyph,
hb_codepoint_t right_glyph,
void *user_data HB_UNUSED)
{
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
return font->em_scale_x (ot_font->kern->get_h_kerning (left_glyph, right_glyph));
}
static hb_bool_t
hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED,
void *font_data,
@ -650,7 +685,7 @@ retry:
hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ot_get_glyph_v_advance, nullptr, nullptr);
//hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr);
//hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
//hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr); TODO
hb_font_funcs_set_glyph_h_kerning_func (funcs, hb_ot_get_glyph_h_kerning, nullptr, nullptr);
//hb_font_funcs_set_glyph_v_kerning_func (funcs, hb_ot_get_glyph_v_kerning, nullptr, nullptr);
hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr);
//hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr); TODO

View File

@ -0,0 +1,389 @@
/*
* Copyright © 2017 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_OT_KERN_TABLE_HH
#define HB_OT_KERN_TABLE_HH
#include "hb-open-type-private.hh"
namespace OT {
/*
* kern -- Kerning
*/
#define HB_OT_TAG_kern HB_TAG('k','e','r','n')
struct hb_glyph_pair_t
{
hb_codepoint_t left;
hb_codepoint_t right;
};
struct KernPair
{
inline int get_kerning (void) const
{ return value; }
inline int cmp (const hb_glyph_pair_t &o) const
{
int ret = left.cmp (o.left);
if (ret) return ret;
return right.cmp (o.right);
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
protected:
GlyphID left;
GlyphID right;
FWORD value;
public:
DEFINE_SIZE_STATIC (6);
};
struct KernSubTableFormat0
{
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{
hb_glyph_pair_t pair = {left, right};
int i = pairs.bsearch (pair);
if (i == -1)
return 0;
return pairs[i].get_kerning ();
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (pairs.sanitize (c));
}
protected:
BinSearchArrayOf<KernPair> pairs; /* Array of kerning pairs. */
public:
DEFINE_SIZE_ARRAY (8, pairs);
};
struct KernClassTable
{
inline unsigned int get_class (hb_codepoint_t g) const { return classes[g - firstGlyph]; }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (firstGlyph.sanitize (c) && classes.sanitize (c));
}
protected:
USHORT firstGlyph; /* First glyph in class range. */
ArrayOf<USHORT> classes; /* Glyph classes. */
public:
DEFINE_SIZE_ARRAY (4, classes);
};
struct KernSubTableFormat2
{
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
{
unsigned int l = (this+leftClassTable).get_class (left);
unsigned int r = (this+leftClassTable).get_class (left);
unsigned int offset = l * rowWidth + r * sizeof (FWORD);
const FWORD *arr = &(this+array);
if (unlikely ((const void *) arr < (const void *) this || (const void *) arr >= (const void *) end))
return 0;
const FWORD *v = &StructAtOffset<FWORD> (arr, offset);
if (unlikely ((const void *) v < (const void *) arr || (const void *) (v + 1) > (const void *) end))
return 0;
return *v;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (rowWidth.sanitize (c) &&
leftClassTable.sanitize (c, this) &&
rightClassTable.sanitize (c, this) &&
array.sanitize (c, this));
}
protected:
USHORT rowWidth; /* The width, in bytes, of a row in the table. */
OffsetTo<KernClassTable>
leftClassTable; /* Offset from beginning of this subtable to
* left-hand class table. */
OffsetTo<KernClassTable>
rightClassTable;/* Offset from beginning of this subtable to
* right-hand class table. */
OffsetTo<FWORD>
array; /* Offset from beginning of this subtable to
* the start of the kerning array. */
public:
DEFINE_SIZE_MIN (8);
};
struct KernSubTable
{
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end, unsigned int format) const
{
switch (format) {
case 0: return u.format0.get_kerning (left, right);
case 2: return u.format2.get_kerning (left, right, end);
default:return 0;
}
}
inline bool sanitize (hb_sanitize_context_t *c, unsigned int format) const
{
TRACE_SANITIZE (this);
switch (format) {
case 0: return_trace (u.format0.sanitize (c));
case 2: return_trace (u.format2.sanitize (c));
default:return_trace (true);
}
}
protected:
union {
KernSubTableFormat0 format0;
KernSubTableFormat2 format2;
} u;
public:
DEFINE_SIZE_MIN (0);
};
template <typename T>
struct KernSubTableWrapper
{
/* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
inline const T* thiz (void) const { return static_cast<const T *> (this); }
inline bool is_horizontal (void) const
{ return (thiz()->coverage & T::COVERAGE_CHECK_FLAGS) == T::COVERAGE_CHECK_HORIZONTAL; }
inline bool is_override (void) const
{ return bool (thiz()->coverage & T::COVERAGE_OVERRIDE_FLAG); }
inline int get_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
{ return thiz()->subtable.get_kerning (left, right, end, thiz()->format); }
inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, const char *end) const
{ return is_horizontal () ? get_kerning (left, right, end) : 0; }
inline unsigned int get_size (void) const { return thiz()->length; }
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (thiz()) &&
thiz()->length >= thiz()->min_size &&
c->check_array (thiz(), 1, thiz()->length) &&
thiz()->subtable.sanitize (c, thiz()->format));
}
};
template <typename T>
struct KernTable
{
/* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */
inline const T* thiz (void) const { return static_cast<const T *> (this); }
inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const
{
int v = 0;
const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (thiz()->data);
unsigned int count = thiz()->nTables;
for (unsigned int i = 0; i < count; i++)
{
if (st->is_override ())
v = 0;
v += st->get_h_kerning (left, right, table_length + (const char *) this);
st = &StructAfter<typename T::SubTableWrapper> (*st);
}
return v;
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (unlikely (!c->check_struct (thiz()) ||
thiz()->version != T::VERSION))
return_trace (false);
const typename T::SubTableWrapper *st = CastP<typename T::SubTableWrapper> (thiz()->data);
unsigned int count = thiz()->nTables;
for (unsigned int i = 0; i < count; i++)
{
if (unlikely (!st->sanitize (c)))
return_trace (false);
st = &StructAfter<typename T::SubTableWrapper> (*st);
}
return_trace (true);
}
};
struct KernOT : KernTable<KernOT>
{
friend struct KernTable<KernOT>;
static const uint16_t VERSION = 0x0000u;
struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
{
friend struct KernSubTableWrapper<SubTableWrapper>;
enum coverage_flags_t {
COVERAGE_DIRECTION_FLAG = 0x01u,
COVERAGE_MINIMUM_FLAG = 0x02u,
COVERAGE_CROSSSTREAM_FLAG = 0x04u,
COVERAGE_OVERRIDE_FLAG = 0x08u,
COVERAGE_VARIATION_FLAG = 0x00u, /* Not supported. */
COVERAGE_CHECK_FLAGS = 0x07u,
COVERAGE_CHECK_HORIZONTAL = 0x01u
};
protected:
USHORT versionZ; /* Unused. */
USHORT length; /* Length of the subtable (including this header). */
BYTE format; /* Subtable format. */
BYTE coverage; /* Coverage bits. */
KernSubTable subtable; /* Subtable data. */
public:
DEFINE_SIZE_MIN (6);
};
protected:
USHORT version; /* Version--0x0000u */
USHORT nTables; /* Number of subtables in the kerning table. */
BYTE data[VAR];
public:
DEFINE_SIZE_ARRAY (4, data);
};
struct KernAAT : KernTable<KernAAT>
{
friend struct KernTable<KernAAT>;
static const uint32_t VERSION = 0x00010000u;
struct SubTableWrapper : KernSubTableWrapper<SubTableWrapper>
{
friend struct KernSubTableWrapper<SubTableWrapper>;
enum coverage_flags_t {
COVERAGE_DIRECTION_FLAG = 0x80u,
COVERAGE_CROSSSTREAM_FLAG = 0x40u,
COVERAGE_VARIATION_FLAG = 0x20u,
COVERAGE_OVERRIDE_FLAG = 0x00u, /* Not supported. */
COVERAGE_CHECK_FLAGS = 0xE0u,
COVERAGE_CHECK_HORIZONTAL = 0x00u
};
protected:
ULONG length; /* Length of the subtable (including this header). */
BYTE coverage; /* Coverage bits. */
BYTE format; /* Subtable format. */
USHORT tupleIndex; /* The tuple index (used for variations fonts).
* This value specifies which tuple this subtable covers. */
KernSubTable subtable; /* Subtable data. */
public:
DEFINE_SIZE_MIN (8);
};
protected:
ULONG version; /* Version--0x00010000u */
ULONG nTables; /* Number of subtables in the kerning table. */
BYTE data[VAR];
public:
DEFINE_SIZE_ARRAY (8, data);
};
struct kern
{
static const hb_tag_t tableTag = HB_OT_TAG_kern;
inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right, unsigned int table_length) const
{
switch (u.major) {
case 0: return u.ot.get_h_kerning (left, right, table_length);
case 1: return u.aat.get_h_kerning (left, right, table_length);
default:return 0;
}
}
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
if (!u.major.sanitize (c)) return_trace (false);
switch (u.major) {
case 0: return_trace (u.ot.sanitize (c));
case 1: return_trace (u.aat.sanitize (c));
default:return_trace (true);
}
}
struct accelerator_t
{
inline void init (const kern *table_, unsigned int table_length_)
{
table = table_;
table_length = table_length_;
}
inline void fini (void) {}
inline int get_h_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ return table->get_h_kerning (left, right, table_length); }
private:
const kern *table;
unsigned int table_length;
};
protected:
union {
USHORT major;
KernOT ot;
KernAAT aat;
} u;
public:
DEFINE_SIZE_UNION (2, major);
};
} /* namespace OT */
#endif /* HB_OT_KERN_TABLE_HH */

View File

@ -29,6 +29,8 @@
#ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
#define HB_OT_LAYOUT_COMMON_PRIVATE_HH
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-ot-layout-private.hh"
#include "hb-open-type-private.hh"
#include "hb-set-private.hh"
@ -45,12 +47,6 @@
namespace OT {
#define TRACE_DISPATCH(this, format) \
hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"format %d", (int) format);
#define NOT_COVERED ((unsigned int) -1)

View File

@ -29,6 +29,8 @@
#ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-buffer-private.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-set-private.hh"
@ -37,15 +39,6 @@
namespace OT {
#ifndef HB_DEBUG_CLOSURE
#define HB_DEBUG_CLOSURE (HB_DEBUG+0)
#endif
#define TRACE_CLOSURE(this) \
hb_auto_trace_t<HB_DEBUG_CLOSURE, hb_void_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"");
struct hb_closure_context_t :
hb_dispatch_context_t<hb_closure_context_t, hb_void_t, HB_DEBUG_CLOSURE>
{
@ -85,16 +78,6 @@ struct hb_closure_context_t :
};
#ifndef HB_DEBUG_WOULD_APPLY
#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
#endif
#define TRACE_WOULD_APPLY(this) \
hb_auto_trace_t<HB_DEBUG_WOULD_APPLY, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"%d glyphs", c->len);
struct hb_would_apply_context_t :
hb_dispatch_context_t<hb_would_apply_context_t, bool, HB_DEBUG_WOULD_APPLY>
{
@ -122,16 +105,6 @@ struct hb_would_apply_context_t :
};
#ifndef HB_DEBUG_COLLECT_GLYPHS
#define HB_DEBUG_COLLECT_GLYPHS (HB_DEBUG+0)
#endif
#define TRACE_COLLECT_GLYPHS(this) \
hb_auto_trace_t<HB_DEBUG_COLLECT_GLYPHS, hb_void_t> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"");
struct hb_collect_glyphs_context_t :
hb_dispatch_context_t<hb_collect_glyphs_context_t, hb_void_t, HB_DEBUG_COLLECT_GLYPHS>
{
@ -219,10 +192,6 @@ struct hb_collect_glyphs_context_t :
#ifndef HB_DEBUG_GET_COVERAGE
#define HB_DEBUG_GET_COVERAGE (HB_DEBUG+0)
#endif
/* XXX Can we remove this? */
template <typename set_t>
@ -249,17 +218,6 @@ struct hb_add_coverage_context_t :
};
#ifndef HB_DEBUG_APPLY
#define HB_DEBUG_APPLY (HB_DEBUG+0)
#endif
#define TRACE_APPLY(this) \
hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \
(&c->debug_depth, c->get_name (), this, HB_FUNC, \
"idx %d gid %u lookup %d", \
c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index);
struct hb_apply_context_t :
hb_dispatch_context_t<hb_apply_context_t, bool, HB_DEBUG_APPLY>
{
@ -1247,11 +1205,11 @@ struct Rule
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return inputCount.sanitize (c)
&& lookupCount.sanitize (c)
&& c->check_range (inputZ,
inputZ[0].static_size * inputCount
+ lookupRecordX[0].static_size * lookupCount);
return_trace (inputCount.sanitize (c) &&
lookupCount.sanitize (c) &&
c->check_range (inputZ,
inputZ[0].static_size * inputCount +
lookupRecordX[0].static_size * lookupCount));
}
protected:

View File

@ -35,10 +35,14 @@
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-name-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-map-private.hh"
const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
hb_ot_layout_t *
_hb_ot_layout_create (hb_face_t *face)
{

View File

@ -63,8 +63,12 @@ struct hb_ot_map_t
unsigned short auto_zwj : 1;
hb_mask_t mask;
static int cmp (const lookup_map_t *a, const lookup_map_t *b)
{ return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
static int cmp (const void *pa, const void *pb)
{
const lookup_map_t *a = (const lookup_map_t *) pa;
const lookup_map_t *b = (const lookup_map_t *) pb;
return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
}
};
typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer);
@ -210,9 +214,13 @@ struct hb_ot_map_builder_t
unsigned int default_value; /* for non-global features, what should the unset glyphs take */
unsigned int stage[2]; /* GSUB/GPOS */
static int cmp (const feature_info_t *a, const feature_info_t *b)
{ return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) :
(a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); }
static int cmp (const void *pa, const void *pb)
{
const feature_info_t *a = (const feature_info_t *) pa;
const feature_info_t *b = (const feature_info_t *) pb;
return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) :
(a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
}
};
struct stage_info_t {

View File

@ -42,8 +42,10 @@ namespace OT {
struct NameRecord
{
static int cmp (const NameRecord *a, const NameRecord *b)
static int cmp (const void *pa, const void *pb)
{
const NameRecord *a = (const NameRecord *) pa;
const NameRecord *b = (const NameRecord *) pb;
int ret;
ret = b->platformID.cmp (a->platformID);
if (ret) return ret;
@ -89,7 +91,7 @@ struct name
key.encodingID.set (encoding_id);
key.languageID.set (language_id);
key.nameID.set (name_id);
NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), (hb_compare_func_t) NameRecord::cmp);
NameRecord *match = (NameRecord *) bsearch (&key, nameRecord, count, sizeof (nameRecord[0]), NameRecord::cmp);
if (!match)
return 0;

View File

@ -0,0 +1,294 @@
/*
* Copyright © 2017 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_OT_POST_MACROMAN_HH
#if 0 /* Make checks happy. */
#define HB_OT_POST_MACROMAN_HH
#include "hb-private.hh"
#endif
_S(".notdef")
_S(".null")
_S("nonmarkingreturn")
_S("space")
_S("exclam")
_S("quotedbl")
_S("numbersign")
_S("dollar")
_S("percent")
_S("ampersand")
_S("quotesingle")
_S("parenleft")
_S("parenright")
_S("asterisk")
_S("plus")
_S("comma")
_S("hyphen")
_S("period")
_S("slash")
_S("zero")
_S("one")
_S("two")
_S("three")
_S("four")
_S("five")
_S("six")
_S("seven")
_S("eight")
_S("nine")
_S("colon")
_S("semicolon")
_S("less")
_S("equal")
_S("greater")
_S("question")
_S("at")
_S("A")
_S("B")
_S("C")
_S("D")
_S("E")
_S("F")
_S("G")
_S("H")
_S("I")
_S("J")
_S("K")
_S("L")
_S("M")
_S("N")
_S("O")
_S("P")
_S("Q")
_S("R")
_S("S")
_S("T")
_S("U")
_S("V")
_S("W")
_S("X")
_S("Y")
_S("Z")
_S("bracketleft")
_S("backslash")
_S("bracketright")
_S("asciicircum")
_S("underscore")
_S("grave")
_S("a")
_S("b")
_S("c")
_S("d")
_S("e")
_S("f")
_S("g")
_S("h")
_S("i")
_S("j")
_S("k")
_S("l")
_S("m")
_S("n")
_S("o")
_S("p")
_S("q")
_S("r")
_S("s")
_S("t")
_S("u")
_S("v")
_S("w")
_S("x")
_S("y")
_S("z")
_S("braceleft")
_S("bar")
_S("braceright")
_S("asciitilde")
_S("Adieresis")
_S("Aring")
_S("Ccedilla")
_S("Eacute")
_S("Ntilde")
_S("Odieresis")
_S("Udieresis")
_S("aacute")
_S("agrave")
_S("acircumflex")
_S("adieresis")
_S("atilde")
_S("aring")
_S("ccedilla")
_S("eacute")
_S("egrave")
_S("ecircumflex")
_S("edieresis")
_S("iacute")
_S("igrave")
_S("icircumflex")
_S("idieresis")
_S("ntilde")
_S("oacute")
_S("ograve")
_S("ocircumflex")
_S("odieresis")
_S("otilde")
_S("uacute")
_S("ugrave")
_S("ucircumflex")
_S("udieresis")
_S("dagger")
_S("degree")
_S("cent")
_S("sterling")
_S("section")
_S("bullet")
_S("paragraph")
_S("germandbls")
_S("registered")
_S("copyright")
_S("trademark")
_S("acute")
_S("dieresis")
_S("notequal")
_S("AE")
_S("Oslash")
_S("infinity")
_S("plusminus")
_S("lessequal")
_S("greaterequal")
_S("yen")
_S("mu")
_S("partialdiff")
_S("summation")
_S("product")
_S("pi")
_S("integral")
_S("ordfeminine")
_S("ordmasculine")
_S("Omega")
_S("ae")
_S("oslash")
_S("questiondown")
_S("exclamdown")
_S("logicalnot")
_S("radical")
_S("florin")
_S("approxequal")
_S("Delta")
_S("guillemotleft")
_S("guillemotright")
_S("ellipsis")
_S("nonbreakingspace")
_S("Agrave")
_S("Atilde")
_S("Otilde")
_S("OE")
_S("oe")
_S("endash")
_S("emdash")
_S("quotedblleft")
_S("quotedblright")
_S("quoteleft")
_S("quoteright")
_S("divide")
_S("lozenge")
_S("ydieresis")
_S("Ydieresis")
_S("fraction")
_S("currency")
_S("guilsinglleft")
_S("guilsinglright")
_S("fi")
_S("fl")
_S("daggerdbl")
_S("periodcentered")
_S("quotesinglbase")
_S("quotedblbase")
_S("perthousand")
_S("Acircumflex")
_S("Ecircumflex")
_S("Aacute")
_S("Edieresis")
_S("Egrave")
_S("Iacute")
_S("Icircumflex")
_S("Idieresis")
_S("Igrave")
_S("Oacute")
_S("Ocircumflex")
_S("apple")
_S("Ograve")
_S("Uacute")
_S("Ucircumflex")
_S("Ugrave")
_S("dotlessi")
_S("circumflex")
_S("tilde")
_S("macron")
_S("breve")
_S("dotaccent")
_S("ring")
_S("cedilla")
_S("hungarumlaut")
_S("ogonek")
_S("caron")
_S("Lslash")
_S("lslash")
_S("Scaron")
_S("scaron")
_S("Zcaron")
_S("zcaron")
_S("brokenbar")
_S("Eth")
_S("eth")
_S("Yacute")
_S("yacute")
_S("Thorn")
_S("thorn")
_S("minus")
_S("multiply")
_S("onesuperior")
_S("twosuperior")
_S("threesuperior")
_S("onehalf")
_S("onequarter")
_S("threequarters")
_S("franc")
_S("Gbreve")
_S("gbreve")
_S("Idotaccent")
_S("Scedilla")
_S("scedilla")
_S("Cacute")
_S("cacute")
_S("Ccaron")
_S("ccaron")
_S("dcroat")
#endif /* HB_OT_POST_MACROMAN_HH */

View File

@ -28,50 +28,16 @@
#define HB_OT_POST_TABLE_HH
#include "hb-open-type-private.hh"
#include "hb-dsalgs.hh"
#define HB_STRING_ARRAY_NAME format1_names
#define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh"
#include "hb-string-array.hh"
#undef HB_STRING_ARRAY_LIST
#undef HB_STRING_ARRAY_NAME
#define NUM_FORMAT1_NAMES 258
static const char* const format1_names[NUM_FORMAT1_NAMES] =
{
".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl",
"numbersign", "dollar", "percent", "ampersand", "quotesingle", "parenleft",
"parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash",
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "colon", "semicolon", "less", "equal", "greater", "question", "at",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft",
"backslash", "bracketright", "asciicircum", "underscore", "grave", "a", "b",
"c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q",
"r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar",
"braceright", "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute",
"Ntilde", "Odieresis", "Udieresis", "aacute", "agrave", "acircumflex",
"adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave",
"ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
"ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute",
"ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
"section", "bullet", "paragraph", "germandbls", "registered", "copyright",
"trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity",
"plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff",
"summation", "product", "pi", "integral", "ordfeminine", "ordmasculine",
"Omega", "ae", "oslash", "questiondown", "exclamdown", "logicalnot",
"radical", "florin", "approxequal", "Delta", "guillemotleft",
"guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
"Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright",
"quoteleft", "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis",
"fraction", "currency", "guilsinglleft", "guilsinglright", "fi", "fl",
"daggerdbl", "periodcentered", "quotesinglbase", "quotedblbase",
"perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave",
"Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
"apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
"circumflex", "tilde", "macron", "breve", "dotaccent", "ring", "cedilla",
"hungarumlaut", "ogonek", "caron", "Lslash", "lslash", "Scaron", "scaron",
"Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn",
"thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior",
"onehalf", "onequarter", "threequarters", "franc", "Gbreve", "gbreve",
"Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron", "ccaron",
"dcroat",
};
namespace OT {
@ -87,13 +53,10 @@ struct postV2Tail
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (numberOfGlyphs.sanitize (c) &&
c->check_array (glyphNameIndex, sizeof (USHORT), numberOfGlyphs));
return_trace (glyphNameIndex.sanitize (c));
}
USHORT numberOfGlyphs; /* Number of glyphs (this should be the
* same as numGlyphs in 'maxp' table). */
USHORT glyphNameIndex[VAR]; /* This is not an offset, but is the
ArrayOf<USHORT>glyphNameIndex; /* This is not an offset, but is the
* ordinal number of the glyph in 'post'
* string tables. */
BYTE namesX[VAR]; /* Glyph names with length bytes [variable]
@ -119,134 +82,157 @@ struct post
return_trace (true);
}
inline bool get_glyph_name (hb_codepoint_t glyph,
char *buffer, unsigned int buffer_length,
unsigned int blob_len) const
struct accelerator_t
{
if (version.to_int () == 0x00010000)
inline void init (const post *table, unsigned int post_len)
{
if (glyph >= NUM_FORMAT1_NAMES)
return false;
version = table->version.to_int ();
index_to_offset.init ();
if (version != 0x00020000)
return;
if (!buffer_length)
const postV2Tail &v2 = StructAfter<postV2Tail> (*table);
glyphNameIndex = &v2.glyphNameIndex;
pool = &StructAfter<uint8_t> (v2.glyphNameIndex);
const uint8_t *end = (uint8_t *) table + post_len;
for (const uint8_t *data = pool; data < end && data + *data <= end; data += 1 + *data)
{
uint32_t *offset = index_to_offset.push ();
if (unlikely (!offset))
break;
*offset = data - pool;
}
}
inline void fini (void)
{
index_to_offset.finish ();
free (gids_sorted_by_name);
}
inline bool get_glyph_name (hb_codepoint_t glyph,
char *buf, unsigned int buf_len) const
{
hb_string_t s = find_glyph_name (glyph);
if (!s.len)
return false;
if (!buf_len)
return true;
strncpy (buffer, format1_names[glyph], buffer_length);
buffer[buffer_length - 1] = '\0';
if (buf_len <= s.len) /* What to do with truncation? Returning false for now. */
return false;
strncpy (buf, s.bytes, s.len);
buf[s.len] = '\0';
return true;
}
if (version.to_int () == 0x00020000)
inline bool get_glyph_from_name (const char *name, int len,
hb_codepoint_t *glyph) const
{
const postV2Tail &v2 = StructAfter<postV2Tail> (*this);
unsigned int count = get_glyph_count ();
if (unlikely (!count))
return false;
if (glyph >= v2.numberOfGlyphs)
if (len < 0)
len = strlen (name);
if (unlikely (!len))
return false;
if (!buffer_length)
return true;
retry:
uint16_t *gids = (uint16_t *) hb_atomic_ptr_get (&gids_sorted_by_name);
unsigned int index = v2.glyphNameIndex[glyph];
if (index < NUM_FORMAT1_NAMES)
if (unlikely (!gids))
{
if (!buffer_length)
return true;
strncpy (buffer, format1_names[index], buffer_length);
buffer[buffer_length - 1] = '\0';
gids = (uint16_t *) malloc (count * sizeof (gids[0]));
if (unlikely (!gids))
return false; /* Anything better?! */
for (unsigned int i = 0; i < count; i++)
gids[i] = i;
hb_sort_r (gids, count, sizeof (gids[0]), cmp_gids, (void *) this);
if (!hb_atomic_ptr_cmpexch (&gids_sorted_by_name, nullptr, gids)) {
free (gids);
goto retry;
}
}
hb_string_t st (name, len);
const uint16_t *gid = (const uint16_t *) hb_bsearch_r (&st, gids, count, sizeof (gids[0]), cmp_key, (void *) this);
if (gid)
{
*glyph = *gid;
return true;
}
return false;
}
protected:
inline unsigned int get_glyph_count (void) const
{
if (version == 0x00010000)
return NUM_FORMAT1_NAMES;
if (version == 0x00020000)
return glyphNameIndex->len;
return 0;
}
static inline int cmp_gids (const void *pa, const void *pb, void *arg)
{
const accelerator_t *thiz = (const accelerator_t *) arg;
uint16_t a = * (const uint16_t *) pa;
uint16_t b = * (const uint16_t *) pb;
return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a));
}
static inline int cmp_key (const void *pk, const void *po, void *arg)
{
const accelerator_t *thiz = (const accelerator_t *) arg;
const hb_string_t *key = (const hb_string_t *) pk;
uint16_t o = * (const uint16_t *) po;
return thiz->find_glyph_name (o).cmp (*key);
}
inline hb_string_t find_glyph_name (hb_codepoint_t glyph) const
{
if (version == 0x00010000)
{
if (glyph >= NUM_FORMAT1_NAMES)
return hb_string_t ();
return format1_names (glyph);
}
if (version != 0x00020000 || glyph >= glyphNameIndex->len)
return hb_string_t ();
unsigned int index = glyphNameIndex->array[glyph];
if (index < NUM_FORMAT1_NAMES)
return format1_names (index);
index -= NUM_FORMAT1_NAMES;
unsigned int offset = min_size + v2.min_size + 2 * v2.numberOfGlyphs;
unsigned char *data = (unsigned char *) this + offset;
unsigned char *end = (unsigned char *) this + blob_len;
for (unsigned int i = 0; data < end; i++)
{
unsigned int name_length = data[0];
data++;
if (i == index)
{
if (unlikely (!name_length))
return false;
if (index >= index_to_offset.len)
return hb_string_t ();
unsigned int offset = index_to_offset.array[index];
unsigned int remaining = end - data;
name_length = MIN (name_length, buffer_length - 1);
name_length = MIN (name_length, remaining);
memcpy (buffer, data, name_length);
buffer[name_length] = '\0';
return true;
}
data += name_length;
}
const uint8_t *data = pool + offset;
unsigned int name_length = *data;
data++;
return false;
return hb_string_t ((const char *) data, name_length);
}
return false;
}
inline bool get_glyph_from_name (const char *name, int len,
hb_codepoint_t *glyph,
unsigned int blob_len) const
{
if (len < 0)
len = strlen (name);
if (version.to_int () == 0x00010000)
{
for (int i = 0; i < NUM_FORMAT1_NAMES; i++)
{
if (strncmp (name, format1_names[i], len) == 0 && format1_names[i][len] == '\0')
{
*glyph = i;
return true;
}
}
return false;
}
if (version.to_int () == 0x00020000)
{
const postV2Tail &v2 = StructAfter<postV2Tail> (*this);
unsigned int offset = min_size + v2.min_size + 2 * v2.numberOfGlyphs;
char* data = (char*) this + offset;
/* XXX The following code is wrong. */
return false;
for (hb_codepoint_t gid = 0; gid < v2.numberOfGlyphs; gid++)
{
unsigned int index = v2.glyphNameIndex[gid];
if (index < NUM_FORMAT1_NAMES)
{
if (strncmp (name, format1_names[index], len) == 0 && format1_names[index][len] == '\0')
{
*glyph = gid;
return true;
}
continue;
}
index -= NUM_FORMAT1_NAMES;
for (unsigned int i = 0; data < (char*) this + blob_len; i++)
{
unsigned int name_length = data[0];
unsigned int remaining = (char*) this + blob_len - data - 1;
name_length = MIN (name_length, remaining);
if (name_length == (unsigned int) len && strncmp (name, data + 1, len) == 0)
{
*glyph = gid;
return true;
}
data += name_length + 1;
}
return false;
}
return false;
}
return false;
}
uint32_t version;
const ArrayOf<USHORT> *glyphNameIndex;
hb_prealloced_array_t<uint32_t, 1> index_to_offset;
const uint8_t *pool;
mutable uint16_t *gids_sorted_by_name;
};
public:
FixedVersion<>version; /* 0x00010000 for version 1.0

View File

@ -24,15 +24,12 @@
* Google Author(s): Behdad Esfahbod
*/
#include "hb-private.hh"
#include "hb-debug.hh"
#include "hb-ot-shape-complex-arabic-private.hh"
#include "hb-ot-shape-private.hh"
#ifndef HB_DEBUG_ARABIC
#define HB_DEBUG_ARABIC (HB_DEBUG+0)
#endif
/* buffer var allocations */
#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
@ -563,6 +560,7 @@ apply_stch (const hb_ot_shape_plan_t *plan,
}
else
{
buffer->unsafe_to_break (context, end);
hb_position_t x_offset = 0;
for (unsigned int k = end; k > start; k--)
{
@ -689,7 +687,6 @@ reorder_marks_arabic (const hb_ot_shape_plan_t *plan,
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
{
"arabic",
collect_features_arabic,
nullptr, /* override_features */
data_create_arabic,

View File

@ -29,7 +29,6 @@
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
{
"default",
nullptr, /* collect_features */
nullptr, /* override_features */
nullptr, /* data_create */

View File

@ -414,7 +414,6 @@ setup_masks_hangul (const hb_ot_shape_plan_t *plan,
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul =
{
"hangul",
collect_features_hangul,
override_features_hangul,
data_create_hangul,

View File

@ -169,7 +169,6 @@ disable_otl_hebrew (const hb_ot_shape_plan_t *plan)
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
{
"hebrew",
nullptr, /* collect_features */
nullptr, /* override_features */
nullptr, /* data_create */

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