mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge inbound and m-c.
This commit is contained in:
commit
c6e480b87f
@ -605,6 +605,9 @@
|
||||
@BINPATH@/res/EditorOverride.css
|
||||
@BINPATH@/res/contenteditable.css
|
||||
@BINPATH@/res/designmode.css
|
||||
@BINPATH@/res/ImageDocument.css
|
||||
@BINPATH@/res/TopLevelImageDocument.css
|
||||
@BINPATH@/res/TopLevelVideoDocument.css
|
||||
@BINPATH@/res/table-add-column-after-active.gif
|
||||
@BINPATH@/res/table-add-column-after-hover.gif
|
||||
@BINPATH@/res/table-add-column-after.gif
|
||||
|
@ -516,6 +516,41 @@ BrowserGlue.prototype = {
|
||||
nb.PRIORITY_INFO_LOW, buttons);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a notification bar offering a reset if the profile has been unused for some time.
|
||||
*/
|
||||
_resetUnusedProfileNotification: function () {
|
||||
let win = this.getMostRecentBrowserWindow();
|
||||
if (!win)
|
||||
return;
|
||||
|
||||
Cu.import("resource://gre/modules/ResetProfile.jsm");
|
||||
if (!ResetProfile.resetSupported())
|
||||
return;
|
||||
|
||||
let productName = Services.strings
|
||||
.createBundle("chrome://branding/locale/brand.properties")
|
||||
.GetStringFromName("brandShortName");
|
||||
let resetBundle = Services.strings
|
||||
.createBundle("chrome://global/locale/resetProfile.properties");
|
||||
|
||||
let message = resetBundle.formatStringFromName("resetUnusedProfile.message", [productName], 1);
|
||||
let buttons = [
|
||||
{
|
||||
label: resetBundle.formatStringFromName("resetProfile.resetButton.label", [productName], 1),
|
||||
accessKey: resetBundle.GetStringFromName("resetProfile.resetButton.accesskey"),
|
||||
callback: function () {
|
||||
ResetProfile.openConfirmationDialog(win);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
let nb = win.document.getElementById("global-notificationbox");
|
||||
nb.appendNotification(message, "reset-unused-profile",
|
||||
"chrome://global/skin/icons/question-16.png",
|
||||
nb.PRIORITY_INFO_LOW, buttons);
|
||||
},
|
||||
|
||||
// the first browser window has finished initializing
|
||||
_onFirstWindowLoaded: function BG__onFirstWindowLoaded() {
|
||||
#ifdef XP_WIN
|
||||
@ -530,6 +565,15 @@ BrowserGlue.prototype = {
|
||||
#endif
|
||||
|
||||
this._trackSlowStartup();
|
||||
|
||||
// Offer to reset a user's profile if it hasn't been used for 60 days.
|
||||
const OFFER_PROFILE_RESET_INTERVAL_MS = 60 * 24 * 60 * 60 * 1000;
|
||||
let processStartupTime = Services.startup.getStartupInfo().process;
|
||||
let lastUse = Services.appinfo.replacedLockTime;
|
||||
if (processStartupTime && lastUse &&
|
||||
processStartupTime.getTime() - lastUse >= OFFER_PROFILE_RESET_INTERVAL_MS) {
|
||||
this._resetUnusedProfileNotification();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,8 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ForgetAboutSite",
|
||||
"resource://gre/modules/ForgetAboutSite.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
|
@ -4,8 +4,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsNullPrincipalURI.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsEscape.h"
|
||||
#include "nsCRT.h"
|
||||
|
@ -4340,7 +4340,7 @@ MOZ_ARG_WITH_STRING(google-api-keyfile,
|
||||
if test -z "$MOZ_GOOGLE_API_KEY"; then
|
||||
MOZ_GOOGLE_API_KEY=no-google-api-key
|
||||
fi
|
||||
AC_DEFINE_UNQUOTED(MOZ_GOOGLE_API_KEY, $MOZ_GOOGLE_API_KEY)
|
||||
AC_SUBST(MOZ_GOOGLE_API_KEY)
|
||||
|
||||
# Allow the application to influence configure with a confvars.sh script.
|
||||
AC_MSG_CHECKING([if app-specific confvars.sh exists])
|
||||
|
@ -8,8 +8,10 @@
|
||||
* the two is unified to minimize footprint.
|
||||
*/
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsAttrAndChildArray.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsMappedAttributeElement.h"
|
||||
#include "prbit.h"
|
||||
#include "nsString.h"
|
||||
|
@ -20,8 +20,10 @@
|
||||
* nsIAtom pointers, and the values are void pointers.
|
||||
*/
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsPropertyTable.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "pldhash.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIAtom.h"
|
||||
|
@ -4,8 +4,9 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLAnchorElement.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLAnchorElementBinding.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -4,8 +4,9 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLAreaElement.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLAreaElementBinding.h"
|
||||
#include "base/compiler_specific.h"
|
||||
|
||||
|
@ -3,9 +3,9 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLLinkElement.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/dom/HTMLLinkElementBinding.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "gfxDWriteFonts.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "gfxDWriteShaper.h"
|
||||
#include "gfxHarfBuzzShaper.h"
|
||||
#include <algorithm>
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "gfxGDIFont.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "gfxGDIShaper.h"
|
||||
#include "gfxUniscribeShaper.h"
|
||||
#include "gfxHarfBuzzShaper.h"
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "gfxMacFont.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "gfxCoreTextShaper.h"
|
||||
#include "gfxHarfBuzzShaper.h"
|
||||
#include <algorithm>
|
||||
|
@ -3,9 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "ImageWrapper.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
using mozilla::layers::LayerManager;
|
||||
using mozilla::layers::ImageContainer;
|
||||
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "FramePropertyTable.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "prlog.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -2,9 +2,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "StackArena.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#define STACK_ARENA_MARK_INCREMENT 50
|
||||
|
@ -3,9 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsTextRunTransformations.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsTextFrameUtils.h"
|
||||
#include "gfxSkipChars.h"
|
||||
#include "nsGkAtoms.h"
|
||||
|
@ -9,8 +9,9 @@
|
||||
* declarations
|
||||
*/
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/css/StyleRule.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/css/GroupRule.h"
|
||||
#include "mozilla/css/Declaration.h"
|
||||
#include "nsCSSStyleSheet.h"
|
||||
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsAnimationManager.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsPresContext.h"
|
||||
#include "nsRuleProcessorData.h"
|
||||
#include "nsStyleSet.h"
|
||||
|
@ -20,6 +20,7 @@ import org.webrtc.videoengine.VideoCaptureDeviceInfoAndroid.AndroidVideoCaptureD
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.graphics.YuvImage;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.PreviewCallback;
|
||||
@ -27,6 +28,10 @@ import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceHolder.Callback;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import android.view.TextureView.SurfaceTextureListener;
|
||||
import android.view.View;
|
||||
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
@ -50,6 +55,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
private boolean isCaptureRunning = false;
|
||||
private boolean isSurfaceReady = false;
|
||||
private SurfaceHolder surfaceHolder = null;
|
||||
private SurfaceTexture surfaceTexture = null;
|
||||
|
||||
private final int numCaptureBuffers = 3;
|
||||
private int expectedFrameSize = 0;
|
||||
@ -64,9 +70,42 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
private int mCaptureWidth = -1;
|
||||
private int mCaptureHeight = -1;
|
||||
private int mCaptureFPS = -1;
|
||||
private int mCaptureRotation = 0;
|
||||
|
||||
private AppStateListener mAppStateListener = null;
|
||||
|
||||
public class MySurfaceTextureListener implements TextureView.SurfaceTextureListener {
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
||||
Log.d(TAG, "VideoCaptureAndroid::onSurfaceTextureAvailable");
|
||||
|
||||
captureLock.lock();
|
||||
isSurfaceReady = true;
|
||||
surfaceTexture = surface;
|
||||
|
||||
tryStartCapture(mCaptureWidth, mCaptureHeight, mCaptureFPS);
|
||||
captureLock.unlock();
|
||||
}
|
||||
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surface,
|
||||
int width, int height) {
|
||||
// Ignored, Camera does all the work for us
|
||||
// Note that for a TextureView we start on onSurfaceTextureAvailable,
|
||||
// for a SurfaceView we start on surfaceChanged. TextureView
|
||||
// will not give out an onSurfaceTextureSizeChanged during creation.
|
||||
}
|
||||
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||
Log.d(TAG, "VideoCaptureAndroid::onSurfaceTextureDestroyed");
|
||||
isSurfaceReady = false;
|
||||
DetachCamera();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
||||
// Invoked every time there's a new Camera preview frame
|
||||
}
|
||||
}
|
||||
|
||||
public static
|
||||
void DeleteVideoCaptureAndroid(VideoCaptureAndroid captureAndroid) {
|
||||
Log.d(TAG, "DeleteVideoCaptureAndroid");
|
||||
@ -78,8 +117,13 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
captureAndroid.camera = null;
|
||||
captureAndroid.context = 0;
|
||||
|
||||
GeckoAppShell.getGeckoInterface().getCameraView().getHolder().
|
||||
removeCallback(captureAndroid);
|
||||
View cameraView = GeckoAppShell.getGeckoInterface().getCameraView();
|
||||
if (cameraView instanceof SurfaceView) {
|
||||
((SurfaceView)cameraView).getHolder().removeCallback(captureAndroid);
|
||||
} else if (cameraView instanceof TextureView) {
|
||||
// No need to explicitly remove the Listener:
|
||||
// i.e. ((SurfaceView)cameraView).setSurfaceTextureListener(null);
|
||||
}
|
||||
ThreadUtils.getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -102,9 +146,16 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
camera = in_camera;
|
||||
cameraId = in_cameraId;
|
||||
currentDevice = in_device;
|
||||
mCaptureRotation = GetRotateAmount();
|
||||
|
||||
try {
|
||||
GeckoAppShell.getGeckoInterface().getCameraView().getHolder().addCallback(this);
|
||||
View cameraView = GeckoAppShell.getGeckoInterface().getCameraView();
|
||||
if (cameraView instanceof SurfaceView) {
|
||||
((SurfaceView)cameraView).getHolder().addCallback(this);
|
||||
} else if (cameraView instanceof TextureView) {
|
||||
MySurfaceTextureListener listener = new MySurfaceTextureListener();
|
||||
((TextureView)cameraView).setSurfaceTextureListener(listener);
|
||||
}
|
||||
ThreadUtils.getUiHandler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -145,6 +196,10 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
tryStartCapture(mCaptureWidth, mCaptureHeight, mCaptureFPS);
|
||||
captureLock.unlock();
|
||||
}
|
||||
@Override
|
||||
public void onConfigurationChanged() {
|
||||
mCaptureRotation = GetRotateAmount();
|
||||
}
|
||||
};
|
||||
|
||||
GeckoAppShell.getGeckoInterface().addAppStateListener(mAppStateListener);
|
||||
@ -190,7 +245,10 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
}
|
||||
|
||||
try {
|
||||
camera.setPreviewDisplay(surfaceHolder);
|
||||
if (surfaceHolder != null)
|
||||
camera.setPreviewDisplay(surfaceHolder);
|
||||
if (surfaceTexture != null)
|
||||
camera.setPreviewTexture(surfaceTexture);
|
||||
|
||||
CaptureCapabilityAndroid currentCapability =
|
||||
new CaptureCapabilityAndroid();
|
||||
@ -268,7 +326,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
return DetachCamera();
|
||||
}
|
||||
|
||||
native void ProvideCameraFrame(byte[] data, int length, long captureObject);
|
||||
native void ProvideCameraFrame(byte[] data, int length, int rotation,
|
||||
long captureObject);
|
||||
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
previewBufferLock.lock();
|
||||
@ -280,7 +339,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
// If StartCapture has been called but not StopCapture
|
||||
// Call the C++ layer with the captured frame
|
||||
if (data.length == expectedFrameSize) {
|
||||
ProvideCameraFrame(data, expectedFrameSize, context);
|
||||
ProvideCameraFrame(data, expectedFrameSize, mCaptureRotation,
|
||||
context);
|
||||
if (ownsBuffers) {
|
||||
// Give the video buffer to the camera service again.
|
||||
camera.addCallbackBuffer(data);
|
||||
@ -290,46 +350,6 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback {
|
||||
previewBufferLock.unlock();
|
||||
}
|
||||
|
||||
// Sets the rotation of the preview render window.
|
||||
// Does not affect the captured video image.
|
||||
public void SetPreviewRotation(int rotation) {
|
||||
Log.v(TAG, "SetPreviewRotation: " + rotation);
|
||||
|
||||
if (camera != null) {
|
||||
previewBufferLock.lock();
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int framerate = 0;
|
||||
boolean wasCaptureRunning = isCaptureRunning;
|
||||
|
||||
if (isCaptureRunning) {
|
||||
width = mCaptureWidth;
|
||||
height = mCaptureHeight;
|
||||
framerate = mCaptureFPS;
|
||||
StopCapture();
|
||||
}
|
||||
|
||||
int resultRotation = 0;
|
||||
if (currentDevice.frontCameraType ==
|
||||
VideoCaptureDeviceInfoAndroid.FrontFacingCameraType.Android23) {
|
||||
// this is a 2.3 or later front facing camera.
|
||||
// SetDisplayOrientation will flip the image horizontally
|
||||
// before doing the rotation.
|
||||
resultRotation=(360-rotation) % 360; // compensate the mirror
|
||||
}
|
||||
else {
|
||||
// Back facing or 2.2 or previous front camera
|
||||
resultRotation=rotation;
|
||||
}
|
||||
camera.setDisplayOrientation(resultRotation);
|
||||
|
||||
if (wasCaptureRunning) {
|
||||
StartCapture(width, height, framerate);
|
||||
}
|
||||
previewBufferLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.d(TAG, "VideoCaptureAndroid::surfaceChanged");
|
||||
|
@ -92,7 +92,7 @@ WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM,
|
||||
return -1;
|
||||
}
|
||||
JNINativeMethod nativeFunctions =
|
||||
{ "ProvideCameraFrame", "([BIJ)V",
|
||||
{ "ProvideCameraFrame", "([BIIJ)V",
|
||||
(void*) &VideoCaptureAndroid::ProvideCameraFrame };
|
||||
if (env->RegisterNatives(g_javaCmClass, &nativeFunctions, 1) == 0) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
|
||||
@ -251,11 +251,29 @@ void JNICALL VideoCaptureAndroid::ProvideCameraFrame(JNIEnv * env,
|
||||
jobject,
|
||||
jbyteArray javaCameraFrame,
|
||||
jint length,
|
||||
jint rotation,
|
||||
jlong context) {
|
||||
VideoCaptureAndroid* captureModule =
|
||||
reinterpret_cast<VideoCaptureAndroid*>(context);
|
||||
WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture,
|
||||
-1, "%s: IncomingFrame %d", __FUNCTION__,length);
|
||||
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
captureModule->SetCaptureRotation(kCameraRotate90);
|
||||
break;
|
||||
case 180:
|
||||
captureModule->SetCaptureRotation(kCameraRotate180);
|
||||
break;
|
||||
case 270:
|
||||
captureModule->SetCaptureRotation(kCameraRotate270);
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
captureModule->SetCaptureRotation(kCameraRotate0);
|
||||
break;
|
||||
}
|
||||
|
||||
jbyte* cameraFrame= env->GetByteArrayElements(javaCameraFrame,NULL);
|
||||
captureModule->IncomingFrame((WebRtc_UWord8*) cameraFrame,
|
||||
length,captureModule->_frameInfo,0);
|
||||
@ -451,7 +469,6 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture(
|
||||
|
||||
bool isAttached = false;
|
||||
WebRtc_Word32 result = 0;
|
||||
WebRtc_Word32 rotation = 0;
|
||||
// get the JNI env for this thread
|
||||
JNIEnv *env;
|
||||
if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
|
||||
@ -498,21 +515,6 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture(
|
||||
"%s: Failed to find StartCapture id", __FUNCTION__);
|
||||
}
|
||||
|
||||
// get the method ID for the Android Java
|
||||
// CaptureClass static GetRotateAmount method.
|
||||
cid = env->GetMethodID(g_javaCmClass, "GetRotateAmount", "()I");
|
||||
if (cid != NULL) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
|
||||
"%s: Call GetRotateAmount", __FUNCTION__);
|
||||
rotation = env->CallIntMethod(_javaCaptureObj, cid);
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1,
|
||||
"%s, GetRotateAmount = %d", __FUNCTION__, rotation);
|
||||
}
|
||||
else {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
|
||||
"%s: Failed to find GetRotateAmount id", __FUNCTION__);
|
||||
}
|
||||
|
||||
// Detach this thread if it was attached
|
||||
if (isAttached) {
|
||||
if (g_jvm->DetachCurrentThread() < 0) {
|
||||
@ -521,22 +523,6 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture(
|
||||
}
|
||||
}
|
||||
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
SetCaptureRotation(kCameraRotate90);
|
||||
break;
|
||||
case 180:
|
||||
SetCaptureRotation(kCameraRotate180);
|
||||
break;
|
||||
case 270:
|
||||
SetCaptureRotation(kCameraRotate270);
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
SetCaptureRotation(kCameraRotate0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
_requestedCapability = capability;
|
||||
_captureStarted = true;
|
||||
@ -618,65 +604,7 @@ WebRtc_Word32 VideoCaptureAndroid::CaptureSettings(
|
||||
WebRtc_Word32 VideoCaptureAndroid::SetCaptureRotation(
|
||||
VideoCaptureRotation rotation) {
|
||||
CriticalSectionScoped cs(&_apiCs);
|
||||
if (VideoCaptureImpl::SetCaptureRotation(rotation) == 0) {
|
||||
if (!g_jvm)
|
||||
return -1;
|
||||
|
||||
// get the JNI env for this thread
|
||||
JNIEnv *env;
|
||||
bool isAttached = false;
|
||||
|
||||
// get the JNI env for this thread
|
||||
if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
|
||||
// try to attach the thread and get the env
|
||||
// Attach this thread to JVM
|
||||
jint res = g_jvm->AttachCurrentThread(&env, NULL);
|
||||
if ((res < 0) || !env) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture,
|
||||
_id,
|
||||
"%s: Could not attach thread to JVM (%d, %p)",
|
||||
__FUNCTION__, res, env);
|
||||
return -1;
|
||||
}
|
||||
isAttached = true;
|
||||
}
|
||||
|
||||
jmethodID cid = env->GetMethodID(g_javaCmClass, "SetPreviewRotation",
|
||||
"(I)V");
|
||||
if (cid == NULL) {
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1,
|
||||
"%s: could not get java SetPreviewRotation ID",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
jint rotateFrame = 0;
|
||||
switch (rotation) {
|
||||
case kCameraRotate0:
|
||||
rotateFrame = 0;
|
||||
break;
|
||||
case kCameraRotate90:
|
||||
rotateFrame = 90;
|
||||
break;
|
||||
case kCameraRotate180:
|
||||
rotateFrame = 180;
|
||||
break;
|
||||
case kCameraRotate270:
|
||||
rotateFrame = 270;
|
||||
break;
|
||||
}
|
||||
env->CallVoidMethod(_javaCaptureObj, cid, rotateFrame);
|
||||
|
||||
// Detach this thread if it was attached
|
||||
if (isAttached) {
|
||||
if (g_jvm->DetachCurrentThread() < 0) {
|
||||
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceAudioDevice,
|
||||
_id, "%s: Could not detach thread from JVM",
|
||||
__FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
return VideoCaptureImpl::SetCaptureRotation(rotation);
|
||||
}
|
||||
|
||||
} // namespace videocapturemodule
|
||||
|
@ -47,7 +47,9 @@ class VideoCaptureAndroid : public VideoCaptureImpl {
|
||||
static void JNICALL ProvideCameraFrame (JNIEnv * env,
|
||||
jobject,
|
||||
jbyteArray javaCameraFrame,
|
||||
jint length, jlong context);
|
||||
jint length,
|
||||
jint rotation,
|
||||
jlong context);
|
||||
DeviceInfoAndroid _capInfo;
|
||||
jobject _javaCaptureObj; // Java Camera object.
|
||||
VideoCaptureCapability _frameInfo;
|
||||
|
@ -83,6 +83,7 @@ import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
@ -163,7 +164,7 @@ abstract public class GeckoApp
|
||||
protected RelativeLayout mMainLayout;
|
||||
protected RelativeLayout mGeckoLayout;
|
||||
public View getView() { return mGeckoLayout; }
|
||||
public SurfaceView mCameraView;
|
||||
private View mCameraView;
|
||||
public List<GeckoAppShell.AppStateListener> mAppStateListeners;
|
||||
private static GeckoApp sAppContext;
|
||||
protected MenuPanel mMenuPanel;
|
||||
@ -257,7 +258,7 @@ abstract public class GeckoApp
|
||||
return GeckoApp.sAppContext.getSharedPreferences(PREFS_NAME, 0);
|
||||
}
|
||||
|
||||
public SurfaceView getCameraView() {
|
||||
public View getCameraView() {
|
||||
return mCameraView;
|
||||
}
|
||||
|
||||
@ -1303,8 +1304,12 @@ abstract public class GeckoApp
|
||||
mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
|
||||
|
||||
if (mCameraView == null) {
|
||||
mCameraView = new SurfaceView(this);
|
||||
mCameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
mCameraView = new SurfaceView(this);
|
||||
((SurfaceView)mCameraView).getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
} else {
|
||||
mCameraView = new TextureView(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (mLayerView == null) {
|
||||
@ -1688,12 +1693,23 @@ abstract public class GeckoApp
|
||||
}
|
||||
|
||||
public void enableCameraView() {
|
||||
if (mCameraView instanceof SurfaceView) {
|
||||
// Try to make it fully transparent.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
mCameraView.setAlpha(0.0f);
|
||||
}
|
||||
} else if (mCameraView instanceof TextureView) {
|
||||
mCameraView.setAlpha(0.0f);
|
||||
}
|
||||
RelativeLayout mCameraLayout = (RelativeLayout) findViewById(R.id.camera_layout);
|
||||
// Some phones (eg. nexus S) need at least a 8x16 preview size
|
||||
mMainLayout.addView(mCameraView, new AbsoluteLayout.LayoutParams(8, 16, 0, 0));
|
||||
mCameraLayout.addView(mCameraView,
|
||||
new AbsoluteLayout.LayoutParams(8, 16, 0, 0));
|
||||
}
|
||||
|
||||
public void disableCameraView() {
|
||||
mMainLayout.removeView(mCameraView);
|
||||
RelativeLayout mCameraLayout = (RelativeLayout) findViewById(R.id.camera_layout);
|
||||
mCameraLayout.removeView(mCameraView);
|
||||
}
|
||||
|
||||
public String getDefaultUAString() {
|
||||
@ -2060,6 +2076,12 @@ abstract public class GeckoApp
|
||||
mFormAssistPopup.hide();
|
||||
refreshChrome();
|
||||
}
|
||||
|
||||
if (mAppStateListeners != null) {
|
||||
for (GeckoAppShell.AppStateListener listener: mAppStateListeners) {
|
||||
listener.onConfigurationChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,6 +69,7 @@ import android.view.ContextThemeWrapper;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
@ -1964,6 +1965,7 @@ public class GeckoAppShell
|
||||
public interface AppStateListener {
|
||||
public void onPause();
|
||||
public void onResume();
|
||||
public void onConfigurationChanged();
|
||||
}
|
||||
|
||||
public interface GeckoInterface {
|
||||
@ -1981,7 +1983,7 @@ public class GeckoAppShell
|
||||
public void disableCameraView();
|
||||
public void addAppStateListener(AppStateListener listener);
|
||||
public void removeAppStateListener(AppStateListener listener);
|
||||
public SurfaceView getCameraView();
|
||||
public View getCameraView();
|
||||
public void notifyWakeLockChanged(String topic, String state);
|
||||
public FormAssistPopup getFormAssistPopup();
|
||||
public boolean areTabsShown();
|
||||
@ -2070,12 +2072,18 @@ public class GeckoAppShell
|
||||
}
|
||||
|
||||
try {
|
||||
if (getGeckoInterface() != null)
|
||||
sCamera.setPreviewDisplay(getGeckoInterface().getCameraView().getHolder());
|
||||
if (getGeckoInterface() != null) {
|
||||
View cameraView = getGeckoInterface().getCameraView();
|
||||
if (cameraView instanceof SurfaceView) {
|
||||
sCamera.setPreviewDisplay(((SurfaceView)cameraView).getHolder());
|
||||
} else if (cameraView instanceof TextureView) {
|
||||
sCamera.setPreviewTexture(((TextureView)cameraView).getSurfaceTexture());
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
Log.w(LOGTAG, "Error setPreviewDisplay:", e);
|
||||
Log.w(LOGTAG, "Error setPreviewXXX:", e);
|
||||
} catch(RuntimeException e) {
|
||||
Log.w(LOGTAG, "Error setPreviewDisplay:", e);
|
||||
Log.w(LOGTAG, "Error setPreviewXXX:", e);
|
||||
}
|
||||
|
||||
sCamera.setParameters(params);
|
||||
|
@ -35,6 +35,13 @@
|
||||
style="@style/FindBar"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<RelativeLayout android:id="@+id/camera_layout"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true">
|
||||
</RelativeLayout>
|
||||
|
||||
<org.mozilla.gecko.BrowserToolbar android:id="@+id/browser_toolbar"
|
||||
style="@style/BrowserToolbar"
|
||||
android:layout_width="fill_parent"
|
||||
|
@ -12,8 +12,10 @@ pref("security.ssl.renego_unrestricted_hosts", "");
|
||||
pref("security.ssl.treat_unsafe_negotiation_as_broken", false);
|
||||
pref("security.ssl.require_safe_negotiation", false);
|
||||
pref("security.ssl.warn_missing_rfc5746", 1);
|
||||
pref("security.ssl.enable_false_start", false);
|
||||
pref("security.ssl.enable_ocsp_stapling", true);
|
||||
pref("security.ssl.enable_false_start", true);
|
||||
pref("security.ssl.false_start.require-npn", true);
|
||||
pref("security.ssl.false_start.require-forward-secrecy", false);
|
||||
|
||||
pref("security.ssl3.rsa_rc4_128_md5", true);
|
||||
pref("security.ssl3.rsa_rc4_128_sha", true);
|
||||
|
@ -127,8 +127,8 @@ nsHttpConnection::PrintDiagnostics(nsCString &log)
|
||||
{
|
||||
log.AppendPrintf(" CanDirectlyActivate = %d\n", CanDirectlyActivate());
|
||||
|
||||
log.AppendPrintf(" npncomplete = %d setupNPNCalled = %d\n",
|
||||
mNPNComplete, mSetupNPNCalled);
|
||||
log.AppendPrintf(" npncomplete = %d setupSSLCalled = %d\n",
|
||||
mNPNComplete, mSetupSSLCalled);
|
||||
|
||||
log.AppendPrintf(" spdyVersion = %d reportedSpdy = %d everspdy = %d\n",
|
||||
mUsingSpdyVersion, mReportedSpdy, mEverUsedSpdy);
|
||||
|
@ -58,6 +58,11 @@ typedef uint8_t nsHttpVersion;
|
||||
// group is currently blocking on some resources
|
||||
#define NS_HTTP_LOAD_UNBLOCKED (1<<8)
|
||||
|
||||
// These flags allow a transaction to use TLS false start with
|
||||
// weaker security profiles based on past history
|
||||
#define NS_HTTP_ALLOW_RSA_FALSESTART (1<<9)
|
||||
#define NS_HTTP_ALLOW_RC4_FALSESTART (1<<10)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// some default values
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -40,6 +40,14 @@
|
||||
#include "NullHttpTransaction.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/VisualEventTracer.h"
|
||||
#include "nsISSLSocketControl.h"
|
||||
#include "sslt.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISSLStatus.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
@ -391,6 +399,7 @@ nsHttpChannel::Connect()
|
||||
return NS_ERROR_UNKNOWN_HOST;
|
||||
|
||||
// Consider opening a TCP connection right away
|
||||
RetrieveSSLOptions();
|
||||
SpeculativeConnect();
|
||||
|
||||
// Don't allow resuming when cache must be used
|
||||
@ -527,8 +536,9 @@ nsHttpChannel::SpeculativeConnect()
|
||||
|
||||
mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
|
||||
mConnectionInfo->SetPrivate(mPrivateBrowsing);
|
||||
gHttpHandler->SpeculativeConnect(mConnectionInfo,
|
||||
callbacks);
|
||||
gHttpHandler->SpeculativeConnect(
|
||||
mConnectionInfo, callbacks,
|
||||
mCaps & (NS_HTTP_ALLOW_RSA_FALSESTART | NS_HTTP_ALLOW_RC4_FALSESTART | NS_HTTP_DISALLOW_SPDY));
|
||||
}
|
||||
|
||||
void
|
||||
@ -693,6 +703,37 @@ nsHttpChannel::SetupTransactionLoadGroupInfo()
|
||||
mTransaction->SetLoadGroupConnectionInfo(ci);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::RetrieveSSLOptions()
|
||||
{
|
||||
if (!IsHTTPS() || mPrivateBrowsing)
|
||||
return;
|
||||
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
if (!principal)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
if (!permMgr)
|
||||
return;
|
||||
|
||||
uint32_t perm;
|
||||
nsresult rv = permMgr->TestPermissionFromPrincipal(principal,
|
||||
"falsestart-rsa", &perm);
|
||||
if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) {
|
||||
LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] "
|
||||
"falsestart-rsa permission found\n", this));
|
||||
mCaps |= NS_HTTP_ALLOW_RSA_FALSESTART;
|
||||
}
|
||||
rv = permMgr->TestPermissionFromPrincipal(principal, "falsestart-rc4", &perm);
|
||||
if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) {
|
||||
LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] "
|
||||
"falsestart-rc4 permission found\n", this));
|
||||
mCaps |= NS_HTTP_ALLOW_RC4_FALSESTART;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::SetupTransaction()
|
||||
{
|
||||
@ -1160,6 +1201,89 @@ nsHttpChannel::ProcessSTSHeader()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsHttpChannel::IsHTTPS()
|
||||
{
|
||||
bool isHttps;
|
||||
if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::ProcessSSLInformation()
|
||||
{
|
||||
// If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
|
||||
// can be whitelisted for TLS False Start in future sessions. We could
|
||||
// do the same for DH but its rarity doesn't justify the lookup.
|
||||
// Also do the same for RC4 symmetric ciphers.
|
||||
|
||||
if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo ||
|
||||
!IsHTTPS() || mPrivateBrowsing)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(mSecurityInfo);
|
||||
nsCOMPtr<nsISSLStatusProvider> statusProvider =
|
||||
do_QueryInterface(mSecurityInfo);
|
||||
if (!ssl || !statusProvider)
|
||||
return;
|
||||
nsCOMPtr<nsISSLStatus> sslstat;
|
||||
statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
|
||||
if (!sslstat)
|
||||
return;
|
||||
|
||||
// If certificate exceptions are being used don't record this information
|
||||
// in the permission manager.
|
||||
bool trustCheck;
|
||||
if (NS_FAILED(sslstat->GetIsDomainMismatch(&trustCheck)) || trustCheck)
|
||||
return;
|
||||
if (NS_FAILED(sslstat->GetIsNotValidAtThisTime(&trustCheck)) || trustCheck)
|
||||
return;
|
||||
if (NS_FAILED(sslstat->GetIsUntrusted(&trustCheck)) || trustCheck)
|
||||
return;
|
||||
|
||||
int16_t kea = ssl->GetKEAUsed();
|
||||
int16_t symcipher = ssl->GetSymmetricCipherUsed();
|
||||
|
||||
nsIPrincipal *principal = GetPrincipal();
|
||||
if (!principal)
|
||||
return;
|
||||
|
||||
// set a permission manager flag that future transactions can
|
||||
// use via RetrieveSSLOptions(()
|
||||
|
||||
nsCOMPtr<nsIPermissionManager> permMgr =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
|
||||
if (!permMgr)
|
||||
return;
|
||||
|
||||
// Allow this to stand for a week
|
||||
int64_t expireTime = (PR_Now() / PR_USEC_PER_MSEC) +
|
||||
(86400 * 7 * PR_MSEC_PER_SEC);
|
||||
|
||||
if (kea == ssl_kea_rsa) {
|
||||
permMgr->AddFromPrincipal(principal, "falsestart-rsa",
|
||||
nsIPermissionManager::ALLOW_ACTION,
|
||||
nsIPermissionManager::EXPIRE_TIME,
|
||||
expireTime);
|
||||
LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
|
||||
"falsestart-rsa permission granted for this host\n", this));
|
||||
} else {
|
||||
permMgr->RemoveFromPrincipal(principal, "falsestart-rsa");
|
||||
}
|
||||
|
||||
if (symcipher == ssl_calg_rc4) {
|
||||
permMgr->AddFromPrincipal(principal, "falsestart-rc4",
|
||||
nsIPermissionManager::ALLOW_ACTION,
|
||||
nsIPermissionManager::EXPIRE_TIME,
|
||||
expireTime);
|
||||
LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
|
||||
"falsestart-rc4 permission granted for this host\n", this));
|
||||
} else {
|
||||
permMgr->RemoveFromPrincipal(principal, "falsestart-rc4");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::ProcessResponse()
|
||||
{
|
||||
@ -1192,6 +1316,8 @@ nsHttpChannel::ProcessResponse()
|
||||
|
||||
MOZ_ASSERT(!mCachedContentIsValid);
|
||||
|
||||
ProcessSSLInformation();
|
||||
|
||||
// notify "http-on-examine-response" observers
|
||||
gHttpHandler->OnExamineResponse(this);
|
||||
|
||||
@ -6037,6 +6163,30 @@ nsHttpChannel::ShouldSkipCache()
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIPrincipal *
|
||||
nsHttpChannel::GetPrincipal()
|
||||
{
|
||||
if (mPrincipal)
|
||||
return mPrincipal;
|
||||
|
||||
nsIScriptSecurityManager *securityManager =
|
||||
nsContentUtils::GetSecurityManager();
|
||||
|
||||
if (!securityManager)
|
||||
return nullptr;
|
||||
|
||||
securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal));
|
||||
if (!mPrincipal)
|
||||
return nullptr;
|
||||
|
||||
// principals with unknown app ids do not work with the permission manager
|
||||
if (mPrincipal->GetUnknownAppId())
|
||||
mPrincipal = nullptr;
|
||||
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
|
||||
{
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
class nsAHttpConnection;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
@ -174,6 +175,9 @@ private:
|
||||
nsresult ContinueProcessFallback(nsresult);
|
||||
void HandleAsyncAbort();
|
||||
nsresult EnsureAssocReq();
|
||||
void ProcessSSLInformation();
|
||||
bool IsHTTPS();
|
||||
void RetrieveSSLOptions();
|
||||
|
||||
nsresult ContinueOnStartRequest1(nsresult);
|
||||
nsresult ContinueOnStartRequest2(nsresult);
|
||||
@ -373,6 +377,10 @@ protected:
|
||||
|
||||
private: // cache telemetry
|
||||
bool mDidReval;
|
||||
|
||||
private:
|
||||
nsIPrincipal *GetPrincipal();
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsISSLSocketControl.h"
|
||||
#include "sslt.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "netCore.h"
|
||||
#include "nsNetCID.h"
|
||||
@ -64,11 +65,12 @@ nsHttpConnection::nsHttpConnection()
|
||||
, mRemainingConnectionUses(0xffffffff)
|
||||
, mClassification(nsAHttpTransaction::CLASS_GENERAL)
|
||||
, mNPNComplete(false)
|
||||
, mSetupNPNCalled(false)
|
||||
, mSetupSSLCalled(false)
|
||||
, mUsingSpdyVersion(0)
|
||||
, mPriority(nsISupportsPriority::PRIORITY_NORMAL)
|
||||
, mReportedSpdy(false)
|
||||
, mEverUsedSpdy(false)
|
||||
, mTransactionCaps(0)
|
||||
{
|
||||
LOG(("Creating nsHttpConnection @%x\n", this));
|
||||
|
||||
@ -236,8 +238,6 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion)
|
||||
bool
|
||||
nsHttpConnection::EnsureNPNComplete()
|
||||
{
|
||||
// NPN is only used by SPDY right now.
|
||||
//
|
||||
// If for some reason the components to check on NPN aren't available,
|
||||
// this function will just return true to continue on and disable SPDY
|
||||
|
||||
@ -308,6 +308,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
|
||||
LOG(("nsHttpConnection::Activate [this=%p trans=%x caps=%x]\n",
|
||||
this, trans, caps));
|
||||
|
||||
mTransactionCaps = caps;
|
||||
mPriority = pri;
|
||||
if (mTransaction && mUsingSpdyVersion)
|
||||
return AddTransaction(trans, pri);
|
||||
@ -323,7 +324,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri
|
||||
trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
||||
SetSecurityCallbacks(callbacks);
|
||||
|
||||
SetupNPN(caps); // only for spdy
|
||||
SetupSSL(caps);
|
||||
|
||||
// take ownership of the transaction
|
||||
mTransaction = trans;
|
||||
@ -360,55 +361,70 @@ failed_activation:
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::SetupNPN(uint32_t caps)
|
||||
nsHttpConnection::SetupSSL(uint32_t caps)
|
||||
{
|
||||
if (mSetupNPNCalled) /* do only once */
|
||||
LOG(("nsHttpConnection::SetupSSL %p caps=0x%X\n", this, caps));
|
||||
|
||||
if (mSetupSSLCalled) // do only once
|
||||
return;
|
||||
mSetupNPNCalled = true;
|
||||
mSetupSSLCalled = true;
|
||||
|
||||
// Setup NPN Negotiation if necessary (only for SPDY)
|
||||
if (!mNPNComplete) {
|
||||
if (mNPNComplete)
|
||||
return;
|
||||
|
||||
mNPNComplete = true;
|
||||
// we flip this back to false if SetNPNList succeeds at the end
|
||||
// of this function
|
||||
mNPNComplete = true;
|
||||
|
||||
if (mConnInfo->UsingSSL()) {
|
||||
LOG(("nsHttpConnection::SetupNPN Setting up "
|
||||
"Next Protocol Negotiation"));
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
nsresult rv =
|
||||
mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
if (!mConnInfo->UsingSSL())
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsISSLSocketControl> ssl =
|
||||
do_QueryInterface(securityInfo, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
LOG(("nsHttpConnection::SetupSSL Setting up "
|
||||
"Next Protocol Negotiation"));
|
||||
nsCOMPtr<nsISupports> securityInfo;
|
||||
nsresult rv =
|
||||
mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
nsTArray<nsCString> protocolArray;
|
||||
nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
// The first protocol is used as the fallback if none of the
|
||||
// protocols supported overlap with the server's list.
|
||||
// In the case of overlap, matching priority is driven by
|
||||
// the order of the server's advertisement.
|
||||
protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
|
||||
if (caps & NS_HTTP_ALLOW_RSA_FALSESTART) {
|
||||
LOG(("nsHttpConnection::SetupSSL %p "
|
||||
">= RSA Key Exchange Expected\n", this));
|
||||
ssl->SetKEAExpected(ssl_kea_rsa);
|
||||
}
|
||||
|
||||
if (gHttpHandler->IsSpdyEnabled() &&
|
||||
!(caps & NS_HTTP_DISALLOW_SPDY)) {
|
||||
LOG(("nsHttpConnection::SetupNPN Allow SPDY NPN selection"));
|
||||
if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
|
||||
protocolArray.AppendElement(
|
||||
gHttpHandler->SpdyInfo()->VersionString[0]);
|
||||
if (gHttpHandler->SpdyInfo()->ProtocolEnabled(1))
|
||||
protocolArray.AppendElement(
|
||||
gHttpHandler->SpdyInfo()->VersionString[1]);
|
||||
}
|
||||
if (caps & NS_HTTP_ALLOW_RC4_FALSESTART) {
|
||||
LOG(("nsHttpConnection::SetupSSL %p "
|
||||
">= RC4 Key Exchange Expected\n", this));
|
||||
ssl->SetSymmetricCipherExpected(ssl_calg_rc4);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
|
||||
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
|
||||
mNPNComplete = false;
|
||||
}
|
||||
}
|
||||
nsTArray<nsCString> protocolArray;
|
||||
|
||||
// The first protocol is used as the fallback if none of the
|
||||
// protocols supported overlap with the server's list.
|
||||
// In the case of overlap, matching priority is driven by
|
||||
// the order of the server's advertisement.
|
||||
protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
|
||||
|
||||
if (gHttpHandler->IsSpdyEnabled() &&
|
||||
!(caps & NS_HTTP_DISALLOW_SPDY)) {
|
||||
LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
|
||||
if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
|
||||
protocolArray.AppendElement(
|
||||
gHttpHandler->SpdyInfo()->VersionString[0]);
|
||||
if (gHttpHandler->SpdyInfo()->ProtocolEnabled(1))
|
||||
protocolArray.AppendElement(
|
||||
gHttpHandler->SpdyInfo()->VersionString[1]);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
|
||||
LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
|
||||
mNPNComplete = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -596,7 +612,7 @@ nsHttpConnection::IsAlive()
|
||||
|
||||
// SocketTransport::IsAlive can run the SSL state machine, so make sure
|
||||
// the NPN options are set before that happens.
|
||||
SetupNPN(0);
|
||||
SetupSSL(mTransactionCaps);
|
||||
|
||||
bool alive;
|
||||
nsresult rv = mSocketTransport->IsAlive(&alive);
|
||||
|
@ -158,6 +158,7 @@ public:
|
||||
void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks);
|
||||
void PrintDiagnostics(nsCString &log);
|
||||
|
||||
void SetTransactionCaps(uint32_t aCaps) { mTransactionCaps = aCaps; }
|
||||
private:
|
||||
// called to cause the underlying socket to start speaking SSL
|
||||
nsresult ProxyStartSSL();
|
||||
@ -175,7 +176,7 @@ private:
|
||||
// Makes certain the SSL handshake is complete and NPN negotiation
|
||||
// has had a chance to happen
|
||||
bool EnsureNPNComplete();
|
||||
void SetupNPN(uint32_t caps);
|
||||
void SetupSSL(uint32_t caps);
|
||||
|
||||
// Start the Spdy transaction handler when NPN indicates spdy/*
|
||||
void StartSpdy(uint8_t versionLevel);
|
||||
@ -240,7 +241,7 @@ private:
|
||||
|
||||
// SPDY related
|
||||
bool mNPNComplete;
|
||||
bool mSetupNPNCalled;
|
||||
bool mSetupSSLCalled;
|
||||
|
||||
// version level in use, 0 if unused
|
||||
uint8_t mUsingSpdyVersion;
|
||||
@ -251,6 +252,9 @@ private:
|
||||
|
||||
// mUsingSpdyVersion is cleared when mSpdySession is freed, this is permanent
|
||||
bool mEverUsedSpdy;
|
||||
|
||||
// The capabailities associated with the most recent transaction
|
||||
uint32_t mTransactionCaps;
|
||||
};
|
||||
|
||||
#endif // nsHttpConnection_h__
|
||||
|
@ -326,7 +326,8 @@ nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
|
||||
nsIInterfaceRequestor *callbacks)
|
||||
nsIInterfaceRequestor *callbacks,
|
||||
uint32_t caps)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
|
||||
ci->HashKey().get()));
|
||||
@ -336,7 +337,7 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
|
||||
nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
|
||||
NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
|
||||
|
||||
uint32_t caps = ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
|
||||
caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
|
||||
nsRefPtr<NullHttpTransaction> trans =
|
||||
new NullHttpTransaction(ci, wrappedCallbacks, caps);
|
||||
|
||||
@ -2814,6 +2815,10 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
"Created new nshttpconnection %p\n", conn.get()));
|
||||
|
||||
// Some capabilities are needed before a transaciton actually gets
|
||||
// scheduled (e.g. how to negotiate false start)
|
||||
conn->SetTransactionCaps(mTransaction->Caps());
|
||||
|
||||
NetAddr peeraddr;
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
||||
|
@ -113,7 +113,8 @@ public:
|
||||
// connection manager, nor is the submitter obligated to actually submit a
|
||||
// real transaction for this connectionInfo.
|
||||
nsresult SpeculativeConnect(nsHttpConnectionInfo *,
|
||||
nsIInterfaceRequestor *);
|
||||
nsIInterfaceRequestor *,
|
||||
uint32_t caps = 0);
|
||||
|
||||
// called when a connection is done processing a transaction. if the
|
||||
// connection can be reused then it will be added to the idle list, else
|
||||
|
@ -181,9 +181,10 @@ public:
|
||||
}
|
||||
|
||||
nsresult SpeculativeConnect(nsHttpConnectionInfo *ci,
|
||||
nsIInterfaceRequestor *callbacks)
|
||||
nsIInterfaceRequestor *callbacks,
|
||||
uint32_t caps = 0)
|
||||
{
|
||||
return mConnMgr->SpeculativeConnect(ci, callbacks);
|
||||
return mConnMgr->SpeculativeConnect(ci, callbacks, caps);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -14,7 +14,7 @@ class nsCString;
|
||||
%}
|
||||
[ref] native nsCStringTArrayRef(nsTArray<nsCString>);
|
||||
|
||||
[scriptable, uuid(bb2bb490-3ba4-4254-b8f5-8b43c7b714ea)]
|
||||
[scriptable, builtinclass, uuid(c5eb9af4-238c-4fc6-bdec-d5ab5e7dce68)]
|
||||
interface nsISSLSocketControl : nsISupports {
|
||||
attribute nsIInterfaceRequestor notificationCallbacks;
|
||||
|
||||
@ -52,6 +52,22 @@ interface nsISSLSocketControl : nsISupports {
|
||||
in ACString hostname,
|
||||
in long port);
|
||||
|
||||
/* The Key Exchange Algorithm and Symmetric Cipher
|
||||
is used when determining whether or not to do false start.
|
||||
After a handshake is complete it can be read from *Used,
|
||||
before a handshake is started it may be set through *Expected.
|
||||
The values correspond to the SSLKEAType and SSLCipherAlgorithm
|
||||
enums in NSS or the *_UNKNOWN constant defined below.
|
||||
*/
|
||||
|
||||
[infallible] readonly attribute short KEAUsed;
|
||||
[infallible] attribute short KEAExpected;
|
||||
[infallible] readonly attribute short SymmetricCipherUsed;
|
||||
[infallible] attribute short SymmetricCipherExpected;
|
||||
|
||||
const short KEY_EXCHANGE_UNKNOWN = -1;
|
||||
const short SYMMETRIC_CIPHER_UNKNOWN = -1;
|
||||
|
||||
/*
|
||||
* The original flags from the socket provider.
|
||||
*/
|
||||
|
@ -824,6 +824,186 @@ PK11PasswordPrompt(PK11SlotInfo* slot, PRBool retry, void* arg)
|
||||
return runnable->mResult;
|
||||
}
|
||||
|
||||
// call with shutdown prevention lock held
|
||||
static void
|
||||
PreliminaryHandshakeDone(PRFileDesc* fd)
|
||||
{
|
||||
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
|
||||
if (!infoObject)
|
||||
return;
|
||||
|
||||
if (infoObject->IsPreliminaryHandshakeDone())
|
||||
return;
|
||||
|
||||
infoObject->SetPreliminaryHandshakeDone();
|
||||
infoObject->SetFirstServerHelloReceived();
|
||||
|
||||
// Get the NPN value.
|
||||
SSLNextProtoState state;
|
||||
unsigned char npnbuf[256];
|
||||
unsigned int npnlen;
|
||||
|
||||
if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) {
|
||||
if (state == SSL_NEXT_PROTO_NEGOTIATED) {
|
||||
infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
|
||||
}
|
||||
else {
|
||||
infoObject->SetNegotiatedNPN(nullptr, 0);
|
||||
}
|
||||
mozilla::Telemetry::Accumulate(Telemetry::SSL_NPN_TYPE, state);
|
||||
}
|
||||
else {
|
||||
infoObject->SetNegotiatedNPN(nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SECStatus
|
||||
CanFalseStartCallback(PRFileDesc* fd, void* client_data, PRBool *canFalseStart)
|
||||
{
|
||||
*canFalseStart = false;
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
|
||||
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
|
||||
if (!infoObject) {
|
||||
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (infoObject->isAlreadyShutDown()) {
|
||||
MOZ_CRASH("SSL socket used after NSS shut down");
|
||||
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
PreliminaryHandshakeDone(fd);
|
||||
|
||||
SSLChannelInfo channelInfo;
|
||||
if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) != SECSuccess) {
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
SSLCipherSuiteInfo cipherInfo;
|
||||
if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
|
||||
sizeof (cipherInfo)) != SECSuccess) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
" KEA %d\n", fd,
|
||||
static_cast<int32_t>(cipherInfo.keaType)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
if (channelInfo.protocolVersion < SSL_LIBRARY_VERSION_TLS_1_0) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"SSL Version must be >= TLS1 %x\n", fd,
|
||||
static_cast<int32_t>(channelInfo.protocolVersion)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
// never do false start without one of these key exchange algorithms
|
||||
if (cipherInfo.keaType != ssl_kea_rsa &&
|
||||
cipherInfo.keaType != ssl_kea_dh &&
|
||||
cipherInfo.keaType != ssl_kea_ecdh) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"unsupported KEA %d\n", fd,
|
||||
static_cast<int32_t>(cipherInfo.keaType)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
// never do false start without at least 80 bits of key material. This should
|
||||
// be redundant to an NSS precondition
|
||||
if (cipherInfo.effectiveKeyBits < 80) {
|
||||
MOZ_CRASH("NSS is not enforcing the precondition that the effective "
|
||||
"key size must be >= 80 bits for false start");
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"key too small %d\n", fd,
|
||||
static_cast<int32_t>(cipherInfo.effectiveKeyBits)));
|
||||
PR_SetError(PR_INVALID_STATE_ERROR, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
// XXX: An attacker can choose which protocols are advertised in the
|
||||
// NPN extension. TODO(Bug 861311): We should restrict the ability
|
||||
// of an attacker leverage this capability by restricting false start
|
||||
// to the same protocol we previously saw for the server, after the
|
||||
// first successful connection to the server.
|
||||
|
||||
// Enforce NPN to do false start if policy requires it. Do this as an
|
||||
// indicator if server compatibility.
|
||||
nsSSLIOLayerHelpers& helpers = infoObject->SharedState().IOLayerHelpers();
|
||||
if (helpers.mFalseStartRequireNPN) {
|
||||
nsAutoCString negotiatedNPN;
|
||||
if (NS_FAILED(infoObject->GetNegotiatedNPN(negotiatedNPN)) ||
|
||||
!negotiatedNPN.Length()) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"NPN cannot be verified\n", fd));
|
||||
return SECSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not using eliptical curve kea then make sure we've seen the
|
||||
// same kea from this host in the past, to limit the potential for downgrade
|
||||
// attacks.
|
||||
if (cipherInfo.keaType != ssl_kea_ecdh) {
|
||||
|
||||
if (helpers.mFalseStartRequireForwardSecrecy) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"KEA used is %d, but "
|
||||
"require-forward-secrecy configured.\n",
|
||||
fd, static_cast<int32_t>(cipherInfo.keaType)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
int16_t expected = infoObject->GetKEAExpected();
|
||||
if (cipherInfo.keaType != expected) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"KEA used is %d, expected %d\n", fd,
|
||||
static_cast<int32_t>(cipherInfo.keaType),
|
||||
static_cast<int32_t>(expected)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
// whitelist the expected key exchange algorithms that are
|
||||
// acceptable for false start when seen before.
|
||||
if (expected != ssl_kea_rsa && expected != ssl_kea_dh &&
|
||||
expected != ssl_kea_ecdh) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"KEA used, %d, "
|
||||
"is not supported with False Start.\n",
|
||||
fd, static_cast<int32_t>(expected)));
|
||||
return SECSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not using AES then verify that this is the historically expected
|
||||
// symmetrical cipher for this host, to limit potential for downgrade attacks.
|
||||
if (cipherInfo.symCipher != ssl_calg_aes) {
|
||||
int16_t expected = infoObject->GetSymmetricCipherExpected();
|
||||
if (cipherInfo.symCipher != expected) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"Symmetric cipher used is %d, expected %d\n",
|
||||
fd, static_cast<int32_t>(cipherInfo.symCipher),
|
||||
static_cast<int32_t>(expected)));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
// whitelist the expected ciphers that are
|
||||
// acceptable for false start when seen before.
|
||||
if ((expected != ssl_calg_rc4) && (expected != ssl_calg_3des) &&
|
||||
(expected != ssl_calg_aes)) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] failed - "
|
||||
"Symmetric cipher used, %d, "
|
||||
"is not supported with False Start.\n",
|
||||
fd, static_cast<int32_t>(expected)));
|
||||
return SECSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
infoObject->NoteTimeUntilReady();
|
||||
*canFalseStart = true;
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CanFalseStartCallback [%p] ok\n", fd));
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
int32_t sslStatus;
|
||||
@ -834,11 +1014,14 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
|
||||
|
||||
// certificate validation sets FirstServerHelloReceived, so if that flag
|
||||
// is absent at handshake time we have a resumed session.
|
||||
// is absent at handshake time we have a resumed session. Check this before
|
||||
// PreliminaryHandshakeDone() because that function also sets that flag.
|
||||
bool isResumedSession = !(infoObject->GetFirstServerHelloReceived());
|
||||
|
||||
// This is the first callback on resumption handshakes
|
||||
infoObject->SetFirstServerHelloReceived();
|
||||
// Do the bookkeeping that needs to be done after the
|
||||
// server's ServerHello...ServerHelloDone have been processed, but that doesn't
|
||||
// need the handshake to be completed.
|
||||
PreliminaryHandshakeDone(fd);
|
||||
|
||||
// If the handshake completed, then we know the site is TLS tolerant (if this
|
||||
// was a TLS connection).
|
||||
@ -930,21 +1113,6 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
status->mSecretKeyLength = encryptBits;
|
||||
status->mCipherName.Assign(cipherName);
|
||||
|
||||
// Get the NPN value.
|
||||
SSLNextProtoState state;
|
||||
unsigned char npnbuf[256];
|
||||
unsigned int npnlen;
|
||||
|
||||
if (SSL_GetNextProto(fd, &state, npnbuf, &npnlen, 256) == SECSuccess) {
|
||||
if (state == SSL_NEXT_PROTO_NEGOTIATED)
|
||||
infoObject->SetNegotiatedNPN(reinterpret_cast<char *>(npnbuf), npnlen);
|
||||
else
|
||||
infoObject->SetNegotiatedNPN(nullptr, 0);
|
||||
mozilla::Telemetry::Accumulate(Telemetry::SSL_NPN_TYPE, state);
|
||||
}
|
||||
else
|
||||
infoObject->SetNegotiatedNPN(nullptr, 0);
|
||||
|
||||
SSLChannelInfo channelInfo;
|
||||
if (SSL_GetChannelInfo(fd, &channelInfo, sizeof(channelInfo)) == SECSuccess) {
|
||||
// Get the protocol version for telemetry
|
||||
@ -954,13 +1122,16 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
|
||||
SSLCipherSuiteInfo cipherInfo;
|
||||
if (SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
|
||||
sizeof (cipherInfo)) == SECSuccess) {
|
||||
sizeof (cipherInfo)) == SECSuccess) {
|
||||
// keyExchange null=0, rsa=1, dh=2, fortezza=3, ecdh=4
|
||||
Telemetry::Accumulate(Telemetry::SSL_KEY_EXCHANGE_ALGORITHM,
|
||||
cipherInfo.keaType);
|
||||
infoObject->SetKEAUsed(cipherInfo.keaType);
|
||||
infoObject->SetSymmetricCipherUsed(cipherInfo.symCipher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
infoObject->NoteTimeUntilReady();
|
||||
infoObject->SetHandshakeCompleted(isResumedSession);
|
||||
|
||||
PORT_Free(cipherName);
|
||||
|
@ -23,6 +23,8 @@ char*
|
||||
PK11PasswordPrompt(PK11SlotInfo *slot, PRBool retry, void* arg);
|
||||
|
||||
void HandshakeCallback(PRFileDesc *fd, void *client_data);
|
||||
SECStatus CanFalseStartCallback(PRFileDesc* fd, void* client_data,
|
||||
PRBool *canFalseStart);
|
||||
|
||||
SECStatus RegisterMyOCSPAIAInfoCallback();
|
||||
SECStatus UnregisterMyOCSPAIAInfoCallback();
|
||||
|
@ -83,12 +83,18 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags)
|
||||
mHandshakeInProgress(false),
|
||||
mAllowTLSIntoleranceTimeout(true),
|
||||
mRememberClientAuthCertificate(false),
|
||||
mPreliminaryHandshakeDone(false),
|
||||
mHandshakeStartTime(0),
|
||||
mFirstServerHelloReceived(false),
|
||||
mNPNCompleted(false),
|
||||
mHandshakeCompleted(false),
|
||||
mJoined(false),
|
||||
mSentClientCert(false),
|
||||
mNotedTimeUntilReady(false),
|
||||
mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
|
||||
mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
|
||||
mSymmetricCipherUsed(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
|
||||
mSymmetricCipherExpected(nsISSLSocketControl::SYMMETRIC_CIPHER_UNKNOWN),
|
||||
mProviderFlags(providerFlags),
|
||||
mSocketCreationTimestamp(TimeStamp::Now()),
|
||||
mPlaintextBytesRead(0)
|
||||
@ -106,6 +112,48 @@ nsNSSSocketInfo::GetProviderFlags(uint32_t* aProviderFlags)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetKEAUsed(int16_t *aKea)
|
||||
{
|
||||
*aKea = mKEAUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetKEAExpected(int16_t *aKea)
|
||||
{
|
||||
*aKea = mKEAExpected;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::SetKEAExpected(int16_t aKea)
|
||||
{
|
||||
mKEAExpected = aKea;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetSymmetricCipherUsed(int16_t *aSymmetricCipher)
|
||||
{
|
||||
*aSymmetricCipher = mSymmetricCipherUsed;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::GetSymmetricCipherExpected(int16_t *aSymmetricCipher)
|
||||
{
|
||||
*aSymmetricCipher = mSymmetricCipherExpected;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSSocketInfo::SetSymmetricCipherExpected(int16_t aSymmetricCipher)
|
||||
{
|
||||
mSymmetricCipherExpected = aSymmetricCipher;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNSSSocketInfo::GetHandshakePending(bool *aHandshakePending)
|
||||
{
|
||||
@ -197,12 +245,27 @@ getSecureBrowserUI(nsIInterfaceRequestor * callbacks,
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
nsNSSSocketInfo::NoteTimeUntilReady()
|
||||
{
|
||||
if (mNotedTimeUntilReady)
|
||||
return;
|
||||
|
||||
mNotedTimeUntilReady = true;
|
||||
|
||||
// This will include TCP and proxy tunnel wait time
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
|
||||
mSocketCreationTimestamp, TimeStamp::Now());
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("[%p] nsNSSSocketInfo::NoteTimeUntilReady\n", mFd));
|
||||
}
|
||||
|
||||
void
|
||||
nsNSSSocketInfo::SetHandshakeCompleted(bool aResumedSession)
|
||||
{
|
||||
if (!mHandshakeCompleted) {
|
||||
// This will include TCP and proxy tunnel wait time
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_READY,
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::SSL_TIME_UNTIL_HANDSHAKE_FINISHED,
|
||||
mSocketCreationTimestamp, TimeStamp::Now());
|
||||
|
||||
// If the handshake is completed for the first time from just 1 callback
|
||||
@ -220,6 +283,9 @@ nsNSSSocketInfo::SetHandshakeCompleted(bool aResumedSession)
|
||||
}
|
||||
|
||||
mHandshakeCompleted = true;
|
||||
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("[%p] nsNSSSocketInfo::SetHandshakeCompleted\n", (void*)mFd));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1095,6 +1161,8 @@ nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
|
||||
, mRenegoUnrestrictedSites(nullptr)
|
||||
, mTreatUnsafeNegotiationAsBroken(false)
|
||||
, mWarnLevelMissingRFC5746(1)
|
||||
, mFalseStartRequireNPN(true)
|
||||
, mFalseStartRequireForwardSecrecy(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1285,6 +1353,12 @@ PrefObserver::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
int32_t warnLevel = 1;
|
||||
Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
|
||||
mOwner->setWarnLevelMissingRFC5746(warnLevel);
|
||||
} else if (prefName.Equals("security.ssl.false_start.require-npn")) {
|
||||
Preferences::GetBool("security.ssl.false_start.require-npn",
|
||||
&mOwner->mFalseStartRequireNPN);
|
||||
} else if (prefName.Equals("security.ssl.false_start.require-forward-secrecy")) {
|
||||
Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
|
||||
&mOwner->mFalseStartRequireForwardSecrecy);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
@ -1312,6 +1386,8 @@ nsSSLIOLayerHelpers::~nsSSLIOLayerHelpers()
|
||||
Preferences::RemoveObserver(mPrefObserver, "security.ssl.renego_unrestricted_hosts");
|
||||
Preferences::RemoveObserver(mPrefObserver, "security.ssl.treat_unsafe_negotiation_as_broken");
|
||||
Preferences::RemoveObserver(mPrefObserver, "security.ssl.warn_missing_rfc5746");
|
||||
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-npn");
|
||||
Preferences::RemoveObserver(mPrefObserver, "security.ssl.false_start.require-forward-secrecy");
|
||||
}
|
||||
|
||||
nsresult nsSSLIOLayerHelpers::Init()
|
||||
@ -1386,6 +1462,11 @@ nsresult nsSSLIOLayerHelpers::Init()
|
||||
Preferences::GetInt("security.ssl.warn_missing_rfc5746", &warnLevel);
|
||||
setWarnLevelMissingRFC5746(warnLevel);
|
||||
|
||||
Preferences::GetBool("security.ssl.false_start.require-npn",
|
||||
&mFalseStartRequireNPN);
|
||||
Preferences::GetBool("security.ssl.false_start.require-forward-secrecy",
|
||||
&mFalseStartRequireForwardSecrecy);
|
||||
|
||||
mPrefObserver = new PrefObserver(this);
|
||||
Preferences::AddStrongObserver(mPrefObserver,
|
||||
"security.ssl.renego_unrestricted_hosts");
|
||||
@ -1393,7 +1474,10 @@ nsresult nsSSLIOLayerHelpers::Init()
|
||||
"security.ssl.treat_unsafe_negotiation_as_broken");
|
||||
Preferences::AddStrongObserver(mPrefObserver,
|
||||
"security.ssl.warn_missing_rfc5746");
|
||||
|
||||
Preferences::AddStrongObserver(mPrefObserver,
|
||||
"security.ssl.false_start.require-npn");
|
||||
Preferences::AddStrongObserver(mPrefObserver,
|
||||
"security.ssl.false_start.require-forward-secrecy");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2479,6 +2563,7 @@ nsSSLIOLayerImportFD(PRFileDesc *fd,
|
||||
}
|
||||
SSL_SetPKCS11PinArg(sslSock, (nsIInterfaceRequestor*)infoObject);
|
||||
SSL_HandshakeCallback(sslSock, HandshakeCallback, infoObject);
|
||||
SSL_SetCanFalseStartCallback(sslSock, CanFalseStartCallback, infoObject);
|
||||
|
||||
// Disable this hook if we connect anonymously. See bug 466080.
|
||||
uint32_t flags = 0;
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
|
||||
void SetNegotiatedNPN(const char *value, uint32_t length);
|
||||
void SetHandshakeCompleted(bool aResumedSession);
|
||||
void NoteTimeUntilReady();
|
||||
|
||||
bool GetJoined() { return mJoined; }
|
||||
void SetSentClientCert() { mSentClientCert = true; }
|
||||
@ -93,6 +94,30 @@ public:
|
||||
void SetTLSEnabled(bool enabled) { mTLSEnabled = enabled; }
|
||||
|
||||
void AddPlaintextBytesRead(uint64_t val) { mPlaintextBytesRead += val; }
|
||||
|
||||
bool IsPreliminaryHandshakeDone() const { return mPreliminaryHandshakeDone; }
|
||||
void SetPreliminaryHandshakeDone() { mPreliminaryHandshakeDone = true; }
|
||||
|
||||
void SetKEAUsed(PRUint16 kea) { mKEAUsed = kea; }
|
||||
inline int16_t GetKEAExpected() // infallible in nsISSLSocketControl
|
||||
{
|
||||
int16_t result;
|
||||
mozilla::DebugOnly<nsresult> rv = GetKEAExpected(&result);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return result;
|
||||
}
|
||||
void SetSymmetricCipherUsed(PRUint16 symmetricCipher)
|
||||
{
|
||||
mSymmetricCipherUsed = symmetricCipher;
|
||||
}
|
||||
inline int16_t GetSymmetricCipherExpected() // infallible in nsISSLSocketControl
|
||||
{
|
||||
int16_t result;
|
||||
mozilla::DebugOnly<nsresult> rv = GetSymmetricCipherExpected(&result);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
PRFileDesc* mFd;
|
||||
|
||||
@ -107,6 +132,7 @@ private:
|
||||
bool mHandshakeInProgress;
|
||||
bool mAllowTLSIntoleranceTimeout;
|
||||
bool mRememberClientAuthCertificate;
|
||||
bool mPreliminaryHandshakeDone; // after false start items are complete
|
||||
PRIntervalTime mHandshakeStartTime;
|
||||
bool mFirstServerHelloReceived;
|
||||
|
||||
@ -117,6 +143,14 @@ private:
|
||||
bool mHandshakeCompleted;
|
||||
bool mJoined;
|
||||
bool mSentClientCert;
|
||||
bool mNotedTimeUntilReady;
|
||||
|
||||
// mKEA* and mSymmetricCipher* are used in false start detetermination
|
||||
// values are from nsISSLSocketControl
|
||||
PRInt16 mKEAUsed;
|
||||
PRInt16 mKEAExpected;
|
||||
PRInt16 mSymmetricCipherUsed;
|
||||
PRInt16 mSymmetricCipherExpected;
|
||||
|
||||
uint32_t mProviderFlags;
|
||||
mozilla::TimeStamp mSocketCreationTimestamp;
|
||||
@ -164,6 +198,9 @@ public:
|
||||
bool isRenegoUnrestrictedSite(const nsCString &str);
|
||||
|
||||
void clearStoredData();
|
||||
|
||||
bool mFalseStartRequireNPN;
|
||||
bool mFalseStartRequireForwardSecrecy;
|
||||
private:
|
||||
nsCOMPtr<nsIObserver> mPrefObserver;
|
||||
};
|
||||
|
@ -10,3 +10,4 @@
|
||||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
@ -163,3 +163,10 @@ SSL_SetStapledOCSPResponses;
|
||||
;+ local:
|
||||
;+*;
|
||||
;+};
|
||||
;+NSS_3.15.2 { # NSS 3.15.2 release
|
||||
;+ global:
|
||||
SSL_SetCanFalseStartCallback;
|
||||
SSL_DefaultCanFalseStart;
|
||||
;+ local:
|
||||
;+*;
|
||||
;+};
|
||||
|
@ -121,14 +121,22 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd);
|
||||
#define SSL_ENABLE_FALSE_START 22 /* Enable SSL false start (off by */
|
||||
/* default, applies only to */
|
||||
/* clients). False start is a */
|
||||
/* mode where an SSL client will start sending application data before */
|
||||
/* verifying the server's Finished message. This means that we could end up */
|
||||
/* sending data to an imposter. However, the data will be encrypted and */
|
||||
/* only the true server can derive the session key. Thus, so long as the */
|
||||
/* cipher isn't broken this is safe. Because of this, False Start will only */
|
||||
/* occur on RSA or DH ciphersuites where the cipher's key length is >= 80 */
|
||||
/* bits. The advantage of False Start is that it saves a round trip for */
|
||||
/* client-speaks-first protocols when performing a full handshake. */
|
||||
/* mode where an SSL client will start sending application data before
|
||||
* verifying the server's Finished message. This means that we could end up
|
||||
* sending data to an imposter. However, the data will be encrypted and
|
||||
* only the true server can derive the session key. Thus, so long as the
|
||||
* cipher isn't broken this is safe. The advantage of false start is that
|
||||
* it saves a round trip for client-speaks-first protocols when performing a
|
||||
* full handshake.
|
||||
*
|
||||
* See SSL_DefaultCanFalseStart for the default criteria that NSS uses to
|
||||
* determine whether to false start or not. See SSL_SetCanFalseStartCallback
|
||||
* for how to change that criteria. In addition to those criteria, false start
|
||||
* will only be done when the server selects a cipher suite with an effective
|
||||
* key length of 80 bits or more (including RC4-128). Also, see
|
||||
* SSL_HandshakeCallback for a description on how false start affects when the
|
||||
* handshake callback gets called.
|
||||
*/
|
||||
|
||||
/* For SSL 3.0 and TLS 1.0, by default we prevent chosen plaintext attacks
|
||||
* on SSL CBC mode cipher suites (see RFC 4346 Section F.3) by splitting
|
||||
@ -653,14 +661,59 @@ SSL_IMPORT SECStatus SSL_SetMaxServerCacheLocks(PRUint32 maxLocks);
|
||||
SSL_IMPORT SECStatus SSL_InheritMPServerSIDCache(const char * envString);
|
||||
|
||||
/*
|
||||
** Set the callback on a particular socket that gets called when we finish
|
||||
** performing a handshake.
|
||||
** Set the callback that normally gets called when the TLS handshake
|
||||
** is complete. If false start is not enabled, then the handshake callback is
|
||||
** called after verifying the peer's Finished message and before sending
|
||||
** outgoing application data and before processing incoming application data.
|
||||
**
|
||||
** If false start is enabled and there is a custom CanFalseStartCallback
|
||||
** callback set, then the handshake callback gets called after the peer's
|
||||
** Finished message has been verified, which may be after application data is
|
||||
** sent.
|
||||
**
|
||||
** If false start is enabled and there is not a custom CanFalseStartCallback
|
||||
** callback established with SSL_SetCanFalseStartCallback then the handshake
|
||||
** callback gets called before any application data is sent, which may be
|
||||
** before the peer's Finished message has been verified.
|
||||
*/
|
||||
typedef void (PR_CALLBACK *SSLHandshakeCallback)(PRFileDesc *fd,
|
||||
void *client_data);
|
||||
SSL_IMPORT SECStatus SSL_HandshakeCallback(PRFileDesc *fd,
|
||||
SSLHandshakeCallback cb, void *client_data);
|
||||
|
||||
/* Applications that wish to customize TLS false start should set this callback
|
||||
** function. NSS will invoke the functon to determine if a particular
|
||||
** connection should use false start or not. SECSuccess indicates that the
|
||||
** callback completed successfully, and if so *canFalseStart indicates if false
|
||||
** start can be used. If the callback does not return SECSuccess then the
|
||||
** handshake will be canceled.
|
||||
**
|
||||
** Applications that do not set the callback will use an internal set of
|
||||
** criteria to determine if the connection should false start. If
|
||||
** the callback is set false start will never be used without invoking the
|
||||
** callback function, but some connections (e.g. resumed connections) will
|
||||
** never use false start and therefore will not invoke the callback.
|
||||
**
|
||||
** NSS's internal criteria for this connection can be evaluated by calling
|
||||
** SSL_DefaultCanFalseStart() from the custom callback.
|
||||
**
|
||||
** See the description of SSL_HandshakeCallback for important information on
|
||||
** how registering a custom false start callback affects when the handshake
|
||||
** callback gets called.
|
||||
**/
|
||||
typedef SECStatus (PR_CALLBACK *SSLCanFalseStartCallback)(
|
||||
PRFileDesc *fd, void *arg, PRBool *canFalseStart);
|
||||
|
||||
SSL_IMPORT SECStatus SSL_SetCanFalseStartCallback(
|
||||
PRFileDesc *fd, SSLCanFalseStartCallback callback, void *arg);
|
||||
|
||||
/* A utility function that can be called from a custom CanFalseStartCallback
|
||||
** function to determine what NSS would have done for this connection if the
|
||||
** custom callback was not implemented.
|
||||
**/
|
||||
SSL_IMPORT SECStatus SSL_DefaultCanFalseStart(PRFileDesc *fd,
|
||||
PRBool *canFalseStart);
|
||||
|
||||
/*
|
||||
** For the server, request a new handshake. For the client, begin a new
|
||||
** handshake. If flushCache is non-zero, the SSL3 cache entry will be
|
||||
|
@ -6669,35 +6669,51 @@ done:
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
ssl3_CanFalseStart(sslSocket *ss) {
|
||||
PRBool rv;
|
||||
static SECStatus
|
||||
ssl3_CheckFalseStart(sslSocket *ss)
|
||||
{
|
||||
SECStatus rv;
|
||||
PRBool maybeFalseStart = PR_TRUE;
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
|
||||
PORT_Assert( !ss->ssl3.hs.authCertificatePending );
|
||||
|
||||
/* XXX: does not take into account whether we are waiting for
|
||||
* SSL_AuthCertificateComplete or SSL_RestartHandshakeAfterCertReq. If/when
|
||||
* that is done, this function could return different results each time it
|
||||
* would be called.
|
||||
*/
|
||||
/* An attacker can control the selected ciphersuite so we only wish to
|
||||
* do False Start in the case that the selected ciphersuite is
|
||||
* sufficiently strong that the attack can gain no advantage.
|
||||
* Therefore we always require an 80-bit cipher. */
|
||||
|
||||
ssl_GetSpecReadLock(ss);
|
||||
rv = ss->opt.enableFalseStart &&
|
||||
!ss->sec.isServer &&
|
||||
!ss->ssl3.hs.isResuming &&
|
||||
ss->ssl3.cwSpec &&
|
||||
|
||||
/* An attacker can control the selected ciphersuite so we only wish to
|
||||
* do False Start in the case that the selected ciphersuite is
|
||||
* sufficiently strong that the attack can gain no advantage.
|
||||
* Therefore we require an 80-bit cipher and a forward-secret key
|
||||
* exchange. */
|
||||
ss->ssl3.cwSpec->cipher_def->secret_key_size >= 10 &&
|
||||
(ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa);
|
||||
if (ss->ssl3.cwSpec->cipher_def->secret_key_size < 10) {
|
||||
ss->ssl3.hs.canFalseStart = PR_FALSE;
|
||||
maybeFalseStart = PR_FALSE;
|
||||
}
|
||||
ssl_ReleaseSpecReadLock(ss);
|
||||
if (!maybeFalseStart) {
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
if (!ss->canFalseStartCallback) {
|
||||
rv = SSL_DefaultCanFalseStart(ss->fd, &ss->ssl3.hs.canFalseStart);
|
||||
|
||||
if (rv == SECSuccess &&
|
||||
ss->ssl3.hs.canFalseStart && ss->handshakeCallback) {
|
||||
/* Call the handshake callback here for backwards compatibility
|
||||
* with applications that were using false start before
|
||||
* canFalseStartCallback was added.
|
||||
*/
|
||||
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
|
||||
}
|
||||
} else {
|
||||
rv = (ss->canFalseStartCallback)(ss->fd,
|
||||
ss->canFalseStartCallbackData,
|
||||
&ss->ssl3.hs.canFalseStart);
|
||||
}
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
ss->ssl3.hs.canFalseStart = PR_FALSE;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -6727,6 +6743,7 @@ ssl3_HandleServerHelloDone(sslSocket *ss)
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ss->enoughFirstHsDone = PR_TRUE;
|
||||
rv = ssl3_SendClientSecondRound(ss);
|
||||
|
||||
return rv;
|
||||
@ -6825,6 +6842,23 @@ ssl3_SendClientSecondRound(sslSocket *ss)
|
||||
if (rv != SECSuccess) {
|
||||
goto loser; /* err code was set. */
|
||||
}
|
||||
|
||||
if (ss->opt.enableFalseStart) {
|
||||
if (!ss->ssl3.hs.authCertificatePending) {
|
||||
rv = ssl3_CheckFalseStart(ss);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
}
|
||||
} else {
|
||||
/* The certificate authentication and the server's Finished
|
||||
* message are going to race each other. If the certificate
|
||||
* authentication wins, then we will try to false start. If the
|
||||
* server's Finished message wins, then ssl3_HandleFinished will
|
||||
* reset restartTarget to ssl3_FinishHandshake.
|
||||
*/
|
||||
ss->ssl3.hs.restartTarget = ssl3_CheckFalseStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = ssl3_SendFinished(ss, 0);
|
||||
@ -6839,11 +6873,6 @@ ssl3_SendClientSecondRound(sslSocket *ss)
|
||||
else
|
||||
ss->ssl3.hs.ws = wait_change_cipher;
|
||||
|
||||
/* Do the handshake callback for sslv3 here, if we can false start. */
|
||||
if (ss->handshakeCallback != NULL && ssl3_CanFalseStart(ss)) {
|
||||
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
|
||||
}
|
||||
|
||||
return SECSuccess;
|
||||
|
||||
loser:
|
||||
@ -9416,13 +9445,6 @@ ssl3_AuthCertificate(sslSocket *ss)
|
||||
|
||||
ss->ssl3.hs.authCertificatePending = PR_TRUE;
|
||||
rv = SECSuccess;
|
||||
|
||||
/* XXX: Async cert validation and False Start don't work together
|
||||
* safely yet; if we leave False Start enabled, we may end up false
|
||||
* starting (sending application data) before we
|
||||
* SSL_AuthCertificateComplete has been called.
|
||||
*/
|
||||
ss->opt.enableFalseStart = PR_FALSE;
|
||||
}
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
@ -10070,6 +10092,11 @@ xmit_loser:
|
||||
ss->ssl3.hs.cacheSID = rv == SECSuccess;
|
||||
}
|
||||
|
||||
/* Cancel false start check since we already completed the handshake */
|
||||
if (ss->ssl3.hs.restartTarget == ssl3_CheckFalseStart) {
|
||||
ss->ssl3.hs.restartTarget = NULL;
|
||||
}
|
||||
|
||||
if (ss->ssl3.hs.authCertificatePending) {
|
||||
if (ss->ssl3.hs.restartTarget) {
|
||||
PR_NOT_REACHED("ssl3_HandleFinished: unexpected restartTarget");
|
||||
@ -10088,6 +10115,8 @@ xmit_loser:
|
||||
SECStatus
|
||||
ssl3_FinishHandshake(sslSocket * ss)
|
||||
{
|
||||
PRBool falseStarted;
|
||||
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) );
|
||||
PORT_Assert( ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss) );
|
||||
PORT_Assert( ss->ssl3.hs.restartTarget == NULL );
|
||||
@ -10095,6 +10124,7 @@ ssl3_FinishHandshake(sslSocket * ss)
|
||||
/* The first handshake is now completed. */
|
||||
ss->handshake = NULL;
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->enoughFirstHsDone = PR_TRUE;
|
||||
|
||||
if (ss->ssl3.hs.cacheSID) {
|
||||
(*ss->sec.cache)(ss->sec.ci.sid);
|
||||
@ -10102,9 +10132,14 @@ ssl3_FinishHandshake(sslSocket * ss)
|
||||
}
|
||||
|
||||
ss->ssl3.hs.ws = idle_handshake;
|
||||
falseStarted = ss->ssl3.hs.canFalseStart;
|
||||
ss->ssl3.hs.canFalseStart = PR_FALSE; /* False Start phase is complete */
|
||||
|
||||
/* Do the handshake callback for sslv3 here, if we cannot false start. */
|
||||
if (ss->handshakeCallback != NULL && !ssl3_CanFalseStart(ss)) {
|
||||
/* Call the handshake callback for sslv3 here, unless we called it already
|
||||
* for the case where false start was done without a canFalseStartCallback.
|
||||
*/
|
||||
if (ss->handshakeCallback != NULL &&
|
||||
!(falseStarted && !ss->canFalseStartCallback)) {
|
||||
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
|
||||
}
|
||||
|
||||
|
@ -374,9 +374,7 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags)
|
||||
*/
|
||||
if (ss->opt.enableFalseStart) {
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher ||
|
||||
ss->ssl3.hs.ws == wait_new_session_ticket) &&
|
||||
ssl3_CanFalseStart(ss);
|
||||
canFalseStart = ss->ssl3.hs.canFalseStart;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
}
|
||||
} while (ss->ssl3.hs.ws != idle_handshake &&
|
||||
|
@ -60,7 +60,6 @@ SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1,
|
||||
sslSocket *ss;
|
||||
const char *cipherName;
|
||||
PRBool isDes = PR_FALSE;
|
||||
PRBool enoughFirstHsDone = PR_FALSE;
|
||||
|
||||
ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
@ -78,14 +77,7 @@ SSL_SecurityStatus(PRFileDesc *fd, int *op, char **cp, int *kp0, int *kp1,
|
||||
*op = SSL_SECURITY_STATUS_OFF;
|
||||
}
|
||||
|
||||
if (ss->firstHsDone) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
} else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
|
||||
ssl3_CanFalseStart(ss)) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
}
|
||||
|
||||
if (ss->opt.useSecurity && enoughFirstHsDone) {
|
||||
if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
|
||||
if (ss->version < SSL_LIBRARY_VERSION_3_0) {
|
||||
cipherName = ssl_cipherName[ss->sec.cipherType];
|
||||
} else {
|
||||
|
@ -868,6 +868,8 @@ const ssl3CipherSuiteDef *suite_def;
|
||||
PRUint32 rtTimeoutMs; /* The length of the current timeout
|
||||
* used for backoff (in ms) */
|
||||
PRUint32 rtRetries; /* The retry counter */
|
||||
PRBool canFalseStart; /* Can/did we False Start */
|
||||
|
||||
} SSL3HandshakeState;
|
||||
|
||||
|
||||
@ -1116,6 +1118,10 @@ struct sslSocketStr {
|
||||
unsigned long clientAuthRequested;
|
||||
unsigned long delayDisabled; /* Nagle delay disabled */
|
||||
unsigned long firstHsDone; /* first handshake is complete. */
|
||||
unsigned long enoughFirstHsDone; /* enough of the handshake is done
|
||||
* for callbacks to be able to
|
||||
* retrieve channel security
|
||||
* parameters from callback functions. */
|
||||
unsigned long handshakeBegun;
|
||||
unsigned long lastWriteBlocked;
|
||||
unsigned long recvdCloseNotify; /* received SSL EOF. */
|
||||
@ -1156,6 +1162,8 @@ const unsigned char * preferredCipher;
|
||||
void *badCertArg;
|
||||
SSLHandshakeCallback handshakeCallback;
|
||||
void *handshakeCallbackData;
|
||||
SSLCanFalseStartCallback canFalseStartCallback;
|
||||
void *canFalseStartCallbackData;
|
||||
void *pkcs11PinArg;
|
||||
SSLNextProtoCallback nextProtoCallback;
|
||||
void *nextProtoArg;
|
||||
@ -1358,7 +1366,6 @@ extern void ssl3_SetAlwaysBlock(sslSocket *ss);
|
||||
|
||||
extern SECStatus ssl_EnableNagleDelay(sslSocket *ss, PRBool enabled);
|
||||
|
||||
extern PRBool ssl3_CanFalseStart(sslSocket *ss);
|
||||
extern SECStatus
|
||||
ssl3_CompressMACEncryptRecord(ssl3CipherSpec * cwSpec,
|
||||
PRBool isServer,
|
||||
|
@ -26,7 +26,6 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
|
||||
sslSocket * ss;
|
||||
SSLChannelInfo inf;
|
||||
sslSessionID * sid;
|
||||
PRBool enoughFirstHsDone = PR_FALSE;
|
||||
|
||||
if (!info || len < sizeof inf.length) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
@ -43,14 +42,7 @@ SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len)
|
||||
memset(&inf, 0, sizeof inf);
|
||||
inf.length = PR_MIN(sizeof inf, len);
|
||||
|
||||
if (ss->firstHsDone) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
} else if (ss->version >= SSL_LIBRARY_VERSION_3_0 &&
|
||||
ssl3_CanFalseStart(ss)) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
}
|
||||
|
||||
if (ss->opt.useSecurity && enoughFirstHsDone) {
|
||||
if (ss->opt.useSecurity && ss->enoughFirstHsDone) {
|
||||
sid = ss->sec.ci.sid;
|
||||
inf.protocolVersion = ss->version;
|
||||
inf.authKeyBits = ss->sec.authKeyBits;
|
||||
|
@ -77,7 +77,6 @@ SSL_HandshakeNegotiatedExtension(PRFileDesc * socket,
|
||||
{
|
||||
/* some decisions derived from SSL_GetChannelInfo */
|
||||
sslSocket * sslsocket = NULL;
|
||||
PRBool enoughFirstHsDone = PR_FALSE;
|
||||
|
||||
if (!pYes) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
@ -93,14 +92,8 @@ SSL_HandshakeNegotiatedExtension(PRFileDesc * socket,
|
||||
|
||||
*pYes = PR_FALSE;
|
||||
|
||||
if (sslsocket->firstHsDone) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
} else if (sslsocket->ssl3.initialized && ssl3_CanFalseStart(sslsocket)) {
|
||||
enoughFirstHsDone = PR_TRUE;
|
||||
}
|
||||
|
||||
/* according to public API SSL_GetChannelInfo, this doesn't need a lock */
|
||||
if (sslsocket->opt.useSecurity && enoughFirstHsDone) {
|
||||
if (sslsocket->opt.useSecurity && sslsocket->enoughFirstHsDone) {
|
||||
if (sslsocket->ssl3.initialized) { /* SSL3 and TLS */
|
||||
/* now we know this socket went through ssl3_InitState() and
|
||||
* ss->xtnData got initialized, which is the only member accessed by
|
||||
|
@ -108,10 +108,12 @@ ssl_Do1stHandshake(sslSocket *ss)
|
||||
if ((ss->handshakeCallback != NULL) && /* has callback */
|
||||
(!ss->firstHsDone) && /* only first time */
|
||||
(ss->version < SSL_LIBRARY_VERSION_3_0)) { /* not ssl3 */
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->enoughFirstHsDone = PR_TRUE;
|
||||
(ss->handshakeCallback)(ss->fd, ss->handshakeCallbackData);
|
||||
}
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->firstHsDone = PR_TRUE;
|
||||
ss->enoughFirstHsDone = PR_TRUE;
|
||||
ss->gs.writeOffset = 0;
|
||||
ss->gs.readOffset = 0;
|
||||
break;
|
||||
@ -206,6 +208,7 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
|
||||
ss->firstHsDone = PR_FALSE;
|
||||
ss->enoughFirstHsDone = PR_FALSE;
|
||||
if ( asServer ) {
|
||||
ss->handshake = ssl2_BeginServerHandshake;
|
||||
ss->handshaking = sslHandshakingAsServer;
|
||||
@ -221,6 +224,8 @@ SSL_ResetHandshake(PRFileDesc *s, PRBool asServer)
|
||||
ssl_ReleaseRecvBufLock(ss);
|
||||
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
ss->ssl3.hs.canFalseStart = PR_FALSE;
|
||||
ss->ssl3.hs.restartTarget = NULL;
|
||||
|
||||
/*
|
||||
** Blow away old security state and get a fresh setup.
|
||||
@ -331,6 +336,74 @@ SSL_HandshakeCallback(PRFileDesc *fd, SSLHandshakeCallback cb,
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* Register an application callback to be called when false start may happen.
|
||||
** Acquires and releases HandshakeLock.
|
||||
*/
|
||||
SECStatus
|
||||
SSL_SetCanFalseStartCallback(PRFileDesc *fd, SSLCanFalseStartCallback cb,
|
||||
void *client_data)
|
||||
{
|
||||
sslSocket *ss;
|
||||
|
||||
ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetCanFalseStartCallback",
|
||||
SSL_GETPID(), fd));
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (!ss->opt.useSecurity) {
|
||||
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
|
||||
ss->canFalseStartCallback = cb;
|
||||
ss->canFalseStartCallbackData = client_data;
|
||||
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
ssl_Release1stHandshakeLock(ss);
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* A utility function that can be called from a custom CanFalseStartCallback
|
||||
** function to determine what NSS would have done for this connection if the
|
||||
** custom callback was not implemented.
|
||||
*/
|
||||
SECStatus
|
||||
SSL_DefaultCanFalseStart(PRFileDesc *fd, PRBool *canFalseStart)
|
||||
{
|
||||
sslSocket *ss;
|
||||
*canFalseStart = PR_FALSE;
|
||||
ss = ssl_FindSocket(fd);
|
||||
if (!ss) {
|
||||
SSL_DBG(("%d: SSL[%d]: bad socket in SSL_DefaultCanFalseStart",
|
||||
SSL_GETPID(), fd));
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (!ss->ssl3.initialized) {
|
||||
PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
if (ss->version <= SSL_LIBRARY_VERSION_3_0) {
|
||||
PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
/* Require a forward-secret key exchange. */
|
||||
*canFalseStart = ss->ssl3.hs.kea_def->kea == kea_dhe_dss ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_dhe_rsa ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_ecdsa ||
|
||||
ss->ssl3.hs.kea_def->kea == kea_ecdhe_rsa;
|
||||
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
/* Try to make progress on an SSL handshake by attempting to read the
|
||||
** next handshake from the peer, and sending any responses.
|
||||
** For non-blocking sockets, returns PR_ERROR_WOULD_BLOCK if it cannot
|
||||
@ -1195,12 +1268,7 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags)
|
||||
ssl_Get1stHandshakeLock(ss);
|
||||
if (ss->version >= SSL_LIBRARY_VERSION_3_0) {
|
||||
ssl_GetSSL3HandshakeLock(ss);
|
||||
if ((ss->ssl3.hs.ws == wait_change_cipher ||
|
||||
ss->ssl3.hs.ws == wait_finished ||
|
||||
ss->ssl3.hs.ws == wait_new_session_ticket) &&
|
||||
ssl3_CanFalseStart(ss)) {
|
||||
canFalseStart = PR_TRUE;
|
||||
}
|
||||
canFalseStart = ss->ssl3.hs.canFalseStart;
|
||||
ssl_ReleaseSSL3HandshakeLock(ss);
|
||||
}
|
||||
if (!canFalseStart &&
|
||||
|
@ -2341,9 +2341,13 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_flags, PRInt16 *p_out_flags)
|
||||
} else if (new_flags & PR_POLL_WRITE) {
|
||||
/* The caller is trying to write, but the handshake is
|
||||
** blocked waiting for data to read, and the first
|
||||
** handshake has been sent. so do NOT to poll on write.
|
||||
** handshake has been sent. So do NOT to poll on write
|
||||
** unless we did false start.
|
||||
*/
|
||||
new_flags ^= PR_POLL_WRITE; /* don't select on write. */
|
||||
if (!(ss->version >= SSL_LIBRARY_VERSION_3_0 &&
|
||||
ss->ssl3.hs.canFalseStart)) {
|
||||
new_flags ^= PR_POLL_WRITE; /* don't select on write. */
|
||||
}
|
||||
new_flags |= PR_POLL_READ; /* do select on read. */
|
||||
}
|
||||
}
|
||||
|
@ -1,2 +1,6 @@
|
||||
This directory contains patches that were added locally
|
||||
on top of the NSS release.
|
||||
|
||||
bug-658222-false-start.patch: First iteration of false start. The patch will be
|
||||
further modified to make it work for Chromium and
|
||||
other projects before being checked in.
|
||||
|
1577
security/patches/bug-658222-false-start.patch
Normal file
1577
security/patches/bug-658222-false-start.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -843,6 +843,12 @@
|
||||
"extended_statistics_ok": true,
|
||||
"description": "ms of SSL wait time including TCP and proxy tunneling"
|
||||
},
|
||||
"SSL_TIME_UNTIL_HANDSHAKE_FINISHED": {
|
||||
"kind": "exponential",
|
||||
"high": "60000",
|
||||
"n_buckets": 200,
|
||||
"description": "ms of SSL wait time for full handshake including TCP and proxy tunneling"
|
||||
},
|
||||
"SSL_BYTES_BEFORE_CERT_CALLBACK": {
|
||||
"kind": "exponential",
|
||||
"high": "32000",
|
||||
|
@ -25,8 +25,6 @@ DEFINES += \
|
||||
-DCXX_VERSION="$(CXX_VERSION)" \
|
||||
-DCXXFLAGS="$(CXXFLAGS)" \
|
||||
-DCPPFLAGS="$(CPPFLAGS)" \
|
||||
-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
|
||||
-DMOZ_BUILD_APP=$(MOZ_BUILD_APP) \
|
||||
$(NULL)
|
||||
|
||||
MOZ_SOURCE_STAMP ?= $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null)
|
||||
|
@ -6,6 +6,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/Troubleshoot.jsm");
|
||||
Components.utils.import("resource://gre/modules/ResetProfile.jsm");
|
||||
|
||||
window.addEventListener("load", function onload(event) {
|
||||
window.removeEventListener("load", onload, false);
|
||||
@ -516,31 +517,6 @@ function showUpdateHistory() {
|
||||
* Profile reset is only supported for the default profile if the appropriate migrator exists.
|
||||
*/
|
||||
function populateResetBox() {
|
||||
if (resetSupported())
|
||||
if (ResetProfile.resetSupported())
|
||||
$("reset-box").style.visibility = "visible";
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the application to reset the profile.
|
||||
*/
|
||||
function resetProfileAndRestart() {
|
||||
let branding = Services.strings.createBundle("chrome://branding/locale/brand.properties");
|
||||
let brandShortName = branding.GetStringFromName("brandShortName");
|
||||
|
||||
// Prompt the user to confirm.
|
||||
let retVals = {
|
||||
reset: false,
|
||||
};
|
||||
window.openDialog("chrome://global/content/resetProfile.xul", null,
|
||||
"chrome,modal,centerscreen,titlebar,dialog=yes", retVals);
|
||||
if (!retVals.reset)
|
||||
return;
|
||||
|
||||
// Set the reset profile environment variable.
|
||||
let env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
env.set("MOZ_RESET_PROFILE_RESTART", "1");
|
||||
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
<div id="reset-box" style="visibility: hidden">
|
||||
<h3>&resetProfile.title;</h3>
|
||||
<p>&resetProfile.description;</p>
|
||||
<button onclick="resetProfileAndRestart()">
|
||||
<button onclick="ResetProfile.openConfirmationDialog(window)">
|
||||
&resetProfile.button.label2;
|
||||
</button>
|
||||
</div>
|
||||
|
@ -43,7 +43,7 @@ toolkit.jar:
|
||||
+ content/global/mozilla.xhtml (mozilla.xhtml)
|
||||
content/global/nsDragAndDrop.js (nsDragAndDrop.js)
|
||||
content/global/resetProfile.css (resetProfile.css)
|
||||
* content/global/resetProfile.js (resetProfile.js)
|
||||
content/global/resetProfile.js (resetProfile.js)
|
||||
content/global/resetProfile.xul (resetProfile.xul)
|
||||
content/global/resetProfileProgress.xul (resetProfileProgress.xul)
|
||||
content/global/treeUtils.js (treeUtils.js)
|
||||
|
@ -2,16 +2,18 @@
|
||||
* 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/Services.jsm");
|
||||
"use strict";
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/ResetProfile.jsm");
|
||||
|
||||
// based on onImportItemsPageShow from migration.js
|
||||
function populateResetPane(aContainerID) {
|
||||
let resetProfileItems = document.getElementById(aContainerID);
|
||||
try {
|
||||
let dataTypes = getMigratedData();
|
||||
let dataTypes = ResetProfile.getMigratedData();
|
||||
for (let dataType of dataTypes) {
|
||||
let label = document.createElement("label");
|
||||
label.setAttribute("value", dataType);
|
||||
@ -26,59 +28,6 @@ function onResetProfileLoad() {
|
||||
populateResetPane("migratedItems");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if reset is supported for the currently running profile.
|
||||
*
|
||||
* @return boolean whether reset is supported.
|
||||
*/
|
||||
function resetSupported() {
|
||||
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
||||
getService(Ci.nsIToolkitProfileService);
|
||||
let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
|
||||
#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
|
||||
|
||||
// Reset is only supported for the default profile if the self-migrator used for reset exists.
|
||||
try {
|
||||
return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
|
||||
("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
|
||||
} catch (e) {
|
||||
// Catch exception when there is no selected profile.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getMigratedData() {
|
||||
Components.utils.import("resource:///modules/MigrationUtils.jsm");
|
||||
|
||||
#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
|
||||
#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
|
||||
|
||||
// From migration.properties
|
||||
const MIGRATED_TYPES = [
|
||||
128,// Windows/Tabs
|
||||
4, // History and Bookmarks
|
||||
16, // Passwords
|
||||
8, // Form History
|
||||
2, // Cookies
|
||||
];
|
||||
|
||||
// Loop over possible data to migrate to give the user a list of what will be preserved.
|
||||
let dataTypes = [];
|
||||
for (let itemID of MIGRATED_TYPES) {
|
||||
try {
|
||||
let typeName = MigrationUtils.getLocalizedString(itemID + "_" + MOZ_APP_NAME);
|
||||
dataTypes.push(typeName);
|
||||
} catch (x) {
|
||||
// Catch exceptions when the string for a data type doesn't exist.
|
||||
Components.utils.reportError(x);
|
||||
}
|
||||
}
|
||||
return dataTypes;
|
||||
}
|
||||
|
||||
function onResetProfileAccepted() {
|
||||
let retVals = window.arguments[0];
|
||||
retVals.reset = true;
|
||||
|
12
toolkit/locales/en-US/chrome/global/resetProfile.properties
Normal file
12
toolkit/locales/en-US/chrome/global/resetProfile.properties
Normal file
@ -0,0 +1,12 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# LOCALIZATION NOTE: These strings are used for profile reset.
|
||||
|
||||
# LOCALIZATION NOTE (resetUnusedProfile.message): %S is brandShortName.
|
||||
resetUnusedProfile.message=It looks like you haven't started %S in a while. Do you want to clean it up for a fresh, like-new experience? And by the way, welcome back!
|
||||
|
||||
# LOCALIZATION NOTE (resetProfile.resetButton.label): %S is brandShortName.
|
||||
resetProfile.resetButton.label=Reset %S…
|
||||
resetProfile.resetButton.accesskey=e
|
@ -58,6 +58,7 @@
|
||||
locale/@AB_CD@/global/printProgress.dtd (%chrome/global/printProgress.dtd)
|
||||
locale/@AB_CD@/global/regionNames.properties (%chrome/global/regionNames.properties)
|
||||
locale/@AB_CD@/global/resetProfile.dtd (%chrome/global/resetProfile.dtd)
|
||||
locale/@AB_CD@/global/resetProfile.properties (%chrome/global/resetProfile.properties)
|
||||
locale/@AB_CD@/global/dialog.properties (%chrome/global/dialog.properties)
|
||||
locale/@AB_CD@/global/tree.dtd (%chrome/global/tree.dtd)
|
||||
locale/@AB_CD@/global/textcontext.dtd (%chrome/global/textcontext.dtd)
|
||||
|
@ -13,8 +13,14 @@ ifdef MOZ_TOOLKIT_SEARCH
|
||||
DEFINES += -DMOZ_TOOLKIT_SEARCH
|
||||
endif
|
||||
|
||||
DEFINES += \
|
||||
-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
|
||||
-DMOZ_BUILD_APP=$(MOZ_BUILD_APP) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
CertUtils.jsm \
|
||||
ResetProfile.jsm \
|
||||
Services.jsm \
|
||||
Troubleshoot.jsm \
|
||||
UpdateChannel.jsm \
|
||||
|
84
toolkit/modules/ResetProfile.jsm
Normal file
84
toolkit/modules/ResetProfile.jsm
Normal file
@ -0,0 +1,84 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ResetProfile"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
#expand const MOZ_APP_NAME = "__MOZ_APP_NAME__";
|
||||
#expand const MOZ_BUILD_APP = "__MOZ_BUILD_APP__";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.ResetProfile = {
|
||||
/**
|
||||
* Check if reset is supported for the currently running profile.
|
||||
*
|
||||
* @return boolean whether reset is supported.
|
||||
*/
|
||||
resetSupported: function() {
|
||||
let profileService = Cc["@mozilla.org/toolkit/profile-service;1"].
|
||||
getService(Ci.nsIToolkitProfileService);
|
||||
let currentProfileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
|
||||
// Reset is only supported for the default profile if the self-migrator used for reset exists.
|
||||
try {
|
||||
return currentProfileDir.equals(profileService.selectedProfile.rootDir) &&
|
||||
("@mozilla.org/profile/migrator;1?app=" + MOZ_BUILD_APP + "&type=" + MOZ_APP_NAME in Cc);
|
||||
} catch (e) {
|
||||
// Catch exception when there is no selected profile.
|
||||
Cu.reportError(e);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
getMigratedData: function() {
|
||||
Cu.import("resource:///modules/MigrationUtils.jsm");
|
||||
|
||||
// From migration.properties
|
||||
const MIGRATED_TYPES = [
|
||||
128,// Windows/Tabs
|
||||
4, // History and Bookmarks
|
||||
16, // Passwords
|
||||
8, // Form History
|
||||
2, // Cookies
|
||||
];
|
||||
|
||||
// Loop over possible data to migrate to give the user a list of what will be preserved.
|
||||
let dataTypes = [];
|
||||
for (let itemID of MIGRATED_TYPES) {
|
||||
try {
|
||||
let typeName = MigrationUtils.getLocalizedString(itemID + "_" + MOZ_APP_NAME);
|
||||
dataTypes.push(typeName);
|
||||
} catch (x) {
|
||||
// Catch exceptions when the string for a data type doesn't exist.
|
||||
Cu.reportError(x);
|
||||
}
|
||||
}
|
||||
return dataTypes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Ask the user if they wish to restart the application to reset the profile.
|
||||
*/
|
||||
openConfirmationDialog: function(window) {
|
||||
// Prompt the user to confirm.
|
||||
let params = {
|
||||
reset: false,
|
||||
};
|
||||
window.openDialog("chrome://global/content/resetProfile.xul", null,
|
||||
"chrome,modal,centerscreen,titlebar,dialog=yes", params);
|
||||
if (!params.reset)
|
||||
return;
|
||||
|
||||
// Set the reset profile environment variable.
|
||||
let env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
env.set("MOZ_RESET_PROFILE_RESTART", "1");
|
||||
|
||||
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
|
||||
},
|
||||
};
|
@ -3,8 +3,10 @@
|
||||
* 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/. */
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
// This specialization is private to nsCOMArray.
|
||||
|
Loading…
Reference in New Issue
Block a user