зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
4eaaa7bc4c
|
@ -111,9 +111,13 @@ void XULListboxAccessible::Value(nsString& aValue) const {
|
|||
RefPtr<Element> element;
|
||||
select->GetSelectedItem(getter_AddRefs(element));
|
||||
|
||||
if (element) {
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem =
|
||||
element->AsXULSelectControlItem();
|
||||
if (selectedItem) selectedItem->GetLabel(aValue);
|
||||
if (selectedItem) {
|
||||
selectedItem->GetLabel(aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +180,9 @@ bool XULListboxAccessible::IsRowSelected(uint32_t aRowIdx) {
|
|||
RefPtr<Element> element;
|
||||
nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(element));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
if (!element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
|
||||
element->AsXULSelectControlItem();
|
||||
|
@ -337,6 +344,9 @@ void XULListboxAccessible::SelectRow(uint32_t aRowIdx) {
|
|||
|
||||
RefPtr<Element> item;
|
||||
control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
|
||||
item->AsXULSelectControlItem();
|
||||
|
@ -351,6 +361,9 @@ void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) {
|
|||
|
||||
RefPtr<Element> item;
|
||||
control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
|
||||
item->AsXULSelectControlItem();
|
||||
|
|
|
@ -57,9 +57,6 @@ method PushSubscription.unsubscribe
|
|||
// window.sidebar
|
||||
attribute Window.sidebar
|
||||
|
||||
// External interface
|
||||
method External.AddSearchProvider
|
||||
|
||||
// AppCache API
|
||||
method OfflineResourceList.swapCache
|
||||
method OfflineResourceList.update
|
||||
|
|
|
@ -50,3 +50,4 @@ DEPRECATED_OPERATION(CreateImageBitmapCanvasRenderingContext2D)
|
|||
DEPRECATED_OPERATION(MozRequestFullScreenDeprecatedPrefix)
|
||||
DEPRECATED_OPERATION(MozfullscreenchangeDeprecatedPrefix)
|
||||
DEPRECATED_OPERATION(MozfullscreenerrorDeprecatedPrefix)
|
||||
DEPRECATED_OPERATION(External_AddSearchProvider)
|
||||
|
|
|
@ -368,3 +368,5 @@ MozRequestFullScreenDeprecatedPrefixWarning=mozRequestFullScreen() is deprecated
|
|||
MozfullscreenchangeDeprecatedPrefixWarning=onmozfullscreenchange is deprecated.
|
||||
# LOCALIZATION NOTE (MozfullscreenerrorDeprecatedPrefixWarning): Do not translate onmozfullscreenerror.
|
||||
MozfullscreenerrorDeprecatedPrefixWarning=onmozfullscreenerror is deprecated.
|
||||
# LOCALIZATION NOTE(External_AddSearchProviderWarning): Do not translate AddSearchProvider.
|
||||
External_AddSearchProviderWarning=AddSearchProvider is deprecated.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
[NoInterfaceObject, JSImplementation="@mozilla.org/sidebar;1"]
|
||||
interface External
|
||||
{
|
||||
[UseCounter]
|
||||
[Deprecated="External_AddSearchProvider"]
|
||||
void AddSearchProvider(DOMString aDescriptionURL);
|
||||
void IsSearchProviderInstalled();
|
||||
};
|
||||
|
|
|
@ -2991,6 +2991,13 @@ void WorkerPrivate::ShutdownGCTimers() {
|
|||
bool WorkerPrivate::InterruptCallback(JSContext* aCx) {
|
||||
MOZ_ACCESS_THREAD_BOUND(mWorkerThreadAccessible, data);
|
||||
|
||||
// If we are here it's because a WorkerControlRunnable has been dispatched.
|
||||
// The runnable could be processed here or it could have already been
|
||||
// processed by a sync event loop.
|
||||
// The most important thing this method must do, is to decide if the JS
|
||||
// execution should continue or not. If the runnable returns an error or if
|
||||
// the worker status is >= Canceling, we should stop the JS execution.
|
||||
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(aCx));
|
||||
|
||||
bool mayContinue = true;
|
||||
|
@ -3004,11 +3011,19 @@ bool WorkerPrivate::InterruptCallback(JSContext* aCx) {
|
|||
}
|
||||
|
||||
bool mayFreeze = data->mFrozen;
|
||||
if (mayFreeze) {
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mayFreeze) {
|
||||
mayFreeze = mStatus <= Running;
|
||||
}
|
||||
|
||||
if (mStatus >= Canceling) {
|
||||
mayContinue = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mayContinue || !mayFreeze) {
|
||||
break;
|
||||
}
|
||||
|
@ -3510,10 +3525,11 @@ bool WorkerPrivate::RunCurrentSyncLoop() {
|
|||
|
||||
auto result = ProcessAllControlRunnablesLocked();
|
||||
if (result != ProcessAllControlRunnablesResult::Nothing) {
|
||||
// XXXkhuey how should we handle Abort here? See Bug 1003730.
|
||||
|
||||
// The state of the world may have changed. Recheck it.
|
||||
normalRunnablesPending = NS_HasPendingEvents(mThread);
|
||||
// The state of the world may have changed. Recheck it if we need to
|
||||
// continue.
|
||||
normalRunnablesPending =
|
||||
result == ProcessAllControlRunnablesResult::MayContinue &&
|
||||
NS_HasPendingEvents(mThread);
|
||||
|
||||
// NB: If we processed a NotifyRunnable, we might have run
|
||||
// non-control runnables, one of which may have shut down the
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<script>
|
||||
try { o1 = window.getSelection() } catch (e) {}
|
||||
try { o2 = document.createRange() } catch (e) {}
|
||||
try { document.head.replaceWith('', document.documentElement) } catch (e) {}
|
||||
try { o1.addRange(o2) } catch (e) {}
|
||||
try { document.designMode = 'on' } catch (e) {}
|
||||
try { o1.focusNode.execCommand('justifyleft', false, null) } catch (e) {}
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -101,6 +101,7 @@ load 1423767.html
|
|||
needs-focus load 1423776.html
|
||||
needs-focus load 1424450.html
|
||||
load 1425091.html
|
||||
load 1441619.html
|
||||
load 1443664.html
|
||||
skip-if(Android) needs-focus load 1444630.html
|
||||
load 1446451.html
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace mozilla {
|
|||
using namespace mozilla::intl;
|
||||
|
||||
nsCString* DateTimeFormat::mLocale = nullptr;
|
||||
nsDataHashtable<nsCStringHashKey, UDateFormat*>* DateTimeFormat::mFormatCache;
|
||||
|
||||
/*static*/ nsresult DateTimeFormat::Initialize() {
|
||||
if (mLocale) {
|
||||
|
@ -74,6 +75,34 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
return rv;
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
|
||||
nsAutoCString key;
|
||||
key.AppendInt((int)aDateFormatSelector);
|
||||
key.Append(':');
|
||||
key.AppendInt((int)aTimeFormatSelector);
|
||||
if (aTimeParameters) {
|
||||
key.Append(':');
|
||||
key.AppendInt(aTimeParameters->tp_gmt_offset);
|
||||
key.Append(':');
|
||||
key.AppendInt(aTimeParameters->tp_dst_offset);
|
||||
}
|
||||
|
||||
if (mFormatCache && mFormatCache->Count() == kMaxCachedFormats) {
|
||||
// Don't allow a pathological page to extend the cache unreasonably.
|
||||
NS_WARNING("flushing UDateFormat cache");
|
||||
DeleteCache();
|
||||
}
|
||||
if (!mFormatCache) {
|
||||
mFormatCache =
|
||||
new nsDataHashtable<nsCStringHashKey, UDateFormat*>(kMaxCachedFormats);
|
||||
}
|
||||
|
||||
UDateFormat*& dateTimeFormat = mFormatCache->GetOrInsert(key);
|
||||
|
||||
if (!dateTimeFormat) {
|
||||
// We didn't have a cached formatter for this key, so create one.
|
||||
|
||||
// Get the date style for the formatter.
|
||||
nsAutoString skeletonDate;
|
||||
nsAutoString patternDate;
|
||||
|
@ -115,7 +144,6 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
if (haveSkeleton) {
|
||||
// Get pattern for skeleton.
|
||||
UDateTimePatternGenerator* patternGenerator =
|
||||
|
@ -137,8 +165,8 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
patternGenerator,
|
||||
reinterpret_cast<const UChar*>(skeletonDate.BeginReading()),
|
||||
skeletonDate.Length(),
|
||||
reinterpret_cast<UChar*>(patternDate.BeginWriting()), patternLength,
|
||||
&status);
|
||||
reinterpret_cast<UChar*>(patternDate.BeginWriting()),
|
||||
patternLength, &status);
|
||||
}
|
||||
}
|
||||
udatpg_close(patternGenerator);
|
||||
|
@ -186,7 +214,8 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
nsAutoString timeZoneID(u"GMT");
|
||||
if (aTimeParameters) {
|
||||
int32_t totalOffsetMinutes =
|
||||
(aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) / 60;
|
||||
(aTimeParameters->tp_gmt_offset + aTimeParameters->tp_dst_offset) /
|
||||
60;
|
||||
if (totalOffsetMinutes != 0) {
|
||||
char sign = totalOffsetMinutes < 0 ? '-' : '+';
|
||||
int32_t hours = abs(totalOffsetMinutes) / 60;
|
||||
|
@ -195,7 +224,6 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
}
|
||||
}
|
||||
|
||||
UDateFormat* dateTimeFormat;
|
||||
if (aTimeParameters) {
|
||||
dateTimeFormat =
|
||||
udat_open(UDAT_PATTERN, UDAT_PATTERN, mLocale->get(),
|
||||
|
@ -209,6 +237,7 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
reinterpret_cast<const UChar*>(pattern.BeginReading()),
|
||||
pattern.Length(), &status);
|
||||
}
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status) && dateTimeFormat) {
|
||||
aStringOut.SetLength(DATETIME_FORMAT_INITIAL_LEN);
|
||||
|
@ -230,14 +259,21 @@ nsCString* DateTimeFormat::mLocale = nullptr;
|
|||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (dateTimeFormat) {
|
||||
udat_close(dateTimeFormat);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*static*/ void DateTimeFormat::DeleteCache() {
|
||||
if (mFormatCache) {
|
||||
for (auto i = mFormatCache->Iter(); !i.Done(); i.Next()) {
|
||||
udat_close(i.Data());
|
||||
}
|
||||
delete mFormatCache;
|
||||
mFormatCache = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*static*/ void DateTimeFormat::Shutdown() {
|
||||
DeleteCache();
|
||||
if (mLocale) {
|
||||
delete mLocale;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <time.h>
|
||||
#include "gtest/MozGtestFriend.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "prtime.h"
|
||||
#include "unicode/udat.h"
|
||||
|
@ -55,6 +56,8 @@ class DateTimeFormat {
|
|||
DateTimeFormat() = delete;
|
||||
|
||||
static nsresult Initialize();
|
||||
static void DeleteCache();
|
||||
static const size_t kMaxCachedFormats = 15;
|
||||
|
||||
FRIEND_TEST(DateTimeFormat, FormatPRExplodedTime);
|
||||
FRIEND_TEST(DateTimeFormat, DateFormatSelectors);
|
||||
|
@ -69,6 +72,7 @@ class DateTimeFormat {
|
|||
const PRTimeParameters* aTimeParameters, nsAString& aStringOut);
|
||||
|
||||
static nsCString* mLocale;
|
||||
static nsDataHashtable<nsCStringHashKey, UDateFormat*>* mFormatCache;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -333,11 +333,34 @@ OSPreferences::GetDateTimePattern(int32_t aDateFormatStyle,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, aRetVal)) {
|
||||
if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, aRetVal)) {
|
||||
// Create a cache key from the locale + style options
|
||||
nsAutoCString key(aLocale);
|
||||
key.Append(':');
|
||||
key.AppendInt(aDateFormatStyle);
|
||||
key.Append(':');
|
||||
key.AppendInt(aTimeFormatStyle);
|
||||
|
||||
nsString pattern;
|
||||
if (mPatternCache.Get(key, &pattern)) {
|
||||
aRetVal = pattern;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!ReadDateTimePattern(dateStyle, timeStyle, aLocale, pattern)) {
|
||||
if (!GetDateTimePatternForStyle(dateStyle, timeStyle, aLocale, pattern)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (mPatternCache.Count() == kMaxCachedPatterns) {
|
||||
// Don't allow unlimited cache growth; just throw it away in the case of
|
||||
// pathological behavior where a page keeps requesting different formats
|
||||
// and locales.
|
||||
NS_WARNING("flushing DateTimePattern cache");
|
||||
mPatternCache.Clear();
|
||||
}
|
||||
mPatternCache.Put(key, pattern);
|
||||
|
||||
aRetVal = pattern;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define mozilla_intl_IntlOSPreferences_h__
|
||||
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "unicode/uloc.h"
|
||||
|
@ -97,6 +98,9 @@ class OSPreferences : public mozIOSPreferences {
|
|||
nsTArray<nsCString> mSystemLocales;
|
||||
nsTArray<nsCString> mRegionalPrefsLocales;
|
||||
|
||||
const size_t kMaxCachedPatterns = 15;
|
||||
nsDataHashtable<nsCStringHashKey, nsString> mPatternCache;
|
||||
|
||||
private:
|
||||
virtual ~OSPreferences();
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ JSObject* SymbolObject::initClass(JSContext* cx, Handle<GlobalObject*> global,
|
|||
WellKnownSymbols* wks = cx->runtime()->wellKnownSymbols;
|
||||
for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
|
||||
#ifndef NIGHTLY_BUILD
|
||||
if (i == SymbolCode::matchAll) {
|
||||
if (i == static_cast<size_t>(SymbolCode::matchAll)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5133,12 +5133,10 @@ void js::gc::DelayCrossCompartmentGrayMarking(JSObject* src) {
|
|||
}
|
||||
|
||||
void GCRuntime::markIncomingCrossCompartmentPointers(MarkColor color) {
|
||||
MOZ_ASSERT(color == MarkColor::Black || color == MarkColor::Gray);
|
||||
|
||||
static const gcstats::PhaseKind statsPhases[] = {
|
||||
gcstats::PhaseKind::SWEEP_MARK_INCOMING_BLACK,
|
||||
gcstats::PhaseKind::SWEEP_MARK_INCOMING_GRAY};
|
||||
gcstats::AutoPhase ap1(stats(), statsPhases[unsigned(color)]);
|
||||
gcstats::AutoPhase ap(
|
||||
stats(),
|
||||
color == MarkColor::Black ? gcstats::PhaseKind::SWEEP_MARK_INCOMING_BLACK
|
||||
: gcstats::PhaseKind::SWEEP_MARK_INCOMING_GRAY);
|
||||
|
||||
bool unlinkList = color == MarkColor::Gray;
|
||||
|
||||
|
@ -5325,7 +5323,7 @@ IncrementalProgress GCRuntime::markGrayReferencesInCurrentGroup(
|
|||
}
|
||||
#endif
|
||||
|
||||
return marker.markUntilBudgetExhausted(budget) ? Finished : NotFinished;
|
||||
return markUntilBudgetExhausted(budget, gcstats::PhaseKind::SWEEP_MARK_GRAY);
|
||||
}
|
||||
|
||||
IncrementalProgress GCRuntime::endMarkingSweepGroup(FreeOp* fop,
|
||||
|
|
|
@ -3546,7 +3546,7 @@ void UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing) {
|
|||
// case, push any cells in zones that are currently being marked onto the
|
||||
// mark stack and they will eventually get marked black.
|
||||
Zone* zone = tenured.zone();
|
||||
if (zone->needsIncrementalBarrier()) {
|
||||
if (zone->isGCMarkingBlackAndGray()) {
|
||||
if (!cell->isMarkedBlack()) {
|
||||
Cell* tmp = cell;
|
||||
TraceManuallyBarrieredGenericPointerEdge(zone->barrierTracer(), &tmp,
|
||||
|
@ -3557,7 +3557,6 @@ void UnmarkGrayTracer::onChild(const JS::GCCellPtr& thing) {
|
|||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!zone->isGCMarkingBlackAndGray());
|
||||
if (!tenured.isMarkedGray()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -488,9 +488,6 @@ skip script test262/language/expressions/await/async-generator-interleaved.js
|
|||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1321616
|
||||
skip script test262/annexB/built-ins/Function/createdynfn-html-close-comment-params.js
|
||||
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1462741
|
||||
skip script test262/built-ins/Function/prototype/toString/well-known-intrinsic-object-functions.js
|
||||
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1462745
|
||||
skip script test262/annexB/language/function-code/block-decl-nested-blocks-with-fun-decl.js
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class WptreportHandler(object):
|
|||
"""
|
||||
self.formatter.suite_start({
|
||||
"time": time(),
|
||||
"run_info": {},
|
||||
})
|
||||
|
||||
def suite_end(self):
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
// Global functions are configurable in a browser environment.
|
||||
var functionDeclarationsConfigurable = typeof document !== "undefined";
|
||||
if (typeof getBuildConfiguration === "undefined") {
|
||||
var getBuildConfiguration = SpecialPowers.Cu.getJSTestingFunctions().getBuildConfiguration;
|
||||
}
|
||||
|
||||
// Global functions are configurable in a browser environment on nightly.
|
||||
var functionDeclarationsConfigurable = typeof document !== "undefined" &&
|
||||
!getBuildConfiguration().release_or_beta;
|
||||
|
||||
var o = { f: "string-f" };
|
||||
with (o) {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head><title>Forms2</title></head>
|
||||
<body>
|
||||
<form>
|
||||
<input type="text" id="firstname">
|
||||
<input type="text" id="lastname">
|
||||
<input type="text" id="user1" value="foo">
|
||||
<input type="password" id="pass1" value="foo">
|
||||
</form>
|
||||
<iframe id="iframe"></iframe>
|
||||
<script>
|
||||
addEventListener("load", function(e) {
|
||||
if (window.parent === window) {
|
||||
document.getElementById("iframe").contentWindow.location.href = window.location.href;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -29,6 +29,7 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
|
|||
const val CONTENT_CRASH_URL = "about:crashcontent"
|
||||
const val DOWNLOAD_HTML_PATH = "/assets/www/download.html"
|
||||
const val FORMS_HTML_PATH = "/assets/www/forms.html"
|
||||
const val FORMS2_HTML_PATH = "/assets/www/forms2.html"
|
||||
const val HELLO_HTML_PATH = "/assets/www/hello.html"
|
||||
const val HELLO2_HTML_PATH = "/assets/www/hello2.html"
|
||||
const val INPUTS_PATH = "/assets/www/inputs.html"
|
||||
|
|
|
@ -335,6 +335,9 @@ class ContentDelegateTest : BaseSessionTest() {
|
|||
InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ->
|
||||
arrayOf(View.AUTOFILL_HINT_EMAIL_ADDRESS)
|
||||
InputType.TYPE_CLASS_PHONE -> arrayOf(View.AUTOFILL_HINT_PHONE)
|
||||
InputType.TYPE_CLASS_TEXT or
|
||||
InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT ->
|
||||
arrayOf(View.AUTOFILL_HINT_USERNAME)
|
||||
else -> null
|
||||
}))
|
||||
|
||||
|
@ -455,6 +458,71 @@ class ContentDelegateTest : BaseSessionTest() {
|
|||
countAutoFillNodes({ it.isFocused }), equalTo(0))
|
||||
}
|
||||
|
||||
@WithDevToolsAPI
|
||||
@Test fun autofill_userpass() {
|
||||
if (Build.VERSION.SDK_INT < 26) {
|
||||
return
|
||||
}
|
||||
|
||||
mainSession.loadTestPath(FORMS2_HTML_PATH)
|
||||
// Wait for the auto-fill nodes to populate.
|
||||
sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
|
||||
@AssertCalled(count = 2)
|
||||
override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
|
||||
}
|
||||
})
|
||||
|
||||
mainSession.evaluateJS("$('#pass1').focus()")
|
||||
sessionRule.waitUntilCalled(object : Callbacks.TextInputDelegate {
|
||||
@AssertCalled(count = 1)
|
||||
override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
|
||||
}
|
||||
})
|
||||
|
||||
val rootNode = ViewNode.newInstance()
|
||||
val rootStructure = ViewNodeBuilder.newInstance(AssistStructure(), rootNode,
|
||||
/* async */ false) as ViewStructure
|
||||
|
||||
// Perform auto-fill and return number of auto-fills performed.
|
||||
fun checkAutoFillChild(child: AssistStructure.ViewNode): Int {
|
||||
var sum = 0
|
||||
// Seal the node info instance so we can perform actions on it.
|
||||
if (child.childCount > 0) {
|
||||
for (i in 0 until child.childCount) {
|
||||
sum += checkAutoFillChild(child.getChildAt(i))
|
||||
}
|
||||
}
|
||||
|
||||
if (child === rootNode) {
|
||||
return sum
|
||||
}
|
||||
|
||||
assertThat("ID should be valid", child.id, not(equalTo(View.NO_ID)))
|
||||
|
||||
if (EditText::class.java.name == child.className) {
|
||||
val htmlInfo = child.htmlInfo
|
||||
assertThat("Should have HTML tag", htmlInfo.tag, equalTo("input"))
|
||||
|
||||
if (child.autofillHints == null) {
|
||||
return sum
|
||||
}
|
||||
child.autofillHints.forEach {
|
||||
when (it) {
|
||||
View.AUTOFILL_HINT_USERNAME, View.AUTOFILL_HINT_PASSWORD -> {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
mainSession.textInput.onProvideAutofillVirtualStructure(rootStructure, 0)
|
||||
// form and iframe have each 2 hints.
|
||||
assertThat("autofill hint count",
|
||||
checkAutoFillChild(rootNode), equalTo(4))
|
||||
}
|
||||
|
||||
private fun goFullscreen() {
|
||||
sessionRule.setPrefsUntilTestEnd(mapOf("full-screen-api.allow-trusted-requests-only" to false))
|
||||
mainSession.loadTestPath(FULLSCREEN_PATH)
|
||||
|
|
|
@ -654,6 +654,7 @@ public final class SessionTextInput {
|
|||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26 && "INPUT".equals(tag)) {
|
||||
// LastPass will fill password to the feild that setAutofillHints is unset and setInputType is set.
|
||||
switch (type) {
|
||||
case "email":
|
||||
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_EMAIL_ADDRESS });
|
||||
|
@ -672,14 +673,18 @@ public final class SessionTextInput {
|
|||
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_PHONE });
|
||||
structure.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||
break;
|
||||
case "text":
|
||||
structure.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
|
||||
break;
|
||||
case "url":
|
||||
structure.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_URI);
|
||||
break;
|
||||
case "text":
|
||||
final String autofillhint = bundle.getString("autofillhint", "");
|
||||
if (autofillhint.equals("username")) {
|
||||
structure.setAutofillHints(new String[] { View.AUTOFILL_HINT_USERNAME });
|
||||
structure.setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ ChromeUtils.import("resource://gre/modules/GeckoViewUtils.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
||||
FormLikeFactory: "resource://gre/modules/FormLikeFactory.jsm",
|
||||
LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
|
||||
});
|
||||
|
||||
GeckoViewUtils.initLogging("AutoFill", this);
|
||||
|
@ -78,7 +79,7 @@ class GeckoViewAutoFill {
|
|||
|
||||
let sendFocusEvent = false;
|
||||
const window = aFormLike.rootElement.ownerGlobal;
|
||||
const getInfo = (element, parent, root) => {
|
||||
const getInfo = (element, parent, root, usernameField) => {
|
||||
let info = this._autoFillInfos.get(element);
|
||||
if (info) {
|
||||
return info;
|
||||
|
@ -99,17 +100,28 @@ class GeckoViewAutoFill {
|
|||
.filter(attr => attr.localName !== "value")
|
||||
.map(attr => ({[attr.localName]: attr.value}))),
|
||||
origin: element.ownerDocument.location.origin,
|
||||
autofillhint: "",
|
||||
};
|
||||
|
||||
if (element === usernameField) {
|
||||
info.autofillhint = "username"; // AUTOFILL_HINT_USERNAME
|
||||
}
|
||||
|
||||
this._autoFillInfos.set(element, info);
|
||||
this._autoFillElements.set(info.id, Cu.getWeakReference(element));
|
||||
sendFocusEvent |= (element === element.ownerDocument.activeElement);
|
||||
return info;
|
||||
};
|
||||
|
||||
const rootInfo = getInfo(aFormLike.rootElement, null, undefined);
|
||||
let [usernameField] =
|
||||
LoginManagerContent.getUserNameAndPasswordFields(aFormLike.elements[0]);
|
||||
|
||||
const rootInfo = getInfo(aFormLike.rootElement, null, undefined, null);
|
||||
rootInfo.root = rootInfo.id;
|
||||
rootInfo.children = aFormLike.elements.map(
|
||||
element => getInfo(element, rootInfo.id, rootInfo.id));
|
||||
rootInfo.children = aFormLike.elements
|
||||
.filter(element => (!usernameField || element.type != "text" ||
|
||||
element == usernameField))
|
||||
.map(element => getInfo(element, rootInfo.id, rootInfo.id, usernameField));
|
||||
|
||||
this._eventDispatcher.dispatch("GeckoView:AddAutoFill", rootInfo, {
|
||||
onSuccess: responses => {
|
||||
|
|
|
@ -5990,8 +5990,6 @@ pref("dom.datatransfer.mozAtAPIs", true);
|
|||
pref("prio.enabled", false);
|
||||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("dom.sidebar.enabled", false);
|
||||
#else
|
||||
// External.AddSearchProvider is deprecated and it will be removed in the next
|
||||
// cycles.
|
||||
pref("dom.sidebar.enabled", true);
|
||||
#endif
|
||||
|
|
|
@ -1197,9 +1197,15 @@ void nsHtml5StreamParser::DoDataAvailableBuffer(mozilla::Buffer<uint8_t>&& aBuff
|
|||
DoDataAvailable(mBufferedLocalFileData.LastElement());
|
||||
} else {
|
||||
// Truncation OK, because the constant is small enough.
|
||||
size_t overBoundary =
|
||||
bufferedPlusLength.value() - LOCAL_FILE_UTF_8_BUFFER_SIZE;
|
||||
MOZ_RELEASE_ASSERT(overBoundary < aBuffer.Length());
|
||||
size_t untilBoundary = aBuffer.Length() - overBoundary;
|
||||
auto span = aBuffer.AsSpan();
|
||||
auto head = span.To(LOCAL_FILE_UTF_8_BUFFER_SIZE);
|
||||
auto tail = span.From(LOCAL_FILE_UTF_8_BUFFER_SIZE);
|
||||
auto head = span.To(untilBoundary);
|
||||
auto tail = span.From(untilBoundary);
|
||||
MOZ_RELEASE_ASSERT(mLocalFileBytesBuffered + untilBoundary ==
|
||||
LOCAL_FILE_UTF_8_BUFFER_SIZE);
|
||||
// We make a theoretically useless copy here, because avoiding
|
||||
// the copy adds too much complexity.
|
||||
Maybe<Buffer<uint8_t>> maybe = Buffer<uint8_t>::CopyFrom(head);
|
||||
|
|
|
@ -1333,6 +1333,33 @@ var LoginManagerContent = {
|
|||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the username and password fields found in the form by input
|
||||
* element into form.
|
||||
*
|
||||
* @param {HTMLInputElement} aField
|
||||
* A form field into form.
|
||||
* @return {Array} [usernameField, newPasswordField, oldPasswordField]
|
||||
*
|
||||
* More detail of these values is same as _getFormFields.
|
||||
*/
|
||||
getUserNameAndPasswordFields(aField) {
|
||||
// If the element is not a proper form field, return null.
|
||||
if (ChromeUtils.getClassName(aField) !== "HTMLInputElement" ||
|
||||
(aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) ||
|
||||
aField.nodePrincipal.isNullPrincipal ||
|
||||
!aField.ownerDocument) {
|
||||
return [null, null, null];
|
||||
}
|
||||
let form = LoginFormFactory.createFromField(aField);
|
||||
|
||||
let doc = aField.ownerDocument;
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView);
|
||||
|
||||
return this._getFormFields(form, false, recipes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify if a field is a valid login form field and
|
||||
* returns some information about it's FormLike.
|
||||
|
@ -1352,14 +1379,9 @@ var LoginManagerContent = {
|
|||
!aField.ownerDocument) {
|
||||
return null;
|
||||
}
|
||||
let form = LoginFormFactory.createFromField(aField);
|
||||
|
||||
let doc = aField.ownerDocument;
|
||||
let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI);
|
||||
let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView);
|
||||
|
||||
let [usernameField, newPasswordField] =
|
||||
this._getFormFields(form, false, recipes);
|
||||
this.getUserNameAndPasswordFields(aField);
|
||||
|
||||
// If we are not verifying a password field, we want
|
||||
// to use aField as the username field.
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Test for LoginManagerContent.getUserNameAndPasswordFields
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
// Services.prefs.setBoolPref("signon.debug", true);
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
||||
|
||||
const LMCBackstagePass = ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", {});
|
||||
const { LoginManagerContent, LoginFormFactory } = LMCBackstagePass;
|
||||
const TESTCASES = [
|
||||
{
|
||||
description: "1 password field outside of a <form>",
|
||||
document: `<input id="pw1" type=password>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "1 text field outside of a <form> without a password field",
|
||||
document: `<input id="un1">`,
|
||||
returnedFieldIDs: [null, null, null],
|
||||
},
|
||||
{
|
||||
description: "1 username & password field outside of a <form>",
|
||||
document: `<input id="un1">
|
||||
<input id="pw1" type=password>`,
|
||||
returnedFieldIDs: ["un1", "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "1 username & password field in a <form>",
|
||||
document: `<form>
|
||||
<input id="un1">
|
||||
<input id="pw1" type=password>
|
||||
</form>`,
|
||||
returnedFieldIDs: ["un1", "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "4 empty password fields outside of a <form>",
|
||||
document: `<input id="pw1" type=password>
|
||||
<input id="pw2" type=password>
|
||||
<input id="pw3" type=password>
|
||||
<input id="pw4" type=password>`,
|
||||
returnedFieldIDs: [null, null, null],
|
||||
},
|
||||
{
|
||||
description: "Form with 1 password field",
|
||||
document: `<form><input id="pw1" type=password></form>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "Form with 2 password fields",
|
||||
document: `<form><input id="pw1" type=password><input id='pw2' type=password></form>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "1 password field in a form, 1 outside (not processed)",
|
||||
document: `<form><input id="pw1" type=password></form><input id="pw2" type=password>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "1 password field in a form, 1 text field outside (not processed)",
|
||||
document: `<form><input id="pw1" type=password></form><input>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
{
|
||||
description: "1 text field in a form, 1 password field outside (not processed)",
|
||||
document: `<form><input></form><input id="pw1" type=password>`,
|
||||
returnedFieldIDs: [null, null, null],
|
||||
},
|
||||
{
|
||||
description: "2 password fields outside of a <form> with 1 linked via @form",
|
||||
document: `<input id="pw1" type=password><input id="pw2" type=password form='form1'>
|
||||
<form id="form1"></form>`,
|
||||
returnedFieldIDs: [null, "pw1", null],
|
||||
},
|
||||
];
|
||||
|
||||
for (let tc of TESTCASES) {
|
||||
info("Sanity checking the testcase: " + tc.description);
|
||||
|
||||
(function() {
|
||||
let testcase = tc;
|
||||
add_task(async function() {
|
||||
info("Starting testcase: " + testcase.description);
|
||||
let document = MockDocument.createTestDocument("http://localhost:8080/test/",
|
||||
testcase.document);
|
||||
|
||||
let input = document.querySelector("input");
|
||||
MockDocument.mockOwnerDocumentProperty(input, document, "http://localhost:8080/test/");
|
||||
MockDocument.mockNodePrincipalProperty(input, "http://localhost:8080/test/");
|
||||
|
||||
// Additional mock to cache recipes
|
||||
let win = {};
|
||||
Object.defineProperty(document, "defaultView", {
|
||||
value: win,
|
||||
});
|
||||
let formOrigin = LMCBackstagePass.LoginUtils._getPasswordOrigin(document.documentURI);
|
||||
LoginRecipesContent.cacheRecipes(formOrigin, win, new Set());
|
||||
|
||||
let actual = LoginManagerContent.getUserNameAndPasswordFields(input);
|
||||
|
||||
Assert.strictEqual(testcase.returnedFieldIDs.length, 3,
|
||||
"getUserNameAndPasswordFields returns 3 elements");
|
||||
|
||||
for (let i = 0; i < testcase.returnedFieldIDs.length; i++) {
|
||||
let expectedID = testcase.returnedFieldIDs[i];
|
||||
if (expectedID === null) {
|
||||
Assert.strictEqual(actual[i], expectedID,
|
||||
"Check returned field " + i + " is null");
|
||||
} else {
|
||||
Assert.strictEqual(actual[i].id, expectedID,
|
||||
"Check returned field " + i + " ID");
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
|
@ -23,6 +23,7 @@ run-if = buildapp == "browser"
|
|||
[test_getFormFields.js]
|
||||
[test_getPasswordFields.js]
|
||||
[test_getPasswordOrigin.js]
|
||||
[test_getUserNameAndPasswordFields.js]
|
||||
[test_isOriginMatching.js]
|
||||
[test_legacy_empty_formSubmitURL.js]
|
||||
[test_legacy_validation.js]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
var EXPORTED_SYMBOLS = ["MockDocument"];
|
||||
|
||||
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {});
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
const MockDocument = {
|
||||
/**
|
||||
|
@ -64,6 +65,13 @@ const MockDocument = {
|
|||
});
|
||||
},
|
||||
|
||||
mockNodePrincipalProperty(aElement, aURL) {
|
||||
Object.defineProperty(aElement, "nodePrincipal", {
|
||||
value: Services.scriptSecurityManager.createCodebasePrincipal(
|
||||
Services.io.newURI(aURL), {}),
|
||||
});
|
||||
},
|
||||
|
||||
createTestDocumentFromFile(aDocumentURL, aFile) {
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
|
|
Загрузка…
Ссылка в новой задаче