зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-central to autoland. r=merge a=merge on a CLOSED TREE
--HG-- extra : amend_source : 5afba38563fd825a1d36aaee0debd5844aca3432
This commit is contained in:
Коммит
a92de1bfc3
|
@ -128,7 +128,8 @@ HandlerProvider::GetAndSerializePayload(const MutexAutoLock&)
|
|||
}
|
||||
|
||||
HRESULT
|
||||
HandlerProvider::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
|
||||
HandlerProvider::GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
|
||||
NotNull<DWORD*> aOutPayloadSize)
|
||||
{
|
||||
MOZ_ASSERT(mscom::IsCurrentThreadMTA());
|
||||
|
||||
|
@ -151,6 +152,33 @@ HandlerProvider::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename CondFnT, typename ExeFnT>
|
||||
class MOZ_RAII ExecuteWhen final
|
||||
{
|
||||
public:
|
||||
ExecuteWhen(CondFnT& aCondFn, ExeFnT& aExeFn)
|
||||
: mCondFn(aCondFn)
|
||||
, mExeFn(aExeFn)
|
||||
{
|
||||
}
|
||||
|
||||
~ExecuteWhen()
|
||||
{
|
||||
if (mCondFn()) {
|
||||
mExeFn();
|
||||
}
|
||||
}
|
||||
|
||||
ExecuteWhen(const ExecuteWhen&) = delete;
|
||||
ExecuteWhen(ExecuteWhen&&) = delete;
|
||||
ExecuteWhen& operator=(const ExecuteWhen&) = delete;
|
||||
ExecuteWhen& operator=(ExecuteWhen&&) = delete;
|
||||
|
||||
private:
|
||||
CondFnT& mCondFn;
|
||||
ExeFnT& mExeFn;
|
||||
};
|
||||
|
||||
void
|
||||
HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
|
||||
{
|
||||
|
@ -162,18 +190,104 @@ HandlerProvider::BuildIA2Data(IA2Data* aOutIA2Data)
|
|||
RefPtr<NEWEST_IA2_INTERFACE>
|
||||
target(static_cast<NEWEST_IA2_INTERFACE*>(mTargetUnk.get()));
|
||||
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
|
||||
auto hasFailed = [&hr]() -> bool {
|
||||
return FAILED(hr);
|
||||
};
|
||||
|
||||
auto cleanup = [this, aOutIA2Data]() -> void {
|
||||
ClearIA2Data(*aOutIA2Data);
|
||||
};
|
||||
|
||||
ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed, cleanup);
|
||||
|
||||
const VARIANT kChildIdSelf = {VT_I4};
|
||||
VARIANT varVal;
|
||||
|
||||
hr = target->accLocation(&aOutIA2Data->mLeft, &aOutIA2Data->mTop,
|
||||
&aOutIA2Data->mWidth, &aOutIA2Data->mHeight,
|
||||
kChildIdSelf);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accRole(kChildIdSelf, &aOutIA2Data->mRole);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accState(kChildIdSelf, &varVal);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOutIA2Data->mState = varVal.lVal;
|
||||
|
||||
hr = target->get_accKeyboardShortcut(kChildIdSelf,
|
||||
&aOutIA2Data->mKeyboardShortcut);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accName(kChildIdSelf, &aOutIA2Data->mName);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accDescription(kChildIdSelf, &aOutIA2Data->mDescription);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accChildCount(&aOutIA2Data->mChildCount);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_accValue(kChildIdSelf, &aOutIA2Data->mValue);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_states(&aOutIA2Data->mIA2States);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->get_attributes(&aOutIA2Data->mAttributes);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HWND hwnd;
|
||||
hr = target->get_windowHandle(&hwnd);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOutIA2Data->mHwnd = PtrToLong(hwnd);
|
||||
|
||||
hr = target->get_locale(&aOutIA2Data->mIA2Locale);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = target->role(&aOutIA2Data->mIA2Role);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NB: get_uniqueID should be the final property retrieved in this method,
|
||||
// as its presence is used to determine whether the rest of this data
|
||||
// retrieval was successful.
|
||||
HRESULT hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
|
||||
if (FAILED(hr)) {
|
||||
ClearIA2Data(*aOutIA2Data);
|
||||
}
|
||||
hr = target->get_uniqueID(&aOutIA2Data->mUniqueId);
|
||||
}
|
||||
|
||||
void
|
||||
HandlerProvider::ClearIA2Data(IA2Data& aData)
|
||||
{
|
||||
::VariantClear(&aData.mRole);
|
||||
ZeroMemory(&aData, sizeof(IA2Data));
|
||||
}
|
||||
|
||||
|
@ -184,7 +298,8 @@ HandlerProvider::IsTargetInterfaceCacheable()
|
|||
}
|
||||
|
||||
HRESULT
|
||||
HandlerProvider::WriteHandlerPayload(NotNull<IStream*> aStream)
|
||||
HandlerProvider::WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
|
||||
NotNull<IStream*> aStream)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
|
|
|
@ -41,8 +41,10 @@ public:
|
|||
|
||||
// IHandlerProvider
|
||||
STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
|
||||
STDMETHODIMP GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize) override;
|
||||
STDMETHODIMP WriteHandlerPayload(NotNull<IStream*> aStream) override;
|
||||
STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
|
||||
NotNull<DWORD*> aOutPayloadSize) override;
|
||||
STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
|
||||
NotNull<IStream*> aStream) override;
|
||||
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
|
||||
STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
|
||||
ULONG aCallMethod) override;
|
||||
|
|
|
@ -49,7 +49,8 @@ static const mozilla::mscom::ArrayData sPlatformChildArrayData[] = {
|
|||
// we intend to instantiate them. Therefore RegisterProxy() must be called
|
||||
// via EnsureMTA.
|
||||
PlatformChild::PlatformChild()
|
||||
: mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
|
||||
: mIA2Proxy(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"))
|
||||
, mAccTypelib(mozilla::mscom::RegisterTypelib(L"oleacc.dll",
|
||||
mozilla::mscom::RegistrationFlags::eUseSystemDirectory))
|
||||
, mMiscTypelib(mozilla::mscom::RegisterTypelib(L"Accessible.tlb"))
|
||||
, mSdnTypelib(mozilla::mscom::RegisterTypelib(L"AccessibleMarshal.dll"))
|
||||
|
@ -72,11 +73,12 @@ PlatformChild::PlatformChild()
|
|||
});
|
||||
mCustomProxy = Move(customProxy);
|
||||
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> ia2Proxy;
|
||||
mozilla::mscom::EnsureMTA([&ia2Proxy]() -> void {
|
||||
ia2Proxy = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
|
||||
// IA2 needs to be registered in both the main thread's STA as well as the MTA
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> ia2ProxyMTA;
|
||||
mozilla::mscom::EnsureMTA([&ia2ProxyMTA]() -> void {
|
||||
ia2ProxyMTA = Move(mozilla::mscom::RegisterProxy(L"ia2marshal.dll"));
|
||||
});
|
||||
mIA2Proxy = Move(ia2Proxy);
|
||||
mIA2ProxyMTA = Move(ia2ProxyMTA);
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -28,6 +28,7 @@ private:
|
|||
mscom::MTADeletePtr<mozilla::mscom::ActivationContextRegion> mActCtxMTA;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mCustomProxy;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mIA2Proxy;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mIA2ProxyMTA;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mAccTypelib;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mMiscTypelib;
|
||||
UniquePtr<mozilla::mscom::RegisteredProxy> mSdnTypelib;
|
||||
|
|
|
@ -368,6 +368,12 @@ AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
|
|||
pVarResult, pExcepInfo, puArgErr);
|
||||
}
|
||||
|
||||
inline static BSTR
|
||||
CopyBSTR(BSTR aSrc)
|
||||
{
|
||||
return ::SysAllocStringLen(aSrc, ::SysStringLen(aSrc));
|
||||
}
|
||||
|
||||
#define BEGIN_CACHE_ACCESS \
|
||||
{ \
|
||||
HRESULT hr; \
|
||||
|
@ -376,11 +382,15 @@ AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
|
|||
} \
|
||||
}
|
||||
|
||||
static BSTR
|
||||
CopyBSTR(BSTR aSrc)
|
||||
{
|
||||
return SysAllocStringLen(aSrc, SysStringLen(aSrc));
|
||||
}
|
||||
#define GET_FIELD(member, assignTo) \
|
||||
{ \
|
||||
assignTo = mCachedData.mData.member; \
|
||||
}
|
||||
|
||||
#define GET_BSTR(member, assignTo) \
|
||||
{ \
|
||||
assignTo = CopyBSTR(mCachedData.mData.member); \
|
||||
}
|
||||
|
||||
HRESULT
|
||||
AccessibleHandler::get_accParent(IDispatch **ppdispParent)
|
||||
|
@ -398,11 +408,18 @@ AccessibleHandler::get_accChildCount(long *pcountChildren)
|
|||
if (!pcountChildren) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accChildCount(pcountChildren);
|
||||
}
|
||||
return mIA2PassThru->get_accChildCount(pcountChildren);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_FIELD(mChildCount, *pcountChildren);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -430,11 +447,18 @@ AccessibleHandler::get_accName(VARIANT varChild, BSTR *pszName)
|
|||
if (!pszName) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accName(varChild, pszName);
|
||||
}
|
||||
return mIA2PassThru->get_accName(varChild, pszName);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mName, *pszName);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -443,11 +467,18 @@ AccessibleHandler::get_accValue(VARIANT varChild, BSTR *pszValue)
|
|||
if (!pszValue) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accValue(varChild, pszValue);
|
||||
}
|
||||
return mIA2PassThru->get_accValue(varChild, pszValue);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mValue, *pszValue);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -456,11 +487,18 @@ AccessibleHandler::get_accDescription(VARIANT varChild, BSTR *pszDescription)
|
|||
if (!pszDescription) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accDescription(varChild, pszDescription);
|
||||
}
|
||||
return mIA2PassThru->get_accDescription(varChild, pszDescription);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mDescription, *pszDescription);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -470,11 +508,17 @@ AccessibleHandler::get_accRole(VARIANT varChild, VARIANT *pvarRole)
|
|||
if (!pvarRole) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accRole(varChild, pvarRole);
|
||||
}
|
||||
return mIA2PassThru->get_accRole(varChild, pvarRole);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
return ::VariantCopy(pvarRole, &mCachedData.mData.mRole);
|
||||
}
|
||||
|
||||
|
||||
|
@ -484,11 +528,19 @@ AccessibleHandler::get_accState(VARIANT varChild, VARIANT *pvarState)
|
|||
if (!pvarState) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accState(varChild, pvarState);
|
||||
}
|
||||
return mIA2PassThru->get_accState(varChild, pvarState);
|
||||
|
||||
pvarState->vt = VT_I4;
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_FIELD(mState, pvarState->lVal);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -522,11 +574,18 @@ AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
|
|||
if (!pszKeyboardShortcut) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
|
||||
}
|
||||
return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -556,11 +615,18 @@ AccessibleHandler::get_accDefaultAction(VARIANT varChild,
|
|||
if (!pszDefaultAction) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
|
||||
}
|
||||
return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mDefaultAction, *pszDefaultAction);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -577,12 +643,25 @@ HRESULT
|
|||
AccessibleHandler::accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
|
||||
long *pcyHeight, VARIANT varChild)
|
||||
{
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
|
||||
varChild);
|
||||
}
|
||||
return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
|
||||
varChild);
|
||||
|
||||
if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_FIELD(mLeft, *pxLeft);
|
||||
GET_FIELD(mTop, *pyTop);
|
||||
GET_FIELD(mWidth, *pcxWidth);
|
||||
GET_FIELD(mHeight, *pcyHeight);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -669,11 +748,18 @@ AccessibleHandler::role(long* role)
|
|||
if (!role) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->role(role);
|
||||
}
|
||||
return mIA2PassThru->role(role);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_FIELD(mIA2Role, *role);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -715,11 +801,18 @@ AccessibleHandler::get_states(AccessibleStates* states)
|
|||
if (!states) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_states(states);
|
||||
}
|
||||
return mIA2PassThru->get_states(states);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_FIELD(mIA2States, *states);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -805,11 +898,20 @@ AccessibleHandler::get_windowHandle(HWND* windowHandle)
|
|||
if (!windowHandle) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_windowHandle(windowHandle);
|
||||
}
|
||||
return mIA2PassThru->get_windowHandle(windowHandle);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
long hwnd = 0;
|
||||
GET_FIELD(mHwnd, hwnd);
|
||||
*windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
@ -828,11 +930,19 @@ AccessibleHandler::get_locale(IA2Locale* locale)
|
|||
if (!locale) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_locale(locale);
|
||||
}
|
||||
return mIA2PassThru->get_locale(locale);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mIA2Locale.language, locale->language);
|
||||
GET_BSTR(mIA2Locale.country, locale->country);
|
||||
GET_BSTR(mIA2Locale.variant, locale->variant);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -842,11 +952,18 @@ AccessibleHandler::get_attributes(BSTR* attributes)
|
|||
if (!attributes) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
||||
if (!HasPayload()) {
|
||||
HRESULT hr = ResolveIA2();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
return mIA2PassThru->get_attributes(attributes);
|
||||
}
|
||||
return mIA2PassThru->get_attributes(attributes);
|
||||
|
||||
BEGIN_CACHE_ACCESS;
|
||||
GET_BSTR(mAttributes, *attributes);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
|
|
|
@ -14,7 +14,24 @@ import "AccessibleText.idl";
|
|||
|
||||
typedef struct _IA2Data
|
||||
{
|
||||
long mUniqueId;
|
||||
VARIANT mRole;
|
||||
long mState;
|
||||
long mChildCount;
|
||||
long mIA2Role;
|
||||
AccessibleStates mIA2States;
|
||||
long mLeft;
|
||||
long mTop;
|
||||
long mWidth;
|
||||
long mHeight;
|
||||
long mHwnd;
|
||||
BSTR mKeyboardShortcut;
|
||||
BSTR mName;
|
||||
BSTR mDescription;
|
||||
BSTR mDefaultAction;
|
||||
BSTR mValue;
|
||||
BSTR mAttributes;
|
||||
IA2Locale mIA2Locale;
|
||||
long mUniqueId;
|
||||
} IA2Data;
|
||||
|
||||
interface IGeckoBackChannel;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
docload_wnd.html
|
||||
focus.html
|
||||
scroll.html
|
||||
!/accessible/tests/mochitest/*.js
|
||||
|
@ -19,8 +18,6 @@ support-files =
|
|||
[test_coalescence.html]
|
||||
[test_contextmenu.html]
|
||||
[test_descrchange.html]
|
||||
[test_docload.html]
|
||||
[test_docload_aria.html]
|
||||
[test_dragndrop.html]
|
||||
[test_flush.html]
|
||||
[test_focus_aria_activedescendant.html]
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
docload_wnd.html
|
||||
!/accessible/tests/mochitest/*.js
|
||||
|
||||
[test_docload_aria.html]
|
||||
[test_docload_busy.html]
|
||||
[test_docload_embedded.html]
|
||||
[test_docload_iframe.html]
|
||||
[test_docload_root.html]
|
||||
[test_docload_shutdown.html]
|
|
@ -10,13 +10,13 @@
|
|||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
src="../../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
src="../../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
|
@ -43,9 +43,6 @@
|
|||
|
||||
var gQueue = null;
|
||||
|
||||
// Debug stuff.
|
||||
// gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTests() {
|
||||
gQueue = new eventQueue();
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function makeIFrameVisible(aID) {
|
||||
this.DOMNode = getNode(aID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode),
|
||||
{
|
||||
type: EVENT_STATE_CHANGE,
|
||||
get target() {
|
||||
return getAccessible("iframe").firstChild;
|
||||
},
|
||||
match(aEvent) {
|
||||
// The document shouldn't have busy state (the DOM document was
|
||||
// loaded before its accessible was created). Do this test lately to
|
||||
// make sure the content of document accessible was created
|
||||
// initially, prior to this the document accessible keeps busy
|
||||
// state. The initial creation happens asynchronously after document
|
||||
// creation, there are no events we could use to catch it.
|
||||
let { state, isEnabled } = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
|
||||
return state & STATE_BUSY && !isEnabled;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.invoke = () => (this.DOMNode.style.visibility = "visible");
|
||||
|
||||
this.getID = () =>
|
||||
"The accessible for DOM document loaded before it's shown shouldn't have busy state.";
|
||||
}
|
||||
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
function doTests() {
|
||||
const gQueue = new eventQueue();
|
||||
gQueue.push(new makeIFrameVisible("iframe"));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
|
||||
title="The DOM document loaded before it's shown shouldn't have busy state">
|
||||
Mozilla Bug 658185
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="testContainer"><iframe id="iframe" src="about:" style="visibility: hidden;"></iframe></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,86 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function changeIframeSrc(aIdentifier, aURL) {
|
||||
this.DOMNode = getNode(aIdentifier);
|
||||
|
||||
function getIframeDoc() {
|
||||
return getAccessible(getNode(aIdentifier).contentDocument);
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
|
||||
new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
|
||||
];
|
||||
|
||||
this.invoke = () => (this.DOMNode.src = aURL);
|
||||
|
||||
this.finalCheck = () =>
|
||||
testAccessibleTree(this.DOMNode, {
|
||||
role: ROLE_INTERNAL_FRAME,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_DOCUMENT,
|
||||
name: aURL == "about:" ? "About:" : aURL
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.getID = () => `change iframe src on ${aURL}`;
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
function doTests() {
|
||||
const gQueue = new eventQueue();
|
||||
gQueue.push(new changeIframeSrc("iframe", "about:"));
|
||||
gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
|
||||
title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
|
||||
Mozilla Bug 420845
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
|
||||
title="Fire document load events on iframes too">
|
||||
Mozilla Bug 754165
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="testContainer"><iframe id="iframe"></iframe></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,100 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../states.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
const kHide = 1;
|
||||
const kShow = 2;
|
||||
const kRemove = 3;
|
||||
|
||||
function morphIFrame(aIdentifier, aAction) {
|
||||
this.DOMNode = getNode(aIdentifier);
|
||||
this.IFrameContainerDOMNode = this.DOMNode.parentNode;
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(aAction === kShow ? EVENT_SHOW : EVENT_HIDE, this.DOMNode),
|
||||
new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode)
|
||||
];
|
||||
|
||||
this.invoke = () => {
|
||||
if (aAction === kRemove) {
|
||||
this.IFrameContainerDOMNode.removeChild(this.DOMNode);
|
||||
} else {
|
||||
this.DOMNode.style.display = aAction === kHide ? "none" : "block";
|
||||
}
|
||||
};
|
||||
|
||||
this.finalCheck = () =>
|
||||
testAccessibleTree(this.IFrameContainerDOMNode, {
|
||||
role: ROLE_SECTION,
|
||||
children: (aAction == kHide || aAction == kRemove) ? [ ] :
|
||||
[
|
||||
{
|
||||
role: ROLE_INTERNAL_FRAME,
|
||||
children: [
|
||||
{ role: ROLE_DOCUMENT }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.getID = () => {
|
||||
if (aAction === kRemove) {
|
||||
return "remove iframe";
|
||||
}
|
||||
|
||||
return `change display style of iframe to ${aAction === kHide ? "none" : "block"}`;
|
||||
};
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
function doTests() {
|
||||
const gQueue = new eventQueue(EVENT_REORDER);
|
||||
gQueue.push(new morphIFrame("iframe", kHide));
|
||||
gQueue.push(new morphIFrame("iframe", kShow));
|
||||
gQueue.push(new morphIFrame("iframe", kRemove));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
|
||||
title="Reorganize accessible document handling">
|
||||
Mozilla Bug 566103
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="testContainer"><iframe id="iframe"></iframe></div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,135 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../role.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// Front end stuff sometimes likes to stuff things in the hidden window(s)
|
||||
// in which case there's accessibles for that content.
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Force the creation of an accessible for the hidden window's document.
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
gAccService.getAccessibleFor(doc);
|
||||
|
||||
// The private hidden window will be lazily created that's why we need to do
|
||||
// it here *before* loading '../../events.js' or else we'll have a duplicate
|
||||
// reorder event.
|
||||
let privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
|
||||
|
||||
// Force the creation of an accessible for the private hidden window's doc.
|
||||
gAccService.getAccessibleFor(privateDoc);
|
||||
</script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
let gDialog;
|
||||
let gDialogDoc;
|
||||
let gRootAcc;
|
||||
|
||||
function openDialogWnd(aURL) {
|
||||
// Get application root accessible.
|
||||
let docAcc = getAccessible(document);
|
||||
while (docAcc) {
|
||||
gRootAcc = docAcc;
|
||||
try {
|
||||
docAcc = docAcc.parent;
|
||||
} catch (e) {
|
||||
ok(false, `Can't get parent for ${prettyName(docAcc)}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
this.eventSeq = [ new invokerChecker(EVENT_REORDER, gRootAcc) ];
|
||||
|
||||
this.invoke = () => (gDialog = window.openDialog(aURL));
|
||||
|
||||
this.finalCheck = () => {
|
||||
const accTree = {
|
||||
role: ROLE_APP_ROOT,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree(gRootAcc, accTree)
|
||||
|
||||
gDialogDoc = gDialog.document;
|
||||
ok(isAccessibleInCache(gDialogDoc),
|
||||
`The document accessible for '${aURL}' is not in cache!`);
|
||||
};
|
||||
|
||||
this.getID = () => `open dialog '${aURL}'`;
|
||||
}
|
||||
|
||||
function closeDialogWnd() {
|
||||
this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
|
||||
|
||||
this.invoke = () => gDialog.close();
|
||||
|
||||
this.finalCheck = () => {
|
||||
ok(!isAccessibleInCache(gDialogDoc),
|
||||
`The document accessible for dialog is in cache still!`);
|
||||
|
||||
gDialog = gDialogDoc = gRootAcc = null;
|
||||
};
|
||||
|
||||
this.getID = () => "close dialog";
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
function doTests() {
|
||||
const gQueue = new eventQueue();
|
||||
gQueue.push(new openDialogWnd("about:"));
|
||||
gQueue.push(new closeDialogWnd());
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
|
||||
title="Fire event_reorder application root accessible">
|
||||
Mozilla Bug 506206
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,157 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../../role.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// Front end stuff sometimes likes to stuff things in the hidden window(s)
|
||||
// in which case there's accessibles for that content.
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Force the creation of an accessible for the hidden window's document.
|
||||
let doc = Services.appShell.hiddenDOMWindow.document;
|
||||
gAccService.getAccessibleFor(doc);
|
||||
|
||||
// The private hidden window will be lazily created that's why we need to do
|
||||
// it here *before* loading '../../events.js' or else we'll have a duplicate
|
||||
// reorder event.
|
||||
let privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
|
||||
|
||||
// Force the creation of an accessible for the private hidden window's doc.
|
||||
gAccService.getAccessibleFor(privateDoc);
|
||||
</script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
let gDialog;
|
||||
let gDialogDoc;
|
||||
let gRootAcc;
|
||||
let gIframeDoc
|
||||
|
||||
function openWndShutdownDoc(aURL) {
|
||||
// Get application root accessible.
|
||||
let docAcc = getAccessible(document);
|
||||
while (docAcc) {
|
||||
gRootAcc = docAcc;
|
||||
try {
|
||||
docAcc = docAcc.parent;
|
||||
} catch (e) {
|
||||
ok(false, `Can't get parent for ${prettyName(docAcc)}`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, gRootAcc),
|
||||
{
|
||||
type: EVENT_HIDE,
|
||||
get target() {
|
||||
gDialogDoc = gDialog.document;
|
||||
const iframe = gDialogDoc.getElementById("iframe");
|
||||
gIframeDoc = iframe.contentDocument;
|
||||
return iframe;
|
||||
},
|
||||
get targetDescr() {
|
||||
return "inner iframe of docload_wnd.html document";
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
this.invoke = () => gDialog = window.openDialog(aURL);
|
||||
|
||||
this.finalCheck = () => {
|
||||
const accTree = {
|
||||
role: ROLE_APP_ROOT,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree(gRootAcc, accTree)
|
||||
// After timeout after event hide for iframe was handled the document
|
||||
// accessible for iframe's document should no longer be in cache.
|
||||
ok(!isAccessibleInCache(gIframeDoc),
|
||||
"The document accessible for iframe is in cache still after iframe hide!");
|
||||
ok(isAccessibleInCache(gDialogDoc),
|
||||
`The document accessible for '${aURL}' is not in cache!`);
|
||||
};
|
||||
|
||||
this.getID = () => `open dialog '${aURL}'`;
|
||||
}
|
||||
|
||||
function closeWndShutdownDoc() {
|
||||
this.eventSeq = [ new invokerChecker(EVENT_FOCUS, getAccessible(document)) ];
|
||||
|
||||
this.invoke = () => gDialog.close();
|
||||
|
||||
this.finalCheck = () => {
|
||||
ok(!isAccessibleInCache(gDialogDoc),
|
||||
"The document accessible for dialog is in cache still!");
|
||||
// After the window is closed all alive subdocument accessibles should
|
||||
// be shut down.
|
||||
ok(!isAccessibleInCache(gIframeDoc),
|
||||
"The document accessible for iframe is in cache still!");
|
||||
|
||||
gDialog = gDialogDoc = gRootAcc = gIframeDoc = null;
|
||||
};
|
||||
|
||||
this.getID = () => "close dialog";
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
function doTests() {
|
||||
const gQueue = new eventQueue();
|
||||
gQueue.push(new openWndShutdownDoc("../../events/docload/docload_wnd.html"));
|
||||
gQueue.push(new closeWndShutdownDoc());
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
|
||||
title="Shutdown document accessible when presshell goes away">
|
||||
Mozilla Bug 571459
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1,337 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<title>Accessible events testing for document</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../states.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// Front end stuff sometimes likes to stuff things in the hidden window(s)
|
||||
// in which case there's accessibles for that content.
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Force the creation of an accessible for the hidden window's document.
|
||||
var doc = Services.appShell.hiddenDOMWindow.document;
|
||||
gAccService.getAccessibleFor(doc);
|
||||
|
||||
// The private hidden window will be lazily created that's why we need to do
|
||||
// it here *before* loading '../events.js' or else we'll have a duplicate
|
||||
// reorder event.
|
||||
var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
|
||||
|
||||
// Force the creation of an accessible for the private hidden window's doc.
|
||||
gAccService.getAccessibleFor(privateDoc);
|
||||
</script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Invokers
|
||||
|
||||
function changeIframeSrc(aIdentifier, aURL) {
|
||||
this.DOMNode = getNode(aIdentifier);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
|
||||
new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
|
||||
];
|
||||
|
||||
this.invoke = function changeIframeSrc_invoke() {
|
||||
this.DOMNode.src = aURL;
|
||||
};
|
||||
|
||||
this.finalCheck = function changeIframeSrc_finalCheck() {
|
||||
var accTree = {
|
||||
role: ROLE_INTERNAL_FRAME,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_DOCUMENT,
|
||||
name: aURL == "about:" ? "About:" : aURL
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree(this.DOMNode, accTree);
|
||||
};
|
||||
|
||||
this.getID = function changeIframeSrc_getID() {
|
||||
return "change iframe src on " + aURL;
|
||||
};
|
||||
|
||||
function getIframeDoc() {
|
||||
return getAccessible(getNode(aIdentifier).contentDocument);
|
||||
}
|
||||
}
|
||||
|
||||
const kHide = 1;
|
||||
const kShow = 2;
|
||||
const kRemove = 3;
|
||||
function morphIFrame(aIdentifier, aAction) {
|
||||
this.DOMNode = getNode(aIdentifier);
|
||||
this.IFrameContainerDOMNode = this.DOMNode.parentNode;
|
||||
|
||||
this.eventSeq = [];
|
||||
|
||||
var checker = null;
|
||||
if (aAction == kShow)
|
||||
checker = new invokerChecker(EVENT_SHOW, this.DOMNode);
|
||||
else
|
||||
checker = new invokerChecker(EVENT_HIDE, this.DOMNode);
|
||||
this.eventSeq.push(checker);
|
||||
|
||||
var reorderChecker =
|
||||
new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode);
|
||||
this.eventSeq.push(reorderChecker);
|
||||
|
||||
this.invoke = function morphIFrame_invoke() {
|
||||
if (aAction == kHide) {
|
||||
this.DOMNode.style.display = "none";
|
||||
} else if (aAction == kShow) {
|
||||
this.DOMNode.style.display = "block";
|
||||
} else {
|
||||
this.IFrameContainerDOMNode.removeChild(this.DOMNode);
|
||||
}
|
||||
};
|
||||
|
||||
this.finalCheck = function morphIFrame_finalCheck() {
|
||||
var accTree = {
|
||||
role: ROLE_SECTION,
|
||||
children: (aAction == kHide || aAction == kRemove) ? [ ] :
|
||||
[
|
||||
{
|
||||
role: ROLE_INTERNAL_FRAME,
|
||||
children: [
|
||||
{ role: ROLE_DOCUMENT }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree(this.IFrameContainerDOMNode, accTree);
|
||||
};
|
||||
|
||||
this.getID = function morphIFrame_getID() {
|
||||
if (aAction == kRemove)
|
||||
return "remove iframe";
|
||||
|
||||
return "change display style of iframe to " +
|
||||
((aAction == kHide) ? "none" : "block");
|
||||
};
|
||||
}
|
||||
|
||||
function makeIFrameVisible(aID) {
|
||||
this.DOMNode = getNode(aID);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode)
|
||||
];
|
||||
|
||||
this.invoke = function makeIFrameVisible_invoke() {
|
||||
this.DOMNode.style.visibility = "visible";
|
||||
};
|
||||
|
||||
this.getID = function makeIFrameVisible_getID() {
|
||||
return "The accessible for DOM document loaded before it's shown shouldn't have busy state.";
|
||||
};
|
||||
}
|
||||
|
||||
function openDialogWnd(aURL) {
|
||||
// Get application root accessible.
|
||||
var docAcc = getAccessible(document);
|
||||
while (docAcc) {
|
||||
this.mRootAcc = docAcc;
|
||||
try {
|
||||
docAcc = docAcc.parent;
|
||||
} catch (e) {
|
||||
ok(false, "Can't get parent for " + prettyName(docAcc));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_REORDER, this.mRootAcc)
|
||||
];
|
||||
|
||||
this.invoke = function openDialogWnd_invoke() {
|
||||
this.mDialog = window.openDialog(aURL);
|
||||
};
|
||||
|
||||
this.finalCheck = function openDialogWnd_finalCheck() {
|
||||
this.finalCheckImpl();
|
||||
};
|
||||
|
||||
this.finalCheckImpl = function openDialogWnd_finalCheckImpl() {
|
||||
var accTree = {
|
||||
role: ROLE_APP_ROOT,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
},
|
||||
{
|
||||
role: ROLE_CHROME_WINDOW
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
testAccessibleTree(this.mRootAcc, accTree);
|
||||
|
||||
var dlgDoc = this.mDialog.document;
|
||||
ok(isAccessibleInCache(dlgDoc),
|
||||
"The document accessible for '" + aURL + "' is not in cache!");
|
||||
|
||||
this.mDialog.close();
|
||||
|
||||
// close() is asynchronous.
|
||||
SimpleTest.executeSoon(function() {
|
||||
ok(!isAccessibleInCache(dlgDoc),
|
||||
"The document accessible for '" + aURL + "' is in cache still!");
|
||||
});
|
||||
};
|
||||
|
||||
this.getID = function openDialogWnd_getID() {
|
||||
return "open dialog '" + aURL + "'";
|
||||
};
|
||||
}
|
||||
|
||||
function openWndShutdownDoc() {
|
||||
this.__proto__ =
|
||||
new openDialogWnd("../events/docload_wnd.html");
|
||||
|
||||
var thisObj = this;
|
||||
var docChecker = {
|
||||
type: EVENT_HIDE,
|
||||
get target() {
|
||||
var iframe = this.invoker.mDialog.document.getElementById("iframe");
|
||||
this.invoker.iframeDoc = iframe.contentDocument;
|
||||
return iframe;
|
||||
},
|
||||
get targetDescr() {
|
||||
return "inner iframe of docload_wnd.html document";
|
||||
},
|
||||
invoker: thisObj
|
||||
};
|
||||
|
||||
this.eventSeq.push(docChecker);
|
||||
|
||||
this.finalCheck = function openWndShutdownDoc_finalCheck() {
|
||||
// After timeout after event hide for iframe was handled the document
|
||||
// accessible for iframe's document is in cache still.
|
||||
ok(!isAccessibleInCache(this.iframeDoc),
|
||||
"The document accessible for iframe is in cache still after iframe hide!");
|
||||
|
||||
this.finalCheckImpl();
|
||||
|
||||
// After the window is closed all alive subdocument accessibles should
|
||||
// be shut down.
|
||||
ok(!isAccessibleInCache(this.iframeDoc),
|
||||
"The document accessible for iframe is in cache still!");
|
||||
};
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
// Debug stuff.
|
||||
// gA11yEventDumpID = "eventdump";
|
||||
// gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTests() {
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new changeIframeSrc("iframe", "about:"));
|
||||
gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
|
||||
gQueue.push(new morphIFrame("iframe", kHide));
|
||||
gQueue.push(new morphIFrame("iframe", kShow));
|
||||
gQueue.push(new morphIFrame("iframe", kRemove));
|
||||
gQueue.push(new makeIFrameVisible("iframe2"));
|
||||
gQueue.push(new openDialogWnd("about:"));
|
||||
gQueue.push(new openWndShutdownDoc());
|
||||
|
||||
gQueue.onFinish = doLastCallTests;
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
function doLastCallTests() {
|
||||
// ////////////////////////////////////////////////////////////////////////
|
||||
// makeIFrameVisible() test, part2
|
||||
|
||||
// The document shouldn't have busy state (the DOM document was loaded
|
||||
// before its accessible was created). Do this test lately to make sure
|
||||
// the content of document accessible was created initially, prior to this
|
||||
// the document accessible keeps busy state. The initial creation happens
|
||||
// asynchronously after document creation, there are no events we could
|
||||
// use to catch it.
|
||||
var iframeDoc = getAccessible("iframe2").firstChild;
|
||||
testStates(iframeDoc, 0, 0, STATE_BUSY);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
|
||||
title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
|
||||
Mozilla Bug 420845
|
||||
</a><br>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
|
||||
title="Fire event_reorder application root accessible">
|
||||
Mozilla Bug 506206
|
||||
</a><br>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
|
||||
title="Reorganize accessible document handling">
|
||||
Mozilla Bug 566103
|
||||
</a><br>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
|
||||
title="Shutdown document accessible when presshell goes away">
|
||||
Mozilla Bug 571459
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
|
||||
title="The DOM document loaded before it's shown shouldn't have busy state">
|
||||
Mozilla Bug 658185
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
|
||||
title="Fire document load events on iframes too">
|
||||
Mozilla Bug 754165
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="testContainer"><iframe id="iframe"></iframe></div>
|
||||
<div id="testContainer2"><iframe id="iframe2" src="about:" style="visibility: hidden;"></iframe></div>
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -13,6 +13,7 @@ A11Y_MANIFESTS += [
|
|||
'editabletext/a11y.ini',
|
||||
'elm/a11y.ini',
|
||||
'events/a11y.ini',
|
||||
'events/docload/a11y.ini',
|
||||
'focus/a11y.ini',
|
||||
'hittest/a11y.ini',
|
||||
'hyperlink/a11y.ini',
|
||||
|
|
|
@ -42,6 +42,10 @@ ia2Accessible::QueryInterface(REFIID iid, void** ppv)
|
|||
|
||||
*ppv = nullptr;
|
||||
|
||||
// NOTE: If any new versions of IAccessible2 are added here, they should
|
||||
// also be added to the IA2 Handler in
|
||||
// /accessible/ipc/win/handler/AccessibleHandler.cpp
|
||||
|
||||
if (IID_IAccessible2_3 == iid)
|
||||
*ppv = static_cast<IAccessible2_3*>(this);
|
||||
else if (IID_IAccessible2_2 == iid)
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsEventMap.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "mozilla/mscom/AsyncInvoker.h"
|
||||
|
||||
|
@ -1136,6 +1137,35 @@ AccessibleWrap::SetID(uint32_t aID)
|
|||
mID = aID;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsHandlerInvalidationNeeded(uint32_t aEvent)
|
||||
{
|
||||
// We want to return true for any events that would indicate that something
|
||||
// in the handler cache is out of date.
|
||||
switch (aEvent) {
|
||||
case EVENT_OBJECT_STATECHANGE:
|
||||
case EVENT_OBJECT_LOCATIONCHANGE:
|
||||
case EVENT_OBJECT_NAMECHANGE:
|
||||
case EVENT_OBJECT_DESCRIPTIONCHANGE:
|
||||
case EVENT_OBJECT_VALUECHANGE:
|
||||
case IA2_EVENT_ACTION_CHANGED:
|
||||
case IA2_EVENT_DOCUMENT_LOAD_COMPLETE:
|
||||
case IA2_EVENT_DOCUMENT_LOAD_STOPPED:
|
||||
case IA2_EVENT_DOCUMENT_ATTRIBUTE_CHANGED:
|
||||
case IA2_EVENT_DOCUMENT_CONTENT_CHANGED:
|
||||
case IA2_EVENT_PAGE_CHANGED:
|
||||
case IA2_EVENT_TEXT_ATTRIBUTE_CHANGED:
|
||||
case IA2_EVENT_TEXT_CHANGED:
|
||||
case IA2_EVENT_TEXT_INSERTED:
|
||||
case IA2_EVENT_TEXT_REMOVED:
|
||||
case IA2_EVENT_TEXT_UPDATED:
|
||||
case IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
|
||||
{
|
||||
|
@ -1158,6 +1188,10 @@ AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
|
|||
return;
|
||||
}
|
||||
|
||||
if (IsHandlerInvalidationNeeded(winEvent)) {
|
||||
InvalidateHandlers();
|
||||
}
|
||||
|
||||
// Fire MSAA event for client area window.
|
||||
::NotifyWinEvent(winEvent, hwnd, OBJID_CLIENT, childID);
|
||||
|
||||
|
@ -1659,6 +1693,38 @@ AccessibleWrap::SetHandlerControl(DWORD aPid, RefPtr<IHandlerControl> aCtrl)
|
|||
sHandlerControllers->AppendElement(Move(ctrlData));
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
AccessibleWrap::InvalidateHandlers()
|
||||
{
|
||||
static const HRESULT kErrorServerDied =
|
||||
HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE);
|
||||
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!sHandlerControllers || sHandlerControllers->IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We iterate in reverse so that we may safely remove defunct elements while
|
||||
// executing the loop.
|
||||
for (auto& controller : Reversed(*sHandlerControllers)) {
|
||||
MOZ_ASSERT(controller.mPid);
|
||||
MOZ_ASSERT(controller.mCtrl);
|
||||
|
||||
ASYNC_INVOKER_FOR(IHandlerControl) invoker(controller.mCtrl,
|
||||
Some(controller.mIsProxy));
|
||||
|
||||
HRESULT hr = ASYNC_INVOKE(invoker, Invalidate);
|
||||
|
||||
if (hr == CO_E_OBJNOTCONNECTED || hr == kErrorServerDied) {
|
||||
sHandlerControllers->RemoveElement(controller);
|
||||
} else {
|
||||
NS_WARN_IF(FAILED(hr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AccessibleWrap::DispatchTextChangeToHandler(bool aIsInsert,
|
||||
|
|
|
@ -199,6 +199,8 @@ public:
|
|||
|
||||
static void SetHandlerControl(DWORD aPid, RefPtr<IHandlerControl> aCtrl);
|
||||
|
||||
static void InvalidateHandlers();
|
||||
|
||||
bool DispatchTextChangeToHandler(bool aIsInsert, const nsString& aText,
|
||||
int32_t aStart, uint32_t aLen);
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ subsuite = clipboard
|
|||
[browser_newtab_drop_preview.js]
|
||||
[browser_newtab_focus.js]
|
||||
[browser_newtab_fullscreen_focus.js]
|
||||
skip-if = os == "mac" && debug # bug 1394963
|
||||
skip-if = os == "mac" # bug 1394963
|
||||
[browser_newtab_perwindow_private_browsing.js]
|
||||
[browser_newtab_reflow_load.js]
|
||||
support-files =
|
||||
|
|
|
@ -60,6 +60,18 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
|
|||
times: 60, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
times: 6, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
// Bug 1359989
|
||||
{
|
||||
stack: [
|
||||
|
|
|
@ -60,6 +60,18 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
|
|||
times: 60, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
{
|
||||
stack: [
|
||||
"_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
|
||||
"_openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openAutocompletePopup@chrome://browser/content/urlbarBindings.xml",
|
||||
"openPopup@chrome://global/content/bindings/autocomplete.xml",
|
||||
"set_popupOpen@chrome://global/content/bindings/autocomplete.xml",
|
||||
],
|
||||
times: 6, // This number should only ever go down - never up.
|
||||
},
|
||||
|
||||
// Bug 1359989
|
||||
{
|
||||
stack: [
|
||||
|
|
|
@ -1815,15 +1815,6 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
onget="return this._margins;">
|
||||
<setter>
|
||||
<![CDATA[
|
||||
if (val == this._margins) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val && this._margins && val.start == this._margins.start &&
|
||||
val.end == this._margins.end) {
|
||||
return val;
|
||||
}
|
||||
|
||||
this._margins = val;
|
||||
|
||||
if (val) {
|
||||
|
@ -1840,9 +1831,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
this.style.removeProperty("--item-padding-start");
|
||||
this.style.removeProperty("--item-padding-end");
|
||||
}
|
||||
for (let item of this.richlistbox.childNodes) {
|
||||
item.handleOverUnderflow();
|
||||
}
|
||||
|
||||
return val;
|
||||
]]>
|
||||
</setter>
|
||||
|
@ -1904,6 +1893,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
// 250px". Do this *before* adding any items because when the new
|
||||
// value of the margins are different from the previous value, over-
|
||||
// and underflow must be handled for each item already in the popup.
|
||||
let needsHandleOverUnderflow = false;
|
||||
let boundToCheck = popupDirection == "rtl" ? "right" : "left";
|
||||
let inputRect = this.DOMWindowUtils.getBoundsWithoutFlushing(aInput);
|
||||
let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
|
||||
|
@ -1922,17 +1912,20 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
let identityIcon = document.getElementById("identity-icon");
|
||||
let identityRect =
|
||||
this.DOMWindowUtils.getBoundsWithoutFlushing(identityIcon);
|
||||
if (popupDirection == "rtl") {
|
||||
this.margins = { start: documentRect.right - identityRect.right,
|
||||
end: endOffset };
|
||||
} else {
|
||||
this.margins = { start: identityRect.left,
|
||||
end: endOffset };
|
||||
let start = popupDirection == "rtl" ?
|
||||
documentRect.right - identityRect.right :
|
||||
identityRect.left;
|
||||
if (!this.margins || start != this.margins.start ||
|
||||
endOffset != this.margins.end ||
|
||||
width != this.margins.width) {
|
||||
this.margins = { start, end: endOffset, width };
|
||||
needsHandleOverUnderflow = true;
|
||||
}
|
||||
} else {
|
||||
} else if (this.margins) {
|
||||
// Reset the alignment so that the site icons are positioned
|
||||
// according to whatever's in the CSS.
|
||||
this.margins = undefined;
|
||||
needsHandleOverUnderflow = true;
|
||||
}
|
||||
|
||||
// Now that the margins have been set, start adding items (via
|
||||
|
@ -1970,6 +1963,14 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
this.DOMWindowUtils.getBoundsWithoutFlushing(aInput).bottom);
|
||||
|
||||
this.openPopup(aElement, "after_start", 0, yOffset, false, false);
|
||||
|
||||
// Do this immediately after we've requested the popup to open. This
|
||||
// will cause sync reflows but prevents flickering.
|
||||
if (needsHandleOverUnderflow) {
|
||||
for (let item of this.richlistbox.childNodes) {
|
||||
item.handleOverUnderflow();
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
This is the PDF.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 1.9.659
|
||||
Current extension version is: 1.10.88
|
||||
|
||||
Taken from upstream commit: 3ac4baff
|
||||
Taken from upstream commit: c62a1938
|
||||
|
|
|
@ -1990,7 +1990,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
|
|||
if (worker.destroyed) {
|
||||
return Promise.reject(new Error('Worker was destroyed'));
|
||||
}
|
||||
let apiVersion = '1.9.659';
|
||||
let apiVersion = '1.10.88';
|
||||
source.disableAutoFetch = (0, _dom_utils.getDefaultSetting)('disableAutoFetch');
|
||||
source.disableStream = (0, _dom_utils.getDefaultSetting)('disableStream');
|
||||
source.chunkedViewerLoading = !!pdfDataRangeTransport;
|
||||
|
@ -3311,8 +3311,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
|
|||
}();
|
||||
var version, build;
|
||||
{
|
||||
exports.version = version = '1.9.659';
|
||||
exports.build = build = '3ac4baff';
|
||||
exports.version = version = '1.10.88';
|
||||
exports.build = build = 'c62a1938';
|
||||
}
|
||||
exports.getDocument = getDocument;
|
||||
exports.LoopbackPort = LoopbackPort;
|
||||
|
@ -5051,8 +5051,8 @@ exports.SVGGraphics = SVGGraphics;
|
|||
"use strict";
|
||||
|
||||
|
||||
var pdfjsVersion = '1.9.659';
|
||||
var pdfjsBuild = '3ac4baff';
|
||||
var pdfjsVersion = '1.10.88';
|
||||
var pdfjsBuild = 'c62a1938';
|
||||
var pdfjsSharedUtil = __w_pdfjs_require__(0);
|
||||
var pdfjsDisplayGlobal = __w_pdfjs_require__(13);
|
||||
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
|
||||
|
@ -8177,8 +8177,8 @@ if (!_global_scope2.default.PDFJS) {
|
|||
}
|
||||
var PDFJS = _global_scope2.default.PDFJS;
|
||||
{
|
||||
PDFJS.version = '1.9.659';
|
||||
PDFJS.build = '3ac4baff';
|
||||
PDFJS.version = '1.10.88';
|
||||
PDFJS.build = 'c62a1938';
|
||||
}
|
||||
PDFJS.pdfBug = false;
|
||||
if (PDFJS.verbosity !== undefined) {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -71,5 +71,19 @@ this.LightweightThemes = {
|
|||
});
|
||||
},
|
||||
},
|
||||
|
||||
compactLight: {
|
||||
selectors: ["#navigator-toolbox"],
|
||||
applyConfig() {
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-light@mozilla.org");
|
||||
},
|
||||
},
|
||||
|
||||
compactDark: {
|
||||
selectors: ["#navigator-toolbox"],
|
||||
applyConfig() {
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme("firefox-compact-dark@mozilla.org");
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@ def rust_compiler(rustc_info, cargo_info):
|
|||
You can install rust by running './mach bootstrap'
|
||||
or by directly running the installer from https://rustup.rs/
|
||||
'''))
|
||||
rustc_min_version = Version('1.20.0')
|
||||
rustc_min_version = Version('1.21.0')
|
||||
cargo_min_version = Version('0.{}'.format(rustc_min_version.minor + 1))
|
||||
|
||||
version = rustc_info.version
|
||||
|
|
|
@ -1441,7 +1441,7 @@ already_AddRefed<SystemPrincipal>
|
|||
nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
|
||||
{
|
||||
if (gScriptSecMan)
|
||||
return do_AddRef(gScriptSecMan->mSystemPrincipal.get()).downcast<SystemPrincipal>();
|
||||
return do_AddRef(gScriptSecMan->mSystemPrincipal).downcast<SystemPrincipal>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createEnum } = require("devtools/client/shared/enum");
|
||||
|
||||
createEnum([
|
||||
|
||||
// Update the entire changes state with the new list of changes.
|
||||
"UPDATE_CHANGES",
|
||||
|
||||
], module.exports);
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'index.js',
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const ChangesApp = createFactory(require("./components/ChangesApp"));
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
class ChangesView {
|
||||
|
||||
constructor(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let changesApp = ChangesApp({});
|
||||
|
||||
let provider = createElement(Provider, {
|
||||
id: "changesview",
|
||||
key: "changesview",
|
||||
store: this.store,
|
||||
title: INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle")
|
||||
}, changesApp);
|
||||
|
||||
// Expose the provider to let inspector.js use it in setupSidebar.
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.document = null;
|
||||
this.inspector = null;
|
||||
this.store = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ChangesView;
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
DOM: dom,
|
||||
PureComponent,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
class ChangesApp extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return dom.div(
|
||||
{
|
||||
id: "changes-container",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(state => state)(ChangesApp);
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'ChangesApp.js',
|
||||
)
|
|
@ -0,0 +1,15 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
'actions',
|
||||
'components',
|
||||
'reducers',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'changes.js',
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const INITIAL_CHANGES = [];
|
||||
|
||||
let reducers = {
|
||||
|
||||
};
|
||||
|
||||
module.exports = function (changes = INITIAL_CHANGES, action) {
|
||||
let reducer = reducers[action.type];
|
||||
if (!reducer) {
|
||||
return changes;
|
||||
}
|
||||
return reducer(changes, action);
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.changes = require("./changes");
|
|
@ -0,0 +1,10 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'changes.js',
|
||||
'index.js',
|
||||
)
|
|
@ -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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createEnum } = require("devtools/client/shared/enum");
|
||||
|
||||
createEnum([
|
||||
|
||||
// Update the entire events state with the new list of events.
|
||||
"UPDATE_EVENTS",
|
||||
|
||||
], module.exports);
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'index.js',
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
DOM: dom,
|
||||
PureComponent,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
class EventsApp extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return dom.div(
|
||||
{
|
||||
id: "events-container",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = connect(state => state)(EventsApp);
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'EventsApp.js',
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const EventsApp = createFactory(require("./components/EventsApp"));
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
class EventsView {
|
||||
|
||||
constructor(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let eventsApp = EventsApp({});
|
||||
|
||||
let provider = createElement(Provider, {
|
||||
id: "eventsview",
|
||||
key: "eventsview",
|
||||
store: this.store,
|
||||
title: INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle")
|
||||
}, eventsApp);
|
||||
|
||||
// Expose the provider to let inspector.js use it in setupSidebar.
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.document = null;
|
||||
this.inspector = null;
|
||||
this.store = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = EventsView;
|
|
@ -0,0 +1,15 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += [
|
||||
'actions',
|
||||
'components',
|
||||
'reducers',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
'events.js',
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const INITIAL_EVENTS = [];
|
||||
|
||||
let reducers = {
|
||||
|
||||
};
|
||||
|
||||
module.exports = function (events = INITIAL_EVENTS, action) {
|
||||
let reducer = reducers[action.type];
|
||||
if (!reducer) {
|
||||
return events;
|
||||
}
|
||||
return reducer(events, action);
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
exports.events = require("./events");
|
|
@ -0,0 +1,10 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'events.js',
|
||||
'index.js',
|
||||
)
|
|
@ -4,10 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
createElement, createFactory,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
|
||||
const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const ExtensionSidebarComponent = createFactory(require("./components/ExtensionSidebar"));
|
||||
|
|
|
@ -8,4 +8,7 @@ const { createEnum } = require("devtools/client/shared/enum");
|
|||
|
||||
createEnum([
|
||||
|
||||
// Update the entire flexboxes state with the new list of flexboxes.
|
||||
"UPDATE_FLEXBOXES",
|
||||
|
||||
], module.exports);
|
||||
|
|
|
@ -4,6 +4,14 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
|
||||
exports.flexbox = {
|
||||
|
||||
// The id of the flexbox container.
|
||||
id: PropTypes.number,
|
||||
|
||||
// The node front of the flexbox container.
|
||||
nodeFront: PropTypes.object,
|
||||
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ const Types = require("../types");
|
|||
|
||||
const PREVIEW_UPDATE_DELAY = 150;
|
||||
|
||||
class App extends PureComponent {
|
||||
class FontsApp extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
fonts: PropTypes.arrayOf(PropTypes.shape(Types.font)).isRequired,
|
||||
|
@ -74,4 +74,4 @@ class App extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = connect(state => state)(App);
|
||||
module.exports = connect(state => state)(FontsApp);
|
|
@ -5,7 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'App.js',
|
||||
'Font.js',
|
||||
'FontList.js',
|
||||
'FontsApp.js',
|
||||
)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Task } = require("devtools/shared/task");
|
||||
const { getColor } = require("devtools/client/shared/theme");
|
||||
|
||||
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
|
||||
|
@ -14,7 +13,7 @@ const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
|||
|
||||
const { gDevTools } = require("devtools/client/framework/devtools");
|
||||
|
||||
const App = createFactory(require("./components/App"));
|
||||
const FontsApp = createFactory(require("./components/FontsApp"));
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
|
@ -23,29 +22,30 @@ const INSPECTOR_L10N =
|
|||
const { updateFonts } = require("./actions/fonts");
|
||||
const { updatePreviewText, updateShowAllFonts } = require("./actions/font-options");
|
||||
|
||||
function FontInspector(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.pageStyle = this.inspector.pageStyle;
|
||||
this.store = this.inspector.store;
|
||||
class FontInspector {
|
||||
|
||||
this.update = this.update.bind(this);
|
||||
constructor(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.pageStyle = this.inspector.pageStyle;
|
||||
this.store = this.inspector.store;
|
||||
|
||||
this.onNewNode = this.onNewNode.bind(this);
|
||||
this.onPreviewFonts = this.onPreviewFonts.bind(this);
|
||||
this.onShowAllFont = this.onShowAllFont.bind(this);
|
||||
this.onThemeChanged = this.onThemeChanged.bind(this);
|
||||
this.update = this.update.bind(this);
|
||||
|
||||
this.init();
|
||||
}
|
||||
this.onNewNode = this.onNewNode.bind(this);
|
||||
this.onPreviewFonts = this.onPreviewFonts.bind(this);
|
||||
this.onShowAllFont = this.onShowAllFont.bind(this);
|
||||
this.onThemeChanged = this.onThemeChanged.bind(this);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
FontInspector.prototype = {
|
||||
init() {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
let app = App({
|
||||
let fontsApp = FontsApp({
|
||||
onPreviewFonts: this.onPreviewFonts,
|
||||
onShowAllFont: this.onShowAllFont,
|
||||
});
|
||||
|
@ -55,7 +55,7 @@ FontInspector.prototype = {
|
|||
key: "fontinspector",
|
||||
store: this.store,
|
||||
title: INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle"),
|
||||
}, app);
|
||||
}, fontsApp);
|
||||
|
||||
// Expose the provider to let inspector.js use it in setupSidebar.
|
||||
this.provider = provider;
|
||||
|
@ -69,13 +69,13 @@ FontInspector.prototype = {
|
|||
this.store.dispatch(updatePreviewText(""));
|
||||
this.store.dispatch(updateShowAllFonts(false));
|
||||
this.update(false, "");
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruction function called when the inspector is destroyed. Removes event listeners
|
||||
* and cleans up references.
|
||||
*/
|
||||
destroy: function () {
|
||||
destroy() {
|
||||
this.inspector.selection.off("new-node-front", this.onNewNode);
|
||||
this.inspector.sidebar.off("fontinspector-selected", this.onNewNode);
|
||||
gDevTools.off("theme-switched", this.onThemeChanged);
|
||||
|
@ -84,7 +84,7 @@ FontInspector.prototype = {
|
|||
this.inspector = null;
|
||||
this.pageStyle = null;
|
||||
this.store = null;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the font inspector panel is visible, and false otherwise.
|
||||
|
@ -92,7 +92,7 @@ FontInspector.prototype = {
|
|||
isPanelVisible() {
|
||||
return this.inspector.sidebar &&
|
||||
this.inspector.sidebar.getCurrentTabID() === "fontinspector";
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Selection 'new-node' event handler.
|
||||
|
@ -102,7 +102,23 @@ FontInspector.prototype = {
|
|||
this.store.dispatch(updateShowAllFonts(false));
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for change in preview input.
|
||||
*/
|
||||
onPreviewFonts(value) {
|
||||
this.store.dispatch(updatePreviewText(value));
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for click on show all fonts button.
|
||||
*/
|
||||
onShowAllFont() {
|
||||
this.store.dispatch(updateShowAllFonts(true));
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the "theme-switched" event.
|
||||
|
@ -111,25 +127,9 @@ FontInspector.prototype = {
|
|||
if (frame === this.document.defaultView) {
|
||||
this.update();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for change in preview input.
|
||||
*/
|
||||
onPreviewFonts(value) {
|
||||
this.store.dispatch(updatePreviewText(value));
|
||||
this.update();
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for click on show all fonts button.
|
||||
*/
|
||||
onShowAllFont() {
|
||||
this.store.dispatch(updateShowAllFonts(true));
|
||||
this.update();
|
||||
},
|
||||
|
||||
update: Task.async(function* () {
|
||||
async update() {
|
||||
// Stop refreshing if the inspector or store is already destroyed.
|
||||
if (!this.inspector || !this.store) {
|
||||
return;
|
||||
|
@ -158,10 +158,10 @@ FontInspector.prototype = {
|
|||
};
|
||||
|
||||
if (showAllFonts) {
|
||||
fonts = yield this.pageStyle.getAllUsedFontFaces(options)
|
||||
fonts = await this.pageStyle.getAllUsedFontFaces(options)
|
||||
.catch(console.error);
|
||||
} else {
|
||||
fonts = yield this.pageStyle.getUsedFontFaces(node, options)
|
||||
fonts = await this.pageStyle.getUsedFontFaces(node, options)
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ FontInspector.prototype = {
|
|||
}
|
||||
|
||||
for (let font of fonts) {
|
||||
font.previewUrl = yield font.preview.data.string();
|
||||
font.previewUrl = await font.preview.data.string();
|
||||
}
|
||||
|
||||
// in case we've been destroyed in the meantime
|
||||
|
@ -183,7 +183,8 @@ FontInspector.prototype = {
|
|||
this.store.dispatch(updateFonts(fonts));
|
||||
|
||||
this.inspector.emit("fontinspector-updated");
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FontInspector;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const { Task } = require("devtools/shared/task");
|
||||
|
||||
const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
|
||||
const { throttle } = require("devtools/client/inspector/shared/utils");
|
||||
|
@ -44,45 +43,45 @@ const GRID_COLORS = [
|
|||
"#005A71"
|
||||
];
|
||||
|
||||
function GridInspector(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.highlighters = inspector.highlighters;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
this.telemetry = inspector.telemetry;
|
||||
this.walker = this.inspector.walker;
|
||||
class GridInspector {
|
||||
|
||||
this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
|
||||
this.updateGridPanel = this.updateGridPanel.bind(this);
|
||||
constructor(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.highlighters = inspector.highlighters;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
this.telemetry = inspector.telemetry;
|
||||
this.walker = this.inspector.walker;
|
||||
|
||||
this.onHighlighterChange = this.onHighlighterChange.bind(this);
|
||||
this.onNavigate = this.onNavigate.bind(this);
|
||||
this.onReflow = throttle(this.onReflow, 500, this);
|
||||
this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
|
||||
this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
|
||||
this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
|
||||
this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
|
||||
this.onSidebarSelect = this.onSidebarSelect.bind(this);
|
||||
this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
|
||||
this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
|
||||
this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
|
||||
this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
|
||||
this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
|
||||
this.updateGridPanel = this.updateGridPanel.bind(this);
|
||||
|
||||
this.init();
|
||||
}
|
||||
this.onHighlighterChange = this.onHighlighterChange.bind(this);
|
||||
this.onNavigate = this.onNavigate.bind(this);
|
||||
this.onReflow = throttle(this.onReflow, 500, this);
|
||||
this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
|
||||
this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
|
||||
this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
|
||||
this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
|
||||
this.onSidebarSelect = this.onSidebarSelect.bind(this);
|
||||
this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
|
||||
this.onToggleShowGridAreas = this.onToggleShowGridAreas.bind(this);
|
||||
this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
|
||||
this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
|
||||
|
||||
GridInspector.prototype = {
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the grid inspector by fetching the LayoutFront from the walker, loading
|
||||
* the highlighter settings and initalizing the SwatchColorPicker instance.
|
||||
*/
|
||||
init: Task.async(function* () {
|
||||
async init() {
|
||||
if (!this.inspector) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
|
||||
this.layoutInspector = await this.inspector.walker.getLayoutInspector();
|
||||
|
||||
this.loadHighlighterSettings();
|
||||
|
||||
|
@ -101,7 +100,7 @@ GridInspector.prototype = {
|
|||
this.inspector.on("new-root", this.onNavigate);
|
||||
|
||||
this.onSidebarSelect();
|
||||
}),
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruction function called when the inspector is destroyed. Removes event listeners
|
||||
|
@ -128,7 +127,7 @@ GridInspector.prototype = {
|
|||
this.store = null;
|
||||
this.swatchColorPickerTooltip = null;
|
||||
this.walker = null;
|
||||
},
|
||||
}
|
||||
|
||||
getComponentProps() {
|
||||
return {
|
||||
|
@ -142,7 +141,7 @@ GridInspector.prototype = {
|
|||
onToggleShowGridLineNumbers: this.onToggleShowGridLineNumbers,
|
||||
onToggleShowInfiniteLines: this.onToggleShowInfiniteLines,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initial color linked to a grid container. Will attempt to check the
|
||||
|
@ -169,7 +168,7 @@ GridInspector.prototype = {
|
|||
}
|
||||
|
||||
return color || fallbackColor;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color set for the grid highlighter associated with the provided
|
||||
|
@ -188,7 +187,7 @@ GridInspector.prototype = {
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a highlighter settings object for the provided nodeFront.
|
||||
|
@ -206,14 +205,14 @@ GridInspector.prototype = {
|
|||
return Object.assign({}, highlighterSettings, {
|
||||
color
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the shared SwatchColorPicker instance.
|
||||
*/
|
||||
getSwatchColorPickerTooltip() {
|
||||
return this.swatchColorPickerTooltip;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of new grid fronts, and if we have a currently highlighted grid, check
|
||||
|
@ -239,7 +238,7 @@ GridInspector.prototype = {
|
|||
const newFragments = newGridFront.gridFragments;
|
||||
|
||||
return !compareFragmentsGeometry(oldFragments, newFragments);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the layout panel is visible, and false otherwise.
|
||||
|
@ -248,7 +247,7 @@ GridInspector.prototype = {
|
|||
return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
|
||||
this.inspector.toolbox.currentToolId === "inspector" &&
|
||||
this.inspector.sidebar.getCurrentTabID() === "layoutview";
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the grid highligher display settings into the store from the stored preferences.
|
||||
|
@ -263,7 +262,7 @@ GridInspector.prototype = {
|
|||
dispatch(updateShowGridAreas(showGridAreas));
|
||||
dispatch(updateShowGridLineNumbers(showGridLineNumbers));
|
||||
dispatch(updateShowInfiniteLines(showInfinteLines));
|
||||
},
|
||||
}
|
||||
|
||||
showGridHighlighter(node, settings) {
|
||||
this.lastHighlighterColor = settings.color;
|
||||
|
@ -271,7 +270,7 @@ GridInspector.prototype = {
|
|||
this.lastHighlighterState = true;
|
||||
|
||||
this.highlighters.showGridHighlighter(node, settings);
|
||||
},
|
||||
}
|
||||
|
||||
toggleGridHighlighter(node, settings) {
|
||||
this.lastHighlighterColor = settings.color;
|
||||
|
@ -279,13 +278,13 @@ GridInspector.prototype = {
|
|||
this.lastHighlighterState = node !== this.highlighters.gridHighlighterShown;
|
||||
|
||||
this.highlighters.toggleGridHighlighter(node, settings, "grid");
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the grid panel by dispatching the new grid data. This is called when the
|
||||
* layout view becomes visible or the view needs to be updated with new grid data.
|
||||
*/
|
||||
updateGridPanel: Task.async(function* () {
|
||||
async updateGridPanel() {
|
||||
// Stop refreshing if the inspector or store is already destroyed.
|
||||
if (!this.inspector || !this.store) {
|
||||
return;
|
||||
|
@ -294,7 +293,7 @@ GridInspector.prototype = {
|
|||
// Get all the GridFront from the server if no gridFronts were provided.
|
||||
let gridFronts;
|
||||
try {
|
||||
gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
|
||||
gridFronts = await this.layoutInspector.getAllGrids(this.walker.rootNode);
|
||||
} catch (e) {
|
||||
// This call might fail if called asynchrously after the toolbox is finished
|
||||
// closing.
|
||||
|
@ -319,7 +318,7 @@ GridInspector.prototype = {
|
|||
// particular DOM Node in the tree yet, or when we are connected to an older server.
|
||||
if (!nodeFront) {
|
||||
try {
|
||||
nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
|
||||
nodeFront = await this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
|
||||
} catch (e) {
|
||||
// This call might fail if called asynchrously after the toolbox is finished
|
||||
// closing.
|
||||
|
@ -341,7 +340,7 @@ GridInspector.prototype = {
|
|||
}
|
||||
|
||||
this.store.dispatch(updateGrids(grids));
|
||||
}),
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
|
||||
|
@ -374,7 +373,7 @@ GridInspector.prototype = {
|
|||
this.lastHighlighterColor = null;
|
||||
this.lastHighlighterNode = null;
|
||||
this.lastHighlighterState = null;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for "new-root" event fired by the inspector, which indicates a page
|
||||
|
@ -384,7 +383,7 @@ GridInspector.prototype = {
|
|||
if (this.isPanelVisible()) {
|
||||
this.updateGridPanel();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
|
||||
|
@ -399,7 +398,7 @@ GridInspector.prototype = {
|
|||
* after the reflow, as well as the grid fragment data on the currently highlighted
|
||||
* grid.
|
||||
*/
|
||||
onReflow: Task.async(function* () {
|
||||
async onReflow() {
|
||||
if (!this.isPanelVisible()) {
|
||||
return;
|
||||
}
|
||||
|
@ -410,7 +409,7 @@ GridInspector.prototype = {
|
|||
// The new list of grids from the server.
|
||||
let newGridFronts;
|
||||
try {
|
||||
newGridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
|
||||
newGridFronts = await this.layoutInspector.getAllGrids(this.walker.rootNode);
|
||||
} catch (e) {
|
||||
// This call might fail if called asynchrously after the toolbox is finished
|
||||
// closing.
|
||||
|
@ -444,7 +443,7 @@ GridInspector.prototype = {
|
|||
|
||||
// Either the list of containers or the current fragments have changed, do update.
|
||||
this.updateGridPanel(newGridFronts);
|
||||
}),
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for a change in the grid overlay color picker for a grid container.
|
||||
|
@ -467,7 +466,7 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(node, highlighterSettings);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the grid area in the CSS Grid Highlighter for the given grid.
|
||||
|
@ -491,7 +490,7 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(node, highlighterSettings);
|
||||
|
||||
this.store.dispatch(updateGridHighlighted(node, true));
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the grid cell in the CSS Grid Highlighter for the given grid.
|
||||
|
@ -521,7 +520,7 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(node, highlighterSettings);
|
||||
|
||||
this.store.dispatch(updateGridHighlighted(node, true));
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the grid line in the CSS Grid Highlighter for the given grid.
|
||||
|
@ -554,7 +553,7 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(node, highlighterSettings);
|
||||
|
||||
this.store.dispatch(updateGridHighlighted(node, true));
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the inspector sidebar "select" event. Starts tracking reflows
|
||||
|
@ -569,7 +568,7 @@ GridInspector.prototype = {
|
|||
|
||||
this.inspector.reflowTracker.trackReflows(this, this.onReflow);
|
||||
this.updateGridPanel();
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for a change in the input checkboxes in the GridList component.
|
||||
|
@ -585,7 +584,7 @@ GridInspector.prototype = {
|
|||
|
||||
this.store.dispatch(updateGridHighlighted(node,
|
||||
node !== this.highlighters.gridHighlighterShown));
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for a change in the show grid areas checkbox in the GridDisplaySettings
|
||||
|
@ -611,7 +610,7 @@ GridInspector.prototype = {
|
|||
this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for a change in the show grid line numbers checkbox in the
|
||||
|
@ -638,7 +637,7 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(grid.nodeFront, highlighterSettings);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for a change in the extend grid lines infinitely checkbox in the
|
||||
|
@ -665,8 +664,8 @@ GridInspector.prototype = {
|
|||
this.showGridHighlighter(grid.nodeFront, highlighterSettings);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = GridInspector;
|
||||
|
|
|
@ -646,11 +646,64 @@ Inspector.prototype = {
|
|||
this.browserRequire("devtools/client/inspector/layout/layout");
|
||||
this.layoutview = new LayoutView(this, this.panelWin);
|
||||
}
|
||||
|
||||
return this.layoutview.provider;
|
||||
}
|
||||
},
|
||||
defaultTab == layoutId);
|
||||
|
||||
if (Services.prefs.getBoolPref("devtools.changesview.enabled")) {
|
||||
// Inject a lazy loaded react tab by exposing a fake React object
|
||||
// with a lazy defined Tab thanks to `panel` being a function
|
||||
let changesId = "changesview";
|
||||
let changesTitle = INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle");
|
||||
this.sidebar.addTab(
|
||||
changesId,
|
||||
changesTitle,
|
||||
{
|
||||
props: {
|
||||
id: changesId,
|
||||
title: changesTitle
|
||||
},
|
||||
panel: () => {
|
||||
if (!this.changesview) {
|
||||
const ChangesView =
|
||||
this.browserRequire("devtools/client/inspector/changes/changes");
|
||||
this.changesview = new ChangesView(this, this.panelWin);
|
||||
}
|
||||
|
||||
return this.changesview.provider;
|
||||
}
|
||||
},
|
||||
defaultTab == changesId);
|
||||
}
|
||||
|
||||
if (Services.prefs.getBoolPref("devtools.eventsview.enabled")) {
|
||||
// Inject a lazy loaded react tab by exposing a fake React object
|
||||
// with a lazy defined Tab thanks to `panel` being a function
|
||||
let eventsId = "eventsview";
|
||||
let eventsTitle = INSPECTOR_L10N.getStr("inspector.sidebar.eventsViewTitle");
|
||||
this.sidebar.addTab(
|
||||
eventsId,
|
||||
eventsTitle,
|
||||
{
|
||||
props: {
|
||||
id: eventsId,
|
||||
title: eventsTitle
|
||||
},
|
||||
panel: () => {
|
||||
if (!this.eventview) {
|
||||
const EventsView =
|
||||
this.browserRequire("devtools/client/inspector/events/events");
|
||||
this.eventsview = new EventsView(this, this.panelWin);
|
||||
}
|
||||
|
||||
return this.eventsview.provider;
|
||||
}
|
||||
},
|
||||
defaultTab == eventsId);
|
||||
}
|
||||
|
||||
if (this.target.form.animationsActor) {
|
||||
this.sidebar.addFrameTab(
|
||||
"animationinspector",
|
||||
|
@ -678,6 +731,7 @@ Inspector.prototype = {
|
|||
this.browserRequire("devtools/client/inspector/fonts/fonts");
|
||||
this.fontinspector = new FontInspector(this, this.panelWin);
|
||||
}
|
||||
|
||||
return this.fontinspector.provider;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -35,7 +35,7 @@ const BOXMODEL_OPENED_PREF = "devtools.layout.boxmodel.opened";
|
|||
const FLEXBOX_OPENED_PREF = "devtools.layout.flexbox.opened";
|
||||
const GRID_OPENED_PREF = "devtools.layout.grid.opened";
|
||||
|
||||
class App extends PureComponent {
|
||||
class LayoutApp extends PureComponent {
|
||||
static get propTypes() {
|
||||
return {
|
||||
boxModel: PropTypes.shape(BoxModelTypes.boxModel).isRequired,
|
||||
|
@ -102,4 +102,4 @@ class App extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports = connect(state => state)(App);
|
||||
module.exports = connect(state => state)(LayoutApp);
|
|
@ -7,5 +7,5 @@
|
|||
DevToolsModules(
|
||||
'Accordion.css',
|
||||
'Accordion.js',
|
||||
'App.js',
|
||||
'LayoutApp.js',
|
||||
)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
|
||||
const { Provider } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const App = createFactory(require("./components/App"));
|
||||
const LayoutApp = createFactory(require("./components/LayoutApp"));
|
||||
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
|
@ -16,15 +16,15 @@ const INSPECTOR_L10N =
|
|||
loader.lazyRequireGetter(this, "FlexboxInspector", "devtools/client/inspector/flexbox/flexbox");
|
||||
loader.lazyRequireGetter(this, "GridInspector", "devtools/client/inspector/grids/grid-inspector");
|
||||
|
||||
function LayoutView(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
class LayoutView {
|
||||
|
||||
this.init();
|
||||
}
|
||||
constructor(inspector, window) {
|
||||
this.document = window.document;
|
||||
this.inspector = inspector;
|
||||
this.store = inspector.store;
|
||||
|
||||
LayoutView.prototype = {
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.inspector) {
|
||||
|
@ -56,7 +56,7 @@ LayoutView.prototype = {
|
|||
onToggleShowInfiniteLines,
|
||||
} = this.gridInspector.getComponentProps();
|
||||
|
||||
let app = App({
|
||||
let layoutApp = LayoutApp({
|
||||
getSwatchColorPickerTooltip,
|
||||
setSelectedNode,
|
||||
/**
|
||||
|
@ -84,11 +84,11 @@ LayoutView.prototype = {
|
|||
key: "layoutview",
|
||||
store: this.store,
|
||||
title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
|
||||
}, app);
|
||||
}, layoutApp);
|
||||
|
||||
// Expose the provider to let inspector.js use it in setupSidebar.
|
||||
this.provider = provider;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Destruction function called when the inspector is destroyed. Cleans up references.
|
||||
|
@ -99,8 +99,8 @@ LayoutView.prototype = {
|
|||
this.document = null;
|
||||
this.inspector = null;
|
||||
this.store = null;
|
||||
},
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = LayoutView;
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
DIRS += [
|
||||
'boxmodel',
|
||||
'changes',
|
||||
'components',
|
||||
'computed',
|
||||
'events',
|
||||
'extensions',
|
||||
'flexbox',
|
||||
'fonts',
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// settings.
|
||||
|
||||
exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
|
||||
exports.changes = require("devtools/client/inspector/changes/reducers/changes");
|
||||
exports.events = require("devtools/client/inspector/events/reducers/events");
|
||||
exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
|
||||
exports.flexboxes = require("devtools/client/inspector/flexbox/reducers/flexboxes");
|
||||
exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
|
||||
|
|
|
@ -7,11 +7,13 @@ support-files =
|
|||
csp_json.json
|
||||
csp_json.json^headers^
|
||||
doc_frame_script.js
|
||||
empty.html
|
||||
head.js
|
||||
invalid_json.json
|
||||
invalid_json.json^headers^
|
||||
manifest_json.json
|
||||
manifest_json.json^headers^
|
||||
passthrough-sw.js
|
||||
simple_json.json
|
||||
simple_json.json^headers^
|
||||
valid_json.json
|
||||
|
@ -48,3 +50,4 @@ support-files =
|
|||
[browser_jsonview_slash.js]
|
||||
[browser_jsonview_valid_json.js]
|
||||
[browser_json_refresh.js]
|
||||
[browser_jsonview_serviceworker.js]
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_JSON_URL = URL_ROOT + "valid_json.json";
|
||||
const EMPTY_PAGE = URL_ROOT + "empty.html";
|
||||
const SW = URL_ROOT + "passthrough-sw.js";
|
||||
|
||||
add_task(async function () {
|
||||
info("Test valid JSON with service worker started");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
]});
|
||||
|
||||
let swTab = BrowserTestUtils.addTab(gBrowser, EMPTY_PAGE);
|
||||
let browser = gBrowser.getBrowserForTab(swTab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
await ContentTask.spawn(browser, { script: SW, scope: TEST_JSON_URL }, async opts => {
|
||||
let reg = await content.navigator.serviceWorker.register(opts.script,
|
||||
{ scope: opts.scope });
|
||||
return new content.window.Promise(resolve => {
|
||||
let worker = reg.installing;
|
||||
if (worker.state === "activated") {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
worker.addEventListener("statechange", evt => {
|
||||
if (worker.state === "activated") {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let tab = await addJsonViewTab(TEST_JSON_URL);
|
||||
|
||||
ok(tab.linkedBrowser.contentPrincipal.isNullPrincipal, "Should have null principal");
|
||||
|
||||
is(await countRows(), 3, "There must be three rows");
|
||||
|
||||
let objectCellCount = await getElementCount(
|
||||
".jsonPanelBox .treeTable .objectCell");
|
||||
is(objectCellCount, 1, "There must be one object cell");
|
||||
|
||||
let objectCellText = await getElementText(
|
||||
".jsonPanelBox .treeTable .objectCell");
|
||||
is(objectCellText, "", "The summary is hidden when object is expanded");
|
||||
|
||||
// Clicking the value does not collapse it (so that it can be selected and copied).
|
||||
await clickJsonNode(".jsonPanelBox .treeTable .treeValueCell");
|
||||
is(await countRows(), 3, "There must still be three rows");
|
||||
|
||||
// Clicking the label collapses the auto-expanded node.
|
||||
await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
|
||||
is(await countRows(), 1, "There must be one row");
|
||||
|
||||
await ContentTask.spawn(browser, { script: SW, scope: TEST_JSON_URL }, async opts => {
|
||||
let reg = await content.navigator.serviceWorker.getRegistration(opts.scope);
|
||||
await reg.unregister();
|
||||
});
|
||||
|
||||
await BrowserTestUtils.removeTab(swTab);
|
||||
});
|
||||
|
||||
function countRows() {
|
||||
return getElementCount(".jsonPanelBox .treeTable .treeRow");
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<!doctype html>
|
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
addEventListener("fetch", evt => {
|
||||
evt.respondWith(fetch(evt.request));
|
||||
});
|
|
@ -349,6 +349,16 @@ inspector.sidebar.layoutViewTitle2=Layout
|
|||
# name. Used to promote new/recent panels such as the layout panel.
|
||||
inspector.sidebar.newBadge=new!
|
||||
|
||||
# LOCALIZATION NOTE (inspector.sidebar.changesViewTitle):
|
||||
# This is the title shown in a tab in the side panel of the Inspector panel
|
||||
# that corresponds to the tool displaying CSS changes.
|
||||
inspector.sidebar.changesViewTitle=Changes
|
||||
|
||||
# LOCALIZATION NOTE (inspector.sidebar.eventsViewTitle):
|
||||
# This is the title shown in a tab in the side panel of the Inspector panel
|
||||
# that corresponds to the tool displaying the list of event listeners used in the page.
|
||||
inspector.sidebar.eventsViewTitle=Events
|
||||
|
||||
# LOCALIZATION NOTE (inspector.sidebar.animationInspectorTitle):
|
||||
# This is the title shown in a tab in the side panel of the Inspector panel
|
||||
# that corresponds to the tool displaying animations defined in the page.
|
||||
|
|
|
@ -60,7 +60,10 @@ pref("devtools.inspector.showAllAnonymousContent", false);
|
|||
pref("devtools.inspector.colorWidget.enabled", false);
|
||||
// Enable the CSS shapes highlighter
|
||||
pref("devtools.inspector.shapesHighlighter.enabled", true);
|
||||
|
||||
// Enable the Changes View
|
||||
pref("devtools.changesview.enabled", false);
|
||||
// Enable the Events View
|
||||
pref("devtools.eventsview.enabled", false);
|
||||
// Enable the Flexbox Inspector
|
||||
pref("devtools.flexboxinspector.enabled", false);
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@ const { AppConstants } = devtools.require("resource://gre/modules/AppConstants.j
|
|||
|
||||
const BROWSER_BASED_DIRS = [
|
||||
"resource://devtools/client/inspector/boxmodel",
|
||||
"resource://devtools/client/inspector/changes",
|
||||
"resource://devtools/client/inspector/computed",
|
||||
"resource://devtools/client/inspector/events",
|
||||
"resource://devtools/client/inspector/flexbox",
|
||||
"resource://devtools/client/inspector/fonts",
|
||||
"resource://devtools/client/inspector/grids",
|
||||
|
|
|
@ -1051,8 +1051,12 @@ CustomElementReactionsStack::PopAndInvokeElementQueue()
|
|||
// element, see https://github.com/w3c/webcomponents/issues/635.
|
||||
// We usually report the error to entry global in gecko, so just follow the
|
||||
// same behavior here.
|
||||
// This may be null if it's called from parser, see the case of
|
||||
// attributeChangedCallback in
|
||||
// https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
|
||||
// In that case, the exception of callback reactions will be automatically
|
||||
// reported in CallSetup.
|
||||
nsIGlobalObject* global = GetEntryGlobal();
|
||||
MOZ_ASSERT(global, "Should always have a entry global here!");
|
||||
InvokeReactions(elementQueue, global);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace mozilla {
|
|||
namespace dom {
|
||||
class Element;
|
||||
class NodeInfo;
|
||||
struct CustomElementDefinition;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -41,7 +42,8 @@ nsresult
|
|||
NS_NewHTMLElement(mozilla::dom::Element** aResult,
|
||||
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser,
|
||||
const nsAString* aIs = nullptr);
|
||||
const nsAString* aIs = nullptr,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
|
||||
|
||||
// First argument should be nsHTMLTag, but that adds dependency to parser
|
||||
// for a bunch of files.
|
||||
|
|
|
@ -1596,7 +1596,8 @@ nsIDocument::nsIDocument()
|
|||
mNotifiedPageForUseCounter(0),
|
||||
mIncCounters(),
|
||||
mUserHasInteracted(false),
|
||||
mServoRestyleRootDirtyBits(0)
|
||||
mServoRestyleRootDirtyBits(0),
|
||||
mThrowOnDynamicMarkupInsertionCounter(0)
|
||||
{
|
||||
SetIsInDocument();
|
||||
for (auto& cnt : mIncCounters) {
|
||||
|
@ -2500,7 +2501,7 @@ nsDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
|
|||
|
||||
MOZ_ASSERT(expanded->WhiteList().Length() > 0);
|
||||
|
||||
return do_AddRef(expanded->WhiteList().LastElement().get());
|
||||
return do_AddRef(expanded->WhiteList().LastElement());
|
||||
}
|
||||
|
||||
if (!sChromeInContentPrefCached) {
|
||||
|
|
|
@ -3195,6 +3195,22 @@ public:
|
|||
|
||||
inline void SetServoRestyleRoot(nsINode* aRoot, uint32_t aDirtyBits);
|
||||
|
||||
bool ShouldThrowOnDynamicMarkupInsertion()
|
||||
{
|
||||
return mThrowOnDynamicMarkupInsertionCounter;
|
||||
}
|
||||
|
||||
void IncrementThrowOnDynamicMarkupInsertionCounter()
|
||||
{
|
||||
++mThrowOnDynamicMarkupInsertionCounter;
|
||||
}
|
||||
|
||||
void DecrementThrowOnDynamicMarkupInsertionCounter()
|
||||
{
|
||||
MOZ_ASSERT(mThrowOnDynamicMarkupInsertionCounter);
|
||||
--mThrowOnDynamicMarkupInsertionCounter;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool GetUseCounter(mozilla::UseCounter aUseCounter)
|
||||
{
|
||||
|
@ -3736,6 +3752,11 @@ protected:
|
|||
// root corresponds to.
|
||||
nsCOMPtr<nsINode> mServoRestyleRoot;
|
||||
uint32_t mServoRestyleRootDirtyBits;
|
||||
|
||||
// Used in conjunction with the create-an-element-for-the-token algorithm to
|
||||
// prevent custom element constructors from being able to use document.open(),
|
||||
// document.close(), and document.write() when they are invoked by the parser.
|
||||
uint32_t mThrowOnDynamicMarkupInsertionCounter;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
|
||||
|
@ -3792,6 +3813,23 @@ private:
|
|||
uint32_t mMicroTaskLevel;
|
||||
};
|
||||
|
||||
class MOZ_RAII AutoSetThrowOnDynamicMarkupInsertionCounter final {
|
||||
public:
|
||||
explicit AutoSetThrowOnDynamicMarkupInsertionCounter(
|
||||
nsIDocument* aDocument)
|
||||
: mDocument(aDocument)
|
||||
{
|
||||
mDocument->IncrementThrowOnDynamicMarkupInsertionCounter();
|
||||
}
|
||||
|
||||
~AutoSetThrowOnDynamicMarkupInsertionCounter() {
|
||||
mDocument->DecrementThrowOnDynamicMarkupInsertionCounter();
|
||||
}
|
||||
|
||||
private:
|
||||
nsIDocument* mDocument;
|
||||
};
|
||||
|
||||
// XXX These belong somewhere else
|
||||
nsresult
|
||||
NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData = false);
|
||||
|
|
|
@ -702,10 +702,10 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
|||
break;
|
||||
}
|
||||
|
||||
const gfx::IntSize destSize(mWidth, mHeight);
|
||||
const gfx::IntSize dstSize(mWidth, mHeight);
|
||||
const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
|
||||
: gl::OriginPos::BottomLeft);
|
||||
if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, dstOrigin)) {
|
||||
if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, dstSize, dstOrigin)) {
|
||||
fallbackReason = "likely bug: failed to blit";
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -94,11 +94,6 @@ HTMLBodyElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
|||
int32_t bodyLeftMargin = -1;
|
||||
int32_t bodyRightMargin = -1;
|
||||
|
||||
// check the mode (fortunately, the GenericSpecifiedValues has a presContext for us to use!)
|
||||
NS_ASSERTION(aData->mPresContext, "null presContext in MapAttributesIntoRule was unexpected");
|
||||
nsCompatibility mode = aData->mPresContext->CompatibilityMode();
|
||||
|
||||
|
||||
const nsAttrValue* value;
|
||||
// if marginwidth/marginheight are set, reflect them as 'margin'
|
||||
value = aAttributes->GetAttr(nsGkAtoms::marginwidth);
|
||||
|
@ -177,20 +172,6 @@ HTMLBodyElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
|||
nscoord frameMarginHeight=-1; // default value
|
||||
docShell->GetMarginWidth(&frameMarginWidth); // -1 indicates not set
|
||||
docShell->GetMarginHeight(&frameMarginHeight);
|
||||
if (frameMarginWidth >= 0 && bodyMarginWidth == -1) { // set in <frame> & not in <body>
|
||||
if (eCompatibility_NavQuirks == mode) {
|
||||
if (bodyMarginHeight == -1 && 0 > frameMarginHeight) { // nav quirk
|
||||
frameMarginHeight = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (frameMarginHeight >= 0 && bodyMarginHeight == -1) { // set in <frame> & not in <body>
|
||||
if (eCompatibility_NavQuirks == mode) {
|
||||
if (bodyMarginWidth == -1 && 0 > frameMarginWidth) { // nav quirk
|
||||
frameMarginWidth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bodyMarginWidth == -1 && frameMarginWidth >= 0) {
|
||||
if (bodyLeftMargin == -1) {
|
||||
|
|
|
@ -251,7 +251,8 @@ DoCustomElementCreate(Element** aElement, nsIDocument* aDoc, nsAtom* aLocalName,
|
|||
|
||||
nsresult
|
||||
NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
|
||||
FromParser aFromParser, const nsAString* aIs)
|
||||
FromParser aFromParser, const nsAString* aIs,
|
||||
mozilla::dom::CustomElementDefinition* aDefinition)
|
||||
{
|
||||
*aResult = nullptr;
|
||||
|
||||
|
@ -268,8 +269,8 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
|||
// We only handle the "synchronous custom elements flag is set" now.
|
||||
// For the unset case (e.g. cloning a node), see bug 1319342 for that.
|
||||
// Step 4.
|
||||
CustomElementDefinition* definition = nullptr;
|
||||
if (CustomElementRegistry::IsCustomElementEnabled()) {
|
||||
CustomElementDefinition* definition = aDefinition;
|
||||
if (!definition && CustomElementRegistry::IsCustomElementEnabled()) {
|
||||
definition =
|
||||
nsContentUtils::LookupCustomElementDefinition(nodeInfo->GetDocument(),
|
||||
nodeInfo->LocalName(),
|
||||
|
@ -294,9 +295,20 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
|||
bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT ||
|
||||
aFromParser == dom::NOT_FROM_PARSER;
|
||||
// Per discussion in https://github.com/w3c/webcomponents/issues/635,
|
||||
// use entry global in those places that are called from JS APIs.
|
||||
nsIGlobalObject* global = GetEntryGlobal();
|
||||
MOZ_ASSERT(global);
|
||||
// use entry global in those places that are called from JS APIs and use the
|
||||
// node document's global object if it is called from parser.
|
||||
nsIGlobalObject* global;
|
||||
if (aFromParser == dom::NOT_FROM_PARSER) {
|
||||
global = GetEntryGlobal();
|
||||
} else {
|
||||
global = nodeInfo->GetDocument()->GetScopeObject();
|
||||
}
|
||||
if (!global) {
|
||||
// In browser chrome code, one may have access to a document which doesn't
|
||||
// have scope object anymore.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
AutoEntryScript aes(global, "create custom elements");
|
||||
JSContext* cx = aes.cx();
|
||||
ErrorResult rv;
|
||||
|
@ -336,6 +348,7 @@ NS_NewHTMLElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&&
|
|||
|
||||
// Step 6.2.
|
||||
NS_IF_ADDREF(*aResult = NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
|
||||
(*aResult)->SetCustomElementData(new CustomElementData(definition->mType));
|
||||
nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -1502,6 +1502,11 @@ nsHTMLDocument::Open(JSContext* cx,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (ShouldThrowOnDynamicMarkupInsertion()) {
|
||||
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoCString contentType;
|
||||
contentType.AssignLiteral("text/html");
|
||||
|
||||
|
@ -1841,6 +1846,11 @@ nsHTMLDocument::Close(ErrorResult& rv)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ShouldThrowOnDynamicMarkupInsertion()) {
|
||||
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mParser || !mParser->IsScriptCreated()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1925,6 +1935,11 @@ nsHTMLDocument::WriteCommon(JSContext *cx,
|
|||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
|
||||
if (ShouldThrowOnDynamicMarkupInsertion()) {
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
}
|
||||
|
||||
if (mParserAborted) {
|
||||
// Hixie says aborting the parser doesn't undefine the insertion point.
|
||||
// However, since we null out mParser in that case, we track the
|
||||
|
|
|
@ -18602,6 +18602,7 @@ Maintenance::DirectoryWork()
|
|||
continue;
|
||||
}
|
||||
|
||||
nsCString suffix;
|
||||
nsCString group;
|
||||
nsCString origin;
|
||||
nsTArray<nsString> databasePaths;
|
||||
|
@ -18659,17 +18660,17 @@ Maintenance::DirectoryWork()
|
|||
|
||||
// Found a database.
|
||||
if (databasePaths.IsEmpty()) {
|
||||
MOZ_ASSERT(suffix.IsEmpty());
|
||||
MOZ_ASSERT(group.IsEmpty());
|
||||
MOZ_ASSERT(origin.IsEmpty());
|
||||
|
||||
int64_t dummyTimeStamp;
|
||||
bool dummyPersisted;
|
||||
nsCString dummySuffix;
|
||||
if (NS_WARN_IF(NS_FAILED(
|
||||
quotaManager->GetDirectoryMetadata2(originDir,
|
||||
&dummyTimeStamp,
|
||||
&dummyPersisted,
|
||||
dummySuffix,
|
||||
suffix,
|
||||
group,
|
||||
origin)))) {
|
||||
// Not much we can do here...
|
||||
|
@ -18687,6 +18688,21 @@ Maintenance::DirectoryWork()
|
|||
group,
|
||||
origin,
|
||||
Move(databasePaths)));
|
||||
|
||||
nsCOMPtr<nsIFile> directory;
|
||||
|
||||
// Idle maintenance may occur before origin is initailized.
|
||||
// Ensure origin is initialized first. It will initialize all origins
|
||||
// for temporary storage including IDB origins.
|
||||
rv = quotaManager->EnsureOriginIsInitialized(persistenceType,
|
||||
suffix,
|
||||
group,
|
||||
origin,
|
||||
getter_AddRefs(directory));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
|
||||
function boom()
|
||||
{
|
||||
var a = new AudioContext();
|
||||
var b = new BroadcastChannel("x");
|
||||
a.addEventListener("statechange", bye, false);
|
||||
}
|
||||
|
||||
function bye()
|
||||
{
|
||||
location = "data:text/html,2";
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
|
||||
function zombieWindow()
|
||||
{
|
||||
var frame = document.createElement("iframe");
|
||||
document.body.appendChild(frame);
|
||||
var frameWin = frame.contentWindow;
|
||||
frame.remove();
|
||||
return frameWin;
|
||||
}
|
||||
|
||||
function boom() {
|
||||
zombieWindow().navigator.mozGetUserMedia({ "fake": true, "audio": true }, function(stream) {}, function(e) {});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="boom();"></body>
|
||||
</html>
|
|
@ -14,6 +14,8 @@ load 855796.html
|
|||
load 860143.html
|
||||
load 861958.html
|
||||
load 863929.html
|
||||
load 1185191.html
|
||||
load 1281695.html
|
||||
load 1306476.html
|
||||
load 1348381.html
|
||||
load 1367930_1.html
|
||||
|
|
|
@ -10,6 +10,7 @@ support-files =
|
|||
[test_content_element.html]
|
||||
[test_custom_element_adopt_callbacks.html]
|
||||
[test_custom_element_callback_innerhtml.html]
|
||||
skip-if = true # disabled - See bug 1390396
|
||||
[test_custom_element_clone_callbacks_extended.html]
|
||||
[test_custom_element_htmlconstructor.html]
|
||||
skip-if = os == 'android' # bug 1323645
|
||||
|
@ -20,6 +21,7 @@ support-files =
|
|||
[test_custom_element_in_shadow.html]
|
||||
skip-if = true # disabled - See bug 1390396
|
||||
[test_custom_element_register_invalid_callbacks.html]
|
||||
[test_custom_element_throw_on_dynamic_markup_insertion.html]
|
||||
[test_custom_element_get.html]
|
||||
[test_custom_element_when_defined.html]
|
||||
[test_custom_element_upgrade.html]
|
||||
|
@ -34,7 +36,6 @@ support-files =
|
|||
[test_document_adoptnode.html]
|
||||
[test_document_importnode.html]
|
||||
[test_document_register.html]
|
||||
[test_document_register_base_queue.html]
|
||||
[test_document_register_lifecycle.html]
|
||||
skip-if = true # disabled - See bug 1390396
|
||||
[test_document_register_parser.html]
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1378079
|
||||
-->
|
||||
<head>
|
||||
<title>Test throw on dynamic markup insertion when creating element synchronously from parser</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1378079">Bug 1378079</a>
|
||||
<div id="container"></div>
|
||||
|
||||
<script>
|
||||
|
||||
class DoDocumentOpenInCtor extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
document.open();
|
||||
}
|
||||
};
|
||||
customElements.define('x-document-open-in-ctor', DoDocumentOpenInCtor);
|
||||
|
||||
class DoDocumentWriteInCtor extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
document.write('<div>This should not be shown</div>');
|
||||
}
|
||||
};
|
||||
customElements.define('x-document-write-in-ctor', DoDocumentWriteInCtor);
|
||||
|
||||
class DoDocumentCloseInCtor extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
document.close();
|
||||
}
|
||||
};
|
||||
customElements.define('x-document-close-in-ctor', DoDocumentCloseInCtor);
|
||||
|
||||
window.errors = [];
|
||||
window.onerror = function(message, url, lineNumber, columnNumber, error) {
|
||||
errors.push(error.name);
|
||||
return true;
|
||||
}
|
||||
var expectedErrorCount = 0;
|
||||
|
||||
document.write("<x-document-open-in-ctor></x-document-open-in-ctor>");
|
||||
expectedErrorCount++;
|
||||
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
|
||||
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
|
||||
|
||||
document.write("<x-document-write-in-ctor></x-document-write-in-ctor>");
|
||||
expectedErrorCount++;
|
||||
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
|
||||
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
|
||||
|
||||
document.write("<x-document-close-in-ctor></x-document-close-in-ctor>");
|
||||
expectedErrorCount++;
|
||||
is(window.errors.length, expectedErrorCount, "expectedErrorCount should be " + expectedErrorCount);
|
||||
is(window.errors[expectedErrorCount - 1], 'InvalidStateError', "Error name should be 'InvalidStateError'");
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=783129
|
||||
-->
|
||||
<head>
|
||||
<title>Test for document.registerElement lifecycle callback</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script>
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
|
||||
var createdCallbackCallCount = 0;
|
||||
|
||||
// By the time the base element queue is processed via the microtask,
|
||||
// both x-hello elements should be in the document.
|
||||
p.createdCallback = function() {
|
||||
var one = document.getElementById("one");
|
||||
var two = document.getElementById("two");
|
||||
isnot(one, null, "First x-hello element should be in the tree.");
|
||||
isnot(two, null, "Second x-hello element should be in the tree.");
|
||||
createdCallbackCallCount++;
|
||||
if (createdCallbackCallCount == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
if (createdCallbackCallCount > 2) {
|
||||
ok(false, "Created callback called too much.");
|
||||
}
|
||||
};
|
||||
|
||||
p.attributeChangedCallback = function(name, oldValue, newValue) {
|
||||
ok(false, "Attribute changed callback should not be called because callbacks should not be queued until created callback invoked.");
|
||||
};
|
||||
|
||||
document.registerElement("x-hello", { prototype: p });
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
|
||||
<x-hello id="one"></x-hello>
|
||||
<x-hello id="two"></x-hello>
|
||||
<script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -11,7 +11,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=783129
|
|||
|
||||
var extendedButtonProto = Object.create(HTMLButtonElement.prototype);
|
||||
var buttonCallbackCalled = false;
|
||||
extendedButtonProto.createdCallback = function() {
|
||||
extendedButtonProto.connectedCallback = function() {
|
||||
is(buttonCallbackCalled, false, "created callback for x-button should only be called once.");
|
||||
is(this.tagName, "BUTTON", "Only the <button> element should be upgraded.");
|
||||
buttonCallbackCalled = true;
|
||||
|
@ -21,7 +21,7 @@ document.registerElement("x-button", { prototype: extendedButtonProto, extends:
|
|||
|
||||
var divProto = Object.create(HTMLDivElement.prototype);
|
||||
var divCallbackCalled = false;
|
||||
divProto.createdCallback = function() {
|
||||
divProto.connectedCallback = function() {
|
||||
is(divCallbackCalled, false, "created callback for x-div should only be called once.");
|
||||
is(buttonCallbackCalled, true, "crated callback should be called for x-button before x-div.");
|
||||
is(this.tagName, "X-DIV", "Only the <x-div> element should be upgraded.");
|
||||
|
|
|
@ -56,7 +56,11 @@ const char* const kFragHeader_Tex2DRect = "\
|
|||
const char* const kFragHeader_TexExt = "\
|
||||
#extension GL_OES_EGL_image_external : require \n\
|
||||
#define SAMPLER samplerExternalOES \n\
|
||||
#define TEXTURE texture2D \n\
|
||||
#if __VERSION__ >= 130 \n\
|
||||
#define TEXTURE texture \n\
|
||||
#else \n\
|
||||
#define TEXTURE texture2D \n\
|
||||
#endif \n\
|
||||
";
|
||||
|
||||
const char* const kFragBody_RGBA = "\
|
||||
|
@ -71,14 +75,13 @@ const char* const kFragBody_RGBA = "\
|
|||
const char* const kFragBody_CrYCb = "\
|
||||
VARYING vec2 vTexCoord0; \n\
|
||||
uniform SAMPLER uTex0; \n\
|
||||
uniform mat4 uColorMatrix; \n\
|
||||
uniform MAT4X3 uColorMatrix; \n\
|
||||
\n\
|
||||
void main(void) \n\
|
||||
{ \n\
|
||||
vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).gbr, \n\
|
||||
1.0); \n\
|
||||
vec4 rgb = uColorMatrix * yuv; \n\
|
||||
FRAG_COLOR = vec4(rgb.rgb, 1.0); \n\
|
||||
FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
|
||||
} \n\
|
||||
";
|
||||
const char* const kFragBody_NV12 = "\
|
||||
|
@ -86,15 +89,14 @@ const char* const kFragBody_NV12 = "\
|
|||
VARYING vec2 vTexCoord1; \n\
|
||||
uniform SAMPLER uTex0; \n\
|
||||
uniform SAMPLER uTex1; \n\
|
||||
uniform mat4 uColorMatrix; \n\
|
||||
uniform MAT4X3 uColorMatrix; \n\
|
||||
\n\
|
||||
void main(void) \n\
|
||||
{ \n\
|
||||
vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\
|
||||
TEXTURE(uTex1, vTexCoord1).xy, \n\
|
||||
1.0); \n\
|
||||
vec4 rgb = uColorMatrix * yuv; \n\
|
||||
FRAG_COLOR = vec4(rgb.rgb, 1.0); \n\
|
||||
FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
|
||||
} \n\
|
||||
";
|
||||
const char* const kFragBody_PlanarYUV = "\
|
||||
|
@ -103,7 +105,7 @@ const char* const kFragBody_PlanarYUV = "\
|
|||
uniform SAMPLER uTex0; \n\
|
||||
uniform SAMPLER uTex1; \n\
|
||||
uniform SAMPLER uTex2; \n\
|
||||
uniform mat4 uColorMatrix; \n\
|
||||
uniform MAT4X3 uColorMatrix; \n\
|
||||
\n\
|
||||
void main(void) \n\
|
||||
{ \n\
|
||||
|
@ -111,13 +113,88 @@ const char* const kFragBody_PlanarYUV = "\
|
|||
TEXTURE(uTex1, vTexCoord1).x, \n\
|
||||
TEXTURE(uTex2, vTexCoord1).x, \n\
|
||||
1.0); \n\
|
||||
vec4 rgb = uColorMatrix * yuv; \n\
|
||||
FRAG_COLOR = vec4(rgb.rgb, 1.0); \n\
|
||||
FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\
|
||||
} \n\
|
||||
";
|
||||
|
||||
// --
|
||||
|
||||
template<uint8_t N>
|
||||
/*static*/ Mat<N>
|
||||
Mat<N>::Zero()
|
||||
{
|
||||
Mat<N> ret;
|
||||
for (auto& x : ret.m) {
|
||||
x = 0.0f;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<uint8_t N>
|
||||
/*static*/ Mat<N>
|
||||
Mat<N>::I()
|
||||
{
|
||||
auto ret = Mat<N>::Zero();
|
||||
for (uint8_t i = 0; i < N; i++) {
|
||||
ret.at(i,i) = 1.0f;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<uint8_t N>
|
||||
Mat<N>
|
||||
Mat<N>::operator*(const Mat<N>& r) const
|
||||
{
|
||||
Mat<N> ret;
|
||||
for (uint8_t x = 0; x < N; x++) {
|
||||
for (uint8_t y = 0; y < N; y++) {
|
||||
float sum = 0.0f;
|
||||
for (uint8_t i = 0; i < N; i++) {
|
||||
sum += at(i,y) * r.at(x,i);
|
||||
}
|
||||
ret.at(x,y) = sum;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Mat3
|
||||
SubRectMat3(const float x, const float y, const float w, const float h)
|
||||
{
|
||||
auto ret = Mat3::Zero();
|
||||
ret.at(0,0) = w;
|
||||
ret.at(1,1) = h;
|
||||
ret.at(2,0) = x;
|
||||
ret.at(2,1) = y;
|
||||
ret.at(2,2) = 1.0f;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Mat3
|
||||
SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size)
|
||||
{
|
||||
return SubRectMat3(float(subrect.x) / size.width,
|
||||
float(subrect.y) / size.height,
|
||||
float(subrect.width) / size.width,
|
||||
float(subrect.height) / size.height);
|
||||
}
|
||||
|
||||
Mat3
|
||||
SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize,
|
||||
const gfx::IntSize& divisors)
|
||||
{
|
||||
const float x = float(bigSubrect.x) / divisors.width;
|
||||
const float y = float(bigSubrect.y) / divisors.height;
|
||||
const float w = float(bigSubrect.width) / divisors.width;
|
||||
const float h = float(bigSubrect.height) / divisors.height;
|
||||
return SubRectMat3(x / smallSize.width,
|
||||
y / smallSize.height,
|
||||
w / smallSize.width,
|
||||
h / smallSize.height);
|
||||
}
|
||||
|
||||
// --
|
||||
|
||||
ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, const uint8_t texCount,
|
||||
const GLenum texTarget)
|
||||
: mGL(*gl)
|
||||
|
@ -266,6 +343,7 @@ public:
|
|||
mGL.fColorMask(true, true, true, true);
|
||||
|
||||
mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport);
|
||||
MOZ_ASSERT(destSize.width && destSize.height);
|
||||
mGL.fViewport(0, 0, destSize.width, destSize.height);
|
||||
}
|
||||
|
||||
|
@ -294,19 +372,33 @@ public:
|
|||
DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog)
|
||||
: mParent(*parent)
|
||||
, mProg(prog)
|
||||
, mLoc_u1ForYFlip(mParent.mGL->fGetUniformLocation(mProg, "u1ForYFlip"))
|
||||
, mLoc_uSrcRect(mParent.mGL->fGetUniformLocation(mProg, "uSrcRect"))
|
||||
, mLoc_uTexSize0(mParent.mGL->fGetUniformLocation(mProg, "uTexSize0"))
|
||||
, mLoc_uTexSize1(mParent.mGL->fGetUniformLocation(mProg, "uTexSize1"))
|
||||
, mLoc_uDivisors(mParent.mGL->fGetUniformLocation(mProg, "uDivisors"))
|
||||
, mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix"))
|
||||
, mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0"))
|
||||
, mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1"))
|
||||
, mLoc_uColorMatrix(mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix"))
|
||||
, mType_uColorMatrix(0)
|
||||
{
|
||||
MOZ_ASSERT(mLoc_u1ForYFlip != -1);
|
||||
MOZ_ASSERT(mLoc_uSrcRect != -1);
|
||||
MOZ_ASSERT(mLoc_uTexSize0 != -1);
|
||||
MOZ_ASSERT(mLoc_uDestMatrix != -1);
|
||||
MOZ_ASSERT(mLoc_uTexMatrix0 != -1);
|
||||
if (mLoc_uColorMatrix != -1) {
|
||||
MOZ_ASSERT(mLoc_uTexSize1 != -1);
|
||||
MOZ_ASSERT(mLoc_uDivisors != -1);
|
||||
MOZ_ASSERT(mLoc_uTexMatrix1 != -1);
|
||||
|
||||
const auto& gl = mParent.mGL;
|
||||
int32_t numActiveUniforms = 0;
|
||||
gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms);
|
||||
|
||||
const size_t kMaxNameSize = 32;
|
||||
char name[kMaxNameSize] = {0};
|
||||
GLint size = 0;
|
||||
GLenum type = 0;
|
||||
for (int32_t i = 0; i < numActiveUniforms; i++) {
|
||||
gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type, name);
|
||||
if (strcmp("uColorMatrix", name) == 0) {
|
||||
mType_uColorMatrix = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(mType_uColorMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,18 +421,49 @@ DrawBlitProg::Draw(const BaseArgs& args, const YUVArgs* const argsYUV) const
|
|||
|
||||
// --
|
||||
|
||||
gl->fUniform1f(mLoc_u1ForYFlip, args.yFlip ? 1 : 0);
|
||||
gl->fUniform4f(mLoc_uSrcRect,
|
||||
args.srcRect.x, args.srcRect.y,
|
||||
args.srcRect.width, args.srcRect.height);
|
||||
gl->fUniform2f(mLoc_uTexSize0, args.texSize0.width, args.texSize0.height);
|
||||
Mat3 destMatrix;
|
||||
if (args.destRect) {
|
||||
const auto& destRect = args.destRect.value();
|
||||
destMatrix = SubRectMat3(destRect.x / args.destSize.width,
|
||||
destRect.y / args.destSize.height,
|
||||
destRect.width / args.destSize.width,
|
||||
destRect.height / args.destSize.height);
|
||||
} else {
|
||||
destMatrix = Mat3::I();
|
||||
}
|
||||
|
||||
if (args.yFlip) {
|
||||
// Apply the y-flip matrix before the destMatrix.
|
||||
// That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect.
|
||||
destMatrix.at(2,1) += destMatrix.at(1,1);
|
||||
destMatrix.at(1,1) *= -1.0f;
|
||||
}
|
||||
|
||||
gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m);
|
||||
gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m);
|
||||
|
||||
MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1));
|
||||
if (argsYUV) {
|
||||
gl->fUniform2f(mLoc_uTexSize1, argsYUV->texSize1.width, argsYUV->texSize1.height);
|
||||
gl->fUniform2f(mLoc_uDivisors, argsYUV->divisors.width, argsYUV->divisors.height);
|
||||
gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m);
|
||||
|
||||
const auto& colorMatrix = gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace);
|
||||
gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
|
||||
float mat4x3[4*3];
|
||||
switch (mType_uColorMatrix) {
|
||||
case LOCAL_GL_FLOAT_MAT4:
|
||||
gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix);
|
||||
break;
|
||||
case LOCAL_GL_FLOAT_MAT4x3:
|
||||
for (int x = 0; x < 4; x++) {
|
||||
for (int y = 0; y < 3; y++) {
|
||||
mat4x3[3*x+y] = colorMatrix[4*x+y];
|
||||
}
|
||||
}
|
||||
gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3);
|
||||
break;
|
||||
default:
|
||||
gfxCriticalError() << "Bad mType_uColorMatrix: "
|
||||
<< gfx::hexa(mType_uColorMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
// --
|
||||
|
@ -424,8 +547,12 @@ GLBlitHelper::GLBlitHelper(GLContext* const gl)
|
|||
|
||||
// --
|
||||
|
||||
if (!mGL->IsGLES()) {
|
||||
const auto glslVersion = mGL->ShadingLanguageVersion();
|
||||
const auto glslVersion = mGL->ShadingLanguageVersion();
|
||||
if (mGL->IsGLES()) {
|
||||
if (glslVersion >= 300) {
|
||||
mDrawBlitProg_VersionLine = nsPrintfCString("#version %u es\n", glslVersion);
|
||||
}
|
||||
} else {
|
||||
if (glslVersion >= 130) {
|
||||
mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion);
|
||||
}
|
||||
|
@ -440,31 +567,24 @@ GLBlitHelper::GLBlitHelper(GLContext* const gl)
|
|||
#define VARYING varying \n\
|
||||
#endif \n\
|
||||
\n\
|
||||
ATTRIBUTE vec2 aVert; \n\
|
||||
ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\
|
||||
\n\
|
||||
uniform float u1ForYFlip; \n\
|
||||
uniform vec4 uSrcRect; \n\
|
||||
uniform vec2 uTexSize0; \n\
|
||||
uniform vec2 uTexSize1; \n\
|
||||
uniform vec2 uDivisors; \n\
|
||||
uniform mat3 uDestMatrix; \n\
|
||||
uniform mat3 uTexMatrix0; \n\
|
||||
uniform mat3 uTexMatrix1; \n\
|
||||
\n\
|
||||
VARYING vec2 vTexCoord0; \n\
|
||||
VARYING vec2 vTexCoord1; \n\
|
||||
\n\
|
||||
void main(void) \n\
|
||||
{ \n\
|
||||
vec2 vertPos = aVert * 2.0 - 1.0; \n\
|
||||
gl_Position = vec4(vertPos, 0.0, 1.0); \n\
|
||||
vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\
|
||||
gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\
|
||||
\n\
|
||||
vec2 texCoord = aVert; \n\
|
||||
texCoord.y = abs(u1ForYFlip - texCoord.y); \n\
|
||||
texCoord = texCoord * uSrcRect.zw + uSrcRect.xy; \n\
|
||||
\n\
|
||||
vTexCoord0 = texCoord / uTexSize0; \n\
|
||||
vTexCoord1 = texCoord / (uTexSize1 * uDivisors); \n\
|
||||
vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\
|
||||
vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\
|
||||
} \n\
|
||||
";
|
||||
|
||||
const char* const parts[] = {
|
||||
mDrawBlitProg_VersionLine.get(),
|
||||
kVertSource
|
||||
|
@ -506,7 +626,6 @@ GLBlitHelper::GetDrawBlitProg(const DrawBlitProg::Key& key) const
|
|||
return pair.second;
|
||||
}
|
||||
|
||||
|
||||
const DrawBlitProg*
|
||||
GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const
|
||||
{
|
||||
|
@ -522,11 +641,16 @@ GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const
|
|||
#if __VERSION__ >= 130 \n\
|
||||
#define VARYING in \n\
|
||||
#define FRAG_COLOR oFragColor \n\
|
||||
\n\
|
||||
out vec4 FRAG_COLOR; \n\
|
||||
#else \n\
|
||||
#define VARYING varying \n\
|
||||
#define FRAG_COLOR gl_FragColor \n\
|
||||
#endif \n\
|
||||
\n\
|
||||
#if __VERSION__ >= 120 \n\
|
||||
#define MAT4X3 mat4x3 \n\
|
||||
#else \n\
|
||||
#define MAT4X3 mat4 \n\
|
||||
#endif \n\
|
||||
";
|
||||
|
||||
|
@ -578,13 +702,13 @@ GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const
|
|||
mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen);
|
||||
const UniquePtr<char[]> vsLog(new char[vsLogLen+1]);
|
||||
mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get());
|
||||
progLog[progLogLen] = 0;
|
||||
vsLog[vsLogLen] = 0;
|
||||
|
||||
GLuint fsLogLen = 0;
|
||||
mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen);
|
||||
const UniquePtr<char[]> fsLog(new char[fsLogLen+1]);
|
||||
mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get());
|
||||
progLog[progLogLen] = 0;
|
||||
fsLog[fsLogLen] = 0;
|
||||
|
||||
gfxCriticalError() << "DrawBlitProg link failed:\n"
|
||||
<< "progLog: " << progLog.get() << "\n"
|
||||
|
@ -596,9 +720,9 @@ GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
|
||||
GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage,
|
||||
const gfx::IntSize& destSize,
|
||||
OriginPos destOrigin)
|
||||
const OriginPos destOrigin)
|
||||
{
|
||||
switch (srcImage->GetFormat()) {
|
||||
case ImageFormat::PLANAR_YCBCR:
|
||||
|
@ -618,8 +742,7 @@ GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage,
|
|||
return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destSize,
|
||||
destOrigin);
|
||||
case ImageFormat::D3D11_YCBCR_IMAGE:
|
||||
return BlitImage((layers::D3D11YCbCrImage*)srcImage, destSize,
|
||||
destOrigin);
|
||||
return BlitImage((layers::D3D11YCbCrImage*)srcImage, destSize, destOrigin);
|
||||
case ImageFormat::D3D9_RGB32_TEXTURE:
|
||||
return false; // todo
|
||||
#endif
|
||||
|
@ -774,14 +897,18 @@ GLBlitHelper::BlitImage(layers::PlanarYCbCrImage* const yuvImage,
|
|||
|
||||
// --
|
||||
|
||||
const auto& clipRect = yuvData->GetPictureRect();
|
||||
const auto srcOrigin = OriginPos::BottomLeft;
|
||||
const bool yFlip = (destOrigin != srcOrigin);
|
||||
const auto& clipRect = yuvData->GetPictureRect();
|
||||
const auto& colorSpace = yuvData->mYUVColorSpace;
|
||||
|
||||
const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, yTexSize };
|
||||
const DrawBlitProg::YUVArgs yuvArgs = { uvTexSize, divisors, colorSpace };
|
||||
|
||||
const DrawBlitProg::BaseArgs baseArgs = {
|
||||
SubRectMat3(clipRect, yTexSize),
|
||||
yFlip, destSize, Nothing()
|
||||
};
|
||||
const DrawBlitProg::YUVArgs yuvArgs = {
|
||||
SubRectMat3(clipRect, uvTexSize, divisors),
|
||||
yuvData->mYUVColorSpace
|
||||
};
|
||||
prog->Draw(baseArgs, &yuvArgs);
|
||||
return true;
|
||||
}
|
||||
|
@ -802,13 +929,14 @@ GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage,
|
|||
const auto cglContext = glCGL->GetCGLContext();
|
||||
|
||||
const auto& srcOrigin = OriginPos::BottomLeft;
|
||||
const bool yFlip = destOrigin != srcOrigin;
|
||||
const gfx::IntRect clipRect({0, 0}, srcImage->GetSize());
|
||||
const gfx::IntSize texRectNormFactor(1, 1);
|
||||
|
||||
const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect,
|
||||
texRectNormFactor };
|
||||
DrawBlitProg::YUVArgs yuvArgs = { texRectNormFactor, {2,2}, YUVColorSpace::BT601 };
|
||||
DrawBlitProg::BaseArgs baseArgs;
|
||||
baseArgs.yFlip = (destOrigin != srcOrigin);
|
||||
baseArgs.destSize = destSize;
|
||||
|
||||
DrawBlitProg::YUVArgs yuvArgs;
|
||||
yuvArgs.colorSpace = YUVColorSpace::BT601;
|
||||
|
||||
const DrawBlitProg::YUVArgs* pYuvArgs = nullptr;
|
||||
|
||||
auto planes = iosurf->GetPlaneCount();
|
||||
|
@ -918,6 +1046,11 @@ GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage,
|
|||
gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (p == 0) {
|
||||
baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height);
|
||||
yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& prog = GetDrawBlitProg({fragHeader, fragBody});
|
||||
|
@ -937,31 +1070,29 @@ GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex,
|
|||
const gfx::IntSize& destSize,
|
||||
const GLenum srcTarget) const
|
||||
{
|
||||
const gfx::IntRect clipRect(0, 0, srcSize.width, srcSize.height);
|
||||
|
||||
DrawBlitProg::Key key;
|
||||
gfx::IntSize texSizeDivisor;
|
||||
const char* fragHeader;
|
||||
Mat3 texMatrix0;
|
||||
switch (srcTarget) {
|
||||
case LOCAL_GL_TEXTURE_2D:
|
||||
key = {kFragHeader_Tex2D, kFragBody_RGBA};
|
||||
texSizeDivisor = srcSize;
|
||||
fragHeader = kFragHeader_Tex2D;
|
||||
texMatrix0 = Mat3::I();
|
||||
break;
|
||||
case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
|
||||
key = {kFragHeader_Tex2DRect, kFragBody_RGBA};
|
||||
texSizeDivisor = gfx::IntSize(1, 1);
|
||||
fragHeader = kFragHeader_Tex2DRect;
|
||||
texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height);
|
||||
break;
|
||||
default:
|
||||
gfxCriticalError() << "Unexpected srcTarget: " << srcTarget;
|
||||
return;
|
||||
}
|
||||
const auto& prog = GetDrawBlitProg(key);
|
||||
const auto& prog = GetDrawBlitProg({ fragHeader, kFragBody_RGBA});
|
||||
MOZ_ASSERT(prog);
|
||||
|
||||
const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget);
|
||||
mGL->fBindTexture(srcTarget, srcTex);
|
||||
|
||||
const bool yFlip = false;
|
||||
const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, texSizeDivisor };
|
||||
const DrawBlitProg::BaseArgs baseArgs = { texMatrix0, yFlip, destSize, Nothing() };
|
||||
prog->Draw(baseArgs);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,21 +41,41 @@ bool
|
|||
GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
|
||||
gfx::IntSize* const out_divisors);
|
||||
|
||||
template<uint8_t N>
|
||||
struct Mat
|
||||
{
|
||||
float m[N*N]; // column-major, for GL
|
||||
|
||||
float& at(const uint8_t x, const uint8_t y) {
|
||||
return m[N*x+y];
|
||||
}
|
||||
|
||||
static Mat<N> Zero();
|
||||
static Mat<N> I();
|
||||
|
||||
Mat<N> operator*(const Mat<N>& r) const;
|
||||
};
|
||||
typedef Mat<3> Mat3;
|
||||
|
||||
Mat3 SubRectMat3(float x, float y, float w, float h);
|
||||
Mat3 SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size);
|
||||
Mat3 SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize,
|
||||
const gfx::IntSize& divisors);
|
||||
|
||||
class DrawBlitProg final
|
||||
{
|
||||
const GLBlitHelper& mParent;
|
||||
const GLuint mProg;
|
||||
const GLint mLoc_u1ForYFlip;
|
||||
const GLint mLoc_uSrcRect;
|
||||
const GLint mLoc_uTexSize0;
|
||||
const GLint mLoc_uTexSize1;
|
||||
const GLint mLoc_uDivisors;
|
||||
const GLint mLoc_uDestMatrix;
|
||||
const GLint mLoc_uTexMatrix0;
|
||||
const GLint mLoc_uTexMatrix1;
|
||||
const GLint mLoc_uColorMatrix;
|
||||
GLenum mType_uColorMatrix;
|
||||
|
||||
public:
|
||||
struct Key final {
|
||||
const char* fragHeader;
|
||||
const char* fragBody;
|
||||
const char* const fragHeader;
|
||||
const char* const fragBody;
|
||||
|
||||
bool operator <(const Key& x) const {
|
||||
if (fragHeader != x.fragHeader)
|
||||
|
@ -68,14 +88,13 @@ public:
|
|||
~DrawBlitProg();
|
||||
|
||||
struct BaseArgs final {
|
||||
gfx::IntSize destSize;
|
||||
Mat3 texMatrix0;
|
||||
bool yFlip;
|
||||
gfx::IntRect srcRect;
|
||||
gfx::IntSize texSize0;
|
||||
gfx::IntSize destSize; // Always needed for (at least) setting the viewport.
|
||||
Maybe<gfx::IntRect> destRect;
|
||||
};
|
||||
struct YUVArgs final {
|
||||
gfx::IntSize texSize1;
|
||||
gfx::IntSize divisors;
|
||||
Mat3 texMatrix1;
|
||||
YUVColorSpace colorSpace;
|
||||
};
|
||||
|
||||
|
@ -181,8 +200,8 @@ private:
|
|||
bool BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
|
||||
const gfx::IntRect& clipRect,
|
||||
const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
|
||||
const YUVColorSpace colorSpace,
|
||||
const gfx::IntSize& destSize, OriginPos destOrigin) const;
|
||||
const YUVColorSpace colorSpace, const gfx::IntSize& destSize,
|
||||
OriginPos destOrigin) const;
|
||||
|
||||
bool BlitAnglePlanes(uint8_t numPlanes, const RefPtr<ID3D11Texture2D>* texD3DList,
|
||||
const DrawBlitProg* prog, const DrawBlitProg::BaseArgs& baseArgs,
|
||||
|
|
|
@ -20,8 +20,7 @@ namespace mozilla {
|
|||
namespace gl {
|
||||
|
||||
static EGLStreamKHR
|
||||
StreamFromD3DTexture(ID3D11Texture2D* const texD3D,
|
||||
const EGLAttrib* const postAttribs)
|
||||
StreamFromD3DTexture(ID3D11Texture2D* const texD3D, const EGLAttrib* const postAttribs)
|
||||
{
|
||||
auto& egl = sEGLLibrary;
|
||||
if (!egl.IsExtensionSupported(GLLibraryEGL::NV_stream_consumer_gltexture_yuv) ||
|
||||
|
@ -244,7 +243,8 @@ GLBlitHelper::BlitImage(layers::D3D11YCbCrImage* const srcImage,
|
|||
|
||||
bool
|
||||
GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
|
||||
const gfx::IntSize& destSize, OriginPos destOrigin) const
|
||||
const gfx::IntSize& destSize,
|
||||
const OriginPos destOrigin) const
|
||||
{
|
||||
const auto& d3d = GetD3D11();
|
||||
if (!d3d)
|
||||
|
@ -295,8 +295,14 @@ GLBlitHelper::BlitDescriptor(const layers::SurfaceDescriptorD3D10& desc,
|
|||
ySize.height / divisors.height);
|
||||
|
||||
const bool yFlip = destOrigin != srcOrigin;
|
||||
const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, ySize };
|
||||
const DrawBlitProg::YUVArgs yuvArgs = { uvSize, divisors, colorSpace };
|
||||
const DrawBlitProg::BaseArgs baseArgs = {
|
||||
SubRectMat3(clipRect, ySize),
|
||||
yFlip, destSize, Nothing()
|
||||
};
|
||||
const DrawBlitProg::YUVArgs yuvArgs = {
|
||||
SubRectMat3(clipRect, uvSize, divisors),
|
||||
colorSpace
|
||||
};
|
||||
|
||||
const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_NV12});
|
||||
MOZ_RELEASE_ASSERT(prog);
|
||||
|
@ -310,8 +316,8 @@ bool
|
|||
GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
|
||||
const gfx::IntRect& clipRect,
|
||||
const gfx::IntSize& ySize, const gfx::IntSize& uvSize,
|
||||
const YUVColorSpace colorSpace,
|
||||
const gfx::IntSize& destSize, OriginPos destOrigin) const
|
||||
const YUVColorSpace colorSpace, const gfx::IntSize& destSize,
|
||||
const OriginPos destOrigin) const
|
||||
{
|
||||
const auto& d3d = GetD3D11();
|
||||
if (!d3d)
|
||||
|
@ -331,8 +337,14 @@ GLBlitHelper::BlitAngleYCbCr(const WindowsHandle (&handleList)[3],
|
|||
const BindAnglePlanes bindPlanes(this, 3, texList);
|
||||
|
||||
const bool yFlip = destOrigin != srcOrigin;
|
||||
const DrawBlitProg::BaseArgs baseArgs = { destSize, yFlip, clipRect, ySize };
|
||||
const DrawBlitProg::YUVArgs yuvArgs = { uvSize, divisors, colorSpace };
|
||||
const DrawBlitProg::BaseArgs baseArgs = {
|
||||
SubRectMat3(clipRect, ySize),
|
||||
yFlip, destSize, Nothing()
|
||||
};
|
||||
const DrawBlitProg::YUVArgs yuvArgs = {
|
||||
SubRectMat3(clipRect, uvSize, divisors),
|
||||
colorSpace
|
||||
};
|
||||
|
||||
const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_PlanarYUV});
|
||||
MOZ_RELEASE_ASSERT(prog);
|
||||
|
|
|
@ -144,7 +144,7 @@ nsFontCache::GetMetricsFor(const nsFont& aFont,
|
|||
RefPtr<nsFontMetrics> fm = new nsFontMetrics(aFont, params, mContext);
|
||||
// the mFontMetrics list has the "head" at the end, because append
|
||||
// is cheaper than insert
|
||||
mFontMetrics.AppendElement(do_AddRef(fm.get()).take());
|
||||
mFontMetrics.AppendElement(do_AddRef(fm).take());
|
||||
return fm.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
struct IInterceptor;
|
||||
|
||||
struct HandlerProvider
|
||||
{
|
||||
virtual STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) = 0;
|
||||
virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize) = 0;
|
||||
virtual STDMETHODIMP WriteHandlerPayload(NotNull<IStream*> aStream) = 0;
|
||||
virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0;
|
||||
virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, NotNull<IStream*> aStream) = 0;
|
||||
virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -247,7 +247,8 @@ Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
|
|||
}
|
||||
|
||||
DWORD payloadSize = 0;
|
||||
hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(&payloadSize));
|
||||
hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(this),
|
||||
WrapNotNull(&payloadSize));
|
||||
if (hr == E_NOTIMPL) {
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -307,7 +308,7 @@ Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
|
|||
}
|
||||
#endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
|
||||
|
||||
hr = mEventSink->WriteHandlerPayload(WrapNotNull(pStm));
|
||||
hr = mEventSink->WriteHandlerPayload(WrapNotNull(this), WrapNotNull(pStm));
|
||||
if (hr == E_NOTIMPL) {
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
@ -517,21 +517,23 @@ MainThreadHandoff::GetHandler(NotNull<CLSID*> aHandlerClsid)
|
|||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize)
|
||||
MainThreadHandoff::GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
|
||||
NotNull<DWORD*> aOutPayloadSize)
|
||||
{
|
||||
if (!mHandlerProvider) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
return mHandlerProvider->GetHandlerPayloadSize(aOutPayloadSize);
|
||||
return mHandlerProvider->GetHandlerPayloadSize(aInterceptor, aOutPayloadSize);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
MainThreadHandoff::WriteHandlerPayload(NotNull<IStream*> aStream)
|
||||
MainThreadHandoff::WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
|
||||
NotNull<IStream*> aStream)
|
||||
{
|
||||
if (!mHandlerProvider) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
return mHandlerProvider->WriteHandlerPayload(aStream);
|
||||
return mHandlerProvider->WriteHandlerPayload(aInterceptor, aStream);
|
||||
}
|
||||
|
||||
REFIID
|
||||
|
|
|
@ -61,8 +61,10 @@ public:
|
|||
// IInterceptorSink
|
||||
STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) override;
|
||||
STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
|
||||
STDMETHODIMP GetHandlerPayloadSize(NotNull<DWORD*> aOutPayloadSize) override;
|
||||
STDMETHODIMP WriteHandlerPayload(NotNull<IStream*> aStream) override;
|
||||
STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
|
||||
NotNull<DWORD*> aOutPayloadSize) override;
|
||||
STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
|
||||
NotNull<IStream*> aStream) override;
|
||||
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
|
||||
|
||||
// ICallFrameWalker
|
||||
|
|
|
@ -42,6 +42,14 @@ const size_t CellAlignMask = CellAlignBytes - 1;
|
|||
|
||||
const size_t CellBytesPerMarkBit = CellAlignBytes;
|
||||
|
||||
/*
|
||||
* We sometimes use an index to refer to a cell in an arena. The index for a
|
||||
* cell is found by dividing by the cell alignment so not all indicies refer to
|
||||
* valid cells.
|
||||
*/
|
||||
const size_t ArenaCellIndexBytes = CellAlignBytes;
|
||||
const size_t MaxArenaCellIndex = ArenaSize / CellAlignBytes;
|
||||
|
||||
/* These are magic constants derived from actual offsets in gc/Heap.h. */
|
||||
#ifdef JS_GC_SMALL_CHUNK_SIZE
|
||||
const size_t ChunkMarkBitmapOffset = 258104;
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace JS {
|
|||
_(GetProp_Unboxed) \
|
||||
_(GetProp_CommonGetter) \
|
||||
_(GetProp_InlineAccess) \
|
||||
_(GetProp_InlineProtoAccess) \
|
||||
_(GetProp_Innerize) \
|
||||
_(GetProp_InlineCache) \
|
||||
_(GetProp_SharedCache) \
|
||||
_(GetProp_ModuleNamespace) \
|
||||
\
|
||||
_(SetProp_CommonSetter) \
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "jsapi.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsiter.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsprf.h"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "gc/ArenaList.h"
|
||||
#include "gc/GCInternals.h"
|
||||
#include "gc/GCTrace.h"
|
||||
#include "gc/Nursery.h"
|
||||
|
@ -297,7 +298,7 @@ GCRuntime::refillFreeListFromActiveCooperatingThread(JSContext* cx, AllocKind th
|
|||
Zone *zone = cx->zone();
|
||||
MOZ_ASSERT(!JS::CurrentThreadIsHeapBusy(), "allocating while under GC");
|
||||
|
||||
return cx->arenas()->allocateFromArena(zone, thingKind, CheckThresholds);
|
||||
return cx->arenas()->allocateFromArena(zone, thingKind, ShouldCheckThresholds::CheckThresholds);
|
||||
}
|
||||
|
||||
/* static */ TenuredCell*
|
||||
|
@ -308,7 +309,7 @@ GCRuntime::refillFreeListFromHelperThread(JSContext* cx, AllocKind thingKind)
|
|||
Zone* zone = cx->zone();
|
||||
MOZ_ASSERT(!zone->wasGCStarted());
|
||||
|
||||
return cx->arenas()->allocateFromArena(zone, thingKind, CheckThresholds);
|
||||
return cx->arenas()->allocateFromArena(zone, thingKind, ShouldCheckThresholds::CheckThresholds);
|
||||
}
|
||||
|
||||
/* static */ TenuredCell*
|
||||
|
@ -323,7 +324,7 @@ GCRuntime::refillFreeListInGC(Zone* zone, AllocKind thingKind)
|
|||
MOZ_ASSERT(JS::CurrentThreadIsHeapCollecting());
|
||||
MOZ_ASSERT_IF(!JS::CurrentThreadIsHeapMinorCollecting(), !rt->gc.isBackgroundSweeping());
|
||||
|
||||
return zone->arenas.allocateFromArena(zone, thingKind, DontCheckThresholds);
|
||||
return zone->arenas.allocateFromArena(zone, thingKind, ShouldCheckThresholds::DontCheckThresholds);
|
||||
}
|
||||
|
||||
TenuredCell*
|
||||
|
@ -415,15 +416,16 @@ GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind,
|
|||
MOZ_ASSERT(chunk->hasAvailableArenas());
|
||||
|
||||
// Fail the allocation if we are over our heap size limits.
|
||||
if (checkThresholds && usage.gcBytes() >= tunables.gcMaxBytes())
|
||||
if ((checkThresholds != ShouldCheckThresholds::DontCheckThresholds) &&
|
||||
(usage.gcBytes() >= tunables.gcMaxBytes()))
|
||||
return nullptr;
|
||||
|
||||
Arena* arena = chunk->allocateArena(rt, zone, thingKind, lock);
|
||||
zone->usage.addGCArena();
|
||||
|
||||
// Trigger an incremental slice if needed.
|
||||
if (checkThresholds)
|
||||
maybeAllocTriggerGC(zone, lock);
|
||||
if (checkThresholds != ShouldCheckThresholds::DontCheckThresholds)
|
||||
maybeAllocTriggerZoneGC(zone, lock);
|
||||
|
||||
return arena;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 gc_ArenaList_h
|
||||
#define gc_ArenaList_h
|
||||
|
||||
#include "gc/Heap.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "threading/ProtectedData.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
struct Zone;
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
||||
class Nursery;
|
||||
class TenuringTracer;
|
||||
|
||||
namespace gc {
|
||||
|
||||
struct FinalizePhase;
|
||||
|
||||
/*
|
||||
* A single segment of a SortedArenaList. Each segment has a head and a tail,
|
||||
* which track the start and end of a segment for O(1) append and concatenation.
|
||||
*/
|
||||
struct SortedArenaListSegment
|
||||
{
|
||||
Arena* head;
|
||||
Arena** tailp;
|
||||
|
||||
void clear() {
|
||||
head = nullptr;
|
||||
tailp = &head;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
return tailp == &head;
|
||||
}
|
||||
|
||||
// Appends |arena| to this segment.
|
||||
void append(Arena* arena) {
|
||||
MOZ_ASSERT(arena);
|
||||
MOZ_ASSERT_IF(head, head->getAllocKind() == arena->getAllocKind());
|
||||
*tailp = arena;
|
||||
tailp = &arena->next;
|
||||
}
|
||||
|
||||
// Points the tail of this segment at |arena|, which may be null. Note
|
||||
// that this does not change the tail itself, but merely which arena
|
||||
// follows it. This essentially turns the tail into a cursor (see also the
|
||||
// description of ArenaList), but from the perspective of a SortedArenaList
|
||||
// this makes no difference.
|
||||
void linkTo(Arena* arena) {
|
||||
*tailp = arena;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Arena lists have a head and a cursor. The cursor conceptually lies on arena
|
||||
* boundaries, i.e. before the first arena, between two arenas, or after the
|
||||
* last arena.
|
||||
*
|
||||
* Arenas are usually sorted in order of increasing free space, with the cursor
|
||||
* following the Arena currently being allocated from. This ordering should not
|
||||
* be treated as an invariant, however, as the free lists may be cleared,
|
||||
* leaving arenas previously used for allocation partially full. Sorting order
|
||||
* is restored during sweeping.
|
||||
|
||||
* Arenas following the cursor should not be full.
|
||||
*/
|
||||
class ArenaList {
|
||||
// The cursor is implemented via an indirect pointer, |cursorp_|, to allow
|
||||
// for efficient list insertion at the cursor point and other list
|
||||
// manipulations.
|
||||
//
|
||||
// - If the list is empty: |head| is null, |cursorp_| points to |head|, and
|
||||
// therefore |*cursorp_| is null.
|
||||
//
|
||||
// - If the list is not empty: |head| is non-null, and...
|
||||
//
|
||||
// - If the cursor is at the start of the list: |cursorp_| points to
|
||||
// |head|, and therefore |*cursorp_| points to the first arena.
|
||||
//
|
||||
// - If cursor is at the end of the list: |cursorp_| points to the |next|
|
||||
// field of the last arena, and therefore |*cursorp_| is null.
|
||||
//
|
||||
// - If the cursor is at neither the start nor the end of the list:
|
||||
// |cursorp_| points to the |next| field of the arena preceding the
|
||||
// cursor, and therefore |*cursorp_| points to the arena following the
|
||||
// cursor.
|
||||
//
|
||||
// |cursorp_| is never null.
|
||||
//
|
||||
Arena* head_;
|
||||
Arena** cursorp_;
|
||||
|
||||
void copy(const ArenaList& other) {
|
||||
other.check();
|
||||
head_ = other.head_;
|
||||
cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
|
||||
check();
|
||||
}
|
||||
|
||||
public:
|
||||
ArenaList() {
|
||||
clear();
|
||||
}
|
||||
|
||||
ArenaList(const ArenaList& other) {
|
||||
copy(other);
|
||||
}
|
||||
|
||||
ArenaList& operator=(const ArenaList& other) {
|
||||
copy(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit ArenaList(const SortedArenaListSegment& segment) {
|
||||
head_ = segment.head;
|
||||
cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
|
||||
check();
|
||||
}
|
||||
|
||||
// This does checking just of |head_| and |cursorp_|.
|
||||
void check() const {
|
||||
#ifdef DEBUG
|
||||
// If the list is empty, it must have this form.
|
||||
MOZ_ASSERT_IF(!head_, cursorp_ == &head_);
|
||||
|
||||
// If there's an arena following the cursor, it must not be full.
|
||||
Arena* cursor = *cursorp_;
|
||||
MOZ_ASSERT_IF(cursor, cursor->hasFreeThings());
|
||||
#endif
|
||||
}
|
||||
|
||||
void clear() {
|
||||
head_ = nullptr;
|
||||
cursorp_ = &head_;
|
||||
check();
|
||||
}
|
||||
|
||||
ArenaList copyAndClear() {
|
||||
ArenaList result = *this;
|
||||
clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isEmpty() const {
|
||||
check();
|
||||
return !head_;
|
||||
}
|
||||
|
||||
// This returns nullptr if the list is empty.
|
||||
Arena* head() const {
|
||||
check();
|
||||
return head_;
|
||||
}
|
||||
|
||||
bool isCursorAtHead() const {
|
||||
check();
|
||||
return cursorp_ == &head_;
|
||||
}
|
||||
|
||||
bool isCursorAtEnd() const {
|
||||
check();
|
||||
return !*cursorp_;
|
||||
}
|
||||
|
||||
void moveCursorToEnd() {
|
||||
while (!isCursorAtEnd())
|
||||
cursorp_ = &(*cursorp_)->next;
|
||||
}
|
||||
|
||||
// This can return nullptr.
|
||||
Arena* arenaAfterCursor() const {
|
||||
check();
|
||||
return *cursorp_;
|
||||
}
|
||||
|
||||
// This returns the arena after the cursor and moves the cursor past it.
|
||||
Arena* takeNextArena() {
|
||||
check();
|
||||
Arena* arena = *cursorp_;
|
||||
if (!arena)
|
||||
return nullptr;
|
||||
cursorp_ = &arena->next;
|
||||
check();
|
||||
return arena;
|
||||
}
|
||||
|
||||
// This does two things.
|
||||
// - Inserts |a| at the cursor.
|
||||
// - Leaves the cursor sitting just before |a|, if |a| is not full, or just
|
||||
// after |a|, if |a| is full.
|
||||
void insertAtCursor(Arena* a) {
|
||||
check();
|
||||
a->next = *cursorp_;
|
||||
*cursorp_ = a;
|
||||
// At this point, the cursor is sitting before |a|. Move it after |a|
|
||||
// if necessary.
|
||||
if (!a->hasFreeThings())
|
||||
cursorp_ = &a->next;
|
||||
check();
|
||||
}
|
||||
|
||||
// Inserts |a| at the cursor, then moves the cursor past it.
|
||||
void insertBeforeCursor(Arena* a) {
|
||||
check();
|
||||
a->next = *cursorp_;
|
||||
*cursorp_ = a;
|
||||
cursorp_ = &a->next;
|
||||
check();
|
||||
}
|
||||
|
||||
// This inserts |other|, which must be full, at the cursor of |this|.
|
||||
ArenaList& insertListWithCursorAtEnd(const ArenaList& other) {
|
||||
check();
|
||||
other.check();
|
||||
MOZ_ASSERT(other.isCursorAtEnd());
|
||||
if (other.isCursorAtHead())
|
||||
return *this;
|
||||
// Insert the full arenas of |other| after those of |this|.
|
||||
*other.cursorp_ = *cursorp_;
|
||||
*cursorp_ = other.head_;
|
||||
cursorp_ = other.cursorp_;
|
||||
check();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Arena* removeRemainingArenas(Arena** arenap);
|
||||
Arena** pickArenasToRelocate(size_t& arenaTotalOut, size_t& relocTotalOut);
|
||||
Arena* relocateArenas(Arena* toRelocate, Arena* relocated,
|
||||
js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
|
||||
};
|
||||
|
||||
/*
|
||||
* A class that holds arenas in sorted order by appending arenas to specific
|
||||
* segments. Each segment has a head and a tail, which can be linked up to
|
||||
* other segments to create a contiguous ArenaList.
|
||||
*/
|
||||
class SortedArenaList
|
||||
{
|
||||
public:
|
||||
// The minimum size, in bytes, of a GC thing.
|
||||
static const size_t MinThingSize = 16;
|
||||
|
||||
static_assert(ArenaSize <= 4096, "When increasing the Arena size, please consider how"\
|
||||
" this will affect the size of a SortedArenaList.");
|
||||
|
||||
static_assert(MinThingSize >= 16, "When decreasing the minimum thing size, please consider"\
|
||||
" how this will affect the size of a SortedArenaList.");
|
||||
|
||||
private:
|
||||
// The maximum number of GC things that an arena can hold.
|
||||
static const size_t MaxThingsPerArena = (ArenaSize - ArenaHeaderSize) / MinThingSize;
|
||||
|
||||
size_t thingsPerArena_;
|
||||
SortedArenaListSegment segments[MaxThingsPerArena + 1];
|
||||
|
||||
// Convenience functions to get the nth head and tail.
|
||||
Arena* headAt(size_t n) { return segments[n].head; }
|
||||
Arena** tailAt(size_t n) { return segments[n].tailp; }
|
||||
|
||||
public:
|
||||
explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena) {
|
||||
reset(thingsPerArena);
|
||||
}
|
||||
|
||||
void setThingsPerArena(size_t thingsPerArena) {
|
||||
MOZ_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
|
||||
thingsPerArena_ = thingsPerArena;
|
||||
}
|
||||
|
||||
// Resets the first |thingsPerArena| segments of this list for further use.
|
||||
void reset(size_t thingsPerArena = MaxThingsPerArena) {
|
||||
setThingsPerArena(thingsPerArena);
|
||||
// Initialize the segments.
|
||||
for (size_t i = 0; i <= thingsPerArena; ++i)
|
||||
segments[i].clear();
|
||||
}
|
||||
|
||||
// Inserts an arena, which has room for |nfree| more things, in its segment.
|
||||
void insertAt(Arena* arena, size_t nfree) {
|
||||
MOZ_ASSERT(nfree <= thingsPerArena_);
|
||||
segments[nfree].append(arena);
|
||||
}
|
||||
|
||||
// Remove all empty arenas, inserting them as a linked list.
|
||||
void extractEmpty(Arena** empty) {
|
||||
SortedArenaListSegment& segment = segments[thingsPerArena_];
|
||||
if (segment.head) {
|
||||
*segment.tailp = *empty;
|
||||
*empty = segment.head;
|
||||
segment.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Links up the tail of each non-empty segment to the head of the next
|
||||
// non-empty segment, creating a contiguous list that is returned as an
|
||||
// ArenaList. This is not a destructive operation: neither the head nor tail
|
||||
// of any segment is modified. However, note that the Arenas in the
|
||||
// resulting ArenaList should be treated as read-only unless the
|
||||
// SortedArenaList is no longer needed: inserting or removing arenas would
|
||||
// invalidate the SortedArenaList.
|
||||
ArenaList toArenaList() {
|
||||
// Link the non-empty segment tails up to the non-empty segment heads.
|
||||
size_t tailIndex = 0;
|
||||
for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
|
||||
if (headAt(headIndex)) {
|
||||
segments[tailIndex].linkTo(headAt(headIndex));
|
||||
tailIndex = headIndex;
|
||||
}
|
||||
}
|
||||
// Point the tail of the final non-empty segment at null. Note that if
|
||||
// the list is empty, this will just set segments[0].head to null.
|
||||
segments[tailIndex].linkTo(nullptr);
|
||||
// Create an ArenaList with head and cursor set to the head and tail of
|
||||
// the first segment (if that segment is empty, only the head is used).
|
||||
return ArenaList(segments[0]);
|
||||
}
|
||||
};
|
||||
|
||||
enum class ShouldCheckThresholds
|
||||
{
|
||||
DontCheckThresholds = 0,
|
||||
CheckThresholds = 1
|
||||
};
|
||||
|
||||
class ArenaLists
|
||||
{
|
||||
JSRuntime* const runtime_;
|
||||
|
||||
/*
|
||||
* For each arena kind its free list is represented as the first span with
|
||||
* free things. Initially all the spans are initialized as empty. After we
|
||||
* find a new arena with available things we move its first free span into
|
||||
* the list and set the arena as fully allocated. way we do not need to
|
||||
* update the arena after the initial allocation. When starting the
|
||||
* GC we only move the head of the of the list of spans back to the arena
|
||||
* only for the arena that was not fully allocated.
|
||||
*/
|
||||
ZoneGroupData<AllAllocKindArray<FreeSpan*>> freeLists_;
|
||||
FreeSpan*& freeLists(AllocKind i) { return freeLists_.ref()[i]; }
|
||||
FreeSpan* freeLists(AllocKind i) const { return freeLists_.ref()[i]; }
|
||||
|
||||
// Because the JITs can allocate from the free lists, they cannot be null.
|
||||
// We use a placeholder FreeSpan that is empty (and wihout an associated
|
||||
// Arena) so the JITs can fall back gracefully.
|
||||
static FreeSpan placeholder;
|
||||
|
||||
ZoneGroupOrGCTaskData<AllAllocKindArray<ArenaList>> arenaLists_;
|
||||
ArenaList& arenaLists(AllocKind i) { return arenaLists_.ref()[i]; }
|
||||
const ArenaList& arenaLists(AllocKind i) const { return arenaLists_.ref()[i]; }
|
||||
|
||||
enum BackgroundFinalizeStateEnum { BFS_DONE, BFS_RUN };
|
||||
|
||||
typedef mozilla::Atomic<BackgroundFinalizeStateEnum, mozilla::SequentiallyConsistent>
|
||||
BackgroundFinalizeState;
|
||||
|
||||
/* The current background finalization state, accessed atomically. */
|
||||
UnprotectedData<AllAllocKindArray<BackgroundFinalizeState>> backgroundFinalizeState_;
|
||||
BackgroundFinalizeState& backgroundFinalizeState(AllocKind i) { return backgroundFinalizeState_.ref()[i]; }
|
||||
const BackgroundFinalizeState& backgroundFinalizeState(AllocKind i) const { return backgroundFinalizeState_.ref()[i]; }
|
||||
|
||||
/* For each arena kind, a list of arenas remaining to be swept. */
|
||||
ActiveThreadOrGCTaskData<AllAllocKindArray<Arena*>> arenaListsToSweep_;
|
||||
Arena*& arenaListsToSweep(AllocKind i) { return arenaListsToSweep_.ref()[i]; }
|
||||
Arena* arenaListsToSweep(AllocKind i) const { return arenaListsToSweep_.ref()[i]; }
|
||||
|
||||
/* During incremental sweeping, a list of the arenas already swept. */
|
||||
ZoneGroupOrGCTaskData<AllocKind> incrementalSweptArenaKind;
|
||||
ZoneGroupOrGCTaskData<ArenaList> incrementalSweptArenas;
|
||||
|
||||
// Arena lists which have yet to be swept, but need additional foreground
|
||||
// processing before they are swept.
|
||||
ZoneGroupData<Arena*> gcShapeArenasToUpdate;
|
||||
ZoneGroupData<Arena*> gcAccessorShapeArenasToUpdate;
|
||||
ZoneGroupData<Arena*> gcScriptArenasToUpdate;
|
||||
ZoneGroupData<Arena*> gcObjectGroupArenasToUpdate;
|
||||
|
||||
// While sweeping type information, these lists save the arenas for the
|
||||
// objects which have already been finalized in the foreground (which must
|
||||
// happen at the beginning of the GC), so that type sweeping can determine
|
||||
// which of the object pointers are marked.
|
||||
ZoneGroupData<ObjectAllocKindArray<ArenaList>> savedObjectArenas_;
|
||||
ArenaList& savedObjectArenas(AllocKind i) { return savedObjectArenas_.ref()[i]; }
|
||||
ZoneGroupData<Arena*> savedEmptyObjectArenas;
|
||||
|
||||
public:
|
||||
explicit ArenaLists(JSRuntime* rt, ZoneGroup* group);
|
||||
~ArenaLists();
|
||||
|
||||
const void* addressOfFreeList(AllocKind thingKind) const {
|
||||
return reinterpret_cast<const void*>(&freeLists_.refNoCheck()[thingKind]);
|
||||
}
|
||||
|
||||
Arena* getFirstArena(AllocKind thingKind) const {
|
||||
return arenaLists(thingKind).head();
|
||||
}
|
||||
|
||||
Arena* getFirstArenaToSweep(AllocKind thingKind) const {
|
||||
return arenaListsToSweep(thingKind);
|
||||
}
|
||||
|
||||
Arena* getFirstSweptArena(AllocKind thingKind) const {
|
||||
if (thingKind != incrementalSweptArenaKind.ref())
|
||||
return nullptr;
|
||||
return incrementalSweptArenas.ref().head();
|
||||
}
|
||||
|
||||
Arena* getArenaAfterCursor(AllocKind thingKind) const {
|
||||
return arenaLists(thingKind).arenaAfterCursor();
|
||||
}
|
||||
|
||||
bool arenaListsAreEmpty() const {
|
||||
for (auto i : AllAllocKinds()) {
|
||||
/*
|
||||
* The arena cannot be empty if the background finalization is not yet
|
||||
* done.
|
||||
*/
|
||||
if (backgroundFinalizeState(i) != BFS_DONE)
|
||||
return false;
|
||||
if (!arenaLists(i).isEmpty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void unmarkAll() {
|
||||
for (auto i : AllAllocKinds()) {
|
||||
/* The background finalization must have stopped at this point. */
|
||||
MOZ_ASSERT(backgroundFinalizeState(i) == BFS_DONE);
|
||||
for (Arena* arena = arenaLists(i).head(); arena; arena = arena->next)
|
||||
arena->unmarkAll();
|
||||
}
|
||||
}
|
||||
|
||||
bool doneBackgroundFinalize(AllocKind kind) const {
|
||||
return backgroundFinalizeState(kind) == BFS_DONE;
|
||||
}
|
||||
|
||||
bool needBackgroundFinalizeWait(AllocKind kind) const {
|
||||
return backgroundFinalizeState(kind) != BFS_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the free lists so we won't try to allocate from swept arenas.
|
||||
*/
|
||||
void purge() {
|
||||
for (auto i : AllAllocKinds())
|
||||
freeLists(i) = &placeholder;
|
||||
}
|
||||
|
||||
inline void prepareForIncrementalGC();
|
||||
|
||||
/* Check if this arena is in use. */
|
||||
bool arenaIsInUse(Arena* arena, AllocKind kind) const {
|
||||
MOZ_ASSERT(arena);
|
||||
return arena == freeLists(kind)->getArenaUnchecked();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
|
||||
return freeLists(thingKind)->allocate(thingSize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves all arenas from |fromArenaLists| into |this|.
|
||||
*/
|
||||
void adoptArenas(JSRuntime* runtime, ArenaLists* fromArenaLists, bool targetZoneIsCollecting);
|
||||
|
||||
/* True if the Arena in question is found in this ArenaLists */
|
||||
bool containsArena(JSRuntime* runtime, Arena* arena);
|
||||
|
||||
void checkEmptyFreeLists() {
|
||||
#ifdef DEBUG
|
||||
for (auto i : AllAllocKinds())
|
||||
checkEmptyFreeList(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool checkEmptyArenaLists() {
|
||||
bool empty = true;
|
||||
#ifdef DEBUG
|
||||
for (auto i : AllAllocKinds()) {
|
||||
if (!checkEmptyArenaList(i))
|
||||
empty = false;
|
||||
}
|
||||
#endif
|
||||
return empty;
|
||||
}
|
||||
|
||||
void checkEmptyFreeList(AllocKind kind) {
|
||||
MOZ_ASSERT(freeLists(kind)->isEmpty());
|
||||
}
|
||||
|
||||
bool checkEmptyArenaList(AllocKind kind);
|
||||
|
||||
bool relocateArenas(JS::Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
|
||||
js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
|
||||
|
||||
void queueForegroundObjectsForSweep(FreeOp* fop);
|
||||
void queueForegroundThingsForSweep(FreeOp* fop);
|
||||
|
||||
void mergeForegroundSweptObjectArenas();
|
||||
|
||||
bool foregroundFinalize(FreeOp* fop, AllocKind thingKind, js::SliceBudget& sliceBudget,
|
||||
SortedArenaList& sweepList);
|
||||
static void backgroundFinalize(FreeOp* fop, Arena* listHead, Arena** empty);
|
||||
|
||||
// When finalizing arenas, whether to keep empty arenas on the list or
|
||||
// release them immediately.
|
||||
enum KeepArenasEnum {
|
||||
RELEASE_ARENAS,
|
||||
KEEP_ARENAS
|
||||
};
|
||||
|
||||
private:
|
||||
inline void queueForForegroundSweep(FreeOp* fop, const FinalizePhase& phase);
|
||||
inline void queueForBackgroundSweep(FreeOp* fop, const FinalizePhase& phase);
|
||||
inline void queueForForegroundSweep(FreeOp* fop, AllocKind thingKind);
|
||||
inline void queueForBackgroundSweep(FreeOp* fop, AllocKind thingKind);
|
||||
inline void mergeSweptArenas(AllocKind thingKind);
|
||||
|
||||
TenuredCell* allocateFromArena(JS::Zone* zone, AllocKind thingKind,
|
||||
ShouldCheckThresholds checkThresholds);
|
||||
inline TenuredCell* allocateFromArenaInner(JS::Zone* zone, Arena* arena, AllocKind kind);
|
||||
|
||||
friend class GCRuntime;
|
||||
friend class js::Nursery;
|
||||
friend class js::TenuringTracer;
|
||||
};
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* gc_ArenaList_h */
|
||||
|
|
@ -9,13 +9,14 @@
|
|||
|
||||
#include "NamespaceImports.h"
|
||||
#include "ds/Bitmap.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "threading/ProtectedData.h"
|
||||
#include "vm/Symbol.h"
|
||||
|
||||
namespace js {
|
||||
namespace gc {
|
||||
|
||||
class Arena;
|
||||
|
||||
// This class manages state used for marking atoms during GCs.
|
||||
// See AtomMarking.cpp for details.
|
||||
class AtomMarkingRuntime
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#include "gc/Heap.h"
|
||||
#include "gc/Cell.h"
|
||||
#include "gc/StoreBuffer.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/Id.h"
|
||||
|
|
|
@ -0,0 +1,412 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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 gc_Cell_h
|
||||
#define gc_Cell_h
|
||||
|
||||
#include "gc/GCEnum.h"
|
||||
#include "gc/Heap.h"
|
||||
#include "js/GCAnnotations.h"
|
||||
|
||||
namespace JS {
|
||||
|
||||
namespace shadow {
|
||||
struct Zone;
|
||||
} /* namespace shadow */
|
||||
|
||||
enum class TraceKind;
|
||||
struct Zone;
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
||||
class GenericPrinter;
|
||||
|
||||
extern bool
|
||||
RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// Barriers can't be triggered during backend Ion compilation, which may run on
|
||||
// a helper thread.
|
||||
extern bool
|
||||
CurrentThreadIsIonCompiling();
|
||||
#endif
|
||||
|
||||
extern void
|
||||
TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name);
|
||||
|
||||
namespace gc {
|
||||
class Arena;
|
||||
enum class AllocKind;
|
||||
struct Chunk;
|
||||
class TenuredCell;
|
||||
|
||||
// A GC cell is the base class for all GC things.
|
||||
struct Cell
|
||||
{
|
||||
public:
|
||||
MOZ_ALWAYS_INLINE bool isTenured() const { return !IsInsideNursery(this); }
|
||||
MOZ_ALWAYS_INLINE const TenuredCell& asTenured() const;
|
||||
MOZ_ALWAYS_INLINE TenuredCell& asTenured();
|
||||
|
||||
MOZ_ALWAYS_INLINE bool isMarkedAny() const;
|
||||
MOZ_ALWAYS_INLINE bool isMarkedBlack() const;
|
||||
MOZ_ALWAYS_INLINE bool isMarkedGray() const;
|
||||
|
||||
inline JSRuntime* runtimeFromActiveCooperatingThread() const;
|
||||
|
||||
// Note: Unrestricted access to the runtime of a GC thing from an arbitrary
|
||||
// thread can easily lead to races. Use this method very carefully.
|
||||
inline JSRuntime* runtimeFromAnyThread() const;
|
||||
|
||||
// May be overridden by GC thing kinds that have a compartment pointer.
|
||||
inline JSCompartment* maybeCompartment() const { return nullptr; }
|
||||
|
||||
// The StoreBuffer used to record incoming pointers from the tenured heap.
|
||||
// This will return nullptr for a tenured cell.
|
||||
inline StoreBuffer* storeBuffer() const;
|
||||
|
||||
inline JS::TraceKind getTraceKind() const;
|
||||
|
||||
static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone);
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool isAligned() const;
|
||||
void dump(GenericPrinter& out) const;
|
||||
void dump() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
uintptr_t address() const;
|
||||
inline Chunk* chunk() const;
|
||||
} JS_HAZ_GC_THING;
|
||||
|
||||
// A GC TenuredCell gets behaviors that are valid for things in the Tenured
|
||||
// heap, such as access to the arena and mark bits.
|
||||
class TenuredCell : public Cell
|
||||
{
|
||||
public:
|
||||
// Construct a TenuredCell from a void*, making various sanity assertions.
|
||||
static MOZ_ALWAYS_INLINE TenuredCell* fromPointer(void* ptr);
|
||||
static MOZ_ALWAYS_INLINE const TenuredCell* fromPointer(const void* ptr);
|
||||
|
||||
// Mark bit management.
|
||||
MOZ_ALWAYS_INLINE bool isMarkedAny() const;
|
||||
MOZ_ALWAYS_INLINE bool isMarkedBlack() const;
|
||||
MOZ_ALWAYS_INLINE bool isMarkedGray() const;
|
||||
|
||||
// The return value indicates if the cell went from unmarked to marked.
|
||||
MOZ_ALWAYS_INLINE bool markIfUnmarked(MarkColor color = MarkColor::Black) const;
|
||||
MOZ_ALWAYS_INLINE void markBlack() const;
|
||||
MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
|
||||
|
||||
// Access to the arena.
|
||||
inline Arena* arena() const;
|
||||
inline AllocKind getAllocKind() const;
|
||||
inline JS::TraceKind getTraceKind() const;
|
||||
inline JS::Zone* zone() const;
|
||||
inline JS::Zone* zoneFromAnyThread() const;
|
||||
inline bool isInsideZone(JS::Zone* zone) const;
|
||||
|
||||
MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZone() const {
|
||||
return JS::shadow::Zone::asShadowZone(zone());
|
||||
}
|
||||
MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
|
||||
return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void readBarrier(TenuredCell* thing);
|
||||
static MOZ_ALWAYS_INLINE void writeBarrierPre(TenuredCell* thing);
|
||||
|
||||
static void MOZ_ALWAYS_INLINE writeBarrierPost(void* cellp, TenuredCell* prior,
|
||||
TenuredCell* next);
|
||||
|
||||
// Default implementation for kinds that don't require fixup.
|
||||
void fixupAfterMovingGC() {}
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool isAligned() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
MOZ_ALWAYS_INLINE const TenuredCell&
|
||||
Cell::asTenured() const
|
||||
{
|
||||
MOZ_ASSERT(isTenured());
|
||||
return *static_cast<const TenuredCell*>(this);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE TenuredCell&
|
||||
Cell::asTenured()
|
||||
{
|
||||
MOZ_ASSERT(isTenured());
|
||||
return *static_cast<TenuredCell*>(this);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
Cell::isMarkedAny() const
|
||||
{
|
||||
return !isTenured() || asTenured().isMarkedAny();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
Cell::isMarkedBlack() const
|
||||
{
|
||||
return !isTenured() || asTenured().isMarkedBlack();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
Cell::isMarkedGray() const
|
||||
{
|
||||
return isTenured() && asTenured().isMarkedGray();
|
||||
}
|
||||
|
||||
inline JSRuntime*
|
||||
Cell::runtimeFromActiveCooperatingThread() const
|
||||
{
|
||||
JSRuntime* rt = chunk()->trailer.runtime;
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
return rt;
|
||||
}
|
||||
|
||||
inline JSRuntime*
|
||||
Cell::runtimeFromAnyThread() const
|
||||
{
|
||||
return chunk()->trailer.runtime;
|
||||
}
|
||||
|
||||
inline uintptr_t
|
||||
Cell::address() const
|
||||
{
|
||||
uintptr_t addr = uintptr_t(this);
|
||||
MOZ_ASSERT(addr % CellAlignBytes == 0);
|
||||
MOZ_ASSERT(Chunk::withinValidRange(addr));
|
||||
return addr;
|
||||
}
|
||||
|
||||
Chunk*
|
||||
Cell::chunk() const
|
||||
{
|
||||
uintptr_t addr = uintptr_t(this);
|
||||
MOZ_ASSERT(addr % CellAlignBytes == 0);
|
||||
addr &= ~ChunkMask;
|
||||
return reinterpret_cast<Chunk*>(addr);
|
||||
}
|
||||
|
||||
inline StoreBuffer*
|
||||
Cell::storeBuffer() const
|
||||
{
|
||||
return chunk()->trailer.storeBuffer;
|
||||
}
|
||||
|
||||
inline JS::TraceKind
|
||||
Cell::getTraceKind() const
|
||||
{
|
||||
return isTenured() ? asTenured().getTraceKind() : JS::TraceKind::Object;
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE bool
|
||||
Cell::needWriteBarrierPre(JS::Zone* zone) {
|
||||
return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE TenuredCell*
|
||||
TenuredCell::fromPointer(void* ptr)
|
||||
{
|
||||
MOZ_ASSERT(static_cast<TenuredCell*>(ptr)->isTenured());
|
||||
return static_cast<TenuredCell*>(ptr);
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE const TenuredCell*
|
||||
TenuredCell::fromPointer(const void* ptr)
|
||||
{
|
||||
MOZ_ASSERT(static_cast<const TenuredCell*>(ptr)->isTenured());
|
||||
return static_cast<const TenuredCell*>(ptr);
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::isMarkedAny() const
|
||||
{
|
||||
MOZ_ASSERT(arena()->allocated());
|
||||
return chunk()->bitmap.isMarkedAny(this);
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::isMarkedBlack() const
|
||||
{
|
||||
MOZ_ASSERT(arena()->allocated());
|
||||
return chunk()->bitmap.isMarkedBlack(this);
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::isMarkedGray() const
|
||||
{
|
||||
MOZ_ASSERT(arena()->allocated());
|
||||
return chunk()->bitmap.isMarkedGray(this);
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::markIfUnmarked(MarkColor color /* = Black */) const
|
||||
{
|
||||
return chunk()->bitmap.markIfUnmarked(this, color);
|
||||
}
|
||||
|
||||
void
|
||||
TenuredCell::markBlack() const
|
||||
{
|
||||
chunk()->bitmap.markBlack(this);
|
||||
}
|
||||
|
||||
void
|
||||
TenuredCell::copyMarkBitsFrom(const TenuredCell* src)
|
||||
{
|
||||
ChunkBitmap& bitmap = chunk()->bitmap;
|
||||
bitmap.copyMarkBit(this, src, ColorBit::BlackBit);
|
||||
bitmap.copyMarkBit(this, src, ColorBit::GrayOrBlackBit);
|
||||
}
|
||||
|
||||
inline Arena*
|
||||
TenuredCell::arena() const
|
||||
{
|
||||
MOZ_ASSERT(isTenured());
|
||||
uintptr_t addr = address();
|
||||
addr &= ~ArenaMask;
|
||||
return reinterpret_cast<Arena*>(addr);
|
||||
}
|
||||
|
||||
AllocKind
|
||||
TenuredCell::getAllocKind() const
|
||||
{
|
||||
return arena()->getAllocKind();
|
||||
}
|
||||
|
||||
JS::TraceKind
|
||||
TenuredCell::getTraceKind() const
|
||||
{
|
||||
return MapAllocToTraceKind(getAllocKind());
|
||||
}
|
||||
|
||||
JS::Zone*
|
||||
TenuredCell::zone() const
|
||||
{
|
||||
JS::Zone* zone = arena()->zone;
|
||||
MOZ_ASSERT(CurrentThreadCanAccessZone(zone));
|
||||
return zone;
|
||||
}
|
||||
|
||||
JS::Zone*
|
||||
TenuredCell::zoneFromAnyThread() const
|
||||
{
|
||||
return arena()->zone;
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::isInsideZone(JS::Zone* zone) const
|
||||
{
|
||||
return zone == arena()->zone;
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE void
|
||||
TenuredCell::readBarrier(TenuredCell* thing)
|
||||
{
|
||||
MOZ_ASSERT(!CurrentThreadIsIonCompiling());
|
||||
MOZ_ASSERT(thing);
|
||||
MOZ_ASSERT(CurrentThreadCanAccessZone(thing->zoneFromAnyThread()));
|
||||
|
||||
// It would be good if barriers were never triggered during collection, but
|
||||
// at the moment this can happen e.g. when rekeying tables containing
|
||||
// read-barriered GC things after a moving GC.
|
||||
//
|
||||
// TODO: Fix this and assert we're not collecting if we're on the active
|
||||
// thread.
|
||||
|
||||
JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
|
||||
if (shadowZone->needsIncrementalBarrier()) {
|
||||
// Barriers are only enabled on the active thread and are disabled while collecting.
|
||||
MOZ_ASSERT(!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone));
|
||||
Cell* tmp = thing;
|
||||
TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
|
||||
MOZ_ASSERT(tmp == thing);
|
||||
}
|
||||
|
||||
if (thing->isMarkedGray()) {
|
||||
// There shouldn't be anything marked grey unless we're on the active thread.
|
||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()));
|
||||
if (!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone))
|
||||
JS::UnmarkGrayGCThingRecursively(JS::GCCellPtr(thing, thing->getTraceKind()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AssertSafeToSkipBarrier(TenuredCell* thing);
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE void
|
||||
TenuredCell::writeBarrierPre(TenuredCell* thing)
|
||||
{
|
||||
MOZ_ASSERT(!CurrentThreadIsIonCompiling());
|
||||
if (!thing)
|
||||
return;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
// When verifying pre barriers we need to switch on all barriers, even
|
||||
// those on the Atoms Zone. Normally, we never enter a parse task when
|
||||
// collecting in the atoms zone, so will filter out atoms below.
|
||||
// Unfortuantely, If we try that when verifying pre-barriers, we'd never be
|
||||
// able to handle off thread parse tasks at all as we switch on the verifier any
|
||||
// time we're not doing GC. This would cause us to deadlock, as off thread parsing
|
||||
// is meant to resume after GC work completes. Instead we filter out any
|
||||
// off thread barriers that reach us and assert that they would normally not be
|
||||
// possible.
|
||||
if (!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread())) {
|
||||
AssertSafeToSkipBarrier(thing);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
|
||||
if (shadowZone->needsIncrementalBarrier()) {
|
||||
MOZ_ASSERT(!RuntimeFromActiveCooperatingThreadIsHeapMajorCollecting(shadowZone));
|
||||
Cell* tmp = thing;
|
||||
TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "pre barrier");
|
||||
MOZ_ASSERT(tmp == thing);
|
||||
}
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
AssertValidToSkipBarrier(TenuredCell* thing)
|
||||
{
|
||||
MOZ_ASSERT(!IsInsideNursery(thing));
|
||||
MOZ_ASSERT_IF(thing, MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::Object);
|
||||
}
|
||||
|
||||
/* static */ MOZ_ALWAYS_INLINE void
|
||||
TenuredCell::writeBarrierPost(void* cellp, TenuredCell* prior, TenuredCell* next)
|
||||
{
|
||||
AssertValidToSkipBarrier(next);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
Cell::isAligned() const
|
||||
{
|
||||
if (!isTenured())
|
||||
return true;
|
||||
return asTenured().isAligned();
|
||||
}
|
||||
|
||||
bool
|
||||
TenuredCell::isAligned() const
|
||||
{
|
||||
return Arena::isAligned(address(), arena()->getThingSize());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} /* namespace gc */
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* gc_Cell_h */
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче