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:
2024-05-23 23:25:42 +08:00
parent 15a91f51ae
commit df2987f659
132 changed files with 3093 additions and 1719 deletions
+2
View File
@@ -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
+2
View File
@@ -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
+41 -32
View File
@@ -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);
+3 -1
View File
@@ -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,
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
-26
View File
@@ -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,
-8
View File
@@ -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
+20
View File
@@ -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)
{
+2
View File
@@ -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
View File
@@ -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);
+6 -11
View File
@@ -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;
+14 -1
View File
@@ -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;
}
+12 -1
View File
@@ -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>.
+8
View File
@@ -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,
+4
View File
@@ -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:
+1 -1
View File
@@ -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
+2
View File
@@ -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);
+2 -2
View File
@@ -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
View File
@@ -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);
}
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"));
+6 -1
View File
@@ -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').
+3 -23
View File
@@ -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
+44 -17
View File
@@ -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',
+8 -6
View File
@@ -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;
}
+1 -3
View File
@@ -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
{
+13
View File
@@ -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
+4
View File
@@ -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()
{
+4
View File
@@ -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();
+53 -7
View File
@@ -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
+21 -4
View File
@@ -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();
+18
View File
@@ -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();
+311 -28
View File
@@ -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)
+42 -1
View File
@@ -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
+28
View File
@@ -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()
{
+9
View File
@@ -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.
+4
View File
@@ -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
+13
View File
@@ -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()
{
+5
View File
@@ -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);
+121 -23
View File
@@ -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
View File
@@ -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
+10 -197
View File
@@ -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);
}
}
+2 -1
View File
@@ -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;
+3 -1
View File
@@ -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
+4 -3
View File
@@ -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);
};
+3 -3
View File
@@ -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;
}
}
}
+2 -2
View File
@@ -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 {
+3 -2
View File
@@ -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;
});
}
}
+5 -4
View File
@@ -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;
}
+16 -7
View File
@@ -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
View File
@@ -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);
};
+5 -26
View File
@@ -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
+1
View File
@@ -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 = {
+3 -2
View File
@@ -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
+2 -2
View File
@@ -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;
+15 -10
View File
@@ -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"
);
+22 -16
View File
@@ -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;
}
}
+156 -68
View File
@@ -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}
+15
View File
@@ -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')
+19 -5
View File
@@ -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;
}
+5 -2
View File
@@ -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);
+11 -6
View File
@@ -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;
}
}
+30 -136
View File
@@ -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>
+3 -1
View File
@@ -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;
+4 -1
View File
@@ -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);
+12 -4
View File
@@ -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);
},
+5 -1
View File
@@ -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) {
+7 -2
View File
@@ -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);
+51 -8
View File
@@ -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