cherry-picked mozilla upstream changes:

bug1359051, bug1343256, bug1356179, bug1334097, bug1355520, bug1359142, bug1358469, bug1345910, bug1331335, bug1367267, bug1366140, bug1359837, bug1348791, bug1339826
This commit is contained in:
2018-06-18 16:59:55 +08:00
parent 1cef0b2de3
commit 0f5a8dc5ed
31 changed files with 1189 additions and 196 deletions
@@ -136,6 +136,12 @@ var gEditItemOverlay = {
throw new Error("_initKeywordField called unexpectedly");
}
// Reset the field status synchronously now, eventually we'll reinit it
// later if we find an existing keyword. This way we can ensure to be in a
// consistent status when reusing the panel across different bookmarks.
this._keyword = newKeyword;
this._initTextField(this._keywordField, newKeyword);
if (!newKeyword) {
let entries = [];
yield PlacesUtils.keywords.fetch({ url: this._paneInfo.uri.spec },
@@ -150,11 +156,12 @@ var gEditItemOverlay = {
existingKeyword = sameEntry ? sameEntry.keyword : "";
}
if (existingKeyword) {
this._keyword = newKeyword = existingKeyword;
this._keyword = existingKeyword;
// Update the text field to the existing keyword.
this._initTextField(this._keywordField, this._keyword);
}
}
}
this._initTextField(this._keywordField, newKeyword);
}),
_initLoadInSidebar: Task.async(function* () {
@@ -0,0 +1,99 @@
<?xml version="1.0"?>
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
<!DOCTYPE window [
<!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
%editBookmarkOverlayDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Bug 1343256 - Bookmark keywords disappear from one bookmark when adding a keyword to another bookmark"
onload="runTest();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<body xmlns="http://www.w3.org/1999/xhtml" />
<vbox id="editBookmarkPanelContent"/>
<script type="application/javascript">
<![CDATA[
function runTest() {
SimpleTest.waitForExplicitFinish();
Task.spawn(test.bind(this))
.catch(ex => ok(false, ex))
.then(() => PlacesUtils.bookmarks.eraseEverything())
.then(SimpleTest.finish);
}
function promiseOnItemChanged() {
return new Promise(resolve => {
PlacesUtils.bookmarks.addObserver({
onBeginUpdateBatch() {},
onEndUpdateBatch() {},
onItemAdded() {},
onItemRemoved() {},
onItemVisited() {},
onItemMoved() {},
onItemChanged(id, property, isAnno, value) {
PlacesUtils.bookmarks.removeObserver(this);
resolve({ property, value });
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
}, false);
});
}
function* test() {
ok(gEditItemOverlay, "Sanity check: gEditItemOverlay is in context");
let keywordField = document.getElementById("editBMPanel_keywordField");
for (let i = 0; i < 2; ++i) {
let bm = yield PlacesUtils.bookmarks.insert({
url: `http://www.test${i}.me/`,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
});
info(`Init panel on bookmark #${i+1}`);
let node = yield PlacesUIUtils.promiseNodeLikeFromFetchInfo(bm);
gEditItemOverlay.initPanel({ node });
is(document.getElementById("editBMPanel_keywordField").value, "",
"The keyword field should be empty");
info("Add a keyword to the bookmark");
let promise = promiseOnItemChanged();
keywordField.focus();
keywordField.value = "kw";
synthesizeKey(i.toString(), {});
synthesizeKey("VK_RETURN", {});
keywordField.blur();
let {property, value} = yield promise;
is(property, "keyword", "The keyword should have been changed");
is(value, `kw${i}`, "The new keyword value is correct");
}
for (let i = 0; i < 2; ++i) {
let entry = yield PlacesUtils.keywords.fetch({ url: `http://www.test${i}.me/` });
is(entry.keyword, `kw${i}`, `The keyword for http://www.test${i}.me/ is correct`);
}
};
]]>
</script>
</window>
+1 -1
View File
@@ -289,7 +289,7 @@ MOZ_ARG_ENABLE_BOOL(pie,
if test "$GNU_CC" -a -n "$MOZ_PIE"; then
AC_MSG_CHECKING([for PIE support])
_SAVE_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -pie"
LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS -pie"
AC_TRY_LINK(,,AC_MSG_RESULT([yes])
[MOZ_PROGRAM_LDFLAGS="$MOZ_PROGRAM_LDFLAGS -pie"],
AC_MSG_RESULT([no])
+6 -2
View File
@@ -9812,8 +9812,12 @@ nsDocShell::InternalLoad(nsIURI* aURI,
if (!aWindowTarget.IsEmpty()) {
// Locate the target DocShell.
nsCOMPtr<nsIDocShellTreeItem> targetItem;
// Only _self, _parent, and _top are supported in noopener case.
if (!(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
// Only _self, _parent, and _top are supported in noopener case. But we
// have to be careful to not apply that to the noreferrer case. See bug
// 1358469.
bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
if (allowNamedTarget ||
aWindowTarget.LowerCaseEqualsLiteral("_self") ||
aWindowTarget.LowerCaseEqualsLiteral("_parent") ||
aWindowTarget.LowerCaseEqualsLiteral("_top")) {
+131 -33
View File
@@ -23,6 +23,7 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/Casting.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Maybe.h"
#include "mozilla/Preferences.h"
@@ -9335,6 +9336,7 @@ class Maintenance final
RefPtr<DirectoryLock> mDirectoryLock;
nsTArray<DirectoryInfo> mDirectoryInfos;
nsDataHashtable<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
nsresult mResultCode;
Atomic<bool> mAborted;
State mState;
@@ -9342,6 +9344,7 @@ public:
explicit Maintenance(QuotaClient* aQuotaClient)
: mQuotaClient(aQuotaClient)
, mStartTime(PR_Now())
, mResultCode(NS_OK)
, mAborted(false)
, mState(State::Initial)
{
@@ -18463,21 +18466,32 @@ Maintenance::DirectoryWork()
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
if (NS_WARN_IF(NS_FAILED(quotaManager->EnsureStorageIsInitialized()))) {
return NS_ERROR_FAILURE;
nsresult rv = quotaManager->EnsureStorageIsInitialized();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIFile> storageDir = GetFileForPath(quotaManager->GetStoragePath());
MOZ_ASSERT(storageDir);
if (NS_WARN_IF(!storageDir)) {
return NS_ERROR_FAILURE;
}
bool exists;
MOZ_ALWAYS_SUCCEEDS(storageDir->Exists(&exists));
rv = storageDir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
return NS_ERROR_NOT_AVAILABLE;
}
bool isDirectory;
MOZ_ALWAYS_SUCCEEDS(storageDir->IsDirectory(&isDirectory));
rv = storageDir->IsDirectory(&isDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!isDirectory)) {
return NS_ERROR_FAILURE;
}
@@ -18512,25 +18526,41 @@ Maintenance::DirectoryWork()
}
nsCOMPtr<nsIFile> persistenceDir;
MOZ_ALWAYS_SUCCEEDS(
storageDir->Clone(getter_AddRefs(persistenceDir)));
MOZ_ALWAYS_SUCCEEDS(
persistenceDir->Append(NS_ConvertASCIItoUTF16(persistenceTypeString)));
rv = storageDir->Clone(getter_AddRefs(persistenceDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = persistenceDir->Append(NS_ConvertASCIItoUTF16(persistenceTypeString));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = persistenceDir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ALWAYS_SUCCEEDS(persistenceDir->Exists(&exists));
if (!exists) {
continue;
}
MOZ_ALWAYS_SUCCEEDS(persistenceDir->IsDirectory(&isDirectory));
rv = persistenceDir->IsDirectory(&isDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!isDirectory)) {
continue;
}
nsCOMPtr<nsISimpleEnumerator> persistenceDirEntries;
MOZ_ALWAYS_SUCCEEDS(
persistenceDir->GetDirectoryEntries(
getter_AddRefs(persistenceDirEntries)));
rv = persistenceDir->GetDirectoryEntries(
getter_AddRefs(persistenceDirEntries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!persistenceDirEntries) {
continue;
}
@@ -18542,46 +18572,76 @@ Maintenance::DirectoryWork()
}
bool persistenceDirHasMoreEntries;
MOZ_ALWAYS_SUCCEEDS(
persistenceDirEntries->HasMoreElements(&persistenceDirHasMoreEntries));
rv = persistenceDirEntries->HasMoreElements(
&persistenceDirHasMoreEntries);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!persistenceDirHasMoreEntries) {
break;
}
nsCOMPtr<nsISupports> persistenceDirEntry;
MOZ_ALWAYS_SUCCEEDS(
persistenceDirEntries->GetNext(getter_AddRefs(persistenceDirEntry)));
rv = persistenceDirEntries->GetNext(getter_AddRefs(persistenceDirEntry));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIFile> originDir = do_QueryInterface(persistenceDirEntry);
MOZ_ASSERT(originDir);
MOZ_ASSERT(NS_SUCCEEDED(originDir->Exists(&exists)));
rv = originDir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(exists);
MOZ_ALWAYS_SUCCEEDS(originDir->IsDirectory(&isDirectory));
rv = originDir->IsDirectory(&isDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!isDirectory) {
continue;
}
nsCOMPtr<nsIFile> idbDir;
MOZ_ALWAYS_SUCCEEDS(originDir->Clone(getter_AddRefs(idbDir)));
rv = originDir->Clone(getter_AddRefs(idbDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ALWAYS_SUCCEEDS(idbDir->Append(idbDirName));
rv = idbDir->Append(idbDirName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = idbDir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ALWAYS_SUCCEEDS(idbDir->Exists(&exists));
if (!exists) {
continue;
}
MOZ_ALWAYS_SUCCEEDS(idbDir->IsDirectory(&isDirectory));
rv = idbDir->IsDirectory(&isDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (NS_WARN_IF(!isDirectory)) {
continue;
}
nsCOMPtr<nsISimpleEnumerator> idbDirEntries;
MOZ_ALWAYS_SUCCEEDS(
idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries)));
rv = idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!idbDirEntries) {
continue;
}
@@ -18597,31 +18657,46 @@ Maintenance::DirectoryWork()
}
bool idbDirHasMoreEntries;
MOZ_ALWAYS_SUCCEEDS(
idbDirEntries->HasMoreElements(&idbDirHasMoreEntries));
rv = idbDirEntries->HasMoreElements(&idbDirHasMoreEntries);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!idbDirHasMoreEntries) {
break;
}
nsCOMPtr<nsISupports> idbDirEntry;
MOZ_ALWAYS_SUCCEEDS(
idbDirEntries->GetNext(getter_AddRefs(idbDirEntry)));
rv = idbDirEntries->GetNext(getter_AddRefs(idbDirEntry));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIFile> idbDirFile = do_QueryInterface(idbDirEntry);
MOZ_ASSERT(idbDirFile);
nsString idbFilePath;
MOZ_ALWAYS_SUCCEEDS(idbDirFile->GetPath(idbFilePath));
rv = idbDirFile->GetPath(idbFilePath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!StringEndsWith(idbFilePath, sqliteExtension)) {
continue;
}
MOZ_ASSERT(NS_SUCCEEDED(idbDirFile->Exists(&exists)));
rv = idbDirFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(exists);
MOZ_ALWAYS_SUCCEEDS(idbDirFile->IsDirectory(&isDirectory));
rv = idbDirFile->IsDirectory(&isDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (isDirectory) {
continue;
}
@@ -18750,8 +18825,19 @@ Maintenance::Finish()
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State::Finishing);
if (NS_FAILED(mResultCode)) {
nsCString errorName;
GetErrorName(mResultCode, errorName);
IDB_WARNING("Maintenance finished with error: %s", errorName.get());
}
mDirectoryLock = nullptr;
// It can happen that we are only referenced by mCurrentMaintenance which is
// cleared in NoteFinishedMaintenance()
RefPtr<Maintenance> kungFuDeathGrip = this;
mQuotaClient->NoteFinishedMaintenance(this);
mState = State::Complete;
@@ -18796,6 +18882,10 @@ Maintenance::Run()
}
if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
if (NS_SUCCEEDED(mResultCode)) {
mResultCode = rv;
}
// Must set mState before dispatching otherwise we will race with the owning
// thread.
mState = State::Finishing;
@@ -18822,6 +18912,10 @@ Maintenance::DirectoryLockAcquired(DirectoryLock* aLock)
nsresult rv = DirectoryOpen();
if (NS_WARN_IF(NS_FAILED(rv))) {
if (NS_SUCCEEDED(mResultCode)) {
mResultCode = rv;
}
mState = State::Finishing;
Finish();
@@ -18836,6 +18930,10 @@ Maintenance::DirectoryLockFailed()
MOZ_ASSERT(mState == State::DirectoryOpenPending);
MOZ_ASSERT(!mDirectoryLock);
if (NS_SUCCEEDED(mResultCode)) {
mResultCode = NS_ERROR_FAILURE;
}
mState = State::Finishing;
Finish();
}
+6 -3
View File
@@ -11,9 +11,12 @@
#include "IndexedDatabase.h"
#define IDB_WARNING(x) \
mozilla::dom::indexedDB::ReportInternalError(__FILE__, __LINE__, x); \
NS_WARNING(x)
#define IDB_WARNING(...) \
do { \
nsPrintfCString s(__VA_ARGS__); \
mozilla::dom::indexedDB::ReportInternalError(__FILE__, __LINE__, s.get()); \
NS_WARNING(s.get()); \
} while (0)
#define IDB_REPORT_INTERNAL_ERR() \
mozilla::dom::indexedDB::ReportInternalError(__FILE__, __LINE__, \
+1
View File
@@ -332,6 +332,7 @@ if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
'renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp',
]
CXXFLAGS += CONFIG['SSE2_FLAGS']
if CONFIG['GNU_CXX']:
CXXFLAGS += [
-3
View File
@@ -1822,7 +1822,6 @@ MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDe
// Interrupt in-calls have raced. The winner, if there is one, gets to defer
// processing of the other side's in-call.
bool defer;
const char* winner;
const MessageInfo parentMsgInfo =
(mSide == ChildSide) ? MessageInfo(aMsg) : mInterruptStack.top();
const MessageInfo childMsgInfo =
@@ -1830,11 +1829,9 @@ MessageChannel::ShouldDeferInterruptMessage(const Message& aMsg, size_t aStackDe
switch (mListener->MediateInterruptRace(parentMsgInfo, childMsgInfo))
{
case RIPChildWins:
winner = "child";
defer = (mSide == ChildSide);
break;
case RIPParentWins:
winner = "parent";
defer = (mSide != ChildSide);
break;
case RIPError:
+1 -1
View File
@@ -259,7 +259,7 @@ class ExecutableAllocator
{
__clear_cache(code, reinterpret_cast<char*>(code) + size);
}
#elif defined(JS_CODEGEN_ARM) && defined(XP_IOS)
#elif (defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)) && defined(XP_IOS)
static void cacheFlush(void* code, size_t size)
{
sys_icache_invalidate(code, size);
+29 -1
View File
@@ -117,7 +117,19 @@ class AutoSetHandlingSegFault
# define R12_sig(p) ((p)->sc_r12)
# define R13_sig(p) ((p)->sc_r13)
# define R14_sig(p) ((p)->sc_r14)
# define R15_sig(p) ((p)->sc_r15)
# if defined(__arm__)
# define R15_sig(p) ((p)->sc_pc)
# else
# define R15_sig(p) ((p)->sc_r15)
# endif
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->sc_elr)
# define RFP_sig(p) ((p)->sc_x[29])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->sc_pc)
# define RFP_sig(p) ((p)->sc_regs[30])
# endif
#elif defined(__linux__) || defined(SOLARIS)
# if defined(__linux__)
# define XMM_sig(p,i) ((p)->uc_mcontext.fpregs->_xmm[i])
@@ -175,6 +187,14 @@ class AutoSetHandlingSegFault
# define R13_sig(p) ((p)->uc_mcontext.__gregs[_REG_R13])
# define R14_sig(p) ((p)->uc_mcontext.__gregs[_REG_R14])
# define R15_sig(p) ((p)->uc_mcontext.__gregs[_REG_R15])
# if defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_PC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_X29])
# endif
# if defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.__gregs[_REG_EPC])
# define RFP_sig(p) ((p)->uc_mcontext.__gregs[_REG_S8])
# endif
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
# if defined(__DragonFly__)
# define XMM_sig(p,i) (((union savefpu*)(p)->uc_mcontext.mc_fpregs)->sv_xmm.sv_xmm[i])
@@ -203,6 +223,14 @@ class AutoSetHandlingSegFault
# else
# define R15_sig(p) ((p)->uc_mcontext.mc_r15)
# endif
# if defined(__FreeBSD__) && defined(__aarch64__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_elr)
# define RFP_sig(p) ((p)->uc_mcontext.mc_gpregs.gp_x[29])
# endif
# if defined(__FreeBSD__) && defined(__mips__)
# define EPC_sig(p) ((p)->uc_mcontext.mc_pc)
# define RFP_sig(p) ((p)->uc_mcontext.mc_regs[30])
# endif
#elif defined(XP_DARWIN)
# define EIP_sig(p) ((p)->uc_mcontext->__ss.__eip)
# define RIP_sig(p) ((p)->uc_mcontext->__ss.__rip)
+5 -3
View File
@@ -7430,9 +7430,11 @@ nsDisplayMask::PaintAsLayer(nsDisplayListBuilder* aBuilder,
// Clip the drawing target by mVisibleRect, which contains the visible
// region of the target frame and its out-of-flow and inflow descendants.
gfxContext* context = aCtx->ThebesContext();
context->Clip(NSRectToSnappedRect(mVisibleRect,
mFrame->PresContext()->AppUnitsPerDevPixel(),
*aCtx->GetDrawTarget()));
Rect bounds =
NSRectToRect(mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
bounds.RoundOut();
context->Clip(bounds);
ComputeMaskGeometry(params);
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<defs>
<clipPath clipPathUnits="objectBoundingBox" id="myPath">
<rect x="0" y="0" height="1" width="1"/>
</clipPath>
</defs>
<!-- You should see a thin horizontal light lime line. -->
<path d="M0 99.6 L500 99.6 L500 100.3 L0 100.3 Z" fill="lime" clip-path="url(#myPath)"/>
<!-- You should see a vertical light lime line. -->
<path d="M199.9 0 L199.9 500 L200.4 500 L200.4 0 Z" fill="lime" clip-path="url(#myPath)"/>
</svg>

After

Width:  |  Height:  |  Size: 555 B

+2 -1
View File
@@ -41,6 +41,7 @@ fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)||/^Windows\x20NT\x206\.[12]/
== clipPath-basic-05.svg pass.svg
== clipPath-basic-06.svg pass.svg
== clipPath-basic-07.svg pass.svg
!= clipPath-on-thin-object.svg about:blank
== clipPath-winding-01.svg pass.svg
== clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
== conditions-01.svg pass.svg
@@ -411,7 +412,7 @@ fuzzy-if(skiaContent,1,100) == tspan-xy-anchor-end-01.svg tspan-xy-anchor-end-re
== svg-effects-area-zoomed-in.xhtml svg-effects-area-zoomed-in-ref.xhtml
== svg-effects-area-zoomed-out.xhtml svg-effects-area-zoomed-out-ref.xhtml
== href-attr-change-restyles.svg href-attr-change-restyles-ref.svg
== mask-img.html mask-img-ref.html
fuzzy(128,141) == mask-img.html mask-img-ref.html
skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-color-burn.svg blend-color-burn-ref.svg
skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-color-dodge.svg blend-color-dodge-ref.svg
+78 -45
View File
@@ -557,10 +557,10 @@ static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
static int
audiounit_reinit_stream(cubeb_stream * stm, bool is_started)
audiounit_reinit_stream(cubeb_stream * stm)
{
auto_lock context_lock(stm->context->mutex);
if (is_started) {
if (!stm->shutdown) {
audiounit_stream_stop_internal(stm);
}
@@ -585,7 +585,7 @@ audiounit_reinit_stream(cubeb_stream * stm, bool is_started)
stm->frames_read = 0;
// If the stream was running, start it again.
if (is_started) {
if (!stm->shutdown) {
audiounit_stream_start_internal(stm);
}
}
@@ -599,8 +599,6 @@ audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_coun
{
cubeb_stream * stm = (cubeb_stream*) user;
stm->switching_device = true;
// Note if the stream was running or not
bool was_running = !stm->shutdown;
LOG("(%p) Audio device changed, %u events.", stm, (unsigned int) address_count);
for (UInt32 i = 0; i < address_count; i++) {
@@ -629,9 +627,10 @@ audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_coun
stm->input_device = 0;
}
break;
case kAudioDevicePropertyDataSource:
LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource", (unsigned int) i);
break;
case kAudioDevicePropertyDataSource: {
LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i);
return noErr;
}
default:
LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
return noErr;
@@ -657,7 +656,7 @@ audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_coun
// Use a new thread, through the queue, to avoid deadlock when calling
// Get/SetProperties method from inside notify callback
dispatch_async(stm->context->serial_queue, ^() {
if (audiounit_reinit_stream(stm, was_running) != CUBEB_OK) {
if (audiounit_reinit_stream(stm) != CUBEB_OK) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
LOG("(%p) Could not reopen the stream after switching.", stm);
}
@@ -718,17 +717,6 @@ audiounit_install_device_changed_callback(cubeb_stream * stm)
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource", r);
return CUBEB_ERROR;
}
/* This event will notify us when the default audio device changes,
* for example when the user plugs in a USB headset and the system chooses it
* automatically as the default, or when another device is chosen in the
* dropdown list. */
r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice", r);
return CUBEB_ERROR;
}
}
if (stm->input_unit) {
@@ -746,14 +734,6 @@ audiounit_install_device_changed_callback(cubeb_stream * stm)
return CUBEB_ERROR;
}
/* This event will notify us when the default input device changes. */
r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice", r);
return CUBEB_ERROR;
}
/* Event to notify when the input is going away. */
AudioDeviceID dev = stm->input_device ? stm->input_device :
audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
@@ -768,6 +748,37 @@ audiounit_install_device_changed_callback(cubeb_stream * stm)
return CUBEB_OK;
}
static int
audiounit_install_system_changed_callback(cubeb_stream * stm)
{
OSStatus r;
if (stm->output_unit) {
/* This event will notify us when the default audio device changes,
* for example when the user plugs in a USB headset and the system chooses it
* automatically as the default, or when another device is chosen in the
* dropdown list. */
r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
return CUBEB_ERROR;
}
}
if (stm->input_unit) {
/* This event will notify us when the default input device changes. */
r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
static int
audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
{
@@ -785,12 +796,6 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
if (r != noErr) {
return CUBEB_ERROR;
}
r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
}
if (stm->input_unit) {
@@ -806,8 +811,26 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
static int
audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
{
OSStatus r;
if (stm->output_unit) {
r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
}
if (stm->input_unit) {
r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
@@ -1717,6 +1740,12 @@ audiounit_setup_stream(cubeb_stream * stm)
stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
}
r = audiounit_install_device_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not install the device change callback.", stm);
return r;
}
return CUBEB_OK;
}
@@ -1786,7 +1815,7 @@ audiounit_stream_init(cubeb * context,
return r;
}
r = audiounit_install_device_changed_callback(stm.get());
r = audiounit_install_system_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not install the device change callback.", stm.get());
return r;
@@ -1801,6 +1830,12 @@ static void
audiounit_close_stream(cubeb_stream *stm)
{
stm->mutex.assert_current_thread_owns();
int r = audiounit_uninstall_device_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not uninstall the device changed callback", stm);
}
if (stm->input_unit) {
AudioUnitUninitialize(stm->input_unit);
AudioComponentInstanceDispose(stm->input_unit);
@@ -1821,6 +1856,11 @@ audiounit_stream_destroy(cubeb_stream * stm)
{
stm->shutdown = true;
int r = audiounit_uninstall_system_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not uninstall the device changed callback", stm);
}
auto_lock context_lock(stm->context->mutex);
audiounit_stream_stop_internal(stm);
@@ -1829,13 +1869,6 @@ audiounit_stream_destroy(cubeb_stream * stm)
audiounit_close_stream(stm);
}
#if !TARGET_OS_IPHONE
int r = audiounit_uninstall_device_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not uninstall the device changed callback", stm);
}
#endif
assert(stm->context->active_streams >= 1);
stm->context->active_streams -= 1;
@@ -1860,10 +1893,10 @@ audiounit_stream_start_internal(cubeb_stream * stm)
static int
audiounit_stream_start(cubeb_stream * stm)
{
auto_lock context_lock(stm->context->mutex);
stm->shutdown = false;
stm->draining = false;
auto_lock context_lock(stm->context->mutex);
audiounit_stream_start_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
@@ -1889,9 +1922,9 @@ audiounit_stream_stop_internal(cubeb_stream * stm)
static int
audiounit_stream_stop(cubeb_stream * stm)
{
auto_lock context_lock(stm->context->mutex);
stm->shutdown = true;
auto_lock context_lock(stm->context->mutex);
audiounit_stream_stop_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
@@ -0,0 +1,402 @@
diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -594,20 +594,20 @@ audiounit_get_input_device_id(AudioDevic
return CUBEB_OK;
}
static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
static int
-audiounit_reinit_stream(cubeb_stream * stm, bool is_started)
+audiounit_reinit_stream(cubeb_stream * stm)
{
auto_lock context_lock(stm->context->mutex);
- if (is_started) {
+ if (!stm->shutdown) {
audiounit_stream_stop_internal(stm);
}
{
auto_lock lock(stm->mutex);
float volume = 0.0;
int vol_rv = audiounit_stream_get_volume(stm, &volume);
@@ -622,32 +622,30 @@ audiounit_reinit_stream(cubeb_stream * s
audiounit_stream_set_volume(stm, volume);
}
// Reset input frames to force new stream pre-buffer
// silence if needed, check `is_extra_input_needed()`
stm->frames_read = 0;
// If the stream was running, start it again.
- if (is_started) {
+ if (!stm->shutdown) {
audiounit_stream_start_internal(stm);
}
}
return CUBEB_OK;
}
static OSStatus
audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count,
const AudioObjectPropertyAddress * addresses,
void * user)
{
cubeb_stream * stm = (cubeb_stream*) user;
stm->switching_device = true;
- // Note if the stream was running or not
- bool was_running = !stm->shutdown;
LOG("(%p) Audio device changed, %d events.", stm, address_count);
for (UInt32 i = 0; i < address_count; i++) {
switch(addresses[i].mSelector) {
case kAudioHardwarePropertyDefaultOutputDevice: {
LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
// Allow restart to choose the new default
stm->output_device = nullptr;
@@ -666,19 +664,20 @@ audiounit_property_listener_callback(Aud
if (stm->is_default_input) {
LOG("It's the default input device, ignore the event");
return noErr;
}
// Allow restart to choose the new default. Event register only for input.
stm->input_device = nullptr;
}
break;
- case kAudioDevicePropertyDataSource:
- LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i);
- break;
+ case kAudioDevicePropertyDataSource: {
+ LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i);
+ return noErr;
+ }
}
}
for (UInt32 i = 0; i < address_count; i++) {
switch(addresses[i].mSelector) {
case kAudioHardwarePropertyDefaultOutputDevice:
case kAudioHardwarePropertyDefaultInputDevice:
case kAudioDevicePropertyDeviceIsAlive:
@@ -691,17 +690,17 @@ audiounit_property_listener_callback(Aud
break;
}
}
}
// Use a new thread, through the queue, to avoid deadlock when calling
// Get/SetProperties method from inside notify callback
dispatch_async(stm->context->serial_queue, ^() {
- if (audiounit_reinit_stream(stm, was_running) != CUBEB_OK) {
+ if (audiounit_reinit_stream(stm) != CUBEB_OK) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
LOG("(%p) Could not reopen the stream after switching.", stm);
}
stm->switching_device = false;
});
return noErr;
}
@@ -752,27 +751,16 @@ audiounit_install_device_changed_callbac
}
r = audiounit_add_listener(stm, output_dev_id, kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback);
if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource", r);
return CUBEB_ERROR;
}
-
- /* This event will notify us when the default audio device changes,
- * for example when the user plugs in a USB headset and the system chooses it
- * automatically as the default, or when another device is chosen in the
- * dropdown list. */
- r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
- kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
- if (r != noErr) {
- PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice", r);
- return CUBEB_ERROR;
- }
}
if (stm->input_unit) {
/* This event will notify us when the data source on the input device changes. */
AudioDeviceID input_dev_id;
r = audiounit_get_input_device_id(&input_dev_id);
if (r != noErr) {
return CUBEB_ERROR;
@@ -780,78 +768,112 @@ audiounit_install_device_changed_callbac
r = audiounit_add_listener(stm, input_dev_id, kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback);
if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource", r);
return CUBEB_ERROR;
}
- /* This event will notify us when the default input device changes. */
- r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
- kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
- if (r != noErr) {
- PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice", r);
- return CUBEB_ERROR;
- }
-
/* Event to notify when the input is going away. */
AudioDeviceID dev = stm->input_device ? reinterpret_cast<intptr_t>(stm->input_device) :
audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
r = audiounit_add_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive,
kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r);
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
static int
+audiounit_install_system_changed_callback(cubeb_stream * stm)
+{
+ OSStatus r;
+
+ if (stm->output_unit) {
+ /* This event will notify us when the default audio device changes,
+ * for example when the user plugs in a USB headset and the system chooses it
+ * automatically as the default, or when another device is chosen in the
+ * dropdown list. */
+ r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
+ return CUBEB_ERROR;
+ }
+ }
+
+ if (stm->input_unit) {
+ /* This event will notify us when the default input device changes. */
+ r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
+ return CUBEB_ERROR;
+ }
+ }
+
+ return CUBEB_OK;
+}
+
+static int
audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
{
OSStatus r;
if (stm->output_unit) {
AudioDeviceID output_dev_id;
r = audiounit_get_output_device_id(&output_dev_id);
if (r != noErr) {
return CUBEB_ERROR;
}
r = audiounit_remove_listener(stm, output_dev_id, kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
-
- r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
- kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
- if (r != noErr) {
- return CUBEB_ERROR;
- }
}
if (stm->input_unit) {
AudioDeviceID input_dev_id;
r = audiounit_get_input_device_id(&input_dev_id);
if (r != noErr) {
return CUBEB_ERROR;
}
r = audiounit_remove_listener(stm, input_dev_id, kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
-
+ }
+ return CUBEB_OK;
+}
+
+static int
+audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
+{
+ OSStatus r;
+
+ if (stm->output_unit) {
+ r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
+ }
+
+ if (stm->input_unit) {
r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
- kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
/* Get the acceptable buffer size (in frames) that this device can work with. */
@@ -1764,16 +1786,22 @@ audiounit_setup_stream(cubeb_stream * st
if (stm->input_unit && stm->output_unit) {
// According to the I/O hardware rate it is expected a specific pattern of callbacks
// for example is input is 44100 and output is 48000 we expected no more than 2
// out callback in a row.
stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
}
+ r = audiounit_install_device_changed_callback(stm);
+ if (r != CUBEB_OK) {
+ LOG("(%p) Could not install the device change callback.", stm);
+ return r;
+ }
+
return CUBEB_OK;
}
static int
audiounit_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * /* stream_name */,
cubeb_devid input_device,
@@ -1838,31 +1866,37 @@ audiounit_stream_init(cubeb * context,
}
if (r != CUBEB_OK) {
LOG("(%p) Could not setup the audiounit stream.", stm);
audiounit_stream_destroy(stm);
return r;
}
- r = audiounit_install_device_changed_callback(stm);
+ r = audiounit_install_system_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not install the device change callback.", stm);
return r;
}
*stream = stm;
LOG("Cubeb stream (%p) init successful.", stm);
return CUBEB_OK;
}
static void
audiounit_close_stream(cubeb_stream *stm)
{
stm->mutex.assert_current_thread_owns();
+
+ int r = audiounit_uninstall_device_changed_callback(stm);
+ if (r != CUBEB_OK) {
+ LOG("(%p) Could not uninstall the device changed callback", stm);
+ }
+
if (stm->input_unit) {
AudioUnitUninitialize(stm->input_unit);
AudioComponentInstanceDispose(stm->input_unit);
}
audiounit_destroy_input_linear_buffer(stm);
if (stm->output_unit) {
@@ -1873,31 +1907,29 @@ audiounit_close_stream(cubeb_stream *stm
cubeb_resampler_destroy(stm->resampler);
}
static void
audiounit_stream_destroy(cubeb_stream * stm)
{
stm->shutdown = true;
+ int r = audiounit_uninstall_system_changed_callback(stm);
+ if (r != CUBEB_OK) {
+ LOG("(%p) Could not uninstall the device changed callback", stm);
+ }
+
auto_lock context_lock(stm->context->mutex);
audiounit_stream_stop_internal(stm);
{
auto_lock lock(stm->mutex);
audiounit_close_stream(stm);
}
-#if !TARGET_OS_IPHONE
- int r = audiounit_uninstall_device_changed_callback(stm);
- if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall the device changed callback", stm);
- }
-#endif
-
assert(stm->context->active_streams >= 1);
stm->context->active_streams -= 1;
LOG("Cubeb stream (%p) destroyed successful.", stm);
stm->~cubeb_stream();
free(stm);
}
@@ -1914,20 +1946,20 @@ audiounit_stream_start_internal(cubeb_st
r = AudioOutputUnitStart(stm->output_unit);
assert(r == 0);
}
}
static int
audiounit_stream_start(cubeb_stream * stm)
{
+ auto_lock context_lock(stm->context->mutex);
stm->shutdown = false;
stm->draining = false;
- auto_lock context_lock(stm->context->mutex);
audiounit_stream_start_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
LOG("Cubeb stream (%p) started successfully.", stm);
return CUBEB_OK;
}
@@ -1943,19 +1975,19 @@ audiounit_stream_stop_internal(cubeb_str
r = AudioOutputUnitStop(stm->output_unit);
assert(r == 0);
}
}
static int
audiounit_stream_stop(cubeb_stream * stm)
{
+ auto_lock context_lock(stm->context->mutex);
stm->shutdown = true;
- auto_lock context_lock(stm->context->mutex);
audiounit_stream_stop_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
LOG("Cubeb stream (%p) stopped successfully.", stm);
return CUBEB_OK;
}
+2
View File
@@ -4347,6 +4347,8 @@ pref("signon.storeWhenAutocompleteOff", true);
pref("signon.debug", false);
pref("signon.recipes.path", "chrome://passwordmgr/content/recipes.json");
pref("signon.schemeUpgrades", false);
// This temporarily prevents the master password to reprompt for autocomplete.
pref("signon.masterPasswordReprompt.timeout_ms", 900000); // 15 Minutes
// Satchel (Form Manager) prefs
pref("browser.formfill.debug", false);
+6
View File
@@ -93,6 +93,12 @@ typedef uint8_t nsHttpVersion;
// interop problems with critical infrastructure
#define NS_HTTP_BE_CONSERVATIVE (1<<11)
// (1<<12) is used for NS_HTTP_URGENT_START on a newer branch
// A sticky connection of the transaction is explicitly allowed to be restarted
// on ERROR_NET_RESET.
#define NS_HTTP_CONNECTION_RESTARTABLE (1<<13)
//-----------------------------------------------------------------------------
// some default values
//-----------------------------------------------------------------------------
+20 -3
View File
@@ -327,6 +327,7 @@ nsHttpChannel::nsHttpChannel()
, mIsCorsPreflightDone(0)
, mStronglyFramed(false)
, mUsedNetwork(0)
, mAuthConnectionRestartable(0)
, mPushedStream(nullptr)
, mLocalBlocklist(false)
, mWarningReporter(nullptr)
@@ -5660,6 +5661,14 @@ NS_IMETHODIMP nsHttpChannel::CloseStickyConnection()
return NS_OK;
}
NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable)
{
LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d",
this, aRestartable));
mAuthConnectionRestartable = aRestartable;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsISupports
//-----------------------------------------------------------------------------
@@ -7622,9 +7631,17 @@ nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
}
// set sticky connection flag and disable pipelining.
mCaps |= NS_HTTP_STICKY_CONNECTION;
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
// always set sticky connection flag
mCaps |= NS_HTTP_STICKY_CONNECTION;
// and when needed, allow restart regardless the sticky flag
if (mAuthConnectionRestartable) {
LOG((" connection made restartable"));
mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
mAuthConnectionRestartable = false;
} else {
LOG((" connection made non-restartable"));
mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
}
// and create a new one...
rv = SetupTransaction();
+4
View File
@@ -111,6 +111,7 @@ public:
NS_IMETHOD OnAuthAvailable() override;
NS_IMETHOD OnAuthCancelled(bool userCancel) override;
NS_IMETHOD CloseStickyConnection() override;
NS_IMETHOD ConnectionRestartable(bool) override;
// Functions we implement from nsIHttpAuthenticableChannel but are
// declared in HttpBaseChannel must be implemented in this class. We
// just call the HttpBaseChannel:: impls.
@@ -581,6 +582,9 @@ private:
// true if an HTTP transaction is created for the socket thread
uint32_t mUsedNetwork : 1;
// the next authentication request can be sent on a whole new connection
uint32_t mAuthConnectionRestartable : 1;
nsCOMPtr<nsIChannel> mPreflightChannel;
nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
@@ -782,6 +782,11 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
if (entry)
sessionStateGrip = entry->mMetaData;
// remember if we already had the continuation state. it means we are in
// the middle of the authentication exchange and the connection must be
// kept sticky then (and only then).
bool authAtProgress = !!*continuationState;
// for digest auth, maybe our cached nonce value simply timed out...
bool identityInvalid;
nsISupports *sessionState = sessionStateGrip;
@@ -816,11 +821,16 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
// use of the cached identity and not asking the user again
mProxyIdent.Clear();
}
mConnectionBased = false;
}
mConnectionBased = !!(authFlags & nsIHttpAuthenticator::CONNECTION_BASED);
// It's legal if the peer closes the connection after the first 401/7.
// Making the connection sticky will prevent its restart giving the user
// a 'network reset' error every time. Hence, we mark the connection
// as restartable.
mAuthChannel->ConnectionRestartable(mConnectionBased && !authAtProgress);
if (identityInvalid) {
if (entry) {
if (ident->Equals(entry->Identity())) {
+1 -1
View File
@@ -1003,7 +1003,7 @@ nsHttpTransaction::Close(nsresult reason)
if ((reason == NS_ERROR_NET_RESET ||
reason == NS_OK ||
reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA)) &&
!(mCaps & NS_HTTP_STICKY_CONNECTION)) {
(!(mCaps & NS_HTTP_STICKY_CONNECTION) || (mCaps & NS_HTTP_CONNECTION_RESTARTABLE))) {
if (mForceRestart && NS_SUCCEEDED(Restart())) {
if (mResponseHead) {
@@ -112,4 +112,11 @@ interface nsIHttpAuthenticableChannel : nsIProxiedChannel
* the same connection.
*/
void closeStickyConnection();
/**
* Tells the channel to mark the connection as allowed to restart on
* authentication retry. This is allowed when the request is a start
* of a new authentication round.
*/
void connectionRestartable(in boolean restartable);
};
@@ -36,5 +36,50 @@
hyperlink1.click()
hyperlink2.click()
})
}, "Following a noreferrer link with a named target should not cause creation of a window that can be targeted by another noreferrer link with the same named target");
async_test(function(t) {
var ifr = document.createElement("iframe");
ifr.name = "sufficientlyrandomwindownameamiright2";
ifr.onload = t.step_func(function() {
var hyperlink = document.body.appendChild(document.createElement("a"));
t.add_cleanup(function() {
hyperlink.remove();
});
hyperlink.rel = "noreferrer";
hyperlink.href = URL.createObjectURL(new Blob(["hello subframe"],
{ type: "text/html"}));
hyperlink.target = "sufficientlyrandomwindownameamiright2";
ifr.onload = t.step_func_done(function() {
assert_equals(ifr.contentDocument.documentElement.textContent,
"hello subframe");
});
hyperlink.click();
});
document.body.appendChild(ifr);
t.add_cleanup(function() {
ifr.remove();
});
}, "Targeting a rel=noreferrer link at an existing named subframe should work");
async_test(function(t) {
var win = window.open("", "sufficientlyrandomwindownameamiright3");
t.add_cleanup(function() {
win.close();
});
var hyperlink = document.body.appendChild(document.createElement("a"));
t.add_cleanup(function() {
hyperlink.remove();
});
hyperlink.rel = "noreferrer";
hyperlink.href = URL.createObjectURL(new Blob(["hello window"],
{ type: "text/html"}));
hyperlink.target = "sufficientlyrandomwindownameamiright3";
win.onload = t.step_func_done(function() {
assert_equals(win.document.documentElement.textContent,
"hello window");
});
hyperlink.click();
}, "Targeting a rel=noreferrer link at an existing named window should work");
</script>
@@ -38,6 +38,37 @@ var LoginManagerParent = {
*/
_recipeManager: null,
// Tracks the last time the user cancelled the master password prompt,
// to avoid spamming master password prompts on autocomplete searches.
_lastMPLoginCancelled: Math.NEGATIVE_INFINITY,
_searchAndDedupeLogins: function (formOrigin, actionOrigin) {
let logins;
try {
logins = LoginHelper.searchLoginsWithObject({
hostname: formOrigin,
formSubmitURL: actionOrigin,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
} catch (e) {
// Record the last time the user cancelled the MP prompt
// to avoid spamming them with MP prompts for autocomplete.
if (e.result == Cr.NS_ERROR_ABORT) {
log("User cancelled master password prompt.");
this._lastMPLoginCancelled = Date.now();
return [];
}
throw e;
}
// Dedupe so the length checks below still make sense with scheme upgrades.
let resolveBy = [
"scheme",
"timePasswordChanged",
];
return LoginHelper.dedupeLogins(logins, ["username"], resolveBy, formOrigin);
},
init() {
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
@@ -204,15 +235,8 @@ var LoginManagerParent = {
return;
}
let logins = LoginHelper.searchLoginsWithObject({
formSubmitURL: actionOrigin,
hostname: formOrigin,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
let resolveBy = [
"scheme",
"timePasswordChanged",
];
let logins = this._searchAndDedupeLogins(formOrigin, actionOrigin);
logins = LoginHelper.dedupeLogins(logins, ["username"], resolveBy, formOrigin);
log("sendLoginDataToChild:", logins.length, "deduped logins");
// Convert the array of nsILoginInfo to vanilla JS objects since nsILoginInfo
@@ -232,6 +256,22 @@ var LoginManagerParent = {
// Note: previousResult is a regular object, not an
// nsIAutoCompleteResult.
// Cancel if we unsuccessfully prompted for the master password too recently.
if (!Services.logins.isLoggedIn) {
let timeDiff = Date.now() - this._lastMPLoginCancelled;
if (timeDiff < this._repromptTimeout) {
log("Not searching logins for autocomplete since the master password " +
`prompt was last cancelled ${Math.round(timeDiff / 1000)} seconds ago.`);
// Send an empty array to make LoginManagerContent clear the
// outstanding request it has temporarily saved.
target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", {
requestId,
logins: [],
});
return;
}
}
let searchStringLower = searchString.toLowerCase();
let logins;
if (previousResult &&
@@ -244,17 +284,7 @@ var LoginManagerParent = {
} else {
log("Creating new autocomplete search result.");
// Grab the logins from the database.
logins = LoginHelper.searchLoginsWithObject({
formSubmitURL: actionOrigin,
hostname: formOrigin,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
let resolveBy = [
"scheme",
"timePasswordChanged",
];
logins = LoginHelper.dedupeLogins(logins, ["username"], resolveBy, formOrigin);
logins = this._searchAndDedupeLogins(formOrigin, actionOrigin);
}
let matchingLogins = logins.filter(function(fullMatch) {
@@ -323,20 +353,9 @@ var LoginManagerParent = {
(usernameField ? usernameField.name : ""),
newPasswordField.name);
let logins = LoginHelper.searchLoginsWithObject({
formSubmitURL,
hostname,
schemeUpgrades: LoginHelper.schemeUpgrades,
});
// Dedupe so the length checks below still make sense with scheme upgrades.
// Below here we have one login per hostPort + action + username with the
// matching scheme being preferred.
let resolveBy = [
"scheme",
"timePasswordChanged",
];
logins = LoginHelper.dedupeLogins(logins, ["username"], resolveBy, hostname);
let logins = this._searchAndDedupeLogins(hostname, formSubmitURL);
// If we didn't find a username field, but seem to be changing a
// password, allow the user to select from a list of applicable
@@ -488,3 +507,6 @@ var LoginManagerParent = {
.CustomEvent("InsecureLoginFormsStateChange"));
},
};
XPCOMUtils.defineLazyPreferenceGetter(LoginManagerParent, "_repromptTimeout",
"signon.masterPasswordReprompt.timeout_ms", 900000); // 15 Minutes
@@ -53,6 +53,7 @@ support-files =
[browser_insecurePasswordConsoleWarning.js]
support-files =
form_cross_origin_insecure_action.html
[browser_master_password_autocomplete.js]
[browser_notifications.js]
[browser_notifications_username.js]
[browser_notifications_password.js]
@@ -0,0 +1,59 @@
const HOST = "https://example.com";
const URL = HOST + "/browser/toolkit/components/passwordmgr/test/browser/form_basic.html";
const TIMEOUT_PREF = "signon.masterPasswordReprompt.timeout_ms";
// Waits for the master password prompt and cancels it.
function waitForDialog() {
let dialogShown = TestUtils.topicObserved("common-dialog-loaded");
return dialogShown.then(function([subject]) {
let dialog = subject.Dialog;
is(dialog.args.title, "Password Required");
dialog.ui.button1.click();
});
}
// Test that autocomplete does not trigger a master password prompt
// for a certain time after it was cancelled.
add_task(function* test_mpAutocompleteTimeout() {
let login = LoginTestUtils.testData.formLogin({
hostname: "https://example.com",
formSubmitURL: "https://example.com",
username: "username",
password: "password",
});
Services.logins.addLogin(login);
LoginTestUtils.masterPassword.enable();
registerCleanupFunction(function() {
LoginTestUtils.masterPassword.disable();
Services.logins.removeAllLogins();
});
// Set master password prompt timeout to 3s.
// If this test goes intermittent, you likely have to increase this value.
yield SpecialPowers.pushPrefEnv({set: [[TIMEOUT_PREF, 3000]]});
// Wait for initial master password dialog after opening the tab.
let dialogShown = waitForDialog();
yield BrowserTestUtils.withNewTab(URL, function*(browser) {
yield dialogShown;
yield ContentTask.spawn(browser, null, function*() {
// Focus the password field to trigger autocompletion.
content.document.getElementById("form-basic-password").focus();
});
// Wait 4s, dialog should not have been shown
// (otherwise the code below will not work).
yield new Promise((c) => setTimeout(c, 4000));
dialogShown = waitForDialog();
yield ContentTask.spawn(browser, null, function*() {
// Re-focus the password field to trigger autocompletion.
content.document.getElementById("form-basic-username").focus();
content.document.getElementById("form-basic-password").focus();
});
yield dialogShown;
});
});
+3 -3
View File
@@ -676,10 +676,10 @@ var Bookmarks = Object.freeze({
* @resolves once the removal is complete.
*/
eraseEverything(options = {}) {
const folderGuids = [this.toolbarGuid, this.menuGuid, this.unfiledGuid,
this.mobileGuid];
return PlacesUtils.withConnectionWrapper("Bookmarks.jsm: eraseEverything",
db => db.executeTransaction(function* () {
const folderGuids = [this.toolbarGuid, this.menuGuid, this.unfiledGuid,
this.mobileGuid];
yield removeFoldersContents(db, folderGuids, options);
const time = PlacesUtils.toPRTime(new Date());
const syncChangeDelta =
@@ -691,7 +691,7 @@ var Bookmarks = Object.freeze({
WHERE id IN (SELECT id FROM moz_bookmarks WHERE guid = :folderGuid )
`, { folderGuid, time, syncChangeDelta });
}
}.bind(this))
})
);
},
@@ -20,6 +20,22 @@ using namespace mozilla::dom;
static bool
IsValidHost(const nsACString& host) {
// This is ugly, but Preferences.h doesn't have support
// for default prefs or locked prefs
nsCOMPtr<nsIPrefService> prefService (do_GetService(NS_PREFSERVICE_CONTRACTID));
nsCOMPtr<nsIPrefBranch> prefs;
if (prefService) {
prefService->GetDefaultBranch(nullptr, getter_AddRefs(prefs));
bool isEnabled;
if (NS_SUCCEEDED(prefs->GetBoolPref("xpinstall.enabled", &isEnabled)) && !isEnabled) {
bool isLocked;
prefs->PrefIsLocked("xpinstall.enabled", &isLocked);
if (isLocked) {
return false;
}
}
}
if (host.Equals("addons.mozilla.org") ||
host.Equals("discovery.addons.mozilla.org") ||
host.Equals("testpilot.firefox.com")) {
+172 -58
View File
@@ -1323,6 +1323,14 @@ TSFTextStore::Init(nsWindowBase* aWidget,
("0x%p TSFTextStore::Init(aWidget=0x%p)",
this, aWidget));
if (NS_WARN_IF(!aWidget) || NS_WARN_IF(aWidget->Destroyed())) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED due to being initialized with "
"destroyed widget",
this));
return false;
}
TSFStaticSink::GetInstance()->EnsureInitActiveTIPKeyboard();
if (mDocumentMgr) {
@@ -1332,14 +1340,6 @@ TSFTextStore::Init(nsWindowBase* aWidget,
return false;
}
// Create document manager
HRESULT hr = sThreadMgr->CreateDocumentMgr(getter_AddRefs(mDocumentMgr));
if (FAILED(hr)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create DocumentMgr "
"(0x%08X)", this, hr));
return false;
}
mWidget = aWidget;
if (NS_WARN_IF(!mWidget)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
@@ -1355,30 +1355,62 @@ TSFTextStore::Init(nsWindowBase* aWidget,
return false;
}
SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode);
// Create document manager
RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
RefPtr<ITfDocumentMgr> documentMgr;
HRESULT hr = threadMgr->CreateDocumentMgr(getter_AddRefs(documentMgr));
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr "
"(0x%08X)", this, hr));
return false;
}
if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfDocumentMgr due to "
"TextStore being destroyed during calling "
"ITfThreadMgr::CreateDocumentMgr()", this));
return false;
}
// Create context and add it to document manager
hr = mDocumentMgr->CreateContext(sClientId, 0,
static_cast<ITextStoreACP*>(this),
getter_AddRefs(mContext), &mEditCookie);
if (FAILED(hr)) {
RefPtr<ITfContext> context;
hr = documentMgr->CreateContext(sClientId, 0,
static_cast<ITextStoreACP*>(this),
getter_AddRefs(context), &mEditCookie);
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create the context "
"(0x%08X)", this, hr));
mDocumentMgr = nullptr;
return false;
}
if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
"TextStore being destroyed during calling "
"ITfDocumentMgr::CreateContext()", this));
return false;
}
SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode);
hr = mDocumentMgr->Push(mContext);
if (FAILED(hr)) {
hr = documentMgr->Push(context);
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to push the context (0x%08X)",
this, hr));
// XXX Why don't we use NS_IF_RELEASE() here??
mContext = nullptr;
mDocumentMgr = nullptr;
return false;
}
if (NS_WARN_IF(mDestroyed)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("0x%p TSFTextStore::Init() FAILED to create ITfContext due to "
"TextStore being destroyed during calling ITfDocumentMgr::Push()",
this));
documentMgr->Pop(TF_POPF_ALL);
return false;
}
mDocumentMgr = documentMgr;
mContext = context;
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p TSFTextStore::Init() succeeded: "
@@ -1428,7 +1460,8 @@ TSFTextStore::Destroy()
("0x%p TSFTextStore::Destroy(), calling "
"ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
this));
mSink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
RefPtr<ITextStoreACPSink> sink = mSink;
sink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
}
// If this is called during handling a keydown or keyup message, we should
@@ -1452,8 +1485,8 @@ TSFTextStore::ReleaseTSFObjects()
mContext = nullptr;
if (mDocumentMgr) {
mDocumentMgr->Pop(TF_POPF_ALL);
mDocumentMgr = nullptr;
RefPtr<ITfDocumentMgr> documentMgr = mDocumentMgr.forget();
documentMgr->Pop(TF_POPF_ALL);
}
mSink = nullptr;
mWidget = nullptr;
@@ -1620,7 +1653,8 @@ TSFTextStore::RequestLock(DWORD dwLockFlags,
// Don't release this instance during this lock because this is called by
// TSF but they don't grab us during this call.
RefPtr<TSFTextStore> kungFuDeathGrip(this);
*phrSession = mSink->OnLockGranted(mLock);
RefPtr<ITextStoreACPSink> sink = mSink;
*phrSession = sink->OnLockGranted(mLock);
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
@@ -1633,7 +1667,7 @@ TSFTextStore::RequestLock(DWORD dwLockFlags,
("0x%p Locking for the request in the queue (%s) >>>>>>>>>>>>>>"
">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",
this, GetLockFlagNameStr(mLock).get()));
mSink->OnLockGranted(mLock);
sink->OnLockGranted(mLock);
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<",
@@ -1957,6 +1991,8 @@ TSFTextStore::MaybeFlushPendingNotifications()
// When there is no cached content, we can sync actual contents and TSF/TIP
// expecting contents.
RefPtr<TSFTextStore> kungFuDeathGrip = this;
Unused << kungFuDeathGrip;
if (!mContentForTSF.IsInitialized()) {
if (mPendingTextChangeData.IsValid()) {
MOZ_LOG(sTextStoreLog, LogLevel::Info,
@@ -4717,29 +4753,47 @@ TSFTextStore::OnFocusChange(bool aGotFocus,
}
RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
RefPtr<TSFTextStore> oldTextStore = sEnabledTextStore.forget();
// If currently sEnableTextStore has focus, notifies TSF of losing focus.
if (ThinksHavingFocus()) {
RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
DebugOnly<HRESULT> hr =
sThreadMgr->AssociateFocus(
sEnabledTextStore->mWidget->GetWindowHandle(),
threadMgr->AssociateFocus(
oldTextStore->mWidget->GetWindowHandle(),
nullptr, getter_AddRefs(prevFocusedDocumentMgr));
NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed");
NS_ASSERTION(prevFocusedDocumentMgr == sEnabledTextStore->mDocumentMgr,
NS_ASSERTION(prevFocusedDocumentMgr == oldTextStore->mDocumentMgr,
"different documentMgr has been associated with the window");
}
// If there is sEnabledTextStore, we don't use it in the new focused editor.
// Release it now.
if (sEnabledTextStore) {
sEnabledTextStore->Destroy();
sEnabledTextStore = nullptr;
if (oldTextStore) {
oldTextStore->Destroy();
}
if (NS_WARN_IF(!sThreadMgr)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::OnFocusChange() FAILED, due to "
"sThreadMgr being destroyed during calling "
"ITfThreadMgr::AssociateFocus()"));
return NS_ERROR_FAILURE;
}
if (NS_WARN_IF(sEnabledTextStore)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::OnFocusChange() FAILED, due to "
"nested event handling has created another focused TextStore during "
"calling ITfThreadMgr::AssociateFocus()"));
return NS_ERROR_FAILURE;
}
// If this is a notification of blur, move focus to the dummy document
// manager.
if (!aGotFocus || !aContext.mIMEState.IsEditable()) {
HRESULT hr = sThreadMgr->SetFocus(sDisabledDocumentMgr);
RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
RefPtr<ITfDocumentMgr> disabledDocumentMgr = sDisabledDocumentMgr;
HRESULT hr = threadMgr->SetFocus(disabledDocumentMgr);
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::OnFocusChange() FAILED due to "
@@ -4754,17 +4808,23 @@ TSFTextStore::OnFocusChange(bool aGotFocus,
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::OnFocusChange() FAILED due to "
"ITfThreadMgr::CreateAndSetFocus() failure"));
// If setting focus, we should destroy the TextStore completely because
// it causes memory leak.
if (sEnabledTextStore) {
sEnabledTextStore->Destroy();
sEnabledTextStore = nullptr;
}
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// static
void
TSFTextStore::EnsureToDestroyAndReleaseEnabledTextStoreIf(
RefPtr<TSFTextStore>& aTextStore)
{
aTextStore->Destroy();
if (sEnabledTextStore == aTextStore) {
sEnabledTextStore = nullptr;
}
aTextStore = nullptr;
}
// static
bool
TSFTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
@@ -4779,49 +4839,87 @@ TSFTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"TSFTextStore::Init() failure"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (NS_WARN_IF(!textStore->mDocumentMgr)) {
RefPtr<ITfDocumentMgr> newDocMgr = textStore->mDocumentMgr;
if (NS_WARN_IF(!newDocMgr)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"invalid TSFTextStore::mDocumentMgr"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (aContext.mIMEState.mEnabled == IMEState::PASSWORD) {
MarkContextAsKeyboardDisabled(textStore->mContext);
RefPtr<ITfContext> topContext;
textStore->mDocumentMgr->GetTop(getter_AddRefs(topContext));
newDocMgr->GetTop(getter_AddRefs(topContext));
if (topContext && topContext != textStore->mContext) {
MarkContextAsKeyboardDisabled(topContext);
}
}
HRESULT hr;
RefPtr<ITfThreadMgr> threadMgr = sThreadMgr;
{
// Windows 10's softwware keyboard requires that SetSelection must be
// always successful into SetFocus. If returning error, it might crash
// into TextInputFramework.dll.
AutoSetTemporarySelection setSelection(textStore->SelectionForTSFRef());
hr = sThreadMgr->SetFocus(textStore->mDocumentMgr);
hr = threadMgr->SetFocus(newDocMgr);
}
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"ITfTheadMgr::SetFocus() failure"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (NS_WARN_IF(!sThreadMgr)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"sThreadMgr being destroyed during calling "
"ITfTheadMgr::SetFocus()"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (NS_WARN_IF(sEnabledTextStore != textStore)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"creating TextStore has lost focus during calling "
"ITfThreadMgr::SetFocus()"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
// Use AssociateFocus() for ensuring that any native focus event
// never steal focus from our documentMgr.
RefPtr<ITfDocumentMgr> prevFocusedDocumentMgr;
hr = sThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(),
textStore->mDocumentMgr,
getter_AddRefs(prevFocusedDocumentMgr));
hr = threadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(), newDocMgr,
getter_AddRefs(prevFocusedDocumentMgr));
if (NS_WARN_IF(FAILED(hr))) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"ITfTheadMgr::AssociateFocus() failure"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (NS_WARN_IF(!sThreadMgr)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"sThreadMgr being destroyed during calling "
"ITfTheadMgr::AssociateFocus()"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
if (NS_WARN_IF(sEnabledTextStore != textStore)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"creating TextStore has lost focus during calling "
"ITfTheadMgr::AssociateFocus()"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
@@ -4830,7 +4928,16 @@ TSFTextStore::CreateAndSetFocus(nsWindowBase* aFocusedWidget,
(" TSFTextStore::CreateAndSetFocus(), calling "
"ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE) for 0x%p...",
textStore.get()));
textStore->mSink->OnLayoutChange(TS_LC_CREATE, TEXTSTORE_DEFAULT_VIEW);
RefPtr<ITextStoreACPSink> sink = textStore->mSink;
sink->OnLayoutChange(TS_LC_CREATE, TEXTSTORE_DEFAULT_VIEW);
if (NS_WARN_IF(sEnabledTextStore != textStore)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
(" TSFTextStore::CreateAndSetFocus() FAILED due to "
"creating TextStore has lost focus during calling "
"ITextStoreACPSink::OnLayoutChange(TS_LC_CREATE)"));
EnsureToDestroyAndReleaseEnabledTextStoreIf(textStore);
return false;
}
}
return true;
}
@@ -4955,7 +5062,8 @@ TSFTextStore::NotifyTSFOfTextChange()
"ITextStoreACPSink::OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
"acpNewEnd=%ld })...", this, textChange.acpStart,
textChange.acpOldEnd, textChange.acpNewEnd));
mSink->OnTextChange(0, &textChange);
RefPtr<ITextStoreACPSink> sink = mSink;
sink->OnTextChange(0, &textChange);
}
nsresult
@@ -5034,7 +5142,8 @@ TSFTextStore::NotifyTSFOfSelectionChange()
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p TSFTextStore::NotifyTSFOfSelectionChange(), calling "
"ITextStoreACPSink::OnSelectionChange()...", this));
mSink->OnSelectionChange();
RefPtr<ITextStoreACPSink> sink = mSink;
sink->OnSelectionChange();
}
nsresult
@@ -5106,7 +5215,8 @@ TSFTextStore::NotifyTSFOfLayoutChange()
("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
"calling ITextStoreACPSink::OnLayoutChange()...",
this));
HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
RefPtr<ITextStoreACPSink> sink = mSink;
HRESULT hr = sink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW);
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p TSFTextStore::NotifyTSFOfLayoutChange(), "
"called ITextStoreACPSink::OnLayoutChange()",
@@ -5466,7 +5576,8 @@ TSFTextStore::CommitCompositionInternal(bool aDiscard)
"mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
"acpNewEnd=%ld })...", this, textChange.acpStart,
textChange.acpOldEnd, textChange.acpNewEnd));
mSink->OnTextChange(0, &textChange);
RefPtr<ITextStoreACPSink> sink = mSink;
sink->OnTextChange(0, &textChange);
}
}
// Terminate two contexts, the base context (mContext) and the top
@@ -5869,35 +5980,37 @@ TSFTextStore::ProcessRawKeyMessage(const MSG& aMsg)
if (aMsg.message == WM_KEYDOWN) {
BOOL eaten;
HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
if (FAILED(hr) || !eaten) {
RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
HRESULT hr = keystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten);
if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
return false;
}
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
if (textStore) {
textStore->OnStartToHandleKeyMessage();
}
hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
hr = keystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten);
if (textStore) {
textStore->OnEndHandlingKeyMessage();
}
return SUCCEEDED(hr) && eaten;
return SUCCEEDED(hr) && (eaten || !sKeystrokeMgr);
}
if (aMsg.message == WM_KEYUP) {
BOOL eaten;
HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
if (FAILED(hr) || !eaten) {
RefPtr<ITfKeystrokeMgr> keystrokeMgr = sKeystrokeMgr;
HRESULT hr = keystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten);
if (FAILED(hr) || !sKeystrokeMgr || !eaten) {
return false;
}
RefPtr<TSFTextStore> textStore(sEnabledTextStore);
if (textStore) {
textStore->OnStartToHandleKeyMessage();
}
hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
hr = keystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten);
if (textStore) {
textStore->OnEndHandlingKeyMessage();
}
return SUCCEEDED(hr) && eaten;
return SUCCEEDED(hr) && (eaten || !sKeystrokeMgr);
}
return false;
}
@@ -6254,7 +6367,8 @@ TSFTextStore::MouseTracker::OnMouseButtonEvent(ULONG aEdge,
MOZ_ASSERT(IsUsing(), "The caller must check before calling OnMouseEvent()");
BOOL eaten = FALSE;
HRESULT hr = mSink->OnMouseEvent(aEdge, aQuadrant, aButtonStatus, &eaten);
RefPtr<ITfMouseSink> sink = mSink;
HRESULT hr = sink->OnMouseEvent(aEdge, aQuadrant, aButtonStatus, &eaten);
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("0x%p TSFTextStore::MouseTracker::OnMouseEvent(aEdge=%d, "
+2
View File
@@ -264,6 +264,8 @@ protected:
static bool CreateAndSetFocus(nsWindowBase* aFocusedWidget,
const InputContext& aContext);
static void EnsureToDestroyAndReleaseEnabledTextStoreIf(
RefPtr<TSFTextStore>& aTextStore);
static void MarkContextAsKeyboardDisabled(ITfContext* aContext);
static void MarkContextAsEmpty(ITfContext* aContext);
+2 -2
View File
@@ -723,7 +723,7 @@ WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
}
#endif
#ifdef NS_ENABLE_TSF
ITfMessagePump* msgPump = TSFTextStore::GetMessagePump();
RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
if (msgPump) {
BOOL ret = FALSE;
HRESULT hr = msgPump->PeekMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,
@@ -741,7 +741,7 @@ WinUtils::GetMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage,
UINT aLastMessage)
{
#ifdef NS_ENABLE_TSF
ITfMessagePump* msgPump = TSFTextStore::GetMessagePump();
RefPtr<ITfMessagePump> msgPump = TSFTextStore::GetMessagePump();
if (msgPump) {
BOOL ret = FALSE;
HRESULT hr = msgPump->GetMessageW(aMsg, aWnd, aFirstMessage, aLastMessage,