mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Merge mozilla-central to autoland r=merge on a CLOSED TREE
This commit is contained in:
commit
79499f4044
@ -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
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1698,7 +1698,7 @@ protected:
|
||||
void Invalidate();
|
||||
void DestroyResourcesAndContext();
|
||||
|
||||
void MakeContextCurrent() const;
|
||||
void MakeContextCurrent() const { } // MakeCurrent is implicit now.
|
||||
|
||||
// helpers
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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) == '|');
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
486
dom/u2f/U2F.cpp
486
dom/u2f/U2F.cpp
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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']
|
||||
|
6
dom/u2f/tests/browser/browser.ini
Normal file
6
dom/u2f/tests/browser/browser.ini
Normal file
@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
tab_u2f_result.html
|
||||
skip-if = !e10s
|
||||
|
||||
[browser_abort_visibility.js]
|
117
dom/u2f/tests/browser/browser_abort_visibility.js
Normal file
117
dom/u2f/tests/browser/browser_abort_visibility.js
Normal 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");
|
||||
});
|
14
dom/u2f/tests/browser/tab_u2f_result.html
Normal file
14
dom/u2f/tests/browser/tab_u2f_result.html
Normal 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>
|
85
dom/u2f/tests/frame_override_request.html
Normal file
85
dom/u2f/tests/frame_override_request.html
Normal 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>
|
@ -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]
|
||||
|
34
dom/u2f/tests/test_override_request.html
Normal file
34
dom/u2f/tests/test_override_request.html
Normal 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>
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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/)
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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" \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 ();
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
419
gfx/harfbuzz/src/hb-debug.hh
Normal file
419
gfx/harfbuzz/src/hb-debug.hh
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
161
gfx/harfbuzz/src/hb-dsalgs.hh
Normal file
161
gfx/harfbuzz/src/hb-dsalgs.hh
Normal 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 */
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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
|
||||
|
389
gfx/harfbuzz/src/hb-ot-kern-table.hh
Normal file
389
gfx/harfbuzz/src/hb-ot-kern-table.hh
Normal 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 */
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
294
gfx/harfbuzz/src/hb-ot-post-macroman.hh
Normal file
294
gfx/harfbuzz/src/hb-ot-post-macroman.hh
Normal 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 */
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user