mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:34:03 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- bit of Bug 1219134: P2. Use LibAV/FFmpeg logic to detect invalid pts. r=edwin (34d2a7d442)
- Bug 1195018 - Support 'background_color' member in the manifest processor. r=marcosc (1451b14321)
- Bug 1240490 - Fix console prefix used by the App Manifest processor. r=marcosc (e3196b53c8)
- Bug 1086997 - Localize developer warnings issued by the manifest processor. r=baku (7659fc5dd6)
- Bug 1258899 - teach manifest processor about dir member. r=baku (00b40c51c2)
- Bug 1262739 - Remove support for splash_screens member in Manifest Processor r=mconley (bceb0d2517)
- Bug 1264813 - Remove image object's density member from Web Manifest processor. r=mconley (f34da7259e)
- Bug 1264816 - Drop background_color from Web manifest image object. r=mconley (40c55f7bf9)
- Bug 1186908 - Return manifest members to canonical form after processing. r=mconley. (59d9a12a17)
- Bug 293394 - javascript: should never execute with chrome privs. r=bz (0dcc8f146b)
- bit of Bug 1153267 - part 2 (a0c5f7fda8)
- Bug 1254320 - Remove SEC_NORMAL from dom/json/ . r=bz (cd068db51c)
- Bug 1262590 - [e10s] AppProcessChecker.cpp has DEUBG typo preventing DEBUG messages. r=mconley (f9ffab4e57)
- Bug 1263556 - Remove EmptyBlobImpl in dom/ipc/Blob.cpp, r=smaug (3f4fe63e05)
- Bug 1261072 - File::GetName() should be const, r=smaug (bccd7475f1)
- Bug 1257759 part.1 Use switch-case at the first message handling in PluginInstanceChild::PluginWindowProcInternal() r=m_kato (bc0d4c457e)
- Bug 1257759 part.2 Separate Windows' message and related definitions from nsWindowDefs.h to mozilla/widget/WinMessages.h r=jimm (f8b5cb62f8)
- Bug 1153829 - Don't use MOZ_ALWAYS_INLINE for non-inline functions. r=jimm (c0e5b26c35)
- Bug 1257759 part.3 ModifierKeyState should be available in plugin module r=jimm (aa3fc81f63)
- Bug 1257759 part.4 Rename WidgetGUIEvent::PluginEvent to NativeEventData for using this class to send native event from plugin process to content and/or chrome process r=smaug (49392203ad)
- Bug 1255968 - Part 1: Let the callback of PeekMessages() to return a boolean to be able to break out the loop. r=dvander (d7f926ee7e)
- Bug 1255968 - Part 2: Implement puppet widget's HasPendingInputEvent() for interruptible reflow to work in content process. r=bz (5a0915e650)
- Bug 1257759 part.5 PluginInstanceChild should post received native key event to chrome process if the key combination may be a shortcut key r=jimm (ad1e3ad1c0)
- Bug 1252152 - Make plugin instances destroyed while that instance is on the stack crash earlier and more usefully, r=jimm (2238cc2a79)
- Bug 1257759 part.6 Keep event order between keyboard events and IME events in a plugin process r=jimm (32cbe1b13e)
- Bug 1257759 part.7 Add new internal events which represent key events on plugin r=smaug (161725755a)
- Bug 1257759 part.8 nsXBLWindowKeyHandler should handle eKeyDownOnPlugin and eKeyUpOnPlugin events only with reserved shortcut key handlers r=smaug (aa7cd1d786)
- Bug 1257759 part.9 Implement nsWindow::OnKeyEventInPluginProcess() on Windows r=jimm (245b2709e4)
- Bug 1257759 part.10 PluginInstanceChild should consume WM_*CHAR messages which follow consumed WM_*KEYDOWN or WM_*KEYUP message r=jimm (db355e6a77)
- Bug 1261735 (part 1) - Overhaul the atom implementation. r=froydnj,erahm. (abbed483f7)
- Bug 1261735 (part 2) - Inline some {Dynamic,Static}Atom methods. r=erahm. (97b11fa656)
- Bug 1261735 (part 3) - De-virtualize nsIAtom::IsStaticAtom(). r=froydnj,erahm. (4e0465a84a)
- Bug 1257207 - Increase ATOM_HASHTABLE_INITIAL_LENGTH. r=froydnj. (fe663e9c77)
- Bug 1261735 (part 4) - Change StaticAtomEntry::mAtom to |StaticAtom*|. r=erahm. (999fff7b8c)
- Bug 1261744 - Add two missing null checks for nsStringBuffer::Alloc(). r=erahm. (dd9ef00b6a)
- Bug 1266295 - Remove unnecessary compiler version checks for gcc <= 4.8.0 in xpcom. r=froydnj (b2f17f8d1a)
- Bug 1259706: Add NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING macro. r=froydnj (d56a5fd48b)
- just a space (1508ec768f)
- Bug 1236991 - part 1: allow forwarding label direction through nsITooltipTextProvider, r=enndeakin (8d49127ffa)
- Bug 1245649: Enable no-nested-ternary. r=mconley (43c7b99372)
- Bug 1236991 - part 2: implement a default tooltiptextprovider in toolkit, r=enndeakin (a0a378979f)
- Bug 1251032 - Send RenderFrame info down to child in BrowserFrameOpenWindow. r=kanru (aec01fcec9)
- Bug 1246327 - Remove dom.always_allow_move_resize_window preference. r=jst (d06d83d596)
- Bug 1158228 - merge github's readability code into m-c, rs=me (38da823ce7)
- Bug 1158184 - merge recent github readability changes into m-c, rs=me (3b634d63c8)
- Bug 1162917 - update readability from github repo, rs=me (29dd18a9f0)
- No bug - update readability from github repo, includes fix for bug 1230050, rs=uplift-with-r+-patches-from-github (fa04927c71)
- Bug 1265866 - update Readability to the latest version from github, rs=me (c89e4dbeac)
- bits of Bug 1158228 (950dd3d561)
- Bug 1249579 - part2 : audio competing suspend/resume methods. r=snorp, baku. (3b6ddc018d)
- Bug 1266221 P1 Get devtools http service worker testing option from top window. r=bz (e8492118f0)
- Bug 225910 - Use nsIURI's GetRef and GetHasRef in nsDocShell. r=bz (76da6473d4)
- Bug 1261471: Remove support for getting mozIDOMWindowProxy via GetInterface. r=mrbkap (04904ed142)
- Bug 1264725 - Isolate Troubleshoot.jsm (about:support) from addons with null names. r=felipe (4ebb240827)
- Bug 1227730 - Support closing libnotify alerts. r=karlt,MattN (73d8b583dc)
This commit is contained in:
@@ -428,6 +428,8 @@
|
||||
@RESPATH@/components/nsLoginInfo.js
|
||||
@RESPATH@/components/nsLoginManager.js
|
||||
@RESPATH@/components/nsLoginManagerPrompter.js
|
||||
@RESPATH@/components/TooltipTextProvider.js
|
||||
@RESPATH@/components/TooltipTextProvider.manifest
|
||||
@RESPATH@/components/NetworkGeolocationProvider.manifest
|
||||
@RESPATH@/components/NetworkGeolocationProvider.js
|
||||
@RESPATH@/components/TVSimulatorService.js
|
||||
|
||||
@@ -413,6 +413,8 @@
|
||||
@RESPATH@/components/nsLoginManagerPrompter.js
|
||||
@RESPATH@/components/storage-json.js
|
||||
@RESPATH@/components/crypto-SDR.js
|
||||
@RESPATH@/components/TooltipTextProvider.js
|
||||
@RESPATH@/components/TooltipTextProvider.manifest
|
||||
@RESPATH@/components/jsconsole-clhandler.manifest
|
||||
@RESPATH@/components/jsconsole-clhandler.js
|
||||
#ifdef MOZ_DEVTOOLS
|
||||
|
||||
@@ -10028,31 +10028,44 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_LINK) {
|
||||
nsCOMPtr<nsIURI> currentURI = mCurrentURI;
|
||||
// Split currentURI and aURI on the '#' character. Make sure we read
|
||||
// the return values of SplitURIAtHash; if it fails, we don't want to
|
||||
// allow a short-circuited navigation.
|
||||
nsAutoCString curBeforeHash, curHash, newBeforeHash, newHash;
|
||||
nsresult splitRv1, splitRv2;
|
||||
splitRv1 = currentURI ?
|
||||
nsContentUtils::SplitURIAtHash(currentURI, curBeforeHash, curHash) :
|
||||
NS_ERROR_FAILURE;
|
||||
splitRv2 = nsContentUtils::SplitURIAtHash(aURI, newBeforeHash, newHash);
|
||||
|
||||
bool sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
|
||||
NS_SUCCEEDED(splitRv2) &&
|
||||
curBeforeHash.Equals(newBeforeHash);
|
||||
nsAutoCString curHash, newHash;
|
||||
bool curURIHasRef = false, newURIHasRef = false;
|
||||
|
||||
nsresult rvURINew = aURI->GetRef(newHash);
|
||||
if (NS_SUCCEEDED(rvURINew)) {
|
||||
rvURINew = aURI->GetHasRef(&newURIHasRef);
|
||||
}
|
||||
|
||||
bool sameExceptHashes = false;
|
||||
if (currentURI && NS_SUCCEEDED(rvURINew)) {
|
||||
nsresult rvURIOld = currentURI->GetRef(curHash);
|
||||
if (NS_SUCCEEDED(rvURIOld)) {
|
||||
rvURIOld = currentURI->GetHasRef(&curURIHasRef);
|
||||
}
|
||||
if (NS_SUCCEEDED(rvURIOld)) {
|
||||
if (NS_FAILED(currentURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
|
||||
sameExceptHashes = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sameExceptHashes && sURIFixup && currentURI &&
|
||||
NS_SUCCEEDED(splitRv2)) {
|
||||
NS_SUCCEEDED(rvURINew)) {
|
||||
// Maybe aURI came from the exposable form of currentURI?
|
||||
nsCOMPtr<nsIURI> currentExposableURI;
|
||||
rv = sURIFixup->CreateExposableURI(currentURI,
|
||||
getter_AddRefs(currentExposableURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
splitRv1 = nsContentUtils::SplitURIAtHash(currentExposableURI,
|
||||
curBeforeHash, curHash);
|
||||
sameExceptHashes =
|
||||
NS_SUCCEEDED(splitRv1) && curBeforeHash.Equals(newBeforeHash);
|
||||
nsresult rvURIOld = currentExposableURI->GetRef(curHash);
|
||||
if (NS_SUCCEEDED(rvURIOld)) {
|
||||
rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
|
||||
}
|
||||
if (NS_SUCCEEDED(rvURIOld)) {
|
||||
if (NS_FAILED(currentExposableURI->EqualsExceptRef(aURI, &sameExceptHashes))) {
|
||||
sameExceptHashes = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool historyNavBetweenSameDoc = false;
|
||||
@@ -10088,7 +10101,7 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
bool doShortCircuitedLoad =
|
||||
(historyNavBetweenSameDoc && mOSHE != aSHEntry) ||
|
||||
(!aSHEntry && !aPostData &&
|
||||
sameExceptHashes && !newHash.IsEmpty());
|
||||
sameExceptHashes && newURIHasRef);
|
||||
|
||||
if (doShortCircuitedLoad) {
|
||||
// Save the position of the scrollers.
|
||||
@@ -10243,7 +10256,7 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
// arguments it receives. But even if we don't end up scrolling,
|
||||
// ScrollToAnchor performs other important tasks, such as informing
|
||||
// the presShell that we have a new hash. See bug 680257.
|
||||
rv = ScrollToAnchor(curHash, newHash, aLoadType);
|
||||
rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash, aLoadType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
/* restore previous position of scroller(s), if we're moving
|
||||
@@ -10266,7 +10279,8 @@ nsDocShell::InternalLoad2(nsIURI* aURI,
|
||||
// reference to avoid null derefs. See bug 914521.
|
||||
if (win) {
|
||||
// Fire a hashchange event URIs differ, and only in their hashes.
|
||||
bool doHashchange = sameExceptHashes && !curHash.Equals(newHash);
|
||||
bool doHashchange = sameExceptHashes &&
|
||||
(curURIHasRef != newURIHasRef || !curHash.Equals(newHash));
|
||||
|
||||
if (historyNavBetweenSameDoc || doHashchange) {
|
||||
win->DispatchSyncPopState();
|
||||
@@ -11151,8 +11165,8 @@ nsDocShell::DoChannelLoad(nsIChannel* aChannel,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::ScrollToAnchor(nsACString& aCurHash, nsACString& aNewHash,
|
||||
uint32_t aLoadType)
|
||||
nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
|
||||
nsACString& aNewHash, uint32_t aLoadType)
|
||||
{
|
||||
if (!mCurrentURI) {
|
||||
return NS_OK;
|
||||
@@ -11174,25 +11188,20 @@ nsDocShell::ScrollToAnchor(nsACString& aCurHash, nsACString& aNewHash,
|
||||
// current anchor and we are doing a history load. So return if we have no
|
||||
// new anchor, and there is no current anchor or the load is not a history
|
||||
// load.
|
||||
if ((aCurHash.IsEmpty() || aLoadType != LOAD_HISTORY) &&
|
||||
aNewHash.IsEmpty()) {
|
||||
if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Take the '#' off aNewHash to get the ref name. (aNewHash might be empty,
|
||||
// but that's fine.)
|
||||
nsDependentCSubstring newHashName(aNewHash, 1);
|
||||
|
||||
// Both the new and current URIs refer to the same page. We can now
|
||||
// browse to the hash stored in the new URI.
|
||||
|
||||
if (!newHashName.IsEmpty()) {
|
||||
if (!aNewHash.IsEmpty()) {
|
||||
// anchor is there, but if it's a load from history,
|
||||
// we don't have any anchor jumping to do
|
||||
bool scroll = aLoadType != LOAD_HISTORY &&
|
||||
aLoadType != LOAD_RELOAD_NORMAL;
|
||||
|
||||
char* str = ToNewCString(newHashName);
|
||||
char* str = ToNewCString(aNewHash);
|
||||
if (!str) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -11234,7 +11243,7 @@ nsDocShell::ScrollToAnchor(nsACString& aCurHash, nsACString& aNewHash,
|
||||
nsXPIDLString uStr;
|
||||
|
||||
rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
|
||||
PromiseFlatCString(newHashName).get(),
|
||||
PromiseFlatCString(aNewHash).get(),
|
||||
getter_Copies(uStr));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
@@ -11244,7 +11253,7 @@ nsDocShell::ScrollToAnchor(nsACString& aCurHash, nsACString& aNewHash,
|
||||
// with the new URI no matter whether we actually scrolled
|
||||
// somewhere).
|
||||
//
|
||||
// When newHashName contains "%00", unescaped string may be empty.
|
||||
// When aNewHash contains "%00", unescaped string may be empty.
|
||||
// And GoToAnchor asserts if we ask it to scroll to an empty ref.
|
||||
shell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
|
||||
nsIPresShell::SCROLL_SMOOTH_AUTO);
|
||||
|
||||
@@ -377,7 +377,9 @@ protected:
|
||||
nsIURILoader* aURILoader,
|
||||
bool aBypassClassifier);
|
||||
|
||||
nsresult ScrollToAnchor(nsACString& aCurHash, nsACString& aNewHash,
|
||||
nsresult ScrollToAnchor(bool aCurHasRef,
|
||||
bool aNewHasRef,
|
||||
nsACString& aNewHash,
|
||||
uint32_t aLoadType);
|
||||
|
||||
// Returns true if would have called FireOnLocationChange,
|
||||
|
||||
@@ -397,7 +397,8 @@ AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
|
||||
}
|
||||
|
||||
*aVolume *= window->GetAudioVolume();
|
||||
*aMuted = *aMuted || window->GetAudioMuted();
|
||||
// TODO : distiguish between suspend and mute, it would be done in bug1242874.
|
||||
*aMuted = *aMuted || window->GetMediaSuspended() || window->GetAudioMuted();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> win = window->GetScriptableParent();
|
||||
if (window == win) {
|
||||
|
||||
+12
-2
@@ -456,7 +456,7 @@ File::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
void
|
||||
File::GetName(nsAString& aFileName)
|
||||
File::GetName(nsAString& aFileName) const
|
||||
{
|
||||
mImpl->GetName(aFileName);
|
||||
}
|
||||
@@ -675,7 +675,7 @@ NS_IMPL_ISUPPORTS(BlobImpl, BlobImpl)
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(BlobImplFile, BlobImpl)
|
||||
|
||||
void
|
||||
BlobImplBase::GetName(nsAString& aName)
|
||||
BlobImplBase::GetName(nsAString& aName) const
|
||||
{
|
||||
NS_ASSERTION(mIsFile, "Should only be called on files");
|
||||
aName = mName;
|
||||
@@ -951,6 +951,11 @@ EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
|
||||
{
|
||||
MOZ_ASSERT(!aStart && !aLength);
|
||||
RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(impl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
return impl.forget();
|
||||
}
|
||||
|
||||
@@ -958,6 +963,11 @@ void
|
||||
EmptyBlobImpl::GetInternalStream(nsIInputStream** aStream,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (NS_WARN_IF(!aStream)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = NS_NewCStringInputStream(aStream, EmptyCString());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
|
||||
+14
-4
@@ -224,7 +224,7 @@ public:
|
||||
const ChromeFilePropertyBag& aBag,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetName(nsAString& aName);
|
||||
void GetName(nsAString& aName) const;
|
||||
|
||||
int64_t GetLastModified(ErrorResult& aRv);
|
||||
|
||||
@@ -256,7 +256,7 @@ public:
|
||||
|
||||
BlobImpl() {}
|
||||
|
||||
virtual void GetName(nsAString& aName) = 0;
|
||||
virtual void GetName(nsAString& aName) const = 0;
|
||||
|
||||
virtual void GetPath(nsAString& aName, ErrorResult& aRv) = 0;
|
||||
|
||||
@@ -392,7 +392,7 @@ public:
|
||||
mContentType.SetIsVoid(false);
|
||||
}
|
||||
|
||||
virtual void GetName(nsAString& aName) override;
|
||||
virtual void GetName(nsAString& aName) const override;
|
||||
|
||||
virtual void GetPath(nsAString& aName, ErrorResult& aRv) override;
|
||||
|
||||
@@ -759,7 +759,17 @@ public:
|
||||
|
||||
explicit EmptyBlobImpl(const nsAString& aContentType)
|
||||
: BlobImplBase(aContentType, 0 /* aLength */)
|
||||
{}
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
EmptyBlobImpl(const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
int64_t aLastModifiedDate)
|
||||
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
virtual void GetInternalStream(nsIInputStream** aStream,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
@@ -5416,32 +5416,6 @@ nsContentUtils::URIIsLocalFile(nsIURI *aURI)
|
||||
isFile;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentUtils::SplitURIAtHash(nsIURI *aURI,
|
||||
nsACString &aBeforeHash,
|
||||
nsACString &aAfterHash)
|
||||
{
|
||||
// See bug 225910 for why we can't do this using nsIURL.
|
||||
|
||||
aBeforeHash.Truncate();
|
||||
aAfterHash.Truncate();
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
nsAutoCString spec;
|
||||
nsresult rv = aURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
int32_t index = spec.FindChar('#');
|
||||
if (index == -1) {
|
||||
index = spec.Length();
|
||||
}
|
||||
|
||||
aBeforeHash.Assign(Substring(spec, 0, index));
|
||||
aAfterHash.Assign(Substring(spec, index));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsIScriptContext*
|
||||
nsContentUtils::GetContextForEventHandlers(nsINode* aNode,
|
||||
|
||||
@@ -1537,14 +1537,6 @@ public:
|
||||
*/
|
||||
static bool URIIsLocalFile(nsIURI *aURI);
|
||||
|
||||
/**
|
||||
* Given a URI, return set beforeHash to the part before the '#', and
|
||||
* afterHash to the remainder of the URI, including the '#'.
|
||||
*/
|
||||
static nsresult SplitURIAtHash(nsIURI *aURI,
|
||||
nsACString &aBeforeHash,
|
||||
nsACString &aAfterHash);
|
||||
|
||||
/**
|
||||
* Get the application manifest URI for this document. The manifest URI
|
||||
* is specified in the manifest= attribute of the root element of the
|
||||
|
||||
@@ -3759,6 +3759,26 @@ nsDOMWindowUtils::PostRestyleSelfEvent(nsIDOMElement* aElement)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetMediaSuspended(bool* aSuspended)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_STATE(window);
|
||||
|
||||
*aSuspended = window->GetMediaSuspended();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SetMediaSuspended(bool aSuspended)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_STATE(window);
|
||||
|
||||
window->SetMediaSuspended(aSuspended);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetAudioMuted(bool* aMuted)
|
||||
{
|
||||
|
||||
@@ -831,6 +831,8 @@ GK_ATOM(onmozbrowserbeforekeydown, "onmozbrowserbeforekeydown")
|
||||
GK_ATOM(onmozbrowserbeforekeyup, "onmozbrowserbeforekeyup")
|
||||
GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
|
||||
GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
|
||||
GK_ATOM(onmozkeydownonplugin, "onmozkeydownonplugin")
|
||||
GK_ATOM(onmozkeyuponplugin, "onmozkeyuponplugin")
|
||||
GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")
|
||||
GK_ATOM(onmozpointerlockerror, "onmozpointerlockerror")
|
||||
GK_ATOM(onmoztimechange, "onmoztimechange")
|
||||
|
||||
+59
-11
@@ -618,7 +618,7 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow)
|
||||
mMayHavePointerEnterLeaveEventListener(false),
|
||||
mInnerObjectsFreed(false),
|
||||
mIsModalContentWindow(false),
|
||||
mIsActive(false), mIsBackground(false),
|
||||
mIsActive(false), mIsBackground(false), mMediaSuspended(false),
|
||||
mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
|
||||
mDesktopModeViewport(false), mInnerWindow(nullptr),
|
||||
mOuterWindow(aOuterWindow),
|
||||
@@ -3717,6 +3717,32 @@ nsPIDOMWindow::CreatePerformanceObjectIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsPIDOMWindow::GetMediaSuspended() const
|
||||
{
|
||||
if (IsInnerWindow()) {
|
||||
return mOuterWindow->GetMediaSuspended();
|
||||
}
|
||||
|
||||
return mMediaSuspended;
|
||||
}
|
||||
|
||||
void
|
||||
nsPIDOMWindow::SetMediaSuspended(bool aSuspended)
|
||||
{
|
||||
if (IsInnerWindow()) {
|
||||
mOuterWindow->SetMediaSuspended(aSuspended);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMediaSuspended == aSuspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
mMediaSuspended = aSuspended;
|
||||
RefreshMediaElements();
|
||||
}
|
||||
|
||||
bool
|
||||
nsPIDOMWindow::GetAudioMuted() const
|
||||
{
|
||||
@@ -3782,6 +3808,28 @@ nsPIDOMWindow::RefreshMediaElements()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPIDOMWindow::SetServiceWorkersTestingEnabled(bool aEnabled)
|
||||
{
|
||||
// Devtools should only be setting this on the top level window. Its
|
||||
// ok if devtools clears the flag on clean up of nested windows, though.
|
||||
// It will have no affect.
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsPIDOMWindow> topWindow = GetScriptableTop();
|
||||
MOZ_ASSERT_IF(aEnabled, this == topWindow);
|
||||
#endif
|
||||
mServiceWorkersTestingEnabled = aEnabled;
|
||||
}
|
||||
|
||||
bool
|
||||
nsPIDOMWindow::GetServiceWorkersTestingEnabled()
|
||||
{
|
||||
// Automatically get this setting from the top level window so that nested
|
||||
// iframes get the correct devtools setting.
|
||||
nsCOMPtr<nsPIDOMWindow> topWindow = GetScriptableTop();
|
||||
return topWindow->mServiceWorkersTestingEnabled;
|
||||
}
|
||||
|
||||
bool
|
||||
nsPIDOMWindow::GetAudioCaptured() const
|
||||
{
|
||||
@@ -6674,10 +6722,7 @@ nsGlobalWindow::CanMoveResizeWindows(bool aCallerIsChrome)
|
||||
}
|
||||
}
|
||||
|
||||
// The preference is useful for the webapp runtime. Webapps should be able
|
||||
// to resize or move their window.
|
||||
if (mDocShell && !Preferences::GetBool("dom.always_allow_move_resize_window",
|
||||
false)) {
|
||||
if (mDocShell) {
|
||||
bool allow;
|
||||
nsresult rv = mDocShell->GetAllowWindowControl(&allow);
|
||||
if (NS_SUCCEEDED(rv) && !allow)
|
||||
@@ -10404,12 +10449,15 @@ nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI)
|
||||
|
||||
// Make sure that aOldURI and aNewURI are identical up to the '#', and that
|
||||
// their hashes are different.
|
||||
nsAutoCString oldBeforeHash, oldHash, newBeforeHash, newHash;
|
||||
nsContentUtils::SplitURIAtHash(aOldURI, oldBeforeHash, oldHash);
|
||||
nsContentUtils::SplitURIAtHash(aNewURI, newBeforeHash, newHash);
|
||||
|
||||
NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash));
|
||||
NS_ENSURE_STATE(!oldHash.Equals(newHash));
|
||||
bool equal = false;
|
||||
NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->EqualsExceptRef(aNewURI, &equal)) && equal);
|
||||
nsAutoCString oldHash, newHash;
|
||||
bool oldHasHash, newHasHash;
|
||||
NS_ENSURE_STATE(NS_SUCCEEDED(aOldURI->GetRef(oldHash)) &&
|
||||
NS_SUCCEEDED(aNewURI->GetRef(newHash)) &&
|
||||
NS_SUCCEEDED(aOldURI->GetHasRef(&oldHasHash)) &&
|
||||
NS_SUCCEEDED(aNewURI->GetHasRef(&newHasHash)) &&
|
||||
(oldHasHash != newHasHash || !oldHash.Equals(newHash)));
|
||||
|
||||
nsAutoCString oldSpec, newSpec;
|
||||
aOldURI->GetSpec(oldSpec);
|
||||
|
||||
@@ -202,6 +202,9 @@ public:
|
||||
virtual bool IsRunningTimeout() = 0;
|
||||
|
||||
// Audio API
|
||||
bool GetMediaSuspended() const;
|
||||
void SetMediaSuspended(bool aSuspended);
|
||||
|
||||
bool GetAudioMuted() const;
|
||||
void SetAudioMuted(bool aMuted);
|
||||
|
||||
@@ -211,17 +214,8 @@ public:
|
||||
bool GetAudioCaptured() const;
|
||||
nsresult SetAudioCapture(bool aCapture);
|
||||
|
||||
virtual void SetServiceWorkersTestingEnabled(bool aEnabled)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
mServiceWorkersTestingEnabled = aEnabled;
|
||||
}
|
||||
|
||||
bool GetServiceWorkersTestingEnabled()
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
return mServiceWorkersTestingEnabled;
|
||||
}
|
||||
void SetServiceWorkersTestingEnabled(bool aEnabled);
|
||||
bool GetServiceWorkersTestingEnabled();
|
||||
|
||||
already_AddRefed<mozilla::dom::ServiceWorkerRegistrationMainThread>
|
||||
GetServiceWorkerRegistration(const nsAString& aScope);
|
||||
@@ -850,6 +844,7 @@ protected:
|
||||
// "active". Only used on outer windows.
|
||||
bool mIsBackground;
|
||||
|
||||
bool mMediaSuspended;
|
||||
bool mAudioMuted;
|
||||
float mAudioVolume;
|
||||
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
#include "nsVariant.h"
|
||||
#include "mozilla/dom/BrowserElementDictionariesBinding.h"
|
||||
#include "mozilla/dom/CustomEvent.h"
|
||||
#include "mozilla/layout/RenderFrameParent.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::layout;
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -205,9 +208,12 @@ BrowserElementParent::DispatchOpenWindowEvent(Element* aOpenerFrameElement,
|
||||
BrowserElementParent::OpenWindowResult
|
||||
BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
|
||||
TabParent* aPopupTabParent,
|
||||
PRenderFrameParent* aRenderFrame,
|
||||
const nsAString& aURL,
|
||||
const nsAString& aName,
|
||||
const nsAString& aFeatures)
|
||||
const nsAString& aFeatures,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId)
|
||||
{
|
||||
// Create an iframe owned by the same document which owns openerFrameElement.
|
||||
nsCOMPtr<Element> openerFrameElement = aOpenerTabParent->GetOwnerElement();
|
||||
@@ -241,6 +247,13 @@ BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
|
||||
aPopupTabParent->SetOwnerElement(popupFrameElement);
|
||||
popupFrameElement->AllowCreateFrameLoader();
|
||||
popupFrameElement->CreateRemoteFrameLoader(aPopupTabParent);
|
||||
|
||||
RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
|
||||
if (!aPopupTabParent->SetRenderFrame(rfp) ||
|
||||
!aPopupTabParent->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
|
||||
return BrowserElementParent::OPEN_WINDOW_IGNORED;
|
||||
}
|
||||
|
||||
return opened;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@ namespace dom {
|
||||
class TabParent;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
struct TextureFactoryIdentifier;
|
||||
} // namespace layers
|
||||
|
||||
namespace layout {
|
||||
class PRenderFrameParent;
|
||||
} // namespace layout
|
||||
|
||||
/**
|
||||
* BrowserElementParent implements a portion of the parent-process side of
|
||||
* <iframe mozbrowser>.
|
||||
@@ -87,9 +95,12 @@ public:
|
||||
static OpenWindowResult
|
||||
OpenWindowOOP(dom::TabParent* aOpenerTabParent,
|
||||
dom::TabParent* aPopupTabParent,
|
||||
layout::PRenderFrameParent* aRenderFrame,
|
||||
const nsAString& aURL,
|
||||
const nsAString& aName,
|
||||
const nsAString& aFeatures);
|
||||
const nsAString& aFeatures,
|
||||
layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId);
|
||||
|
||||
/**
|
||||
* Handle a window.open call from an in-process <iframe mozbrowser>.
|
||||
|
||||
@@ -238,6 +238,14 @@ EVENT(keyup,
|
||||
eKeyUp,
|
||||
EventNameType_HTMLXUL,
|
||||
eKeyboardEventClass)
|
||||
EVENT(mozkeydownonplugin,
|
||||
eKeyDownOnPlugin,
|
||||
EventNameType_None,
|
||||
eKeyboardEventClass)
|
||||
EVENT(mozkeyuponplugin,
|
||||
eKeyUpOnPlugin,
|
||||
EventNameType_None,
|
||||
eKeyboardEventClass)
|
||||
NON_IDL_EVENT(mozbrowserbeforekeydown,
|
||||
eBeforeKeyDown,
|
||||
EventNameType_None,
|
||||
|
||||
@@ -190,9 +190,11 @@ KeyboardEvent::CharCode()
|
||||
switch (mEvent->mMessage) {
|
||||
case eBeforeKeyDown:
|
||||
case eKeyDown:
|
||||
case eKeyDownOnPlugin:
|
||||
case eAfterKeyDown:
|
||||
case eBeforeKeyUp:
|
||||
case eKeyUp:
|
||||
case eKeyUpOnPlugin:
|
||||
case eAfterKeyUp:
|
||||
return 0;
|
||||
case eKeyPress:
|
||||
@@ -236,9 +238,11 @@ KeyboardEvent::Which()
|
||||
switch (mEvent->mMessage) {
|
||||
case eBeforeKeyDown:
|
||||
case eKeyDown:
|
||||
case eKeyDownOnPlugin:
|
||||
case eAfterKeyDown:
|
||||
case eBeforeKeyUp:
|
||||
case eKeyUp:
|
||||
case eKeyUpOnPlugin:
|
||||
case eAfterKeyUp:
|
||||
return KeyCode();
|
||||
case eKeyPress:
|
||||
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
|
||||
// BlobImpl
|
||||
virtual void
|
||||
GetName(nsAString& aName) override
|
||||
GetName(nsAString& aName) const override
|
||||
{
|
||||
mBlobImpl->GetName(aName);
|
||||
}
|
||||
|
||||
@@ -1743,6 +1743,12 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
void postRestyleSelfEvent(in nsIDOMElement aElement);
|
||||
|
||||
/**
|
||||
* Used to pause or resume all MediaElements in this window. Use-cases are
|
||||
* audio competing and remote media control.
|
||||
*/
|
||||
attribute boolean mediaSuspended;
|
||||
|
||||
/**
|
||||
* With this it's possible to mute all the MediaElements in this window.
|
||||
* We have audioMuted and audioVolume to preserve the volume across
|
||||
|
||||
@@ -19,6 +19,8 @@ interface nsITabChild : nsISupports
|
||||
|
||||
[notxpcom] void sendRequestFocus(in boolean canFocus);
|
||||
|
||||
[notxpcom] void sendGetTabCount(out uint32_t tabCount);
|
||||
|
||||
[noscript, notxpcom] void enableDisableCommands(in AString action,
|
||||
in CommandsArrayRef enabledCommands,
|
||||
in CommandsArrayRef disabledCommands);
|
||||
|
||||
@@ -36,8 +36,8 @@ class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if DEUBG
|
||||
#define LOG(args...) printf_stderr(args)
|
||||
#if DEBUG
|
||||
#define LOG(...) printf_stderr(__VA_ARGS__)
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
+2
-54
@@ -657,58 +657,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class EmptyBlobImpl final
|
||||
: public BlobImplBase
|
||||
{
|
||||
public:
|
||||
explicit EmptyBlobImpl(const nsAString& aContentType)
|
||||
: BlobImplBase(aContentType, 0)
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
EmptyBlobImpl(const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
int64_t aLastModifiedDate)
|
||||
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual already_AddRefed<BlobImpl>
|
||||
CreateSlice(uint64_t /* aStart */,
|
||||
uint64_t aLength,
|
||||
const nsAString& aContentType,
|
||||
ErrorResult& /* aRv */) override
|
||||
{
|
||||
MOZ_ASSERT(!aLength);
|
||||
|
||||
RefPtr<BlobImpl> sliceImpl = new EmptyBlobImpl(aContentType);
|
||||
|
||||
DebugOnly<bool> isMutable;
|
||||
MOZ_ASSERT(NS_SUCCEEDED(sliceImpl->GetMutable(&isMutable)));
|
||||
MOZ_ASSERT(!isMutable);
|
||||
|
||||
return sliceImpl.forget();
|
||||
}
|
||||
|
||||
virtual void
|
||||
GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override
|
||||
{
|
||||
if (NS_WARN_IF(!aStream)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsString emptyString;
|
||||
aRv = NS_NewStringInputStream(aStream, emptyString);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS CreateBlobImplMetadata final
|
||||
{
|
||||
nsString mContentType;
|
||||
@@ -1977,7 +1925,7 @@ public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
virtual void
|
||||
GetName(nsAString& aName) override;
|
||||
GetName(nsAString& aName) const override;
|
||||
|
||||
virtual void
|
||||
GetPath(nsAString& aPath, ErrorResult& aRv) override;
|
||||
@@ -2639,7 +2587,7 @@ NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
|
||||
|
||||
void
|
||||
BlobParent::
|
||||
RemoteBlobImpl::GetName(nsAString& aName)
|
||||
RemoteBlobImpl::GetName(nsAString& aName) const
|
||||
{
|
||||
mBlobImpl->GetName(aName);
|
||||
}
|
||||
|
||||
@@ -854,9 +854,10 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
|
||||
|
||||
if (aIframeMoz) {
|
||||
MOZ_ASSERT(aTabOpener);
|
||||
newChild->SendBrowserFrameOpenWindow(aTabOpener, NS_ConvertUTF8toUTF16(url),
|
||||
newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
|
||||
name, NS_ConvertUTF8toUTF16(features),
|
||||
aWindowIsNew);
|
||||
aWindowIsNew, &textureFactoryIdentifier,
|
||||
&layersId);
|
||||
} else {
|
||||
nsAutoCString baseURIString;
|
||||
if (aTabOpener) {
|
||||
|
||||
+31
-4
@@ -67,6 +67,7 @@ using mozilla::EventMessage from "mozilla/EventForwards.h";
|
||||
using nsEventStatus from "mozilla/EventForwards.h";
|
||||
using nsSizeMode from "nsIWidgetListener.h";
|
||||
using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
|
||||
using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@@ -283,6 +284,16 @@ parent:
|
||||
*/
|
||||
async SetCandidateWindowForPlugin(CandidateWindowPosition aPosition);
|
||||
|
||||
/**
|
||||
* Notifies the parent process of native key event data received in a
|
||||
* plugin process directly.
|
||||
*
|
||||
* aKeyEventData The native key event data. The actual type copied into
|
||||
* NativeEventData depending on the caller. Please check
|
||||
* PluginInstanceChild.
|
||||
*/
|
||||
prio(urgent) async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
|
||||
|
||||
/**
|
||||
* When plugin event isn't consumed, call this
|
||||
*/
|
||||
@@ -374,7 +385,7 @@ parent:
|
||||
* Show/hide a tooltip when the mouse hovers over an element in the content
|
||||
* document.
|
||||
*/
|
||||
async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip);
|
||||
async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip, nsString direction);
|
||||
async HideTooltip();
|
||||
|
||||
/**
|
||||
@@ -409,9 +420,11 @@ parent:
|
||||
*
|
||||
* @param opener the PBrowser whose content called window.open.
|
||||
*/
|
||||
sync BrowserFrameOpenWindow(PBrowser opener, nsString aURL,
|
||||
nsString aName, nsString aFeatures)
|
||||
returns (bool windowOpened);
|
||||
sync BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame,
|
||||
nsString aURL, nsString aName, nsString aFeatures)
|
||||
returns (bool windowOpened,
|
||||
TextureFactoryIdentifier textureFactoryIdentifier,
|
||||
uint64_t layersId);
|
||||
|
||||
/**
|
||||
* Tells the containing widget whether the given input block results in a
|
||||
@@ -468,6 +481,8 @@ parent:
|
||||
bool aLongTap,
|
||||
uint64_t aObserverId);
|
||||
async ClearNativeTouchSequence(uint64_t aObserverId);
|
||||
|
||||
sync GetTabCount() returns (uint32_t value);
|
||||
child:
|
||||
async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
|
||||
|
||||
@@ -718,6 +733,18 @@ child:
|
||||
*/
|
||||
async SetUseGlobalHistory(bool aUse);
|
||||
|
||||
/**
|
||||
* HandledWindowedPluginKeyEvent() is always called after posting a native
|
||||
* key event with OnWindowedPluginKeyEvent().
|
||||
*
|
||||
* @param aKeyEventData The key event which was posted to the parent
|
||||
* process.
|
||||
* @param aIsConsumed true if aKeyEventData is consumed in the
|
||||
* parent process. Otherwise, false.
|
||||
*/
|
||||
async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
|
||||
bool aIsConsumed);
|
||||
|
||||
/*
|
||||
* FIXME: write protocol!
|
||||
|
||||
|
||||
+22
-2
@@ -2484,6 +2484,18 @@ TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNaviga
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvHandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
const bool& aIsConsumed)
|
||||
{
|
||||
if (NS_WARN_IF(!mPuppetWidget)) {
|
||||
return true;
|
||||
}
|
||||
mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
|
||||
return true;
|
||||
}
|
||||
|
||||
PRenderFrameChild*
|
||||
TabChild::AllocPRenderFrameChild()
|
||||
{
|
||||
@@ -2728,6 +2740,12 @@ TabChild::SendRequestFocus(bool aCanFocus)
|
||||
PBrowserChild::SendRequestFocus(aCanFocus);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::SendGetTabCount(uint32_t* tabCount)
|
||||
{
|
||||
PBrowserChild::SendGetTabCount(tabCount);
|
||||
}
|
||||
|
||||
void
|
||||
TabChild::EnableDisableCommands(const nsAString& aAction,
|
||||
nsTArray<nsCString>& aEnabledCommands,
|
||||
@@ -2895,10 +2913,12 @@ TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText)
|
||||
TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText,
|
||||
const char16_t *aTipDir)
|
||||
{
|
||||
nsString str(aTipText);
|
||||
SendShowTooltip(aXCoords, aYCoords, str);
|
||||
nsString dir(aTipDir);
|
||||
SendShowTooltip(aXCoords, aYCoords, str, dir);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,6 +545,10 @@ public:
|
||||
|
||||
virtual bool RecvSetUseGlobalHistory(const bool& aUse) override;
|
||||
|
||||
virtual bool RecvHandledWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aKeyEventData,
|
||||
const bool& aIsConsumed) override;
|
||||
|
||||
/**
|
||||
* Native widget remoting protocol for use with windowed plugins with e10s.
|
||||
*/
|
||||
|
||||
+61
-4
@@ -1741,14 +1741,15 @@ TabParent::RecvSetStatus(const uint32_t& aType, const nsString& aStatus)
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip)
|
||||
TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip,
|
||||
const nsString& aDirection)
|
||||
{
|
||||
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
|
||||
if (!xulBrowserWindow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
xulBrowserWindow->ShowTooltip(aX, aY, aTooltip);
|
||||
xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1893,6 +1894,44 @@ TabParent::RecvOnEventNeedingAckHandled(const EventMessage& aMessage)
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TabParent::HandledWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
bool ok = SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
|
||||
NS_WARN_IF(!ok);
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvOnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (NS_WARN_IF(!widget)) {
|
||||
// Notifies the plugin process of the key event being not consumed by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return true;
|
||||
}
|
||||
nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Notifies the plugin process of the key event being not consumed by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the key event is posted to another process, we need to wait a call
|
||||
// of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case.
|
||||
if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, the key event is handled synchronously. Let's notify the
|
||||
// plugin process of the key event's result.
|
||||
bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvRequestFocus(const bool& aCanRaise)
|
||||
{
|
||||
@@ -2604,14 +2643,18 @@ TabParent::ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
|
||||
|
||||
bool
|
||||
TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
|
||||
PRenderFrameParent* aRenderFrame,
|
||||
const nsString& aURL,
|
||||
const nsString& aName,
|
||||
const nsString& aFeatures,
|
||||
bool* aOutWindowOpened)
|
||||
bool* aOutWindowOpened,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId)
|
||||
{
|
||||
BrowserElementParent::OpenWindowResult opened =
|
||||
BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener),
|
||||
this, aURL, aName, aFeatures);
|
||||
this, aRenderFrame, aURL, aName, aFeatures,
|
||||
aTextureFactoryIdentifier, aLayersId);
|
||||
*aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
|
||||
return true;
|
||||
}
|
||||
@@ -3197,6 +3240,20 @@ TabParent::AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvGetTabCount(uint32_t* aValue)
|
||||
{
|
||||
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
|
||||
NS_ENSURE_TRUE(xulBrowserWindow, false);
|
||||
|
||||
uint32_t tabCount;
|
||||
nsresult rv = xulBrowserWindow->GetTabCount(&tabCount);
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
*aValue = tabCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
|
||||
{
|
||||
|
||||
+18
-2
@@ -22,6 +22,7 @@
|
||||
#include "nsIAuthPromptProvider.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIKeyEventInPluginCallback.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsITabParent.h"
|
||||
#include "nsIXULBrowserWindow.h"
|
||||
@@ -81,6 +82,7 @@ class TabParent final : public PBrowserParent
|
||||
, public nsITabParent
|
||||
, public nsIAuthPromptProvider
|
||||
, public nsISecureBrowserUI
|
||||
, public nsIKeyEventInPluginCallback
|
||||
, public nsSupportsWeakReference
|
||||
, public TabContext
|
||||
, public nsAPostRefreshObserver
|
||||
@@ -163,10 +165,13 @@ public:
|
||||
RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override;
|
||||
|
||||
virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
|
||||
PRenderFrameParent* aRenderFrame,
|
||||
const nsString& aURL,
|
||||
const nsString& aName,
|
||||
const nsString& aFeatures,
|
||||
bool* aOutWindowOpened) override;
|
||||
bool* aOutWindowOpened,
|
||||
TextureFactoryIdentifier* aTextureFactoryIdentifier,
|
||||
uint64_t* aLayersId) override;
|
||||
|
||||
virtual bool
|
||||
RecvSyncMessage(const nsString& aMessage,
|
||||
@@ -251,6 +256,14 @@ public:
|
||||
const int32_t& aCause,
|
||||
const int32_t& aFocusChange) override;
|
||||
|
||||
// See nsIKeyEventInPluginCallback
|
||||
virtual void HandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed) override;
|
||||
|
||||
virtual bool RecvOnWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData) override;
|
||||
|
||||
virtual bool
|
||||
RecvRequestFocus(const bool& aCanRaise) override;
|
||||
|
||||
@@ -278,7 +291,8 @@ public:
|
||||
|
||||
virtual bool RecvShowTooltip(const uint32_t& aX,
|
||||
const uint32_t& aY,
|
||||
const nsString& aTooltip) override;
|
||||
const nsString& aTooltip,
|
||||
const nsString& aDirection) override;
|
||||
|
||||
virtual bool RecvHideTooltip() override;
|
||||
|
||||
@@ -577,6 +591,8 @@ protected:
|
||||
const int32_t& aX, const int32_t& aY,
|
||||
const int32_t& aCx, const int32_t& aCy) override;
|
||||
|
||||
virtual bool RecvGetTabCount(uint32_t* aValue) override;
|
||||
|
||||
virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
|
||||
const bool& aActive) override;
|
||||
|
||||
|
||||
+4
-1
@@ -414,11 +414,14 @@ nsJSON::DecodeInternal(JSContext* cx,
|
||||
nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
|
||||
NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
|
||||
|
||||
// The ::Decode function is deprecated [Bug 675797] and the following
|
||||
// channel is never openend, so it does not matter what securityFlags
|
||||
// we pass to NS_NewInputStreamChannel here.
|
||||
rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel),
|
||||
mURI,
|
||||
aStream,
|
||||
nullPrincipal,
|
||||
nsILoadInfo::SEC_NORMAL,
|
||||
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
|
||||
nsIContentPolicy::TYPE_OTHER,
|
||||
NS_LITERAL_CSTRING("application/json"));
|
||||
|
||||
|
||||
@@ -259,6 +259,11 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
||||
return NS_ERROR_DOM_RETVAL_UNDEFINED;
|
||||
}
|
||||
|
||||
// Fail if someone tries to execute in a global with system principal.
|
||||
if (nsContentUtils::IsSystemPrincipal(objectPrincipal)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> v (cx, JS::UndefinedValue());
|
||||
// Finally, we have everything needed to evaluate the expression.
|
||||
JS::CompileOptions options(cx);
|
||||
@@ -1222,6 +1227,7 @@ nsJSProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
nsIChannel** result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(uri);
|
||||
|
||||
RefPtr<nsJSChannel> channel = new nsJSChannel();
|
||||
@@ -1237,7 +1243,6 @@ nsJSProtocolHandler::NewChannel2(nsIURI* uri,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
*result = channel;
|
||||
channel.forget(result);
|
||||
}
|
||||
return rv;
|
||||
|
||||
@@ -83,6 +83,7 @@ FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin
|
||||
HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode.
|
||||
InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2.
|
||||
ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context.
|
||||
WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context.
|
||||
TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context.
|
||||
JSONCharsetWarning=An attempt was made to declare a non-UTF-8 encoding for JSON retrieved using XMLHttpRequest. Only UTF-8 is supported for decoding JSON.
|
||||
# LOCALIZATION NOTE: Do not translate AudioBufferSourceNode
|
||||
@@ -199,6 +200,16 @@ InterceptedNonResponseWithURL=Failed to load '%1$S'. A ServiceWorker passed a pr
|
||||
# LOCALIZATION NOTE: Do not translate "ServiceWorker", "Service-Worker-Allowed" or "HTTP". %1$S and %2$S are URLs.
|
||||
ServiceWorkerScopePathMismatch=Failed to register a ServiceWorker: The path of the provided scope '%1$S' is not under the max scope allowed '%2$S'. Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
|
||||
ExecCommandCutCopyDeniedNotInputDriven=document.execCommand('cut'/'copy') was denied because it was not called from inside a short running user-generated event handler.
|
||||
ManifestShouldBeObject=Manifest should be an object.
|
||||
ManifestScopeURLInvalid=The scope URL is invalid.
|
||||
ManifestScopeNotSameOrigin=The scope URL must be same origin as document.
|
||||
ManifestStartURLOutsideScope=The start URL is outside the scope, so the scope is invalid.
|
||||
ManifestStartURLInvalid=The start URL is invalid.
|
||||
ManifestStartURLShouldBeSameOrigin=The start URL must be same origin as document.
|
||||
# LOCALIZATION NOTE: %1$S is the name of the object whose property is invalid. %2$S is the name of the invalid property. %3$S is the expected type of the property value. E.g. "Expected the manifest's start_url member to be a string."
|
||||
ManifestInvalidType=Expected the %1$S's %2$S member to be a %3$S.
|
||||
# LOCALIZATION NOTE: %1$S is the name of the property whose value is invalid. %2$S is the (invalid) value of the property. E.g. "theme_color: 42 is not a valid CSS color."
|
||||
ManifestInvalidCSSColor=%1$S: %2$S is not a valid CSS color.
|
||||
PatternAttributeCompileFailure=Unable to check <input pattern='%S'> because the pattern is not a valid regexp: %S
|
||||
# LOCALIZATION NOTE: Do not translate "postMessage" or DOMWindow. %S values are origins, like https://domain.com:port
|
||||
TargetPrincipalDoesNotMatch=Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('%S') does not match the recipient window's origin ('%S').
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Implementation of Image Object processing algorithms from:
|
||||
* http://www.w3.org/TR/appmanifest/#image-object-and-its-members
|
||||
*
|
||||
* This is intended to be used in conjunction with ManifestProcessor.js
|
||||
* This is intended to be used in conjunction with ManifestProcessor.jsm
|
||||
*
|
||||
* Creates an object to process Image Objects as defined by the
|
||||
* W3C specification. This is used to process things like the
|
||||
@@ -75,8 +75,6 @@ ImageObjectProcessor.prototype.process = function(
|
||||
'src': processSrcMember(aImageSpec, aBaseURL),
|
||||
'type': processTypeMember(aImageSpec),
|
||||
'sizes': processSizesMember(aImageSpec),
|
||||
'density': processDensityMember(aImageSpec),
|
||||
'background_color': processBackgroundColorMember(aImageSpec)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,13 +95,6 @@ ImageObjectProcessor.prototype.process = function(
|
||||
return value || undefined;
|
||||
}
|
||||
|
||||
function processDensityMember(aImage) {
|
||||
const value = parseFloat(aImage.density);
|
||||
const validNum = Number.isNaN(value) || value === +Infinity || value <=
|
||||
0;
|
||||
return (validNum) ? 1.0 : value;
|
||||
}
|
||||
|
||||
function processSrcMember(aImage, aBaseURL) {
|
||||
const spec = {
|
||||
objectName: 'image',
|
||||
@@ -136,9 +127,9 @@ ImageObjectProcessor.prototype.process = function(
|
||||
// Split on whitespace and filter out invalid values.
|
||||
value.split(/\s+/)
|
||||
.filter(isValidSizeValue)
|
||||
.forEach(size => sizes.add(size));
|
||||
.reduce((collector, size) => collector.add(size), sizes);
|
||||
}
|
||||
return sizes;
|
||||
return (sizes.size) ? Array.from(sizes).join(" ") : undefined;
|
||||
// Implementation of HTML's link@size attribute checker.
|
||||
function isValidSizeValue(aSize) {
|
||||
const size = aSize.toLowerCase();
|
||||
@@ -157,17 +148,6 @@ ImageObjectProcessor.prototype.process = function(
|
||||
return (validStarts && validDecimals);
|
||||
}
|
||||
}
|
||||
|
||||
function processBackgroundColorMember(aImage) {
|
||||
const spec = {
|
||||
objectName: 'image',
|
||||
object: aImage,
|
||||
property: 'background_color',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
return extractor.extractColorValue(spec);
|
||||
}
|
||||
};
|
||||
this.ImageObjectProcessor = ImageObjectProcessor; // jshint ignore:line
|
||||
this.EXPORTED_SYMBOLS = ['ImageObjectProcessor']; // jshint ignore:line
|
||||
|
||||
@@ -32,7 +32,10 @@ const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
|
||||
'portrait-primary', 'portrait-secondary', 'landscape-primary',
|
||||
'landscape-secondary'
|
||||
]);
|
||||
const textDirections = new Set(['ltr', 'rtl', 'auto']);
|
||||
|
||||
Cu.import('resource://gre/modules/devtools/Console.jsm');
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
// ValueExtractor is used by the various processors to get values
|
||||
// from the manifest and to report errors.
|
||||
Cu.import('resource://gre/modules/ValueExtractor.jsm');
|
||||
@@ -49,6 +52,9 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
get orientationTypes() {
|
||||
return orientationTypes;
|
||||
},
|
||||
get textDirections() {
|
||||
return textDirections;
|
||||
},
|
||||
// process() method processes JSON text into a clean manifest
|
||||
// that conforms with the W3C specification. Takes an object
|
||||
// expecting the following dictionary items:
|
||||
@@ -60,8 +66,10 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
manifestURL: aManifestURL,
|
||||
docURL: aDocURL
|
||||
}) {
|
||||
const domBundle = Services.strings.createBundle("chrome://global/locale/dom/dom.properties");
|
||||
|
||||
const console = new ConsoleAPI({
|
||||
prefix: 'Web Manifest: '
|
||||
prefix: 'Web Manifest'
|
||||
});
|
||||
const manifestURL = new URL(aManifestURL);
|
||||
const docURL = new URL(aDocURL);
|
||||
@@ -70,13 +78,13 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
rawManifest = JSON.parse(jsonText);
|
||||
} catch (e) {}
|
||||
if (typeof rawManifest !== 'object' || rawManifest === null) {
|
||||
let msg = 'Manifest needs to be an object.';
|
||||
console.warn(msg);
|
||||
console.warn(domBundle.GetStringFromName('ManifestShouldBeObject'));
|
||||
rawManifest = {};
|
||||
}
|
||||
const extractor = new ValueExtractor(console);
|
||||
const extractor = new ValueExtractor(console, domBundle);
|
||||
const imgObjProcessor = new ImageObjectProcessor(console, extractor);
|
||||
const processedManifest = {
|
||||
'dir': processDirMember.call(this),
|
||||
'lang': processLangMember(),
|
||||
'start_url': processStartURLMember(),
|
||||
'display': processDisplayMember.call(this),
|
||||
@@ -85,15 +93,28 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
'icons': imgObjProcessor.process(
|
||||
rawManifest, manifestURL, 'icons'
|
||||
),
|
||||
'splash_screens': imgObjProcessor.process(
|
||||
rawManifest, manifestURL, 'splash_screens'
|
||||
),
|
||||
'short_name': processShortNameMember(),
|
||||
'theme_color': processThemeColorMember(),
|
||||
'background_color': processBackgroundColorMember(),
|
||||
};
|
||||
processedManifest.scope = processScopeMember();
|
||||
return processedManifest;
|
||||
|
||||
function processDirMember() {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: rawManifest,
|
||||
property: 'dir',
|
||||
expectedType: 'string',
|
||||
trim: true,
|
||||
};
|
||||
const value = extractor.extractValue(spec);
|
||||
if (this.textDirections.has(value)) {
|
||||
return value;
|
||||
}
|
||||
return 'auto';
|
||||
}
|
||||
|
||||
function processNameMember() {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
@@ -164,21 +185,17 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
try {
|
||||
scopeURL = new URL(value, manifestURL);
|
||||
} catch (e) {
|
||||
let msg = 'The URL of scope is invalid.';
|
||||
console.warn(msg);
|
||||
console.warn(domBundle.GetStringFromName('ManifestScopeURLInvalid'));
|
||||
return undefined;
|
||||
}
|
||||
if (scopeURL.origin !== docURL.origin) {
|
||||
let msg = 'Scope needs to be same-origin as Document.';
|
||||
console.warn(msg);
|
||||
console.warn(domBundle.GetStringFromName('ManifestScopeNotSameOrigin'));
|
||||
return undefined;
|
||||
}
|
||||
// If start URL is not within scope of scope URL:
|
||||
let isSameOrigin = startURL && startURL.origin !== scopeURL.origin;
|
||||
if (isSameOrigin || !startURL.pathname.startsWith(scopeURL.pathname)) {
|
||||
let msg =
|
||||
'The start URL is outside the scope, so scope is invalid.';
|
||||
console.warn(msg);
|
||||
console.warn(domBundle.GetStringFromName('ManifestStartURLOutsideScope'));
|
||||
return undefined;
|
||||
}
|
||||
return scopeURL.href;
|
||||
@@ -201,12 +218,11 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
try {
|
||||
potentialResult = new URL(value, manifestURL);
|
||||
} catch (e) {
|
||||
console.warn('Invalid URL.');
|
||||
console.warn(domBundle.GetStringFromName('ManifestStartURLInvalid'))
|
||||
return result;
|
||||
}
|
||||
if (potentialResult.origin !== docURL.origin) {
|
||||
let msg = 'start_url must be same origin as document.';
|
||||
console.warn(msg);
|
||||
console.warn(domBundle.GetStringFromName('ManifestStartURLShouldBeSameOrigin'));
|
||||
} else {
|
||||
result = potentialResult.href;
|
||||
}
|
||||
@@ -224,6 +240,17 @@ this.ManifestProcessor = { // jshint ignore:line
|
||||
return extractor.extractColorValue(spec);
|
||||
}
|
||||
|
||||
function processBackgroundColorMember() {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
object: rawManifest,
|
||||
property: 'background_color',
|
||||
expectedType: 'string',
|
||||
trim: true
|
||||
};
|
||||
return extractor.extractColorValue(spec);
|
||||
}
|
||||
|
||||
function processLangMember() {
|
||||
const spec = {
|
||||
objectName: 'manifest',
|
||||
|
||||
@@ -12,8 +12,9 @@ const {
|
||||
interfaces: Ci
|
||||
} = Components;
|
||||
|
||||
function ValueExtractor(aConsole) {
|
||||
function ValueExtractor(aConsole, aBundle) {
|
||||
this.console = aConsole;
|
||||
this.domBundle = aBundle;
|
||||
}
|
||||
|
||||
ValueExtractor.prototype = {
|
||||
@@ -32,9 +33,9 @@ ValueExtractor.prototype = {
|
||||
const type = (isArray) ? 'array' : typeof value;
|
||||
if (type !== expectedType) {
|
||||
if (type !== 'undefined') {
|
||||
let msg = `Expected the ${objectName}'s ${property} `;
|
||||
msg += `member to be a ${expectedType}.`;
|
||||
this.console.log(msg);
|
||||
this.console.warn(this.domBundle.formatStringFromName("ManifestInvalidType",
|
||||
[objectName, property, expectedType],
|
||||
3));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -53,8 +54,9 @@ ValueExtractor.prototype = {
|
||||
if (DOMUtils.isValidCSSColor(value)) {
|
||||
color = value;
|
||||
} else if (value) {
|
||||
const msg = `background_color: ${value} is not a valid CSS color.`;
|
||||
this.console.warn(msg);
|
||||
this.console.warn(this.domBundle.formatStringFromName("ManifestInvalidCSSColor",
|
||||
[spec.property, value],
|
||||
2));
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ support-files =
|
||||
common.js
|
||||
resource.sjs
|
||||
manifestLoader.html
|
||||
[test_ImageObjectProcessor_background_color.html]
|
||||
[test_ImageObjectProcessor_density.html]
|
||||
[test_ImageObjectProcessor_sizes.html]
|
||||
[test_ImageObjectProcessor_src.html]
|
||||
[test_ImageObjectProcessor_type.html]
|
||||
[test_ManifestProcessor_dir.html]
|
||||
[test_ManifestProcessor_display.html]
|
||||
[test_ManifestProcessor_icons.html]
|
||||
[test_ManifestProcessor_JSON.html]
|
||||
@@ -16,5 +15,4 @@ support-files =
|
||||
[test_ManifestProcessor_name_and_short_name.html]
|
||||
[test_ManifestProcessor_orientation.html]
|
||||
[test_ManifestProcessor_scope.html]
|
||||
[test_ManifestProcessor_splash_screens.html]
|
||||
[test_ManifestProcessor_start_url.html]
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1162808
|
||||
-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1162808</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script src="common.js"></script>
|
||||
<script type="application/javascript">
|
||||
/**
|
||||
* Image object's type member
|
||||
* https://w3c.github.io/manifest/#type-member
|
||||
*
|
||||
* Checks that invalid and valid colors are processed correctly.
|
||||
**/
|
||||
/*globals data, processor, is*/
|
||||
'use strict';
|
||||
var testIcon = {
|
||||
icons: [{
|
||||
src: 'test',
|
||||
'background_color': undefined
|
||||
}]
|
||||
};
|
||||
|
||||
// Test invalid colors.
|
||||
var invalidColors = [
|
||||
'marooon',
|
||||
'f000000',
|
||||
'#ff00000',
|
||||
'rgb(255 0 0)',
|
||||
'rgb(100, 0%, 0%)',
|
||||
'rgb(255,0)',
|
||||
'rgb(300 0 0)',
|
||||
'rbg(255,-10,0)',
|
||||
'rgb(110, 0%, 0%)',
|
||||
'(255,0,0) }',
|
||||
'rgba(255)',
|
||||
' rgb(100%,0%,0%) }',
|
||||
'hsl 120, 100%, 50%',
|
||||
'hsla{120, 100%, 50%, 1}'
|
||||
];
|
||||
invalidColors.forEach((invalidColor) => {
|
||||
var expected = `Treat invalid color (${invalidColor}) as undefined.`;
|
||||
testIcon.icons[0].background_color = invalidColor;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
is(result.icons[0].background_color, undefined, expected);
|
||||
});
|
||||
|
||||
// Test valid colors.
|
||||
var validColors = [
|
||||
'maroon',
|
||||
'#f00',
|
||||
'#ff0000',
|
||||
'rgb(255,0,0)',
|
||||
'rgb(100%, 0%, 0%)',
|
||||
'rgb(255,0,0)',
|
||||
'rgb(300,0,0)',
|
||||
'rgb(255,-10,0)',
|
||||
'rgb(110%, 0%, 0%)',
|
||||
'rgb(255,0,0)',
|
||||
'rgba(255,0,0,1)',
|
||||
'rgb(100%,0%,0%)',
|
||||
'rgba(100%,0%,0%,1)',
|
||||
'rgba(0,0,255,0.5)',
|
||||
'rgba(100%, 50%, 0%, 0.1)',
|
||||
'hsl(120, 100%, 50%)',
|
||||
'hsla(120, 100%, 50%, 1)'
|
||||
];
|
||||
|
||||
validColors.forEach((color) => {
|
||||
var expected = `Treat valid CSS color (${color}) as valid input.`;
|
||||
testIcon.icons[0].background_color = color;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
is(result.icons[0].background_color, color, expected);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1162808">Mozilla Bug 1162808</a>
|
||||
@@ -1,57 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1079453
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1079453</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* Image object density member
|
||||
* https://w3c.github.io/manifest/#density-member
|
||||
**/
|
||||
'use strict';
|
||||
|
||||
var testIcon = {
|
||||
icons: [{
|
||||
src: 'test',
|
||||
density: undefined
|
||||
}]
|
||||
};
|
||||
|
||||
var iconDensityValueTests = [null, {},
|
||||
[], false, '', -0, '-0', -1.0000, -123131132, -1.2e+200,
|
||||
'Infinity', '-Infinity',
|
||||
'-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
|
||||
'1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
];
|
||||
|
||||
iconDensityValueTests.forEach((density) => {
|
||||
var expected = `Expect density to default to 1.0.`;
|
||||
testIcon.icons[0].density = density;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
ise(result.icons[0].density, 1.0, expected);
|
||||
});
|
||||
|
||||
testIcon = {
|
||||
icons: [{
|
||||
src: 'test',
|
||||
density: undefined
|
||||
}]
|
||||
};
|
||||
|
||||
var parseFloatTests = [3.14, '3.14', `${whiteSpace}3.14${whiteSpace}`, 12e300];
|
||||
parseFloatTests.forEach((testNumber) => {
|
||||
var expected = `Expect density to be ${parseFloat(testNumber)}.`;
|
||||
testIcon.icons[0].density = testNumber;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
ise(result.icons[0].density, parseFloat(testNumber), expected);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -57,19 +57,13 @@ var testIcon = {
|
||||
}]
|
||||
};
|
||||
|
||||
validSizes.forEach(({
|
||||
test, expect
|
||||
}) => {
|
||||
validSizes.forEach(({test, expect}) => {
|
||||
testIcon.icons[0].sizes = test;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
var sizes = result.icons[0].sizes;
|
||||
for (var expectedSize of expect) {
|
||||
var expected = `Expect sizes to contain ${expectedSize}`;
|
||||
ise(sizes.has(expectedSize), true, expected);
|
||||
}
|
||||
expected = `Expect the size of the set to be ${expect.length}.`;
|
||||
ise(sizes.size, expect.length, expected);
|
||||
var expected = `Expect sizes to equal ${expect.join(" ")}`;
|
||||
is(sizes, expect.join(" "), expected);
|
||||
});
|
||||
|
||||
var testIcon = {
|
||||
@@ -81,21 +75,21 @@ var testIcon = {
|
||||
|
||||
var invalidSizes = ['invalid', '', ' ', '16 x 16', '32', '21', '16xx16', '16 x x 6'];
|
||||
invalidSizes.forEach((invalidSize) => {
|
||||
var expected = 'Expect invalid sizes to return an empty set.';
|
||||
var expected = 'Expect invalid sizes to return undefined.';
|
||||
testIcon.icons[0].sizes = invalidSize;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
var sizes = result.icons[0].sizes;
|
||||
ise(sizes.size, 0, expected);
|
||||
is(sizes, undefined, expected);
|
||||
});
|
||||
|
||||
typeTests.forEach((type) => {
|
||||
var expected = `Expect non-string sizes to be empty set: ${typeof type}.`;
|
||||
var expected = `Expect non-string sizes ${typeof type} to be undefined.`;
|
||||
testIcon.icons[0].sizes = type;
|
||||
data.jsonText = JSON.stringify(testIcon);
|
||||
var result = processor.process(data);
|
||||
var sizes = result.icons[0].sizes;
|
||||
ise(sizes.size, 0, expected);
|
||||
is(sizes, undefined, expected);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@@ -22,14 +22,11 @@ var noSrc = {
|
||||
src: {}
|
||||
}, {
|
||||
src: null
|
||||
}, {
|
||||
density: '1'
|
||||
}, {
|
||||
type: 'image/jpg'
|
||||
}, {
|
||||
sizes: '1x1,2x2'
|
||||
}, {
|
||||
density: '1',
|
||||
sizes: 'any',
|
||||
type: 'image/jpg'
|
||||
}]
|
||||
@@ -77,7 +74,6 @@ var withSrc = {
|
||||
src: 'pass'
|
||||
}, {
|
||||
src: 'pass',
|
||||
density: 1
|
||||
}, {}, {
|
||||
foo: 'foo'
|
||||
}]
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1258899
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1258899</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* dir member
|
||||
* https://w3c.github.io/manifest/#dir-member
|
||||
**/
|
||||
'use strict';
|
||||
//Type checks
|
||||
typeTests.forEach((type) => {
|
||||
var expected = `Expect non - string dir to default to "auto".`;
|
||||
data.jsonText = JSON.stringify({
|
||||
dir: type
|
||||
});
|
||||
var result = processor.process(data);
|
||||
is(result.dir, 'auto', expected);
|
||||
});
|
||||
|
||||
/*Test valid values*/
|
||||
var validDirs = ['ltr', 'rtl', 'auto']
|
||||
validDirs.forEach((dir) => {
|
||||
var expected = `Expect dir value to be ${dir}.`;
|
||||
data.jsonText = JSON.stringify({dir});
|
||||
var result = processor.process(data);
|
||||
is(result.dir, dir, expected);
|
||||
});
|
||||
|
||||
//trim tests
|
||||
validDirs.forEach((dir) => {
|
||||
var expected = `Expect trimmed dir to be returned.`;
|
||||
var expandeddir = seperators + lineTerminators + dir + lineTerminators + seperators;
|
||||
data.jsonText = JSON.stringify({
|
||||
dir: expandeddir
|
||||
});
|
||||
var result = processor.process(data);
|
||||
is(result.dir, dir, expected);
|
||||
});
|
||||
|
||||
//Unknown/Invalid directions
|
||||
var invalidDirs = ['LTR', 'RtL', `fooo${whiteSpace}rtl`, '', 'bar baz, some value', 'ltr rtl auto', 'AuTo'];
|
||||
invalidDirs.forEach((dir) => {
|
||||
var expected = `Expect default dir "auto" to be returned: '${dir}'`;
|
||||
data.jsonText = JSON.stringify({dir});
|
||||
var result = processor.process(data);
|
||||
is(result.dir, 'auto', expected);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1162808
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1162808</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script src="common.js"></script>
|
||||
<script>
|
||||
/**
|
||||
* Manifest splash_screens member
|
||||
* http://www.w3.org/TR/appmanifest/#splash_screens-member
|
||||
**/
|
||||
|
||||
'use strict';
|
||||
|
||||
typeTests.forEach((type) => {
|
||||
var expected = `Expect non-array splash_screens to be empty: ${typeof type}.`;
|
||||
data.jsonText = JSON.stringify({
|
||||
splash_screens: type
|
||||
});
|
||||
var result = processor.process(data);
|
||||
is(result.splash_screens.length, 0, expected);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
@@ -385,6 +385,14 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessDrain()
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
FFmpegH264Decoder<LIBAV_VER>::ProcessFlush()
|
||||
{
|
||||
mPtsContext.Reset();
|
||||
mDurationMap.Clear();
|
||||
FFmpegDataDecoder::ProcessFlush();
|
||||
}
|
||||
|
||||
FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
|
||||
{
|
||||
MOZ_COUNT_DTOR(FFmpegH264Decoder);
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
RefPtr<InitPromise> Init() override;
|
||||
nsresult Input(MediaRawData* aSample) override;
|
||||
void ProcessDrain() override;
|
||||
void ProcessFlush() override;
|
||||
void InitCodecContext() override;
|
||||
const char* GetDescriptionName() const override
|
||||
{
|
||||
|
||||
@@ -329,4 +329,17 @@ PluginPRLibrary::UpdateScrollState(NPP aInstance, bool aIsScrolling)
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
PluginPRLibrary::HandledWindowedPluginKeyEvent(
|
||||
NPP aInstance,
|
||||
const NativeEventData& aNativeKeyData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
nsNPAPIPluginInstance* instance = (nsNPAPIPluginInstance*)aInstance->ndata;
|
||||
if (NS_WARN_IF(!instance)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -129,6 +129,10 @@ public:
|
||||
virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override;
|
||||
virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) override;
|
||||
#endif
|
||||
virtual nsresult HandledWindowedPluginKeyEvent(
|
||||
NPP aInstance,
|
||||
const mozilla::NativeEventData& aNativeKeyData,
|
||||
bool aIsCOnsumed) override;
|
||||
|
||||
private:
|
||||
NP_InitializeFunc mNP_Initialize;
|
||||
|
||||
@@ -1170,6 +1170,23 @@ nsNPAPIPluginInstance::UpdateScrollState(bool aIsScrolling)
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsNPAPIPluginInstance::HandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
if (NS_WARN_IF(!mPlugin)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
PluginLibrary* library = mPlugin->GetLibrary();
|
||||
if (NS_WARN_IF(!library)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return library->HandledWindowedPluginKeyEvent(&mNPP, aKeyEventData,
|
||||
aIsConsumed);
|
||||
}
|
||||
|
||||
void
|
||||
nsNPAPIPluginInstance::DidComposite()
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
class PluginEventRunnable;
|
||||
#endif
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/PluginLibrary.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
@@ -126,6 +127,9 @@ public:
|
||||
nsresult GetScrollCaptureContainer(mozilla::layers::ImageContainer **aContainer);
|
||||
nsresult UpdateScrollState(bool aIsScrolling);
|
||||
#endif
|
||||
nsresult HandledWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed);
|
||||
nsPluginInstanceOwner* GetOwner();
|
||||
void SetOwner(nsPluginInstanceOwner *aOwner);
|
||||
void DidComposite();
|
||||
|
||||
@@ -73,13 +73,8 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
#ifdef XP_WIN
|
||||
#include <wtypes.h>
|
||||
#include <winuser.h>
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL (0x020E)
|
||||
#endif
|
||||
#ifndef SPI_GETWHEELSCROLLCHARS
|
||||
#define SPI_GETWHEELSCROLLCHARS (0x006C)
|
||||
#endif
|
||||
#endif
|
||||
#include "mozilla/widget/WinMessages.h"
|
||||
#endif // #ifdef XP_WIN
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "ComplexTextInputPanel.h"
|
||||
@@ -423,6 +418,7 @@ NS_IMPL_ISUPPORTS(nsPluginInstanceOwner,
|
||||
nsIPluginInstanceOwner,
|
||||
nsIDOMEventListener,
|
||||
nsIPrivacyTransitionObserver,
|
||||
nsIKeyEventInPluginCallback,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsresult
|
||||
@@ -999,8 +995,58 @@ nsPluginInstanceOwner::RequestCommitOrCancel(bool aCommitted)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // #ifdef XP_WIN
|
||||
|
||||
void
|
||||
nsPluginInstanceOwner::HandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
if (NS_WARN_IF(!mInstance)) {
|
||||
return;
|
||||
}
|
||||
nsresult rv =
|
||||
mInstance->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
}
|
||||
|
||||
void
|
||||
nsPluginInstanceOwner::OnWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData)
|
||||
{
|
||||
if (NS_WARN_IF(!mPluginFrame)) {
|
||||
// Notifies the plugin process of the key event being not consumed by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWidget> widget = mPluginFrame->PresContext()->GetRootWidget();
|
||||
if (NS_WARN_IF(!widget)) {
|
||||
// Notifies the plugin process of the key event being not consumed by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// Notifies the plugin process of the key event being not consumed by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the key event is posted to another process, we need to wait a call
|
||||
// of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case.
|
||||
if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, the key event is handled synchronously. Let's notify the
|
||||
// plugin process of the key event's result.
|
||||
bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsPluginInstanceOwner::SetEventModel(int32_t eventModel)
|
||||
{
|
||||
#ifdef XP_MACOSX
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "npapi.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIKeyEventInPluginCallback.h"
|
||||
#include "nsIPluginInstanceOwner.h"
|
||||
#include "nsIPrivacyTransitionObserver.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
@@ -52,10 +53,11 @@ using mozilla::widget::PuppetWidget;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class nsPluginInstanceOwner final : public nsIPluginInstanceOwner,
|
||||
public nsIDOMEventListener,
|
||||
public nsIPrivacyTransitionObserver,
|
||||
public nsSupportsWeakReference
|
||||
class nsPluginInstanceOwner final : public nsIPluginInstanceOwner
|
||||
, public nsIDOMEventListener
|
||||
, public nsIPrivacyTransitionObserver
|
||||
, public nsIKeyEventInPluginCallback
|
||||
, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
@@ -278,6 +280,21 @@ public:
|
||||
const mozilla::widget::CandidateWindowPosition& aPosition);
|
||||
bool RequestCommitOrCancel(bool aCommitted);
|
||||
|
||||
// See nsIKeyEventInPluginCallback
|
||||
virtual void HandledWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed) override;
|
||||
|
||||
/**
|
||||
* OnWindowedPluginKeyEvent() is called when the plugin process receives
|
||||
* native key event directly.
|
||||
*
|
||||
* @param aNativeKeyData The key event which was received by the
|
||||
* plugin process directly.
|
||||
*/
|
||||
void OnWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aNativeKeyData);
|
||||
|
||||
void GetCSSZoomFactor(float *result);
|
||||
private:
|
||||
virtual ~nsPluginInstanceOwner();
|
||||
|
||||
@@ -32,6 +32,7 @@ using nsIntRect from "nsRect.h";
|
||||
using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
|
||||
using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h";
|
||||
using struct mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
|
||||
using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
@@ -140,6 +141,16 @@ child:
|
||||
intr NPP_Destroy()
|
||||
returns (NPError rv);
|
||||
|
||||
// HandledWindowedPluginKeyEvent() is always called after posting a native
|
||||
// key event with OnWindowedPluginKeyEvent().
|
||||
//
|
||||
// @param aKeyEventData The key event which was posted to the parent
|
||||
// process.
|
||||
// @param aIsConsumed true if aKeyEventData is consumed in the
|
||||
// parent process. Otherwise, false.
|
||||
async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
|
||||
bool aIsConsumed);
|
||||
|
||||
parent:
|
||||
intr NPN_GetValue_NPNVWindowNPObject()
|
||||
returns (nullable PPluginScriptableObject value, NPError result);
|
||||
@@ -271,6 +282,13 @@ parent:
|
||||
async SetCandidateWindow(CandidateWindowPosition aPosition);
|
||||
async RequestCommitOrCancel(bool aCommitted);
|
||||
|
||||
// Notifies the parent process of a plugin instance receiving key event
|
||||
// directly.
|
||||
//
|
||||
// @param aKeyEventData The native key event which will be sent to
|
||||
// plugin from native event handler.
|
||||
async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
|
||||
|
||||
both:
|
||||
async PPluginScriptableObject();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ using mozilla::gfx::SharedDIBSurface;
|
||||
#include "gfxAlphaRecovery.h"
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/ipc/MessageChannel.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
@@ -43,6 +44,7 @@ using mozilla::ipc::ProcessChild;
|
||||
using namespace mozilla::plugins;
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gfx;
|
||||
using namespace mozilla::widget;
|
||||
using namespace std;
|
||||
|
||||
#ifdef MOZ_WIDGET_GTK
|
||||
@@ -56,10 +58,13 @@ using namespace std;
|
||||
#undef KeyPress
|
||||
#undef KeyRelease
|
||||
#elif defined(OS_WIN)
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x020E
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#include "mozilla/widget/WinMessages.h"
|
||||
#include "mozilla/widget/WinModifierKeyState.h"
|
||||
#include "mozilla/widget/WinNativeEventData.h"
|
||||
#include "nsWindowsDllInterceptor.h"
|
||||
|
||||
typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
|
||||
@@ -94,9 +99,6 @@ static const HIMC sHookIMC = (const HIMC)0xefefefef;
|
||||
|
||||
using mozilla::gfx::SharedDIB;
|
||||
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
// Flash WM_USER message delay time for PostDelayedTask. Borrowed
|
||||
// from Chromium's web plugin delegate src. See 'flash msg throttling
|
||||
// helpers' section for details.
|
||||
@@ -138,6 +140,8 @@ CreateDrawTargetForSurface(gfxASurface *aSurface)
|
||||
return drawTarget;
|
||||
}
|
||||
|
||||
bool PluginInstanceChild::sIsIMEComposing = false;
|
||||
|
||||
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
const nsCString& aMimeType,
|
||||
const uint16_t& aMode,
|
||||
@@ -151,6 +155,8 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
#if defined(XP_DARWIN)
|
||||
, mContentsScaleFactor(1.0)
|
||||
#endif
|
||||
, mPostingKeyEvents(0)
|
||||
, mPostingKeyEventsOutdated(0)
|
||||
, mDrawingModel(kDefaultDrawingModel)
|
||||
, mCurrentDirectSurface(nullptr)
|
||||
, mAsyncInvalidateMutex("PluginInstanceChild::mAsyncInvalidateMutex")
|
||||
@@ -196,6 +202,10 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
, mHasPainted(false)
|
||||
, mSurfaceDifferenceRect(0,0,0,0)
|
||||
, mDestroyed(false)
|
||||
#ifdef XP_WIN
|
||||
, mLastKeyEventConsumed(false)
|
||||
#endif // #ifdef XP_WIN
|
||||
, mStackDepth(0)
|
||||
{
|
||||
memset(&mWindow, 0, sizeof(mWindow));
|
||||
mWindow.type = NPWindowTypeWindow;
|
||||
@@ -383,6 +393,7 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
switch(aVar) {
|
||||
|
||||
@@ -591,6 +602,8 @@ PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue)
|
||||
|
||||
AssertPluginThread();
|
||||
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
switch (aVar) {
|
||||
case NPPVpluginWindowBool: {
|
||||
NPError rv;
|
||||
@@ -695,6 +708,7 @@ PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginWantsAllNetworkStreams(
|
||||
bool* wantsAllStreams, NPError* rv)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
uint32_t value = 0;
|
||||
if (!mPluginIface->getvalue) {
|
||||
@@ -713,6 +727,7 @@ PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(
|
||||
bool* needs, NPError* rv)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
#ifdef MOZ_X11
|
||||
// The documentation on the types for many variables in NP(N|P)_GetValue
|
||||
@@ -747,6 +762,7 @@ PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject(
|
||||
NPError* aResult)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
NPObject* object = nullptr;
|
||||
NPError result = NPERR_GENERIC_ERROR;
|
||||
@@ -784,6 +800,7 @@ PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(
|
||||
NPError* aResult)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
#if MOZ_ACCESSIBILITY_ATK
|
||||
|
||||
@@ -856,6 +873,7 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
#if defined(MOZ_X11) && defined(DEBUG)
|
||||
if (GraphicsExpose == event.event.type)
|
||||
@@ -938,6 +956,7 @@ PluginInstanceChild::AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
PaintTracker pt;
|
||||
|
||||
@@ -1042,6 +1061,7 @@ PluginInstanceChild::AnswerNPP_HandleEvent_IOSurface(const NPRemoteEvent& event,
|
||||
{
|
||||
PLUGIN_LOG_DEBUG_FUNCTION;
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
PaintTracker pt;
|
||||
|
||||
@@ -1237,6 +1257,7 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
|
||||
NS_ASSERTION(!mLayersRendering && !mPendingPluginCall,
|
||||
"Shouldn't be receiving NPP_SetWindow with layer rendering");
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
NS_ASSERTION(mWsInfo.display, "We should have a valid display!");
|
||||
@@ -1420,6 +1441,95 @@ PluginInstanceChild::Initialize()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceChild::RecvHandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
const bool& aIsConsumed)
|
||||
{
|
||||
#if defined(OS_WIN)
|
||||
const WinNativeKeyEventData* eventData =
|
||||
static_cast<const WinNativeKeyEventData*>(aKeyEventData);
|
||||
switch (eventData->mMessage) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
mLastKeyEventConsumed = aIsConsumed;
|
||||
break;
|
||||
case WM_CHAR:
|
||||
case WM_SYSCHAR:
|
||||
case WM_DEADCHAR:
|
||||
case WM_SYSDEADCHAR:
|
||||
// If preceding keydown or keyup event is consumed by the chrome
|
||||
// process, we should consume WM_*CHAR messages too.
|
||||
if (mLastKeyEventConsumed) {
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
MOZ_CRASH("Needs to handle all messages posted to the parent");
|
||||
}
|
||||
#endif // #if defined(OS_WIN)
|
||||
|
||||
// Unknown key input shouldn't be sent to plugin for security.
|
||||
// XXX Is this possible if a plugin process which posted the message
|
||||
// already crashed and this plugin process is recreated?
|
||||
if (NS_WARN_IF(!mPostingKeyEvents && !mPostingKeyEventsOutdated)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there is outdated posting key events, we should consume the key
|
||||
// events.
|
||||
if (mPostingKeyEventsOutdated) {
|
||||
mPostingKeyEventsOutdated--;
|
||||
return true;
|
||||
}
|
||||
|
||||
mPostingKeyEvents--;
|
||||
|
||||
// If composition has been started after posting the key event,
|
||||
// we should discard the event since if we send the event to plugin,
|
||||
// the plugin may be confused and the result may be broken because
|
||||
// the event order is shuffled.
|
||||
if (aIsConsumed || sIsIMEComposing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
UINT message = 0;
|
||||
switch (eventData->mMessage) {
|
||||
case WM_KEYDOWN:
|
||||
message = MOZ_WM_KEYDOWN;
|
||||
break;
|
||||
case WM_SYSKEYDOWN:
|
||||
message = MOZ_WM_SYSKEYDOWN;
|
||||
break;
|
||||
case WM_KEYUP:
|
||||
message = MOZ_WM_KEYUP;
|
||||
break;
|
||||
case WM_SYSKEYUP:
|
||||
message = MOZ_WM_SYSKEYUP;
|
||||
break;
|
||||
case WM_CHAR:
|
||||
message = MOZ_WM_CHAR;
|
||||
break;
|
||||
case WM_SYSCHAR:
|
||||
message = MOZ_WM_SYSCHAR;
|
||||
break;
|
||||
case WM_DEADCHAR:
|
||||
message = MOZ_WM_DEADCHAR;
|
||||
break;
|
||||
case WM_SYSDEADCHAR:
|
||||
message = MOZ_WM_SYSDEADCHAR;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Needs to handle all messages posted to the parent");
|
||||
}
|
||||
PluginWindowProcInternal(mPluginWindowHWND, message,
|
||||
eventData->mWParam, eventData->mLParam);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow");
|
||||
@@ -1554,30 +1664,107 @@ PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
|
||||
NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!");
|
||||
NS_ASSERTION(self->mPluginWndProc != PluginWindowProc, "Self-referential windowproc. Infinite recursion will happen soon.");
|
||||
|
||||
// Adobe's shockwave positions the plugin window relative to the browser
|
||||
// frame when it initializes. With oopp disabled, this wouldn't have an
|
||||
// effect. With oopp, GeckoPluginWindow is a child of the parent plugin
|
||||
// window, so the move offsets the child within the parent. Generally
|
||||
// we don't want plugins moving or sizing our window, so we prevent these
|
||||
// changes here.
|
||||
if (message == WM_WINDOWPOSCHANGING) {
|
||||
WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
|
||||
if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
|
||||
pos->x = pos->y = 0;
|
||||
pos->cx = self->mPluginSize.x;
|
||||
pos->cy = self->mPluginSize.y;
|
||||
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
|
||||
lParam);
|
||||
pos->x = pos->y = 0;
|
||||
pos->cx = self->mPluginSize.x;
|
||||
pos->cy = self->mPluginSize.y;
|
||||
return res;
|
||||
}
|
||||
bool isIMECompositionMessage = false;
|
||||
switch (message) {
|
||||
// Adobe's shockwave positions the plugin window relative to the browser
|
||||
// frame when it initializes. With oopp disabled, this wouldn't have an
|
||||
// effect. With oopp, GeckoPluginWindow is a child of the parent plugin
|
||||
// window, so the move offsets the child within the parent. Generally
|
||||
// we don't want plugins moving or sizing our window, so we prevent
|
||||
// these changes here.
|
||||
case WM_WINDOWPOSCHANGING: {
|
||||
WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
|
||||
if (pos &&
|
||||
(!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) {
|
||||
pos->x = pos->y = 0;
|
||||
pos->cx = self->mPluginSize.x;
|
||||
pos->cy = self->mPluginSize.y;
|
||||
LRESULT res = CallWindowProc(self->mPluginWndProc,
|
||||
hWnd, message, wParam, lParam);
|
||||
pos->x = pos->y = 0;
|
||||
pos->cx = self->mPluginSize.x;
|
||||
pos->cy = self->mPluginSize.y;
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_SETFOCUS:
|
||||
// If this gets focus, ensure that there is no pending key events.
|
||||
// Even if there were, we should ignore them for performance reason.
|
||||
// Although, such case shouldn't occur.
|
||||
NS_WARN_IF(self->mPostingKeyEvents > 0);
|
||||
self->mPostingKeyEvents = 0;
|
||||
self->mLastKeyEventConsumed = false;
|
||||
break;
|
||||
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
if (self->MaybePostKeyMessage(message, wParam, lParam)) {
|
||||
// If PreHandleKeyMessage() posts the message to the parent
|
||||
// process, we need to wait RecvOnKeyEventHandledBeforePlugin()
|
||||
// to be called.
|
||||
return 0; // Consume current message temporarily.
|
||||
}
|
||||
break;
|
||||
|
||||
case MOZ_WM_KEYDOWN:
|
||||
message = WM_KEYDOWN;
|
||||
break;
|
||||
case MOZ_WM_SYSKEYDOWN:
|
||||
message = WM_SYSKEYDOWN;
|
||||
break;
|
||||
case MOZ_WM_KEYUP:
|
||||
message = WM_KEYUP;
|
||||
break;
|
||||
case MOZ_WM_SYSKEYUP:
|
||||
message = WM_SYSKEYUP;
|
||||
break;
|
||||
case MOZ_WM_CHAR:
|
||||
message = WM_CHAR;
|
||||
break;
|
||||
case MOZ_WM_SYSCHAR:
|
||||
message = WM_SYSCHAR;
|
||||
break;
|
||||
case MOZ_WM_DEADCHAR:
|
||||
message = WM_DEADCHAR;
|
||||
break;
|
||||
case MOZ_WM_SYSDEADCHAR:
|
||||
message = WM_SYSDEADCHAR;
|
||||
break;
|
||||
|
||||
case WM_IME_STARTCOMPOSITION:
|
||||
isIMECompositionMessage = true;
|
||||
sIsIMEComposing = true;
|
||||
break;
|
||||
case WM_IME_ENDCOMPOSITION:
|
||||
isIMECompositionMessage = true;
|
||||
sIsIMEComposing = false;
|
||||
break;
|
||||
case WM_IME_COMPOSITION:
|
||||
isIMECompositionMessage = true;
|
||||
// XXX Some old IME may not send WM_IME_COMPOSITION_START or
|
||||
// WM_IME_COMPSOITION_END properly. So, we need to check
|
||||
// WM_IME_COMPSOITION and if it includes commit string.
|
||||
sIsIMEComposing = !(lParam & GCS_RESULTSTR);
|
||||
break;
|
||||
|
||||
// The plugin received keyboard focus, let the parent know so the dom
|
||||
// is up to date.
|
||||
case WM_MOUSEACTIVATE:
|
||||
self->CallPluginFocusChange(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// The plugin received keyboard focus, let the parent know so the dom is up to date.
|
||||
if (message == WM_MOUSEACTIVATE) {
|
||||
self->CallPluginFocusChange(true);
|
||||
// When a composition is committed, there may be pending key
|
||||
// events which were posted to the parent process before starting
|
||||
// the composition. Then, we shouldn't handle it since they are
|
||||
// now outdated.
|
||||
if (isIMECompositionMessage && !sIsIMEComposing) {
|
||||
self->mPostingKeyEventsOutdated += self->mPostingKeyEvents;
|
||||
self->mPostingKeyEvents = 0;
|
||||
}
|
||||
|
||||
// Prevent lockups due to plugins making rpc calls when the parent
|
||||
@@ -1636,6 +1823,95 @@ PluginInstanceChild::PluginWindowProcInternal(HWND hWnd,
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceChild::ShouldPostKeyMessage(UINT message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
// If there is a composition, we shouldn't post the key message to the
|
||||
// parent process because we cannot handle IME messages asynchronously.
|
||||
// Therefore, if we posted key events to the parent process, the event
|
||||
// order of the posted key events and IME events are shuffled.
|
||||
if (sIsIMEComposing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there are some pending keyboard events which are not handled in
|
||||
// the parent process, we should post the message for avoiding to shuffle
|
||||
// the key event order.
|
||||
if (mPostingKeyEvents) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are not waiting calls of RecvOnKeyEventHandledBeforePlugin(),
|
||||
// we don't need to post WM_*CHAR messages.
|
||||
switch (message) {
|
||||
case WM_CHAR:
|
||||
case WM_SYSCHAR:
|
||||
case WM_DEADCHAR:
|
||||
case WM_SYSDEADCHAR:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, we should post key message which might match with a
|
||||
// shortcut key.
|
||||
ModifierKeyState modifierState;
|
||||
if (!modifierState.MaybeMatchShortcutKey()) {
|
||||
// For better UX, we shouldn't use IPC when user tries to
|
||||
// input character(s).
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore modifier key events and keys already handled by IME.
|
||||
switch (wParam) {
|
||||
case VK_SHIFT:
|
||||
case VK_CONTROL:
|
||||
case VK_MENU:
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
case VK_CAPITAL:
|
||||
case VK_NUMLOCK:
|
||||
case VK_SCROLL:
|
||||
// Following virtual keycodes shouldn't come with WM_(SYS)KEY* message
|
||||
// but check it for avoiding unnecessary cross process communication.
|
||||
case VK_LSHIFT:
|
||||
case VK_RSHIFT:
|
||||
case VK_LCONTROL:
|
||||
case VK_RCONTROL:
|
||||
case VK_LMENU:
|
||||
case VK_RMENU:
|
||||
case VK_PROCESSKEY:
|
||||
case VK_PACKET:
|
||||
case 0xFF: // 0xFF could be sent with unidentified key by the layout.
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceChild::MaybePostKeyMessage(UINT message,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
if (!ShouldPostKeyMessage(message, wParam, lParam)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ModifierKeyState modifierState;
|
||||
WinNativeKeyEventData winNativeKeyData(message, wParam, lParam,
|
||||
modifierState);
|
||||
NativeEventData nativeKeyData;
|
||||
nativeKeyData.Copy(winNativeKeyData);
|
||||
if (NS_WARN_IF(!SendOnWindowedPluginKeyEvent(nativeKeyData))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mPostingKeyEvents++;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* set window long ptr hook for flash */
|
||||
|
||||
/*
|
||||
@@ -2490,6 +2766,7 @@ PluginInstanceChild::DoNPP_NewStream(BrowserStreamChild* actor,
|
||||
uint16_t* stype)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
NPError rv = actor->StreamConstructed(mimeType, seekable, stype);
|
||||
return rv;
|
||||
}
|
||||
@@ -2708,6 +2985,7 @@ PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow,
|
||||
NPStream** aStream)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
PluginStreamChild* ps = new PluginStreamChild();
|
||||
|
||||
@@ -2790,6 +3068,7 @@ PluginInstanceChild::NPN_InitAsyncSurface(NPSize *size, NPImageFormat format,
|
||||
void *initData, NPAsyncSurface *surface)
|
||||
{
|
||||
AssertPluginThread();
|
||||
AutoStackHelper guard(this);
|
||||
|
||||
if (!IsUsingDirectDrawing()) {
|
||||
return NPERR_INVALID_PARAM;
|
||||
@@ -3007,6 +3286,7 @@ PluginInstanceChild::RecvAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
|
||||
{
|
||||
AssertPluginThread();
|
||||
|
||||
AutoStackHelper guard(this);
|
||||
NS_ASSERTION(!aWindow.window, "Remote window should be null.");
|
||||
|
||||
if (mCurrentAsyncSetWindowTask) {
|
||||
@@ -4261,6 +4541,9 @@ PluginInstanceChild::Destroy()
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
if (mStackDepth != 0) {
|
||||
NS_RUNTIMEABORT("Destroying plugin instance on the stack.");
|
||||
}
|
||||
mDestroyed = true;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef dom_plugins_PluginInstanceChild_h
|
||||
#define dom_plugins_PluginInstanceChild_h 1
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/plugins/PPluginInstanceChild.h"
|
||||
#include "mozilla/plugins/PluginScriptableObjectChild.h"
|
||||
#include "mozilla/plugins/StreamNotifyChild.h"
|
||||
@@ -272,6 +273,11 @@ public:
|
||||
void NPN_SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed);
|
||||
|
||||
void DoAsyncRedraw();
|
||||
|
||||
virtual bool RecvHandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
const bool& aIsConsumed) override;
|
||||
|
||||
private:
|
||||
friend class PluginModuleChild;
|
||||
|
||||
@@ -394,7 +400,9 @@ private:
|
||||
bool mWindowed;
|
||||
};
|
||||
|
||||
#endif
|
||||
bool ShouldPostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
|
||||
bool MaybePostKeyMessage(UINT message, WPARAM wParam, LPARAM lParam);
|
||||
#endif // #if defined(OS_WIN)
|
||||
const NPPluginFuncs* mPluginIface;
|
||||
nsCString mMimeType;
|
||||
uint16_t mMode;
|
||||
@@ -406,6 +414,8 @@ private:
|
||||
double mContentsScaleFactor;
|
||||
#endif
|
||||
double mCSSZoomFactor;
|
||||
uint32_t mPostingKeyEvents;
|
||||
uint32_t mPostingKeyEventsOutdated;
|
||||
int16_t mDrawingModel;
|
||||
|
||||
NPAsyncSurface* mCurrentDirectSurface;
|
||||
@@ -660,6 +670,37 @@ private:
|
||||
|
||||
// Has this instance been destroyed, either by ActorDestroy or NPP_Destroy?
|
||||
bool mDestroyed;
|
||||
|
||||
#ifdef XP_WIN
|
||||
// WM_*CHAR messages are never consumed by chrome process's widget.
|
||||
// So, if preceding keydown or keyup event is consumed by reserved
|
||||
// shortcut key in the chrome process, we shouldn't send the following
|
||||
// WM_*CHAR messages to the plugin.
|
||||
bool mLastKeyEventConsumed;
|
||||
#endif // #ifdef XP_WIN
|
||||
|
||||
// While IME in the process has composition, this is set to true.
|
||||
// Otherwise, false.
|
||||
static bool sIsIMEComposing;
|
||||
|
||||
// A counter is incremented by AutoStackHelper to indicate that there is an
|
||||
// active plugin call which should be preventing shutdown.
|
||||
public:
|
||||
class AutoStackHelper {
|
||||
public:
|
||||
explicit AutoStackHelper(PluginInstanceChild* instance)
|
||||
: mInstance(instance)
|
||||
{
|
||||
++mInstance->mStackDepth;
|
||||
}
|
||||
~AutoStackHelper() {
|
||||
--mInstance->mStackDepth;
|
||||
}
|
||||
private:
|
||||
PluginInstanceChild *const mInstance;
|
||||
};
|
||||
private:
|
||||
int32_t mStackDepth;
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include <stdint.h> // for intptr_t
|
||||
|
||||
#include "mozilla/BasicEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "PluginInstanceParent.h"
|
||||
@@ -2662,6 +2663,33 @@ PluginInstanceParent::RecvRequestCommitOrCancel(const bool& aCommitted)
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
PluginInstanceParent::HandledWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
if (NS_WARN_IF(!SendHandledWindowedPluginKeyEvent(aKeyEventData,
|
||||
aIsConsumed))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginInstanceParent::RecvOnWindowedPluginKeyEvent(
|
||||
const NativeEventData& aKeyEventData)
|
||||
{
|
||||
nsPluginInstanceOwner* owner = GetOwner();
|
||||
if (NS_WARN_IF(!owner)) {
|
||||
// Notifies the plugin process of the key event being not consumed
|
||||
// by us.
|
||||
HandledWindowedPluginKeyEvent(aKeyEventData, false);
|
||||
return true;
|
||||
}
|
||||
owner->OnWindowedPluginKeyEvent(aKeyEventData);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceParent::RecordDrawingModel()
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "PluginDataResolver.h"
|
||||
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
|
||||
class gfxASurface;
|
||||
class gfxContext;
|
||||
@@ -368,6 +369,14 @@ public:
|
||||
virtual bool
|
||||
RecvRequestCommitOrCancel(const bool& aCommitted) override;
|
||||
|
||||
// for reserved shortcut key handling with windowed plugin on Windows
|
||||
nsresult HandledWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aKeyEventData,
|
||||
bool aIsConsumed);
|
||||
virtual bool
|
||||
RecvOnWindowedPluginKeyEvent(
|
||||
const mozilla::NativeEventData& aKeyEventData) override;
|
||||
|
||||
private:
|
||||
// Create an appropriate platform surface for a background of size
|
||||
// |aSize|. Return true if successful.
|
||||
|
||||
@@ -82,6 +82,10 @@ public:
|
||||
virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) = 0;
|
||||
virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling) = 0;
|
||||
#endif
|
||||
virtual nsresult HandledWindowedPluginKeyEvent(
|
||||
NPP aInstance,
|
||||
const mozilla::NativeEventData& aNativeKeyData,
|
||||
bool aIsCOnsumed) = 0;
|
||||
|
||||
/**
|
||||
* The next three methods are the third leg in the trip to
|
||||
|
||||
@@ -2016,6 +2016,19 @@ PluginModuleParent::UpdateScrollState(NPP aInstance, bool aIsScrolling)
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
PluginModuleParent::HandledWindowedPluginKeyEvent(
|
||||
NPP aInstance,
|
||||
const NativeEventData& aNativeKeyData,
|
||||
bool aIsConsumed)
|
||||
{
|
||||
PluginInstanceParent* parent = PluginInstanceParent::Cast(aInstance);
|
||||
if (NS_WARN_IF(!parent)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return parent->HandledWindowedPluginKeyEvent(aNativeKeyData, aIsConsumed);
|
||||
}
|
||||
|
||||
void
|
||||
PluginModuleParent::OnInitFailure()
|
||||
{
|
||||
|
||||
@@ -258,6 +258,11 @@ protected:
|
||||
virtual nsresult UpdateScrollState(NPP aInstance, bool aIsScrolling);
|
||||
#endif
|
||||
|
||||
virtual nsresult HandledWindowedPluginKeyEvent(
|
||||
NPP aInstance,
|
||||
const mozilla::NativeEventData& aNativeKeyData,
|
||||
bool aIsConsumed) override;
|
||||
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
|
||||
virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override;
|
||||
#else
|
||||
|
||||
@@ -338,6 +338,8 @@ PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject,
|
||||
NS_ASSERTION(actor, "This shouldn't ever be null!");
|
||||
NS_ASSERTION(actor->Type() == Proxy, "Bad type!");
|
||||
|
||||
PluginInstanceChild::AutoStackHelper guard(actor->mInstance);
|
||||
|
||||
Variant result;
|
||||
bool success;
|
||||
actor->CallGetParentProperty(FromNPIdentifier(aName),
|
||||
@@ -707,6 +709,7 @@ bool
|
||||
PluginScriptableObjectChild::AnswerInvalidate()
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
return true;
|
||||
@@ -731,6 +734,7 @@ PluginScriptableObjectChild::AnswerHasMethod(const PluginIdentifier& aId,
|
||||
bool* aHasMethod)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerHasMethod with an invalidated object!");
|
||||
@@ -758,6 +762,7 @@ PluginScriptableObjectChild::AnswerInvoke(const PluginIdentifier& aId,
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerInvoke with an invalidated object!");
|
||||
@@ -828,6 +833,7 @@ PluginScriptableObjectChild::AnswerInvokeDefault(InfallibleTArray<Variant>&& aAr
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerInvokeDefault with an invalidated object!");
|
||||
@@ -896,6 +902,7 @@ PluginScriptableObjectChild::AnswerHasProperty(const PluginIdentifier& aId,
|
||||
bool* aHasProperty)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerHasProperty with an invalidated object!");
|
||||
@@ -924,6 +931,7 @@ PluginScriptableObjectChild::AnswerGetChildProperty(const PluginIdentifier& aId,
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
*aHasProperty = *aHasMethod = *aSuccess = false;
|
||||
*aResult = void_t();
|
||||
@@ -972,6 +980,7 @@ PluginScriptableObjectChild::AnswerSetProperty(const PluginIdentifier& aId,
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerSetProperty with an invalidated object!");
|
||||
@@ -1010,6 +1019,7 @@ PluginScriptableObjectChild::AnswerRemoveProperty(const PluginIdentifier& aId,
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!");
|
||||
@@ -1040,6 +1050,7 @@ PluginScriptableObjectChild::AnswerEnumerate(InfallibleTArray<PluginIdentifier>*
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerEnumerate with an invalidated object!");
|
||||
@@ -1079,6 +1090,7 @@ PluginScriptableObjectChild::AnswerConstruct(InfallibleTArray<Variant>&& aArgs,
|
||||
bool* aSuccess)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
if (mInvalidated) {
|
||||
NS_WARNING("Calling AnswerConstruct with an invalidated object!");
|
||||
@@ -1165,6 +1177,8 @@ bool
|
||||
PluginScriptableObjectChild::Evaluate(NPString* aScript,
|
||||
NPVariant* aResult)
|
||||
{
|
||||
PluginInstanceChild::AutoStackHelper guard(mInstance);
|
||||
|
||||
nsDependentCString script("");
|
||||
if (aScript->UTF8Characters && aScript->UTF8Length) {
|
||||
script.Rebind(aScript->UTF8Characters, aScript->UTF8Length);
|
||||
|
||||
@@ -314,6 +314,12 @@ nsXBLWindowKeyHandler::InstallKeyboardEventListenersTo(
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtCapture());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtCapture());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtCapture());
|
||||
|
||||
// For reducing the IPC cost, preventing to dispatch reserved keyboard
|
||||
// events into the content process.
|
||||
@@ -326,6 +332,12 @@ nsXBLWindowKeyHandler::InstallKeyboardEventListenersTo(
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
|
||||
// Handle keyboard events in bubbling phase of the system event group.
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
@@ -337,6 +349,12 @@ nsXBLWindowKeyHandler::InstallKeyboardEventListenersTo(
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
aEventListenerManager->AddEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
}
|
||||
|
||||
void
|
||||
@@ -352,6 +370,12 @@ nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtCapture());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtCapture());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtCapture());
|
||||
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keydown"),
|
||||
@@ -362,6 +386,12 @@ nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keydown"),
|
||||
@@ -372,6 +402,30 @@ nsXBLWindowKeyHandler::RemoveKeyboardEventListenersFrom(
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeydownonplugin"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
aEventListenerManager->RemoveEventListenerByType(
|
||||
this, NS_LITERAL_STRING("mozkeyuponplugin"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsXBLWindowKeyHandler::ConvertEventToDOMEventType(
|
||||
const WidgetKeyboardEvent& aWidgetKeyboardEvent) const
|
||||
{
|
||||
if (aWidgetKeyboardEvent.IsKeyDownOrKeyDownOnPlugin()) {
|
||||
return nsGkAtoms::keydown;
|
||||
}
|
||||
if (aWidgetKeyboardEvent.IsKeyUpOrKeyUpOnPlugin()) {
|
||||
return nsGkAtoms::keyup;
|
||||
}
|
||||
if (aWidgetKeyboardEvent.mMessage == eKeyPress) {
|
||||
return nsGkAtoms::keypress;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"All event messages which this instance listens to should be handled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -391,11 +445,33 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
|
||||
NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
|
||||
WidgetKeyboardEvent* widgetKeyboardEvent =
|
||||
aEvent->WidgetEventPtr()->AsKeyboardEvent();
|
||||
if (widgetKeyboardEvent->IsKeyEventOnPlugin()) {
|
||||
// key events on plugin shouldn't execute shortcut key handlers which are
|
||||
// not reserved.
|
||||
if (!widgetKeyboardEvent->mIsReserved) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the event is untrusted event or was already consumed, do nothing.
|
||||
if (!widgetKeyboardEvent->IsTrusted() ||
|
||||
widgetKeyboardEvent->DefaultPrevented()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXX Don't check isReserved here because even if the handler in this
|
||||
// instance isn't reserved but another instance reserves the key
|
||||
// combination, it will be executed when the event is normal keyboard
|
||||
// events...
|
||||
bool isReserved = false;
|
||||
if (!HasHandlerForEvent(keyEvent, &isReserved)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom =
|
||||
ConvertEventToDOMEventType(*widgetKeyboardEvent);
|
||||
return WalkHandlers(keyEvent, eventTypeAtom);
|
||||
}
|
||||
|
||||
@@ -406,7 +482,9 @@ nsXBLWindowKeyHandler::HandleEventOnCaptureInDefaultEventGroup(
|
||||
WidgetKeyboardEvent* widgetKeyboardEvent =
|
||||
aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||
|
||||
if (widgetKeyboardEvent->mFlags.mOnlySystemGroupDispatchInContent) {
|
||||
if (widgetKeyboardEvent->mIsReserved) {
|
||||
MOZ_RELEASE_ASSERT(
|
||||
widgetKeyboardEvent->mFlags.mOnlySystemGroupDispatchInContent);
|
||||
MOZ_RELEASE_ASSERT(
|
||||
widgetKeyboardEvent->mFlags.mNoCrossProcessBoundaryForwarding);
|
||||
return;
|
||||
@@ -414,6 +492,7 @@ nsXBLWindowKeyHandler::HandleEventOnCaptureInDefaultEventGroup(
|
||||
|
||||
bool isReserved = false;
|
||||
if (HasHandlerForEvent(aEvent, &isReserved) && isReserved) {
|
||||
widgetKeyboardEvent->mIsReserved = true;
|
||||
// For reserved commands (such as Open New Tab), we don't to wait for
|
||||
// the content to answer (so mWantReplyFromContentProcess remains false),
|
||||
// neither to give a chance for content to override its behavior.
|
||||
@@ -562,6 +641,16 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
|
||||
bool aExecute,
|
||||
bool* aOutReservedForChrome)
|
||||
{
|
||||
if (aOutReservedForChrome) {
|
||||
*aOutReservedForChrome = false;
|
||||
}
|
||||
|
||||
WidgetKeyboardEvent* widgetKeyboardEvent =
|
||||
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||
if (NS_WARN_IF(!widgetKeyboardEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try all of the handlers until we find one that matches the event.
|
||||
for (nsXBLPrototypeHandler* handler = aFirstHandler;
|
||||
handler;
|
||||
@@ -573,9 +662,17 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
|
||||
}
|
||||
|
||||
if (aExecute) {
|
||||
// If it's executing matched handlers, the event type should exactly be
|
||||
// matched.
|
||||
if (!handler->EventTypeEquals(aEventType)) {
|
||||
// If the event is eKeyDownOnPlugin, it should execute either keydown
|
||||
// handler or keypress handler because eKeyDownOnPlugin events are
|
||||
// never followed by keypress events.
|
||||
if (widgetKeyboardEvent->mMessage == eKeyDownOnPlugin) {
|
||||
if (!handler->EventTypeEquals(nsGkAtoms::keydown) &&
|
||||
!handler->EventTypeEquals(nsGkAtoms::keypress)) {
|
||||
continue;
|
||||
}
|
||||
// The other event types should exactly be matched with the handler's
|
||||
// event type.
|
||||
} else if (!handler->EventTypeEquals(aEventType)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@@ -639,6 +736,12 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's not reserved and the event is a key event on a plugin,
|
||||
// the handler shouldn't be executed.
|
||||
if (!isReserved && widgetKeyboardEvent->IsKeyEventOnPlugin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<EventTarget> target;
|
||||
nsCOMPtr<Element> chromeHandlerElement = GetElement();
|
||||
if (chromeHandlerElement) {
|
||||
@@ -661,15 +764,11 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(
|
||||
// shortcut keys even if the key is pressed. Therefore, if there is no
|
||||
// shortcut key which exactly matches current modifier state, we should
|
||||
// retry to look for a shortcut key without the Windows-Logo key press.
|
||||
if (!aIgnoreModifierState.mOS) {
|
||||
WidgetKeyboardEvent* keyEvent =
|
||||
aKeyEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||
if (keyEvent && keyEvent->IsOS()) {
|
||||
IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
|
||||
ignoreModifierState.mOS = true;
|
||||
return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
|
||||
aCharCode, ignoreModifierState, aExecute);
|
||||
}
|
||||
if (!aIgnoreModifierState.mOS && widgetKeyboardEvent->IsOS()) {
|
||||
IgnoreModifierState ignoreModifierState(aIgnoreModifierState);
|
||||
ignoreModifierState.mOS = true;
|
||||
return WalkHandlersAndExecute(aKeyEvent, aEventType, aFirstHandler,
|
||||
aCharCode, ignoreModifierState, aExecute);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -680,7 +779,9 @@ bool
|
||||
nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
|
||||
bool* aOutReservedForChrome)
|
||||
{
|
||||
if (!aEvent->AsEvent()->InternalDOMEvent()->IsTrusted()) {
|
||||
WidgetKeyboardEvent* widgetKeyboardEvent =
|
||||
aEvent->AsEvent()->WidgetEventPtr()->AsKeyboardEvent();
|
||||
if (NS_WARN_IF(!widgetKeyboardEvent) || !widgetKeyboardEvent->IsTrusted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -693,11 +794,8 @@ nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString eventType;
|
||||
aEvent->AsEvent()->GetType(eventType);
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom = NS_Atomize(eventType);
|
||||
NS_ENSURE_TRUE(eventTypeAtom, false);
|
||||
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom =
|
||||
ConvertEventToDOMEventType(*widgetKeyboardEvent);
|
||||
return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false,
|
||||
aOutReservedForChrome);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef nsXBLWindowKeyHandler_h__
|
||||
#define nsXBLWindowKeyHandler_h__
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsWeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
|
||||
@@ -73,6 +74,12 @@ protected:
|
||||
bool HasHandlerForEvent(nsIDOMKeyEvent* aEvent,
|
||||
bool* aOutReservedForChrome = nullptr);
|
||||
|
||||
// Returns event type for matching between aWidgetKeyboardEvent and
|
||||
// shortcut key handlers. This is used for calling WalkHandlers(),
|
||||
// WalkHandlersInternal() and WalkHandlersAndExecute().
|
||||
nsIAtom* ConvertEventToDOMEventType(
|
||||
const mozilla::WidgetKeyboardEvent& aWidgetKeyboardEvent) const;
|
||||
|
||||
// lazily load the handlers. Overridden to handle being attached
|
||||
// to a particular element rather than the document
|
||||
nsresult EnsureHandlers();
|
||||
|
||||
@@ -11,5 +11,7 @@
|
||||
|
||||
#define NS_TOOLTIPTEXTPROVIDER_CONTRACTID \
|
||||
"@mozilla.org/embedcomp/tooltiptextprovider;1"
|
||||
#define NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID \
|
||||
"@mozilla.org/embedcomp/default-tooltiptextprovider;1"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1065,198 +1065,6 @@ nsDocShellTreeOwner::GetOwnerRequestor()
|
||||
return req.forget();
|
||||
}
|
||||
|
||||
class DefaultTooltipTextProvider final : public nsITooltipTextProvider
|
||||
{
|
||||
public:
|
||||
DefaultTooltipTextProvider();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITOOLTIPTEXTPROVIDER
|
||||
|
||||
protected:
|
||||
~DefaultTooltipTextProvider() {}
|
||||
|
||||
nsCOMPtr<nsIAtom> mTag_dialogHeader;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(DefaultTooltipTextProvider, nsITooltipTextProvider)
|
||||
|
||||
DefaultTooltipTextProvider::DefaultTooltipTextProvider()
|
||||
{
|
||||
// There are certain element types which we don't want to use
|
||||
// as tool tip text.
|
||||
mTag_dialogHeader = NS_Atomize("dialogheader");
|
||||
}
|
||||
|
||||
// A helper routine that determines whether we're still interested in SVG
|
||||
// titles. We need to stop at the SVG root element that has a document node
|
||||
// parent.
|
||||
static bool
|
||||
UseSVGTitle(nsIDOMElement* aCurrElement)
|
||||
{
|
||||
nsCOMPtr<dom::Element> element(do_QueryInterface(aCurrElement));
|
||||
if (!element || !element->IsSVGElement() || !element->GetParentNode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return element->GetParentNode()->NodeType() != nsIDOMNode::DOCUMENT_NODE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
|
||||
bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aNode);
|
||||
NS_ENSURE_ARG_POINTER(aText);
|
||||
|
||||
nsString outText;
|
||||
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
||||
|
||||
bool lookingForSVGTitle = true;
|
||||
bool found = false;
|
||||
nsCOMPtr<nsIDOMNode> current(aNode);
|
||||
|
||||
// If the element implement the constraint validation API and has no title,
|
||||
// show the validation message, if any.
|
||||
nsCOMPtr<nsIConstraintValidation> cvElement = do_QueryInterface(current);
|
||||
if (cvElement) {
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(cvElement);
|
||||
nsCOMPtr<nsIAtom> titleAtom = NS_Atomize("title");
|
||||
|
||||
nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(content);
|
||||
bool formHasNoValidate = false;
|
||||
mozilla::dom::Element* form = formControl->GetFormElement();
|
||||
if (form) {
|
||||
nsCOMPtr<nsIAtom> noValidateAtom = NS_Atomize("novalidate");
|
||||
formHasNoValidate = form->HasAttr(kNameSpaceID_None, noValidateAtom);
|
||||
}
|
||||
|
||||
if (!content->HasAttr(kNameSpaceID_None, titleAtom) && !formHasNoValidate) {
|
||||
cvElement->GetValidationMessage(outText);
|
||||
found = !outText.IsEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
while (!found && current) {
|
||||
nsCOMPtr<nsIDOMElement> currElement(do_QueryInterface(current));
|
||||
if (currElement) {
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(currElement));
|
||||
if (content) {
|
||||
if (!content->IsAnyOfXULElements(nsGkAtoms::dialog,
|
||||
mTag_dialogHeader,
|
||||
nsGkAtoms::window)) {
|
||||
// first try the normal title attribute...
|
||||
if (!content->IsSVGElement()) {
|
||||
// If the element is an <input type="file"> without a title,
|
||||
// we should show the current file selection.
|
||||
if (content->IsHTMLElement(nsGkAtoms::input) &&
|
||||
content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("file"), eIgnoreCase) &&
|
||||
!content->HasAttr(kNameSpaceID_None, nsGkAtoms::title)) {
|
||||
found = true;
|
||||
nsCOMPtr<nsIDOMFileList> fileList;
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> currInputElement(do_QueryInterface(currElement));
|
||||
nsresult rv = currInputElement->GetFiles(getter_AddRefs(fileList));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!fileList) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStringBundleService> bundleService =
|
||||
mozilla::services::GetStringBundleService();
|
||||
if (!bundleService) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIStringBundle> bundle;
|
||||
rv = bundleService->CreateBundle("chrome://global/locale/layout/HtmlForm.properties",
|
||||
getter_AddRefs(bundle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
uint32_t listLength = 0;
|
||||
rv = fileList->GetLength(&listLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (listLength == 0) {
|
||||
if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("NoFilesSelected"),
|
||||
getter_Copies(outText));
|
||||
} else {
|
||||
rv = bundle->GetStringFromName(MOZ_UTF16("NoFileSelected"),
|
||||
getter_Copies(outText));
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
FileList* fl = static_cast<FileList*>(fileList.get());
|
||||
fl->Item(0)->GetName(outText);
|
||||
|
||||
// For UX and performance (jank) reasons we cap the number of
|
||||
// files that we list in the tooltip to 20 plus a "and xxx more"
|
||||
// line, or to 21 if exactly 21 files were picked.
|
||||
const uint32_t TRUNCATED_FILE_COUNT = 20;
|
||||
uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT);
|
||||
for (uint32_t i = 1; i < count; ++i) {
|
||||
nsString fileName;
|
||||
fl->Item(i)->GetName(fileName);
|
||||
outText.Append(NS_LITERAL_STRING("\n"));
|
||||
outText.Append(fileName);
|
||||
}
|
||||
}
|
||||
} else if (NS_SUCCEEDED(currElement->GetAttribute(NS_LITERAL_STRING("title"), outText)) &&
|
||||
outText.Length()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// ...ok, that didn't work, try it in the XLink namespace
|
||||
NS_NAMED_LITERAL_STRING(xlinkNS, "http://www.w3.org/1999/xlink");
|
||||
nsCOMPtr<mozilla::dom::Link> linkContent(
|
||||
do_QueryInterface(currElement));
|
||||
if (linkContent) {
|
||||
nsCOMPtr<nsIURI> uri(linkContent->GetURIExternal());
|
||||
if (uri) {
|
||||
currElement->GetAttributeNS(
|
||||
xlinkNS, NS_LITERAL_STRING("title"), outText);
|
||||
if (outText.Length()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (lookingForSVGTitle) {
|
||||
lookingForSVGTitle = UseSVGTitle(currElement);
|
||||
}
|
||||
if (lookingForSVGTitle) {
|
||||
nsINodeList* childNodes = content->ChildNodes();
|
||||
uint32_t childNodeCount = childNodes->Length();
|
||||
for (uint32_t i = 0; i < childNodeCount; i++) {
|
||||
nsIContent* child = childNodes->Item(i);
|
||||
if (child->IsSVGElement(nsGkAtoms::title)) {
|
||||
static_cast<dom::SVGTitleElement*>(child)->GetTextContent(outText);
|
||||
if (outText.Length()) {
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not found here, walk up to the parent and keep trying
|
||||
if (!found) {
|
||||
nsCOMPtr<nsIDOMNode> temp(current);
|
||||
temp->GetParentNode(getter_AddRefs(current));
|
||||
}
|
||||
}
|
||||
|
||||
*aResult = found;
|
||||
*aText = (found) ? ToNewUnicode(outText) : nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ChromeTooltipListener, nsIDOMEventListener)
|
||||
|
||||
ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
|
||||
@@ -1271,7 +1079,7 @@ ChromeTooltipListener::ChromeTooltipListener(nsWebBrowser* aInBrowser,
|
||||
{
|
||||
mTooltipTextProvider = do_GetService(NS_TOOLTIPTEXTPROVIDER_CONTRACTID);
|
||||
if (!mTooltipTextProvider) {
|
||||
mTooltipTextProvider = new DefaultTooltipTextProvider();
|
||||
mTooltipTextProvider = do_GetService(NS_DEFAULTTOOLTIPTEXTPROVIDER_CONTRACTID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1461,7 +1269,8 @@ ChromeTooltipListener::MouseMove(nsIDOMEvent* aMouseEvent)
|
||||
// Tell the registered chrome that they should show the tooltip.
|
||||
NS_IMETHODIMP
|
||||
ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
|
||||
const nsAString& aInTipText)
|
||||
const nsAString& aInTipText,
|
||||
const nsAString& aTipDir)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
@@ -1470,7 +1279,8 @@ ChromeTooltipListener::ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
|
||||
do_QueryInterface(mWebBrowserChrome));
|
||||
if (tooltipListener) {
|
||||
rv = tooltipListener->OnShowTooltip(aInXCoords, aInYCoords,
|
||||
PromiseFlatString(aInTipText).get());
|
||||
PromiseFlatString(aInTipText).get(),
|
||||
PromiseFlatString(aTipDir).get());
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mShowingTooltip = true;
|
||||
}
|
||||
@@ -1559,14 +1369,17 @@ ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
|
||||
// off a timer to auto-hide it.
|
||||
|
||||
nsXPIDLString tooltipText;
|
||||
nsXPIDLString directionText;
|
||||
if (self->mTooltipTextProvider) {
|
||||
bool textFound = false;
|
||||
|
||||
self->mTooltipTextProvider->GetNodeText(
|
||||
self->mPossibleTooltipNode, getter_Copies(tooltipText), &textFound);
|
||||
self->mPossibleTooltipNode, getter_Copies(tooltipText),
|
||||
getter_Copies(directionText), &textFound);
|
||||
|
||||
if (textFound) {
|
||||
nsString tipText(tooltipText);
|
||||
nsString dirText(directionText);
|
||||
LayoutDeviceIntPoint screenDot = widget->WidgetToScreenOffset();
|
||||
double scaleFactor = 1.0;
|
||||
if (shell->GetPresContext()) {
|
||||
@@ -1577,7 +1390,7 @@ ChromeTooltipListener::sTooltipCallback(nsITimer* aTimer,
|
||||
// ShowTooltip expects widget-relative position.
|
||||
self->ShowTooltip(self->mMouseScreenX - screenDot.x / scaleFactor,
|
||||
self->mMouseScreenY - screenDot.y / scaleFactor,
|
||||
tipText);
|
||||
tipText, dirText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,8 @@ private:
|
||||
NS_IMETHOD RemoveTooltipListener();
|
||||
|
||||
NS_IMETHOD ShowTooltip(int32_t aInXCoords, int32_t aInYCoords,
|
||||
const nsAString& aInTipText);
|
||||
const nsAString& aInTipText,
|
||||
const nsAString& aDirText);
|
||||
NS_IMETHOD HideTooltip();
|
||||
|
||||
nsWebBrowser* mWebBrowser;
|
||||
|
||||
@@ -25,6 +25,7 @@ interface nsITooltipListener : nsISupports
|
||||
* @param aTipText The text to display in the tooltip, typically obtained
|
||||
* from the TITLE attribute of the node (or containing parent)
|
||||
* over which the pointer has been positioned.
|
||||
* @param aTipDir The direction (ltr or rtl) in which to display the text
|
||||
*
|
||||
* @note
|
||||
* Coordinates are specified in pixels, relative to the top-left
|
||||
@@ -32,7 +33,8 @@ interface nsITooltipListener : nsISupports
|
||||
*
|
||||
* @return <code>NS_OK</code> if the tooltip was displayed.
|
||||
*/
|
||||
void onShowTooltip(in long aXCoords, in long aYCoords, in wstring aTipText);
|
||||
void onShowTooltip(in long aXCoords, in long aYCoords, in wstring aTipText,
|
||||
in wstring aTipDir);
|
||||
|
||||
/**
|
||||
* Called when the tooltip should be hidden, either because the pointer
|
||||
|
||||
@@ -32,12 +32,13 @@ interface nsITooltipTextProvider : nsISupports
|
||||
/**
|
||||
* Called to obtain the tooltip text for a node.
|
||||
*
|
||||
* @arg aNode The node to obtain the text from.
|
||||
* @arg aText The tooltip text.
|
||||
* @arg aNode The node to obtain the text from.
|
||||
* @arg aText The tooltip text.
|
||||
* @arg aDirection The text direction (ltr or rtl) to use
|
||||
*
|
||||
* @return <CODE>PR_TRUE</CODE> if tooltip text is associated
|
||||
* with the node and was returned in the aText argument;
|
||||
* <CODE>PR_FALSE</CODE> otherwise.
|
||||
*/
|
||||
boolean getNodeText(in nsIDOMNode aNode, out wstring aText);
|
||||
boolean getNodeText(in nsIDOMNode aNode, out wstring aText, out wstring aDirection);
|
||||
};
|
||||
|
||||
@@ -988,15 +988,15 @@ MessageChannel::OnMessageReceivedFromLink(const Message& aMsg)
|
||||
}
|
||||
|
||||
void
|
||||
MessageChannel::PeekMessages(msgid_t aMsgId, mozilla::function<void(const Message& aMsg)> aInvoke)
|
||||
MessageChannel::PeekMessages(msgid_t aMsgId, mozilla::function<bool(const Message& aMsg)> aInvoke)
|
||||
{
|
||||
MonitorAutoLock lock(*mMonitor);
|
||||
|
||||
for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) {
|
||||
Message &msg = *it;
|
||||
|
||||
if (msg.type() == aMsgId) {
|
||||
aInvoke(msg);
|
||||
if (msg.type() == aMsgId && !aInvoke(msg)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,10 +110,10 @@ class MessageChannel : HasResultCodes
|
||||
mAbortOnError = abort;
|
||||
}
|
||||
|
||||
// Call aInvoke for each pending message of type aId.
|
||||
// Call aInvoke for each pending message of type aId until it returns false.
|
||||
// XXX: You must get permission from an IPC peer to use this function
|
||||
// since it requires custom deserialization and re-orders events.
|
||||
void PeekMessages(Message::msgid_t aId, mozilla::function<void(const Message& aMsg)> aInvoke);
|
||||
void PeekMessages(Message::msgid_t aId, mozilla::function<bool(const Message& aMsg)> aInvoke);
|
||||
|
||||
// Misc. behavioral traits consumers can request for this channel
|
||||
enum ChannelFlags {
|
||||
|
||||
@@ -9229,15 +9229,16 @@ nsLayoutUtils::UpdateDisplayPortMarginsFromPendingMessages() {
|
||||
mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()) {
|
||||
mozilla::dom::ContentChild::GetSingleton()->GetIPCChannel()->PeekMessages(
|
||||
mozilla::layers::PAPZ::Msg_UpdateFrame__ID,
|
||||
[](const IPC::Message& aMsg) {
|
||||
[](const IPC::Message& aMsg) -> bool {
|
||||
void* iter = nullptr;
|
||||
FrameMetrics frame;
|
||||
if (!IPC::ReadParam(&aMsg, &iter, &frame)) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
UpdateDisplayPortMarginsForPendingMetrics(frame);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7155,10 +7155,10 @@ PresShell::HandleKeyboardEvent(nsINode* aTarget,
|
||||
// return true if the event target is in its child process
|
||||
bool targetIsIframe = IsTargetIframe(aTarget);
|
||||
|
||||
// Dispatch event directly if the event is synthesized from
|
||||
// nsITextInputProcessor, or there is no need to fire
|
||||
// beforeKey* and afterKey* events.
|
||||
// Dispatch event directly if the event is a keypress event, a key event on
|
||||
// plugin, or there is no need to fire beforeKey* and afterKey* events.
|
||||
if (aEvent.mMessage == eKeyPress ||
|
||||
aEvent.IsKeyEventOnPlugin() ||
|
||||
!BeforeAfterKeyboardEventEnabled()) {
|
||||
ForwardKeyToInputMethodAppOrDispatch(targetIsIframe, aTarget, aEvent,
|
||||
aStatus, aEventCB);
|
||||
@@ -7225,7 +7225,8 @@ PresShell::ForwardKeyToInputMethodApp(nsINode* aTarget,
|
||||
WidgetKeyboardEvent& aEvent,
|
||||
nsEventStatus* aStatus)
|
||||
{
|
||||
if (!XRE_IsParentProcess() || aEvent.mIsSynthesizedByTIP) {
|
||||
if (!XRE_IsParentProcess() || aEvent.mIsSynthesizedByTIP ||
|
||||
aEvent.IsKeyEventOnPlugin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,18 @@
|
||||
nsHtml5Atom::nsHtml5Atom(const nsAString& aString)
|
||||
{
|
||||
mLength = aString.Length();
|
||||
mIsStatic = false;
|
||||
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
|
||||
if (buf) {
|
||||
mString = static_cast<char16_t*>(buf->Data());
|
||||
} else {
|
||||
buf = nsStringBuffer::Alloc((mLength + 1) * sizeof(char16_t));
|
||||
const size_t size = (mLength + 1) * sizeof(char16_t);
|
||||
buf = nsStringBuffer::Alloc(size);
|
||||
if (MOZ_UNLIKELY(!buf)) {
|
||||
// We OOM because atom allocations should be small and it's hard to
|
||||
// handle them more gracefully in a constructor.
|
||||
NS_ABORT_OOM(size);
|
||||
}
|
||||
mString = static_cast<char16_t*>(buf->Data());
|
||||
CopyUnicodeTo(aString, 0, mString, mLength);
|
||||
mString[mLength] = char16_t(0);
|
||||
@@ -68,15 +75,17 @@ nsHtml5Atom::ToUTF8String(nsACString& aReturn)
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
nsHtml5Atom::IsStaticAtom()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHtml5Atom::ScriptableEquals(const nsAString& aString, bool* aResult)
|
||||
{
|
||||
NS_NOTREACHED("Should not call ScriptableEquals.");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(size_t)
|
||||
nsHtml5Atom::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
||||
{
|
||||
NS_NOTREACHED("Should not call SizeOfIncludingThis.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@
|
||||
"no-negated-in-lhs": 2,
|
||||
|
||||
// Nested ternary statements are confusing
|
||||
// "no-nested-ternary": 2,
|
||||
"no-nested-ternary": 2,
|
||||
|
||||
// Use {} instead of new Object()
|
||||
// "no-new-object": 2,
|
||||
|
||||
@@ -1194,9 +1194,7 @@ TreeNode.compareAmounts = function(aA, aB) {
|
||||
};
|
||||
|
||||
TreeNode.compareUnsafeNames = function(aA, aB) {
|
||||
return aA._unsafeName < aB._unsafeName ? -1 :
|
||||
aA._unsafeName > aB._unsafeName ? 1 :
|
||||
0;
|
||||
return aA._unsafeName.localeCompare(aB._unsafeName);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -30,9 +30,9 @@ interface nsIAlertNotification : nsISupports
|
||||
[optional] in boolean inPrivateBrowsing);
|
||||
|
||||
/**
|
||||
* The name of the notification. This is currently only used on Android and
|
||||
* OS X. On Android, the name is hashed and used as a notification ID.
|
||||
* Notifications will replace previous notifications with the same name.
|
||||
* The name of the notification. On Android, the name is hashed and used as
|
||||
* a notification ID. Notifications will replace previous notifications with
|
||||
* the same name.
|
||||
*/
|
||||
readonly attribute AString name;
|
||||
|
||||
@@ -118,32 +118,11 @@ interface nsIAlertsService : nsISupports
|
||||
void showAlert(in nsIAlertNotification alert,
|
||||
[optional] in nsIObserver alertListener);
|
||||
/**
|
||||
* Displays a sliding notification window.
|
||||
* Initializes and shows an |nsIAlertNotification| with the given parameters.
|
||||
*
|
||||
* @param imageUrl A URL identifying the image to put in the alert.
|
||||
* The OS X implemenation limits the amount of time it
|
||||
* will wait for an icon to load to six seconds. After
|
||||
* that time the alert will show with no icon.
|
||||
* @param title The title for the alert.
|
||||
* @param text The contents of the alert.
|
||||
* @param textClickable If true, causes the alert text to look like a link
|
||||
* and notifies the listener when user attempts to
|
||||
* click the alert text.
|
||||
* @param cookie A blind cookie the alert will pass back to the
|
||||
* consumer during the alert listener callbacks.
|
||||
* @param alertListener Used for callbacks. May be null if the caller
|
||||
* doesn't care about callbacks.
|
||||
* @param name The name of the notification. This is currently only
|
||||
* used on Android and OS X. On Android the name is
|
||||
* hashed and used as a notification ID. Notifications
|
||||
* will replace previous notifications with the same name.
|
||||
* @param dir Bidi override for the title. Valid values are
|
||||
* "auto", "ltr" or "rtl". Only available on supported
|
||||
* platforms.
|
||||
* @param lang Language of title and text of the alert. Only available
|
||||
* on supported platforms.
|
||||
* @param inPrivateBrowsing If set to true, imageUrl will be loaded in private
|
||||
* browsing mode.
|
||||
* @see nsIAlertNotification for descriptions of all other parameters.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE If the notification cannot be displayed.
|
||||
*
|
||||
* The following arguments will be passed to the alertListener's observe()
|
||||
|
||||
@@ -1593,12 +1593,13 @@ module.exports = function (chai, _) {
|
||||
}
|
||||
}
|
||||
|
||||
var actuallyGot = ''
|
||||
, expectedThrown = name !== null
|
||||
? name
|
||||
: desiredError
|
||||
? '#{exp}' //_.inspect(desiredError)
|
||||
: 'an error';
|
||||
var actuallyGot = '';
|
||||
var expectedThrown = 'an error';
|
||||
if (name !== null) {
|
||||
expectedThrown = name;
|
||||
} else if (desiredError) {
|
||||
expectedThrown = '#{exp}'; //_.inspect(desiredError)
|
||||
}
|
||||
|
||||
if (thrown) {
|
||||
actuallyGot = ' but #{act} was thrown'
|
||||
|
||||
@@ -415,7 +415,13 @@ var JsDiff = (function() {
|
||||
var ret = [], change;
|
||||
for ( var i = 0; i < changes.length; i++) {
|
||||
change = changes[i];
|
||||
ret.push([(change.added ? 1 : change.removed ? -1 : 0), change.value]);
|
||||
var order = 0;
|
||||
if (change.added) {
|
||||
order = 1;
|
||||
} else if (change.removed) {
|
||||
order = -1;
|
||||
}
|
||||
ret.push([order, change.value]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -2097,12 +2103,13 @@ var color = exports.color = function(type, str) {
|
||||
*/
|
||||
|
||||
exports.window = {
|
||||
width: isatty
|
||||
? process.stdout.getWindowSize
|
||||
? process.stdout.getWindowSize(1)[0]
|
||||
: tty.getWindowSize()[1]
|
||||
: 75
|
||||
width: 75
|
||||
};
|
||||
if (isatty) {
|
||||
exports.window.width = process.stdout.getWindowSize
|
||||
? process.stdout.getWindowSize(1)[0]
|
||||
: tty.getWindowSize()[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose some basic cursor interactions
|
||||
@@ -2240,11 +2247,13 @@ function Base(runner) {
|
||||
stats.passes = stats.passes || 0;
|
||||
|
||||
var medium = test.slow() / 2;
|
||||
test.speed = test.duration > test.slow()
|
||||
? 'slow'
|
||||
: test.duration > medium
|
||||
? 'medium'
|
||||
: 'fast';
|
||||
if (test.duration > test.slow()) {
|
||||
test.speed = 'slow';
|
||||
} else if (test.duration > medium) {
|
||||
test.speed = 'medium';
|
||||
} else {
|
||||
test.speed = 'fast';
|
||||
}
|
||||
|
||||
stats.passes++;
|
||||
});
|
||||
|
||||
@@ -581,11 +581,11 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&
|
||||
var wrapper = undefined;
|
||||
for (var c = element.firstChild; c; c = c.nextSibling) {
|
||||
var type = c.nodeType;
|
||||
wrapper = (type === 1) // Element Node
|
||||
? (wrapper ? element : c)
|
||||
: (type === 3) // Text Node
|
||||
? (notWs.test(c.nodeValue) ? element : wrapper)
|
||||
: wrapper;
|
||||
if (type === 1) {
|
||||
wrapper = wrapper ? element : c;
|
||||
} else if (type === 3) {
|
||||
wrapper = notWs.test(c.nodeValue) ? element : wrapper;
|
||||
}
|
||||
}
|
||||
return wrapper === element ? undefined : wrapper;
|
||||
}
|
||||
@@ -1411,9 +1411,11 @@ var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&
|
||||
// Look for a class like linenums or linenums:<n> where <n> is the
|
||||
// 1-indexed number of the first line.
|
||||
var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
|
||||
lineNums = lineNums
|
||||
? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
|
||||
: false;
|
||||
if (lineNums) {
|
||||
lineNums = lineNums[1] && lineNums[1].length ? +lineNums[1] : true;
|
||||
} else {
|
||||
lineNums = false;
|
||||
}
|
||||
if (lineNums) { numberLines(cs, lineNums); }
|
||||
|
||||
// do the pretty printing
|
||||
|
||||
@@ -53,6 +53,7 @@ DIRS += [
|
||||
'telemetry',
|
||||
'thumbnails',
|
||||
'timermanager',
|
||||
'tooltiptext',
|
||||
'typeaheadfind',
|
||||
'utils',
|
||||
'urlformatter',
|
||||
|
||||
@@ -267,8 +267,11 @@ BookmarkImporter.prototype = {
|
||||
} else {
|
||||
// Ensure tag folder gets processed last
|
||||
nodes[0].children.sort(function sortRoots(aNode, bNode) {
|
||||
return (aNode.root && aNode.root == "tagsFolder") ? 1 :
|
||||
(bNode.root && bNode.root == "tagsFolder") ? -1 : 0;
|
||||
if (aNode.root && aNode.root == "tagsFolder")
|
||||
return 1;
|
||||
if (bNode.root && bNode.root == "tagsFolder")
|
||||
return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
let batch = {
|
||||
|
||||
@@ -974,8 +974,9 @@ function* reorderChildren(parent, orderedChildrenGuids) {
|
||||
let i = orderedChildrenGuids.indexOf(a.guid);
|
||||
let j = orderedChildrenGuids.indexOf(b.guid);
|
||||
// This works provided fetchBookmarksByParent returns sorted children.
|
||||
return (i == -1 && j == -1) ? 0 :
|
||||
(i != -1 && j != -1 && i < j) || (i != -1 && j == -1) ? -1 : 1;
|
||||
if (i == -1 && j == -1)
|
||||
return 0;
|
||||
return (i != -1 && j != -1 && i < j) || (i != -1 && j == -1) ? -1 : 1;
|
||||
});
|
||||
|
||||
// Update the bookmarks position now. If any unknown guid have been
|
||||
|
||||
@@ -169,7 +169,7 @@ this.PlacesBackups = {
|
||||
this._entries.sort((a, b) => {
|
||||
let aDate = this.getDateForFile(a);
|
||||
let bDate = this.getDateForFile(b);
|
||||
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
|
||||
return bDate - aDate;
|
||||
});
|
||||
return this._entries;
|
||||
},
|
||||
@@ -211,7 +211,7 @@ this.PlacesBackups = {
|
||||
this._backupFiles.sort((a, b) => {
|
||||
let aDate = this.getDateForFile(a);
|
||||
let bDate = this.getDateForFile(b);
|
||||
return aDate < bDate ? 1 : aDate > bDate ? -1 : 0;
|
||||
return bDate - aDate;
|
||||
});
|
||||
|
||||
return this._backupFiles;
|
||||
|
||||
@@ -1498,16 +1498,21 @@ Search.prototype = {
|
||||
let typed = Prefs.autofillTyped || this.hasBehavior("typed");
|
||||
let bookmarked = this.hasBehavior("bookmark") && !this.hasBehavior("history");
|
||||
|
||||
return [
|
||||
bookmarked ? typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
|
||||
: SQL_BOOKMARKED_HOST_QUERY
|
||||
: typed ? SQL_TYPED_HOST_QUERY
|
||||
: SQL_HOST_QUERY,
|
||||
{
|
||||
query_type: QUERYTYPE_AUTOFILL_HOST,
|
||||
searchString: this._searchString.toLowerCase()
|
||||
}
|
||||
];
|
||||
let query = [];
|
||||
if (bookmarked) {
|
||||
query.push(typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
|
||||
: SQL_BOOKMARKED_HOST_QUERY);
|
||||
} else {
|
||||
query.push(typed ? SQL_TYPED_HOST_QUERY
|
||||
: SQL_HOST_QUERY);
|
||||
}
|
||||
|
||||
query.push({
|
||||
query_type: QUERYTYPE_AUTOFILL_HOST,
|
||||
searchString: this._searchString.toLowerCase()
|
||||
});
|
||||
|
||||
return query;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -532,9 +532,12 @@ function check_JSON_backup(aIsAutomaticBackup) {
|
||||
*/
|
||||
function frecencyForUrl(aURI)
|
||||
{
|
||||
let url = aURI instanceof Ci.nsIURI ? aURI.spec
|
||||
: aURI instanceof URL ? aURI.href
|
||||
: aURI;
|
||||
let url = aURI;
|
||||
if (aURI instanceof Ci.nsIURI) {
|
||||
url = aURI.spec;
|
||||
} else if (aURI instanceof URL) {
|
||||
url = aURI.href;
|
||||
}
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT frecency FROM moz_places WHERE url = ?1"
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint-env es6:false*/
|
||||
/*
|
||||
* DO NOT MODIFY THIS FILE DIRECTLY!
|
||||
*
|
||||
@@ -33,10 +34,6 @@
|
||||
*/
|
||||
(function (global) {
|
||||
|
||||
function error(m) {
|
||||
dump("JSDOMParser error: " + m + "\n");
|
||||
}
|
||||
|
||||
// XML only defines these and the numeric ones:
|
||||
|
||||
var entityTable = {
|
||||
@@ -283,6 +280,7 @@
|
||||
"meta": true,
|
||||
"param": true,
|
||||
"source": true,
|
||||
"wbr": true
|
||||
};
|
||||
|
||||
var whitespace = [" ", "\t", "\n", "\r"];
|
||||
@@ -675,9 +673,9 @@
|
||||
arr.push(" " + attr.name + '=' + quote + val + quote);
|
||||
}
|
||||
|
||||
if (child.localName in voidElems) {
|
||||
if (child.localName in voidElems && !child.childNodes.length) {
|
||||
// if this is a self-closing element, end it here
|
||||
arr.push(">");
|
||||
arr.push("/>");
|
||||
} else {
|
||||
// otherwise, add its children
|
||||
arr.push(">");
|
||||
@@ -701,12 +699,13 @@
|
||||
set innerHTML(html) {
|
||||
var parser = new JSDOMParser();
|
||||
var node = parser.parse(html);
|
||||
for (let i = this.childNodes.length; --i >= 0;) {
|
||||
var i;
|
||||
for (i = this.childNodes.length; --i >= 0;) {
|
||||
this.childNodes[i].parentNode = null;
|
||||
}
|
||||
this.childNodes = node.childNodes;
|
||||
this.children = node.children;
|
||||
for (let i = this.childNodes.length; --i >= 0;) {
|
||||
for (i = this.childNodes.length; --i >= 0;) {
|
||||
this.childNodes[i].parentNode = this;
|
||||
}
|
||||
},
|
||||
@@ -848,9 +847,16 @@
|
||||
// makeElementNode(), which saves us from having to allocate a new array
|
||||
// every time.
|
||||
this.retPair = [];
|
||||
|
||||
this.errorState = "";
|
||||
};
|
||||
|
||||
JSDOMParser.prototype = {
|
||||
error: function(m) {
|
||||
dump("JSDOMParser error: " + m + "\n");
|
||||
this.errorState += m + "\n";
|
||||
},
|
||||
|
||||
/**
|
||||
* Look at the next character without advancing the index.
|
||||
*/
|
||||
@@ -905,7 +911,7 @@
|
||||
// After a '=', we should see a '"' for the attribute value
|
||||
var c = this.nextChar();
|
||||
if (c !== '"' && c !== "'") {
|
||||
error("Error reading attribute " + name + ", expecting '\"'");
|
||||
this.error("Error reading attribute " + name + ", expecting '\"'");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -958,12 +964,12 @@
|
||||
}
|
||||
|
||||
// If this is a self-closing tag, read '/>'
|
||||
var closed = tag in voidElems;
|
||||
var closed = false;
|
||||
if (c === "/") {
|
||||
closed = true;
|
||||
c = this.nextChar();
|
||||
if (c !== ">") {
|
||||
error("expected '>' to close " + tag);
|
||||
this.error("expected '>' to close " + tag);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1084,16 +1090,16 @@
|
||||
// Read any text as Text node
|
||||
if (c !== "<") {
|
||||
--this.currentChar;
|
||||
let node = new Text();
|
||||
var textNode = new Text();
|
||||
var n = this.html.indexOf("<", this.currentChar);
|
||||
if (n === -1) {
|
||||
node.innerHTML = this.html.substring(this.currentChar, this.html.length);
|
||||
textNode.innerHTML = this.html.substring(this.currentChar, this.html.length);
|
||||
this.currentChar = this.html.length;
|
||||
} else {
|
||||
node.innerHTML = this.html.substring(this.currentChar, n);
|
||||
textNode.innerHTML = this.html.substring(this.currentChar, n);
|
||||
this.currentChar = n;
|
||||
}
|
||||
return node;
|
||||
return textNode;
|
||||
}
|
||||
|
||||
c = this.peekNext();
|
||||
@@ -1133,7 +1139,7 @@
|
||||
}
|
||||
var closingTag = "</" + localName + ">";
|
||||
if (!this.match(closingTag)) {
|
||||
error("expected '" + closingTag + "'");
|
||||
this.error("expected '" + closingTag + "' and got " + this.html.substr(this.currentChar, closingTag.length));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/*eslint-env es6:false*/
|
||||
/*
|
||||
* DO NOT MODIFY THIS FILE DIRECTLY!
|
||||
*
|
||||
@@ -64,18 +65,23 @@ var Readability = function(uri, doc, options) {
|
||||
// Make an AJAX request for each page and append it to the document.
|
||||
this._curPageNum = 1;
|
||||
|
||||
var logEl;
|
||||
|
||||
// Control whether log messages are sent to the console
|
||||
if (this._debug) {
|
||||
function logEl(e) {
|
||||
logEl = function(e) {
|
||||
var rv = e.nodeName + " ";
|
||||
if (e.nodeType == e.TEXT_NODE) {
|
||||
return rv + '("' + e.textContent + '")';
|
||||
}
|
||||
var classDesc = e.className && ("." + e.className.replace(/ /g, "."));
|
||||
var elDesc = e.id ? "(#" + e.id + classDesc + ")" :
|
||||
(classDesc ? "(" + classDesc + ")" : "");
|
||||
var elDesc = "";
|
||||
if (e.id)
|
||||
elDesc = "(#" + e.id + classDesc + ")";
|
||||
else if (classDesc)
|
||||
elDesc = "(" + classDesc + ")";
|
||||
return rv + elDesc;
|
||||
}
|
||||
};
|
||||
this.log = function () {
|
||||
if ("dump" in root) {
|
||||
var msg = Array.prototype.map.call(arguments, function(x) {
|
||||
@@ -108,18 +114,21 @@ Readability.prototype = {
|
||||
// it quits and just show a link.
|
||||
DEFAULT_MAX_PAGES: 5,
|
||||
|
||||
// Element tags to score by default.
|
||||
DEFAULT_TAGS_TO_SCORE: "section,h2,h3,h4,h5,h6,p,td,pre".toUpperCase().split(","),
|
||||
|
||||
// All of the regular expressions in use within readability.
|
||||
// Defined up here so we don't instantiate them repeatedly in loops.
|
||||
REGEXPS: {
|
||||
unlikelyCandidates: /banner|combx|comment|community|disqus|extra|foot|header|menu|remark|rss|share|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|pagination|pager|popup/i,
|
||||
unlikelyCandidates: /banner|combx|comment|community|disqus|extra|foot|header|menu|modal|related|remark|rss|share|shoutbox|sidebar|skyscraper|sponsor|ad-break|agegate|pagination|pager|popup/i,
|
||||
okMaybeItsACandidate: /and|article|body|column|main|shadow/i,
|
||||
positive: /article|body|content|entry|hentry|main|page|pagination|post|text|blog|story/i,
|
||||
negative: /hidden|banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
|
||||
negative: /hidden|^hid$| hid$| hid |^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|modal|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget/i,
|
||||
extraneous: /print|archive|comment|discuss|e[\-]?mail|share|reply|all|login|sign|single|utility/i,
|
||||
byline: /byline|author|dateline|writtenby/i,
|
||||
replaceFonts: /<(\/?)font[^>]*>/gi,
|
||||
normalize: /\s{2,}/g,
|
||||
videos: /https?:\/\/(www\.)?(youtube|youtube-nocookie|player\.vimeo)\.com/i,
|
||||
videos: /\/\/(www\.)?(dailymotion|youtube|youtube-nocookie|player\.vimeo)\.com/i,
|
||||
nextLink: /(next|weiter|continue|>([^\|]|$)|»([^\|]|$))/i,
|
||||
prevLink: /(prev|earl|old|new|<|«)/i,
|
||||
whitespace: /^\s*$/,
|
||||
@@ -172,7 +181,33 @@ Readability.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts each <a> and <img> uri in the given element to an absolute URI.
|
||||
* Concat all nodelists passed as arguments.
|
||||
*
|
||||
* @return ...NodeList
|
||||
* @return Array
|
||||
*/
|
||||
_concatNodeLists: function() {
|
||||
var slice = Array.prototype.slice;
|
||||
var args = slice.call(arguments);
|
||||
var nodeLists = args.map(function(list) {
|
||||
return slice.call(list);
|
||||
});
|
||||
return Array.prototype.concat.apply([], nodeLists);
|
||||
},
|
||||
|
||||
_getAllNodesWithTag: function(node, tagNames) {
|
||||
if (node.querySelectorAll) {
|
||||
return node.querySelectorAll(tagNames.join(','));
|
||||
}
|
||||
return [].concat.apply([], tagNames.map(function(tag) {
|
||||
var collection = node.getElementsByTagName(tag);
|
||||
return Array.isArray(collection) ? collection : Array.from(collection);
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts each <a> and <img> uri in the given element to an absolute URI,
|
||||
* ignoring #ref URIs.
|
||||
*
|
||||
* @param Element
|
||||
* @return void
|
||||
@@ -199,6 +234,10 @@ Readability.prototype = {
|
||||
if (uri.indexOf("./") === 0)
|
||||
return pathBase + uri.slice(2);
|
||||
|
||||
// Ignore hash URIs:
|
||||
if (uri[0] == "#")
|
||||
return uri;
|
||||
|
||||
// Standard relative URI; add entire path. pathBase already includes a
|
||||
// trailing "/".
|
||||
return pathBase + uri;
|
||||
@@ -252,10 +291,24 @@ Readability.prototype = {
|
||||
if (curTitle.split(' ').length < 3)
|
||||
curTitle = origTitle.replace(/[^\|\-]*[\|\-](.*)/gi,'$1');
|
||||
} else if (curTitle.indexOf(': ') !== -1) {
|
||||
curTitle = origTitle.replace(/.*:(.*)/gi, '$1');
|
||||
// Check if we have an heading containing this exact string, so we
|
||||
// could assume it's the full title.
|
||||
var headings = this._concatNodeLists(
|
||||
doc.getElementsByTagName('h1'),
|
||||
doc.getElementsByTagName('h2')
|
||||
);
|
||||
var match = this._someNode(headings, function(heading) {
|
||||
return heading.textContent === curTitle;
|
||||
});
|
||||
|
||||
if (curTitle.split(' ').length < 3)
|
||||
curTitle = origTitle.replace(/[^:]*[:](.*)/gi,'$1');
|
||||
// If we don't, let's extract the title out of the original title string.
|
||||
if (!match) {
|
||||
curTitle = origTitle.substring(origTitle.lastIndexOf(':') + 1);
|
||||
|
||||
// If the title is now too short, try the first colon instead:
|
||||
if (curTitle.split(' ').length < 3)
|
||||
curTitle = origTitle.substring(origTitle.indexOf(':') + 1);
|
||||
}
|
||||
} else if (curTitle.length > 150 || curTitle.length < 15) {
|
||||
var hOnes = doc.getElementsByTagName('h1');
|
||||
|
||||
@@ -329,9 +382,9 @@ Readability.prototype = {
|
||||
// (which will be replaced with a <p> later).
|
||||
while ((next = this._nextElement(next)) && (next.tagName == "BR")) {
|
||||
replaced = true;
|
||||
let sibling = next.nextSibling;
|
||||
var brSibling = next.nextSibling;
|
||||
next.parentNode.removeChild(next);
|
||||
next = sibling;
|
||||
next = brSibling;
|
||||
}
|
||||
|
||||
// If we removed a <br> chain, replace the remaining <br> with a <p>. Add
|
||||
@@ -351,7 +404,7 @@ Readability.prototype = {
|
||||
}
|
||||
|
||||
// Otherwise, make this node a child of the new <p>.
|
||||
let sibling = next.nextSibling;
|
||||
var sibling = next.nextSibling;
|
||||
p.appendChild(next);
|
||||
next = sibling;
|
||||
}
|
||||
@@ -396,6 +449,7 @@ Readability.prototype = {
|
||||
this._clean(articleContent, "object");
|
||||
this._clean(articleContent, "embed");
|
||||
this._clean(articleContent, "h1");
|
||||
this._clean(articleContent, "footer");
|
||||
|
||||
// If there is only one h2, they are probably using it as a header
|
||||
// and not a subheader, so remove it since we already have a header.
|
||||
@@ -556,6 +610,18 @@ Readability.prototype = {
|
||||
return false;
|
||||
},
|
||||
|
||||
_getNodeAncestors: function(node, maxDepth) {
|
||||
maxDepth = maxDepth || 0;
|
||||
var i = 0, ancestors = [];
|
||||
while (node.parentNode) {
|
||||
ancestors.push(node.parentNode)
|
||||
if (maxDepth && ++i === maxDepth)
|
||||
break;
|
||||
node = node.parentNode;
|
||||
}
|
||||
return ancestors;
|
||||
},
|
||||
|
||||
/***
|
||||
* grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
|
||||
* most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
|
||||
@@ -610,8 +676,9 @@ Readability.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
if (node.tagName === "P" || node.tagName === "TD" || node.tagName === "PRE")
|
||||
if (this.DEFAULT_TAGS_TO_SCORE.indexOf(node.tagName) !== -1) {
|
||||
elementsToScore.push(node);
|
||||
}
|
||||
|
||||
// Turn all divs that don't have children block level elements into p's
|
||||
if (node.tagName === "DIV") {
|
||||
@@ -650,30 +717,18 @@ Readability.prototype = {
|
||||
**/
|
||||
var candidates = [];
|
||||
this._forEachNode(elementsToScore, function(elementToScore) {
|
||||
var parentNode = elementToScore.parentNode;
|
||||
var grandParentNode = parentNode ? parentNode.parentNode : null;
|
||||
var innerText = this._getInnerText(elementToScore);
|
||||
|
||||
if (!parentNode || typeof(parentNode.tagName) === 'undefined')
|
||||
if (!elementToScore.parentNode || typeof(elementToScore.parentNode.tagName) === 'undefined')
|
||||
return;
|
||||
|
||||
// If this paragraph is less than 25 characters, don't even count it.
|
||||
var innerText = this._getInnerText(elementToScore);
|
||||
if (innerText.length < 25)
|
||||
return;
|
||||
|
||||
// Initialize readability data for the parent.
|
||||
if (typeof parentNode.readability === 'undefined') {
|
||||
this._initializeNode(parentNode);
|
||||
candidates.push(parentNode);
|
||||
}
|
||||
|
||||
// Initialize readability data for the grandparent.
|
||||
if (grandParentNode &&
|
||||
typeof(grandParentNode.readability) === 'undefined' &&
|
||||
typeof(grandParentNode.tagName) !== 'undefined') {
|
||||
this._initializeNode(grandParentNode);
|
||||
candidates.push(grandParentNode);
|
||||
}
|
||||
// Exclude nodes with no ancestor.
|
||||
var ancestors = this._getNodeAncestors(elementToScore, 3);
|
||||
if (ancestors.length === 0)
|
||||
return;
|
||||
|
||||
var contentScore = 0;
|
||||
|
||||
@@ -686,11 +741,28 @@ Readability.prototype = {
|
||||
// For every 100 characters in this paragraph, add another point. Up to 3 points.
|
||||
contentScore += Math.min(Math.floor(innerText.length / 100), 3);
|
||||
|
||||
// Add the score to the parent. The grandparent gets half.
|
||||
parentNode.readability.contentScore += contentScore;
|
||||
// Initialize and score ancestors.
|
||||
this._forEachNode(ancestors, function(ancestor, level) {
|
||||
if (!ancestor.tagName)
|
||||
return;
|
||||
|
||||
if (grandParentNode)
|
||||
grandParentNode.readability.contentScore += contentScore / 2;
|
||||
if (typeof(ancestor.readability) === 'undefined') {
|
||||
this._initializeNode(ancestor);
|
||||
candidates.push(ancestor);
|
||||
}
|
||||
|
||||
// Node score divider:
|
||||
// - parent: 1 (no division)
|
||||
// - grandparent: 2
|
||||
// - great grandparent+: ancestor level * 3
|
||||
if (level === 0)
|
||||
var scoreDivider = 1;
|
||||
else if (level === 1)
|
||||
scoreDivider = 2;
|
||||
else
|
||||
scoreDivider = level * 3;
|
||||
ancestor.readability.contentScore += contentScore / scoreDivider;
|
||||
});
|
||||
});
|
||||
|
||||
// After we've calculated scores, loop through all of the possible
|
||||
@@ -801,7 +873,8 @@ Readability.prototype = {
|
||||
|
||||
if (nodeLength > 80 && linkDensity < 0.25) {
|
||||
append = true;
|
||||
} else if (nodeLength < 80 && linkDensity === 0 && nodeContent.search(/\.( |$)/) !== -1) {
|
||||
} else if (nodeLength < 80 && nodeLength > 0 && linkDensity === 0 &&
|
||||
nodeContent.search(/\.( |$)/) !== -1) {
|
||||
append = true;
|
||||
}
|
||||
}
|
||||
@@ -818,10 +891,6 @@ Readability.prototype = {
|
||||
sibling = this._setNodeTag(sibling, "DIV");
|
||||
}
|
||||
|
||||
// To ensure a node does not interfere with readability styles,
|
||||
// remove its classnames.
|
||||
sibling.removeAttribute("class");
|
||||
|
||||
articleContent.appendChild(sibling);
|
||||
// siblings is a reference to the children array, and
|
||||
// sibling is removed from the array when we call appendChild().
|
||||
@@ -913,17 +982,17 @@ Readability.prototype = {
|
||||
|
||||
// Match "description", or Twitter's "twitter:description" (Cards)
|
||||
// in name attribute.
|
||||
var namePattern = /^\s*((twitter)\s*:\s*)?description\s*$/gi;
|
||||
var namePattern = /^\s*((twitter)\s*:\s*)?(description|title)\s*$/gi;
|
||||
|
||||
// Match Facebook's og:description (Open Graph) in property attribute.
|
||||
var propertyPattern = /^\s*og\s*:\s*description\s*$/gi;
|
||||
// Match Facebook's Open Graph title & description properties.
|
||||
var propertyPattern = /^\s*og\s*:\s*(description|title)\s*$/gi;
|
||||
|
||||
// Find description tags.
|
||||
this._forEachNode(metaElements, function(element) {
|
||||
var elementName = element.getAttribute("name");
|
||||
var elementProperty = element.getAttribute("property");
|
||||
|
||||
if (elementName === "author") {
|
||||
if ([elementName, elementProperty].indexOf("author") !== -1) {
|
||||
metadata.byline = element.getAttribute("content");
|
||||
return;
|
||||
}
|
||||
@@ -956,6 +1025,14 @@ Readability.prototype = {
|
||||
metadata.excerpt = values["twitter:description"];
|
||||
}
|
||||
|
||||
if ("og:title" in values) {
|
||||
// Use facebook open graph title.
|
||||
metadata.title = values["og:title"];
|
||||
} else if ("twitter:title" in values) {
|
||||
// Use twitter cards title.
|
||||
metadata.title = values["twitter:title"];
|
||||
}
|
||||
|
||||
return metadata;
|
||||
},
|
||||
|
||||
@@ -1082,7 +1159,7 @@ Readability.prototype = {
|
||||
_getLinkDensity: function(element) {
|
||||
var textLength = this._getInnerText(element).length;
|
||||
if (textLength === 0)
|
||||
return undefined;
|
||||
return 0;
|
||||
|
||||
var linkLength = 0;
|
||||
|
||||
@@ -1559,6 +1636,7 @@ Readability.prototype = {
|
||||
|
||||
var tagsList = e.getElementsByTagName(tag);
|
||||
var curTagsLength = tagsList.length;
|
||||
var isList = tag === "ul" || tag === "ol";
|
||||
|
||||
// Gather counts for other typical elements embedded within.
|
||||
// Traverse backwards so we can remove nodes at the same time
|
||||
@@ -1594,13 +1672,13 @@ Readability.prototype = {
|
||||
var toRemove = false;
|
||||
if (img > p && !this._hasAncestorTag(tagsList[i], "figure")) {
|
||||
toRemove = true;
|
||||
} else if (li > p && tag !== "ul" && tag !== "ol") {
|
||||
} else if (!isList && li > p) {
|
||||
toRemove = true;
|
||||
} else if ( input > Math.floor(p/3) ) {
|
||||
} else if (input > Math.floor(p/3)) {
|
||||
toRemove = true;
|
||||
} else if (contentLength < 25 && (img === 0 || img > 2) ) {
|
||||
} else if (!isList && contentLength < 25 && (img === 0 || img > 2)) {
|
||||
toRemove = true;
|
||||
} else if (weight < 25 && linkDensity > 0.2) {
|
||||
} else if (!isList && weight < 25 && linkDensity > 0.2) {
|
||||
toRemove = true;
|
||||
} else if (weight >= 25 && linkDensity > 0.5) {
|
||||
toRemove = true;
|
||||
@@ -1625,7 +1703,7 @@ Readability.prototype = {
|
||||
for (var headerIndex = 1; headerIndex < 3; headerIndex += 1) {
|
||||
var headers = e.getElementsByTagName('h' + headerIndex);
|
||||
for (var i = headers.length - 1; i >= 0; i -= 1) {
|
||||
if (this._getClassWeight(headers[i]) < 0 || this._getLinkDensity(headers[i]) > 0.33)
|
||||
if (this._getClassWeight(headers[i]) < 0)
|
||||
headers[i].parentNode.removeChild(headers[i]);
|
||||
}
|
||||
}
|
||||
@@ -1648,32 +1726,42 @@ Readability.prototype = {
|
||||
*
|
||||
* @return boolean Whether or not we suspect parse() will suceeed at returning an article object.
|
||||
*/
|
||||
isProbablyReaderable: function() {
|
||||
var nodes = this._doc.getElementsByTagName("p");
|
||||
if (nodes.length < 5) {
|
||||
return false;
|
||||
}
|
||||
isProbablyReaderable: function(helperIsVisible) {
|
||||
var nodes = this._getAllNodesWithTag(this._doc, ["p", "pre"]);
|
||||
|
||||
var possibleParagraphs = 0;
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
// FIXME we should have a fallback for helperIsVisible, but this is
|
||||
// problematic because of jsdom's elem.style handling - see
|
||||
// https://github.com/mozilla/readability/pull/186 for context.
|
||||
|
||||
var score = 0;
|
||||
// This is a little cheeky, we use the accumulator 'score' to decide what to return from
|
||||
// this callback:
|
||||
return this._someNode(nodes, function(node) {
|
||||
if (helperIsVisible && !helperIsVisible(node))
|
||||
return false;
|
||||
var matchString = node.className + " " + node.id;
|
||||
|
||||
if (this.REGEXPS.unlikelyCandidates.test(matchString) &&
|
||||
!this.REGEXPS.okMaybeItsACandidate.test(matchString)) {
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.textContent.trim().length < 100) {
|
||||
continue;
|
||||
if (node.matches && node.matches("li p")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
possibleParagraphs++;
|
||||
if (possibleParagraphs >= 5) {
|
||||
var textContentLength = node.textContent.trim().length;
|
||||
if (textContentLength < 140) {
|
||||
return false;
|
||||
}
|
||||
|
||||
score += Math.sqrt(textContentLength - 140);
|
||||
|
||||
if (score > 20) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1715,8 +1803,8 @@ Readability.prototype = {
|
||||
|
||||
this._prepDocument();
|
||||
|
||||
var articleTitle = this._getArticleTitle();
|
||||
var metadata = this._getArticleMetadata();
|
||||
var articleTitle = metadata.title || this._getArticleTitle();
|
||||
|
||||
var articleContent = this._grabArticle();
|
||||
if (!articleContent)
|
||||
|
||||
@@ -103,8 +103,10 @@ const TESTS = [ {
|
||||
lastUpdateTime : 0
|
||||
} ];
|
||||
|
||||
let gUTM;
|
||||
let gNextFunc;
|
||||
const DEBUG_TEST = false;
|
||||
|
||||
var gUTM;
|
||||
var gNextFunc;
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPref",
|
||||
"@mozilla.org/preferences-service;1",
|
||||
@@ -218,35 +220,35 @@ function finished_test1thru7() {
|
||||
}
|
||||
|
||||
function check_test1thru7() {
|
||||
do_print("Testing: a category registered timer didn't fire due to an " +
|
||||
"invalid default interval");
|
||||
debugDump("Testing: a category registered timer didn't fire due to an " +
|
||||
"invalid default interval");
|
||||
do_check_false(TESTS[0].notified);
|
||||
|
||||
do_print("Testing: a category registered timer didn't fire due to not " +
|
||||
"implementing nsITimerCallback");
|
||||
debugDump("Testing: a category registered timer didn't fire due to not " +
|
||||
"implementing nsITimerCallback");
|
||||
do_check_false(TESTS[1].notified);
|
||||
|
||||
do_print("Testing: a category registered timer didn't fire due to the next " +
|
||||
"update time being in the future");
|
||||
debugDump("Testing: a category registered timer didn't fire due to the " +
|
||||
"next update time being in the future");
|
||||
do_check_false(TESTS[2].notified);
|
||||
|
||||
do_print("Testing: a category registered timer didn't fire due to not " +
|
||||
"having a notify method");
|
||||
debugDump("Testing: a category registered timer didn't fire due to not " +
|
||||
"having a notify method");
|
||||
do_check_false(TESTS[3].notified);
|
||||
|
||||
do_print("Testing: a category registered timer has fired");
|
||||
debugDump("Testing: a category registered timer has fired");
|
||||
do_check_true(TESTS[4].notified);
|
||||
|
||||
do_print("Testing: a category registered timer fired that has an interval " +
|
||||
"preference that overrides a default that wouldn't have fired yet");
|
||||
debugDump("Testing: a category registered timer fired that has an interval " +
|
||||
"preference that overrides a default that wouldn't have fired yet");
|
||||
do_check_true(TESTS[5].notified);
|
||||
|
||||
do_print("Testing: a category registered timer has fired due to the next " +
|
||||
"update time being reset due to a future last update time");
|
||||
debugDump("Testing: a category registered timer has fired due to the next " +
|
||||
"update time being reset due to a future last update time");
|
||||
do_check_true(TESTS[6].notified);
|
||||
|
||||
do_print("Testing: two category registered timers last update time has " +
|
||||
"user values");
|
||||
debugDump("Testing: two category registered timers last update time has " +
|
||||
"user values");
|
||||
do_check_true(gPref.prefHasUserValue(PREF_BRANCH_LAST_UPDATE_TIME +
|
||||
TESTS[4].timerID));
|
||||
do_check_true(gPref.prefHasUserValue(PREF_BRANCH_LAST_UPDATE_TIME +
|
||||
@@ -264,8 +266,8 @@ function check_test1thru7() {
|
||||
gCatMan.deleteCategoryEntry(CATEGORY_UPDATE_TIMER, entry, false);
|
||||
count++;
|
||||
}
|
||||
do_print("Testing: no " + CATEGORY_UPDATE_TIMER + " categories are still " +
|
||||
"registered");
|
||||
debugDump("Testing: no " + CATEGORY_UPDATE_TIMER + " categories are still " +
|
||||
"registered");
|
||||
do_check_eq(count, 0);
|
||||
|
||||
do_execute_soon(run_test8);
|
||||
@@ -287,7 +289,7 @@ function check_test8(aTestTimerCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_print("Testing: two registerTimer registered timers have fired");
|
||||
debugDump("Testing: two registerTimer registered timers have fired");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
do_check_true(TESTS[7 + i].notified);
|
||||
}
|
||||
@@ -298,8 +300,8 @@ function check_test8(aTestTimerCallback) {
|
||||
do_check_true(Math.abs(TESTS[7].notifyTime - TESTS[8].notifyTime) >=
|
||||
MAIN_TIMER_INTERVAL * 0.5);
|
||||
|
||||
do_print("Testing: two registerTimer registered timers last update time have " +
|
||||
"been updated");
|
||||
debugDump("Testing: two registerTimer registered timers last update time " +
|
||||
"have been updated");
|
||||
for (let i = 0; i < 2; i++) {
|
||||
do_check_neq(gPref.getIntPref(PREF_BRANCH_LAST_UPDATE_TIME + TESTS[7 + i].timerID), 1);
|
||||
}
|
||||
@@ -460,3 +462,49 @@ const gTest9Factory = {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logs TEST-INFO messages.
|
||||
*
|
||||
* @param aText
|
||||
* The text to log.
|
||||
* @param aCaller (optional)
|
||||
* An optional Components.stack.caller. If not specified
|
||||
* Components.stack.caller will be used.
|
||||
*/
|
||||
function logTestInfo(aText, aCaller) {
|
||||
let caller = aCaller ? aCaller : Components.stack.caller;
|
||||
let now = new Date;
|
||||
let hh = now.getHours();
|
||||
let mm = now.getMinutes();
|
||||
let ss = now.getSeconds();
|
||||
let ms = now.getMilliseconds();
|
||||
let time = (hh < 10 ? "0" + hh : hh) + ":" +
|
||||
(mm < 10 ? "0" + mm : mm) + ":" +
|
||||
(ss < 10 ? "0" + ss : ss) + ":";
|
||||
if (ms < 10) {
|
||||
time += "00";
|
||||
}
|
||||
else if (ms < 100) {
|
||||
time += "0";
|
||||
}
|
||||
time += ms;
|
||||
let msg = time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
|
||||
" : " + caller.lineNumber + "] " + aText;
|
||||
do_print(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs TEST-INFO messages when DEBUG_TEST evaluates to true.
|
||||
*
|
||||
* @param aText
|
||||
* The text to log.
|
||||
* @param aCaller (optional)
|
||||
* An optional Components.stack.caller. If not specified
|
||||
* Components.stack.caller will be used.
|
||||
*/
|
||||
function debugDump(aText, aCaller) {
|
||||
if (DEBUG_TEST) {
|
||||
logTestInfo(aText, aCaller);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/* 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/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function TooltipTextProvider() {}
|
||||
|
||||
TooltipTextProvider.prototype = {
|
||||
getNodeText(tipElement, textOut, directionOut) {
|
||||
// Don't show the tooltip if the tooltip node is a document, browser, or disconnected.
|
||||
if (!tipElement || !tipElement.ownerDocument ||
|
||||
tipElement.localName == "browser" ||
|
||||
(tipElement.ownerDocument.compareDocumentPosition(tipElement) &
|
||||
tipElement.ownerDocument.DOCUMENT_POSITION_DISCONNECTED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var defView = tipElement.ownerDocument.defaultView;
|
||||
// XXX Work around bug 350679:
|
||||
// "Tooltips can be fired in documents with no view".
|
||||
if (!defView)
|
||||
return false;
|
||||
|
||||
const XLinkNS = "http://www.w3.org/1999/xlink";
|
||||
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
var titleText = null;
|
||||
var XLinkTitleText = null;
|
||||
var SVGTitleText = null;
|
||||
var XULtooltiptextText = null;
|
||||
var lookingForSVGTitle = true;
|
||||
var direction = tipElement.ownerDocument.dir;
|
||||
|
||||
// If the element is invalid per HTML5 Forms specifications and has no title,
|
||||
// show the constraint validation error message.
|
||||
if ((tipElement instanceof defView.HTMLInputElement ||
|
||||
tipElement instanceof defView.HTMLTextAreaElement ||
|
||||
tipElement instanceof defView.HTMLSelectElement ||
|
||||
tipElement instanceof defView.HTMLButtonElement) &&
|
||||
!tipElement.hasAttribute('title') &&
|
||||
(!tipElement.form || !tipElement.form.noValidate)) {
|
||||
// If the element is barred from constraint validation or valid,
|
||||
// the validation message will be the empty string.
|
||||
titleText = tipElement.validationMessage || null;
|
||||
}
|
||||
|
||||
// If the element is an <input type='file'> without a title, we should show
|
||||
// the current file selection.
|
||||
if (!titleText &&
|
||||
tipElement instanceof defView.HTMLInputElement &&
|
||||
tipElement.type == 'file' &&
|
||||
!tipElement.hasAttribute('title')) {
|
||||
let files = tipElement.files;
|
||||
|
||||
try {
|
||||
var bundle =
|
||||
Services.strings.createBundle("chrome://global/locale/layout/HtmlForm.properties");
|
||||
if (files.length == 0) {
|
||||
if (tipElement.multiple) {
|
||||
titleText = bundle.GetStringFromName("NoFilesSelected");
|
||||
} else {
|
||||
titleText = bundle.GetStringFromName("NoFileSelected");
|
||||
}
|
||||
} else {
|
||||
titleText = files[0].name;
|
||||
// For UX and performance (jank) reasons we cap the number of
|
||||
// files that we list in the tooltip to 20 plus a "and xxx more"
|
||||
// line, or to 21 if exactly 21 files were picked.
|
||||
const TRUNCATED_FILE_COUNT = 20;
|
||||
let count = Math.min(files.length, TRUNCATED_FILE_COUNT);
|
||||
for (let i = 1; i < count; ++i) {
|
||||
titleText += "\n" + files[i].name;
|
||||
}
|
||||
if (files.length == TRUNCATED_FILE_COUNT + 1) {
|
||||
titleText += "\n" + files[TRUNCATED_FILE_COUNT].name;
|
||||
} else if (files.length > TRUNCATED_FILE_COUNT + 1) {
|
||||
let xmoreStr = bundle.GetStringFromName("AndNMoreFiles");
|
||||
let xmoreNum = files.length - TRUNCATED_FILE_COUNT;
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/PluralForm.jsm", tmp);
|
||||
let andXMoreStr = tmp.PluralForm.get(xmoreNum, xmoreStr).replace("#1", xmoreNum);
|
||||
titleText += "\n" + andXMoreStr;
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// Check texts against null so that title="" can be used to undefine a
|
||||
// title on a child element.
|
||||
while (tipElement &&
|
||||
(titleText == null) && (XLinkTitleText == null) &&
|
||||
(SVGTitleText == null) && (XULtooltiptextText == null)) {
|
||||
|
||||
if (tipElement.nodeType == defView.Node.ELEMENT_NODE) {
|
||||
if (tipElement.namespaceURI == XULNS)
|
||||
XULtooltiptextText = tipElement.getAttribute("tooltiptext");
|
||||
else if (!(tipElement instanceof defView.SVGElement))
|
||||
titleText = tipElement.getAttribute("title");
|
||||
|
||||
if ((tipElement instanceof defView.HTMLAnchorElement ||
|
||||
tipElement instanceof defView.HTMLAreaElement ||
|
||||
tipElement instanceof defView.HTMLLinkElement ||
|
||||
tipElement instanceof defView.SVGAElement) && tipElement.href) {
|
||||
XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
|
||||
}
|
||||
if (lookingForSVGTitle &&
|
||||
(!(tipElement instanceof defView.SVGElement) ||
|
||||
tipElement.parentNode.nodeType == defView.Node.DOCUMENT_NODE)) {
|
||||
lookingForSVGTitle = false;
|
||||
}
|
||||
if (lookingForSVGTitle) {
|
||||
for (let childNode of tipElement.childNodes) {
|
||||
if (childNode instanceof defView.SVGTitleElement) {
|
||||
SVGTitleText = childNode.textContent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
direction = defView.getComputedStyle(tipElement, "")
|
||||
.getPropertyValue("direction");
|
||||
}
|
||||
|
||||
tipElement = tipElement.parentNode;
|
||||
}
|
||||
|
||||
return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some(function (t) {
|
||||
if (t && /\S/.test(t)) {
|
||||
// Make CRLF and CR render one line break each.
|
||||
textOut.value = t.replace(/\r\n?/g, '\n');
|
||||
directionOut.value = direction;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
||||
classID : Components.ID("{f376627f-0bbc-47b8-887e-fc92574cc91f}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITooltipTextProvider]),
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TooltipTextProvider]);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
component {f376627f-0bbc-47b8-887e-fc92574cc91f} TooltipTextProvider.js
|
||||
contract @mozilla.org/embedcomp/default-tooltiptextprovider;1 {f376627f-0bbc-47b8-887e-fc92574cc91f}
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; 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/.
|
||||
|
||||
# BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'TooltipTextProvider.js',
|
||||
'TooltipTextProvider.manifest',
|
||||
]
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'General')
|
||||
@@ -393,7 +393,11 @@ function testtag_tree_TreeSelection_UI(tree, testid, multiple)
|
||||
// do this three times, one for each state of pageUpOrDownMovesSelection,
|
||||
// and then once with the accel key pressed
|
||||
for (let t = 0; t < 3; t++) {
|
||||
let testidmod = (t == 2) ? " with accel" : (t == 1) ? " rev" : "";
|
||||
let testidmod = "";
|
||||
if (t == 2)
|
||||
testidmod = " with accel"
|
||||
else if (t == 1)
|
||||
testidmod = " rev";
|
||||
var keymod = (t == 2) ? { accelKey: true } : { };
|
||||
|
||||
var moveselection = tree.pageUpOrDownMovesSelection;
|
||||
@@ -1164,10 +1168,20 @@ function testtag_tree_wheel(aTree)
|
||||
function helper(aStart, aDelta, aIntDelta, aDeltaMode)
|
||||
{
|
||||
aTree.treeBoxObject.scrollToRow(aStart);
|
||||
var expected = !aIntDelta ? aStart :
|
||||
aDeltaMode != WheelEvent.DOM_DELTA_PAGE ? aStart + aIntDelta :
|
||||
aIntDelta > 0 ? aStart + aTree.treeBoxObject.getPageLength() :
|
||||
aStart - aTree.treeBoxObject.getPageLength();
|
||||
var expected;
|
||||
if (!aIntDelta) {
|
||||
expected = aStart;
|
||||
}
|
||||
else if (aDeltaMode != WheelEvent.DOM_DELTA_PAGE) {
|
||||
expected = aStart + aIntDelta;
|
||||
}
|
||||
else if (aIntDelta > 0) {
|
||||
expected = aStart + aTree.treeBoxObject.getPageLength();
|
||||
}
|
||||
else {
|
||||
expected = aStart - aTree.treeBoxObject.getPageLength();
|
||||
}
|
||||
|
||||
if (expected < 0) {
|
||||
expected = 0;
|
||||
}
|
||||
|
||||
@@ -63,8 +63,11 @@
|
||||
var state = this.getAttribute("checkState");
|
||||
if (state == "")
|
||||
return this.checked ? 1 : 0;
|
||||
else
|
||||
return state == "0" ? 0 : (state == "2" ? 2 : 1);
|
||||
if (state == "0")
|
||||
return 0;
|
||||
if (state == "2")
|
||||
return 2;
|
||||
return 1;
|
||||
]]></getter>
|
||||
<setter><![CDATA[
|
||||
this.setAttribute("checkState", val);
|
||||
|
||||
@@ -778,24 +778,29 @@
|
||||
|
||||
var yi = 2, mi = 4, di = 6;
|
||||
|
||||
function fieldForNumber(i) {
|
||||
if (i == 2)
|
||||
return "input-one";
|
||||
if (i == 4)
|
||||
return "input-two";
|
||||
return "input-three";
|
||||
}
|
||||
|
||||
for (var i = 1; i < numberFields.length; i++) {
|
||||
switch (Number(numberFields[i])) {
|
||||
case 2:
|
||||
twoDigitYear = true; // fall through
|
||||
case 2002:
|
||||
yi = i;
|
||||
yfield = (i == 2 ? "input-one" :
|
||||
(i == 4 ? "input-two" : "input-three"));
|
||||
yfield = fieldForNumber(i);
|
||||
break;
|
||||
case 9, 10:
|
||||
mi = i;
|
||||
mfield = (i == 2 ? "input-one" :
|
||||
(i == 4 ? "input-two" : "input-three"));
|
||||
mfield = fieldForNumber(i);
|
||||
break;
|
||||
case 4:
|
||||
di = i;
|
||||
dfield = (i == 2 ? "input-one" :
|
||||
(i == 4 ? "input-two" : "input-three"));
|
||||
dfield = fieldForNumber(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,10 +466,18 @@
|
||||
|
||||
// set fading
|
||||
var fade = this.getAttribute("fade");
|
||||
var fadeDelay = (fade == "fast") ? 1 : fade == "slow" ? 4000 : 0;
|
||||
if (fadeDelay) {
|
||||
this._fadeTimer = setTimeout(() => this.hidePopup(true), fadeDelay, this);
|
||||
var fadeDelay = 0;
|
||||
if (fade == "fast") {
|
||||
fadeDelay = 1;
|
||||
}
|
||||
else if (fade == "slow") {
|
||||
fadeDelay = 4000;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fadeTimer = setTimeout(() => this.hidePopup(true), fadeDelay, this);
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="popuphiding" phase="target">
|
||||
@@ -517,149 +525,35 @@
|
||||
else this.removeAttribute('page');
|
||||
return val;"
|
||||
onget="return this.getAttribute('page') == 'true';"/>
|
||||
<property name="textProvider"
|
||||
readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
if (!this._textProvider) {
|
||||
this._textProvider = Components.classes["@mozilla.org/embedcomp/default-tooltiptextprovider;1"]
|
||||
.getService(Components.interfaces.nsITooltipTextProvider);
|
||||
}
|
||||
return this._textProvider;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<!-- Given the supplied element within a page, set the tooltip's text to the text
|
||||
for that element. Returns true if text was assigned, and false if the no text
|
||||
is set, which normally would be used to cancel tooltip display.
|
||||
|
||||
Note that DefaultTooltipTextProvider::GetNodeText() from nsDocShellTreeOwner.cpp
|
||||
also performs the same function, but for embedded clients that don't use a XUL/JS
|
||||
layer. These two should be kept synchronized.
|
||||
-->
|
||||
<method name="fillInPageTooltip">
|
||||
<parameter name="tipElement"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Don't show the tooltip if the tooltip node is a document, browser, or disconnected.
|
||||
if (!tipElement || !tipElement.ownerDocument ||
|
||||
tipElement.localName == "browser" ||
|
||||
(tipElement.ownerDocument.compareDocumentPosition(tipElement) & document.DOCUMENT_POSITION_DISCONNECTED)) {
|
||||
return false;
|
||||
let tttp = this.textProvider;
|
||||
let textObj = {}, dirObj = {};
|
||||
let shouldChangeText = tttp.getNodeText(tipElement, textObj, dirObj);
|
||||
if (shouldChangeText) {
|
||||
this.style.direction = dirObj.value;
|
||||
this.label = textObj.value;
|
||||
}
|
||||
|
||||
var defView = tipElement.ownerDocument.defaultView;
|
||||
// XXX Work around bug 350679:
|
||||
// "Tooltips can be fired in documents with no view".
|
||||
if (!defView)
|
||||
return false;
|
||||
|
||||
const XLinkNS = "http://www.w3.org/1999/xlink";
|
||||
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
var titleText = null;
|
||||
var XLinkTitleText = null;
|
||||
var SVGTitleText = null;
|
||||
var XULtooltiptextText = null;
|
||||
var lookingForSVGTitle = true;
|
||||
var direction = tipElement.ownerDocument.dir;
|
||||
|
||||
// If the element is invalid per HTML5 Forms specifications and has no title,
|
||||
// show the constraint validation error message.
|
||||
if ((tipElement instanceof HTMLInputElement ||
|
||||
tipElement instanceof HTMLTextAreaElement ||
|
||||
tipElement instanceof HTMLSelectElement ||
|
||||
tipElement instanceof HTMLButtonElement) &&
|
||||
!tipElement.hasAttribute('title') &&
|
||||
(!tipElement.form || !tipElement.form.noValidate)) {
|
||||
// If the element is barred from constraint validation or valid,
|
||||
// the validation message will be the empty string.
|
||||
titleText = tipElement.validationMessage || null;
|
||||
}
|
||||
|
||||
// If the element is an <input type='file'> without a title, we should show
|
||||
// the current file selection.
|
||||
if (!titleText &&
|
||||
tipElement instanceof HTMLInputElement &&
|
||||
tipElement.type == 'file' &&
|
||||
!tipElement.hasAttribute('title')) {
|
||||
let files = tipElement.files;
|
||||
|
||||
try {
|
||||
var bundle = Components.classes['@mozilla.org/intl/stringbundle;1']
|
||||
.getService(Components.interfaces.nsIStringBundleService)
|
||||
.createBundle("chrome://global/locale/layout/HtmlForm.properties");
|
||||
if (files.length == 0) {
|
||||
if (tipElement.multiple) {
|
||||
titleText = bundle.GetStringFromName("NoFilesSelected");
|
||||
} else {
|
||||
titleText = bundle.GetStringFromName("NoFileSelected");
|
||||
}
|
||||
} else {
|
||||
titleText = files[0].name;
|
||||
// For UX and performance (jank) reasons we cap the number of
|
||||
// files that we list in the tooltip to 20 plus a "and xxx more"
|
||||
// line, or to 21 if exactly 21 files were picked.
|
||||
const TRUNCATED_FILE_COUNT = 20;
|
||||
let count = Math.min(files.length, TRUNCATED_FILE_COUNT);
|
||||
for (let i = 1; i < count; ++i) {
|
||||
titleText += "\n" + files[i].name;
|
||||
}
|
||||
if (files.length == TRUNCATED_FILE_COUNT + 1) {
|
||||
titleText += "\n" + files[TRUNCATED_FILE_COUNT].name;
|
||||
} else if (files.length > TRUNCATED_FILE_COUNT + 1) {
|
||||
let xmoreStr = bundle.GetStringFromName("AndNMoreFiles");
|
||||
let xmoreNum = files.length - TRUNCATED_FILE_COUNT;
|
||||
let tmp = {};
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm", tmp);
|
||||
let andXMoreStr = tmp.PluralForm.get(xmoreNum, xmoreStr).replace("#1", xmoreNum);
|
||||
titleText += "\n" + andXMoreStr;
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
// Check texts against null so that title="" can be used to undefine a
|
||||
// title on a child element.
|
||||
while (tipElement &&
|
||||
(titleText == null) && (XLinkTitleText == null) &&
|
||||
(SVGTitleText == null) && (XULtooltiptextText == null)) {
|
||||
|
||||
if (tipElement.nodeType == Node.ELEMENT_NODE) {
|
||||
if (tipElement.namespaceURI == XULNS)
|
||||
XULtooltiptextText = tipElement.getAttribute("tooltiptext");
|
||||
else if (!(tipElement instanceof SVGElement))
|
||||
titleText = tipElement.getAttribute("title");
|
||||
|
||||
if ((tipElement instanceof HTMLAnchorElement ||
|
||||
tipElement instanceof HTMLAreaElement ||
|
||||
tipElement instanceof HTMLLinkElement ||
|
||||
tipElement instanceof SVGAElement) && tipElement.href) {
|
||||
XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
|
||||
}
|
||||
if (lookingForSVGTitle &&
|
||||
(!(tipElement instanceof SVGElement) ||
|
||||
tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
|
||||
lookingForSVGTitle = false;
|
||||
}
|
||||
if (lookingForSVGTitle) {
|
||||
for (let childNode of tipElement.childNodes) {
|
||||
if (childNode instanceof SVGTitleElement) {
|
||||
SVGTitleText = childNode.textContent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
direction = defView.getComputedStyle(tipElement, "")
|
||||
.getPropertyValue("direction");
|
||||
}
|
||||
|
||||
tipElement = tipElement.parentNode;
|
||||
}
|
||||
|
||||
this.style.direction = direction;
|
||||
|
||||
return [titleText, XLinkTitleText, SVGTitleText, XULtooltiptextText].some(function (t) {
|
||||
if (t && /\S/.test(t)) {
|
||||
// Make CRLF and CR render one line break each.
|
||||
this.label = t.replace(/\r\n?/g, '\n');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
return false;
|
||||
return shouldChangeText;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
@@ -443,7 +443,9 @@
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
}
|
||||
var val = rv !== undefined ? rv : (this.instantApply ? this.valueFromPreferences : this.value);
|
||||
var val = rv;
|
||||
if (val === undefined)
|
||||
val = this.instantApply ? this.valueFromPreferences : this.value;
|
||||
// if the preference is marked for reset, show default value in UI
|
||||
if (val === undefined)
|
||||
val = this.defaultValue;
|
||||
|
||||
@@ -1240,7 +1240,10 @@
|
||||
<property name="ordinal">
|
||||
<getter><![CDATA[
|
||||
var val = this.getAttribute("ordinal");
|
||||
return "" + (val == "" ? 1 : (val == "0" ? 0 : parseInt(val)));
|
||||
if (val == "")
|
||||
return "1";
|
||||
|
||||
return "" + (val == "0" ? 0 : parseInt(val));
|
||||
]]></getter>
|
||||
<setter><![CDATA[
|
||||
this.setAttribute("ordinal", val);
|
||||
|
||||
@@ -262,10 +262,18 @@ Rect.prototype = {
|
||||
|
||||
/** Ensure this rectangle is inside the other, if possible. Preserves w, h. */
|
||||
translateInside: function translateInside(other) {
|
||||
let offsetX = (this.left <= other.left ? other.left - this.left :
|
||||
(this.right > other.right ? other.right - this.right : 0));
|
||||
let offsetY = (this.top <= other.top ? other.top - this.top :
|
||||
(this.bottom > other.bottom ? other.bottom - this.bottom : 0));
|
||||
let offsetX = 0;
|
||||
if (this.left <= other.left)
|
||||
offsetX = other.left - this.left;
|
||||
else if (this.right > other.right)
|
||||
offsetX = other.right - this.right;
|
||||
|
||||
let offsetY = 0;
|
||||
if (this.top <= other.top)
|
||||
offsetY = other.top - this.top;
|
||||
else if (this.bottom > other.bottom)
|
||||
offsetY = other.bottom - this.bottom;
|
||||
|
||||
return this.translate(offsetX, offsetY);
|
||||
},
|
||||
|
||||
|
||||
@@ -251,7 +251,11 @@ let dataProviders = {
|
||||
extensions.sort(function (a, b) {
|
||||
if (a.isActive != b.isActive)
|
||||
return b.isActive ? 1 : -1;
|
||||
let lc = a.name.localeCompare(b.name);
|
||||
|
||||
// In some unfortunate cases addon names can be null.
|
||||
let aname = a.name || null;
|
||||
let bname = b.name || null;
|
||||
let lc = aname.localeCompare(bname);
|
||||
if (lc != 0)
|
||||
return lc;
|
||||
if (a.version != b.version)
|
||||
|
||||
@@ -80,9 +80,11 @@ this.XPathGenerator = {
|
||||
* @returns a properly quoted string to insert into an XPath query
|
||||
*/
|
||||
quoteArgument: function sss_xph_quoteArgument(aArg) {
|
||||
return !/'/.test(aArg) ? "'" + aArg + "'" :
|
||||
!/"/.test(aArg) ? '"' + aArg + '"' :
|
||||
"concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
|
||||
if (!/'/.test(aArg))
|
||||
return "'" + aArg + "'";
|
||||
if (!/"/.test(aArg))
|
||||
return '"' + aArg + '"';
|
||||
return "concat('" + aArg.replace(/'+/g, "',\"$&\",'") + "')";
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -326,9 +326,13 @@ function parseRDFManifest(aId, aUpdateKey, aRequest, aManifestData) {
|
||||
let extensionRes = gRDF.GetResource(PREFIX_EXTENSION + aId);
|
||||
let themeRes = gRDF.GetResource(PREFIX_THEME + aId);
|
||||
let itemRes = gRDF.GetResource(PREFIX_ITEM + aId);
|
||||
let addonRes = ds.ArcLabelsOut(extensionRes).hasMoreElements() ? extensionRes
|
||||
: ds.ArcLabelsOut(themeRes).hasMoreElements() ? themeRes
|
||||
: itemRes;
|
||||
let addonRes;
|
||||
if (ds.ArcLabelsOut(extensionRes).hasMoreElements())
|
||||
addonRes = extensionRes;
|
||||
else if (ds.ArcLabelsOut(themeRes).hasMoreElements())
|
||||
addonRes = themeRes;
|
||||
else
|
||||
addonRes = itemRes;
|
||||
|
||||
// If we have an update key then the update manifest must be signed
|
||||
if (aUpdateKey) {
|
||||
|
||||
@@ -609,8 +609,13 @@ function logTestInfo(aText, aCaller) {
|
||||
let ms = now.getMilliseconds();
|
||||
let time = (hh < 10 ? "0" + hh : hh) + ":" +
|
||||
(mm < 10 ? "0" + mm : mm) + ":" +
|
||||
(ss < 10 ? "0" + ss : ss) + ":" +
|
||||
(ms < 10 ? "00" + ms : ms < 100 ? "0" + ms : ms);
|
||||
(ss < 10 ? "0" + ss : ss) + ":";
|
||||
if (ms < 10) {
|
||||
time += "00";
|
||||
} else if (ms < 100) {
|
||||
time += "0";
|
||||
}
|
||||
time += ms;
|
||||
let msg = time + " | TEST-INFO | " + caller.filename + " | [" + caller.name +
|
||||
" : " + caller.lineNumber + "] " + aText;
|
||||
LOG_FUNCTION(msg);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "imgIRequest.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsSystemAlertsService.h"
|
||||
#include "nsIAlertsService.h"
|
||||
#include "nsIImageToPixbuf.h"
|
||||
#include "nsIStringBundle.h"
|
||||
@@ -31,6 +32,7 @@ nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_not
|
||||
nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr;
|
||||
nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr;
|
||||
nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr;
|
||||
nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr;
|
||||
|
||||
static void notify_action_cb(NotifyNotification *notification,
|
||||
gchar *action, gpointer user_data)
|
||||
@@ -72,8 +74,11 @@ GetPixbufFromImgRequest(imgIRequest* aRequest)
|
||||
NS_IMPL_ISUPPORTS(nsAlertsIconListener, imgINotificationObserver,
|
||||
nsIObserver, nsISupportsWeakReference)
|
||||
|
||||
nsAlertsIconListener::nsAlertsIconListener()
|
||||
: mLoadedFrame(false),
|
||||
nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend,
|
||||
const nsAString& aAlertName)
|
||||
: mAlertName(aAlertName),
|
||||
mBackend(aBackend),
|
||||
mLoadedFrame(false),
|
||||
mNotification(nullptr)
|
||||
{
|
||||
if (!libNotifyHandle && !libNotifyNotAvail) {
|
||||
@@ -93,7 +98,8 @@ nsAlertsIconListener::nsAlertsIconListener()
|
||||
notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show");
|
||||
notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf");
|
||||
notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action");
|
||||
if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action) {
|
||||
notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close");
|
||||
if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) {
|
||||
dlclose(libNotifyHandle);
|
||||
libNotifyHandle = nullptr;
|
||||
}
|
||||
@@ -102,6 +108,7 @@ nsAlertsIconListener::nsAlertsIconListener()
|
||||
|
||||
nsAlertsIconListener::~nsAlertsIconListener()
|
||||
{
|
||||
mBackend->RemoveListener(mAlertName, this);
|
||||
if (mIconRequest)
|
||||
mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
|
||||
// Don't dlclose libnotify as it uses atexit().
|
||||
@@ -181,6 +188,9 @@ nsAlertsIconListener::OnFrameComplete(imgIRequest* aRequest)
|
||||
nsresult
|
||||
nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
|
||||
{
|
||||
if (!mBackend->IsActiveListener(mAlertName, this))
|
||||
return NS_OK;
|
||||
|
||||
mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(),
|
||||
nullptr, nullptr);
|
||||
|
||||
@@ -211,12 +221,17 @@ nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
|
||||
GClosure* closure = g_closure_new_simple(sizeof(GClosure), this);
|
||||
g_closure_set_marshal(closure, notify_closed_marshal);
|
||||
mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE);
|
||||
gboolean result = notify_notification_show(mNotification, nullptr);
|
||||
GError* error = nullptr;
|
||||
if (!notify_notification_show(mNotification, &error)) {
|
||||
NS_WARNING(error->message);
|
||||
g_error_free(error);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (result && mAlertListener)
|
||||
if (mAlertListener)
|
||||
mAlertListener->Observe(nullptr, "alertshow", mAlertCookie.get());
|
||||
|
||||
return result ? NS_OK : NS_ERROR_FAILURE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@@ -264,8 +279,7 @@ nsAlertsIconListener::SendClosed()
|
||||
g_object_unref(mNotification);
|
||||
mNotification = nullptr;
|
||||
}
|
||||
if (mAlertListener)
|
||||
mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get());
|
||||
NotifyFinished();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -282,6 +296,29 @@ nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAlertsIconListener::Close()
|
||||
{
|
||||
if (mIconRequest) {
|
||||
mIconRequest->Cancel(NS_BINDING_ABORTED);
|
||||
mIconRequest = nullptr;
|
||||
}
|
||||
|
||||
if (!mNotification) {
|
||||
NotifyFinished();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GError* error = nullptr;
|
||||
if (!notify_notification_close(mNotification, &error)) {
|
||||
NS_WARNING(error->message);
|
||||
g_error_free(error);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert,
|
||||
nsIObserver* aAlertListener)
|
||||
@@ -371,3 +408,9 @@ nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert,
|
||||
|
||||
return StartRequest(imageUrl, inPrivateBrowsing);
|
||||
}
|
||||
|
||||
void nsAlertsIconListener::NotifyFinished()
|
||||
{
|
||||
if (mAlertListener)
|
||||
mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user