diff options
29 files changed, 491 insertions, 348 deletions
diff --git a/dom/cache/StreamControl.cpp b/dom/cache/StreamControl.cpp index aab176666..69a72d0b6 100644 --- a/dom/cache/StreamControl.cpp +++ b/dom/cache/StreamControl.cpp @@ -68,7 +68,8 @@ StreamControl::CloseAllReadStreams() { AssertOwningThread(); - ReadStreamList::ForwardIterator iter(mReadStreamList); + auto readStreamList = mReadStreamList; + ReadStreamList::ForwardIterator iter(readStreamList); while (iter.HasMore()) { iter.GetNext()->CloseStream(); } diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index 1c8ed63f0..80abad3cc 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -297,7 +297,7 @@ IMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext) IMEState newState = GetNewIMEState(sPresContext, nullptr); InputContextAction action(InputContextAction::CAUSE_UNKNOWN, InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, sWidget, action); + SetIMEState(newState, nullptr, nullptr, sWidget, action); } sWidget = nullptr; sContent = nullptr; @@ -352,7 +352,7 @@ IMEStateManager::OnRemoveContent(nsPresContext* aPresContext, IMEState newState = GetNewIMEState(sPresContext, nullptr); InputContextAction action(InputContextAction::CAUSE_UNKNOWN, InputContextAction::LOST_FOCUS); - SetIMEState(newState, nullptr, sWidget, action); + SetIMEState(newState, aPresContext, nullptr, sWidget, action); } sWidget = nullptr; @@ -542,7 +542,7 @@ IMEStateManager::OnChangeFocusInternal(nsPresContext* aPresContext, } // Update IME state for new focus widget - SetIMEState(newState, aContent, newWidget, aAction); + SetIMEState(newState, aPresContext, aContent, newWidget, aAction); } sActiveTabParent = newTabParent; @@ -704,7 +704,7 @@ IMEStateManager::OnClickInEditor(nsPresContext* aPresContext, InputContextAction action(cause, InputContextAction::FOCUS_NOT_CHANGED); IMEState newState = GetNewIMEState(aPresContext, aContent); - SetIMEState(newState, aContent, widget, action); + SetIMEState(newState, aPresContext, aContent, widget, action); } // static @@ -912,7 +912,7 @@ IMEStateManager::UpdateIMEState(const IMEState& aNewIMEState, if (updateIMEState) { InputContextAction action(InputContextAction::CAUSE_UNKNOWN, InputContextAction::FOCUS_NOT_CHANGED); - SetIMEState(aNewIMEState, aContent, widget, action); + SetIMEState(aNewIMEState, sPresContext, aContent, widget, action); if (NS_WARN_IF(widget->Destroyed())) { MOZ_LOG(sISMLog, LogLevel::Error, (" UpdateIMEState(), widget has gone during setting input context")); @@ -1022,8 +1022,8 @@ IMEStateManager::SetInputContextForChildProcess( MOZ_LOG(sISMLog, LogLevel::Info, ("SetInputContextForChildProcess(aTabParent=0x%p, " "aInputContext={ mIMEState={ mEnabled=%s, mOpen=%s }, " - "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, " - "aAction={ mCause=%s, mAction=%s }), " + "mHTMLInputType=\"%s\", mHTMLInputInputmode=\"%s\", mActionHint=\"%s\", " + "mInPrivateBrowsing=%s }, aAction={ mCause=%s, mAction=%s }), " "sPresContext=0x%p (available: %s), sWidget=0x%p (available: %s), " "sActiveTabParent=0x%p", aTabParent, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled), @@ -1031,6 +1031,7 @@ IMEStateManager::SetInputContextForChildProcess( NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(), NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(), NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(), + GetBoolName(aInputContext.mInPrivateBrowsing), GetActionCauseName(aAction.mCause), GetActionFocusChangeName(aAction.mFocusChange), sPresContext.get(), GetBoolName(CanHandleWith(sPresContext)), @@ -1070,6 +1071,7 @@ IMEStateManager::SetInputContextForChildProcess( // static void IMEStateManager::SetIMEState(const IMEState& aState, + nsPresContext* aPresContext, nsIContent* aContent, nsIWidget* aWidget, InputContextAction aAction) @@ -1090,6 +1092,10 @@ IMEStateManager::SetIMEState(const IMEState& aState, context.mIMEState = aState; context.mMayBeIMEUnaware = context.mIMEState.IsEditable() && sCheckForIMEUnawareWebApps && MayBeIMEUnawareWebApp(aContent); + + context.mInPrivateBrowsing = + aPresContext && + nsContentUtils::IsInPrivateBrowsing(aPresContext->Document()); if (aContent && aContent->IsAnyOfHTMLElements(nsGkAtoms::input, nsGkAtoms::textarea)) { @@ -1175,7 +1181,8 @@ IMEStateManager::SetInputContext(nsIWidget* aWidget, MOZ_LOG(sISMLog, LogLevel::Info, ("SetInputContext(aWidget=0x%p, aInputContext={ " "mIMEState={ mEnabled=%s, mOpen=%s }, mHTMLInputType=\"%s\", " - "mHTMLInputInputmode=\"%s\", mActionHint=\"%s\" }, " + "mHTMLInputInputmode=\"%s\", mActionHint=\"%s\", " + "mInPrivateBrowsing=%s }, " "aAction={ mCause=%s, mAction=%s }), sActiveTabParent=0x%p", aWidget, GetIMEStateEnabledName(aInputContext.mIMEState.mEnabled), @@ -1183,6 +1190,7 @@ IMEStateManager::SetInputContext(nsIWidget* aWidget, NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(), NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(), NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(), + GetBoolName(aInputContext.mInPrivateBrowsing), GetActionCauseName(aAction.mCause), GetActionFocusChangeName(aAction.mFocusChange), sActiveTabParent.get())); diff --git a/dom/events/IMEStateManager.h b/dom/events/IMEStateManager.h index 7dfc48aa5..34509847f 100644 --- a/dom/events/IMEStateManager.h +++ b/dom/events/IMEStateManager.h @@ -247,6 +247,7 @@ protected: nsIContent* aContent, InputContextAction aAction); static void SetIMEState(const IMEState &aState, + nsPresContext* aPresContext, nsIContent* aContent, nsIWidget* aWidget, InputContextAction aAction); diff --git a/extensions/auth/nsHttpNegotiateAuth.cpp b/extensions/auth/nsHttpNegotiateAuth.cpp index adea54b85..8b6be915e 100644 --- a/extensions/auth/nsHttpNegotiateAuth.cpp +++ b/extensions/auth/nsHttpNegotiateAuth.cpp @@ -530,8 +530,11 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChanne challenge++; len = strlen(challenge); + if (!len)
+ return NS_ERROR_UNEXPECTED;
+ // strip off any padding (see bug 230351) - while (challenge[len - 1] == '=') + while (len && challenge[len - 1] == '=') len--; // diff --git a/js/public/RootingAPI.h b/js/public/RootingAPI.h index 9f6ed8943..ca35ea4a5 100644 --- a/js/public/RootingAPI.h +++ b/js/public/RootingAPI.h @@ -274,6 +274,12 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T> T* unsafeGet() { return &ptr; } + void set(const T& newPtr) { + T tmp = ptr; + ptr = newPtr; + post(tmp, ptr); + } + explicit operator bool() const { return bool(js::BarrierMethods<T>::asGCThingOrNull(ptr)); } @@ -287,12 +293,6 @@ class MOZ_NON_MEMMOVABLE Heap : public js::HeapBase<T> post(GCPolicy<T>::initial(), ptr); } - void set(const T& newPtr) { - T tmp = ptr; - ptr = newPtr; - post(tmp, ptr); - } - void post(const T& prev, const T& next) { js::BarrierMethods<T>::postBarrier(&ptr, prev, next); } @@ -1172,13 +1172,13 @@ class PersistentRooted : public js::PersistentRootedBase<T>, return ptr; } - private: template <typename U> void set(U&& value) { MOZ_ASSERT(initialized()); ptr = mozilla::Forward<U>(value); } + private: // See the comment above Rooted::ptr. using MaybeWrapped = typename mozilla::Conditional< MapTypeToRootKind<T>::kind == JS::RootKind::Traceable, diff --git a/js/public/Value.h b/js/public/Value.h index 7c4f833e3..272d9b478 100644 --- a/js/public/Value.h +++ b/js/public/Value.h @@ -399,25 +399,21 @@ class MOZ_NON_PARAM alignas(8) Value data.asBits = bitsFromTagAndPayload(JSVAL_TAG_MAGIC, payload); } - bool setNumber(uint32_t ui) { + void setNumber(uint32_t ui) { if (ui > JSVAL_INT_MAX) { setDouble((double)ui); - return false; } else { setInt32((int32_t)ui); - return true; } } - bool setNumber(double d) { + void setNumber(double d) { int32_t i; if (mozilla::NumberIsInt32(d, &i)) { setInt32(i); - return true; + } else { + setDouble(d); } - - setDouble(d); - return false; } void setObjectOrNull(JSObject* arg) { @@ -1406,25 +1402,29 @@ class ValueOperations template <class Outer> class MutableValueOperations : public ValueOperations<Outer> { - JS::Value& value() { return static_cast<Outer*>(this)->get(); } + protected: + void set(const JS::Value& v) { + // Call Outer::set to trigger any barriers. + static_cast<Outer*>(this)->set(v); + } public: - void setNull() { value().setNull(); } - void setUndefined() { value().setUndefined(); } - void setInt32(int32_t i) { value().setInt32(i); } - void setDouble(double d) { value().setDouble(d); } + void setNull() { set(JS::NullValue()); } + void setUndefined() { set(JS::UndefinedValue()); } + void setInt32(int32_t i) { set(JS::Int32Value(i)); } + void setDouble(double d) { set(JS::DoubleValue(d)); } void setNaN() { setDouble(JS::GenericNaN()); } - void setBoolean(bool b) { value().setBoolean(b); } - void setMagic(JSWhyMagic why) { value().setMagic(why); } - bool setNumber(uint32_t ui) { return value().setNumber(ui); } - bool setNumber(double d) { return value().setNumber(d); } - void setString(JSString* str) { this->value().setString(str); } - void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); } - void setObject(JSObject& obj) { this->value().setObject(obj); } - void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); } - void setPrivate(void* ptr) { this->value().setPrivate(ptr); } - void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); } - void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); } + void setBoolean(bool b) { set(JS::BooleanValue(b)); } + void setMagic(JSWhyMagic why) { set(JS::MagicValue(why)); } + void setNumber(uint32_t ui) { set(JS::NumberValue(ui)); } + void setNumber(double d) { set(JS::NumberValue(d)); } + void setString(JSString* str) { set(JS::StringValue(str)); } + void setSymbol(JS::Symbol* sym) { set(JS::SymbolValue(sym)); } + void setObject(JSObject& obj) { set(JS::ObjectValue(obj)); } + void setObjectOrNull(JSObject* arg) { set(JS::ObjectOrNullValue(arg)); } + void setPrivate(void* ptr) { set(JS::PrivateValue(ptr)); } + void setPrivateUint32(uint32_t ui) { set(JS::PrivateUint32Value(ui)); } + void setPrivateGCThing(js::gc::Cell* cell) { set(JS::PrivateGCThingValue(cell)); } }; /* @@ -1432,55 +1432,28 @@ class MutableValueOperations : public ValueOperations<Outer> * type-querying, value-extracting, and mutating operations. */ template <> -class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> > +class HeapBase<JS::Value> : public MutableValueOperations<JS::Heap<JS::Value> > { typedef JS::Heap<JS::Value> Outer; friend class ValueOperations<Outer>; - void setBarriered(const JS::Value& v) { - *static_cast<JS::Heap<JS::Value>*>(this) = v; - } - public: - void setNull() { setBarriered(JS::NullValue()); } - void setUndefined() { setBarriered(JS::UndefinedValue()); } - void setInt32(int32_t i) { setBarriered(JS::Int32Value(i)); } - void setDouble(double d) { setBarriered(JS::DoubleValue(d)); } - void setNaN() { setDouble(JS::GenericNaN()); } - void setBoolean(bool b) { setBarriered(JS::BooleanValue(b)); } - void setMagic(JSWhyMagic why) { setBarriered(JS::MagicValue(why)); } - void setString(JSString* str) { setBarriered(JS::StringValue(str)); } - void setSymbol(JS::Symbol* sym) { setBarriered(JS::SymbolValue(sym)); } - void setObject(JSObject& obj) { setBarriered(JS::ObjectValue(obj)); } - void setPrivateGCThing(js::gc::Cell* cell) { setBarriered(JS::PrivateGCThingValue(cell)); } - - bool setNumber(uint32_t ui) { + void setNumber(uint32_t ui) { if (ui > JSVAL_INT_MAX) { - setDouble((double)ui); - return false; + this->setDouble((double)ui); } else { - setInt32((int32_t)ui); - return true; + this->setInt32((int32_t)ui); } } - bool setNumber(double d) { + void setNumber(double d) { int32_t i; if (mozilla::NumberIsInt32(d, &i)) { - setInt32(i); - return true; + this->setInt32(i); + } else { + this->setDouble(d); } - - setDouble(d); - return false; - } - - void setObjectOrNull(JSObject* arg) { - if (arg) - setObject(*arg); - else - setNull(); } }; diff --git a/js/src/jit/AliasAnalysisShared.cpp b/js/src/jit/AliasAnalysisShared.cpp index 0f0d4a66a..400626b33 100644 --- a/js/src/jit/AliasAnalysisShared.cpp +++ b/js/src/jit/AliasAnalysisShared.cpp @@ -86,8 +86,6 @@ GetObject(const MDefinition* ins) case MDefinition::Op_SetInitializedLength: case MDefinition::Op_ArrayLength: case MDefinition::Op_SetArrayLength: - case MDefinition::Op_StoreElementHole: - case MDefinition::Op_FallibleStoreElement: case MDefinition::Op_TypedObjectDescr: case MDefinition::Op_Slots: case MDefinition::Op_Elements: @@ -143,6 +141,8 @@ GetObject(const MDefinition* ins) case MDefinition::Op_WasmStoreGlobalVar: case MDefinition::Op_ArrayJoin: case MDefinition::Op_ArraySlice: + case MDefinition::Op_StoreElementHole: + case MDefinition::Op_FallibleStoreElement: return nullptr; default: #ifdef DEBUG diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6c376d528..7b0ed65f2 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9460,12 +9460,6 @@ class MStoreElementHole TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - AliasSet getAliasSet() const override { - // StoreElementHole can update the initialized length, the array length - // or reallocate obj->elements. - return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); - } - ALLOW_CLONE(MStoreElementHole) }; @@ -9496,9 +9490,6 @@ class MFallibleStoreElement TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, elements), (2, index), (3, value)) - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::ObjectFields | AliasSet::Element); - } bool strict() const { return strict_; } diff --git a/mailnews/base/public/nsIMsgAsyncPrompter.idl b/mailnews/base/public/nsIMsgAsyncPrompter.idl index 5a59c4f39..4e1f81d12 100644 --- a/mailnews/base/public/nsIMsgAsyncPrompter.idl +++ b/mailnews/base/public/nsIMsgAsyncPrompter.idl @@ -35,21 +35,37 @@ interface nsIMsgAsyncPrompter : nsISupports { in nsIMsgAsyncPromptListener aCaller); }; +[scriptable, function, uuid(acca94c9-378e-46e3-9a91-6655bf9c91a3)] +interface nsIMsgAsyncPromptCallback : nsISupports { + /** + * Called when an auth result is available. Can be passed as a function. + * + * @param aResult True if there is auth information available following the + * prompt, false otherwise. + */ + void onAuthResult(in boolean aResult); +}; + /** * This is used in combination with nsIMsgAsyncPrompter. */ [scriptable, uuid(fb5307a3-39d0-462e-92c8-c5c288a2612f)] interface nsIMsgAsyncPromptListener : nsISupports { /** - * Called when the listener should do its prompt. The listener - * should not return until the prompt is complete. - * - * @return True if there is auth information available following the prompt, - * false otherwise. + * This method has been deprecated, please use onPromptStartAsync instead. */ boolean onPromptStart(); /** + * Called when the listener should do its prompt. This can happen + * synchronously or asynchronously, but in any case when done the callback + * method should be called. + * + * @param aCallback The callback to execute when auth prompt has completed. + */ + void onPromptStartAsync(in nsIMsgAsyncPromptCallback aCallback); + + /** * Called in the case that the queued prompt was combined with another and * there is now authentication information available. */ diff --git a/mailnews/base/src/msgAsyncPrompter.js b/mailnews/base/src/msgAsyncPrompter.js index 58b5288e9..ae114683a 100644 --- a/mailnews/base/src/msgAsyncPrompter.js +++ b/mailnews/base/src/msgAsyncPrompter.js @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +Components.utils.import("resource://gre/modules/Deprecated.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); Components.utils.import("resource://gre/modules/Task.jsm"); Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -19,28 +20,46 @@ runnablePrompter.prototype = { _asyncPrompter: null, _hashKey: null, + _promiseAuthPrompt: function(listener) { + return new Promise((resolve, reject) => { + try { + listener.onPromptStartAsync({ onAuthResult: resolve }); + } catch (e) { + if (e.result == Components.results.NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) { + // Fall back to onPromptStart, for add-ons compat + Deprecated.warning("onPromptStart has been replaced by onPromptStartAsync", + "https://bugzilla.mozilla.org/show_bug.cgi?id=1176399"); + let ok = listener.onPromptStart(); + resolve(ok); + } else { + reject(e); + } + } + }); + }, + run: Task.async(function *() { yield Services.logins.initializationPromise; this._asyncPrompter._log.debug("Running prompt for " + this._hashKey); let prompter = this._asyncPrompter._pendingPrompts[this._hashKey]; let ok = false; try { - ok = prompter.first.onPromptStart(); - } - catch (ex) { + ok = yield this._promiseAuthPrompt(prompter.first); + } catch (ex) { Components.utils.reportError("runnablePrompter:run: " + ex + "\n"); + prompter.first.onPromptCanceled(); } delete this._asyncPrompter._pendingPrompts[this._hashKey]; for (var consumer of prompter.consumers) { try { - if (ok) + if (ok) { consumer.onPromptAuthAvailable(); - else + } else { consumer.onPromptCanceled(); - } - catch (ex) { + } + } catch (ex) { // Log the error for extension devs and others to pick up. Components.utils.reportError("runnablePrompter:run: consumer.onPrompt* reported an exception: " + ex + "\n"); } diff --git a/mailnews/base/src/msgOAuth2Module.js b/mailnews/base/src/msgOAuth2Module.js index 407ab0519..22d5dc572 100644 --- a/mailnews/base/src/msgOAuth2Module.js +++ b/mailnews/base/src/msgOAuth2Module.js @@ -126,19 +126,43 @@ OAuth2Module.prototype = { } } - // Otherwise, we need a new login, so create one and fill it in. - let login = Cc["@mozilla.org/login-manager/loginInfo;1"] - .createInstance(Ci.nsILoginInfo); - login.init(this._loginUrl, null, this._scope, this._username, token, - '', ''); - loginMgr.addLogin(login); + // Unless the token is null, we need to create and fill in a new login + if (token) { + let login = Cc["@mozilla.org/login-manager/loginInfo;1"] + .createInstance(Ci.nsILoginInfo); + login.init(this._loginUrl, null, this._scope, this._username, token, + '', ''); + loginMgr.addLogin(login); + } return token; }, connect(aWithUI, aListener) { - this._oauth.connect(() => aListener.onSuccess(this._oauth.accessToken), - x => aListener.onFailure(x), - aWithUI, false); + let oauth = this._oauth; + let promptlistener = { + onPromptStartAsync: function(callback) { + oauth.connect(() => { + this.onPromptAuthAvailable(); + callback.onAuthResult(true); + }, (err) => { + this.onPromptCanceled(); + callback.onAuthResult(false); + }, aWithUI, false); + }, + + onPromptAuthAvailable: function() { + aListener.onSuccess(oauth.accessToken); + }, + onPromptCanceled: function() { + aListener.onFailure(Components.results.NS_ERROR_ABORT); + }, + onPromptStart: function() {} + }; + + let asyncprompter = Components.classes["@mozilla.org/messenger/msgAsyncPrompter;1"] + .getService(Components.interfaces.nsIMsgAsyncPrompter); + let promptkey = this._loginUrl + "/" + this._username; + asyncprompter.queueAsyncAuthPrompt(promptkey, false, promptlistener); }, buildXOAuth2String() { diff --git a/mailnews/base/util/OAuth2.jsm b/mailnews/base/util/OAuth2.jsm index 94f850e0b..8c9282d02 100644 --- a/mailnews/base/util/OAuth2.jsm +++ b/mailnews/base/util/OAuth2.jsm @@ -3,30 +3,37 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * Provides OAuth 2.0 authentication + * Provides OAuth 2.0 authentication. + * @see RFC 6749 */ var EXPORTED_SYMBOLS = ["OAuth2"]; var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components; -Cu.import("resource://gre/modules/Http.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/gloda/log4moz.js"); -function parseURLData(aData) { - let result = {}; - aData.split(/[?#]/, 2)[1].split("&").forEach(function (aParam) { - let [key, value] = aParam.split("="); - result[key] = decodeURIComponent(value); - }); - return result; -} +Cu.importGlobalProperties(["fetch"]); // Only allow one connecting window per endpoint. var gConnecting = {}; -function OAuth2(aBaseURI, aScope, aAppKey, aAppSecret) { +/** + * Constructor for the OAuth2 object. + * + * @constructor + * @param {string} aBaseURI - The base URI for authentication and token + * requests, oauth2/auth or oauth2/token will be added for the actual + * requests. + * @param {?string} aScope - The scope as specified by RFC 6749 Section 3.3. + * Will not be included in the requests if falsy. + * @param {string} aAppKey - The client_id as specified by RFC 6749 Section + * 2.3.1. + * @param {string} [aAppSecret=null] - The client_secret as specified in + * RFC 6749 section 2.3.1. Will not be included in the requests if null. + */ +function OAuth2(aBaseURI, aScope, aAppKey, aAppSecret = null) { this.authURI = aBaseURI + "oauth2/auth"; this.tokenURI = aBaseURI + "oauth2/token"; this.consumerKey = aAppKey; @@ -37,12 +44,7 @@ function OAuth2(aBaseURI, aScope, aAppKey, aAppSecret) { this.log = Log4Moz.getConfiguredLogger("TBOAuth"); } -OAuth2.CODE_AUTHORIZATION = "authorization_code"; -OAuth2.CODE_REFRESH = "refresh_token"; - OAuth2.prototype = { - - responseType: "code", consumerKey: null, consumerSecret: null, completionURI: "http://localhost", @@ -63,7 +65,7 @@ OAuth2.prototype = { if (!aRefresh && this.accessToken) { aSuccess(); } else if (this.refreshToken) { - this.requestAccessToken(this.refreshToken, OAuth2.CODE_REFRESH); + this.requestAccessToken(this.refreshToken, true); } else { if (!aWithUI) { aFailure('{ "error": "auth_noui" }'); @@ -78,25 +80,31 @@ OAuth2.prototype = { }, requestAuthorization: function requestAuthorization() { - let params = [ - ["response_type", this.responseType], - ["client_id", this.consumerKey], - ["redirect_uri", this.completionURI], - ]; - // The scope can be optional. + let params = new URLSearchParams({ + response_type: "code", + client_id: this.consumerKey, + redirect_uri: this.completionURI, + }); + + // The scope is optional. if (this.scope) { - params.push(["scope", this.scope]); + params.append("scope", this.scope); } - // Add extra parameters - params.push(...this.extraAuthParams); + for (let [name, value] of this.extraAuthParams) { + params.append(name, value); + } - // Now map the parameters to a string - params = params.map(([k,v]) => k + "=" + encodeURIComponent(v)).join("&"); + let authEndpointURI = this.authURI + "?" + params.toString(); + this.log.info( + "Interacting with the resource owner to obtain an authorization grant " + + "from the authorization endpoint: " + + authEndpointURI + ); this._browserRequest = { account: this, - url: this.authURI + "?" + params, + url: authEndpointURI, _active: true, iconURI: "", cancelled: function() { @@ -170,65 +178,88 @@ OAuth2.prototype = { delete this._browserRequest; }, - onAuthorizationReceived: function(aData) { - this.log.info("authorization received" + aData); - let results = parseURLData(aData); - if (this.responseType == "code" && results.code) { - this.requestAccessToken(results.code, OAuth2.CODE_AUTHORIZATION); - } else if (this.responseType == "token") { - this.onAccessTokenReceived(JSON.stringify(results)); + // @see RFC 6749 section 4.1.2: Authorization Response + onAuthorizationReceived(aURL) { + this.log.info("OAuth2 authorization received: url=" + aURL); + let params = new URLSearchParams(aURL.split("?", 2)[1]); + if (params.has("code")) { + this.requestAccessToken(params.get("code"), false); + } else { + this.onAuthorizationFailed(null, aURL); } - else - this.onAuthorizationFailed(null, aData); }, onAuthorizationFailed: function(aError, aData) { this.connectFailureCallback(aData); }, - requestAccessToken: function requestAccessToken(aCode, aType) { - let params = [ - ["client_id", this.consumerKey], - ["client_secret", this.consumerSecret], - ["grant_type", aType], - ]; - - if (aType == OAuth2.CODE_AUTHORIZATION) { - params.push(["code", aCode]); - params.push(["redirect_uri", this.completionURI]); - } else if (aType == OAuth2.CODE_REFRESH) { - params.push(["refresh_token", aCode]); + /** + * Request a new access token, or refresh an existing one. + * @param {string} aCode - The token issued to the client. + * @param {boolean} aRefresh - Whether it's a refresh of a token or not. + */ + requestAccessToken(aCode, aRefresh) { + // @see RFC 6749 section 4.1.3. Access Token Request + // @see RFC 6749 section 6. Refreshing an Access Token + + let data = new URLSearchParams(); + data.append("client_id", this.consumerKey); + if (this.consumerSecret !== null) { + // Section 2.3.1. of RFC 6749 states that empty secrets MAY be omitted + // by the client. This OAuth implementation delegates this decission to + // the caller: If the secret is null, it will be omitted. + data.append("client_secret", this.consumerSecret); + } + + if (aRefresh) { + this.log.info( + `Making a refresh request to the token endpoint: ${this.tokenURI}` + ); + data.append("grant_type", "refresh_token"); + data.append("refresh_token", aCode); + } else { + this.log.info( + `Making access token request to the token endpoint: ${this.tokenURI}` + ); + data.append("grant_type", "authorization_code"); + data.append("code", aCode); + data.append("redirect_uri", this.completionURI); } - let options = { - postData: params, - onLoad: this.onAccessTokenReceived.bind(this), - onError: this.onAccessTokenFailed.bind(this) + fetch(this.tokenURI, { + method: "POST", + cache: "no-cache", + body: data, + }) + .then(response => response.json()) + .then(result => { + if ("error" in result) { + // RFC 6749 section 5.2. Error Response + this.log.info( + `The authorization server returned an error response: ${JSON.stringify( + result + )}` + ); + this.connectFailureCallback(result); + return; } - httpRequest(this.tokenURI, options); - }, - - onAccessTokenFailed: function onAccessTokenFailed(aError, aData) { - if (aError != "offline") { - this.refreshToken = null; - } - this.connectFailureCallback(aData); - }, - - onAccessTokenReceived: function onRequestTokenReceived(aData) { - let result = JSON.parse(aData); + // RFC 6749 section 5.1. Successful Response + this.log.info("The authorization server issued an access token."); this.accessToken = result.access_token; if ("refresh_token" in result) { - this.refreshToken = result.refresh_token; + this.refreshToken = result.refresh_token; } if ("expires_in" in result) { - this.tokenExpires = (new Date()).getTime() + (result.expires_in * 1000); + this.tokenExpires = new Date().getTime() + result.expires_in * 1000; } else { - this.tokenExpires = Number.MAX_VALUE; + this.tokenExpires = Number.MAX_VALUE; } - this.tokenType = result.token_type; - this.connectSuccessCallback(); + }) + .catch(err => { + this.log.info(`Connection to authorization server failed: ${err}`); + this.connectFailureCallback(err); + }); } }; diff --git a/mailnews/imap/src/nsImapProtocol.cpp b/mailnews/imap/src/nsImapProtocol.cpp index 4cfa9dab2..940d87cbd 100644 --- a/mailnews/imap/src/nsImapProtocol.cpp +++ b/mailnews/imap/src/nsImapProtocol.cpp @@ -5714,6 +5714,36 @@ void nsImapProtocol::ResetAuthMethods() m_failedAuthMethods = 0; } +nsresult nsImapProtocol::SendDataParseIMAPandCheckForNewMail(const char *aData, const char *aCommand) +{ + nsresult rv; + bool isResend = false; + while (true) + { + // Send authentication string (true: suppress logging the string). + rv = SendData(aData, true); + if (NS_FAILED(rv)) + break; + ParseIMAPandCheckForNewMail(aCommand); + if (!GetServerStateParser().WaitingForMoreClientInput()) + break; + + // The server is asking for the authentication string again. So we send + // the same string again although we know that it might be rejected again. + // We do that to get a firm authentication failure instead of a resend + // request. That keeps things in order before failing authentication and + // trying another method if capable. + if (isResend) + { + rv = NS_ERROR_FAILURE; + break; + } + isResend = true; + } + + return rv; +} + nsresult nsImapProtocol::AuthLogin(const char *userName, const nsCString &password, eIMAPCapabilityFlag flag) { ProgressEventFunctionUsingName("imapStatusSendingAuthLogin"); @@ -5880,29 +5910,7 @@ nsresult nsImapProtocol::AuthLogin(const char *userName, const nsCString &passwo PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s" CRLF, base64Str); PR_Free(base64Str); - bool isResend = false; - while (true) - { - // Send authentication string (true: suppress logging the string). - rv = SendData(m_dataOutputBuf, true); - if (NS_FAILED(rv)) - break; - ParseIMAPandCheckForNewMail(currentCommand); - if (!GetServerStateParser().WaitingForMoreClientInput()) - break; - - // Server is asking for authentication string again. So we send the - // same string again although we already know that it will be - // rejected again. We do that to get a firm authentication failure - // instead of a resend request. That keeps things in order before - // failing "authenticate PLAIN" and trying another method if capable. - if (isResend) - { - rv = NS_ERROR_FAILURE; - break; - } - isResend = true; - } + rv = SendDataParseIMAPandCheckForNewMail(m_dataOutputBuf, currentCommand); } // if the last command succeeded } // if auth plain capability else if (flag & kHasAuthLoginCapability) @@ -5953,9 +5961,8 @@ nsresult nsImapProtocol::AuthLogin(const char *userName, const nsCString &passwo EscapeUserNamePasswordString(password.get(), &correctedPassword); command.Append(correctedPassword); command.Append("\"" CRLF); - rv = SendData(command.get(), true /* suppress logging */); - NS_ENSURE_SUCCESS(rv, rv); - ParseIMAPandCheckForNewMail(); + + rv = SendDataParseIMAPandCheckForNewMail(command.get(), nullptr); } #ifdef MOZ_MAILNEWS_OAUTH2 else if (flag & kHasXOAuth2Capability) @@ -8513,6 +8520,13 @@ nsresult nsImapProtocol::GetPassword(nsCString &password, return rv; } +NS_IMETHODIMP nsImapProtocol::OnPromptStartAsync(nsIMsgAsyncPromptCallback *aCallback) +{ + bool result = false; + OnPromptStart(&result); + return aCallback->OnAuthResult(result); +} + // This is called from the UI thread. NS_IMETHODIMP nsImapProtocol::OnPromptStart(bool *aResult) diff --git a/mailnews/imap/src/nsImapProtocol.h b/mailnews/imap/src/nsImapProtocol.h index ba2594c89..8cee4f4fb 100644 --- a/mailnews/imap/src/nsImapProtocol.h +++ b/mailnews/imap/src/nsImapProtocol.h @@ -485,6 +485,7 @@ private: void Namespace(); void InsecureLogin(const char *userName, const nsCString &password); nsresult AuthLogin(const char *userName, const nsCString &password, eIMAPCapabilityFlag flag); + nsresult SendDataParseIMAPandCheckForNewMail(const char *data, const char *command); void ProcessAuthenticatedStateURL(); void ProcessAfterAuthenticated(); void ProcessSelectedStateURL(); diff --git a/mailnews/local/src/nsPop3Protocol.cpp b/mailnews/local/src/nsPop3Protocol.cpp index 5d9d9145a..de129a494 100644 --- a/mailnews/local/src/nsPop3Protocol.cpp +++ b/mailnews/local/src/nsPop3Protocol.cpp @@ -740,6 +740,13 @@ nsresult nsPop3Protocol::StartGetAsyncPassword(Pop3StatesEnum aNextState) return rv; } +NS_IMETHODIMP nsPop3Protocol::OnPromptStartAsync(nsIMsgAsyncPromptCallback *aCallback) +{ + bool result = false; + OnPromptStart(&result); + return aCallback->OnAuthResult(result); +} + NS_IMETHODIMP nsPop3Protocol::OnPromptStart(bool *aResult) { MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("OnPromptStart()"))); diff --git a/mailnews/news/src/nsNNTPProtocol.cpp b/mailnews/news/src/nsNNTPProtocol.cpp index 8ce367faa..035dff6e6 100644 --- a/mailnews/news/src/nsNNTPProtocol.cpp +++ b/mailnews/news/src/nsNNTPProtocol.cpp @@ -2472,6 +2472,13 @@ nsresult nsNNTPProtocol::PasswordResponse() return NS_ERROR_FAILURE; } +NS_IMETHODIMP nsNNTPProtocol::OnPromptStartAsync(nsIMsgAsyncPromptCallback *aCallback) +{ + bool result = false; + OnPromptStart(&result); + return aCallback->OnAuthResult(result); +} + NS_IMETHODIMP nsNNTPProtocol::OnPromptStart(bool *authAvailable) { NS_ENSURE_ARG_POINTER(authAvailable); diff --git a/media/libsoundtouch/src/SoundTouch.cpp b/media/libsoundtouch/src/SoundTouch.cpp index a9d23fc3c..955818810 100644 --- a/media/libsoundtouch/src/SoundTouch.cpp +++ b/media/libsoundtouch/src/SoundTouch.cpp @@ -283,9 +283,9 @@ void SoundTouch::calcEffectiveRateAndTempo() // Sets sample rate. void SoundTouch::setSampleRate(uint srate) { - bSrateSet = true; // set sample rate, leave other tempo changer parameters as they are. pTDStretch->setParameters((int)srate); + bSrateSet = true; } diff --git a/media/libsoundtouch/src/TDStretch.cpp b/media/libsoundtouch/src/TDStretch.cpp index 81bde22f0..b955bfc96 100644 --- a/media/libsoundtouch/src/TDStretch.cpp +++ b/media/libsoundtouch/src/TDStretch.cpp @@ -126,8 +126,13 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS, int aSeekWindowMS, int aOverlapMS) { // accept only positive parameter values - if zero or negative, use old values instead - if (aSampleRate > 0) this->sampleRate = aSampleRate; - if (aOverlapMS > 0) this->overlapMs = aOverlapMS; + if (aSampleRate > 0) + { + if (aSampleRate > 192000) ST_THROW_RT_ERROR("Error: Excessive samplerate"); + this->sampleRate = aSampleRate; + } + + if (aOverlapMS > 0) this->overlapMs = aOverlapMS; if (aSequenceMS > 0) { diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.cpp b/netwerk/protocol/http/nsHttpNTLMAuth.cpp index aa5b1f8f7..86bfcf4d1 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp +++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp @@ -486,8 +486,8 @@ nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel, len -= 5; // strip off any padding (see bug 230351) - while (challenge[len - 1] == '=') - len--; + while (len && challenge[len - 1] == '=') + len--; // decode into the input secbuffer rv = Base64Decode(challenge, len, (char**)&inBuf, &inBufLen); diff --git a/security/manager/ssl/TransportSecurityInfo.cpp b/security/manager/ssl/TransportSecurityInfo.cpp index 3c7023302..3f4bf4a90 100644 --- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -8,6 +8,7 @@ #include "PSMRunnable.h" #include "mozilla/Casting.h" +#include "mozilla/net/DNS.h" #include "nsComponentManagerUtils.h" #include "nsIArray.h" #include "nsICertOverrideService.h" @@ -681,8 +682,10 @@ GetSubjectAltNames(CERTCertificate* nssCert, nsString& allNames) case certIPAddress: { - char buf[INET6_ADDRSTRLEN]; + // According to DNS.h, this includes space for the null-terminator + char buf[net::kNetAddrMaxCStrBufSize] = {0}; PRNetAddr addr; + memset(&addr, 0, sizeof(addr)); if (current->name.other.len == 4) { addr.inet.family = PR_AF_INET; memcpy(&addr.inet.ip, current->name.other.data, current->name.other.len); diff --git a/security/manager/ssl/nsNSSCertHelper.cpp b/security/manager/ssl/nsNSSCertHelper.cpp index 64c87ad2f..efcb8747a 100644 --- a/security/manager/ssl/nsNSSCertHelper.cpp +++ b/security/manager/ssl/nsNSSCertHelper.cpp @@ -11,6 +11,7 @@ #include "mozilla/NotNull.h" #include "mozilla/Sprintf.h" #include "mozilla/UniquePtr.h" +#include "mozilla/net/DNS.h" #include "nsCOMPtr.h" #include "nsComponentManagerUtils.h" #include "nsDateTimeFormatCID.h" @@ -1006,8 +1007,9 @@ ProcessGeneralName(const UniquePLArenaPool& arena, CERTGeneralName* current, break; case certIPAddress: { - char buf[INET6_ADDRSTRLEN]; PRStatus status = PR_FAILURE; + // According to DNS.h, this includes space for the null-terminator + char buf[net::kNetAddrMaxCStrBufSize] = {0}; PRNetAddr addr; memset(&addr, 0, sizeof(addr)); nssComponent->GetPIPNSSBundleString("CertDumpIPAddress", key); diff --git a/uriloader/exthandler/win/nsOSHelperAppService.cpp b/uriloader/exthandler/win/nsOSHelperAppService.cpp index 48b6f1795..1d1ef2dcb 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp @@ -406,75 +406,107 @@ already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFl return mimeInfo.forget(); } -already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound) +already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, + const nsACString& aFileExt, + bool *aFound) { - *aFound = true; + *aFound = false; const nsCString& flatType = PromiseFlatCString(aMIMEType); - const nsCString& flatExt = PromiseFlatCString(aFileExt); - nsAutoString fileExtension; - /* XXX The Equals is a gross hack to wallpaper over the most common Win32 - * extension issues caused by the fix for bug 116938. See bug - * 120327, comment 271 for why this is needed. Not even sure we + CopyUTF8toUTF16(aFileExt, fileExtension); + + /* XXX The octet-stream check is a gross hack to wallpaper over the most + * common Win32 extension issues caused by the fix for bug 116938. See bug + * 120327, comment 271 for why this is needed. Not even sure we * want to remove this once we have fixed all this stuff to work * right; any info we get from the OS on this type is pretty much - * useless.... - * We'll do extension-based lookup for this type later in this function. + * useless... */ - if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) { - // try to use the windows mime database to see if there is a mapping to a file extension - GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension); - LOG(("Windows mime database: extension '%s'\n", fileExtension.get())); - } - // If we found an extension for the type, do the lookup + bool haveMeaningfulMimeType = + !aMIMEType.IsEmpty() && + !aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM); + LOG(("Extension lookup on '%s' with mimetype '%s'%s\n", fileExtension.get(), + flatType.get(), + haveMeaningfulMimeType ? " (treated as meaningful)" : "")); + RefPtr<nsMIMEInfoWin> mi; - if (!fileExtension.IsEmpty()) - mi = GetByExtension(fileExtension, flatType.get()); - LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); - bool hasDefault = false; - if (mi) { - mi->GetHasDefaultHandler(&hasDefault); - // OK. We might have the case that |aFileExt| is a valid extension for the - // mimetype we were given. In that case, we do want to append aFileExt - // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and - // .mpg, but the primary extension for video/mpeg is .mpeg. But because - // .mpg is an extension for video/mpeg content, we want to append it) - if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) { - LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n", - flatExt.get(), flatType.get())); - bool extExist = false; - mi->ExtensionExists(aFileExt, &extExist); - if (!extExist) - mi->AppendExtension(aFileExt); - } + // We should have *something* to go on here. + if (fileExtension.IsEmpty() && !haveMeaningfulMimeType) { + mi = new nsMIMEInfoWin(flatType.get()); + return mi.forget(); } - if (!mi || !hasDefault) { - RefPtr<nsMIMEInfoWin> miByExt = - GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get()); - LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get())); - if (!miByExt && mi) - return mi.forget(); - if (miByExt && !mi) { - return miByExt.forget(); - } - if (!miByExt && !mi) { - *aFound = false; - mi = new nsMIMEInfoWin(flatType); + + nsAutoString extensionFromMimeType; + if (haveMeaningfulMimeType) { + GetExtensionFromWindowsMimeDatabase(aMIMEType, extensionFromMimeType); + if (extensionFromMimeType.IsEmpty()) { + // We can't verify the mime type and file extension make sense. + mi = new nsMIMEInfoWin(flatType.get()); if (!aFileExt.IsEmpty()) { mi->AppendExtension(aFileExt); } - return mi.forget(); } + } + // Either fileExtension or extensionFromMimeType must now be non-empty. + + *aFound = true; - // if we get here, mi has no default app. copy from extension lookup. - nsCOMPtr<nsIFile> defaultApp; - nsAutoString desc; - miByExt->GetDefaultDescription(desc); + // On Windows, we prefer the file extension for lookups over the mimetype, + // because that's how windows does things. + // If we have no file extension or it doesn't match the mimetype, use the + // mime type's default file extension instead. + bool usedMimeTypeExtensionForLookup = false; + if (fileExtension.IsEmpty() || + (haveMeaningfulMimeType && + !typeFromExtEquals(fileExtension.get(), flatType.get()))) { + usedMimeTypeExtensionForLookup = true; + fileExtension = extensionFromMimeType; + LOG(("Now using '%s' mimetype's default file extension '%s' for lookup\n", + flatType.get(), fileExtension.get())); + } - mi->SetDefaultDescription(desc); + // If we have an extension, use it for lookup: + mi = GetByExtension(fileExtension, flatType.get()); + LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); + + if (mi) { + bool hasDefault = false; + mi->GetHasDefaultHandler(&hasDefault); + // If we don't find a default handler description, see if we can find one + // using the mimetype. + if (!hasDefault && !usedMimeTypeExtensionForLookup) { + RefPtr<nsMIMEInfoWin> miFromMimeType = + GetByExtension(extensionFromMimeType, flatType.get()); + LOG(("Mime-based ext. lookup for '%s' found 0x%p\n", + extensionFromMimeType.get(), miFromMimeType.get())); + if (miFromMimeType) { + nsAutoString desc; + miFromMimeType->GetDefaultDescription(desc); + mi->SetDefaultDescription(desc); + } + } + return mi.forget(); + } + // The extension didn't work. Try the extension from the mimetype if + // different: + if (!extensionFromMimeType.IsEmpty() && !usedMimeTypeExtensionForLookup) { + mi = GetByExtension(extensionFromMimeType, flatType.get()); + LOG(("Mime-based ext. lookup for '%s' found 0x%p\n", + extensionFromMimeType.get(), mi.get())); + } + if (mi) { + return mi.forget(); + } + // This didn't work either, so just return an empty dummy mimeinfo. + *aFound = false; + mi = new nsMIMEInfoWin(flatType.get()); + // If we didn't resort to the mime type's extension, we must have had a + // valid extension, so stick it on the mime info. + if (!usedMimeTypeExtensionForLookup) { + mi->AppendExtension(aFileExt); } return mi.forget(); } diff --git a/widget/IMEData.h b/widget/IMEData.h index b12497be3..04245df26 100644 --- a/widget/IMEData.h +++ b/widget/IMEData.h @@ -300,6 +300,10 @@ struct InputContext final * compatibility with webapps relying on key listeners. */ bool mMayBeIMEUnaware; + /* Whether the owning document of the input element has been loaded + * in private browsing mode. */ + bool mInPrivateBrowsing; + bool IsOriginMainProcess() const { return mOrigin == ORIGIN_MAIN; diff --git a/widget/gtk/nsClipboard.cpp b/widget/gtk/nsClipboard.cpp index 950be1dc4..1ae3673b0 100644 --- a/widget/gtk/nsClipboard.cpp +++ b/widget/gtk/nsClipboard.cpp @@ -120,24 +120,13 @@ NS_IMETHODIMP nsClipboard::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { if (strcmp(aTopic, "quit-application") == 0) { - // application is going to quit, save clipboard content - Store(); + // Application is going to quit, save clipboard content + gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); gdk_window_remove_filter(nullptr, selection_request_filter, nullptr); } return NS_OK; } -nsresult -nsClipboard::Store(void) -{ - // Ask the clipboard manager to store the current clipboard content - if (mGlobalTransferable) { - GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); - gtk_clipboard_store(clipboard); - } - return NS_OK; -} - NS_IMETHODIMP nsClipboard::SetData(nsITransferable *aTransferable, nsIClipboardOwner *aOwner, int32_t aWhichClipboard) @@ -152,9 +141,6 @@ nsClipboard::SetData(nsITransferable *aTransferable, return NS_OK; } - // Clear out the clipboard in order to set the new data - EmptyClipboard(aWhichClipboard); - // List of suported targets GtkTargetList *list = gtk_target_list_new(nullptr, 0); @@ -163,8 +149,12 @@ nsClipboard::SetData(nsITransferable *aTransferable, nsresult rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors)); - if (!flavors || NS_FAILED(rv)) + if (!flavors || NS_FAILED(rv)) { + // Clear references to the any old data and let GTK know that it is no + // longer available. + EmptyClipboard(aWhichClipboard); return NS_ERROR_FAILURE; + } // Add all the flavors to this widget's supported type. bool imagesAdded = false; @@ -232,8 +222,10 @@ nsClipboard::SetData(nsITransferable *aTransferable, } rv = NS_OK; - } - else { + } else { + // Clear references to the any old data and let GTK know that it is no + // longer available. + EmptyClipboard(aWhichClipboard); rv = NS_ERROR_FAILURE; } @@ -373,6 +365,23 @@ nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) NS_IMETHODIMP nsClipboard::EmptyClipboard(int32_t aWhichClipboard) { + if (aWhichClipboard == kSelectionClipboard) { + if (mSelectionTransferable) { + gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); + MOZ_ASSERT(!mSelectionTransferable); + } + } else { + if (mGlobalTransferable) { + gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); + MOZ_ASSERT(!mGlobalTransferable); + } + } + + return NS_OK; +} + +void +nsClipboard::ClearTransferable(int32_t aWhichClipboard) { if (aWhichClipboard == kSelectionClipboard) { if (mSelectionOwner) { mSelectionOwner->LosingOwnership(mSelectionTransferable); @@ -387,8 +396,6 @@ nsClipboard::EmptyClipboard(int32_t aWhichClipboard) } mGlobalTransferable = nullptr; } - - return NS_OK; } NS_IMETHODIMP @@ -652,7 +659,7 @@ nsClipboard::SelectionClearEvent(GtkClipboard *aGtkClipboard) else return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF - EmptyClipboard(whichClipboard); + ClearTransferable(whichClipboard); } void diff --git a/widget/gtk/nsClipboard.h b/widget/gtk/nsClipboard.h index 70c866a01..c3129bf20 100644 --- a/widget/gtk/nsClipboard.h +++ b/widget/gtk/nsClipboard.h @@ -39,13 +39,12 @@ private: static GdkAtom GetSelectionAtom (int32_t aWhichClipboard); static GtkSelectionData *GetTargets (GdkAtom aWhichClipboard); - // Save global clipboard content to gtk - nsresult Store (void); - // Get our hands on the correct transferable, given a specific // clipboard nsITransferable *GetTransferable (int32_t aWhichClipboard); + void ClearTransferable(int32_t aWhichClipboard); + // Hang on to our owners and transferables so we can transfer data // when asked. nsCOMPtr<nsIClipboardOwner> mSelectionOwner; diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp index c80de831c..12b8cd0c1 100644 --- a/widget/windows/TSFTextStore.cpp +++ b/widget/windows/TSFTextStore.cpp @@ -1355,7 +1355,9 @@ TSFTextStore::Init(nsWindowBase* aWidget, return false; } - SetInputScope(aContext.mHTMLInputType, aContext.mHTMLInputInputmode); + SetInputScope(aContext.mHTMLInputType, + aContext.mHTMLInputInputmode, + aContext.mInPrivateBrowsing); // Create document manager RefPtr<ITfThreadMgr> threadMgr = sThreadMgr; @@ -3205,9 +3207,15 @@ TSFTextStore::InsertEmbedded(DWORD dwFlags, void TSFTextStore::SetInputScope(const nsString& aHTMLInputType, - const nsString& aHTMLInputInputMode) + const nsString& aHTMLInputInputMode, + bool aInPrivateBrowsing) { mInputScopes.Clear(); + + if (aInPrivateBrowsing) { + mInputScopes.AppendElement(IS_PRIVATE); + } + if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) { if (aHTMLInputInputMode.EqualsLiteral("url")) { mInputScopes.AppendElement(IS_URL); @@ -5688,7 +5696,8 @@ TSFTextStore::SetInputContext(nsWindowBase* aWidget, if (sEnabledTextStore) { RefPtr<TSFTextStore> textStore(sEnabledTextStore); textStore->SetInputScope(aContext.mHTMLInputType, - aContext.mHTMLInputInputmode); + aContext.mHTMLInputInputmode, + aContext.mInPrivateBrowsing); } return; } diff --git a/widget/windows/TSFTextStore.h b/widget/windows/TSFTextStore.h index 9596510d5..d1431e95d 100644 --- a/widget/windows/TSFTextStore.h +++ b/widget/windows/TSFTextStore.h @@ -355,7 +355,8 @@ protected: ULONG aFilterCount, const TS_ATTRID* aFilterAttrs); void SetInputScope(const nsString& aHTMLInputType, - const nsString& aHTMLInputInputmode); + const nsString& aHTMLInputInputmode, + bool aInPrivateBrowsing); // Creates native caret over our caret. This method only works on desktop // application. Otherwise, this does nothing. diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index d44f729c4..9debaa2dd 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -409,7 +409,7 @@ IMEHandler::OnDestroyWindow(nsWindow* aWindow) if (!sIsInTSFMode) { // MSDN says we need to set IS_DEFAULT to avoid memory leak when we use // SetInputScopes API. Use an empty string to do this. - SetInputScopeForIMM32(aWindow, EmptyString(), EmptyString()); + SetInputScopeForIMM32(aWindow, EmptyString(), EmptyString(), false); } #endif // #ifdef NS_ENABLE_TSF AssociateIMEContext(aWindow, true); @@ -481,8 +481,10 @@ IMEHandler::SetInputContext(nsWindow* aWindow, } } else { // Set at least InputScope even when TextStore is not available. - SetInputScopeForIMM32(aWindow, aInputContext.mHTMLInputType, - aInputContext.mHTMLInputInputmode); + SetInputScopeForIMM32(aWindow, + aInputContext.mHTMLInputType, + aInputContext.mHTMLInputInputmode, + aInputContext.mInPrivateBrowsing); } #endif // #ifdef NS_ENABLE_TSF @@ -583,82 +585,64 @@ IMEHandler::OnKeyboardLayoutChanged() void IMEHandler::SetInputScopeForIMM32(nsWindow* aWindow, const nsAString& aHTMLInputType, - const nsAString& aHTMLInputInputmode) + const nsAString& aHTMLInputInputmode, + bool aInPrivateBrowsing) { if (sIsInTSFMode || !sSetInputScopes || aWindow->Destroyed()) { return; } - UINT arraySize = 0; - const InputScope* scopes = nullptr; + AutoTArray<InputScope, 3> scopes; + + if (aInPrivateBrowsing) { + scopes.AppendElement(IS_PRIVATE); + } + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) { if (aHTMLInputInputmode.EqualsLiteral("url")) { - static const InputScope inputScopes[] = { IS_URL }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_URL); } else if (aHTMLInputInputmode.EqualsLiteral("email")) { - static const InputScope inputScopes[] = { IS_EMAIL_SMTPEMAILADDRESS }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS); } else if (aHTMLInputInputmode.EqualsLiteral("tel")) { - static const InputScope inputScopes[] = - {IS_TELEPHONE_LOCALNUMBER, IS_TELEPHONE_FULLTELEPHONENUMBER}; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_TELEPHONE_LOCALNUMBER); + scopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER); } else if (aHTMLInputInputmode.EqualsLiteral("numeric")) { - static const InputScope inputScopes[] = { IS_NUMBER }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_NUMBER); } else { - static const InputScope inputScopes[] = { IS_DEFAULT }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_DEFAULT); } } else if (aHTMLInputType.EqualsLiteral("url")) { - static const InputScope inputScopes[] = { IS_URL }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_URL); } else if (aHTMLInputType.EqualsLiteral("search")) { - static const InputScope inputScopes[] = { IS_SEARCH }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_SEARCH); } else if (aHTMLInputType.EqualsLiteral("email")) { - static const InputScope inputScopes[] = { IS_EMAIL_SMTPEMAILADDRESS }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS); } else if (aHTMLInputType.EqualsLiteral("password")) { - static const InputScope inputScopes[] = { IS_PASSWORD }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_PASSWORD); } else if (aHTMLInputType.EqualsLiteral("datetime") || aHTMLInputType.EqualsLiteral("datetime-local")) { - static const InputScope inputScopes[] = { - IS_DATE_FULLDATE, IS_TIME_FULLTIME }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_DATE_FULLDATE); + scopes.AppendElement(IS_TIME_FULLTIME); } else if (aHTMLInputType.EqualsLiteral("date") || aHTMLInputType.EqualsLiteral("month") || aHTMLInputType.EqualsLiteral("week")) { - static const InputScope inputScopes[] = { IS_DATE_FULLDATE }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_DATE_FULLDATE); } else if (aHTMLInputType.EqualsLiteral("time")) { - static const InputScope inputScopes[] = { IS_TIME_FULLTIME }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_TIME_FULLTIME); } else if (aHTMLInputType.EqualsLiteral("tel")) { - static const InputScope inputScopes[] = { - IS_TELEPHONE_FULLTELEPHONENUMBER, IS_TELEPHONE_LOCALNUMBER }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER); + scopes.AppendElement(IS_TELEPHONE_LOCALNUMBER); } else if (aHTMLInputType.EqualsLiteral("number")) { - static const InputScope inputScopes[] = { IS_NUMBER }; - scopes = &inputScopes[0]; - arraySize = ArrayLength(inputScopes); + scopes.AppendElement(IS_NUMBER); } - if (scopes && arraySize > 0) { - sSetInputScopes(aWindow->GetWindowHandle(), scopes, arraySize, nullptr, 0, - nullptr, nullptr); + if (!scopes.IsEmpty()) { + sSetInputScopes(aWindow->GetWindowHandle(), + scopes.Elements(), + scopes.Length(), + nullptr, + 0, + nullptr, + nullptr); } } diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index c18a4437e..c836e8626 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -144,7 +144,8 @@ private: static decltype(SetInputScopes)* sSetInputScopes; static void SetInputScopeForIMM32(nsWindow* aWindow, const nsAString& aHTMLInputType, - const nsAString& aHTMLInputInputmode); + const nsAString& aHTMLInputInputmode, + bool aInPrivateBrowsing); static bool sIsInTSFMode; // If sIMMEnabled is false, any IME messages are not handled in TSF mode. // Additionally, IME context is always disassociated from focused window. |