mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1125514 - Use jemalloc's metadata statistics to compute bookkeeping. r=glandium (541dbcfc6f) - Bug 1201462 - Don't count arena allocated metadata once per bin. r=glandium (57e7c31081) - back some warnings (377df69d65) - Bug 1219501. In imagelib, initialize the number of cores to at least 1 in case of error. r=seth (3d7d1635f0) - Bug 1219501. Limit total number of image decoding threads to 32 regardless of number asked for. r=seth (829a7a623d) - Bug 1213744 (Part 1) - Support zero-size frame rects and detecting the end of the frame in Downscaler. r=tn (05e29075cc) - Bug 1213744 (Part 2) - Clamp the GIF frame rect to the visible rect for DDD and don't decode outside it. r=tn (8a25e10a3e) - Bug 1194837. Don't use the inverse orientation matrix when computing the image space invalidate rect. r=seth (cb5e4c2643) - Bug 1214054 - Don't fire DECODE_COMPLETE in VectorImage::OnSVGDocumetError(). r=dholbert (bb7c34e46f) - Bug 1195878 - If we detect animation during a full decode, drop the results of the full decode on the floor. r=tn (a765af2b68) - Bug 1210553 - Remove the alternate flags arguments from SurfaceCache's Lookup functions. r=dholbert (15c6124f98) - Bug 1217320 - Remove more XPIDL signature comments in .cpp files. r=froydnj (411ac93047) - Bug 1186796 - Replace nsBaseHashtable::EnumerateRead() calls in image/ with iterators r=njn (665773ae6d) - Bug 1186792 - Replace nsBaseHashtable::EnumerateRead() calls in hal/ with iterators. r=dhylands. (d57c6b11da) - Bug 1187142 - Replace nsBaseHashtable::Enumerate() calls in hal/ with iterators. r=dhylands. (ec05c5b125) - Bug 1186793 - Replace nsBaseHashtable::EnumerateRead() calls in gfx/ with iterators r=njn (9b3cdd92ce) - Bug 1215900 - Fix clang's -Wimplicit-fallthrough warnings in gfx/ipc/ GfxMessageUtils.h. r=mstange (f55605f1fe) - Bug 618898 - Part 1: Add WGL_NV_DX_interop. r=jgilbert (73390398ed) - Bug 618898 - Add D3D11SharedSurfaceInterop. r=jgilbert (3dde956b85) - Bug 1208513 - Resurrect SharedSurface_GLTexture for use on iOS r=jgilbert (b0fdc90414) - Bug 1150760 - Don't call workaround unless necessary. - r=kamidphish (9bdd135931) - Bug 1151106 - let debugger stop on each iteration of a "for(;;)" loop; r=jimb (b1b921c3a7) - Bug 1223652 - Remove redundant else block after return statement in CGBlockScopeList::findEnclosingScope. r=arai (f1368bfc73) - Bug 1219515 - IonMonkey: Fix ThrowIfNotConstructing was not declared. r=evilpie (1d6cedad10) - Bug 1224044 - Use stable hashing in SavedFramePtrHasher r=terrence (4389cf1b70) - Bug 1206596: Change js::SavedStacks to use mozilla::FastBernoulliTrial. r=fitzgen (1c4a8d1929) - Bug 1206357: Add mfbt/FastBernoulliTrial.h, implementing efficient random sampling. r=waldo (7143e53dba) - No bug: Fix comment in mfbt/FastBernoulliTrial.h. DONTBUILD r=me (e3343f8d9d) - Bug 1217919 - Separate dynamic module scopes from those of function calls r=shu (521f6826e5) - Bug 1202568 - Cherry-pick warning fixes from upstream double-conversion. r=Ms2ger (ef738f753b) - add back some disabled android stuff (0351b0c518) - Bug 1135261 - return new window from window.open in desktop runtime; r=marco,smaug,junior,wesj (fa4d8f2468) - Bug 898075 - Remove the mozbrowserasyncscroll event from Gecko. r=botond,kanru,sicking (b1fdcb7630) - namespace (91374d2db8)
This commit is contained in:
@@ -966,7 +966,6 @@ pref("apz.allow_zooming", true);
|
||||
|
||||
// Gaia relies heavily on scroll events for now, so lets fire them
|
||||
// more often than the default value (100).
|
||||
pref("apz.asyncscroll.throttle", 40);
|
||||
pref("apz.pan_repaint_interval", 16);
|
||||
|
||||
// APZ physics settings, tuned by UX designers
|
||||
|
||||
@@ -17,3 +17,6 @@ FINAL_LIBRARY = 'browsercomps'
|
||||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -39,3 +39,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
# GTK2: Need to link with glib for GNOME shell service
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'):
|
||||
OS_LIBS += CONFIG['TK_LIBS']
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -12,8 +12,15 @@ SOURCES += [
|
||||
'DirectoryProvider.cpp',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
'tests/unit/xpcshell.ini',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'browsercomps'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../build'
|
||||
]
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* 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/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var gProfD = do_get_profile();
|
||||
var gDirSvc = Cc["@mozilla.org/file/directory_service;1"].
|
||||
getService(Ci.nsIProperties);
|
||||
var gPrefSvc = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefBranch);
|
||||
|
||||
function writeTestFile(aParent, aName) {
|
||||
let file = aParent.clone();
|
||||
file.append(aName);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* 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/. */
|
||||
|
||||
// We need to run this test separately since DirectoryProvider persists BMarks
|
||||
|
||||
function run_test() {
|
||||
let dir = gProfD.clone();
|
||||
let tfile = writeTestFile(dir, "bookmarkfile.test");
|
||||
gPrefSvc.setCharPref("browser.bookmarks.file", tfile.path);
|
||||
|
||||
let bmarks = gDirSvc.get("BMarks", Ci.nsIFile);
|
||||
do_check_true(tfile.equals(bmarks));
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/* 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/. */
|
||||
|
||||
function test_bookmarkhtml() {
|
||||
let bmarks = gProfD.clone();
|
||||
bmarks.append("bookmarks.html");
|
||||
|
||||
let tbmarks = gDirSvc.get("BMarks", Ci.nsIFile);
|
||||
do_check_true(bmarks.equals(tbmarks));
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
[test_bookmarkhtml
|
||||
].forEach(function(f) {
|
||||
do_test_pending();
|
||||
print("Running test: " + f.name);
|
||||
f();
|
||||
do_test_finished();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
[DEFAULT]
|
||||
head = head_dirprovider.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||
|
||||
[test_bookmark_pref.js]
|
||||
[test_keys.js]
|
||||
@@ -435,9 +435,6 @@ Exception::ToString(nsACString& _retval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void initialize (in AUTF8String aMessage, in nsresult aResult,
|
||||
* in AUTF8String aName, in nsIStackFrame aLocation,
|
||||
* in nsISupports aData, in nsIException aInner); */
|
||||
NS_IMETHODIMP
|
||||
Exception::Initialize(const nsACString& aMessage, nsresult aResult,
|
||||
const nsACString& aName, nsIStackFrame *aLocation,
|
||||
|
||||
@@ -7525,7 +7525,6 @@ nsIDocument::GetDocumentURIObject() const
|
||||
}
|
||||
|
||||
|
||||
// readonly attribute DOMString compatMode;
|
||||
// Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
|
||||
// in almost standards or full standards mode. See bug 105640. This was
|
||||
// implemented to match MSIE's compatMode property.
|
||||
|
||||
@@ -117,7 +117,7 @@ DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
|
||||
}
|
||||
event->SetTrusted(true);
|
||||
// Dispatch the event.
|
||||
*aStatus = nsEventStatus_eConsumeNoDefault;
|
||||
// We don't initialize aStatus here, as our callers have already done so.
|
||||
nsresult rv =
|
||||
EventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
|
||||
static_cast<Event*>(event),
|
||||
@@ -177,7 +177,7 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
|
||||
return BrowserElementParent::OPEN_WINDOW_CANCELLED;
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
bool dispatchSucceeded =
|
||||
DispatchCustomDOMEvent(aOpenerFrameElement,
|
||||
NS_LITERAL_STRING("mozbrowseropenwindow"),
|
||||
@@ -299,79 +299,4 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
|
||||
return !!*aReturnWindow ? opened : BrowserElementParent::OPEN_WINDOW_CANCELLED;
|
||||
}
|
||||
|
||||
class DispatchAsyncScrollEventRunnable : public nsRunnable
|
||||
{
|
||||
public:
|
||||
DispatchAsyncScrollEventRunnable(TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize)
|
||||
: mTabParent(aTabParent)
|
||||
, mContentRect(aContentRect)
|
||||
, mContentSize(aContentSize)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
private:
|
||||
RefPtr<TabParent> mTabParent;
|
||||
const CSSRect mContentRect;
|
||||
const CSSSize mContentSize;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP DispatchAsyncScrollEventRunnable::Run()
|
||||
{
|
||||
nsCOMPtr<Element> frameElement = mTabParent->GetOwnerElement();
|
||||
NS_ENSURE_STATE(frameElement);
|
||||
nsIDocument *doc = frameElement->OwnerDoc();
|
||||
nsCOMPtr<nsIGlobalObject> globalObject = doc->GetScopeObject();
|
||||
NS_ENSURE_TRUE(globalObject, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Create the event's detail object.
|
||||
AsyncScrollEventDetail detail;
|
||||
detail.mLeft = mContentRect.x;
|
||||
detail.mTop = mContentRect.y;
|
||||
detail.mWidth = mContentRect.width;
|
||||
detail.mHeight = mContentRect.height;
|
||||
detail.mScrollWidth = mContentRect.width;
|
||||
detail.mScrollHeight = mContentRect.height;
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
|
||||
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
|
||||
|
||||
JSAutoCompartment ac(cx, globalJSObject);
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
|
||||
if (!ToJSValue(cx, detail, &val)) {
|
||||
MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
DispatchCustomDOMEvent(frameElement,
|
||||
NS_LITERAL_STRING("mozbrowserasyncscroll"),
|
||||
cx,
|
||||
val, &status);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize)
|
||||
{
|
||||
// Do not dispatch a mozbrowserasyncscroll event of a widget to its embedder
|
||||
nsCOMPtr<Element> frameElement = aTabParent->GetOwnerElement();
|
||||
NS_ENSURE_TRUE(frameElement, false);
|
||||
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
|
||||
if (browserFrame && browserFrame->GetReallyIsWidget()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<DispatchAsyncScrollEventRunnable> runnable =
|
||||
new DispatchAsyncScrollEventRunnable(aTabParent, aContentRect,
|
||||
aContentSize);
|
||||
return NS_SUCCEEDED(NS_DispatchToMainThread(runnable));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -109,26 +109,6 @@ public:
|
||||
const nsACString& aFeatures,
|
||||
nsIDOMWindow** aReturnWindow);
|
||||
|
||||
/**
|
||||
* Fire a mozbrowserasyncscroll CustomEvent on the given TabParent's frame element.
|
||||
* This event's detail is an AsyncScrollEventDetail dictionary.
|
||||
*
|
||||
* @param aContentRect: The portion of the page which is currently visible
|
||||
* onscreen in CSS pixels.
|
||||
*
|
||||
* @param aContentSize: The content width/height in CSS pixels.
|
||||
*
|
||||
* aContentRect.top + aContentRect.height may be larger than aContentSize.height.
|
||||
* This indicates that the content is over-scrolled, which occurs when the
|
||||
* page "rubber-bands" after being scrolled all the way to the bottom.
|
||||
* Similarly, aContentRect.left + aContentRect.width may be greater than
|
||||
* contentSize.width, and both left and top may be negative.
|
||||
*/
|
||||
static bool
|
||||
DispatchAsyncScrollEvent(dom::TabParent* aTabParent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize);
|
||||
|
||||
private:
|
||||
static OpenWindowResult
|
||||
DispatchOpenWindowEvent(dom::Element* aOpenerFrameElement,
|
||||
|
||||
@@ -5322,9 +5322,6 @@ CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
|
||||
JS::ToInt32(dirtyHeight));
|
||||
}
|
||||
|
||||
// void putImageData (in ImageData d, in float x, in float y);
|
||||
// void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
|
||||
|
||||
nsresult
|
||||
CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
|
||||
dom::Uint8ClampedArray* aArray,
|
||||
|
||||
@@ -59,8 +59,6 @@ NS_IMETHODIMP GamepadServiceTest::RemoveGamepad(uint32_t aIndex)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void newButtonEvent (in uint32_t index, in uint32_t button,
|
||||
in boolean pressed); */
|
||||
NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
|
||||
uint32_t aButton,
|
||||
bool aPressed)
|
||||
@@ -69,8 +67,6 @@ NS_IMETHODIMP GamepadServiceTest::NewButtonEvent(uint32_t aIndex,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void newButtonEvent (in uint32_t index, in uint32_t button,
|
||||
in boolean pressed, double value); */
|
||||
NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
|
||||
uint32_t aButton,
|
||||
bool aPressed,
|
||||
@@ -80,8 +76,6 @@ NS_IMETHODIMP GamepadServiceTest::NewButtonValueEvent(uint32_t aIndex,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void newAxisMoveEvent (in uint32_t index, in uint32_t axis,
|
||||
in double value); */
|
||||
NS_IMETHODIMP GamepadServiceTest::NewAxisMoveEvent(uint32_t aIndex,
|
||||
uint32_t aAxis,
|
||||
double aValue)
|
||||
|
||||
@@ -3199,8 +3199,6 @@ ConvertToMidasInternalCommand(const nsAString & inCommandID,
|
||||
}
|
||||
|
||||
/* TODO: don't let this call do anything if the page is not done loading */
|
||||
/* boolean execCommand(in DOMString commandID, in boolean doShowUI,
|
||||
in DOMString value); */
|
||||
NS_IMETHODIMP
|
||||
nsHTMLDocument::ExecCommand(const nsAString& commandID,
|
||||
bool doShowUI,
|
||||
|
||||
@@ -156,7 +156,6 @@ DOMSVGPathSeg::IndexIsValid()
|
||||
// Implementation of DOMSVGPathSeg sub-classes below this point
|
||||
|
||||
#define IMPL_PROP_WITH_TYPE(segName, propName, index, type) \
|
||||
/* attribute type propName; */ \
|
||||
type \
|
||||
DOMSVGPathSeg##segName::propName() \
|
||||
{ \
|
||||
|
||||
@@ -233,7 +233,6 @@ StumblerInfo::DumpStumblerInfo()
|
||||
target->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
/* void notifyGetCellInfoList (in uint32_t count, [array, size_is (count)] in nsICellInfo result); */
|
||||
NS_IMETHODIMP
|
||||
StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
|
||||
{
|
||||
@@ -248,7 +247,6 @@ StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void notifyGetCellInfoListFailed (in DOMString error); */
|
||||
NS_IMETHODIMP StumblerInfo::NotifyGetCellInfoListFailed(const nsAString& error)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@@ -7,15 +7,6 @@
|
||||
* liability, trademark and document use rules apply.
|
||||
*/
|
||||
|
||||
dictionary AsyncScrollEventDetail {
|
||||
float top = 0;
|
||||
float left = 0;
|
||||
float width = 0;
|
||||
float height = 0;
|
||||
float scrollWidth = 0;
|
||||
float scrollHeight = 0;
|
||||
};
|
||||
|
||||
dictionary OpenWindowEventDetail {
|
||||
DOMString url = "";
|
||||
DOMString name = "";
|
||||
|
||||
@@ -85,10 +85,6 @@ NS_IMETHODIMP nsTransactionList::ItemIsBatch(int32_t aIndex, bool *aIsBatch)
|
||||
return item->GetIsBatch(aIsBatch);
|
||||
}
|
||||
|
||||
/* void getData (in long aIndex,
|
||||
[optional] out unsigned long aLength,
|
||||
[array, size_is (aLength), retval]
|
||||
out nsISupports aData); */
|
||||
NS_IMETHODIMP nsTransactionList::GetData(int32_t aIndex,
|
||||
uint32_t *aLength,
|
||||
nsISupports ***aData)
|
||||
|
||||
@@ -327,9 +327,6 @@ NS_IMETHODIMP nsWebBrowserPersist::SetProgressListener(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void saveURI (in nsIURI aURI, in nsISupports aCacheKey, in nsIURI aReferrer,
|
||||
in nsIInputStream aPostData, in wstring aExtraHeaders,
|
||||
in nsISupports aFile, in nsILoadContext aPrivayContext); */
|
||||
NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
|
||||
nsIURI *aURI, nsISupports *aCacheKey,
|
||||
nsIURI *aReferrer, uint32_t aReferrerPolicy,
|
||||
@@ -905,8 +902,6 @@ nsWebBrowserPersist::OnDataAvailable(
|
||||
// nsWebBrowserPersist::nsIProgressEventSink
|
||||
//*****************************************************************************
|
||||
|
||||
/* void onProgress (in nsIRequest request, in nsISupports ctxt,
|
||||
in long long aProgress, in long long aProgressMax); */
|
||||
NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
|
||||
nsIRequest *request, nsISupports *ctxt, int64_t aProgress,
|
||||
int64_t aProgressMax)
|
||||
@@ -958,8 +953,6 @@ NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void onStatus (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsresult status, in wstring statusArg); */
|
||||
NS_IMETHODIMP nsWebBrowserPersist::OnStatus(
|
||||
nsIRequest *request, nsISupports *ctxt, nsresult status,
|
||||
const char16_t *statusArg)
|
||||
|
||||
@@ -5668,7 +5668,6 @@
|
||||
#define LOCAL_GLX_X_VISUAL_TYPE_EXT 0x22
|
||||
#define LOCAL_GLX_Y_INVERTED_EXT 0x20D4
|
||||
|
||||
|
||||
// WGL
|
||||
#define LOCAL_WGL_ACCELERATION_ARB 0x2003
|
||||
#define LOCAL_WGL_ACCELERATION_EXT 0x2003
|
||||
@@ -5949,6 +5948,4 @@
|
||||
#define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB
|
||||
#define LOCAL_WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC
|
||||
|
||||
|
||||
|
||||
#endif // GLCONSTS_H_
|
||||
|
||||
@@ -141,9 +141,11 @@ WGLLibrary::EnsureInitialized()
|
||||
return false;
|
||||
}
|
||||
|
||||
const GLLibraryLoader::PlatformLookupFunction lookupFunc =
|
||||
(GLLibraryLoader::PlatformLookupFunction) fGetProcAddress;
|
||||
|
||||
// Now we can grab all the other symbols that we couldn't without having
|
||||
// a context current.
|
||||
|
||||
GLLibraryLoader::SymLoadStruct pbufferSymbols[] = {
|
||||
{ (PRFuncPtr*) &fCreatePbuffer, { "wglCreatePbufferARB", "wglCreatePbufferEXT", nullptr } },
|
||||
{ (PRFuncPtr*) &fDestroyPbuffer, { "wglDestroyPbufferARB", "wglDestroyPbufferEXT", nullptr } },
|
||||
@@ -159,16 +161,12 @@ WGLLibrary::EnsureInitialized()
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pbufferSymbols[0],
|
||||
(GLLibraryLoader::PlatformLookupFunction)fGetProcAddress))
|
||||
{
|
||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pbufferSymbols[0], lookupFunc)) {
|
||||
// this isn't an error, just means that pbuffers aren't supported
|
||||
fCreatePbuffer = nullptr;
|
||||
}
|
||||
|
||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pixFmtSymbols[0],
|
||||
(GLLibraryLoader::PlatformLookupFunction)fGetProcAddress))
|
||||
{
|
||||
if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pixFmtSymbols[0], lookupFunc)) {
|
||||
// this isn't an error, just means that we don't have the pixel format extension
|
||||
fChoosePixelFormat = nullptr;
|
||||
}
|
||||
@@ -183,14 +181,49 @@ WGLLibrary::EnsureInitialized()
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0],
|
||||
(GLLibraryLoader::PlatformLookupFunction)fGetProcAddress)) {
|
||||
const char *wglExts = fGetExtensionsString(mWindowDC);
|
||||
if (wglExts && HasExtension(wglExts, "WGL_ARB_create_context")) {
|
||||
GLLibraryLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0],
|
||||
(GLLibraryLoader::PlatformLookupFunction)fGetProcAddress);
|
||||
if (HasExtension(wglExts, "WGL_ARB_create_context_robustness")) {
|
||||
mHasRobustness = true;
|
||||
GLLibraryLoader::SymLoadStruct dxInteropSymbols[] = {
|
||||
{ (PRFuncPtr *)&fDXSetResourceShareHandle,{ "wglDXSetResourceShareHandleNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXOpenDevice, { "wglDXOpenDeviceNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXCloseDevice, { "wglDXCloseDeviceNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXRegisterObject, { "wglDXRegisterObjectNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXUnregisterObject, { "wglDXUnregisterObjectNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXObjectAccess, { "wglDXObjectAccessNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXLockObjects, { "wglDXLockObjectsNV", nullptr } },
|
||||
{ (PRFuncPtr *)&fDXUnlockObjects, { "wglDXUnlockObjectsNV", nullptr } },
|
||||
{ nullptr, { nullptr } }
|
||||
};
|
||||
|
||||
if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0], lookupFunc))
|
||||
{
|
||||
const char* extString = fGetExtensionsString(mWindowDC);
|
||||
MOZ_ASSERT(extString);
|
||||
MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string"));
|
||||
|
||||
if (HasExtension(extString, "WGL_ARB_context_create")) {
|
||||
if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0], lookupFunc)) {
|
||||
if (HasExtension(extString, "WGL_ARB_create_context_robustness")) {
|
||||
mHasRobustness = true;
|
||||
}
|
||||
} else {
|
||||
NS_ERROR("WGL supports ARB_create_context without supplying its functions.");
|
||||
fCreateContextAttribs = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (HasExtension(extString, "WGL_NV_DX_interop")) {
|
||||
if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &dxInteropSymbols[0], lookupFunc)) {
|
||||
mHasDXInterop = true;
|
||||
mHasDXInterop2 = HasExtension(extString, "WGL_NV_DX_interop2");
|
||||
} else {
|
||||
NS_ERROR("WGL supports NV_DX_interop without supplying its functions.");
|
||||
fDXSetResourceShareHandle = nullptr;
|
||||
fDXOpenDevice = nullptr;
|
||||
fDXCloseDevice = nullptr;
|
||||
fDXRegisterObject = nullptr;
|
||||
fDXUnregisterObject = nullptr;
|
||||
fDXObjectAccess = nullptr;
|
||||
fDXLockObjects = nullptr;
|
||||
fDXUnlockObjects = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,4 +70,9 @@
|
||||
#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ((EGLNativeDisplayType)-2)
|
||||
#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE ((EGLNativeDisplayType)-3)
|
||||
|
||||
// WGL_NV_DX_interop
|
||||
#define LOCAL_WGL_ACCESS_READ_ONLY 0x0000
|
||||
#define LOCAL_WGL_ACCESS_READ_WRITE 0x0001
|
||||
#define LOCAL_WGL_ACCESS_WRITE_DISCARD 0x0002
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "SharedSurfaceANGLE.h" // for SurfaceFactory_ANGLEShareHandle
|
||||
#include "SharedSurfaceANGLE.h" // for SurfaceFactory_ANGLEShareHandle
|
||||
#include "SharedSurfaceD3D11Interop.h" // for SurfaceFactory_D3D11Interop
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#endif
|
||||
|
||||
@@ -82,6 +83,8 @@ GLScreenBuffer::CreateFactory(GLContext* gl,
|
||||
#elif defined(GL_PROVIDER_GLX)
|
||||
if (sGLXLibrary.UseSurfaceSharing())
|
||||
factory = SurfaceFactory_GLXDrawable::Create(gl, caps, forwarder, flags);
|
||||
#elif defined(MOZ_WIDGET_UIKIT)
|
||||
factory = MakeUnique<SurfaceFactory_GLTexture>(mGLContext, caps, forwarder, mFlags);
|
||||
#else
|
||||
if (gl->GetContextType() == GLContextType::EGL) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
@@ -101,6 +104,10 @@ GLScreenBuffer::CreateFactory(GLContext* gl,
|
||||
{
|
||||
factory = SurfaceFactory_ANGLEShareHandle::Create(gl, caps, forwarder, flags);
|
||||
}
|
||||
|
||||
if (!factory && gfxPrefs::WebGLDXGLEnabled()) {
|
||||
factory = SurfaceFactory_D3D11Interop::Create(gl, caps, forwarder, flags);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -525,6 +532,12 @@ GLScreenBuffer::Swap(const gfx::IntSize& size)
|
||||
if (!newBack)
|
||||
return false;
|
||||
|
||||
// In the case of DXGL interop, the new surface needs to be acquired before
|
||||
// it is attached so that the interop surface is locked, which populates
|
||||
// the GL renderbuffer. This results in the renderbuffer being ready and
|
||||
// attachment to framebuffer succeeds in Attach() call.
|
||||
newBack->Surf()->ProducerAcquire();
|
||||
|
||||
if (!Attach(newBack->Surf(), size))
|
||||
return false;
|
||||
// Attach was successful.
|
||||
@@ -532,10 +545,6 @@ GLScreenBuffer::Swap(const gfx::IntSize& size)
|
||||
mFront = mBack;
|
||||
mBack = newBack;
|
||||
|
||||
if (mBack) {
|
||||
mBack->Surf()->ProducerAcquire();
|
||||
}
|
||||
|
||||
if (ShouldPreserveBuffer() &&
|
||||
mFront &&
|
||||
mBack &&
|
||||
|
||||
@@ -0,0 +1,509 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
|
||||
/* 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 "SharedSurfaceD3D11Interop.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include "GLContext.h"
|
||||
#include "WGLLibrary.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
/*
|
||||
Sample Code for WGL_NV_DX_interop2:
|
||||
Example: Render to Direct3D 11 backbuffer with openGL:
|
||||
|
||||
// create D3D11 device, context and swap chain.
|
||||
ID3D11Device *device;
|
||||
ID3D11DeviceContext *devCtx;
|
||||
IDXGISwapChain *swapChain;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC scd;
|
||||
|
||||
<set appropriate swap chain parameters in scd>
|
||||
|
||||
hr = D3D11CreateDeviceAndSwapChain(NULL, // pAdapter
|
||||
D3D_DRIVER_TYPE_HARDWARE, // DriverType
|
||||
NULL, // Software
|
||||
0, // Flags (Do not set D3D11_CREATE_DEVICE_SINGLETHREADED)
|
||||
NULL, // pFeatureLevels
|
||||
0, // FeatureLevels
|
||||
D3D11_SDK_VERSION, // SDKVersion
|
||||
&scd, // pSwapChainDesc
|
||||
&swapChain, // ppSwapChain
|
||||
&device, // ppDevice
|
||||
NULL, // pFeatureLevel
|
||||
&devCtx); // ppImmediateContext
|
||||
|
||||
// Fetch the swapchain backbuffer
|
||||
ID3D11Texture2D *dxColorbuffer;
|
||||
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *)&dxColorbuffer);
|
||||
|
||||
// Create depth stencil texture
|
||||
ID3D11Texture2D *dxDepthBuffer;
|
||||
D3D11_TEXTURE2D_DESC depthDesc;
|
||||
depthDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||
<set other depthDesc parameters appropriately>
|
||||
|
||||
// Create Views
|
||||
ID3D11RenderTargetView *colorBufferView;
|
||||
D3D11_RENDER_TARGET_VIEW_DESC rtd;
|
||||
<set rtd parameters appropriately>
|
||||
device->CreateRenderTargetView(dxColorbuffer, &rtd, &colorBufferView);
|
||||
|
||||
ID3D11DepthStencilView *depthBufferView;
|
||||
D3D11_DEPTH_STENCIL_VIEW_DESC dsd;
|
||||
<set dsd parameters appropriately>
|
||||
device->CreateDepthStencilView(dxDepthBuffer, &dsd, &depthBufferView);
|
||||
|
||||
// Attach back buffer and depth texture to redertarget for the device.
|
||||
devCtx->OMSetRenderTargets(1, &colorBufferView, depthBufferView);
|
||||
|
||||
// Register D3D11 device with GL
|
||||
HANDLE gl_handleD3D;
|
||||
gl_handleD3D = wglDXOpenDeviceNV(device);
|
||||
|
||||
// register the Direct3D color and depth/stencil buffers as
|
||||
// renderbuffers in opengl
|
||||
GLuint gl_names[2];
|
||||
HANDLE gl_handles[2];
|
||||
|
||||
glGenRenderbuffers(2, gl_names);
|
||||
|
||||
gl_handles[0] = wglDXRegisterObjectNV(gl_handleD3D, dxColorBuffer,
|
||||
gl_names[0],
|
||||
GL_RENDERBUFFER,
|
||||
WGL_ACCESS_READ_WRITE_NV);
|
||||
|
||||
gl_handles[1] = wglDXRegisterObjectNV(gl_handleD3D, dxDepthBuffer,
|
||||
gl_names[1],
|
||||
GL_RENDERBUFFER,
|
||||
WGL_ACCESS_READ_WRITE_NV);
|
||||
|
||||
// attach the Direct3D buffers to an FBO
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER, gl_names[0]);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_names[1]);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl_names[1]);
|
||||
|
||||
while (!done) {
|
||||
<direct3d renders to the render targets>
|
||||
|
||||
// lock the render targets for GL access
|
||||
wglDXLockObjectsNVX(gl_handleD3D, 2, gl_handles);
|
||||
|
||||
<opengl renders to the render targets>
|
||||
|
||||
// unlock the render targets
|
||||
wglDXUnlockObjectsNVX(gl_handleD3D, 2, gl_handles);
|
||||
|
||||
<direct3d renders to the render targets and presents
|
||||
the results on the screen>
|
||||
}
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DXGL Device
|
||||
|
||||
class DXGLDevice : public RefCounted<DXGLDevice>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(DXGLDevice)
|
||||
|
||||
WGLLibrary* const mWGL;
|
||||
const RefPtr<ID3D11Device> mD3D; // Only needed for lifetime guarantee.
|
||||
const HANDLE mDXGLDeviceHandle;
|
||||
|
||||
static already_AddRefed<DXGLDevice> Open(WGLLibrary* wgl)
|
||||
{
|
||||
MOZ_ASSERT(wgl->HasDXInterop2());
|
||||
gfxWindowsPlatform* plat = gfxWindowsPlatform::GetPlatform();
|
||||
|
||||
RefPtr<ID3D11Device> d3d = plat->GetD3D11ContentDevice();
|
||||
if (!d3d) {
|
||||
NS_WARNING("Failed to create D3D11 device.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE dxglDeviceHandle = wgl->fDXOpenDevice(d3d);
|
||||
if (!dxglDeviceHandle) {
|
||||
NS_WARNING("Failed to open D3D device for use by WGL.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeAndAddRef<DXGLDevice>(wgl, d3d, dxglDeviceHandle);
|
||||
}
|
||||
|
||||
DXGLDevice(WGLLibrary* wgl, const RefPtr<ID3D11Device>& d3d, HANDLE dxglDeviceHandle)
|
||||
: mWGL(wgl)
|
||||
, mD3D(d3d)
|
||||
, mDXGLDeviceHandle(dxglDeviceHandle)
|
||||
{ }
|
||||
|
||||
~DXGLDevice() {
|
||||
if (!mWGL->fDXCloseDevice(mDXGLDeviceHandle)) {
|
||||
#ifdef DEBUG
|
||||
uint32_t error = GetLastError();
|
||||
printf_stderr("wglDXCloseDevice(0x%x) failed: GetLastError(): 0x%x\n",
|
||||
mDXGLDeviceHandle, error);
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE RegisterObject(void* dxObject, GLuint name, GLenum type, GLenum access) const {
|
||||
HANDLE ret = mWGL->fDXRegisterObject(mDXGLDeviceHandle, dxObject, name, type,
|
||||
access);
|
||||
if (!ret) {
|
||||
#ifdef DEBUG
|
||||
uint32_t error = GetLastError();
|
||||
printf_stderr("wglDXRegisterObject(0x%x, 0x%x, %u, 0x%x, 0x%x) failed:"
|
||||
" GetLastError(): 0x%x\n", mDXGLDeviceHandle, dxObject, name,
|
||||
type, access, error);
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UnregisterObject(HANDLE hObject) const {
|
||||
bool ret = mWGL->fDXUnregisterObject(mDXGLDeviceHandle, hObject);
|
||||
if (!ret) {
|
||||
#ifdef DEBUG
|
||||
uint32_t error = GetLastError();
|
||||
printf_stderr("wglDXUnregisterObject(0x%x, 0x%x) failed: GetLastError():"
|
||||
" 0x%x\n", mDXGLDeviceHandle, hObject, error);
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool LockObject(HANDLE hObject) const {
|
||||
bool ret = mWGL->fDXLockObjects(mDXGLDeviceHandle, 1, &hObject);
|
||||
if (!ret) {
|
||||
#ifdef DEBUG
|
||||
uint32_t error = GetLastError();
|
||||
printf_stderr("wglDXLockObjects(0x%x, 1, {0x%x}) failed: GetLastError():"
|
||||
" 0x%x\n", mDXGLDeviceHandle, hObject, error);
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool UnlockObject(HANDLE hObject) const {
|
||||
bool ret = mWGL->fDXUnlockObjects(mDXGLDeviceHandle, 1, &hObject);
|
||||
if (!ret) {
|
||||
#ifdef DEBUG
|
||||
uint32_t error = GetLastError();
|
||||
printf_stderr("wglDXUnlockObjects(0x%x, 1, {0x%x}) failed: GetLastError():"
|
||||
" 0x%x\n", mDXGLDeviceHandle, hObject, error);
|
||||
#endif
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Shared Surface
|
||||
|
||||
/*static*/ UniquePtr<SharedSurface_D3D11Interop>
|
||||
SharedSurface_D3D11Interop::Create(const RefPtr<DXGLDevice>& dxgl,
|
||||
GLContext* gl,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha)
|
||||
{
|
||||
auto& d3d = *dxgl->mD3D;
|
||||
|
||||
// Create a texture in case we need to readback.
|
||||
DXGI_FORMAT format = hasAlpha ? DXGI_FORMAT_B8G8R8A8_UNORM
|
||||
: DXGI_FORMAT_B8G8R8X8_UNORM;
|
||||
CD3D11_TEXTURE2D_DESC desc(format, size.width, size.height, 1, 1);
|
||||
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
|
||||
|
||||
RefPtr<ID3D11Texture2D> textureD3D;
|
||||
HRESULT hr = d3d.CreateTexture2D(&desc, nullptr, getter_AddRefs(textureD3D));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to create texture for CanvasLayer!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<IDXGIResource> textureDXGI;
|
||||
hr = textureD3D->QueryInterface(__uuidof(IDXGIResource), getter_AddRefs(textureDXGI));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to open texture for sharing!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<IDXGIKeyedMutex> keyedMutex;
|
||||
hr = textureD3D->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(keyedMutex));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to obtained keyed mutex from texture!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE sharedHandle;
|
||||
textureDXGI->GetSharedHandle(&sharedHandle);
|
||||
|
||||
GLuint renderbufferGL = 0;
|
||||
gl->MakeCurrent();
|
||||
gl->fGenRenderbuffers(1, &renderbufferGL);
|
||||
HANDLE objectWGL = dxgl->RegisterObject(textureD3D, renderbufferGL,
|
||||
LOCAL_GL_RENDERBUFFER,
|
||||
LOCAL_WGL_ACCESS_WRITE_DISCARD_NV);
|
||||
if (!objectWGL) {
|
||||
NS_WARNING("Failed to register D3D object with WGL.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLuint fence = 0;
|
||||
if (gl->IsExtensionSupported(GLContext::NV_fence)) {
|
||||
gl->MakeCurrent();
|
||||
gl->fGenFences(1, &fence);
|
||||
}
|
||||
|
||||
typedef SharedSurface_D3D11Interop ptrT;
|
||||
UniquePtr<ptrT> ret ( new ptrT(gl, size, hasAlpha, renderbufferGL, dxgl, objectWGL,
|
||||
textureD3D, sharedHandle, keyedMutex, fence) );
|
||||
return Move(ret);
|
||||
}
|
||||
|
||||
SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha,
|
||||
GLuint renderbufferGL,
|
||||
const RefPtr<DXGLDevice>& dxgl,
|
||||
HANDLE objectWGL,
|
||||
const RefPtr<ID3D11Texture2D>& textureD3D,
|
||||
HANDLE sharedHandle,
|
||||
const RefPtr<IDXGIKeyedMutex>& keyedMutex,
|
||||
GLuint fence)
|
||||
: SharedSurface(SharedSurfaceType::DXGLInterop2,
|
||||
AttachmentType::GLRenderbuffer,
|
||||
gl,
|
||||
size,
|
||||
hasAlpha,
|
||||
true)
|
||||
, mProdRB(renderbufferGL)
|
||||
, mDXGL(dxgl)
|
||||
, mObjectWGL(objectWGL)
|
||||
, mTextureD3D(textureD3D)
|
||||
, mSharedHandle(sharedHandle)
|
||||
, mKeyedMutex(keyedMutex)
|
||||
, mFence(fence)
|
||||
, mLockedForGL(false)
|
||||
{ }
|
||||
|
||||
SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
|
||||
{
|
||||
MOZ_ASSERT(!mLockedForGL);
|
||||
|
||||
mGL->fDeleteRenderbuffers(1, &mProdRB);
|
||||
|
||||
if (!mDXGL->UnregisterObject(mObjectWGL)) {
|
||||
NS_WARNING("Failed to release a DXGL object, possibly leaking it.");
|
||||
}
|
||||
|
||||
if (mFence) {
|
||||
mGL->MakeCurrent();
|
||||
mGL->fDeleteFences(1, &mFence);
|
||||
}
|
||||
|
||||
// mDXGL is closed when it runs out of refs.
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::LockProdImpl()
|
||||
{ }
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::UnlockProdImpl()
|
||||
{ }
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::Fence()
|
||||
{
|
||||
// TODO fence properly. This kills performance.
|
||||
mGL->fFinish();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_D3D11Interop::WaitSync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_D3D11Interop::PollSync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::ProducerAcquireImpl()
|
||||
{
|
||||
MOZ_ASSERT(!mLockedForGL);
|
||||
|
||||
if (mKeyedMutex) {
|
||||
const uint64_t keyValue = 0;
|
||||
const DWORD timeoutMs = 10000;
|
||||
HRESULT hr = mKeyedMutex->AcquireSync(keyValue, timeoutMs);
|
||||
if (hr == WAIT_TIMEOUT) {
|
||||
// Doubt we should do this? Maybe Wait for ever?
|
||||
MOZ_CRASH("d3d11Interop timeout");
|
||||
}
|
||||
}
|
||||
|
||||
// Now we have the mutex, we can lock for GL.
|
||||
MOZ_ALWAYS_TRUE(mDXGL->LockObject(mObjectWGL));
|
||||
|
||||
mLockedForGL = true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::ProducerReleaseImpl()
|
||||
{
|
||||
MOZ_ASSERT(mLockedForGL);
|
||||
|
||||
mGL->fFlush();
|
||||
MOZ_ALWAYS_TRUE(mDXGL->UnlockObject(mObjectWGL));
|
||||
|
||||
mLockedForGL = false;
|
||||
|
||||
// Now we have unlocked for GL, we can release to consumer.
|
||||
if (mKeyedMutex) {
|
||||
mKeyedMutex->ReleaseSync(0);
|
||||
}
|
||||
Fence();
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::ConsumerAcquireImpl()
|
||||
{
|
||||
if (!mConsumerTexture) {
|
||||
RefPtr<ID3D11Texture2D> tex;
|
||||
RefPtr<ID3D11Device> device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
|
||||
HRESULT hr = device->OpenSharedResource(mSharedHandle,
|
||||
__uuidof(ID3D11Texture2D),
|
||||
(void**)(ID3D11Texture2D**) getter_AddRefs(tex));
|
||||
if (SUCCEEDED(hr)) {
|
||||
mConsumerTexture = tex;
|
||||
RefPtr<IDXGIKeyedMutex> mutex;
|
||||
hr = tex->QueryInterface((IDXGIKeyedMutex**) getter_AddRefs(mutex));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
mConsumerKeyedMutex = mutex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mConsumerKeyedMutex) {
|
||||
const uint64_t keyValue = 0;
|
||||
const DWORD timeoutMs = 10000;
|
||||
HRESULT hr = mConsumerKeyedMutex->AcquireSync(keyValue, timeoutMs);
|
||||
if (hr == WAIT_TIMEOUT) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::ConsumerReleaseImpl()
|
||||
{
|
||||
if (mConsumerKeyedMutex) {
|
||||
mConsumerKeyedMutex->ReleaseSync(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_D3D11Interop::Fence_ContentThread_Impl()
|
||||
{
|
||||
if (mFence) {
|
||||
MOZ_ASSERT(mGL->IsExtensionSupported(GLContext::NV_fence));
|
||||
mGL->fSetFence(mFence, LOCAL_GL_ALL_COMPLETED_NV);
|
||||
mGL->fFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
Fence();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_D3D11Interop::WaitSync_ContentThread_Impl()
|
||||
{
|
||||
if (mFence) {
|
||||
mGL->MakeCurrent();
|
||||
mGL->fFinishFence(mFence);
|
||||
return true;
|
||||
}
|
||||
|
||||
return WaitSync();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_D3D11Interop::PollSync_ContentThread_Impl()
|
||||
{
|
||||
if (mFence) {
|
||||
mGL->MakeCurrent();
|
||||
return mGL->fTestFence(mFence);
|
||||
}
|
||||
|
||||
return PollSync();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_D3D11Interop::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
|
||||
{
|
||||
gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
|
||||
: gfx::SurfaceFormat::B8G8R8X8;
|
||||
*out_descriptor = layers::SurfaceDescriptorD3D10((WindowsHandle)GetSharedHandle(),
|
||||
format, mSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Factory
|
||||
|
||||
/*static*/ UniquePtr<SurfaceFactory_D3D11Interop>
|
||||
SurfaceFactory_D3D11Interop::Create(GLContext* gl, const SurfaceCaps& caps,
|
||||
const RefPtr<layers::ISurfaceAllocator>& allocator,
|
||||
const layers::TextureFlags& flags)
|
||||
{
|
||||
WGLLibrary* wgl = &sWGLLib;
|
||||
if (!wgl || !wgl->HasDXInterop2())
|
||||
return nullptr;
|
||||
|
||||
RefPtr<DXGLDevice> dxgl = DXGLDevice::Open(wgl);
|
||||
if (!dxgl) {
|
||||
NS_WARNING("Failed to open D3D device for use by WGL.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typedef SurfaceFactory_D3D11Interop ptrT;
|
||||
UniquePtr<ptrT> ret(new ptrT(gl, caps, allocator, flags, dxgl));
|
||||
|
||||
return Move(ret);
|
||||
}
|
||||
|
||||
SurfaceFactory_D3D11Interop::SurfaceFactory_D3D11Interop(GLContext* gl,
|
||||
const SurfaceCaps& caps,
|
||||
const RefPtr<layers::ISurfaceAllocator>& allocator,
|
||||
const layers::TextureFlags& flags,
|
||||
const RefPtr<DXGLDevice>& dxgl)
|
||||
: SurfaceFactory(SharedSurfaceType::DXGLInterop2, gl, caps, allocator, flags)
|
||||
, mDXGL(dxgl)
|
||||
{ }
|
||||
|
||||
SurfaceFactory_D3D11Interop::~SurfaceFactory_D3D11Interop()
|
||||
{ }
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,123 @@
|
||||
/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
|
||||
/* 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 SHARED_SURFACE_D3D11_INTEROP_H_
|
||||
#define SHARED_SURFACE_D3D11_INTEROP_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include "SharedSurface.h"
|
||||
|
||||
struct ID3D11Device;
|
||||
struct ID3D11ShaderResourceView;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
class DXGLDevice;
|
||||
class GLContext;
|
||||
class WGLLibrary;
|
||||
|
||||
class SharedSurface_D3D11Interop
|
||||
: public SharedSurface
|
||||
{
|
||||
const GLuint mProdRB;
|
||||
const RefPtr<DXGLDevice> mDXGL;
|
||||
const HANDLE mObjectWGL;
|
||||
const HANDLE mSharedHandle;
|
||||
const RefPtr<ID3D11Texture2D> mTextureD3D;
|
||||
RefPtr<IDXGIKeyedMutex> mKeyedMutex;
|
||||
RefPtr<IDXGIKeyedMutex> mConsumerKeyedMutex;
|
||||
RefPtr<ID3D11Texture2D> mConsumerTexture;
|
||||
const GLuint mFence;
|
||||
|
||||
protected:
|
||||
bool mLockedForGL;
|
||||
|
||||
public:
|
||||
static UniquePtr<SharedSurface_D3D11Interop> Create(const RefPtr<DXGLDevice>& dxgl,
|
||||
GLContext* gl,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha);
|
||||
|
||||
static SharedSurface_D3D11Interop* Cast(SharedSurface* surf) {
|
||||
MOZ_ASSERT(surf->mType == SharedSurfaceType::DXGLInterop2);
|
||||
|
||||
return (SharedSurface_D3D11Interop*)surf;
|
||||
}
|
||||
|
||||
protected:
|
||||
SharedSurface_D3D11Interop(GLContext* gl,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha,
|
||||
GLuint renderbufferGL,
|
||||
const RefPtr<DXGLDevice>& dxgl,
|
||||
HANDLE objectWGL,
|
||||
const RefPtr<ID3D11Texture2D>& textureD3D,
|
||||
HANDLE sharedHandle,
|
||||
const RefPtr<IDXGIKeyedMutex>& keyedMutex,
|
||||
GLuint fence);
|
||||
|
||||
public:
|
||||
virtual ~SharedSurface_D3D11Interop();
|
||||
|
||||
virtual void LockProdImpl() override;
|
||||
virtual void UnlockProdImpl() override;
|
||||
|
||||
virtual void Fence() override;
|
||||
virtual void ProducerAcquireImpl() override;
|
||||
virtual void ProducerReleaseImpl() override;
|
||||
virtual void ConsumerAcquireImpl() override;
|
||||
virtual void ConsumerReleaseImpl() override;
|
||||
virtual bool WaitSync() override;
|
||||
virtual bool PollSync() override;
|
||||
|
||||
virtual void Fence_ContentThread_Impl() override;
|
||||
virtual bool WaitSync_ContentThread_Impl() override;
|
||||
virtual bool PollSync_ContentThread_Impl() override;
|
||||
|
||||
virtual GLuint ProdRenderbuffer() override {
|
||||
return mProdRB;
|
||||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
// Implementation-specific functions below:
|
||||
HANDLE GetSharedHandle() const {
|
||||
return mSharedHandle;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SurfaceFactory_D3D11Interop
|
||||
: public SurfaceFactory
|
||||
{
|
||||
public:
|
||||
const RefPtr<DXGLDevice> mDXGL;
|
||||
|
||||
static UniquePtr<SurfaceFactory_D3D11Interop> Create(GLContext* gl,
|
||||
const SurfaceCaps& caps,
|
||||
const RefPtr<layers::ISurfaceAllocator>& allocator,
|
||||
const layers::TextureFlags& flags);
|
||||
|
||||
protected:
|
||||
SurfaceFactory_D3D11Interop(GLContext* gl, const SurfaceCaps& caps,
|
||||
const RefPtr<layers::ISurfaceAllocator>& allocator,
|
||||
const layers::TextureFlags& flags,
|
||||
const RefPtr<DXGLDevice>& dxgl);
|
||||
|
||||
public:
|
||||
virtual ~SurfaceFactory_D3D11Interop();
|
||||
|
||||
protected:
|
||||
virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
|
||||
bool hasAlpha = mReadCaps.alpha;
|
||||
return SharedSurface_D3D11Interop::Create(mDXGL, mGL, size, hasAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* SHARED_SURFACE_D3D11_INTEROP_H_ */
|
||||
@@ -95,6 +95,7 @@ SharedSurface_Basic::~SharedSurface_Basic()
|
||||
mGL->fDeleteTextures(1, &mTex);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SurfaceFactory_Basic::SurfaceFactory_Basic(GLContext* gl, const SurfaceCaps& caps,
|
||||
@@ -102,6 +103,88 @@ SurfaceFactory_Basic::SurfaceFactory_Basic(GLContext* gl, const SurfaceCaps& cap
|
||||
: SurfaceFactory(SharedSurfaceType::Basic, gl, caps, nullptr, flags)
|
||||
{ }
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// SharedSurface_GLTexture
|
||||
|
||||
/*static*/ UniquePtr<SharedSurface_GLTexture>
|
||||
SharedSurface_GLTexture::Create(GLContext* prodGL,
|
||||
const GLFormats& formats,
|
||||
const IntSize& size,
|
||||
bool hasAlpha)
|
||||
{
|
||||
MOZ_ASSERT(prodGL);
|
||||
|
||||
prodGL->MakeCurrent();
|
||||
|
||||
UniquePtr<SharedSurface_GLTexture> ret;
|
||||
GLContext::LocalErrorScope localError(*prodGL);
|
||||
|
||||
GLuint tex = CreateTextureForOffscreen(prodGL, formats, size);
|
||||
|
||||
GLenum err = localError.GetError();
|
||||
MOZ_ASSERT_IF(err, err == LOCAL_GL_OUT_OF_MEMORY);
|
||||
if (err) {
|
||||
prodGL->fDeleteTextures(1, &tex);
|
||||
return Move(ret);
|
||||
}
|
||||
|
||||
ret.reset(new SharedSurface_GLTexture(prodGL, size,
|
||||
hasAlpha, tex));
|
||||
return Move(ret);
|
||||
}
|
||||
|
||||
SharedSurface_GLTexture::~SharedSurface_GLTexture()
|
||||
{
|
||||
if (!mGL->MakeCurrent())
|
||||
return;
|
||||
|
||||
if (mTex) {
|
||||
mGL->fDeleteTextures(1, &mTex);
|
||||
}
|
||||
|
||||
if (mSync) {
|
||||
mGL->fDeleteSync(mSync);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedSurface_GLTexture::ProducerReleaseImpl()
|
||||
{
|
||||
mGL->MakeCurrent();
|
||||
|
||||
if (mGL->IsExtensionSupported(GLContext::ARB_sync)) {
|
||||
if (mSync) {
|
||||
mGL->fDeleteSync(mSync);
|
||||
mSync = 0;
|
||||
}
|
||||
|
||||
mSync = mGL->fFenceSync(LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
if (mSync) {
|
||||
mGL->fFlush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(!mSync);
|
||||
|
||||
mGL->fFinish();
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_GLTexture::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
|
||||
{
|
||||
*out_descriptor = layers::SurfaceDescriptorSharedGLTexture(ProdTexture(),
|
||||
ProdTextureTarget(),
|
||||
(uintptr_t)mSync,
|
||||
mSize,
|
||||
mHasAlpha);
|
||||
|
||||
// Transfer ownership of the fence to the host
|
||||
mSync = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
@@ -93,6 +93,78 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Using shared GL textures:
|
||||
class SharedSurface_GLTexture
|
||||
: public SharedSurface
|
||||
{
|
||||
public:
|
||||
static UniquePtr<SharedSurface_GLTexture> Create(GLContext* prodGL,
|
||||
const GLFormats& formats,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha);
|
||||
|
||||
static SharedSurface_GLTexture* Cast(SharedSurface* surf) {
|
||||
MOZ_ASSERT(surf->mType == SharedSurfaceType::SharedGLTexture);
|
||||
|
||||
return (SharedSurface_GLTexture*)surf;
|
||||
}
|
||||
|
||||
protected:
|
||||
const GLuint mTex;
|
||||
GLsync mSync;
|
||||
|
||||
SharedSurface_GLTexture(GLContext* prodGL,
|
||||
const gfx::IntSize& size,
|
||||
bool hasAlpha,
|
||||
GLuint tex)
|
||||
: SharedSurface(SharedSurfaceType::SharedGLTexture,
|
||||
AttachmentType::GLTexture,
|
||||
prodGL,
|
||||
size,
|
||||
hasAlpha, true)
|
||||
, mTex(tex)
|
||||
, mSync(0)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~SharedSurface_GLTexture();
|
||||
|
||||
virtual void LockProdImpl() override {}
|
||||
virtual void UnlockProdImpl() override {}
|
||||
|
||||
virtual void ProducerReleaseImpl() override;
|
||||
|
||||
virtual void Fence() override {}
|
||||
virtual bool WaitSync() override { MOZ_CRASH("should not be called"); }
|
||||
virtual bool PollSync() override { MOZ_CRASH("should not be called"); }
|
||||
|
||||
virtual GLuint ProdTexture() override {
|
||||
return mTex;
|
||||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
};
|
||||
|
||||
class SurfaceFactory_GLTexture
|
||||
: public SurfaceFactory
|
||||
{
|
||||
public:
|
||||
SurfaceFactory_GLTexture(GLContext* prodGL,
|
||||
const SurfaceCaps& caps,
|
||||
const RefPtr<layers::ISurfaceAllocator>& allocator,
|
||||
const layers::TextureFlags& flags)
|
||||
: SurfaceFactory(SharedSurfaceType::SharedGLTexture, prodGL, caps, allocator, flags)
|
||||
{
|
||||
}
|
||||
|
||||
virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) override {
|
||||
bool hasAlpha = mReadCaps.alpha;
|
||||
return SharedSurface_GLTexture::Create(mGL, mFormats, size, hasAlpha);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
@@ -55,6 +55,16 @@ SharedSurface_IOSurface::CopyTexImage2D(GLenum target, GLint level, GLenum inter
|
||||
if (width == 0 || height == 0)
|
||||
return false;
|
||||
|
||||
switch (internalformat) {
|
||||
case LOCAL_GL_ALPHA:
|
||||
case LOCAL_GL_LUMINANCE:
|
||||
case LOCAL_GL_LUMINANCE_ALPHA:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGL->IsCurrent());
|
||||
|
||||
ScopedTexture destTex(mGL);
|
||||
@@ -90,23 +100,22 @@ SharedSurface_IOSurface::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei hei
|
||||
// from that.
|
||||
MOZ_ASSERT(mGL->IsCurrent());
|
||||
|
||||
|
||||
ScopedTexture destTex(mGL);
|
||||
{
|
||||
ScopedFramebufferForTexture srcFB(mGL, ProdTexture(), ProdTextureTarget());
|
||||
|
||||
ScopedBindFramebuffer bindFB(mGL, srcFB.FB());
|
||||
ScopedBindTexture bindTex(mGL, destTex.Texture());
|
||||
mGL->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
|
||||
mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB,
|
||||
x, y,
|
||||
width, height, 0);
|
||||
mGL->raw_fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0,
|
||||
mHasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB,
|
||||
x, y,
|
||||
width, height, 0);
|
||||
}
|
||||
|
||||
ScopedFramebufferForTexture destFB(mGL, destTex.Texture());
|
||||
|
||||
ScopedBindFramebuffer bindFB(mGL, destFB.FB());
|
||||
mGL->fReadPixels(0, 0, width, height, format, type, pixels);
|
||||
mGL->raw_fReadPixels(0, 0, width, height, format, type, pixels);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ enum class SharedSurfaceType : uint8_t {
|
||||
Gralloc,
|
||||
IOSurface,
|
||||
GLXDrawable,
|
||||
SharedGLTexture,
|
||||
|
||||
Max
|
||||
};
|
||||
|
||||
+48
-10
@@ -14,14 +14,16 @@ namespace gl {
|
||||
class WGLLibrary
|
||||
{
|
||||
public:
|
||||
WGLLibrary()
|
||||
: mInitialized(false),
|
||||
mOGLLibrary(nullptr),
|
||||
mHasRobustness(false),
|
||||
mWindow (0),
|
||||
mWindowDC(0),
|
||||
mWindowGLContext(0),
|
||||
mWindowPixelFormat (0)
|
||||
WGLLibrary()
|
||||
: mInitialized(false)
|
||||
, mOGLLibrary(nullptr)
|
||||
, mHasRobustness(false)
|
||||
, mHasDXInterop(false)
|
||||
, mHasDXInterop2(false)
|
||||
, mWindow (0)
|
||||
, mWindowDC(0)
|
||||
, mWindowGLContext(0)
|
||||
, mWindowPixelFormat(0)
|
||||
{}
|
||||
|
||||
typedef HGLRC (GLAPIENTRY * PFNWGLCREATECONTEXTPROC) (HDC);
|
||||
@@ -62,10 +64,45 @@ public:
|
||||
typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSPROC) (HDC hdc, HGLRC hShareContext, const int *attribList);
|
||||
PFNWGLCREATECONTEXTATTRIBSPROC fCreateContextAttribs;
|
||||
|
||||
// WGL_NV_DX_interop:
|
||||
// BOOL wglDXSetResourceShareHandleNV(void *dxObject, HANDLE shareHandle);
|
||||
typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLEPROC) (void* dxObject, HANDLE shareHandle);
|
||||
PFNWGLDXSETRESOURCESHAREHANDLEPROC fDXSetResourceShareHandle;
|
||||
|
||||
// HANDLE wglDXOpenDeviceNV(void *dxDevice);
|
||||
typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICEPROC) (void* dxDevice);
|
||||
PFNWGLDXOPENDEVICEPROC fDXOpenDevice;
|
||||
|
||||
// BOOL wglDXCloseDeviceNV(HANDLE hDevice);
|
||||
typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICEPROC) (HANDLE hDevice);
|
||||
PFNWGLDXCLOSEDEVICEPROC fDXCloseDevice;
|
||||
|
||||
// HANDLE wglDXRegisterObjectNV(HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access);
|
||||
typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTPROC) (HANDLE hDevice, void* dxObject, GLuint name, GLenum type, GLenum access);
|
||||
PFNWGLDXREGISTEROBJECTPROC fDXRegisterObject;
|
||||
|
||||
// BOOL wglDXUnregisterObjectNV(HANDLE hDevice, HANDLE hObject);
|
||||
typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECT) (HANDLE hDevice, HANDLE hObject);
|
||||
PFNWGLDXUNREGISTEROBJECT fDXUnregisterObject;
|
||||
|
||||
// BOOL wglDXObjectAccessNV(HANDLE hObject, GLenum access);
|
||||
typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSPROC) (HANDLE hObject, GLenum access);
|
||||
PFNWGLDXOBJECTACCESSPROC fDXObjectAccess;
|
||||
|
||||
// BOOL wglDXLockObjectsNV(HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSPROC) (HANDLE hDevice, GLint count, HANDLE* hObjects);
|
||||
PFNWGLDXLOCKOBJECTSPROC fDXLockObjects;
|
||||
|
||||
// BOOL wglDXUnlockObjectsNV(HANDLE hDevice, GLint count, HANDLE *hObjects);
|
||||
typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSPROC) (HANDLE hDevice, GLint count, HANDLE* hObjects);
|
||||
PFNWGLDXUNLOCKOBJECTSPROC fDXUnlockObjects;
|
||||
|
||||
bool EnsureInitialized();
|
||||
HWND CreateDummyWindow(HDC *aWindowDC = nullptr);
|
||||
|
||||
bool HasRobustness() const { return mHasRobustness; }
|
||||
bool HasDXInterop() const { return mHasDXInterop; }
|
||||
bool HasDXInterop2() const { return mHasDXInterop2; }
|
||||
bool IsInitialized() const { return mInitialized; }
|
||||
HWND GetWindow() const { return mWindow; }
|
||||
HDC GetWindowDC() const {return mWindowDC; }
|
||||
@@ -77,6 +114,8 @@ private:
|
||||
bool mInitialized;
|
||||
PRLibrary *mOGLLibrary;
|
||||
bool mHasRobustness;
|
||||
bool mHasDXInterop;
|
||||
bool mHasDXInterop2;
|
||||
|
||||
HWND mWindow;
|
||||
HDC mWindowDC;
|
||||
@@ -86,8 +125,7 @@ private:
|
||||
};
|
||||
|
||||
// a global WGLLibrary instance
|
||||
extern WGLLibrary sWGLLibrary;
|
||||
extern WGLLibrary sWGLLib;
|
||||
|
||||
} /* namespace gl */
|
||||
} /* namespace mozilla */
|
||||
|
||||
|
||||
@@ -68,11 +68,13 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
EXPORTS += [
|
||||
'GLContextWGL.h',
|
||||
'SharedSurfaceANGLE.h', # Needs <windows.h> for `HANDLE`.
|
||||
'SharedSurfaceD3D11Interop.h',
|
||||
'WGLLibrary.h',
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
'GLContextProviderWGL.cpp',
|
||||
'SharedSurfaceANGLE.cpp',
|
||||
'SharedSurfaceD3D11Interop.cpp',
|
||||
]
|
||||
if CONFIG['MOZ_ENABLE_SKIA_GPU']:
|
||||
EXPORTS += ['SkiaGLGlue.h']
|
||||
|
||||
+36
-47
@@ -891,51 +891,6 @@ struct ParamTraits<mozilla::layers::EventRegions>
|
||||
}
|
||||
};
|
||||
|
||||
struct MessageAndAttributeMap
|
||||
{
|
||||
Message* msg;
|
||||
const mozilla::gfx::AttributeMap& map;
|
||||
};
|
||||
|
||||
static bool
|
||||
WriteAttribute(mozilla::gfx::AttributeName aName,
|
||||
mozilla::gfx::AttributeType aType,
|
||||
void* aUserData)
|
||||
{
|
||||
MessageAndAttributeMap* msgAndMap =
|
||||
static_cast<MessageAndAttributeMap*>(aUserData);
|
||||
|
||||
WriteParam(msgAndMap->msg, aType);
|
||||
WriteParam(msgAndMap->msg, aName);
|
||||
|
||||
switch (aType) {
|
||||
|
||||
#define HANDLE_TYPE(typeName) \
|
||||
case mozilla::gfx::AttributeType::e##typeName: \
|
||||
WriteParam(msgAndMap->msg, msgAndMap->map.Get##typeName(aName)); \
|
||||
break;
|
||||
|
||||
HANDLE_TYPE(Bool)
|
||||
HANDLE_TYPE(Uint)
|
||||
HANDLE_TYPE(Float)
|
||||
HANDLE_TYPE(Size)
|
||||
HANDLE_TYPE(IntSize)
|
||||
HANDLE_TYPE(IntPoint)
|
||||
HANDLE_TYPE(Matrix)
|
||||
HANDLE_TYPE(Matrix5x4)
|
||||
HANDLE_TYPE(Point3D)
|
||||
HANDLE_TYPE(Color)
|
||||
HANDLE_TYPE(AttributeMap)
|
||||
HANDLE_TYPE(Floats)
|
||||
|
||||
#undef HANDLE_TYPE
|
||||
|
||||
default:
|
||||
MOZ_CRASH("unhandled attribute type");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::gfx::AttributeMap>
|
||||
{
|
||||
@@ -944,8 +899,41 @@ struct ParamTraits<mozilla::gfx::AttributeMap>
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.Count());
|
||||
MessageAndAttributeMap msgAndMap = { aMsg, aParam };
|
||||
aParam.EnumerateRead(WriteAttribute, &msgAndMap);
|
||||
for (auto iter = aParam.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
mozilla::gfx::AttributeName name =
|
||||
mozilla::gfx::AttributeName(iter.Key());
|
||||
mozilla::gfx::AttributeType type =
|
||||
mozilla::gfx::AttributeMap::GetType(iter.UserData());
|
||||
|
||||
WriteParam(aMsg, type);
|
||||
WriteParam(aMsg, name);
|
||||
|
||||
switch (type) {
|
||||
|
||||
#define CASE_TYPE(typeName) \
|
||||
case mozilla::gfx::AttributeType::e##typeName: \
|
||||
WriteParam(aMsg, aParam.Get##typeName(name)); \
|
||||
break;
|
||||
|
||||
CASE_TYPE(Bool)
|
||||
CASE_TYPE(Uint)
|
||||
CASE_TYPE(Float)
|
||||
CASE_TYPE(Size)
|
||||
CASE_TYPE(IntSize)
|
||||
CASE_TYPE(IntPoint)
|
||||
CASE_TYPE(Matrix)
|
||||
CASE_TYPE(Matrix5x4)
|
||||
CASE_TYPE(Point3D)
|
||||
CASE_TYPE(Color)
|
||||
CASE_TYPE(AttributeMap)
|
||||
CASE_TYPE(Floats)
|
||||
|
||||
#undef CASE_TYPE
|
||||
|
||||
default:
|
||||
MOZ_CRASH("unhandled attribute type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
@@ -973,6 +961,7 @@ struct ParamTraits<mozilla::gfx::AttributeMap>
|
||||
return false; \
|
||||
} \
|
||||
aResult->Set(name, value); \
|
||||
break; \
|
||||
}
|
||||
|
||||
HANDLE_TYPE(bool, Bool)
|
||||
|
||||
@@ -74,15 +74,6 @@ public:
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
uint64_t aInputBlockId) = 0;
|
||||
|
||||
/**
|
||||
* Requests sending a mozbrowserasyncscroll domevent to embedder.
|
||||
* |aContentRect| is in CSS pixels, relative to the current cssPage.
|
||||
* |aScrollableSize| is the current content width/height in CSS pixels.
|
||||
*/
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRootContent,
|
||||
const CSSRect &aContentRect,
|
||||
const CSSSize &aScrollableSize) = 0;
|
||||
|
||||
/**
|
||||
* Schedules a runnable to run on the controller/UI thread at some time
|
||||
* in the future.
|
||||
|
||||
@@ -104,14 +104,6 @@ using mozilla::gfx::PointTyped;
|
||||
* \li\b apz.allow_checkerboarding
|
||||
* Pref that allows or disallows checkerboarding
|
||||
*
|
||||
* \li\b apz.asyncscroll.throttle
|
||||
* The time period that throttles mozbrowserasyncscroll event.\n
|
||||
* Units: milliseconds
|
||||
*
|
||||
* \li\b apz.asyncscroll.timeout
|
||||
* The timeout for mAsyncScrollTimeoutTask delay task.\n
|
||||
* Units: milliseconds
|
||||
*
|
||||
* \li\b apz.axis_lock.mode
|
||||
* The preferred axis locking style. See AxisLockMode for possible values.
|
||||
*
|
||||
@@ -824,10 +816,6 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
|
||||
mPanDirRestricted(false),
|
||||
mZoomConstraints(false, false, MIN_ZOOM, MAX_ZOOM),
|
||||
mLastSampleTime(GetFrameTime()),
|
||||
mLastAsyncScrollTime(GetFrameTime()),
|
||||
mLastAsyncScrollOffset(0, 0),
|
||||
mCurrentAsyncScrollOffset(0, 0),
|
||||
mAsyncScrollTimeoutTask(nullptr),
|
||||
mState(NOTHING),
|
||||
mNotificationBlockers(0),
|
||||
mInputQueue(aInputQueue),
|
||||
@@ -1285,7 +1273,6 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
|
||||
// events.
|
||||
if (mState != NOTHING) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
SendAsyncScrollEvent();
|
||||
}
|
||||
|
||||
switch (mState) {
|
||||
@@ -2711,7 +2698,6 @@ void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics,
|
||||
return;
|
||||
}
|
||||
|
||||
SendAsyncScrollEvent();
|
||||
if (aThrottled) {
|
||||
mPaintThrottler->PostTask(
|
||||
FROM_HERE,
|
||||
@@ -2755,16 +2741,6 @@ AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncPanZoomController::FireAsyncScrollOnTimeout()
|
||||
{
|
||||
if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
SendAsyncScrollEvent();
|
||||
}
|
||||
mAsyncScrollTimeoutTask = nullptr;
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
|
||||
Vector<Task*>* aOutDeferredTasks)
|
||||
{
|
||||
@@ -2791,7 +2767,6 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
|
||||
} else {
|
||||
mAnimation = nullptr;
|
||||
SetState(NOTHING);
|
||||
SendAsyncScrollEvent();
|
||||
RequestContentRepaint();
|
||||
}
|
||||
UpdateSharedCompositorFrameMetrics();
|
||||
@@ -2870,8 +2845,6 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
|
||||
LogRendertraceRect(GetGuid(), "viewport", "red",
|
||||
CSSRect(mFrameMetrics.GetScrollOffset(),
|
||||
mFrameMetrics.CalculateCompositedSizeInCssPixels()));
|
||||
|
||||
mCurrentAsyncScrollOffset = mFrameMetrics.GetScrollOffset();
|
||||
}
|
||||
|
||||
// Execute any deferred tasks queued up by mAnimation's Sample() (called by
|
||||
@@ -2887,32 +2860,6 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
|
||||
// we want to ask the compositor to schedule a new composite.
|
||||
requestAnimationFrame |= (mAnimation != nullptr);
|
||||
|
||||
// Cancel the mAsyncScrollTimeoutTask because we will fire a
|
||||
// mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
|
||||
if (mAsyncScrollTimeoutTask) {
|
||||
mAsyncScrollTimeoutTask->Cancel();
|
||||
mAsyncScrollTimeoutTask = nullptr;
|
||||
}
|
||||
// Fire the mozbrowserasyncscroll event immediately if it's been
|
||||
// sAsyncScrollThrottleTime ms since the last time we fired the event and the
|
||||
// current scroll offset is different than the mLastAsyncScrollOffset we sent
|
||||
// with the last event.
|
||||
// Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
|
||||
TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
|
||||
if (delta.ToMilliseconds() > gfxPrefs::APZAsyncScrollThrottleTime() &&
|
||||
mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
mLastAsyncScrollTime = aSampleTime;
|
||||
mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
|
||||
SendAsyncScrollEvent();
|
||||
} else {
|
||||
mAsyncScrollTimeoutTask =
|
||||
NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
|
||||
MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
||||
mAsyncScrollTimeoutTask,
|
||||
gfxPrefs::APZAsyncScrollTimeout());
|
||||
}
|
||||
|
||||
return requestAnimationFrame;
|
||||
}
|
||||
|
||||
@@ -3408,27 +3355,6 @@ void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::SendAsyncScrollEvent() {
|
||||
RefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool isRootContent;
|
||||
CSSRect contentRect;
|
||||
CSSSize scrollableSize;
|
||||
{
|
||||
ReentrantMonitorAutoEnter lock(mMonitor);
|
||||
|
||||
isRootContent = mFrameMetrics.IsRootContent();
|
||||
scrollableSize = mFrameMetrics.GetScrollableRect().Size();
|
||||
contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
|
||||
contentRect.MoveTo(mCurrentAsyncScrollOffset);
|
||||
}
|
||||
|
||||
controller->SendAsyncScrollDOMEvent(isRootContent, contentRect, scrollableSize);
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
return aGuid == GetGuid();
|
||||
|
||||
@@ -261,12 +261,6 @@ public:
|
||||
const ParentLayerPoint& aVelocity,
|
||||
double aEstimatedPaintDuration);
|
||||
|
||||
/**
|
||||
* Send an mozbrowserasyncscroll event.
|
||||
* *** The monitor must be held while calling this.
|
||||
*/
|
||||
void SendAsyncScrollEvent();
|
||||
|
||||
nsEventStatus HandleDragEvent(const MouseInput& aEvent,
|
||||
const AsyncDragMetrics& aDragMetrics);
|
||||
|
||||
@@ -617,14 +611,6 @@ protected:
|
||||
*/
|
||||
const RefPtr<InputQueue>& GetInputQueue() const;
|
||||
|
||||
/**
|
||||
* Timeout function for mozbrowserasyncscroll event. Because we throttle
|
||||
* mozbrowserasyncscroll events in some conditions, this function ensures
|
||||
* that the last mozbrowserasyncscroll event will be fired after a period of
|
||||
* time.
|
||||
*/
|
||||
void FireAsyncScrollOnTimeout();
|
||||
|
||||
/**
|
||||
* Convert ScreenPoint relative to the screen to CSSPoint relative
|
||||
* to the parent document. This excludes the transient compositor transform.
|
||||
@@ -731,19 +717,6 @@ private:
|
||||
// to allow panning by moving multiple fingers (thus moving the focus point).
|
||||
ParentLayerPoint mLastZoomFocus;
|
||||
|
||||
// The last time and offset we fire the mozbrowserasyncscroll event when
|
||||
// compositor has sampled the content transform for this frame.
|
||||
TimeStamp mLastAsyncScrollTime;
|
||||
CSSPoint mLastAsyncScrollOffset;
|
||||
|
||||
// The current offset drawn on the screen, it may not be sent since we have
|
||||
// throttling policy for mozbrowserasyncscroll event.
|
||||
CSSPoint mCurrentAsyncScrollOffset;
|
||||
|
||||
// The delay task triggered by the throttling mozbrowserasyncscroll event
|
||||
// ensures the last mozbrowserasyncscroll event is always been fired.
|
||||
CancelableTask* mAsyncScrollTimeoutTask;
|
||||
|
||||
RefPtr<AsyncPanZoomAnimation> mAnimation;
|
||||
|
||||
friend class Axis;
|
||||
|
||||
@@ -67,7 +67,6 @@ public:
|
||||
MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
|
||||
MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
|
||||
MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
|
||||
MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
|
||||
MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
|
||||
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
|
||||
MOCK_METHOD0(NotifyFlushComplete, void());
|
||||
@@ -791,10 +790,8 @@ protected:
|
||||
MakeApzcZoomable();
|
||||
|
||||
if (aShouldTriggerPinch) {
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtMost(2));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
|
||||
@@ -927,7 +924,6 @@ TEST_F(APZCBasicTester, Overzoom) {
|
||||
|
||||
MakeApzcZoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
|
||||
PinchWithPinchInputAndCheckStatus(apzc, 50, 50, 0.5, true);
|
||||
@@ -1056,10 +1052,8 @@ protected:
|
||||
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
|
||||
{
|
||||
if (aShouldTriggerScroll) {
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
} else {
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
}
|
||||
|
||||
@@ -1169,7 +1163,6 @@ TEST_F(APZCPanningTester, PanWithPreventDefault) {
|
||||
}
|
||||
|
||||
TEST_F(APZCBasicTester, Fling) {
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
|
||||
|
||||
int touchStart = 50;
|
||||
@@ -1591,7 +1584,6 @@ protected:
|
||||
void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
|
||||
MakeApzcUnzoomable();
|
||||
|
||||
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(0);
|
||||
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
|
||||
|
||||
int touchX = 10,
|
||||
|
||||
@@ -49,8 +49,6 @@ public:
|
||||
virtual void HandleLongTap(const mozilla::CSSPoint& aPoint, Modifiers aModifiers,
|
||||
const ScrollableLayerGuid& aGuid,
|
||||
uint64_t aInputBlockId) override;
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRootContent, const mozilla::CSSRect &aContentRect,
|
||||
const mozilla::CSSSize &aScrollableSize) override {}
|
||||
virtual void NotifyAPZStateChange(const ScrollableLayerGuid& aGuid,
|
||||
APZStateChange aChange,
|
||||
int aArg) override;
|
||||
|
||||
@@ -210,6 +210,7 @@ TextureHost::Create(const SurfaceDescriptor& aDesc,
|
||||
|
||||
case SurfaceDescriptor::TEGLImageDescriptor:
|
||||
case SurfaceDescriptor::TSurfaceTextureDescriptor:
|
||||
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
|
||||
return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
|
||||
|
||||
case SurfaceDescriptor::TNewSurfaceDescriptorGralloc:
|
||||
|
||||
@@ -82,6 +82,14 @@ struct EGLImageDescriptor {
|
||||
bool hasAlpha;
|
||||
};
|
||||
|
||||
struct SurfaceDescriptorSharedGLTexture {
|
||||
uint32_t texture;
|
||||
uint32_t target;
|
||||
uintptr_t fence;
|
||||
IntSize size;
|
||||
bool hasAlpha;
|
||||
};
|
||||
|
||||
struct NewSurfaceDescriptorGralloc {
|
||||
MaybeMagicGrallocBufferHandle buffer;
|
||||
bool isOpaque;
|
||||
@@ -116,6 +124,7 @@ union SurfaceDescriptor {
|
||||
EGLImageDescriptor;
|
||||
SurfaceDescriptorMacIOSurface;
|
||||
NewSurfaceDescriptorGralloc;
|
||||
SurfaceDescriptorSharedGLTexture;
|
||||
null_t;
|
||||
};
|
||||
|
||||
|
||||
@@ -104,6 +104,16 @@ CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture: {
|
||||
const auto& desc = aDesc.get_SurfaceDescriptorSharedGLTexture();
|
||||
result = new GLTextureHost(aFlags, desc.texture(),
|
||||
desc.target(),
|
||||
(GLsync)desc.fence(),
|
||||
desc.size(),
|
||||
desc.hasAlpha());
|
||||
break;
|
||||
}
|
||||
default: return nullptr;
|
||||
}
|
||||
return result.forget();
|
||||
@@ -645,5 +655,76 @@ EGLImageTextureHost::GetFormat() const
|
||||
return mTextureSource->GetFormat();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
GLTextureHost::GLTextureHost(TextureFlags aFlags,
|
||||
GLuint aTextureHandle,
|
||||
GLenum aTarget,
|
||||
GLsync aSync,
|
||||
gfx::IntSize aSize,
|
||||
bool aHasAlpha)
|
||||
: TextureHost(aFlags)
|
||||
, mTexture(aTextureHandle)
|
||||
, mTarget(aTarget)
|
||||
, mSync(aSync)
|
||||
, mSize(aSize)
|
||||
, mHasAlpha(aHasAlpha)
|
||||
, mCompositor(nullptr)
|
||||
{}
|
||||
|
||||
GLTextureHost::~GLTextureHost()
|
||||
{}
|
||||
|
||||
gl::GLContext*
|
||||
GLTextureHost::gl() const
|
||||
{
|
||||
return mCompositor ? mCompositor->gl() : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
GLTextureHost::Lock()
|
||||
{
|
||||
if (!mCompositor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mSync) {
|
||||
gl()->MakeCurrent();
|
||||
gl()->fWaitSync(mSync, 0, LOCAL_GL_TIMEOUT_IGNORED);
|
||||
gl()->fDeleteSync(mSync);
|
||||
mSync = 0;
|
||||
}
|
||||
|
||||
if (!mTextureSource) {
|
||||
gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
|
||||
: gfx::SurfaceFormat::R8G8B8X8;
|
||||
mTextureSource = new GLTextureSource(mCompositor,
|
||||
mTexture,
|
||||
mTarget,
|
||||
mSize,
|
||||
format,
|
||||
false /* owned by the client */);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void
|
||||
GLTextureHost::SetCompositor(Compositor* aCompositor)
|
||||
{
|
||||
MOZ_ASSERT(aCompositor);
|
||||
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
|
||||
mCompositor = glCompositor;
|
||||
if (mTextureSource) {
|
||||
mTextureSource->SetCompositor(glCompositor);
|
||||
}
|
||||
}
|
||||
|
||||
gfx::SurfaceFormat
|
||||
GLTextureHost::GetFormat() const
|
||||
{
|
||||
MOZ_ASSERT(mTextureSource);
|
||||
return mTextureSource->GetFormat();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -278,6 +278,56 @@ protected:
|
||||
bool mExternallyOwned;
|
||||
};
|
||||
|
||||
class GLTextureHost : public TextureHost
|
||||
{
|
||||
public:
|
||||
GLTextureHost(TextureFlags aFlags,
|
||||
GLuint aTextureHandle,
|
||||
GLenum aTarget,
|
||||
GLsync aSync,
|
||||
gfx::IntSize aSize,
|
||||
bool aHasAlpha);
|
||||
|
||||
virtual ~GLTextureHost();
|
||||
|
||||
// We don't own anything.
|
||||
virtual void DeallocateDeviceData() override {}
|
||||
|
||||
virtual void SetCompositor(Compositor* aCompositor) override;
|
||||
|
||||
virtual bool Lock() override;
|
||||
|
||||
virtual void Unlock() override {}
|
||||
|
||||
virtual gfx::SurfaceFormat GetFormat() const override;
|
||||
|
||||
virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
|
||||
{
|
||||
aTexture = mTextureSource;
|
||||
return !!aTexture;
|
||||
}
|
||||
|
||||
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
|
||||
{
|
||||
return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
|
||||
}
|
||||
|
||||
gl::GLContext* gl() const;
|
||||
|
||||
virtual gfx::IntSize GetSize() const override { return mSize; }
|
||||
|
||||
virtual const char* Name() override { return "GLTextureHost"; }
|
||||
|
||||
protected:
|
||||
const GLuint mTexture;
|
||||
const GLenum mTarget;
|
||||
GLsync mSync;
|
||||
const gfx::IntSize mSize;
|
||||
const bool mHasAlpha;
|
||||
RefPtr<CompositorOGL> mCompositor;
|
||||
RefPtr<GLTextureSource> mTextureSource;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// SurfaceTexture
|
||||
|
||||
|
||||
+31
-66
@@ -2047,20 +2047,13 @@ AttributeMap::~AttributeMap()
|
||||
{
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CopyAttribute(const uint32_t& aAttributeName,
|
||||
Attribute* aAttribute,
|
||||
void* aAttributes)
|
||||
{
|
||||
typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
|
||||
Map* map = static_cast<Map*>(aAttributes);
|
||||
map->Put(aAttributeName, new Attribute(*aAttribute));
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
AttributeMap::AttributeMap(const AttributeMap& aOther)
|
||||
{
|
||||
aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
|
||||
for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
const uint32_t& attributeName = iter.Key();
|
||||
Attribute* attribute = iter.UserData();
|
||||
mMap.Put(attributeName, new Attribute(*attribute));
|
||||
}
|
||||
}
|
||||
|
||||
AttributeMap&
|
||||
@@ -2068,34 +2061,15 @@ AttributeMap::operator=(const AttributeMap& aOther)
|
||||
{
|
||||
if (this != &aOther) {
|
||||
mMap.Clear();
|
||||
aOther.mMap.EnumerateRead(CopyAttribute, &mMap);
|
||||
for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
const uint32_t& attributeName = iter.Key();
|
||||
Attribute* attribute = iter.UserData();
|
||||
mMap.Put(attributeName, new Attribute(*attribute));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct MatchingMap {
|
||||
typedef nsClassHashtable<nsUint32HashKey, Attribute> Map;
|
||||
const Map& map;
|
||||
bool matches;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static PLDHashOperator
|
||||
CheckAttributeEquality(const uint32_t& aAttributeName,
|
||||
Attribute* aAttribute,
|
||||
void* aMatchingMap)
|
||||
{
|
||||
MatchingMap& matchingMap = *static_cast<MatchingMap*>(aMatchingMap);
|
||||
Attribute* matchingAttribute = matchingMap.map.Get(aAttributeName);
|
||||
if (!matchingAttribute ||
|
||||
*matchingAttribute != *aAttribute) {
|
||||
matchingMap.matches = false;
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
bool
|
||||
AttributeMap::operator==(const AttributeMap& aOther) const
|
||||
{
|
||||
@@ -2103,37 +2077,16 @@ AttributeMap::operator==(const AttributeMap& aOther) const
|
||||
return false;
|
||||
}
|
||||
|
||||
MatchingMap matchingMap = { mMap, true };
|
||||
aOther.mMap.EnumerateRead(CheckAttributeEquality, &matchingMap);
|
||||
return matchingMap.matches;
|
||||
}
|
||||
for (auto iter = aOther.mMap.Iter(); !iter.Done(); iter.Next()) {
|
||||
const uint32_t& attributeName = iter.Key();
|
||||
Attribute* attribute = iter.UserData();
|
||||
Attribute* matchingAttribute = mMap.Get(attributeName);
|
||||
if (!matchingAttribute || *matchingAttribute != *attribute) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct HandlerWithUserData
|
||||
{
|
||||
AttributeMap::AttributeHandleCallback handler;
|
||||
void* userData;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static PLDHashOperator
|
||||
PassAttributeToHandleCallback(const uint32_t& aAttributeName,
|
||||
Attribute* aAttribute,
|
||||
void* aHandlerWithUserData)
|
||||
{
|
||||
HandlerWithUserData* handlerWithUserData =
|
||||
static_cast<HandlerWithUserData*>(aHandlerWithUserData);
|
||||
return handlerWithUserData->handler(AttributeName(aAttributeName),
|
||||
aAttribute->Type(),
|
||||
handlerWithUserData->userData) ?
|
||||
PL_DHASH_NEXT : PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
void
|
||||
AttributeMap::EnumerateRead(AttributeMap::AttributeHandleCallback aCallback, void* aUserData) const
|
||||
{
|
||||
HandlerWithUserData handlerWithUserData = { aCallback, aUserData };
|
||||
mMap.EnumerateRead(PassAttributeToHandleCallback, &handlerWithUserData);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@@ -2142,6 +2095,18 @@ AttributeMap::Count() const
|
||||
return mMap.Count();
|
||||
}
|
||||
|
||||
nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator
|
||||
AttributeMap::ConstIter() const
|
||||
{
|
||||
return mMap.ConstIter();
|
||||
}
|
||||
|
||||
/* static */ AttributeType
|
||||
AttributeMap::GetType(FilterAttribute* aAttribute)
|
||||
{
|
||||
return aAttribute->Type();
|
||||
}
|
||||
|
||||
#define MAKE_ATTRIBUTE_HANDLERS_BASIC(type, typeLabel, defaultValue) \
|
||||
type \
|
||||
AttributeMap::Get##typeLabel(AttributeName aName) const { \
|
||||
|
||||
@@ -221,10 +221,12 @@ public:
|
||||
AttributeMap GetAttributeMap(AttributeName aName) const;
|
||||
const nsTArray<float>& GetFloats(AttributeName aName) const;
|
||||
|
||||
typedef bool (*AttributeHandleCallback)(AttributeName aName, AttributeType aType, void* aUserData);
|
||||
void EnumerateRead(AttributeHandleCallback aCallback, void* aUserData) const;
|
||||
uint32_t Count() const;
|
||||
|
||||
nsClassHashtable<nsUint32HashKey, FilterAttribute>::Iterator ConstIter() const;
|
||||
|
||||
static AttributeType GetType(FilterAttribute* aAttribute);
|
||||
|
||||
private:
|
||||
mutable nsClassHashtable<nsUint32HashKey, FilterAttribute> mMap;
|
||||
};
|
||||
|
||||
@@ -139,8 +139,6 @@ private:
|
||||
// The apz prefs are explained in AsyncPanZoomController.cpp
|
||||
DECL_GFX_PREF(Live, "apz.allow_checkerboarding", APZAllowCheckerboarding, bool, true);
|
||||
DECL_GFX_PREF(Live, "apz.allow_zooming", APZAllowZooming, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.asyncscroll.throttle", APZAsyncScrollThrottleTime, int32_t, 100);
|
||||
DECL_GFX_PREF(Live, "apz.asyncscroll.timeout", APZAsyncScrollTimeout, int32_t, 300);
|
||||
DECL_GFX_PREF(Live, "apz.axis_lock.breakout_angle", APZAxisBreakoutAngle, float, float(M_PI / 8.0) /* 22.5 degrees */);
|
||||
DECL_GFX_PREF(Live, "apz.axis_lock.breakout_threshold", APZAxisBreakoutThreshold, float, 1.0f / 32.0f);
|
||||
DECL_GFX_PREF(Live, "apz.axis_lock.direct_pan_angle", APZAllowedDirectPanAngle, float, float(M_PI / 3.0) /* 60 degrees */);
|
||||
@@ -411,6 +409,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "webgl.default-no-alpha", WebGLDefaultNoAlpha, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.disable-angle", WebGLDisableANGLE, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.disable-extensions", WebGLDisableExtensions, bool, false);
|
||||
DECL_GFX_PREF(Live, "webgl.dxgl.enabled", WebGLDXGLEnabled, bool, false);
|
||||
|
||||
DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
|
||||
WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
|
||||
|
||||
+33
-39
@@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* -*- 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/. */
|
||||
@@ -54,46 +55,21 @@ WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
|
||||
return info;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
|
||||
static void
|
||||
CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
|
||||
{
|
||||
MOZ_ASSERT(aUserArg);
|
||||
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
const uint64_t& key = iter.Key();
|
||||
LockCount count = iter.UserData();
|
||||
|
||||
LockCount* totalCount = static_cast<LockCount*>(aUserArg);
|
||||
totalCount->numLocks += aCount.numLocks;
|
||||
totalCount->numHidden += aCount.numHidden;
|
||||
aTotalCount->numLocks += count.numLocks;
|
||||
aTotalCount->numHidden += count.numHidden;
|
||||
|
||||
// This is linear in the number of processes, but that should be small.
|
||||
if (!totalCount->processes.Contains(aKey)) {
|
||||
totalCount->processes.AppendElement(aKey);
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
|
||||
void* aUserArg)
|
||||
{
|
||||
MOZ_ASSERT(aUserArg);
|
||||
|
||||
PLDHashOperator op = PL_DHASH_NEXT;
|
||||
uint64_t childID = *static_cast<uint64_t*>(aUserArg);
|
||||
if (aTable->Get(childID, nullptr)) {
|
||||
aTable->Remove(childID);
|
||||
|
||||
LockCount totalCount;
|
||||
aTable->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
if (!totalCount.numLocks) {
|
||||
op = PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
if (sActiveListeners) {
|
||||
NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
|
||||
// This is linear in the number of processes, but that should be small.
|
||||
if (!aTotalCount->processes.Contains(key)) {
|
||||
aTotalCount->processes.AppendElement(key);
|
||||
}
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
class ClearHashtableOnShutdown final : public nsIObserver {
|
||||
@@ -144,7 +120,25 @@ CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, con
|
||||
nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
|
||||
&childID);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
sLockTable->Enumerate(RemoveChildFromList, &childID);
|
||||
for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsAutoPtr<ProcessLockTable>& table = iter.Data();
|
||||
|
||||
if (table->Get(childID, nullptr)) {
|
||||
table->Remove(childID);
|
||||
|
||||
LockCount totalCount;
|
||||
CountWakeLocks(table, &totalCount);
|
||||
|
||||
if (sActiveListeners) {
|
||||
NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
|
||||
totalCount));
|
||||
}
|
||||
|
||||
if (totalCount.numLocks == 0) {
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("ipc:content-shutdown message without childID property");
|
||||
}
|
||||
@@ -222,7 +216,7 @@ ModifyWakeLock(const nsAString& aTopic,
|
||||
sLockTable->Put(aTopic, table);
|
||||
} else {
|
||||
table->Get(aProcessID, &processCount);
|
||||
table->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
CountWakeLocks(table, &totalCount);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
|
||||
@@ -279,7 +273,7 @@ GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
|
||||
return;
|
||||
}
|
||||
LockCount totalCount;
|
||||
table->EnumerateRead(CountWakeLocks, &totalCount);
|
||||
CountWakeLocks(table, &totalCount);
|
||||
*aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
|
||||
}
|
||||
|
||||
|
||||
@@ -177,3 +177,6 @@ CFLAGS += CONFIG['GLIB_CFLAGS']
|
||||
CFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
|
||||
CXXFLAGS += CONFIG['GLIB_CFLAGS']
|
||||
CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -302,7 +302,7 @@ private:
|
||||
DecodePool::Initialize()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
sNumCores = PR_GetNumberOfProcessors();
|
||||
sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
|
||||
DecodePool::Singleton();
|
||||
}
|
||||
|
||||
@@ -346,6 +346,9 @@ DecodePool::DecodePool()
|
||||
} else {
|
||||
limit = static_cast<uint32_t>(prefLimit);
|
||||
}
|
||||
if (limit > 32) {
|
||||
limit = 32;
|
||||
}
|
||||
|
||||
// Initialize the thread pool.
|
||||
for (uint32_t i = 0 ; i < limit ; ++i) {
|
||||
|
||||
@@ -74,8 +74,8 @@ Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
|
||||
|
||||
mFrameRect = aFrameRect.valueOr(nsIntRect(nsIntPoint(), aOriginalSize));
|
||||
MOZ_ASSERT(mFrameRect.x >= 0 && mFrameRect.y >= 0 &&
|
||||
mFrameRect.width > 0 && mFrameRect.height > 0,
|
||||
"Frame rect must have positive components");
|
||||
mFrameRect.width >= 0 && mFrameRect.height >= 0,
|
||||
"Frame rect must have non-negative components");
|
||||
MOZ_ASSERT(nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
|
||||
.Contains(mFrameRect),
|
||||
"Frame rect must fit inside image");
|
||||
@@ -164,8 +164,13 @@ Downscaler::ResetForNextProgressivePass()
|
||||
mCurrentInLine = 0;
|
||||
mLinesInBuffer = 0;
|
||||
|
||||
// If we have a vertical offset, commit rows to shift us past it.
|
||||
SkipToRow(mFrameRect.y);
|
||||
if (mFrameRect.IsEmpty()) {
|
||||
// Our frame rect is zero size; commit rows until the end of the image.
|
||||
SkipToRow(mOriginalSize.height - 1);
|
||||
} else {
|
||||
// If we have a vertical offset, commit rows to shift us past it.
|
||||
SkipToRow(mFrameRect.y);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -83,6 +83,8 @@ public:
|
||||
bool aHasAlpha,
|
||||
bool aFlipVertically = false);
|
||||
|
||||
bool IsFrameComplete() const { return mCurrentInLine >= mOriginalSize.height; }
|
||||
|
||||
/// Retrieves the buffer into which the Decoder should write each row.
|
||||
uint8_t* RowBuffer()
|
||||
{
|
||||
@@ -161,6 +163,7 @@ public:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool IsFrameComplete() const { return false; }
|
||||
uint8_t* RowBuffer() { return nullptr; }
|
||||
void ClearRow(uint32_t = 0) { }
|
||||
void CommitRow() { }
|
||||
|
||||
@@ -343,7 +343,7 @@ OrientedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
|
||||
}
|
||||
|
||||
// Transform the invalidation rect into the correct orientation.
|
||||
gfxMatrix matrix(OrientationMatrix(innerSize, /* aInvert = */ true));
|
||||
gfxMatrix matrix(OrientationMatrix(innerSize));
|
||||
gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
|
||||
rect.width, rect.height)));
|
||||
invalidRect.RoundOut();
|
||||
|
||||
@@ -27,7 +27,12 @@ class AsyncNotifyRunnable;
|
||||
class AsyncNotifyCurrentStateRunnable;
|
||||
class Image;
|
||||
|
||||
// Image progress bitflags.
|
||||
/**
|
||||
* Image progress bitflags.
|
||||
*
|
||||
* See CheckProgressConsistency() for the invariants we enforce about the
|
||||
* ordering dependencies betweeen these flags.
|
||||
*/
|
||||
enum {
|
||||
FLAG_SIZE_AVAILABLE = 1u << 0, // STATUS_SIZE_AVAILABLE
|
||||
FLAG_DECODE_COMPLETE = 1u << 1, // STATUS_DECODE_COMPLETE
|
||||
|
||||
+41
-37
@@ -173,7 +173,6 @@ RasterImage::Init(const char* aMimeType,
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
|
||||
NS_IMETHODIMP_(void)
|
||||
RasterImage::RequestRefresh(const TimeStamp& aTime)
|
||||
{
|
||||
@@ -297,31 +296,22 @@ RasterImage::LookupFrameInternal(uint32_t aFrameNum,
|
||||
return mAnim->GetCompositedFrame(aFrameNum);
|
||||
}
|
||||
|
||||
Maybe<SurfaceFlags> alternateFlags;
|
||||
if (IsOpaque()) {
|
||||
// If we're opaque, we can always substitute a frame that was decoded with a
|
||||
// different decode flag for premultiplied alpha, because that can only
|
||||
// matter for frames with transparency.
|
||||
alternateFlags.emplace(ToSurfaceFlags(aFlags) ^
|
||||
SurfaceFlags::NO_PREMULTIPLY_ALPHA);
|
||||
}
|
||||
SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
|
||||
|
||||
// We don't want any substitution for sync decodes (except the premultiplied
|
||||
// alpha optimization above), so we use SurfaceCache::Lookup in this case.
|
||||
// We don't want any substitution for sync decodes, so we use
|
||||
// SurfaceCache::Lookup in this case.
|
||||
if (aFlags & FLAG_SYNC_DECODE) {
|
||||
return SurfaceCache::Lookup(ImageKey(this),
|
||||
RasterSurfaceKey(aSize,
|
||||
ToSurfaceFlags(aFlags),
|
||||
aFrameNum),
|
||||
alternateFlags);
|
||||
surfaceFlags,
|
||||
aFrameNum));
|
||||
}
|
||||
|
||||
// We'll return the best match we can find to the requested frame.
|
||||
return SurfaceCache::LookupBestMatch(ImageKey(this),
|
||||
RasterSurfaceKey(aSize,
|
||||
ToSurfaceFlags(aFlags),
|
||||
aFrameNum),
|
||||
alternateFlags);
|
||||
surfaceFlags,
|
||||
aFrameNum));
|
||||
}
|
||||
|
||||
DrawableFrameRef
|
||||
@@ -331,6 +321,12 @@ RasterImage::LookupFrame(uint32_t aFrameNum,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we're opaque, we don't need to care about premultiplied alpha, because
|
||||
// that can only matter for frames with transparency.
|
||||
if (IsOpaque()) {
|
||||
aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
}
|
||||
|
||||
IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
|
||||
? aSize : mSize;
|
||||
if (requestedSize.IsEmpty()) {
|
||||
@@ -559,8 +555,6 @@ RasterImage::CopyFrame(uint32_t aWhichFrame, uint32_t aFlags)
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
|
||||
* in uint32_t aFlags); */
|
||||
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
|
||||
RasterImage::GetFrame(uint32_t aWhichFrame,
|
||||
uint32_t aFlags)
|
||||
@@ -823,14 +817,14 @@ RasterImage::OnAddedFrame(uint32_t aNewFrameCount,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
||||
bool aFromMetadataDecode)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mError) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (aMetadata.HasSize()) {
|
||||
@@ -838,7 +832,7 @@ RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
||||
if (size.width < 0 || size.height < 0) {
|
||||
NS_WARNING("Image has negative intrinsic size");
|
||||
DoError();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aMetadata.HasOrientation());
|
||||
@@ -849,7 +843,7 @@ RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
||||
NS_WARNING("Image changed size or orientation on redecode! "
|
||||
"This should not happen!");
|
||||
DoError();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the size and flag that we have it.
|
||||
@@ -871,7 +865,7 @@ RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
||||
// discovered that it actually was during the full decode. This is a
|
||||
// rare failure that only occurs for corrupt images. To recover, we need
|
||||
// to discard all existing surfaces and redecode.
|
||||
RecoverFromInvalidFrames(mSize, DECODE_FLAGS_DEFAULT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,6 +887,8 @@ RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
||||
Set("hotspotX", intwrapx);
|
||||
Set("hotspotY", intwrapy);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -986,7 +982,6 @@ RasterImage::ResetAnimation()
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
// [notxpcom] void setAnimationStartTime ([const] in TimeStamp aTime);
|
||||
NS_IMETHODIMP_(void)
|
||||
RasterImage::SetAnimationStartTime(const TimeStamp& aTime)
|
||||
{
|
||||
@@ -1294,17 +1289,24 @@ RasterImage::Decode(const IntSize& aSize, uint32_t aFlags)
|
||||
decoderFlags |= DecoderFlags::IS_REDECODE;
|
||||
}
|
||||
|
||||
SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
|
||||
if (IsOpaque()) {
|
||||
// If there's no transparency, it doesn't matter whether we premultiply
|
||||
// alpha or not.
|
||||
surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
|
||||
}
|
||||
|
||||
// Create a decoder.
|
||||
RefPtr<Decoder> decoder;
|
||||
if (mAnim) {
|
||||
decoder = DecoderFactory::CreateAnimationDecoder(mDecoderType, this,
|
||||
mSourceBuffer, decoderFlags,
|
||||
ToSurfaceFlags(aFlags),
|
||||
surfaceFlags,
|
||||
mRequestedResolution);
|
||||
} else {
|
||||
decoder = DecoderFactory::CreateDecoder(mDecoderType, this, mSourceBuffer,
|
||||
targetSize, decoderFlags,
|
||||
ToSurfaceFlags(aFlags),
|
||||
surfaceFlags,
|
||||
mRequestedSampleSize,
|
||||
mRequestedResolution);
|
||||
}
|
||||
@@ -1478,15 +1480,6 @@ RasterImage::DrawInternal(DrawableFrameRef&& aFrameRef,
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [noscript] void draw(in gfxContext aContext,
|
||||
* in Filter aFilter,
|
||||
* [const] in gfxMatrix aUserSpaceToImageSpace,
|
||||
* [const] in gfxRect aFill,
|
||||
* [const] in IntRect aSubimage,
|
||||
* [const] in IntSize aViewportSize,
|
||||
* [const] in SVGImageContext aSVGContext,
|
||||
* in uint32_t aWhichFrame,
|
||||
* in uint32_t aFlags); */
|
||||
NS_IMETHODIMP_(DrawResult)
|
||||
RasterImage::Draw(gfxContext* aContext,
|
||||
const IntSize& aSize,
|
||||
@@ -1724,7 +1717,18 @@ RasterImage::FinalizeDecoder(Decoder* aDecoder)
|
||||
}
|
||||
|
||||
// Record all the metadata the decoder gathered about this image.
|
||||
SetMetadata(aDecoder->GetImageMetadata(), wasMetadata);
|
||||
bool metadataOK = SetMetadata(aDecoder->GetImageMetadata(), wasMetadata);
|
||||
if (!metadataOK) {
|
||||
// This indicates a serious error that requires us to discard all existing
|
||||
// surfaces and redecode to recover. We'll drop the results from this
|
||||
// decoder on the floor, since they aren't valid.
|
||||
aDecoder->TakeProgress();
|
||||
aDecoder->TakeInvalidRect();
|
||||
RecoverFromInvalidFrames(mSize,
|
||||
FromSurfaceFlags(aDecoder->GetSurfaceFlags()));
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mError || mHasSize || !aDecoder->HasSize(),
|
||||
"SetMetadata should've gotten a size");
|
||||
|
||||
|
||||
+4
-1
@@ -324,8 +324,11 @@ private:
|
||||
* @param aMetadata The metadata to set on this image.
|
||||
* @param aFromMetadataDecode True if this metadata came from a metadata
|
||||
* decode; false if it came from a full decode.
|
||||
* @return |true| unless a catastrophic failure was discovered. If |false| is
|
||||
* returned, it indicates that the image is corrupt in a way that requires all
|
||||
* surfaces to be discarded to recover.
|
||||
*/
|
||||
void SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
|
||||
bool SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode);
|
||||
|
||||
/**
|
||||
* In catastrophic circumstances like a GPU driver crash, the contents of our
|
||||
|
||||
@@ -200,9 +200,6 @@ SVGDocumentWrapper::TickRefreshDriver()
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsIInputStream inStr, in unsigned long sourceOffset,
|
||||
in unsigned long count); */
|
||||
NS_IMETHODIMP
|
||||
SVGDocumentWrapper::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
|
||||
nsIInputStream* inStr,
|
||||
@@ -236,8 +233,6 @@ SVGDocumentWrapper::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
|
||||
}
|
||||
|
||||
|
||||
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsresult status); */
|
||||
NS_IMETHODIMP
|
||||
SVGDocumentWrapper::OnStopRequest(nsIRequest* aRequest, nsISupports* ctxt,
|
||||
nsresult status)
|
||||
|
||||
+103
-163
@@ -275,8 +275,7 @@ public:
|
||||
}
|
||||
|
||||
Pair<already_AddRefed<CachedSurface>, MatchType>
|
||||
LookupBestMatch(const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags)
|
||||
LookupBestMatch(const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
// Try for an exact match first.
|
||||
RefPtr<CachedSurface> exactMatch;
|
||||
@@ -286,15 +285,74 @@ public:
|
||||
}
|
||||
|
||||
// There's no perfect match, so find the best match we can.
|
||||
MatchContext matchContext(aSurfaceKey, aAlternateFlags);
|
||||
ForEach(TryToImproveMatch, &matchContext);
|
||||
RefPtr<CachedSurface> bestMatch;
|
||||
for (auto iter = ConstIter(); !iter.Done(); iter.Next()) {
|
||||
CachedSurface* surface = iter.UserData();
|
||||
const SurfaceKey& idealKey = aSurfaceKey;
|
||||
|
||||
// We never match a placeholder.
|
||||
if (surface->IsPlaceholder()) {
|
||||
continue;
|
||||
}
|
||||
// Matching the animation time and SVG context is required.
|
||||
if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
|
||||
aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
|
||||
continue;
|
||||
}
|
||||
// Matching the flags is required.
|
||||
if (aSurfaceKey.Flags() != idealKey.Flags()) {
|
||||
continue;
|
||||
}
|
||||
// Anything is better than nothing! (Within the constraints we just
|
||||
// checked, of course.)
|
||||
if (!bestMatch) {
|
||||
bestMatch = surface;
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(bestMatch, "Should have a current best match");
|
||||
|
||||
// Always prefer completely decoded surfaces.
|
||||
bool bestMatchIsDecoded = bestMatch->IsDecoded();
|
||||
if (bestMatchIsDecoded && !surface->IsDecoded()) {
|
||||
continue;
|
||||
}
|
||||
if (!bestMatchIsDecoded && surface->IsDecoded()) {
|
||||
bestMatch = surface;
|
||||
continue;
|
||||
}
|
||||
|
||||
SurfaceKey bestMatchKey = bestMatch->GetSurfaceKey();
|
||||
|
||||
// Compare sizes. We use an area-based heuristic here instead of computing a
|
||||
// truly optimal answer, since it seems very unlikely to make a difference
|
||||
// for realistic sizes.
|
||||
int64_t idealArea = idealKey.Size().width * idealKey.Size().height;
|
||||
int64_t surfaceArea = aSurfaceKey.Size().width * aSurfaceKey.Size().height;
|
||||
int64_t bestMatchArea =
|
||||
bestMatchKey.Size().width * bestMatchKey.Size().height;
|
||||
|
||||
// If the best match is smaller than the ideal size, prefer bigger sizes.
|
||||
if (bestMatchArea < idealArea) {
|
||||
if (surfaceArea > bestMatchArea) {
|
||||
bestMatch = surface;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Other, prefer sizes closer to the ideal size, but still not smaller.
|
||||
if (idealArea <= surfaceArea && surfaceArea < bestMatchArea) {
|
||||
bestMatch = surface;
|
||||
continue;
|
||||
}
|
||||
// This surface isn't an improvement over the current best match.
|
||||
}
|
||||
|
||||
MatchType matchType;
|
||||
if (matchContext.mBestMatch) {
|
||||
if (bestMatch) {
|
||||
if (!exactMatch) {
|
||||
// No exact match, but we found a substitute.
|
||||
matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
|
||||
} else if (exactMatch != matchContext.mBestMatch) {
|
||||
} else if (exactMatch != bestMatch) {
|
||||
// The exact match is still decoding, but we found a substitute.
|
||||
matchType = MatchType::SUBSTITUTE_BECAUSE_PENDING;
|
||||
} else {
|
||||
@@ -303,8 +361,7 @@ public:
|
||||
}
|
||||
} else {
|
||||
if (exactMatch) {
|
||||
// We found an "exact match", but since TryToImproveMatch didn't return
|
||||
// it, it must have been a placeholder.
|
||||
// We found an "exact match", it must have been a placeholder.
|
||||
MOZ_ASSERT(exactMatch->IsPlaceholder());
|
||||
matchType = MatchType::PENDING;
|
||||
} else {
|
||||
@@ -313,103 +370,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
return MakePair(matchContext.mBestMatch.forget(), matchType);
|
||||
return MakePair(bestMatch.forget(), matchType);
|
||||
}
|
||||
|
||||
void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
|
||||
SurfaceTable::Iterator ConstIter() const
|
||||
{
|
||||
mSurfaces.EnumerateRead(aFunction, aData);
|
||||
return mSurfaces.ConstIter();
|
||||
}
|
||||
|
||||
void SetLocked(bool aLocked) { mLocked = aLocked; }
|
||||
bool IsLocked() const { return mLocked; }
|
||||
|
||||
private:
|
||||
struct MatchContext
|
||||
{
|
||||
MatchContext(const SurfaceKey& aIdealKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags)
|
||||
: mIdealKey(aIdealKey)
|
||||
, mAlternateFlags(aAlternateFlags)
|
||||
{ }
|
||||
|
||||
const SurfaceKey& mIdealKey;
|
||||
const Maybe<SurfaceFlags> mAlternateFlags;
|
||||
RefPtr<CachedSurface> mBestMatch;
|
||||
};
|
||||
|
||||
static PLDHashOperator TryToImproveMatch(const SurfaceKey& aSurfaceKey,
|
||||
CachedSurface* aSurface,
|
||||
void* aContext)
|
||||
{
|
||||
auto context = static_cast<MatchContext*>(aContext);
|
||||
const SurfaceKey& idealKey = context->mIdealKey;
|
||||
|
||||
// We never match a placeholder.
|
||||
if (aSurface->IsPlaceholder()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Matching the animation time and SVG context is required.
|
||||
if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
|
||||
aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Matching the flags is required, but we can match the alternate flags as
|
||||
// well if some were provided.
|
||||
if (aSurfaceKey.Flags() != idealKey.Flags() &&
|
||||
Some(aSurfaceKey.Flags()) != context->mAlternateFlags) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Anything is better than nothing! (Within the constraints we just
|
||||
// checked, of course.)
|
||||
if (!context->mBestMatch) {
|
||||
context->mBestMatch = aSurface;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(context->mBestMatch, "Should have a current best match");
|
||||
|
||||
// Always prefer completely decoded surfaces.
|
||||
bool bestMatchIsDecoded = context->mBestMatch->IsDecoded();
|
||||
if (bestMatchIsDecoded && !aSurface->IsDecoded()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
if (!bestMatchIsDecoded && aSurface->IsDecoded()) {
|
||||
context->mBestMatch = aSurface;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
SurfaceKey bestMatchKey = context->mBestMatch->GetSurfaceKey();
|
||||
|
||||
// Compare sizes. We use an area-based heuristic here instead of computing a
|
||||
// truly optimal answer, since it seems very unlikely to make a difference
|
||||
// for realistic sizes.
|
||||
int64_t idealArea = idealKey.Size().width * idealKey.Size().height;
|
||||
int64_t surfaceArea = aSurfaceKey.Size().width * aSurfaceKey.Size().height;
|
||||
int64_t bestMatchArea =
|
||||
bestMatchKey.Size().width * bestMatchKey.Size().height;
|
||||
|
||||
// If the best match is smaller than the ideal size, prefer bigger sizes.
|
||||
if (bestMatchArea < idealArea) {
|
||||
if (surfaceArea > bestMatchArea) {
|
||||
context->mBestMatch = aSurface;
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Other, prefer sizes closer to the ideal size, but still not smaller.
|
||||
if (idealArea <= surfaceArea && surfaceArea < bestMatchArea) {
|
||||
context->mBestMatch = aSurface;
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// This surface isn't an improvement over the current best match.
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
SurfaceTable mSurfaces;
|
||||
bool mLocked;
|
||||
};
|
||||
@@ -635,8 +607,7 @@ public:
|
||||
}
|
||||
|
||||
LookupResult LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags)
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
|
||||
if (!cache) {
|
||||
@@ -654,8 +625,7 @@ public:
|
||||
DrawableFrameRef ref;
|
||||
MatchType matchType = MatchType::NOT_FOUND;
|
||||
while (true) {
|
||||
Tie(surface, matchType) =
|
||||
cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
|
||||
Tie(surface, matchType) = cache->LookupBestMatch(aSurfaceKey);
|
||||
|
||||
if (!surface) {
|
||||
return LookupResult(matchType); // Lookup in the per-image cache missed.
|
||||
@@ -671,12 +641,13 @@ public:
|
||||
Remove(surface);
|
||||
}
|
||||
|
||||
MOZ_ASSERT((matchType == MatchType::EXACT) ==
|
||||
(surface->GetSurfaceKey() == aSurfaceKey ||
|
||||
(aAlternateFlags &&
|
||||
surface->GetSurfaceKey() ==
|
||||
aSurfaceKey.WithNewFlags(*aAlternateFlags))),
|
||||
"Result differs in a way other than size or alternate flags");
|
||||
MOZ_ASSERT_IF(matchType == MatchType::EXACT,
|
||||
surface->GetSurfaceKey() == aSurfaceKey);
|
||||
MOZ_ASSERT_IF(matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
|
||||
matchType == MatchType::SUBSTITUTE_BECAUSE_PENDING,
|
||||
surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() &&
|
||||
surface->GetSurfaceKey().AnimationTime() == aSurfaceKey.AnimationTime() &&
|
||||
surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
|
||||
|
||||
if (matchType == MatchType::EXACT) {
|
||||
MarkUsed(surface, cache);
|
||||
@@ -728,9 +699,7 @@ public:
|
||||
}
|
||||
|
||||
cache->SetLocked(false);
|
||||
|
||||
// Unlock all the surfaces the per-image cache is holding.
|
||||
cache->ForEach(DoUnlockSurface, this);
|
||||
DoUnlockSurfaces(cache);
|
||||
}
|
||||
|
||||
void UnlockSurfaces(const ImageKey aImageKey)
|
||||
@@ -742,9 +711,7 @@ public:
|
||||
|
||||
// (Note that we *don't* unlock the per-image cache here; that's the
|
||||
// difference between this and UnlockImage.)
|
||||
|
||||
// Unlock all the surfaces the per-image cache is holding.
|
||||
cache->ForEach(DoUnlockSurface, this);
|
||||
DoUnlockSurfaces(cache);
|
||||
}
|
||||
|
||||
void RemoveImage(const ImageKey aImageKey)
|
||||
@@ -759,7 +726,9 @@ public:
|
||||
// removing an element from the costs array. Since n is expected to be
|
||||
// small, performance should be good, but if usage patterns change we should
|
||||
// change the data structure used for mCosts.
|
||||
cache->ForEach(DoStopTracking, this);
|
||||
for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
StopTracking(iter.UserData());
|
||||
}
|
||||
|
||||
// The per-image cache isn't needed anymore, so remove it as well.
|
||||
// This implicitly unlocks the image if it was locked.
|
||||
@@ -815,31 +784,6 @@ public:
|
||||
StartTracking(aSurface);
|
||||
}
|
||||
|
||||
static PLDHashOperator DoStopTracking(const SurfaceKey&,
|
||||
CachedSurface* aSurface,
|
||||
void* aCache)
|
||||
{
|
||||
static_cast<SurfaceCacheImpl*>(aCache)->StopTracking(aSurface);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator DoUnlockSurface(const SurfaceKey&,
|
||||
CachedSurface* aSurface,
|
||||
void* aCache)
|
||||
{
|
||||
if (aSurface->IsPlaceholder() || !aSurface->IsLocked()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
auto cache = static_cast<SurfaceCacheImpl*>(aCache);
|
||||
cache->StopTracking(aSurface);
|
||||
|
||||
aSurface->SetLocked(false);
|
||||
cache->StartTracking(aSurface);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
@@ -888,16 +832,9 @@ public:
|
||||
|
||||
// Report all surfaces in the per-image cache.
|
||||
CachedSurface::SurfaceMemoryReport report(aCounters, aMallocSizeOf);
|
||||
cache->ForEach(DoCollectSizeOfSurface, &report);
|
||||
}
|
||||
|
||||
static PLDHashOperator DoCollectSizeOfSurface(const SurfaceKey&,
|
||||
CachedSurface* aSurface,
|
||||
void* aReport)
|
||||
{
|
||||
auto report = static_cast<CachedSurface::SurfaceMemoryReport*>(aReport);
|
||||
report->Add(aSurface);
|
||||
return PL_DHASH_NEXT;
|
||||
for (auto iter = cache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
report.Add(iter.UserData());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -927,6 +864,20 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void DoUnlockSurfaces(ImageSurfaceCache* aCache)
|
||||
{
|
||||
// Unlock all the surfaces the per-image cache is holding.
|
||||
for (auto iter = aCache->ConstIter(); !iter.Done(); iter.Next()) {
|
||||
CachedSurface* surface = iter.UserData();
|
||||
if (surface->IsPlaceholder() || !surface->IsLocked()) {
|
||||
continue;
|
||||
}
|
||||
StopTracking(surface);
|
||||
surface->SetLocked(false);
|
||||
StartTracking(surface);
|
||||
}
|
||||
}
|
||||
|
||||
struct SurfaceTracker : public nsExpirationTracker<CachedSurface, 2>
|
||||
{
|
||||
explicit SurfaceTracker(uint32_t aSurfaceCacheExpirationTimeMS)
|
||||
@@ -1049,37 +1000,26 @@ SurfaceCache::Shutdown()
|
||||
|
||||
/* static */ LookupResult
|
||||
SurfaceCache::Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags
|
||||
/* = Nothing() */)
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return LookupResult(MatchType::NOT_FOUND);
|
||||
}
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
|
||||
LookupResult result = sInstance->Lookup(aImageKey, aSurfaceKey);
|
||||
if (!result && aAlternateFlags) {
|
||||
result = sInstance->Lookup(aImageKey,
|
||||
aSurfaceKey.WithNewFlags(*aAlternateFlags));
|
||||
}
|
||||
|
||||
return result;
|
||||
return sInstance->Lookup(aImageKey, aSurfaceKey);
|
||||
}
|
||||
|
||||
/* static */ LookupResult
|
||||
SurfaceCache::LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags
|
||||
/* = Nothing() */)
|
||||
const SurfaceKey& aSurfaceKey)
|
||||
{
|
||||
if (!sInstance) {
|
||||
return LookupResult(MatchType::NOT_FOUND);
|
||||
}
|
||||
|
||||
MutexAutoLock lock(sInstance->GetMutex());
|
||||
return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
|
||||
return sInstance->LookupBestMatch(aImageKey, aSurfaceKey);
|
||||
}
|
||||
|
||||
/* static */ InsertOutcome
|
||||
|
||||
+3
-23
@@ -69,11 +69,6 @@ public:
|
||||
float AnimationTime() const { return mAnimationTime; }
|
||||
SurfaceFlags Flags() const { return mFlags; }
|
||||
|
||||
SurfaceKey WithNewFlags(SurfaceFlags aFlags) const
|
||||
{
|
||||
return SurfaceKey(mSize, mSVGContext, mAnimationTime, aFlags);
|
||||
}
|
||||
|
||||
private:
|
||||
SurfaceKey(const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
@@ -181,38 +176,25 @@ struct SurfaceCache
|
||||
* @param aSurfaceKey Key data which uniquely identifies the requested
|
||||
* surface.
|
||||
*
|
||||
* @param aAlternateFlags If not Nothing(), a different set of flags than the
|
||||
* ones specified in @aSurfaceKey which are also
|
||||
* acceptable to the caller. This is more efficient
|
||||
* than calling Lookup() twice, which requires taking a
|
||||
* lock each time.
|
||||
*
|
||||
* @return a LookupResult, which will either contain a
|
||||
* DrawableFrameRef to the requested surface, or an
|
||||
* empty DrawableFrameRef if the surface was not found.
|
||||
*/
|
||||
static LookupResult Lookup(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags
|
||||
= Nothing());
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
/**
|
||||
* Looks up the best matching surface in the cache and returns a drawable
|
||||
* reference to the imgFrame containing it.
|
||||
*
|
||||
* Returned surfaces may vary from the requested surface only in terms of
|
||||
* size, unless @aAlternateFlags is specified.
|
||||
* size.
|
||||
*
|
||||
* @param aImageKey Key data identifying which image the surface belongs
|
||||
* to.
|
||||
*
|
||||
* @param aSurfaceKey Key data which identifies the ideal surface to return.
|
||||
*
|
||||
* @param aAlternateFlags If not Nothing(), a different set of flags than the
|
||||
* ones specified in @aSurfaceKey which are also
|
||||
* acceptable to the caller. This is much more
|
||||
* efficient than calling LookupBestMatch() twice.
|
||||
*
|
||||
* @return a LookupResult, which will either contain a
|
||||
* DrawableFrameRef to a surface similar to the
|
||||
* requested surface, or an empty DrawableFrameRef if
|
||||
@@ -221,9 +203,7 @@ struct SurfaceCache
|
||||
* returned surface exactly matches @aSurfaceKey.
|
||||
*/
|
||||
static LookupResult LookupBestMatch(const ImageKey aImageKey,
|
||||
const SurfaceKey& aSurfaceKey,
|
||||
const Maybe<SurfaceFlags>& aAlternateFlags
|
||||
= Nothing());
|
||||
const SurfaceKey& aSurfaceKey);
|
||||
|
||||
/**
|
||||
* Insert a surface into the cache. If a surface with the same ImageKey and
|
||||
|
||||
@@ -50,6 +50,23 @@ ToSurfaceFlags(uint32_t aFlags)
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a set of SurfaceFlags, returns a set of imgIContainer FLAG_* flags with
|
||||
* the corresponding flags set.
|
||||
*/
|
||||
inline uint32_t
|
||||
FromSurfaceFlags(SurfaceFlags aFlags)
|
||||
{
|
||||
uint32_t flags = imgIContainer::DECODE_FLAGS_DEFAULT;
|
||||
if (aFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA) {
|
||||
flags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
}
|
||||
if (aFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
|
||||
flags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
+1
-17
@@ -683,8 +683,6 @@ VectorImage::IsOpaque()
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame,
|
||||
* in uint32_t aFlags; */
|
||||
NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
|
||||
VectorImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags)
|
||||
{
|
||||
@@ -793,13 +791,6 @@ struct SVGDrawingParameters
|
||||
};
|
||||
|
||||
//******************************************************************************
|
||||
/* [noscript] void draw(in gfxContext aContext,
|
||||
* [const] in nsIntSize aSize,
|
||||
* [const] in ImageRegion aRegion,
|
||||
* in uint32_t aWhichFrame,
|
||||
* in Filter aFilter,
|
||||
* [const] in MaybeSVGImageContext aSVGContext,
|
||||
* in uint32_t aFlags); */
|
||||
NS_IMETHODIMP_(DrawResult)
|
||||
VectorImage::Draw(gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
@@ -1125,8 +1116,6 @@ VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt)
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
/* void onStopRequest(in nsIRequest request, in nsISupports ctxt,
|
||||
in nsresult status); */
|
||||
NS_IMETHODIMP
|
||||
VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt,
|
||||
nsresult aStatus)
|
||||
@@ -1219,9 +1208,7 @@ VectorImage::OnSVGDocumentError()
|
||||
|
||||
if (mProgressTracker) {
|
||||
// Notify observers about the error and unblock page load.
|
||||
Progress progress = FLAG_DECODE_COMPLETE |
|
||||
FLAG_ONLOAD_UNBLOCKED |
|
||||
FLAG_HAS_ERROR;
|
||||
Progress progress = FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR;
|
||||
|
||||
// Merge in any saved progress from OnImageDataComplete.
|
||||
if (mLoadProgress) {
|
||||
@@ -1237,9 +1224,6 @@ VectorImage::OnSVGDocumentError()
|
||||
// nsIStreamListener method
|
||||
|
||||
//******************************************************************************
|
||||
/* void onDataAvailable(in nsIRequest request, in nsISupports ctxt,
|
||||
in nsIInputStream inStr, in unsigned long sourceOffset,
|
||||
in unsigned long count); */
|
||||
NS_IMETHODIMP
|
||||
VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt,
|
||||
nsIInputStream* aInStr, uint64_t aSourceOffset,
|
||||
|
||||
@@ -207,7 +207,7 @@ nsGIFDecoder2::BeginGIF()
|
||||
}
|
||||
|
||||
bool
|
||||
nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
|
||||
nsGIFDecoder2::CheckForTransparency(const IntRect& aFrameRect)
|
||||
{
|
||||
// Check if the image has a transparent color in its palette.
|
||||
if (mGIFStruct.is_transparent) {
|
||||
@@ -231,6 +231,22 @@ nsGIFDecoder2::CheckForTransparency(IntRect aFrameRect)
|
||||
return false;
|
||||
}
|
||||
|
||||
IntRect
|
||||
nsGIFDecoder2::ClampToImageRect(const IntRect& aRect)
|
||||
{
|
||||
IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
|
||||
IntRect visibleFrameRect = aRect.Intersect(imageRect);
|
||||
|
||||
// If there's no intersection, |visibleFrameRect| will be an empty rect
|
||||
// positioned at the maximum of |imageRect|'s and |aRect|'s coordinates, which
|
||||
// is not what we want. Force it to (0, 0) in that case.
|
||||
if (visibleFrameRect.IsEmpty()) {
|
||||
visibleFrameRect.MoveTo(0, 0);
|
||||
}
|
||||
|
||||
return visibleFrameRect;
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
nsresult
|
||||
nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
|
||||
@@ -275,8 +291,8 @@ nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
|
||||
}
|
||||
|
||||
if (mDownscaler) {
|
||||
rv = mDownscaler->BeginFrame(GetSize(), Some(frameRect), mImageData,
|
||||
hasTransparency);
|
||||
rv = mDownscaler->BeginFrame(GetSize(), Some(ClampToImageRect(frameRect)),
|
||||
mImageData, hasTransparency);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@@ -487,6 +503,9 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
|
||||
if (!mGIFStruct.rows_remaining) {
|
||||
return true;
|
||||
}
|
||||
if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy all the decoder state variables into locals so the compiler
|
||||
// won't worry about them being aliased. The locals will be homed
|
||||
@@ -543,6 +562,10 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
|
||||
return (mGIFStruct.rows_remaining == 0);
|
||||
}
|
||||
|
||||
if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
|
||||
goto END;
|
||||
}
|
||||
|
||||
if (oldcode == -1) {
|
||||
if (code >= MAX_BITS) {
|
||||
return false;
|
||||
|
||||
@@ -50,7 +50,8 @@ private:
|
||||
bool DoLzw(const uint8_t* q);
|
||||
bool SetHold(const uint8_t* buf, uint32_t count,
|
||||
const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
|
||||
bool CheckForTransparency(gfx::IntRect aFrameRect);
|
||||
bool CheckForTransparency(const gfx::IntRect& aFrameRect);
|
||||
gfx::IntRect ClampToImageRect(const gfx::IntRect& aFrameRect);
|
||||
|
||||
inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
|
||||
|
||||
|
||||
@@ -268,8 +268,6 @@ nsJPEGEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
|
||||
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
|
||||
}
|
||||
|
||||
/* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter, in
|
||||
voidPtr aClosure, in unsigned long aCount); */
|
||||
NS_IMETHODIMP
|
||||
nsJPEGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
|
||||
void* aClosure, uint32_t aCount, uint32_t* _retval)
|
||||
|
||||
@@ -544,17 +544,12 @@ nsPNGEncoder::Available(uint64_t* _retval)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* [noscript] unsigned long read (in charPtr aBuf,
|
||||
in unsigned long aCount); */
|
||||
NS_IMETHODIMP
|
||||
nsPNGEncoder::Read(char* aBuf, uint32_t aCount, uint32_t* _retval)
|
||||
{
|
||||
return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
|
||||
}
|
||||
|
||||
/* [noscript] unsigned long readSegments (in nsWriteSegmentFun aWriter,
|
||||
in voidPtr aClosure,
|
||||
in unsigned long aCount); */
|
||||
NS_IMETHODIMP
|
||||
nsPNGEncoder::ReadSegments(nsWriteSegmentFun aWriter,
|
||||
void* aClosure, uint32_t aCount,
|
||||
|
||||
+33
-68
@@ -76,8 +76,16 @@ public:
|
||||
nsTArray<ImageMemoryCounter> uncached;
|
||||
|
||||
for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
|
||||
mKnownLoaders[i]->mChromeCache.EnumerateRead(DoRecordCounter, &chrome);
|
||||
mKnownLoaders[i]->mCache.EnumerateRead(DoRecordCounter, &content);
|
||||
for (auto iter = mKnownLoaders[i]->mChromeCache.Iter(); !iter.Done(); iter.Next()) {
|
||||
imgCacheEntry* entry = iter.UserData();
|
||||
RefPtr<imgRequest> req = entry->GetRequest();
|
||||
RecordCounterForRequest(req, &chrome, !entry->HasNoProxies());
|
||||
}
|
||||
for (auto iter = mKnownLoaders[i]->mCache.Iter(); !iter.Done(); iter.Next()) {
|
||||
imgCacheEntry* entry = iter.UserData();
|
||||
RefPtr<imgRequest> req = entry->GetRequest();
|
||||
RecordCounterForRequest(req, &content, !entry->HasNoProxies());
|
||||
}
|
||||
MutexAutoLock lock(mKnownLoaders[i]->mUncachedImagesMutex);
|
||||
for (auto iter = mKnownLoaders[i]->mUncachedImages.Iter();
|
||||
!iter.Done();
|
||||
@@ -110,8 +118,29 @@ public:
|
||||
size_t n = 0;
|
||||
for (uint32_t i = 0; i < imgLoader::sMemReporter->mKnownLoaders.Length();
|
||||
i++) {
|
||||
imgLoader::sMemReporter->mKnownLoaders[i]->
|
||||
mCache.EnumerateRead(DoRecordCounterUsedDecoded, &n);
|
||||
for (auto iter = imgLoader::sMemReporter->mKnownLoaders[i]->mCache.Iter();
|
||||
!iter.Done();
|
||||
iter.Next()) {
|
||||
imgCacheEntry* entry = iter.UserData();
|
||||
if (entry->HasNoProxies()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RefPtr<imgRequest> req = entry->GetRequest();
|
||||
RefPtr<Image> image = req->GetImage();
|
||||
if (!image) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both this and EntryImageSizes measure images/content/raster/used/decoded
|
||||
// memory. This function's measurement is secondary -- the result doesn't
|
||||
// go in the "explicit" tree -- so we use moz_malloc_size_of instead of
|
||||
// ImagesMallocSizeOf to prevent DMD from seeing it reported twice.
|
||||
ImageMemoryCounter counter(image, moz_malloc_size_of, /* aIsUsed = */ true);
|
||||
|
||||
n += counter.Values().DecodedHeap();
|
||||
n += counter.Values().DecodedNonHeap();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -406,17 +435,6 @@ private:
|
||||
aValue, desc, aData);
|
||||
}
|
||||
|
||||
static PLDHashOperator DoRecordCounter(const ImageCacheKey&,
|
||||
imgCacheEntry* aEntry,
|
||||
void* aUserArg)
|
||||
{
|
||||
RefPtr<imgRequest> req = aEntry->GetRequest();
|
||||
RecordCounterForRequest(req,
|
||||
static_cast<nsTArray<ImageMemoryCounter>*>(aUserArg),
|
||||
!aEntry->HasNoProxies());
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static void RecordCounterForRequest(imgRequest* aRequest,
|
||||
nsTArray<ImageMemoryCounter>* aArray,
|
||||
bool aIsUsed)
|
||||
@@ -430,33 +448,6 @@ private:
|
||||
|
||||
aArray->AppendElement(Move(counter));
|
||||
}
|
||||
|
||||
static PLDHashOperator DoRecordCounterUsedDecoded(const ImageCacheKey&,
|
||||
imgCacheEntry* aEntry,
|
||||
void* aUserArg)
|
||||
{
|
||||
if (aEntry->HasNoProxies()) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
RefPtr<imgRequest> req = aEntry->GetRequest();
|
||||
RefPtr<Image> image = req->GetImage();
|
||||
if (!image) {
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// Both this and EntryImageSizes measure images/content/raster/used/decoded
|
||||
// memory. This function's measurement is secondary -- the result doesn't
|
||||
// go in the "explicit" tree -- so we use moz_malloc_size_of instead of
|
||||
// ImagesMallocSizeOf to prevent DMD from seeing it reported twice.
|
||||
ImageMemoryCounter counter(image, moz_malloc_size_of, /* aIsUsed = */ true);
|
||||
|
||||
auto n = static_cast<size_t*>(aUserArg);
|
||||
*n += counter.Values().DecodedHeap();
|
||||
*n += counter.Values().DecodedNonHeap();
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(imgMemoryReporter, nsIMemoryReporter)
|
||||
@@ -2107,15 +2098,6 @@ imgLoader::LoadImageXPCOM(nsIURI* aURI,
|
||||
return rv;
|
||||
}
|
||||
|
||||
// imgIRequest loadImage(in nsIURI aURI,
|
||||
// in nsIURI aInitialDocumentURL,
|
||||
// in nsIURI aReferrerURI,
|
||||
// in nsIPrincipal aLoadingPrincipal,
|
||||
// in nsILoadGroup aLoadGroup,
|
||||
// in imgINotificationObserver aObserver,
|
||||
// in nsISupports aCX,
|
||||
// in nsLoadFlags aLoadFlags,
|
||||
// in nsISupports cacheKey);
|
||||
nsresult
|
||||
imgLoader::LoadImage(nsIURI* aURI,
|
||||
nsIURI* aInitialDocumentURI,
|
||||
@@ -2396,11 +2378,6 @@ imgLoader::LoadImage(nsIURI* aURI,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* imgIRequest
|
||||
loadImageWithChannelXPCOM(in nsIChannel channel,
|
||||
in imgINotificationObserver aObserver,
|
||||
in nsISupports cx,
|
||||
out nsIStreamListener); */
|
||||
NS_IMETHODIMP
|
||||
imgLoader::LoadImageWithChannelXPCOM(nsIChannel* channel,
|
||||
imgINotificationObserver* aObserver,
|
||||
@@ -2915,8 +2892,6 @@ ProxyListener::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
|
||||
return mDestListener->OnStartRequest(aRequest, ctxt);
|
||||
}
|
||||
|
||||
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsresult status); */
|
||||
NS_IMETHODIMP
|
||||
ProxyListener::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* ctxt,
|
||||
@@ -2931,10 +2906,6 @@ ProxyListener::OnStopRequest(nsIRequest* aRequest,
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsIInputStream inStr,
|
||||
in unsigned long long sourceOffset,
|
||||
in unsigned long count); */
|
||||
NS_IMETHODIMP
|
||||
ProxyListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
|
||||
nsIInputStream* inStr, uint64_t sourceOffset,
|
||||
@@ -3139,8 +3110,6 @@ imgCacheValidator::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
|
||||
return mDestListener->OnStartRequest(aRequest, ctxt);
|
||||
}
|
||||
|
||||
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsresult status); */
|
||||
NS_IMETHODIMP
|
||||
imgCacheValidator::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* ctxt,
|
||||
@@ -3159,10 +3128,6 @@ imgCacheValidator::OnStopRequest(nsIRequest* aRequest,
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
|
||||
/* void
|
||||
onDataAvailable (in nsIRequest request, in nsISupports ctxt,
|
||||
in nsIInputStream inStr, in unsigned long long sourceOffset,
|
||||
in unsigned long count); */
|
||||
NS_IMETHODIMP
|
||||
imgCacheValidator::OnDataAvailable(nsIRequest* aRequest, nsISupports* ctxt,
|
||||
nsIInputStream* inStr,
|
||||
|
||||
@@ -804,8 +804,6 @@ imgRequest::OnStartRequest(nsIRequest* aRequest, nsISupports* ctxt)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void onStopRequest (in nsIRequest request, in nsISupports ctxt,
|
||||
nsresult status); */
|
||||
NS_IMETHODIMP
|
||||
imgRequest::OnStopRequest(nsIRequest* aRequest,
|
||||
nsISupports* ctxt, nsresult status)
|
||||
|
||||
@@ -136,9 +136,6 @@ nsScriptableUnicodeConverter::ConvertToUnicode(const nsACString& aSrc, nsAString
|
||||
_retval);
|
||||
}
|
||||
|
||||
/* AString convertFromByteArray([const,array,size_is(aCount)] in octet aData,
|
||||
in unsigned long aCount);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData,
|
||||
uint32_t aCount,
|
||||
@@ -180,10 +177,6 @@ nsScriptableUnicodeConverter::ConvertFromByteArray(const uint8_t* aData,
|
||||
|
||||
}
|
||||
|
||||
/* void convertToByteArray(in AString aString,
|
||||
[optional] out unsigned long aLen,
|
||||
[array, size_is(aLen),retval] out octet aData);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsScriptableUnicodeConverter::ConvertToByteArray(const nsAString& aString,
|
||||
uint32_t* aLen,
|
||||
|
||||
@@ -887,7 +887,7 @@ SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
|
||||
if (!ToInt32(cx, args[0], &seed))
|
||||
return false;
|
||||
|
||||
cx->compartment()->savedStacks().setRNGState((seed ^ RNG_MULTIPLIER) & RNG_MASK);
|
||||
cx->compartment()->savedStacks().setRNGState(seed, seed * 33);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5747,6 +5747,13 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, ptrdiff_t top)
|
||||
|
||||
if (!emitTree(forHead->pn_kid2))
|
||||
return false;
|
||||
} else if (!forHead->pn_kid3) {
|
||||
// If there is no condition clause and no update clause, mark
|
||||
// the loop-ending "goto" with the location of the "for".
|
||||
// This ensures that the debugger will stop on each loop
|
||||
// iteration.
|
||||
if (!updateSourceCoordNotes(pn->pn_pos.begin))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the first note offset so we can find the loop condition. */
|
||||
@@ -8474,11 +8481,9 @@ CGBlockScopeList::findEnclosingScope(uint32_t index)
|
||||
// scope contains POS, it should still be open, so its length should
|
||||
// be zero.
|
||||
return list[index].index;
|
||||
} else {
|
||||
// Conversely, if the length is not zero, it should not contain
|
||||
// POS.
|
||||
MOZ_ASSERT_IF(inPrologue == list[index].endInPrologue, list[index].end <= pos);
|
||||
}
|
||||
// Conversely, if the length is not zero, it should not contain POS.
|
||||
MOZ_ASSERT_IF(inPrologue == list[index].endInPrologue, list[index].end <= pos);
|
||||
}
|
||||
|
||||
return BlockScopeNote::NoBlockScopeIndex;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Test how stepping interacts with for(;;) statements.
|
||||
|
||||
let g = newGlobal();
|
||||
|
||||
// We want a for(;;) loop whose body is evaluated at least once, to
|
||||
// see whether the loop head is hit a second time.
|
||||
g.eval(`function f() {
|
||||
let x = 0;
|
||||
debugger; // +0
|
||||
for(;;) { // +1
|
||||
if (x++ == 1) break; // +2
|
||||
} // +3
|
||||
debugger; // +4
|
||||
}`);
|
||||
|
||||
let dbg = Debugger(g);
|
||||
|
||||
function test(s, expected) {
|
||||
let result = '';
|
||||
|
||||
dbg.onDebuggerStatement = function(frame) {
|
||||
// On the second debugger statement, we're done.
|
||||
dbg.onDebuggerStatement = function(frame) {
|
||||
frame.onStep = undefined;
|
||||
};
|
||||
|
||||
let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber;
|
||||
frame.onStep = function() {
|
||||
// Only examine stops at entry points for the line.
|
||||
let lineNo = this.script.getOffsetLocation(this.offset).lineNumber;
|
||||
if (this.script.getLineOffsets(lineNo).indexOf(this.offset) < 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let delta = this.script.getOffsetLocation(this.offset).lineNumber - debugLine;
|
||||
result += delta;
|
||||
};
|
||||
};
|
||||
g.eval(s);
|
||||
assertEq(result, expected);
|
||||
}
|
||||
|
||||
test('f()', '2124');
|
||||
@@ -32,5 +32,5 @@ dbg.memory.trackingAllocationSites = true;
|
||||
// probability is fine.
|
||||
measure(0.0, 0);
|
||||
measure(1.0, 100);
|
||||
measure(0.1, 9);
|
||||
measure(0.5, 51);
|
||||
measure(0.1, 7);
|
||||
measure(0.5, 44);
|
||||
|
||||
@@ -22,3 +22,4 @@ function test(code) {
|
||||
|
||||
test('while (false)\n;');
|
||||
test('for (;false;)\n;');
|
||||
test('for (;;) break;\n;');
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
#include "vm/NativeObject-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
||||
using JS::IsArrayAnswer;
|
||||
|
||||
@@ -8014,7 +8014,7 @@ DebuggerEnv_getCallee(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
|
||||
JSObject& scope = env->as<DebugScopeObject>().scope();
|
||||
if (!scope.is<CallObject>() || scope.is<ModuleEnvironmentObject>())
|
||||
if (!scope.is<CallObject>())
|
||||
return true;
|
||||
|
||||
CallObject& callobj = scope.as<CallObject>();
|
||||
|
||||
@@ -184,7 +184,7 @@ class SavedFrame : public NativeObject {
|
||||
struct SavedFrame::HashPolicy
|
||||
{
|
||||
typedef SavedFrame::Lookup Lookup;
|
||||
typedef PointerHasher<SavedFrame*, 3> SavedFramePtrHasher;
|
||||
typedef MovableCellHasher<SavedFrame*> SavedFramePtrHasher;
|
||||
typedef PointerHasher<JSPrincipals*, 3> JSPrincipalsPtrHasher;
|
||||
|
||||
static HashNumber hash(const Lookup& lookup);
|
||||
|
||||
+12
-53
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "vm/SavedStacks.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
@@ -986,6 +987,10 @@ SavedFrame::toStringMethod(JSContext* cx, unsigned argc, Value* vp)
|
||||
bool
|
||||
SavedStacks::init()
|
||||
{
|
||||
uint64_t seed[2];
|
||||
random_generateSeed(seed, mozilla::ArrayLength(seed));
|
||||
bernoulli.setRandomState(seed[0], seed[1]);
|
||||
|
||||
if (!pcLocationMap.init())
|
||||
return false;
|
||||
|
||||
@@ -1030,26 +1035,8 @@ SavedStacks::sweep(JSRuntime* rt)
|
||||
{
|
||||
if (frames.initialized()) {
|
||||
for (SavedFrame::Set::Enum e(frames); !e.empty(); e.popFront()) {
|
||||
JSObject* obj = e.front().unbarrieredGet();
|
||||
JSObject* temp = obj;
|
||||
|
||||
if (IsAboutToBeFinalizedUnbarriered(&obj)) {
|
||||
if (IsAboutToBeFinalized(&e.mutableFront()))
|
||||
e.removeFront();
|
||||
} else {
|
||||
SavedFrame* frame = &obj->as<SavedFrame>();
|
||||
|
||||
SavedFrame* parent = frame->getParent();
|
||||
bool parentMoved = parent && IsForwarded(parent);
|
||||
if (parentMoved)
|
||||
parent = Forwarded(parent);
|
||||
|
||||
if (obj != temp || parentMoved) {
|
||||
MOZ_ASSERT(!IsForwarded(frame));
|
||||
SavedFrame::Lookup newLocation(*frame);
|
||||
newLocation.parent = parent;
|
||||
e.rekeyFront(newLocation, ReadBarriered<SavedFrame*>(frame));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,7 +1408,7 @@ SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
|
||||
mozilla::DebugOnly<Debugger**> begin = dbgs->begin();
|
||||
mozilla::DebugOnly<bool> foundAnyDebuggers = false;
|
||||
|
||||
allocationSamplingProbability = 0;
|
||||
double probability = 0;
|
||||
for (Debugger** dbgp = dbgs->begin(); dbgp < dbgs->end(); dbgp++) {
|
||||
// The set of debuggers had better not change while we're iterating,
|
||||
// such that the vector gets reallocated.
|
||||
@@ -1429,11 +1416,13 @@ SavedStacks::chooseSamplingProbability(JSCompartment* compartment)
|
||||
|
||||
if ((*dbgp)->trackingAllocationSites && (*dbgp)->enabled) {
|
||||
foundAnyDebuggers = true;
|
||||
allocationSamplingProbability = std::max((*dbgp)->allocationSamplingProbability,
|
||||
allocationSamplingProbability);
|
||||
probability = std::max((*dbgp)->allocationSamplingProbability,
|
||||
probability);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(foundAnyDebuggers);
|
||||
|
||||
bernoulli.setProbability(probability);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -1442,38 +1431,8 @@ SavedStacksMetadataCallback(JSContext* cx, JSObject* target)
|
||||
RootedObject obj(cx, target);
|
||||
|
||||
SavedStacks& stacks = cx->compartment()->savedStacks();
|
||||
if (stacks.allocationSkipCount > 0) {
|
||||
stacks.allocationSkipCount--;
|
||||
if (!stacks.bernoulli.trial())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stacks.allocationSamplingProbability == 0.0)
|
||||
return nullptr;
|
||||
|
||||
// If the sampling probability is set to 1.0, we are always taking a sample
|
||||
// and can therefore leave allocationSkipCount at 0.
|
||||
if (stacks.allocationSamplingProbability != 1.0) {
|
||||
// Rather than generating a random number on every allocation to decide
|
||||
// if we want to sample that particular allocation (which would be
|
||||
// expensive), we calculate the number of allocations to skip before
|
||||
// taking the next sample.
|
||||
//
|
||||
// P = the probability we sample any given event.
|
||||
//
|
||||
// ~P = 1-P, the probability we don't sample a given event.
|
||||
//
|
||||
// (~P)^n = the probability that we skip at least the next n events.
|
||||
//
|
||||
// let X = random between 0 and 1.
|
||||
//
|
||||
// floor(log base ~P of X) = n, aka the number of events we should skip
|
||||
// until we take the next sample. Any value for X less than (~P)^n
|
||||
// yields a skip count greater than n, so the likelihood of a skip count
|
||||
// greater than n is (~P)^n, as required.
|
||||
double notSamplingProb = 1.0 - stacks.allocationSamplingProbability;
|
||||
stacks.allocationSkipCount = std::floor(std::log(random_nextDouble(&stacks.rngState)) /
|
||||
std::log(notSamplingProb));
|
||||
}
|
||||
|
||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
||||
RootedSavedFrame frame(cx);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifndef vm_SavedStacks_h
|
||||
#define vm_SavedStacks_h
|
||||
|
||||
#include "mozilla/FastBernoulliTrial.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsmath.h"
|
||||
#include "jswrapper.h"
|
||||
@@ -152,12 +154,11 @@ class SavedStacks {
|
||||
JS::ubi::StackFrame& ubiFrame,
|
||||
MutableHandleObject outSavedFrameStack);
|
||||
|
||||
|
||||
public:
|
||||
SavedStacks()
|
||||
: frames(),
|
||||
allocationSamplingProbability(1.0),
|
||||
allocationSkipCount(0),
|
||||
rngState(0),
|
||||
bernoulli(1.0, 0x59fdad7f6b4cc573, 0x91adf38db96a9354),
|
||||
creatingSavedFrame(false)
|
||||
{ }
|
||||
|
||||
@@ -170,16 +171,14 @@ class SavedStacks {
|
||||
void trace(JSTracer* trc);
|
||||
uint32_t count();
|
||||
void clear();
|
||||
void setRNGState(uint64_t state) { rngState = state; }
|
||||
void setRNGState(uint64_t state0, uint64_t state1) { bernoulli.setRandomState(state0, state1); }
|
||||
void chooseSamplingProbability(JSCompartment*);
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
private:
|
||||
SavedFrame::Set frames;
|
||||
double allocationSamplingProbability;
|
||||
uint32_t allocationSkipCount;
|
||||
uint64_t rngState;
|
||||
mozilla::FastBernoulliTrial bernoulli;
|
||||
bool creatingSavedFrame;
|
||||
|
||||
// Similar to mozilla::ReentrancyGuard, but instead of asserting against
|
||||
|
||||
@@ -27,7 +27,7 @@ NearestEnclosingExtensibleLexicalScope(JSObject* scope)
|
||||
inline void
|
||||
ScopeObject::setAliasedVar(JSContext* cx, ScopeCoordinate sc, PropertyName* name, const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
|
||||
MOZ_ASSERT(is<LexicalScopeBase>() || is<ClonedBlockObject>());
|
||||
JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
|
||||
|
||||
// name may be null if we don't need to track side effects on the object.
|
||||
@@ -48,7 +48,8 @@ ScopeObject::setAliasedVar(JSContext* cx, ScopeCoordinate sc, PropertyName* name
|
||||
}
|
||||
|
||||
inline void
|
||||
CallObject::setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name, const Value& v)
|
||||
LexicalScopeBase::setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
|
||||
const Value& v)
|
||||
{
|
||||
MOZ_ASSERT(name == fi->name());
|
||||
setSlot(fi.scopeSlot(), v);
|
||||
@@ -57,7 +58,8 @@ CallObject::setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* nam
|
||||
}
|
||||
|
||||
inline void
|
||||
CallObject::setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id, const Value& v)
|
||||
LexicalScopeBase::setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
|
||||
const Value& v)
|
||||
{
|
||||
setSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue), v);
|
||||
if (isSingleton())
|
||||
@@ -65,7 +67,7 @@ CallObject::setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, js
|
||||
}
|
||||
|
||||
inline void
|
||||
CallObject::initRemainingSlotsToUninitializedLexicals(uint32_t begin)
|
||||
LexicalScopeBase::initRemainingSlotsToUninitializedLexicals(uint32_t begin)
|
||||
{
|
||||
uint32_t end = slotSpan();
|
||||
for (uint32_t slot = begin; slot < end; slot++)
|
||||
@@ -73,7 +75,7 @@ CallObject::initRemainingSlotsToUninitializedLexicals(uint32_t begin)
|
||||
}
|
||||
|
||||
inline void
|
||||
CallObject::initAliasedLexicalsToThrowOnTouch(JSScript* script)
|
||||
LexicalScopeBase::initAliasedLexicalsToThrowOnTouch(JSScript* script)
|
||||
{
|
||||
initRemainingSlotsToUninitializedLexicals(script->bindings.aliasedBodyLevelLexicalBegin());
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ js::ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc)
|
||||
void
|
||||
ScopeObject::setEnclosingScope(HandleObject obj)
|
||||
{
|
||||
MOZ_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
|
||||
MOZ_ASSERT_IF(obj->is<LexicalScopeBase>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
|
||||
obj->isDelegate());
|
||||
setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
|
||||
}
|
||||
@@ -1776,9 +1776,7 @@ class DebugScopeProxy : public BaseProxyHandler
|
||||
|
||||
static bool isFunctionScope(const JSObject& scope)
|
||||
{
|
||||
return scope.is<CallObject>() &&
|
||||
!scope.is<ModuleEnvironmentObject>() &&
|
||||
!scope.as<CallObject>().isForEval();
|
||||
return scope.is<CallObject>() && !scope.as<CallObject>().isForEval();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2203,7 +2201,7 @@ bool
|
||||
DebugScopeObject::isForDeclarative() const
|
||||
{
|
||||
ScopeObject& s = scope();
|
||||
return s.is<CallObject>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
|
||||
return s.is<LexicalScopeBase>() || s.is<BlockObject>() || s.is<DeclEnvObject>();
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -2224,7 +2222,7 @@ DebugScopeObject::isOptimizedOut() const
|
||||
if (s.is<ClonedBlockObject>())
|
||||
return !s.as<ClonedBlockObject>().staticBlock().needsClone();
|
||||
|
||||
if (s.is<CallObject>() && !s.is<ModuleEnvironmentObject>()) {
|
||||
if (s.is<CallObject>()) {
|
||||
return !s.as<CallObject>().isForEval() &&
|
||||
!s.as<CallObject>().callee().needsCallObject() &&
|
||||
!maybeSnapshot();
|
||||
|
||||
+41
-32
@@ -221,7 +221,9 @@ ScopeCoordinateFunctionScript(JSScript* script, jsbytecode* pc);
|
||||
* | | |
|
||||
* | | DeclEnvObject Holds name of recursive/needsCallObject named lambda
|
||||
* | |
|
||||
* | CallObject Scope of entire function or strict eval
|
||||
* | LexicalScopeBase Shared base for function and modules scopes
|
||||
* | | |
|
||||
* | | CallObject Scope of entire function or strict eval
|
||||
* | |
|
||||
* | ModuleEnvironmentObject Module top-level scope on run-time scope chain
|
||||
* |
|
||||
@@ -287,7 +289,36 @@ class ScopeObject : public NativeObject
|
||||
}
|
||||
};
|
||||
|
||||
class CallObject : public ScopeObject
|
||||
class LexicalScopeBase : public ScopeObject
|
||||
{
|
||||
protected:
|
||||
inline void initRemainingSlotsToUninitializedLexicals(uint32_t begin);
|
||||
inline void initAliasedLexicalsToThrowOnTouch(JSScript* script);
|
||||
|
||||
public:
|
||||
/* Get/set the aliased variable referred to by 'fi'. */
|
||||
const Value& aliasedVar(AliasedFormalIter fi) {
|
||||
return getSlot(fi.scopeSlot());
|
||||
}
|
||||
inline void setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
|
||||
const Value& v);
|
||||
|
||||
/*
|
||||
* When an aliased var (var accessed by nested closures) is also aliased by
|
||||
* the arguments object, it must of course exist in one canonical location
|
||||
* and that location is always the CallObject. For this to work, the
|
||||
* ArgumentsObject stores special MagicValue in its array for forwarded-to-
|
||||
* CallObject variables. This MagicValue's payload is the slot of the
|
||||
* CallObject to access.
|
||||
*/
|
||||
const Value& aliasedVarFromArguments(const Value& argsValue) {
|
||||
return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
|
||||
}
|
||||
inline void setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
|
||||
const Value& v);
|
||||
};
|
||||
|
||||
class CallObject : public LexicalScopeBase
|
||||
{
|
||||
protected:
|
||||
static const uint32_t CALLEE_SLOT = 1;
|
||||
@@ -295,9 +326,6 @@ class CallObject : public ScopeObject
|
||||
static CallObject*
|
||||
create(JSContext* cx, HandleScript script, HandleObject enclosing, HandleFunction callee);
|
||||
|
||||
inline void initRemainingSlotsToUninitializedLexicals(uint32_t begin);
|
||||
inline void initAliasedLexicalsToThrowOnTouch(JSScript* script);
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
@@ -347,27 +375,6 @@ class CallObject : public ScopeObject
|
||||
return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
|
||||
}
|
||||
|
||||
/* Get/set the aliased variable referred to by 'bi'. */
|
||||
const Value& aliasedVar(AliasedFormalIter fi) {
|
||||
return getSlot(fi.scopeSlot());
|
||||
}
|
||||
inline void setAliasedVar(JSContext* cx, AliasedFormalIter fi, PropertyName* name,
|
||||
const Value& v);
|
||||
|
||||
/*
|
||||
* When an aliased var (var accessed by nested closures) is also aliased by
|
||||
* the arguments object, it must of course exist in one canonical location
|
||||
* and that location is always the CallObject. For this to work, the
|
||||
* ArgumentsObject stores special MagicValue in its array for forwarded-to-
|
||||
* CallObject variables. This MagicValue's payload is the slot of the
|
||||
* CallObject to access.
|
||||
*/
|
||||
const Value& aliasedVarFromArguments(const Value& argsValue) {
|
||||
return getSlot(ArgumentsObject::SlotFromMagicScopeSlotValue(argsValue));
|
||||
}
|
||||
inline void setAliasedVarFromArguments(JSContext* cx, const Value& argsValue, jsid id,
|
||||
const Value& v);
|
||||
|
||||
/* For jit access. */
|
||||
static size_t offsetOfCallee() {
|
||||
return getFixedSlotOffset(CALLEE_SLOT);
|
||||
@@ -378,13 +385,15 @@ class CallObject : public ScopeObject
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleEnvironmentObject : public CallObject
|
||||
class ModuleEnvironmentObject : public LexicalScopeBase
|
||||
{
|
||||
static const uint32_t MODULE_SLOT = CallObject::CALLEE_SLOT;
|
||||
static const uint32_t MODULE_SLOT = 1;
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static const uint32_t RESERVED_SLOTS = 2;
|
||||
|
||||
static ModuleEnvironmentObject* create(ExclusiveContext* cx, HandleModuleObject module);
|
||||
ModuleObject& module();
|
||||
IndirectBindingMap& importBindings();
|
||||
@@ -1261,9 +1270,9 @@ JSObject::is<js::NestedScopeObject>() const
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
JSObject::is<js::CallObject>() const
|
||||
JSObject::is<js::LexicalScopeBase>() const
|
||||
{
|
||||
return getClass() == &js::CallObject::class_ ||
|
||||
return is<js::CallObject>() ||
|
||||
is<js::ModuleEnvironmentObject>();
|
||||
}
|
||||
|
||||
@@ -1271,7 +1280,7 @@ template<>
|
||||
inline bool
|
||||
JSObject::is<js::ScopeObject>() const
|
||||
{
|
||||
return is<js::CallObject>() ||
|
||||
return is<js::LexicalScopeBase>() ||
|
||||
is<js::DeclEnvObject>() ||
|
||||
is<js::NestedScopeObject>() ||
|
||||
is<js::RuntimeLexicalErrorObject>() ||
|
||||
@@ -1342,7 +1351,7 @@ IsStaticGlobalLexicalScope(JSObject* scope)
|
||||
inline const Value&
|
||||
ScopeObject::aliasedVar(ScopeCoordinate sc)
|
||||
{
|
||||
MOZ_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
|
||||
MOZ_ASSERT(is<LexicalScopeBase>() || is<ClonedBlockObject>());
|
||||
return getSlot(sc.slot());
|
||||
}
|
||||
|
||||
|
||||
@@ -35,10 +35,7 @@ namespace js {
|
||||
static inline bool
|
||||
IsCacheableNonGlobalScope(JSObject* obj)
|
||||
{
|
||||
bool cacheable =
|
||||
(obj->is<CallObject>() && !obj->is<ModuleEnvironmentObject>()) ||
|
||||
obj->is<BlockObject>() ||
|
||||
obj->is<DeclEnvObject>();
|
||||
bool cacheable = obj->is<CallObject>() || obj->is<BlockObject>() || obj->is<DeclEnvObject>();
|
||||
|
||||
MOZ_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
|
||||
return cacheable;
|
||||
|
||||
@@ -1056,8 +1056,6 @@ mozJSComponentLoader::Import(const nsACString& registryLocation,
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
|
||||
in JSObjectPtr targetObj); */
|
||||
NS_IMETHODIMP
|
||||
mozJSComponentLoader::ImportInto(const nsACString& aLocation,
|
||||
JSObject* aTargetObj,
|
||||
|
||||
@@ -1150,13 +1150,6 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* bool call(in nsIXPConnectWrappedNative wrapper,
|
||||
* in JSContextPtr cx,
|
||||
* in JSObjectPtr obj,
|
||||
* in uint32_t argc,
|
||||
* in JSValPtr argv,
|
||||
* in JSValPtr vp);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
|
||||
JSObject* objArg, const CallArgs& args, bool* _retval)
|
||||
@@ -1165,13 +1158,6 @@ nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContex
|
||||
return CallOrConstruct(wrapper, cx, obj, args, _retval);
|
||||
}
|
||||
|
||||
/* bool construct(in nsIXPConnectWrappedNative wrapper,
|
||||
* in JSContextPtr cx,
|
||||
* in JSObjectPtr obj,
|
||||
* in uint32_t argc,
|
||||
* in JSValPtr argv,
|
||||
* in JSValPtr vp);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
|
||||
JSObject* objArg, const CallArgs& args, bool* _retval)
|
||||
|
||||
@@ -115,8 +115,6 @@ private:
|
||||
nsCOMArray<nsIInterfaceInfo> mInterfaces;
|
||||
};
|
||||
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Interfaces::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -342,8 +340,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_InterfacesByID::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -571,8 +567,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Classes::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -780,8 +774,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_ClassesByID::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -1012,8 +1004,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Results::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -1204,8 +1194,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_ID::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -1403,8 +1391,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Exception::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -1770,8 +1756,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCConstructor::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -1997,8 +1981,6 @@ private:
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Constructor::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
@@ -2486,9 +2468,6 @@ nsXPCComponents_Utils::SetSandboxMetadata(HandleValue sandboxVal,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* JSObject import (in AUTF8String registryLocation,
|
||||
* [optional] in JSObject targetObj);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::Import(const nsACString& registryLocation,
|
||||
HandleValue targetObj,
|
||||
@@ -2503,8 +2482,6 @@ nsXPCComponents_Utils::Import(const nsACString& registryLocation,
|
||||
return moduleloader->Import(registryLocation, targetObj, cx, optionalArgc, retval);
|
||||
}
|
||||
|
||||
/* boolean isModuleLoaded (in AUTF8String registryLocation);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::IsModuleLoaded(const nsACString& registryLocation, bool* retval)
|
||||
{
|
||||
@@ -2515,8 +2492,6 @@ nsXPCComponents_Utils::IsModuleLoaded(const nsACString& registryLocation, bool*
|
||||
return moduleloader->IsModuleLoaded(registryLocation, retval);
|
||||
}
|
||||
|
||||
/* unload (in AUTF8String registryLocation);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::Unload(const nsACString & registryLocation)
|
||||
{
|
||||
@@ -2527,9 +2502,6 @@ nsXPCComponents_Utils::Unload(const nsACString & registryLocation)
|
||||
return moduleloader->Unload(registryLocation);
|
||||
}
|
||||
|
||||
/*
|
||||
* JSObject importGlobalProperties (in jsval aPropertyList);
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::ImportGlobalProperties(HandleValue aPropertyList,
|
||||
JSContext* cx)
|
||||
@@ -2714,8 +2686,6 @@ nsXPCComponents_Utils::GetJSTestingFunctions(JSContext* cx,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* jsval callFunctionWithStack(in jsval function, in nsIStackFrame stack,
|
||||
in AString asyncCause); */
|
||||
NS_IMETHODIMP
|
||||
nsXPCComponents_Utils::CallFunctionWithAsyncStack(HandleValue function,
|
||||
nsIStackFrame* stack,
|
||||
|
||||
@@ -79,8 +79,6 @@ BackstagePass::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
|
||||
out nsIIDPtr array); */
|
||||
NS_IMETHODIMP
|
||||
BackstagePass::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
|
||||
{
|
||||
|
||||
@@ -188,9 +188,6 @@ NS_IMETHODIMP nsXPCTestParams::TestJsval(JS::Handle<JS::Value> a,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void testShortArray (in unsigned long aLength, [array, size_is (aLength)] in short a,
|
||||
* inout unsigned long bLength, [array, size_is (bLength)] inout short b,
|
||||
* out unsigned long rvLength, [array, size_is (rvLength), retval] out short rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestShortArray(uint32_t aLength, int16_t* a,
|
||||
uint32_t* bLength, int16_t** b,
|
||||
uint32_t* rvLength, int16_t** rv)
|
||||
@@ -198,9 +195,6 @@ NS_IMETHODIMP nsXPCTestParams::TestShortArray(uint32_t aLength, int16_t* a,
|
||||
BUFFER_METHOD_IMPL(int16_t, 0, TAKE_OWNERSHIP_NOOP);
|
||||
}
|
||||
|
||||
/* void testDoubleArray (in unsigned long aLength, [array, size_is (aLength)] in double a,
|
||||
* inout unsigned long bLength, [array, size_is (bLength)] inout double b,
|
||||
* out unsigned long rvLength, [array, size_is (rvLength), retval] out double rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(uint32_t aLength, double* a,
|
||||
uint32_t* bLength, double** b,
|
||||
uint32_t* rvLength, double** rv)
|
||||
@@ -208,9 +202,6 @@ NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(uint32_t aLength, double* a,
|
||||
BUFFER_METHOD_IMPL(double, 0, TAKE_OWNERSHIP_NOOP);
|
||||
}
|
||||
|
||||
/* void testStringArray (in unsigned long aLength, [array, size_is (aLength)] in string a,
|
||||
* inout unsigned long bLength, [array, size_is (bLength)] inout string b,
|
||||
* out unsigned long rvLength, [array, size_is (rvLength), retval] out string rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestStringArray(uint32_t aLength, const char * *a,
|
||||
uint32_t* bLength, char * **b,
|
||||
uint32_t* rvLength, char * **rv)
|
||||
@@ -218,9 +209,6 @@ NS_IMETHODIMP nsXPCTestParams::TestStringArray(uint32_t aLength, const char * *a
|
||||
BUFFER_METHOD_IMPL(char*, 0, TAKE_OWNERSHIP_STRING);
|
||||
}
|
||||
|
||||
/* void testWstringArray (in unsigned long aLength, [array, size_is (aLength)] in wstring a,
|
||||
* inout unsigned long bLength, [array, size_is (bLength)] inout wstring b,
|
||||
* out unsigned long rvLength, [array, size_is (rvLength), retval] out wstring rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestWstringArray(uint32_t aLength, const char16_t * *a,
|
||||
uint32_t* bLength, char16_t * **b,
|
||||
uint32_t* rvLength, char16_t * **rv)
|
||||
@@ -228,9 +216,6 @@ NS_IMETHODIMP nsXPCTestParams::TestWstringArray(uint32_t aLength, const char16_t
|
||||
BUFFER_METHOD_IMPL(char16_t*, 0, TAKE_OWNERSHIP_WSTRING);
|
||||
}
|
||||
|
||||
/* void testInterfaceArray (in unsigned long aLength, [array, size_is (aLength)] in nsIXPCTestInterfaceA a,
|
||||
* inout unsigned long bLength, [array, size_is (bLength)] inout nsIXPCTestInterfaceA b,
|
||||
* out unsigned long rvLength, [array, size_is (rvLength), retval] out nsIXPCTestInterfaceA rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray(uint32_t aLength, nsIXPCTestInterfaceA** a,
|
||||
uint32_t* bLength, nsIXPCTestInterfaceA * **b,
|
||||
uint32_t* rvLength, nsIXPCTestInterfaceA * **rv)
|
||||
@@ -238,9 +223,6 @@ NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray(uint32_t aLength, nsIXPCTestIn
|
||||
BUFFER_METHOD_IMPL(nsIXPCTestInterfaceA*, 0, TAKE_OWNERSHIP_INTERFACE);
|
||||
}
|
||||
|
||||
/* void testSizedString (in unsigned long aLength, [size_is (aLength)] in string a,
|
||||
* inout unsigned long bLength, [size_is (bLength)] inout string b,
|
||||
* out unsigned long rvLength, [size_is (rvLength), retval] out string rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char * a,
|
||||
uint32_t* bLength, char * *b,
|
||||
uint32_t* rvLength, char * *rv)
|
||||
@@ -248,9 +230,6 @@ NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char * a,
|
||||
BUFFER_METHOD_IMPL(char, 1, TAKE_OWNERSHIP_NOOP);
|
||||
}
|
||||
|
||||
/* void testSizedWstring (in unsigned long aLength, [size_is (aLength)] in wstring a,
|
||||
* inout unsigned long bLength, [size_is (bLength)] inout wstring b,
|
||||
* out unsigned long rvLength, [size_is (rvLength), retval] out wstring rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestSizedWstring(uint32_t aLength, const char16_t * a,
|
||||
uint32_t* bLength, char16_t * *b,
|
||||
uint32_t* rvLength, char16_t * *rv)
|
||||
@@ -258,9 +237,6 @@ NS_IMETHODIMP nsXPCTestParams::TestSizedWstring(uint32_t aLength, const char16_t
|
||||
BUFFER_METHOD_IMPL(char16_t, 1, TAKE_OWNERSHIP_NOOP);
|
||||
}
|
||||
|
||||
/* void testInterfaceIs (in nsIIDPtr aIID, [iid_is (aIID)] in nsQIResult a,
|
||||
* inout nsIIDPtr bIID, [iid_is (bIID)] inout nsQIResult b,
|
||||
* out nsIIDPtr rvIID, [iid_is (rvIID), retval] out nsQIResult rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestInterfaceIs(const nsIID* aIID, void* a,
|
||||
nsIID** bIID, void** b,
|
||||
nsIID** rvIID, void** rv)
|
||||
@@ -294,12 +270,6 @@ NS_IMETHODIMP nsXPCTestParams::TestInterfaceIs(const nsIID* aIID, void* a,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void testInterfaceIsArray (in unsigned long aLength, in nsIIDPtr aIID,
|
||||
* [array, size_is (aLength), iid_is (aIID)] in nsQIResult a,
|
||||
* inout unsigned long bLength, inout nsIIDPtr bIID,
|
||||
* [array, size_is (bLength), iid_is (bIID)] inout nsQIResult b,
|
||||
* out unsigned long rvLength, out nsIIDPtr rvIID,
|
||||
* [retval, array, size_is (rvLength), iid_is (rvIID)] out nsQIResult rv); */
|
||||
NS_IMETHODIMP nsXPCTestParams::TestInterfaceIsArray(uint32_t aLength, const nsIID* aIID,
|
||||
void** a,
|
||||
uint32_t* bLength, nsIID** bIID,
|
||||
@@ -327,9 +297,6 @@ NS_IMETHODIMP nsXPCTestParams::TestOutAString(nsAString & o)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* ACString testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength);
|
||||
*/
|
||||
NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char * *a, uint32_t length, nsACString& out)
|
||||
{
|
||||
out.Truncate();
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
#include "ClientLayerManager.h"
|
||||
#include "FrameLayerBuilder.h"
|
||||
|
||||
#ifdef MOZ_ANDROID_APZ
|
||||
#include "AndroidBridge.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::layers;
|
||||
@@ -197,29 +201,14 @@ public:
|
||||
|
||||
void ClearRenderFrame() { mRenderFrame = nullptr; }
|
||||
|
||||
virtual void SendAsyncScrollDOMEvent(bool aIsRootContent,
|
||||
const CSSRect& aContentRect,
|
||||
const CSSSize& aContentSize) override
|
||||
{
|
||||
if (MessageLoop::current() != mUILoop) {
|
||||
mUILoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this,
|
||||
&RemoteContentController::SendAsyncScrollDOMEvent,
|
||||
aIsRootContent, aContentRect, aContentSize));
|
||||
return;
|
||||
}
|
||||
if (mRenderFrame && aIsRootContent) {
|
||||
TabParent* browser = TabParent::GetFrom(mRenderFrame->Manager());
|
||||
BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect,
|
||||
aContentSize);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void PostDelayedTask(Task* aTask, int aDelayMs) override
|
||||
{
|
||||
#ifdef MOZ_ANDROID_APZ
|
||||
AndroidBridge::Bridge()->PostTaskToUiThread(aTask, aDelayMs);
|
||||
#else
|
||||
(MessageLoop::current() ? MessageLoop::current() : mUILoop)->
|
||||
PostDelayedTask(FROM_HERE, aTask, aDelayMs);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual bool GetTouchSensitiveRegion(CSSRect* aOutRegion) override
|
||||
|
||||
@@ -1333,7 +1333,6 @@ nsCSSFontFaceStyleDecl::GetPropertyValue(nsCSSFontDesc aFontDescID,
|
||||
}
|
||||
|
||||
|
||||
// attribute DOMString cssText;
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::GetCssText(nsAString & aCssText)
|
||||
{
|
||||
@@ -1363,7 +1362,6 @@ nsCSSFontFaceStyleDecl::SetCssText(const nsAString & aCssText)
|
||||
return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
|
||||
}
|
||||
|
||||
// DOMString getPropertyValue (in DOMString propertyName);
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::GetPropertyValue(const nsAString & propertyName,
|
||||
nsAString & aResult)
|
||||
@@ -1380,7 +1378,6 @@ nsCSSFontFaceStyleDecl::GetAuthoredPropertyValue(const nsAString& propertyName,
|
||||
return GetPropertyValue(nsCSSProps::LookupFontDesc(propertyName), aResult);
|
||||
}
|
||||
|
||||
// nsIDOMCSSValue getPropertyCSSValue (in DOMString propertyName);
|
||||
already_AddRefed<dom::CSSValue>
|
||||
nsCSSFontFaceStyleDecl::GetPropertyCSSValue(const nsAString & propertyName,
|
||||
ErrorResult& aRv)
|
||||
@@ -1390,7 +1387,6 @@ nsCSSFontFaceStyleDecl::GetPropertyCSSValue(const nsAString & propertyName,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// DOMString removeProperty (in DOMString propertyName) raises (DOMException);
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::RemoveProperty(const nsAString & propertyName,
|
||||
nsAString & aResult)
|
||||
@@ -1410,7 +1406,6 @@ nsCSSFontFaceStyleDecl::RemoveProperty(const nsAString & propertyName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// DOMString getPropertyPriority (in DOMString propertyName);
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::GetPropertyPriority(const nsAString & propertyName,
|
||||
nsAString & aResult)
|
||||
@@ -1420,8 +1415,6 @@ nsCSSFontFaceStyleDecl::GetPropertyPriority(const nsAString & propertyName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// void setProperty (in DOMString propertyName, in DOMString value,
|
||||
// in DOMString priority) raises (DOMException);
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::SetProperty(const nsAString & propertyName,
|
||||
const nsAString & value,
|
||||
@@ -1430,7 +1423,6 @@ nsCSSFontFaceStyleDecl::SetProperty(const nsAString & propertyName,
|
||||
return NS_ERROR_NOT_IMPLEMENTED; // bug 443978
|
||||
}
|
||||
|
||||
// readonly attribute unsigned long length;
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::GetLength(uint32_t *aLength)
|
||||
{
|
||||
@@ -1445,7 +1437,6 @@ nsCSSFontFaceStyleDecl::GetLength(uint32_t *aLength)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// DOMString item (in unsigned long index);
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::Item(uint32_t aIndex, nsAString& aReturn)
|
||||
{
|
||||
@@ -1476,7 +1467,6 @@ nsCSSFontFaceStyleDecl::IndexedGetter(uint32_t index, bool& aFound, nsAString &
|
||||
aFound = false;
|
||||
}
|
||||
|
||||
// readonly attribute nsIDOMCSSRule parentRule;
|
||||
NS_IMETHODIMP
|
||||
nsCSSFontFaceStyleDecl::GetParentRule(nsIDOMCSSRule** aParentRule)
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ class nsStyleContext;
|
||||
namespace mozilla {
|
||||
class WritingMode;
|
||||
class LogicalMargin;
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
||||
struct nsTableReflowState;
|
||||
struct BCPropertyData;
|
||||
|
||||
@@ -91,8 +91,8 @@ malloc_good_size_impl(size_t size)
|
||||
return je_(nallocx)(size, 0);
|
||||
}
|
||||
|
||||
static size_t
|
||||
compute_bin_unused(unsigned int narenas)
|
||||
static void
|
||||
compute_bin_unused_and_bookkeeping(jemalloc_stats_t *stats, unsigned int narenas)
|
||||
{
|
||||
size_t bin_unused = 0;
|
||||
|
||||
@@ -104,6 +104,9 @@ compute_bin_unused(unsigned int narenas)
|
||||
unsigned int nbins; // number of bins per arena
|
||||
unsigned int i, j;
|
||||
|
||||
size_t stats_metadata;
|
||||
size_t stats_ametadata = 0; // total internal allocations in all arenas
|
||||
|
||||
// narenas also counts uninitialized arenas, and initialized arenas
|
||||
// are not guaranteed to be adjacent
|
||||
VARIABLE_ARRAY(bool, initialized, narenas);
|
||||
@@ -128,7 +131,13 @@ compute_bin_unused(unsigned int narenas)
|
||||
}
|
||||
}
|
||||
|
||||
return bin_unused;
|
||||
CTL_GET("stats.metadata", stats_metadata);
|
||||
|
||||
/* get the summation for all arenas, i == narenas */
|
||||
CTL_I_GET("stats.arenas.0.metadata.allocated", stats_ametadata, narenas);
|
||||
|
||||
stats->bookkeeping = stats_metadata - stats_ametadata;
|
||||
stats->bin_unused = bin_unused;
|
||||
}
|
||||
|
||||
MOZ_JEMALLOC_API void
|
||||
@@ -150,7 +159,6 @@ jemalloc_stats_impl(jemalloc_stats_t *stats)
|
||||
CTL_GET("stats.allocated", allocated);
|
||||
CTL_GET("stats.mapped", mapped);
|
||||
CTL_GET("opt.lg_chunk", lg_chunk);
|
||||
CTL_GET("stats.bookkeeping", stats->bookkeeping);
|
||||
|
||||
/* get the summation for all arenas, i == narenas */
|
||||
CTL_I_GET("stats.arenas.0.pdirty", pdirty, narenas);
|
||||
@@ -160,7 +168,7 @@ jemalloc_stats_impl(jemalloc_stats_t *stats)
|
||||
stats->allocated = allocated;
|
||||
stats->waste = active - allocated;
|
||||
stats->page_cache = pdirty * page;
|
||||
stats->bin_unused = compute_bin_unused(narenas);
|
||||
compute_bin_unused_and_bookkeeping(stats, narenas);
|
||||
stats->waste -= stats->bin_unused;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,3 +33,6 @@ if CONFIG['_MSC_VER']:
|
||||
# This further prevents the CRT name from getting into the .obj file,
|
||||
# by avoiding pulling in a bunch of string code that uses the CRT.
|
||||
DEFINES['mozilla_Char16_h'] = True
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wshadow']
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
/* -*- 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_FastBernoulliTrial_h
|
||||
#define mozilla_FastBernoulliTrial_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/XorShift128PlusRNG.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* class FastBernoulliTrial: Efficient sampling with uniform probability
|
||||
*
|
||||
* When gathering statistics about a program's behavior, we may be observing
|
||||
* events that occur very frequently (e.g., function calls or memory
|
||||
* allocations) and we may be gathering information that is somewhat expensive
|
||||
* to produce (e.g., call stacks). Sampling all the events could have a
|
||||
* significant impact on the program's performance.
|
||||
*
|
||||
* Why not just sample every N'th event? This technique is called "systematic
|
||||
* sampling"; it's simple and efficient, and it's fine if we imagine a
|
||||
* patternless stream of events. But what if we're sampling allocations, and the
|
||||
* program happens to have a loop where each iteration does exactly N
|
||||
* allocations? You would end up sampling the same allocation every time through
|
||||
* the loop; the entire rest of the loop becomes invisible to your measurements!
|
||||
* More generally, if each iteration does M allocations, and M and N have any
|
||||
* common divisor at all, most allocation sites will never be sampled. If
|
||||
* they're both even, say, the odd-numbered allocations disappear from your
|
||||
* results.
|
||||
*
|
||||
* Ideally, we'd like each event to have some probability P of being sampled,
|
||||
* independent of its neighbors and of its position in the sequence. This is
|
||||
* called "Bernoulli sampling", and it doesn't suffer from any of the problems
|
||||
* mentioned above.
|
||||
*
|
||||
* One disadvantage of Bernoulli sampling is that you can't be sure exactly how
|
||||
* many samples you'll get: technically, it's possible that you might sample
|
||||
* none of them, or all of them. But if the number of events N is large, these
|
||||
* aren't likely outcomes; you can generally expect somewhere around P * N
|
||||
* events to be sampled.
|
||||
*
|
||||
* The other disadvantage of Bernoulli sampling is that you have to generate a
|
||||
* random number for every event, which can be slow.
|
||||
*
|
||||
* [significant pause]
|
||||
*
|
||||
* BUT NOT WITH THIS CLASS! FastBernoulliTrial lets you do true Bernoulli
|
||||
* sampling, while generating a fresh random number only when we do decide to
|
||||
* sample an event, not on every trial. When it decides not to sample, a call to
|
||||
* |FastBernoulliTrial::trial| is nothing but decrementing a counter and
|
||||
* comparing it to zero. So the lower your sampling probability is, the less
|
||||
* overhead FastBernoulliTrial imposes.
|
||||
*
|
||||
* Probabilities of 0 and 1 are handled efficiently. (In neither case need we
|
||||
* ever generate a random number at all.)
|
||||
*
|
||||
* The essential API:
|
||||
*
|
||||
* - FastBernoulliTrial(double P)
|
||||
* Construct an instance that selects events with probability P.
|
||||
*
|
||||
* - FastBernoulliTrial::trial()
|
||||
* Return true with probability P. Call this each time an event occurs, to
|
||||
* decide whether to sample it or not.
|
||||
*
|
||||
* - FastBernoulliTrial::trial(size_t n)
|
||||
* Equivalent to calling trial() |n| times, and returning true if any of those
|
||||
* calls do. However, like trial, this runs in fast constant time.
|
||||
*
|
||||
* What is this good for? In some applications, some events are "bigger" than
|
||||
* others. For example, large allocations are more significant than small
|
||||
* allocations. Perhaps we'd like to imagine that we're drawing allocations
|
||||
* from a stream of bytes, and performing a separate Bernoulli trial on every
|
||||
* byte from the stream. We can accomplish this by calling |t.trial(S)| for
|
||||
* the number of bytes S, and sampling the event if that returns true.
|
||||
*
|
||||
* Of course, this style of sampling needs to be paired with analysis and
|
||||
* presentation that makes the size of the event apparent, lest trials with
|
||||
* large values for |n| appear to be indistinguishable from those with small
|
||||
* values for |n|.
|
||||
*/
|
||||
class FastBernoulliTrial {
|
||||
/*
|
||||
* This comment should just read, "Generate skip counts with a geometric
|
||||
* distribution", and leave everyone to go look that up and see why it's the
|
||||
* right thing to do, if they don't know already.
|
||||
*
|
||||
* BUT IF YOU'RE CURIOUS, COMMENTS ARE FREE...
|
||||
*
|
||||
* Instead of generating a fresh random number for every trial, we can
|
||||
* randomly generate a count of how many times we should return false before
|
||||
* the next time we return true. We call this a "skip count". Once we've
|
||||
* returned true, we generate a fresh skip count, and begin counting down
|
||||
* again.
|
||||
*
|
||||
* Here's an awesome fact: by exercising a little care in the way we generate
|
||||
* skip counts, we can produce results indistinguishable from those we would
|
||||
* get "rolling the dice" afresh for every trial.
|
||||
*
|
||||
* In short, skip counts in Bernoulli trials of probability P obey a geometric
|
||||
* distribution. If a random variable X is uniformly distributed from [0..1),
|
||||
* then std::floor(std::log(X) / std::log(1-P)) has the appropriate geometric
|
||||
* distribution for the skip counts.
|
||||
*
|
||||
* Why that formula?
|
||||
*
|
||||
* Suppose we're to return |true| with some probability P, say, 0.3. Spread
|
||||
* all possible futures along a line segment of length 1. In portion P of
|
||||
* those cases, we'll return true on the next call to |trial|; the skip count
|
||||
* is 0. For the remaining portion 1-P of cases, the skip count is 1 or more.
|
||||
*
|
||||
* skip: 0 1 or more
|
||||
* |------------------^-----------------------------------------|
|
||||
* portion: 0.3 0.7
|
||||
* P 1-P
|
||||
*
|
||||
* But the "1 or more" section of the line is subdivided the same way: *within
|
||||
* that section*, in portion P the second call to |trial()| returns true, and in
|
||||
* portion 1-P it returns false a second time; the skip count is two or more.
|
||||
* So we return true on the second call in proportion 0.7 * 0.3, and skip at
|
||||
* least the first two in proportion 0.7 * 0.7.
|
||||
*
|
||||
* skip: 0 1 2 or more
|
||||
* |------------------^------------^----------------------------|
|
||||
* portion: 0.3 0.7 * 0.3 0.7 * 0.7
|
||||
* P (1-P)*P (1-P)^2
|
||||
*
|
||||
* We can continue to subdivide:
|
||||
*
|
||||
* skip >= 0: |------------------------------------------------- (1-P)^0 --|
|
||||
* skip >= 1: | ------------------------------- (1-P)^1 --|
|
||||
* skip >= 2: | ------------------ (1-P)^2 --|
|
||||
* skip >= 3: | ^ ---------- (1-P)^3 --|
|
||||
* skip >= 4: | . --- (1-P)^4 --|
|
||||
* .
|
||||
* ^X, see below
|
||||
*
|
||||
* In other words, the likelihood of the next n calls to |trial| returning
|
||||
* false is (1-P)^n. The longer a run we require, the more the likelihood
|
||||
* drops. Further calls may return false too, but this is the probability
|
||||
* we'll skip at least n.
|
||||
*
|
||||
* This is interesting, because we can pick a point along this line segment
|
||||
* and see which skip count's range it falls within; the point X above, for
|
||||
* example, is within the ">= 2" range, but not within the ">= 3" range, so it
|
||||
* designates a skip count of 2. So if we pick points on the line at random
|
||||
* and use the skip counts they fall under, that will be indistinguishable
|
||||
* from generating a fresh random number between 0 and 1 for each trial and
|
||||
* comparing it to P.
|
||||
*
|
||||
* So to find the skip count for a point X, we must ask: To what whole power
|
||||
* must we raise 1-P such that we include X, but the next power would exclude
|
||||
* it? This is exactly std::floor(std::log(X) / std::log(1-P)).
|
||||
*
|
||||
* Our algorithm is then, simply: When constructed, compute an initial skip
|
||||
* count. Return false from |trial| that many times, and then compute a new skip
|
||||
* count.
|
||||
*
|
||||
* For a call to |trial(n)|, if the skip count is greater than n, return false
|
||||
* and subtract n from the skip count. If the skip count is less than n,
|
||||
* return true and compute a new skip count. Since each trial is independent,
|
||||
* it doesn't matter by how much n overshoots the skip count; we can actually
|
||||
* compute a new skip count at *any* time without affecting the distribution.
|
||||
* This is really beautiful.
|
||||
*/
|
||||
public:
|
||||
/**
|
||||
* Construct a fast Bernoulli trial generator. Calls to |trial()| return true
|
||||
* with probability |aProbability|. Use |aState0| and |aState1| to seed the
|
||||
* random number generator; both may not be zero.
|
||||
*/
|
||||
FastBernoulliTrial(double aProbability, uint64_t aState0, uint64_t aState1)
|
||||
: mGenerator(aState0, aState1)
|
||||
{
|
||||
setProbability(aProbability);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true with probability |mProbability|. Call this each time an event
|
||||
* occurs, to decide whether to sample it or not. The lower |mProbability| is,
|
||||
* the faster this function runs.
|
||||
*/
|
||||
bool trial() {
|
||||
if (mSkipCount) {
|
||||
mSkipCount--;
|
||||
return false;
|
||||
}
|
||||
|
||||
return chooseSkipCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to calling trial() |n| times, and returning true if any of those
|
||||
* calls do. However, like trial, this runs in fast constant time.
|
||||
*
|
||||
* What is this good for? In some applications, some events are "bigger" than
|
||||
* others. For example, large allocations are more significant than small
|
||||
* allocations. Perhaps we'd like to imagine that we're drawing allocations
|
||||
* from a stream of bytes, and performing a separate Bernoulli trial on every
|
||||
* byte from the stream. We can accomplish this by calling |t.trial(S)| for
|
||||
* the number of bytes S, and sampling the event if that returns true.
|
||||
*
|
||||
* Of course, this style of sampling needs to be paired with analysis and
|
||||
* presentation that makes the "size" of the event apparent, lest trials with
|
||||
* large values for |n| appear to be indistinguishable from those with small
|
||||
* values for |n|, despite being potentially much more likely to be sampled.
|
||||
*/
|
||||
bool trial(size_t aCount) {
|
||||
if (mSkipCount > aCount) {
|
||||
mSkipCount -= aCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
return chooseSkipCount();
|
||||
}
|
||||
|
||||
void setRandomState(uint64_t aState0, uint64_t aState1) {
|
||||
mGenerator.setState(aState0, aState1);
|
||||
}
|
||||
|
||||
void setProbability(double aProbability) {
|
||||
MOZ_ASSERT(0 <= aProbability && aProbability <= 1);
|
||||
mProbability = aProbability;
|
||||
if (0 < mProbability && mProbability < 1) {
|
||||
/*
|
||||
* Let's look carefully at how this calculation plays out in floating-
|
||||
* point arithmetic. We'll assume IEEE, but the final C++ code we arrive
|
||||
* at would still be fine if our numbers were mathematically perfect. So,
|
||||
* while we've considered IEEE's edge cases, we haven't done anything that
|
||||
* should be actively bad when using other representations.
|
||||
*
|
||||
* (In the below, read comparisons as exact mathematical comparisons: when
|
||||
* we say something "equals 1", that means it's exactly equal to 1. We
|
||||
* treat approximation using intervals with open boundaries: saying a
|
||||
* value is in (0,1) doesn't specify how close to 0 or 1 the value gets.
|
||||
* When we use closed boundaries like [2**-53, 1], we're careful to ensure
|
||||
* the boundary values are actually representable.)
|
||||
*
|
||||
* - After the comparison above, we know mProbability is in (0,1).
|
||||
*
|
||||
* - The gaps below 1 are 2**-53, so that interval is (0, 1-2**-53].
|
||||
*
|
||||
* - Because the floating-point gaps near 1 are wider than those near
|
||||
* zero, there are many small positive doubles ε such that 1-ε rounds to
|
||||
* exactly 1. However, 2**-53 can be represented exactly. So
|
||||
* 1-mProbability is in [2**-53, 1].
|
||||
*
|
||||
* - log(1 - mProbability) is thus in (-37, 0].
|
||||
*
|
||||
* That range includes zero, but when we use mInvLogNotProbability, it
|
||||
* would be helpful if we could trust that it's negative. So when log(1
|
||||
* - mProbability) is 0, we'll just set mProbability to 0, so that
|
||||
* mInvLogNotProbability is not used in chooseSkipCount.
|
||||
*
|
||||
* - How much of the range of mProbability does this cause us to ignore?
|
||||
* The only value for which log returns 0 is exactly 1; the slope of log
|
||||
* at 1 is 1, so for small ε such that 1 - ε != 1, log(1 - ε) is -ε,
|
||||
* never 0. The gaps near one are larger than the gaps near zero, so if
|
||||
* 1 - ε wasn't 1, then -ε is representable. So if log(1 - mProbability)
|
||||
* isn't 0, then 1 - mProbability isn't 1, which means that mProbability
|
||||
* is at least 2**-53, as discussed earlier. This is a sampling
|
||||
* likelihood of roughly one in ten trillion, which is unlikely to be
|
||||
* distinguishable from zero in practice.
|
||||
*
|
||||
* So by forbidding zero, we've tightened our range to (-37, -2**-53].
|
||||
*
|
||||
* - Finally, 1 / log(1 - mProbability) is in [-2**53, -1/37). This all
|
||||
* falls readily within the range of an IEEE double.
|
||||
*
|
||||
* ALL THAT HAVING BEEN SAID: here are the five lines of actual code:
|
||||
*/
|
||||
double logNotProbability = std::log(1 - mProbability);
|
||||
if (logNotProbability == 0.0)
|
||||
mProbability = 0.0;
|
||||
else
|
||||
mInvLogNotProbability = 1 / logNotProbability;
|
||||
}
|
||||
|
||||
chooseSkipCount();
|
||||
}
|
||||
|
||||
private:
|
||||
/* The likelihood that any given call to |trial| should return true. */
|
||||
double mProbability;
|
||||
|
||||
/*
|
||||
* The value of 1/std::log(1 - mProbability), cached for repeated use.
|
||||
*
|
||||
* If mProbability is exactly 0 or exactly 1, we don't use this value.
|
||||
* Otherwise, we guarantee this value is in the range [-2**53, -1/37), i.e.
|
||||
* definitely negative, as required by chooseSkipCount. See setProbability for
|
||||
* the details.
|
||||
*/
|
||||
double mInvLogNotProbability;
|
||||
|
||||
/* Our random number generator. */
|
||||
non_crypto::XorShift128PlusRNG mGenerator;
|
||||
|
||||
/* The number of times |trial| should return false before next returning true. */
|
||||
size_t mSkipCount;
|
||||
|
||||
/*
|
||||
* Choose the next skip count. This also returns the value that |trial| should
|
||||
* return, since we have to check for the extreme values for mProbability
|
||||
* anyway, and |trial| should never return true at all when mProbability is 0.
|
||||
*/
|
||||
bool chooseSkipCount() {
|
||||
/*
|
||||
* If the probability is 1.0, every call to |trial| returns true. Make sure
|
||||
* mSkipCount is 0.
|
||||
*/
|
||||
if (mProbability == 1.0) {
|
||||
mSkipCount = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the probabilility is zero, |trial| never returns true. Don't bother us
|
||||
* for a while.
|
||||
*/
|
||||
if (mProbability == 0.0) {
|
||||
mSkipCount = SIZE_MAX;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* What sorts of values can this call to std::floor produce?
|
||||
*
|
||||
* Since mGenerator.nextDouble returns a value in [0, 1-2**-53], std::log
|
||||
* returns a value in the range [-infinity, -2**-53], all negative. Since
|
||||
* mInvLogNotProbability is negative (see its comments), the product is
|
||||
* positive and possibly infinite. std::floor returns +infinity unchanged.
|
||||
* So the result will always be positive.
|
||||
*
|
||||
* Converting a double to an integer that is out of range for that integer
|
||||
* is undefined behavior, so we must clamp our result to SIZE_MAX, to ensure
|
||||
* we get an acceptable value for mSkipCount.
|
||||
*
|
||||
* The clamp is written carefully. Note that if we had said:
|
||||
*
|
||||
* if (skipCount > SIZE_MAX)
|
||||
* skipCount = SIZE_MAX;
|
||||
*
|
||||
* that leads to undefined behavior 64-bit machines: SIZE_MAX coerced to
|
||||
* double is 2^64, not 2^64-1, so this doesn't actually set skipCount to a
|
||||
* value that can be safely assigned to mSkipCount.
|
||||
*
|
||||
* Jakub Oleson cleverly suggested flipping the sense of the comparison: if
|
||||
* we require that skipCount < SIZE_MAX, then because of the gaps (2048)
|
||||
* between doubles at that magnitude, the highest double less than 2^64 is
|
||||
* 2^64 - 2048, which is fine to store in a size_t.
|
||||
*
|
||||
* (On 32-bit machines, all size_t values can be represented exactly in
|
||||
* double, so all is well.)
|
||||
*/
|
||||
double skipCount = std::floor(std::log(mGenerator.nextDouble())
|
||||
* mInvLogNotProbability);
|
||||
if (skipCount < SIZE_MAX)
|
||||
mSkipCount = skipCount;
|
||||
else
|
||||
mSkipCount = SIZE_MAX;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace mozilla */
|
||||
|
||||
#endif /* mozilla_FastBernoulliTrial_h */
|
||||
@@ -1,23 +0,0 @@
|
||||
Backport from upstream.
|
||||
|
||||
https://code.google.com/p/double-conversion/source/detail?r=4e24bb31bcc76d6d218f3056b4c24a109d367561
|
||||
|
||||
---
|
||||
mfbt/double-conversion/utils.h | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/mfbt/double-conversion/utils.h
|
||||
+++ b/mfbt/double-conversion/utils.h
|
||||
@@ -58,11 +58,11 @@
|
||||
defined(__mips__) || \
|
||||
defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \
|
||||
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
|
||||
defined(__SH4__) || defined(__alpha__) || \
|
||||
defined(_MIPS_ARCH_MIPS32R2) || \
|
||||
- defined(_AARCH64EL_)
|
||||
+ defined(__AARCH64EL__)
|
||||
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||
#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
|
||||
#if defined(_WIN32)
|
||||
// Windows uses a 64bit wide floating point stack.
|
||||
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||
@@ -1,24 +0,0 @@
|
||||
diff --git a/a/mfbt/double-conversion/strtod.cc b/inbound/mfbt/double-conversion/strtod.cc
|
||||
index 9758989..97fa4a5 100644
|
||||
--- a/a/mfbt/double-conversion/strtod.cc
|
||||
+++ b/inbound/mfbt/double-conversion/strtod.cc
|
||||
@@ -501,17 +501,19 @@ float Strtof(Vector<const char> buffer, int exponent) {
|
||||
// if they would round to the same float. If the guess is not correct we have
|
||||
// to look at four values (since two different doubles could be the correct
|
||||
// double).
|
||||
|
||||
double double_next = Double(double_guess).NextDouble();
|
||||
double double_previous = Double(double_guess).PreviousDouble();
|
||||
|
||||
float f1 = static_cast<float>(double_previous);
|
||||
+#if defined(DEBUG)
|
||||
float f2 = float_guess;
|
||||
+#endif
|
||||
float f3 = static_cast<float>(double_next);
|
||||
float f4;
|
||||
if (is_correct) {
|
||||
f4 = f3;
|
||||
} else {
|
||||
double double_next2 = Double(double_next).NextDouble();
|
||||
f4 = static_cast<float>(double_next2);
|
||||
}
|
||||
@@ -506,9 +506,7 @@ float Strtof(Vector<const char> buffer, int exponent) {
|
||||
double double_previous = Double(double_guess).PreviousDouble();
|
||||
|
||||
float f1 = static_cast<float>(double_previous);
|
||||
#if defined(DEBUG)
|
||||
float f2 = float_guess;
|
||||
#endif
|
||||
float f3 = static_cast<float>(double_next);
|
||||
float f4;
|
||||
if (is_correct) {
|
||||
@@ -517,6 +515,7 @@ float Strtof(Vector<const char> buffer, int exponent) {
|
||||
double double_next2 = Double(double_next).NextDouble();
|
||||
f4 = static_cast<float>(double_next2);
|
||||
}
|
||||
(void) f2; // Mark variable as used.
|
||||
ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4);
|
||||
|
||||
// If the guess doesn't lie near a single-precision boundary we can simply
|
||||
|
||||
@@ -21,7 +21,3 @@ patch -p3 < use-StandardInteger.patch
|
||||
patch -p3 < use-mozilla-assertions.patch
|
||||
patch -p3 < use-static_assert.patch
|
||||
patch -p3 < ToPrecision-exponential.patch
|
||||
patch -p3 < fix-gcc-warnings.patch
|
||||
|
||||
# Merged upstream, part of 2.0.1 version
|
||||
patch -p3 < fix-aarch64-macro.patch
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
|
||||
defined(__SH4__) || defined(__alpha__) || \
|
||||
defined(_MIPS_ARCH_MIPS32R2) || \
|
||||
defined(__AARCH64EL__)
|
||||
defined(__AARCH64EL__) || defined(__aarch64__)
|
||||
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
|
||||
#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
|
||||
#if defined(_WIN32)
|
||||
@@ -141,8 +141,8 @@ template <typename T>
|
||||
class Vector {
|
||||
public:
|
||||
Vector() : start_(NULL), length_(0) {}
|
||||
Vector(T* data, int length) : start_(data), length_(length) {
|
||||
ASSERT(length == 0 || (length > 0 && data != NULL));
|
||||
Vector(T* data, int len) : start_(data), length_(len) {
|
||||
ASSERT(len == 0 || (len > 0 && data != NULL));
|
||||
}
|
||||
|
||||
// Returns a vector using the same backing storage as this one,
|
||||
@@ -184,8 +184,8 @@ class Vector {
|
||||
// buffer bounds on all operations in debug mode.
|
||||
class StringBuilder {
|
||||
public:
|
||||
StringBuilder(char* buffer, int size)
|
||||
: buffer_(buffer, size), position_(0) { }
|
||||
StringBuilder(char* buffer, int buffer_size)
|
||||
: buffer_(buffer, buffer_size), position_(0) { }
|
||||
|
||||
~StringBuilder() { if (!is_finalized()) Finalize(); }
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ EXPORTS.mozilla = [
|
||||
'EnumeratedArray.h',
|
||||
'EnumeratedRange.h',
|
||||
'EnumSet.h',
|
||||
'FastBernoulliTrial.h',
|
||||
'FloatingPoint.h',
|
||||
'Function.h',
|
||||
'GuardObjects.h',
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
/* -*- 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 "mozilla/Assertions.h"
|
||||
#include "mozilla/FastBernoulliTrial.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Note that because we always provide FastBernoulliTrial with a fixed
|
||||
// pseudorandom seed in these tests, the results here are completely
|
||||
// deterministic.
|
||||
//
|
||||
// A non-optimized version of this test runs in .009s on my laptop. Using larger
|
||||
// sample sizes lets us meet tighter bounds on the counts.
|
||||
|
||||
static void
|
||||
TestProportions()
|
||||
{
|
||||
mozilla::FastBernoulliTrial bernoulli(1.0,
|
||||
698079309544035222ULL,
|
||||
6012389156611637584ULL);
|
||||
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
MOZ_RELEASE_ASSERT(bernoulli.trial());
|
||||
|
||||
{
|
||||
bernoulli.setProbability(0.5);
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
count += bernoulli.trial();
|
||||
MOZ_RELEASE_ASSERT(count == 496);
|
||||
}
|
||||
|
||||
{
|
||||
bernoulli.setProbability(0.001);
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
count += bernoulli.trial();
|
||||
MOZ_RELEASE_ASSERT(count == 2);
|
||||
}
|
||||
|
||||
{
|
||||
bernoulli.setProbability(0.85);
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
count += bernoulli.trial();
|
||||
MOZ_RELEASE_ASSERT(count == 852);
|
||||
}
|
||||
|
||||
bernoulli.setProbability(0.0);
|
||||
for (size_t i = 0; i < 100; i++)
|
||||
MOZ_RELEASE_ASSERT(!bernoulli.trial());
|
||||
}
|
||||
|
||||
static void
|
||||
TestHarmonics()
|
||||
{
|
||||
mozilla::FastBernoulliTrial bernoulli(0.1,
|
||||
698079309544035222ULL,
|
||||
6012389156611637584ULL);
|
||||
|
||||
const size_t n = 100000;
|
||||
bool trials[n];
|
||||
for (size_t i = 0; i < n; i++)
|
||||
trials[i] = bernoulli.trial();
|
||||
|
||||
// For each harmonic and phase, check that the proportion sampled is
|
||||
// within acceptable bounds.
|
||||
for (size_t harmonic = 1; harmonic < 20; harmonic++) {
|
||||
size_t expected = n / harmonic / 10;
|
||||
size_t low_expected = expected * 85 / 100;
|
||||
size_t high_expected = expected * 115 / 100;
|
||||
|
||||
for (size_t phase = 0; phase < harmonic; phase++) {
|
||||
size_t count = 0;
|
||||
for (size_t i = phase; i < n; i += harmonic)
|
||||
count += trials[i];
|
||||
|
||||
MOZ_RELEASE_ASSERT(low_expected <= count && count <= high_expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
TestTrialN()
|
||||
{
|
||||
mozilla::FastBernoulliTrial bernoulli(0.01,
|
||||
0x67ff17e25d855942ULL,
|
||||
0x74f298193fe1c5b1ULL);
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 10000; i++)
|
||||
count += bernoulli.trial(1);
|
||||
|
||||
// Expected value: 0.01 * 10000 == 100
|
||||
MOZ_RELEASE_ASSERT(count == 97);
|
||||
}
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 10000; i++)
|
||||
count += bernoulli.trial(3);
|
||||
|
||||
// Expected value: (1 - (1 - 0.01) ** 3) == 0.0297,
|
||||
// 0.0297 * 10000 == 297
|
||||
MOZ_RELEASE_ASSERT(count == 304);
|
||||
}
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 10000; i++)
|
||||
count += bernoulli.trial(10);
|
||||
|
||||
// Expected value: (1 - (1 - 0.01) ** 10) == 0.0956,
|
||||
// 0.0956 * 10000 == 956
|
||||
MOZ_RELEASE_ASSERT(count == 936);
|
||||
}
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 10000; i++)
|
||||
count += bernoulli.trial(100);
|
||||
|
||||
// Expected value: (1 - (1 - 0.01) ** 100) == 0.6339
|
||||
// 0.6339 * 10000 == 6339
|
||||
MOZ_RELEASE_ASSERT(count == 6372);
|
||||
}
|
||||
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < 10000; i++)
|
||||
count += bernoulli.trial(1000);
|
||||
|
||||
// Expected value: (1 - (1 - 0.01) ** 1000) == 0.9999
|
||||
// 0.9999 * 10000 == 9999
|
||||
MOZ_RELEASE_ASSERT(count == 9998);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
TestChangeProbability()
|
||||
{
|
||||
mozilla::FastBernoulliTrial bernoulli(1.0,
|
||||
0x67ff17e25d855942ULL,
|
||||
0x74f298193fe1c5b1ULL);
|
||||
|
||||
// Establish a very high skip count.
|
||||
bernoulli.setProbability(0.0);
|
||||
|
||||
// This should re-establish a zero skip count.
|
||||
bernoulli.setProbability(1.0);
|
||||
|
||||
// So this should return true.
|
||||
MOZ_RELEASE_ASSERT(bernoulli.trial());
|
||||
}
|
||||
|
||||
static void
|
||||
TestCuspProbabilities()
|
||||
{
|
||||
/*
|
||||
* FastBernoulliTrial takes care to avoid screwing up on edge cases. The
|
||||
* checks here all look pretty dumb, but they exercise paths in the code that
|
||||
* could exhibit undefined behavior if coded naïvely.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This should not be perceptibly different from 1; for 64-bit doubles, this
|
||||
* is a one in ten trillion chance of the trial not succeeding. Overflows
|
||||
* converting doubles to size_t skip counts may change this, though.
|
||||
*/
|
||||
mozilla::FastBernoulliTrial bernoulli(nextafter(1, 0),
|
||||
0x67ff17e25d855942ULL,
|
||||
0x74f298193fe1c5b1ULL);
|
||||
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
MOZ_RELEASE_ASSERT(bernoulli.trial());
|
||||
|
||||
/*
|
||||
* This should not be perceptibly different from 0; for 64-bit doubles,
|
||||
* the FastBernoulliTrial will actually treat this as exactly zero.
|
||||
*/
|
||||
bernoulli.setProbability(nextafter(0, 1));
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
MOZ_RELEASE_ASSERT(!bernoulli.trial());
|
||||
|
||||
/*
|
||||
* This should be a vanishingly low probability which FastBernoulliTrial does
|
||||
* *not* treat as exactly zero.
|
||||
*/
|
||||
bernoulli.setProbability(1 - nextafter(1, 0));
|
||||
for (size_t i = 0; i < 1000; i++)
|
||||
MOZ_RELEASE_ASSERT(!bernoulli.trial());
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
TestProportions();
|
||||
TestHarmonics();
|
||||
TestTrialN();
|
||||
TestChangeProbability();
|
||||
TestCuspProbabilities();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -16,6 +16,7 @@ CppUnitTests([
|
||||
'TestCountZeroes',
|
||||
'TestEndian',
|
||||
'TestEnumSet',
|
||||
'TestFastBernoulliTrial',
|
||||
'TestFloatingPoint',
|
||||
'TestFunction',
|
||||
'TestInitializerList',
|
||||
|
||||
@@ -60,8 +60,6 @@ nsresult nsDeflateConverter::Init()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* nsIInputStream convert (in nsIInputStream aFromStream, in string aFromType
|
||||
* in string aToType, in nsISupports aCtxt); */
|
||||
NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream *aFromStream,
|
||||
const char *aFromType,
|
||||
const char *aToType,
|
||||
@@ -71,9 +69,6 @@ NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream *aFromStream,
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* void asyncConvertData (in string aFromType, in string aToType,
|
||||
* in nsIStreamListener aListener,
|
||||
* in nsISupports aCtxt); */
|
||||
NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char *aFromType,
|
||||
const char *aToType,
|
||||
nsIStreamListener *aListener,
|
||||
@@ -100,10 +95,6 @@ NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char *aFromType,
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* void onDataAvailable (in nsIRequest aRequest, in nsISupports aContext,
|
||||
* in nsIInputStream aInputStream,
|
||||
* in unsigned long long aOffset,
|
||||
* in unsigned long aCount); */
|
||||
NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aInputStream,
|
||||
@@ -148,8 +139,6 @@ NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest *aRequest,
|
||||
return mListener->OnStartRequest(aRequest, mContext);
|
||||
}
|
||||
|
||||
/* void onStopRequest (in nsIRequest aRequest, in nsISupports aContext,
|
||||
* in nsresult aStatusCode); */
|
||||
NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user