Merge m-c to fx-team.

This commit is contained in:
Ryan VanderMeulen 2013-06-04 20:47:52 -04:00
commit afd4d2106b
33 changed files with 783 additions and 647 deletions

View File

@ -61,7 +61,6 @@ xul|scrollbarbutton[sbattr="scrollbar-bottom-top"] {
}
xul|thumb {
-moz-appearance: none !important;
background-color: rgba(0, 0, 0, 0.4) !important;
-moz-border-top-colors: none !important;
-moz-border-bottom-colors: none !important;
@ -76,30 +75,30 @@ xul|thumb {
-moz-binding: url("chrome://global/content/bindings/videocontrols.xml#touchControls");
}
html select:not([size]):not([multiple]) > xul|scrollbar,
html select[size="1"] > xul|scrollbar,
html select:not([size]):not([multiple]) xul|scrollbarbutton,
html select[size="1"] xul|scrollbarbutton {
select:not([size]):not([multiple]) > xul|scrollbar,
select[size="1"] > xul|scrollbar,
select:not([size]):not([multiple]) xul|scrollbarbutton,
select[size="1"] xul|scrollbarbutton {
display: block;
margin-left: 0;
min-width: 16px;
}
/* Override inverse OS themes */
html select,
html textarea,
html button,
html xul|button,
html * > input:not([type="image"]) {
select,
textarea,
button,
xul|button,
* > input:not([type="image"]) {
-moz-appearance: none !important; /* See bug 598421 for fixing the platform */
border-radius: 3px;
}
html select[size],
html select[multiple],
html select[size][multiple],
html textarea,
html * > input:not([type="image"]) {
select[size],
select[multiple],
select[size][multiple],
textarea,
* > input:not([type="image"]) {
border-style: solid;
border-color: #7d7d7d;
color: #414141;
@ -107,70 +106,70 @@ html * > input:not([type="image"]) {
}
/* Selects are handled by the form helper, see bug 685197 */
html select option, html select optgroup {
select option, select optgroup {
pointer-events: none;
}
html select:not([size]):not([multiple]),
html select[size="0"],
html select[size="1"],
html * > input[type="button"],
html * > input[type="submit"],
html * > input[type="reset"],
html button {
select:not([size]):not([multiple]),
select[size="0"],
select[size="1"],
* > input[type="button"],
* > input[type="submit"],
* > input[type="reset"],
button {
border-style: solid;
border-color: #7d7d7d;
color: #414141;
background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(215,215,215,0.5) 18px, rgba(115,115,115,0.5) 100%);
}
html input[type="checkbox"] {
input[type="checkbox"] {
background: white linear-gradient(rgba(115,115,115,0.5) 0, rgba(215,215,215,0.5) 2px, rgba(255,255,255,0.2) 6px);
}
html input[type="radio"] {
input[type="radio"] {
background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(195,195,195,0.5) 5px, rgba(115,115,115,0.5) 100%);
}
html select {
select {
border-width: 1px;
padding: 1px;
}
html select:not([size]):not([multiple]),
html select[size="0"],
html select[size="1"] {
select:not([size]):not([multiple]),
select[size="0"],
select[size="1"] {
padding: 0 1px 0 1px;
}
html * > input:not([type="image"]) {
* > input:not([type="image"]) {
border-width: 1px;
padding: 1px;
}
html textarea {
textarea {
resize: none;
border-width: 1px;
padding: 2px 1px 2px 1px;
}
html input[type="button"],
html input[type="submit"],
html input[type="reset"],
html button {
input[type="button"],
input[type="submit"],
input[type="reset"],
button {
border-width: 1px;
padding: 0 7px 0 7px;
}
html input[type="radio"],
html input[type="checkbox"] {
input[type="radio"],
input[type="checkbox"] {
max-width: 14px;
max-height: 14px;
border: 1px solid #a7a7a7 !important;
padding: 2px 1px 2px 1px;
}
html select > button {
select > button {
border-width: 0px !important;
margin: 0px !important;
padding: 0px !important;
@ -188,54 +187,54 @@ html select > button {
font-size: inherit;
}
html select[size]:focus,
html select[multiple]:focus,
html select[size][multiple]:focus,
html textarea:focus,
html input[type="file"]:focus > input[type="text"],
html * > input:not([type="image"]):focus {
select[size]:focus,
select[multiple]:focus,
select[size][multiple]:focus,
textarea:focus,
input[type="file"]:focus > input[type="text"],
* > input:not([type="image"]):focus {
outline: 0px !important;
border-style: solid;
border-color: rgb(94,128,153);
background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 3px, rgba(255,255,255,0.2) 16px);
}
html select:not([size]):not([multiple]):focus,
html select[size="0"]:focus,
html select[size="1"]:focus,
html input[type="button"]:focus,
html input[type="submit"]:focus,
html input[type="reset"]:focus,
html button:focus {
select:not([size]):not([multiple]):focus,
select[size="0"]:focus,
select[size="1"]:focus,
input[type="button"]:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
button:focus {
outline: 0px !important;
border-style: solid;
border-color: rgb(94,128,153);
background: white linear-gradient(rgba(255,255,255,0.2) 0, rgba(198,225,256,0.2) 18px, rgba(27,113,177,0.5) 100%);
}
html input[type="checkbox"]:focus,
html input[type="radio"]:focus {
input[type="checkbox"]:focus,
input[type="radio"]:focus {
border-color: #99c6e0 !important;
}
html input[type="checkbox"]:focus {
input[type="checkbox"]:focus {
background: white linear-gradient(rgba(27,113,177,0.5) 0, rgba(198,225,246,0.2) 2px, rgba(255,255,255,0.2) 6px);
}
html input[type="radio"]:focus {
input[type="radio"]:focus {
background: radial-gradient(at 6px 6px, rgba(255,255,255,0.2) 3px, rgba(198,225,246,0.2) 5px, rgba(27,113,177,0.5) 100%);
}
/* we need to be specific for selects because the above rules are specific too */
html textarea[disabled],
html select[size][disabled],
html select[multiple][disabled],
html select[size][multiple][disabled],
html select:not([size]):not([multiple])[disabled],
html select[size="0"][disabled],
html select[size="1"][disabled],
html button[disabled],
html * > input:not([type="image"])[disabled] {
textarea[disabled],
select[size][disabled],
select[multiple][disabled],
select[size][multiple][disabled],
select:not([size]):not([multiple])[disabled],
select[size="0"][disabled],
select[size="1"][disabled],
button[disabled],
* > input:not([type="image"])[disabled] {
color: rgba(0,0,0,0.3);
border-color: rgba(125,125,125,0.4);
border-style: solid;
@ -243,44 +242,44 @@ html * > input:not([type="image"])[disabled] {
background: transparent linear-gradient(rgba(185,185,185,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(255,255,255,0.4) 100%);
}
html select:not([size]):not([multiple])[disabled],
html select[size="0"][disabled],
html select[size="1"][disabled] {
select:not([size]):not([multiple])[disabled],
select[size="0"][disabled],
select[size="1"][disabled] {
background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
}
html input[type="button"][disabled],
html input[type="submit"][disabled],
html input[type="reset"][disabled],
html button[disabled="true"] {
input[type="button"][disabled],
input[type="submit"][disabled],
input[type="reset"][disabled],
button[disabled="true"] {
padding: 0 7px 0 7px;
background: transparent linear-gradient(rgba(255,255,255,0.4) 0, rgba(235,235,235,0.4) 3px, rgba(185,185,185,0.4) 100%);
}
html input[type="radio"][disabled],
html input[type="radio"][disabled]:active,
html input[type="radio"][disabled]:hover,
html input[type="radio"][disabled]:hover:active,
html input[type="checkbox"][disabled],
html input[type="checkbox"][disabled]:active,
html input[type="checkbox"][disabled]:hover,
html input[type="checkbox"][disabled]:hover:active {
input[type="radio"][disabled],
input[type="radio"][disabled]:active,
input[type="radio"][disabled]:hover,
input[type="radio"][disabled]:hover:active,
input[type="checkbox"][disabled],
input[type="checkbox"][disabled]:active,
input[type="checkbox"][disabled]:hover,
input[type="checkbox"][disabled]:hover:active {
border:1px solid rgba(125,125,125,0.4) !important;
}
html select[disabled] > button {
select[disabled] > button {
opacity: 0.6;
padding: 1px 7px 1px 7px;
}
html *:-moz-any-link:active,
html *[role=button]:active,
html button:active,
html input:active,
html option:active,
html select:active,
html label:active,
html textarea:active {
*:-moz-any-link:active,
*[role=button]:active,
button:active,
input:active,
option:active,
select:active,
label:active,
textarea:active {
background-color: rgba(141, 184, 216, 0.5);
}

View File

@ -539,11 +539,88 @@
extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation implements="nsIObserver">
<constructor><![CDATA[
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
if (document.getBindingParent(this).parentNode.parentNode.localName ==
"toolbarpaletteitem")
return;
this.initialize();
// Initialize fields
this._stringBundle = document.getBindingParent(this)._stringBundle;
this._prefBranch =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
this._suggestEnabled =
this._prefBranch.getBoolPref("browser.search.suggest.enabled");
if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
this.setAttribute("clickSelectsAll", true);
// Add items to context menu and attach controller to handle them
var textBox = document.getAnonymousElementByAttribute(this,
"anonid", "textbox-input-box");
var cxmenu = document.getAnonymousElementByAttribute(textBox,
"anonid", "input-box-contextmenu");
var pasteAndSearch;
cxmenu.addEventListener("popupshowing", function() {
if (!pasteAndSearch)
return;
var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
var enabled = controller.isCommandEnabled("cmd_paste");
if (enabled)
pasteAndSearch.removeAttribute("disabled");
else
pasteAndSearch.setAttribute("disabled", "true");
}, false);
var element, label, akey;
element = document.createElementNS(kXULNS, "menuseparator");
cxmenu.appendChild(element);
var insertLocation = cxmenu.firstChild;
while (insertLocation.nextSibling &&
insertLocation.getAttribute("cmd") != "cmd_paste")
insertLocation = insertLocation.nextSibling;
if (insertLocation) {
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_pasteAndSearch");
element.setAttribute("label", label);
element.setAttribute("anonid", "paste-and-search");
element.setAttribute("oncommand",
"BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();");
cxmenu.insertBefore(element, insertLocation.nextSibling);
pasteAndSearch = element;
}
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_clearHistory");
akey = this._stringBundle.getString("cmd_clearHistory_accesskey");
element.setAttribute("label", label);
element.setAttribute("accesskey", akey);
element.setAttribute("cmd", "cmd_clearhistory");
cxmenu.appendChild(element);
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_showSuggestions");
akey = this._stringBundle.getString("cmd_showSuggestions_accesskey");
element.setAttribute("anonid", "toggle-suggest-item");
element.setAttribute("label", label);
element.setAttribute("accesskey", akey);
element.setAttribute("cmd", "cmd_togglesuggest");
element.setAttribute("type", "checkbox");
element.setAttribute("checked", this._suggestEnabled);
element.setAttribute("autocheck", "false");
this._suggestMenuItem = element;
cxmenu.appendChild(element);
this.controllers.appendController(this.searchbarController);
// Add observer for suggest preference
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.addObserver("browser.search.suggest.enabled", this, false);
]]></constructor>
<destructor><![CDATA[
@ -563,88 +640,6 @@
<field name="_suggestMenuItem"/>
<field name="_suggestEnabled"/>
<method name="initialize">
<body><![CDATA[
const kXULNS =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
// Initialize fields
this._stringBundle = document.getBindingParent(this)._stringBundle;
this._prefBranch =
Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
this._suggestEnabled =
this._prefBranch.getBoolPref("browser.search.suggest.enabled");
if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
this.setAttribute("clickSelectsAll", true);
// Add items to context menu and attach controller to handle them
var textBox = document.getAnonymousElementByAttribute(this,
"anonid", "textbox-input-box");
var cxmenu = document.getAnonymousElementByAttribute(textBox,
"anonid", "input-box-contextmenu");
var pasteAndSearch;
cxmenu.addEventListener("popupshowing", function() {
if (!pasteAndSearch)
return;
var controller = document.commandDispatcher.getControllerForCommand("cmd_paste");
var enabled = controller.isCommandEnabled("cmd_paste");
if (enabled)
pasteAndSearch.removeAttribute("disabled");
else
pasteAndSearch.setAttribute("disabled", "true");
}, false);
var element, label, akey;
element = document.createElementNS(kXULNS, "menuseparator");
cxmenu.appendChild(element);
var insertLocation = cxmenu.firstChild;
while (insertLocation.nextSibling &&
insertLocation.getAttribute("cmd") != "cmd_paste")
insertLocation = insertLocation.nextSibling;
if (insertLocation) {
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_pasteAndSearch");
element.setAttribute("label", label);
element.setAttribute("anonid", "paste-and-search");
element.setAttribute("oncommand",
"BrowserSearch.searchBar.select(); goDoCommand('cmd_paste'); BrowserSearch.searchBar.handleSearchCommand();");
cxmenu.insertBefore(element, insertLocation.nextSibling);
pasteAndSearch = element;
}
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_clearHistory");
akey = this._stringBundle.getString("cmd_clearHistory_accesskey");
element.setAttribute("label", label);
element.setAttribute("accesskey", akey);
element.setAttribute("cmd", "cmd_clearhistory");
cxmenu.appendChild(element);
element = document.createElementNS(kXULNS, "menuitem");
label = this._stringBundle.getString("cmd_showSuggestions");
akey = this._stringBundle.getString("cmd_showSuggestions_accesskey");
element.setAttribute("anonid", "toggle-suggest-item");
element.setAttribute("label", label);
element.setAttribute("accesskey", akey);
element.setAttribute("cmd", "cmd_togglesuggest");
element.setAttribute("type", "checkbox");
element.setAttribute("checked", this._suggestEnabled);
element.setAttribute("autocheck", "false");
this._suggestMenuItem = element;
cxmenu.appendChild(element);
this.controllers.appendController(this.searchbarController);
// Add observer for suggest preference
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
prefs.addObserver("browser.search.suggest.enabled", this, false);
]]></body>
</method>
<!--
This overrides the searchParam property in autocomplete.xml. We're
hijacking this property as a vehicle for delivering the privacy

View File

@ -189,6 +189,12 @@ WebGLContext::WebGLContext()
mAlreadyGeneratedWarnings = 0;
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
if (mMaxWarnings < -1)
{
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
mMaxWarnings = 0;
}
mLastUseIndex = 0;

View File

@ -1134,10 +1134,15 @@ protected:
int mDrawCallsSinceLastFlush;
int mAlreadyGeneratedWarnings;
int mMaxWarnings;
bool mAlreadyWarnedAboutFakeVertexAttrib0;
bool ShouldGenerateWarnings() const {
return mAlreadyGeneratedWarnings < 32;
if (mMaxWarnings == -1) {
return true;
}
return mAlreadyGeneratedWarnings < mMaxWarnings;
}
uint64_t mLastUseIndex;

View File

@ -236,6 +236,9 @@ CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
, mDestroyed(false)
{
MOZ_COUNT_CTOR(CompositorOGL);
mTextures[0] = 0;
mTextures[1] = 0;
mTextures[2] = 0;
sBackend = LAYERS_OPENGL;
}
@ -279,9 +282,29 @@ CompositorOGL::AddPrograms(ShaderProgramType aType)
}
}
GLuint
CompositorOGL::GetTemporaryTexture(GLenum aTextureUnit)
{
if (!mTextures[aTextureUnit - LOCAL_GL_TEXTURE0]) {
gl()->MakeCurrent();
gl()->fGenTextures(1, &mTextures[aTextureUnit - LOCAL_GL_TEXTURE0]);
}
return mTextures[aTextureUnit - LOCAL_GL_TEXTURE0];
}
void
CompositorOGL::Destroy()
{
if (gl()) {
gl()->MakeCurrent();
gl()->fDeleteTextures(3, mTextures);
mTextures[0] = 0;
mTextures[1] = 0;
mTextures[2] = 0;
} else {
MOZ_ASSERT(!mTextures[0] && !mTextures[1] && !mTextures[2]);
}
if (!mDestroyed) {
mDestroyed = true;
CleanupResources();

View File

@ -134,6 +134,13 @@ public:
gl::RGBARectLayerProgramType : gl::RGBALayerProgramType;
}
/**
* The compositor provides with temporary textures for use with direct
* textruing like gralloc texture.
* Doing so lets us use gralloc the way it has been designed to be used
* (see https://wiki.mozilla.org/Platform/GFX/Gralloc)
*/
GLuint GetTemporaryTexture(GLenum aUnit);
private:
/**
* Context target, nullptr when drawing directly to our swap chain.
@ -319,6 +326,9 @@ private:
bool mDestroyed;
nsAutoPtr<FPSState> mFPS;
// Textures used for direct texturing of buffers like gralloc.
// The index of the texture in this array must correspond to the texture unit.
GLuint mTextures[3];
static bool sDrawFPS;
static bool sFrameCounter;
};

View File

@ -700,28 +700,29 @@ TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat)
}
}
GrallocTextureHostOGL::GrallocTextureHostOGL()
: mCompositor(nullptr)
, mTextureTarget(0)
, mEGLImage(0)
{
}
void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
{
CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
if (mGL && !glCompositor) {
if (mCompositor && !glCompositor) {
DeleteTextures();
}
mGL = glCompositor ? glCompositor->gl() : nullptr;
mCompositor = glCompositor;
}
void
GrallocTextureHostOGL::DeleteTextures()
{
if (mGLTexture || mEGLImage) {
mGL->MakeCurrent();
if (mGLTexture) {
mGL->fDeleteTextures(1, &mGLTexture);
mGLTexture = 0;
}
if (mEGLImage) {
mGL->DestroyEGLImage(mEGLImage);
mEGLImage = 0;
}
if (mEGLImage) {
gl()->MakeCurrent();
gl()->DestroyEGLImage(mEGLImage);
mEGLImage = 0;
}
}
@ -767,20 +768,43 @@ GrallocTextureHostOGL::SwapTexturesImpl(const SurfaceDescriptor& aImage,
RegisterTextureHostAtGrallocBufferActor(this, aImage);
}
gl::GLContext*
GrallocTextureHostOGL::gl() const
{
return mCompositor ? mCompositor->gl() : nullptr;
}
void GrallocTextureHostOGL::BindTexture(GLenum aTextureUnit)
{
MOZ_ASSERT(mGLTexture);
/*
* The job of this function is to ensure that the texture is tied to the
* android::GraphicBuffer, so that texturing will source the GraphicBuffer.
*
* To this effect we create an EGLImage wrapping this GraphicBuffer,
* using CreateEGLImageForNativeBuffer, and then we tie this EGLImage to our
* texture using fEGLImageTargetTexture2D.
*
* We try to avoid re-creating the EGLImage everytime, by keeping it around
* as the mEGLImage member of this class.
*/
MOZ_ASSERT(gl());
gl()->MakeCurrent();
mGL->MakeCurrent();
mGL->fActiveTexture(aTextureUnit);
mGL->fBindTexture(mTextureTarget, mGLTexture);
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
GLuint tex = mCompositor->GetTemporaryTexture(aTextureUnit);
gl()->fActiveTexture(aTextureUnit);
gl()->fBindTexture(mTextureTarget, tex);
if (!mEGLImage) {
mEGLImage = gl()->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
}
gl()->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage);
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
}
bool
GrallocTextureHostOGL::IsValid() const
{
return !!mGL && !!mGraphicBuffer.get();
return !!gl() && !!mGraphicBuffer.get();
}
GrallocTextureHostOGL::~GrallocTextureHostOGL()
@ -798,65 +822,14 @@ GrallocTextureHostOGL::~GrallocTextureHostOGL()
bool
GrallocTextureHostOGL::Lock()
{
if (!IsValid()) {
return false;
}
/*
* The job of this function is to ensure that the texture is tied to the
* android::GraphicBuffer, so that texturing will source the GraphicBuffer.
*
* To this effect we create an EGLImage wrapping this GraphicBuffer,
* using CreateEGLImageForNativeBuffer, and then we tie this EGLImage to our
* texture using fEGLImageTargetTexture2D.
*
* We try to avoid re-creating the EGLImage everytime, by keeping it around
* as the mEGLImage member of this class.
*/
MOZ_ASSERT(mGraphicBuffer.get());
mGL->MakeCurrent();
if (!mGLTexture) {
mGL->fGenTextures(1, &mGLTexture);
}
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
mGL->fBindTexture(mTextureTarget, mGLTexture);
if (!mEGLImage) {
mEGLImage = mGL->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
}
mGL->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage);
return true;
// Lock/Unlock is done internally when binding the gralloc buffer to a gl texture
return IsValid();
}
void
GrallocTextureHostOGL::Unlock()
{
/*
* The job of this function is to ensure that we release any read lock placed on
* our android::GraphicBuffer by any drawing code that sourced it via this TextureHost.
*
* Indeed, as soon as we draw with a texture that's tied to a android::GraphicBuffer,
* the GL may place read locks on it. We must ensure that we release them early enough,
* i.e. before the next time that we will try to acquire a write lock on the same buffer,
* because read and write locks on gralloc buffers are mutually exclusive.
*/
if (mGL->Renderer() == GLContext::RendererAdrenoTM205) {
/* XXX This is working around a driver bug exhibited on at least the
* Geeksphone Peak, where retargeting to a different EGL image is very
* slow. See Bug 869696.
*/
if (mGLTexture) {
mGL->MakeCurrent();
mGL->fDeleteTextures(1, &mGLTexture);
mGLTexture = 0;
}
return;
}
mGL->MakeCurrent();
mGL->fActiveTexture(LOCAL_GL_TEXTURE0);
mGL->fBindTexture(mTextureTarget, mGLTexture);
mGL->fEGLImageTargetTexture2D(mTextureTarget, mGL->GetNullEGLImage());
// Lock/Unlock is done internally when binding the gralloc buffer to a gl texture
}
gfx::SurfaceFormat
@ -877,7 +850,7 @@ GrallocTextureHostOGL::SetBuffer(SurfaceDescriptor* aBuffer, ISurfaceAllocator*
RegisterTextureHostAtGrallocBufferActor(this, *mBuffer);
}
#endif
#endif // MOZ_WIDGET_GONK
already_AddRefed<gfxImageSurface>
TextureImageTextureHostOGL::GetAsSurface() {
@ -932,10 +905,20 @@ TiledTextureHostOGL::GetAsSurface() {
#ifdef MOZ_WIDGET_GONK
already_AddRefed<gfxImageSurface>
GrallocTextureHostOGL::GetAsSurface() {
nsRefPtr<gfxImageSurface> surf = IsValid() && mGLTexture ?
mGL->GetTexImage(mGLTexture,
false,
GetShaderProgram())
gl()->MakeCurrent();
GLuint tex = mCompositor->GetTemporaryTexture(LOCAL_GL_TEXTURE0);
gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
gl()->fBindTexture(mTextureTarget, tex);
if (!mEGLImage) {
mEGLImage = gl()->CreateEGLImageForNativeBuffer(mGraphicBuffer->getNativeBuffer());
}
gl()->fEGLImageTargetTexture2D(mTextureTarget, mEGLImage);
nsRefPtr<gfxImageSurface> surf = IsValid() ?
gl()->GetTexImage(tex,
false,
GetShaderProgram())
: nullptr;
return surf.forget();
}

View File

@ -21,6 +21,7 @@ namespace mozilla {
namespace layers {
class TextureImageTextureHostOGL;
class CompositorOGL;
/*
* TextureHost implementations for the OpenGL backend.
@ -568,23 +569,12 @@ class GrallocTextureHostOGL
, public TextureSourceOGL
{
public:
GrallocTextureHostOGL()
: mGL(nullptr)
, mTextureTarget(0)
, mGLTexture(0)
, mEGLImage(0)
{
}
GrallocTextureHostOGL();
~GrallocTextureHostOGL();
virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
virtual GLuint GetTextureHandle()
{
return mGLTexture;
}
virtual void UpdateImpl(const SurfaceDescriptor& aImage,
nsIntRegion* aRegion = nullptr,
nsIntPoint* aOffset = nullptr) MOZ_OVERRIDE;
@ -652,12 +642,13 @@ public:
}
private:
gl::GLContext* gl() const;
void DeleteTextures();
RefPtr<gl::GLContext> mGL;
RefPtr<CompositorOGL> mCompositor;
android::sp<android::GraphicBuffer> mGraphicBuffer;
GLenum mTextureTarget;
GLuint mGLTexture;
EGLImage mEGLImage;
};
#endif

View File

@ -17,6 +17,8 @@ VPATH = \
$(srcdir)/cocoa \
$(NULL)
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = hal_s
@ -25,6 +27,10 @@ LIBXUL_LIBRARY = 1
EXPORT_LIBRARY = 1
FAIL_ON_WARNINGS = 1
MOCHITEST_BROWSER_FILES += \
tests/browser_alarms.js \
$(NULL)
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
CMMSRCS += \
CocoaSensor.mm \

View File

@ -4,24 +4,76 @@
#include "Hal.h"
#include <algorithm>
#include <nsITimer.h>
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace hal_impl {
static void
TimerCallbackFunc(nsITimer *aTimer, void *aClosure)
{
hal::NotifyAlarmFired();
}
static StaticRefPtr<nsITimer> sTimer;
bool
EnableAlarm()
{
return false;
static bool initialized = false;
if (!initialized) {
initialized = true;
ClearOnShutdown(&sTimer);
}
nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
sTimer = timer;
MOZ_ASSERT(sTimer);
return true;
}
void
DisableAlarm()
{
/*
* DisableAlarm() may be called after sTimer has been set to null by
* ClearOnShutdown().
*/
if (sTimer) {
sTimer->Cancel();
}
}
bool
SetAlarm(int32_t aSeconds, int32_t aNanoseconds)
{
return false;
if (!sTimer) {
HAL_LOG(("We should have enabled the alarm"));
MOZ_ASSERT(false);
return false;
}
// Do the math to convert aSeconds and aNanoseconds into milliseconds since
// the epoch.
int64_t milliseconds = static_cast<int64_t>(aSeconds) * 1000 +
static_cast<int64_t>(aNanoseconds) / 1000000;
// nsITimer expects relative milliseconds.
int64_t relMilliseconds = milliseconds - PR_Now() / 1000;
// If the alarm time is in the past relative to PR_Now(),
// we choose to immediately fire the alarm. Passing 0 means nsITimer will
// queue a timeout event immediately.
sTimer->InitWithFuncCallback(TimerCallbackFunc, nullptr,
clamped<int64_t>(relMilliseconds, 0, INT32_MAX),
nsITimer::TYPE_ONE_SHOT);
return true;
}
} // hal_impl

View File

@ -19,6 +19,7 @@ EXPORTS.mozilla += [
'HalWakeLock.h',
]
TEST_DIRS += ['tests']
CPP_SOURCES += [
'Hal.cpp',
'HalWakeLock.cpp',

18
hal/tests/Makefile.in Normal file
View File

@ -0,0 +1,18 @@
# 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/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_BROWSER_FILES = \
browser_alarms.js \
$(NULL)
include $(topsrcdir)/config/rules.mk

187
hal/tests/browser_alarms.js Normal file
View File

@ -0,0 +1,187 @@
XPCOMUtils.defineLazyModuleGetter(this, "AlarmService",
"resource://gre/modules/AlarmService.jsm");
/*
* Tests for Bug 867868 and related Alarm API bugs.
*
* NOTE: These tests pass the alarm time to AlarmService as a number and not as
* a date. See bug 810973 about Date truncating milliseconds. AlarmService does
* not officially allow a integer, but nor does it disallow it. Of course this
* test will break if AlarmService adds a type check, hence this note.
* FIXME: when bug 810973 is fixed.
*/
function add_alarm_future(cb) {
let alarmId = undefined;
AlarmService.add({
date: Date.now() + 143,
ignoreTimezone: true
},
function onAlarmFired(aAlarm) {
ok(alarmId === aAlarm.id, "Future alarm fired successfully.");
cb();
},
function onSuccess(aAlarmId) {
alarmId = aAlarmId;
},
function onError(error) {
ok(false, "Unexpected error adding future alarm " + error);
cb();
});
}
function add_alarm_present(cb) {
let self = this;
let alarmId = undefined;
AlarmService.add({
date: Date.now(),
ignoreTimezone: true
},
function onAlarmFired(aAlarm) {
ok(alarmId === aAlarm.id, "Present alarm fired successfully.");
cb();
},
function onSuccess(aAlarmId) {
alarmId = aAlarmId;
}, function onError(error) {
ok(false, "Unexpected error adding alarm for current time " + error);
cb();
});
}
function add_alarm_past(cb) {
let self = this;
let alarmId = undefined;
AlarmService.add({
date: Date.now() - 5,
ignoreTimezone: true
},
function onAlarmFired(aAlarm) {
ok(alarmId === aAlarm.id, "Past alarm fired successfully.");
cb();
},
function onSuccess(aAlarmId) {
alarmId = aAlarmId;
},
function onError(error) {
ok(false, "Unexpected error adding alarm for time in the past " + error);
cb();
});
}
function trigger_all_alarms(cb) {
let n = 10;
let counter = 0;
let date = Date.now() + 57;
function onAlarmFired() {
counter++;
info("trigger_all_alarms count " + counter);
if (counter == n) {
ok(true, "All " + n + " alarms set to a particular time fired.");
cb();
}
}
for (let i = 0; i < n; i++) {
AlarmService.add(
{
date: date,
ignoreTimezone: true
},
onAlarmFired
);
}
}
function multiple_handlers(cb) {
let d = Date.now() + 100;
let called = 0;
function done() {
if (called == 2) {
ok(true, "Two alarms for the same time fired.");
cb();
}
}
function handler1() {
called++;
done();
}
function handler2() {
called++;
done();
}
AlarmService.add(
{
date: d,
ignoreTimezone: true
},
handler1
);
AlarmService.add(
{
date: d,
ignoreTimezone: true
},
handler2
);
}
function same_time_alarms(cb) {
var fired = 0;
var delay = Date.now() + 100;
function check() {
fired++;
if (fired == 4) {
ok(true, "All alarms set for the same time fired.");
cb();
}
}
function addImmediateAlarm() {
fired++;
AlarmService.add({
date: delay,
ignoreTimezone: true
}, check);
}
AlarmService.add({
date: delay,
ignoreTimezone: true
}, addImmediateAlarm);
AlarmService.add({
date: delay,
ignoreTimezone: true
}, addImmediateAlarm);
}
function test() {
var tests = [
add_alarm_future,
add_alarm_present,
add_alarm_past,
trigger_all_alarms,
multiple_handlers,
same_time_alarms
]
var testIndex = -1;
function nextTest() {
testIndex++;
if (testIndex >= tests.length)
return;
waitForExplicitFinish();
tests[testIndex](function() {
finish();
nextTest();
});
}
nextTest();
}

3
hal/tests/moz.build Normal file
View File

@ -0,0 +1,3 @@
# 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/.

View File

@ -39,7 +39,6 @@ class ProxyBehaviour
virtual imgStatusTracker& GetStatusTracker() const = 0;
virtual imgRequest* GetOwner() const = 0;
virtual void SetOwner(imgRequest* aOwner) = 0;
virtual bool IsStatic() = 0;
};
class RequestBehaviour : public ProxyBehaviour
@ -64,8 +63,6 @@ class RequestBehaviour : public ProxyBehaviour
}
}
virtual bool IsStatic() { return false; }
private:
// We maintain the following invariant:
// The proxy is registered at most with a single imgRequest as an observer,
@ -81,11 +78,6 @@ class RequestBehaviour : public ProxyBehaviour
mozilla::image::Image*
RequestBehaviour::GetImage() const
{
if (mOwnerHasImage && !mOwner) {
NS_WARNING("If mOwnerHasImage is true mOwner must be true");
MOZ_CRASH();
}
if (!mOwnerHasImage)
return nullptr;
return GetStatusTracker().GetImage();
@ -100,11 +92,6 @@ RequestBehaviour::GetStatusTracker() const
// That's why this method uses mOwner->GetStatusTracker() instead of just
// mOwner->mStatusTracker -- we might have a null mImage and yet have an
// mOwner with a non-null mImage (and a null mStatusTracker pointer).
if (mOwnerHasImage && !mOwner) {
NS_WARNING("If mOwnerHasImage is true mOwner must be true");
MOZ_CRASH();
}
return mOwner->GetStatusTracker();
}
@ -178,11 +165,6 @@ nsresult imgRequestProxy::Init(imgRequest* aOwner,
NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
if (!mBehaviour->IsStatic() && !aOwner) {
NS_WARNING("Non-static imgRequestProxies should be initialized with an owner");
MOZ_CRASH();
}
mBehaviour->SetOwner(aOwner);
mListener = aObserver;
// Make sure to addref mListener before the AddProxy call below, since
@ -231,11 +213,6 @@ nsresult imgRequestProxy::ChangeOwner(imgRequest *aNewOwner)
GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
if (!mBehaviour->IsStatic() && !aNewOwner) {
NS_WARNING("Non-static imgRequestProxies should be only changed to a non-null owner");
MOZ_CRASH();
}
mBehaviour->SetOwner(aNewOwner);
// If we were locked, apply the locks here
@ -585,10 +562,6 @@ NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
{
nsresult result;
imgRequestProxy* proxy;
if (mBehaviour->IsStatic()) {
NS_WARNING("Calling non-static imgRequestProxy::Clone with static mBehaviour");
MOZ_CRASH();
}
result = Clone(aObserver, &proxy);
*aClone = proxy;
return result;
@ -1049,8 +1022,6 @@ public:
MOZ_ASSERT(!aOwner, "We shouldn't be giving static requests a non-null owner.");
}
virtual bool IsStatic() { return true; }
private:
// Our image. We have to hold a strong reference here, because that's normally
// the job of the underlying request.
@ -1074,17 +1045,9 @@ NS_IMETHODIMP imgRequestProxyStatic::GetImagePrincipal(nsIPrincipal **aPrincipal
return NS_OK;
}
NS_IMETHODIMP
nsresult
imgRequestProxyStatic::Clone(imgINotificationObserver* aObserver,
imgIRequest** aClone)
imgRequestProxy** aClone)
{
nsresult result;
imgRequestProxy* proxy;
if (!mBehaviour->IsStatic()) {
NS_WARNING("Calling static imgRequestProxy::Clone with non-static mBehaviour");
MOZ_CRASH();
}
result = PerformClone(aObserver, NewStaticProxy, &proxy);
*aClone = proxy;
return result;
return PerformClone(aObserver, NewStaticProxy, aClone);
}

View File

@ -104,7 +104,7 @@ public:
// imgRequest::RemoveProxy
void ClearAnimationConsumers();
nsresult Clone(imgINotificationObserver* aObserver, imgRequestProxy** aClone);
virtual nsresult Clone(imgINotificationObserver* aObserver, imgRequestProxy** aClone);
nsresult GetStaticRequest(imgRequestProxy** aReturn);
protected:
@ -231,8 +231,10 @@ public:
NS_IMETHOD GetImagePrincipal(nsIPrincipal** aPrincipal) MOZ_OVERRIDE;
NS_IMETHOD Clone(imgINotificationObserver* aObserver,
imgIRequest** aClone) MOZ_OVERRIDE;
using imgRequestProxy::Clone;
virtual nsresult Clone(imgINotificationObserver* aObserver,
imgRequestProxy** aClone) MOZ_OVERRIDE;
protected:
friend imgRequestProxy* NewStaticProxy(imgRequestProxy*);

0
js/src/tests/update-test402.sh Executable file → Normal file
View File

View File

@ -297,26 +297,26 @@ WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, wrapScope, wn->Native(), nullptr,
&NS_GET_IID(nsISupports), false,
v.address(), getter_AddRefs(holder));
if (NS_SUCCEEDED(rv)) {
obj = JSVAL_TO_OBJECT(v);
NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object");
NS_ENSURE_SUCCESS(rv, nullptr);
// Because the underlying native didn't have a PreCreate hook, we had
// to a new (or possibly pre-existing) XPCWN in our compartment.
// This could be a problem for chrome code that passes XPCOM objects
// across compartments, because the effects of QI would disappear across
// compartments.
//
// So whenever we pull an XPCWN across compartments in this manner, we
// give the destination object the union of the two native sets. We try
// to do this cleverly in the common case to avoid too much overhead.
XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(ccx, newwn->GetSet(),
wn->GetSet(), false);
if (!unionSet)
return nullptr;
newwn->SetSet(unionSet);
}
obj = JSVAL_TO_OBJECT(v);
NS_ASSERTION(IS_WN_WRAPPER(obj), "bad object");
// Because the underlying native didn't have a PreCreate hook, we had
// to a new (or possibly pre-existing) XPCWN in our compartment.
// This could be a problem for chrome code that passes XPCOM objects
// across compartments, because the effects of QI would disappear across
// compartments.
//
// So whenever we pull an XPCWN across compartments in this manner, we
// give the destination object the union of the two native sets. We try
// to do this cleverly in the common case to avoid too much overhead.
XPCWrappedNative *newwn = static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
XPCNativeSet *unionSet = XPCNativeSet::GetNewOrUsed(ccx, newwn->GetSet(),
wn->GetSet(), false);
if (!unionSet)
return nullptr;
newwn->SetSet(unionSet);
return DoubleWrap(cx, obj, flags);
}

View File

@ -158,7 +158,7 @@ skip-if(B2G) == filter-scaled-02.html filter-scaled-02-ref.html
skip-if(B2G) == foreignObject-ancestor-style-change-01.svg foreignObject-ancestor-style-change-01-ref.svg
skip-if(B2G) == foreignObject-change-transform-01.svg pass.svg
== foreignObject-display-01.svg pass.svg
fails-if(B2G) == foreignObject-form-theme.svg foreignObject-form-theme-ref.html
== foreignObject-form-theme.svg foreignObject-form-theme-ref.html
== foreignObject-img-form-theme.html foreignObject-img-form-theme-ref.html
skip-if(B2G) == foreignObject-move-repaint-01.svg pass.svg
== foreignObject-overflow-01.svg pass.svg

View File

@ -49,7 +49,7 @@
#ifdef MOZ_ANDROID_BEAM
<!-- Android Beam support -->
<uses-permission android:name="android.permission.NFC"/>
<uses-feature android:name="android.hardware.nfc"/>
<uses-feature android:name="android.hardware.nfc" android:required="false"/>
#endif
#ifdef MOZ_WEBRTC

View File

@ -61,6 +61,8 @@ relativesrcdir toolkit/locales:
relativesrcdir dom/locales:
locale/@AB_CD@/browser/overrides/charsetTitles.properties (%chrome/charsetTitles.properties)
locale/@AB_CD@/browser/overrides/global.dtd (%chrome/global.dtd)
locale/@AB_CD@/browser/overrides/AccessFu.properties (%chrome/accessibility/AccessFu.properties)
% override chrome://global/locale/charsetTitles.properties chrome://browser/locale/overrides/charsetTitles.properties
% override chrome://global/locale/global.dtd chrome://browser/locale/overrides/global.dtd
% override chrome://global/locale/AccessFu.properties chrome://browser/locale/overrides/AccessFu.properties

View File

@ -3962,6 +3962,7 @@ pref("webgl.default-no-alpha", false);
pref("webgl.force-layers-readback", false);
pref("webgl.lose-context-on-heap-minimize", false);
pref("webgl.can-lose-context-in-foreground", true);
pref("webgl.max-warnings-per-context", 32);
// Stagefright prefs
pref("stagefright.force-enabled", false);

View File

@ -38,7 +38,7 @@ class GeckoInstance(object):
self.runner = runner_class.create(
binary=self.bin,
profile_args=profile_args,
cmdargs=['-no-remote'],
cmdargs=['-no-remote', '-marionette'],
kp_kwargs={
'processOutputLine': [NullOutput()],
'logfile': self.gecko_log})

View File

@ -894,11 +894,11 @@ function emitMultiEvents(type, touch, touches) {
let win = doc.defaultView;
// touches that are in the same document
let documentTouches = doc.createTouchList(touches.filter(function(t) {
return t.target.ownerDocument === doc;
return ((t.target.ownerDocument === doc) && (type != 'touchcancel'));
}));
// touches on the same target
let targetTouches = doc.createTouchList(touches.filter(function(t) {
return t.target === target;
return ((t.target === target) && ((type != 'touchcancel') || (type != 'touchend')));
}));
// Create changed touches
let changedTouches = doc.createTouchList(touch);

View File

@ -2524,6 +2524,13 @@ nsDownload::~nsDownload()
{
}
NS_IMETHODIMP nsDownload::SetSha256Hash(const nsACString& aHash) {
MOZ_ASSERT(NS_IsMainThread(), "Must call SetSha256Hash on main thread");
// This will be used later to query the application reputation service.
mHash = aHash;
return NS_OK;
}
#ifdef MOZ_ENABLE_GIO
static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpointer user_data)
{
@ -2586,7 +2593,6 @@ nsDownload::SetState(DownloadState aState)
#endif
case nsIDownloadManager::DOWNLOAD_FINISHED:
{
// Do what exthandler would have done if necessary
nsresult rv = ExecuteDesiredAction();
if (NS_FAILED(rv)) {
// We've failed to execute the desired action. As a result, we should
@ -2928,6 +2934,8 @@ nsDownload::OnStateChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, uint32_t aStateFlags,
nsresult aStatus)
{
MOZ_ASSERT(NS_IsMainThread(), "Must call OnStateChange in main thread");
// We don't want to lose access to our member variables
nsRefPtr<nsDownload> kungFuDeathGrip = this;
@ -3187,10 +3195,12 @@ nsDownload::Finalize()
nsresult
nsDownload::ExecuteDesiredAction()
{
// If we have a temp file and we have resumed, we have to do what the
// external helper app service would have done.
if (!mTempFile || !WasResumed())
// nsExternalHelperAppHandler is the only caller of AddDownload that sets a
// tempfile parameter. In this case, execute the desired action according to
// the saved mime info.
if (!mTempFile) {
return NS_OK;
}
// We need to bail if for some reason the temp file got removed
bool fileExists;

View File

@ -421,6 +421,11 @@ private:
enum AutoResume { DONT_RESUME, AUTO_RESUME };
AutoResume mAutoResume;
/**
* Stores the SHA-256 hash associated with the downloaded file.
*/
nsAutoCString mHash;
friend class nsDownloadManager;
};

View File

@ -16,6 +16,11 @@
#define PREF_BDM_SHOWWHENSTARTING "browser.download.manager.showWhenStarting"
#define PREF_BDM_FOCUSWHENSTARTING "browser.download.manager.focusWhenStarting"
// This class only exists because nsDownload cannot inherit from nsITransfer
// directly. The reason for this is that nsDownloadManager (incorrectly) keeps
// an nsCOMArray of nsDownloads, and nsCOMArray is only intended for use with
// abstract classes. Using a concrete class that multiply inherits from classes
// deriving from nsISupports will throw ambiguous base class errors.
class nsDownloadProxy : public nsITransfer
{
public:
@ -143,6 +148,12 @@ public:
return mInner->OnSecurityChange(aWebProgress, aRequest, aState);
}
NS_IMETHODIMP SetSha256Hash(const nsACString& aHash)
{
NS_ENSURE_TRUE(mInner, NS_ERROR_NOT_INITIALIZED);
return mInner->SetSha256Hash(aHash);
}
private:
nsCOMPtr<nsIDownload> mInner;
};

View File

@ -114,8 +114,12 @@ interface nsIDownloadManager : nsISupports {
* @param aTempFile The location of a temporary file; i.e. a file in which
* the received data will be stored, but which is not
* equal to the target file. (will be moved to the real
* target by the caller, when the download is finished)
* May be null.
* target by the DownloadManager, when the download is
* finished). This will be null for all callers except for
* nsExternalHelperAppHandler. Addons should generally pass
* null for aTempFile. This will be moved to the real target
* by the download manager when the download is finished,
* and the action indicated by aMIMEInfo will be executed.
*
* @param aCancelable An object that can be used to abort the download.
* Must not be null.

View File

@ -52,7 +52,8 @@ MockTransfer.prototype = {
onRefreshAttempted: function () {},
/* nsITransfer */
init: function () {}
init: function() {},
setSha256Hash: function() {}
};
// Create an instance of a MockObjectRegisterer whose methods can be used to

View File

@ -10,7 +10,7 @@ interface nsICancelable;
interface nsIMIMEInfo;
interface nsIFile;
[scriptable, uuid(08b0b78c-6d7d-4d97-86c9-be5a695813c9)]
[scriptable, uuid(b1c81100-9d66-11e2-9e96-0800200c9a66)]
interface nsITransfer : nsIWebProgressListener2 {
/**
@ -57,6 +57,14 @@ interface nsITransfer : nsIWebProgressListener2 {
in nsIFile aTempFile,
in nsICancelable aCancelable,
in boolean aIsPrivate);
/*
* Used to notify the transfer object of the hash of the downloaded file.
* Must be called on the main thread, only after the download has finished
* successfully.
* @param aHash The SHA-256 hash in raw bytes of the downloaded file.
*/
void setSha256Hash(in ACString aHash);
};
%{C++

View File

@ -141,8 +141,8 @@ PRLogModuleInfo* nsExternalHelperAppService::mLog = nullptr;
// of PR_LOG_DEBUG (4), and we want less detailed output here
// Using 3 instead of PR_LOG_WARN because we don't output warnings
#undef LOG
#define LOG(args) PR_LOG(mLog, 3, args)
#define LOG_ENABLED() PR_LOG_TEST(mLog, 3)
#define LOG(args) PR_LOG(nsExternalHelperAppService::mLog, 3, args)
#define LOG_ENABLED() PR_LOG_TEST(nsExternalHelperAppService::mLog, 3)
static const char NEVER_ASK_FOR_SAVE_TO_DISK_PREF[] =
"browser.helperApps.neverAsk.saveToDisk";
@ -1111,14 +1111,13 @@ nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo,
, mForceSave(aForceSave)
, mCanceled(false)
, mShouldCloseWindow(false)
, mReceivedDispositionInfo(false)
, mStopRequestIssued(false)
, mProgressListenerInitialized(false)
, mReason(aReason)
, mContentLength(-1)
, mProgress(0)
, mSaver(nullptr)
, mKeepRequestAlive(false)
, mDialogProgressListener(nullptr)
, mTransfer(nullptr)
, mRequest(nullptr)
, mExtProtSvc(aExtProtSvc)
{
@ -1158,24 +1157,9 @@ nsExternalAppHandler::~nsExternalAppHandler()
NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListener2 * aWebProgressListener)
{
// this call back means we've successfully brought up the
// progress window so set the appropriate flag, even though
// aWebProgressListener might be null
if (mReceivedDispositionInfo)
mProgressListenerInitialized = true;
// Go ahead and register the progress listener....
mWebProgressListener = aWebProgressListener;
// while we were bringing up the progress dialog, we actually finished processing the
// url. If that's the case then mStopRequestIssued will be true. We need to execute the
// operation since we are actually done now.
if (mStopRequestIssued && aWebProgressListener)
{
return ExecuteDesiredAction();
}
// This is always called by nsHelperDlg.js. Go ahead and register the
// progress listener. At this point, we don't have mTransfer.
mDialogProgressListener = aWebProgressListener;
return NS_OK;
}
@ -1213,13 +1197,6 @@ NS_IMETHODIMP nsExternalAppHandler::GetContentLength(int64_t *aContentLength)
return NS_OK;
}
NS_IMETHODIMP nsExternalAppHandler::CloseProgressWindow()
{
// release extra state...
mWebProgressListener = nullptr;
return NS_OK;
}
void nsExternalAppHandler::RetargetLoadNotifications(nsIRequest *request)
{
// we are going to run the downloading of the helper app in our own little docloader / load group context.
@ -1400,6 +1377,10 @@ nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel * aChannel)
return rv;
}
rv = mSaver->EnableSha256();
NS_ENSURE_SUCCESS(rv, rv);
LOG(("Enabled hashing"));
rv = mSaver->SetTarget(mTempFile, false);
NS_ENSURE_SUCCESS(rv, rv);
@ -1594,25 +1575,18 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
if (alwaysAsk)
{
// do this first! make sure we don't try to take an action until the user tells us what they want to do
// with it...
mReceivedDispositionInfo = false;
mKeepRequestAlive = true;
// invoke the dialog!!!!! use mWindowContext as the window context parameter for the dialog request
mDialog = do_CreateInstance( NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv );
NS_ENSURE_SUCCESS(rv, rv);
// this will create a reference cycle (the dialog holds a reference to us as
// nsIHelperAppLauncher), which will be broken in Cancel or
// CreateProgressListener.
// nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer.
rv = mDialog->Show( this, mWindowContext, mReason );
// what do we do if the dialog failed? I guess we should call Cancel and abort the load....
}
else
{
mReceivedDispositionInfo = true; // no need to wait for a response from the user
// We need to do the save/open immediately, then.
#ifdef XP_WIN
@ -1657,8 +1631,8 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo
return NS_OK;
}
// Convert error info into proper message text and send OnStatusChange notification
// to the web progress listener.
// Convert error info into proper message text and send OnStatusChange
// notification to the dialog progress listener or nsITransfer implementation.
void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsAFlatString &path)
{
nsAutoString msgId;
@ -1724,7 +1698,7 @@ void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequ
}
PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR,
("Error: %s, type=%i, listener=0x%p, rv=0x%08X\n",
NS_LossyConvertUTF16toASCII(msgId).get(), type, mWebProgressListener.get(), rv));
NS_LossyConvertUTF16toASCII(msgId).get(), type, mDialogProgressListener.get(), rv));
PR_LOG(nsExternalHelperAppService::mLog, PR_LOG_ERROR,
(" path='%s'\n", NS_ConvertUTF16toUTF8(path).get()));
@ -1740,10 +1714,12 @@ void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequ
const PRUnichar *strings[] = { path.get() };
if(NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1, getter_Copies(msgText))))
{
if (mWebProgressListener)
if (mDialogProgressListener)
{
// We have a listener, let it handle the error.
mWebProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
} else if (mTransfer) {
mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
}
else
if (XRE_GetProcessType() == GeckoProcessType_Default) {
@ -1784,11 +1760,10 @@ nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
if (NS_SUCCEEDED(rv))
{
// Send progress notification.
if (mWebProgressListener)
{
mWebProgressListener->OnProgressChange64(nullptr, request, mProgress,
mContentLength, mProgress,
mContentLength);
if (mTransfer) {
mTransfer->OnProgressChange64(nullptr, request, mProgress,
mContentLength, mProgress,
mContentLength);
}
}
else
@ -1811,9 +1786,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISuppor
{
mStopRequestIssued = true;
if (!mKeepRequestAlive)
mRequest = nullptr;
// Cancel if the request did not complete successfully.
if (!mCanceled && NS_FAILED(aStatus))
{
@ -1844,12 +1816,15 @@ NS_IMETHODIMP
nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver *aSaver,
nsresult aStatus)
{
// Free the reference that the saver keeps on us.
mSaver = nullptr;
if (mCanceled)
return NS_OK;
// Save the hash
nsresult rv = mSaver->GetSha256Hash(mHash);
// Free the reference that the saver keeps on us, even if we couldn't get the
// hash.
mSaver = nullptr;
if (NS_FAILED(aStatus)) {
nsAutoString path;
mTempFile->GetPath(path);
@ -1859,55 +1834,46 @@ nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver *aSaver,
return NS_OK;
}
// Do what the user asked for
ExecuteDesiredAction();
// This nsITransfer object holds a reference to us (we are its observer), so
// we need to release the reference to break a reference cycle (and therefore
// to prevent leaking)
mWebProgressListener = nullptr;
// Notify the transfer object that we are done if the user has chosen an
// action. If the user hasn't chosen an action and InitializeDownload hasn't
// been called, the progress listener (nsITransfer) will be notified in
// CreateTransfer.
if (mTransfer) {
rv = NotifyTransfer();
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult nsExternalAppHandler::ExecuteDesiredAction()
nsresult nsExternalAppHandler::NotifyTransfer()
{
nsresult rv = NS_OK;
if (mProgressListenerInitialized && !mCanceled)
{
rv = MoveFile(mFinalFileDestination);
if (NS_SUCCEEDED(rv))
{
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
mMimeInfo->GetPreferredAction(&action);
if (action == nsIMIMEInfo::useHelperApp ||
action == nsIMIMEInfo::useSystemDefault)
{
rv = OpenWithApplication();
}
else if(action == nsIMIMEInfo::saveToDisk)
{
mExtProtSvc->FixFilePermissions(mFinalFileDestination);
}
}
MOZ_ASSERT(NS_IsMainThread(), "Must notify on main thread");
MOZ_ASSERT(!mCanceled, "Can't notify if canceled or action "
"hasn't been chosen");
MOZ_ASSERT(mTransfer, "We must have an nsITransfer");
// Notify dialog that download is complete.
// By waiting till this point, it ensures that the progress dialog doesn't indicate
// success until we're really done.
if(mWebProgressListener)
{
if (!mCanceled)
{
mWebProgressListener->OnProgressChange64(nullptr, nullptr, mProgress, mContentLength, mProgress, mContentLength);
}
mWebProgressListener->OnStateChange(nullptr, nullptr,
nsIWebProgressListener::STATE_STOP |
nsIWebProgressListener::STATE_IS_REQUEST |
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
}
}
LOG(("Notifying progress listener"));
return rv;
nsresult rv = mTransfer->SetSha256Hash(mHash);
NS_ENSURE_SUCCESS(rv, rv);
rv = mTransfer->OnProgressChange64(nullptr, nullptr, mProgress,
mContentLength, mProgress, mContentLength);
NS_ENSURE_SUCCESS(rv, rv);
rv = mTransfer->OnStateChange(nullptr, nullptr,
nsIWebProgressListener::STATE_STOP |
nsIWebProgressListener::STATE_IS_REQUEST |
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
NS_ENSURE_SUCCESS(rv, rv);
// This nsITransfer object holds a reference to us (we are its observer), so
// we need to release the reference to break a reference cycle (and therefore
// to prevent leaking)
mTransfer = nullptr;
return NS_OK;
}
NS_IMETHODIMP nsExternalAppHandler::GetMIMEInfo(nsIMIMEInfo ** aMIMEInfo)
@ -1937,14 +1903,14 @@ nsresult nsExternalAppHandler::InitializeDownload(nsITransfer* aTransfer)
nsCOMPtr<nsIURI> target;
rv = NS_NewFileURI(getter_AddRefs(target), mFinalFileDestination);
if (NS_FAILED(rv)) return rv;
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
rv = aTransfer->Init(mSourceUrl, target, EmptyString(),
mMimeInfo, mTimeDownloadStarted, mTempFile, this,
channel && NS_UsePrivateBrowsing(channel));
if (NS_FAILED(rv)) return rv;
NS_ENSURE_SUCCESS(rv, rv);
// Now let's add the download to history
nsCOMPtr<nsIDownloadHistory> dh(do_GetService(NS_DOWNLOADHISTORY_CONTRACTID));
@ -1963,31 +1929,38 @@ nsresult nsExternalAppHandler::InitializeDownload(nsITransfer* aTransfer)
return rv;
}
nsresult nsExternalAppHandler::CreateProgressListener()
nsresult nsExternalAppHandler::CreateTransfer()
{
// we are back from the helper app dialog (where the user chooses to save or open), but we aren't
// done processing the load. in this case, throw up a progress dialog so the user can see what's going on...
// We are back from the helper app dialog (where the user chooses to save or
// open), but we aren't done processing the load. in this case, throw up a
// progress dialog so the user can see what's going on.
// Also, release our reference to mDialog. We don't need it anymore, and we
// need to break the reference cycle.
mDialog = nullptr;
if (!mDialogProgressListener) {
NS_WARNING("The dialog should nullify the dialog progress listener");
}
nsresult rv;
nsCOMPtr<nsITransfer> tr = do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
InitializeDownload(tr);
// We must be able to create an nsITransfer object. If not, it doesn't matter
// much that we can't launch the helper application or save to disk.
mTransfer = do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = InitializeDownload(mTransfer);
NS_ENSURE_SUCCESS(rv, rv);
if (tr)
tr->OnStateChange(nullptr, mRequest, nsIWebProgressListener::STATE_START |
nsIWebProgressListener::STATE_IS_REQUEST |
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
rv = mTransfer->OnStateChange(nullptr, mRequest,
nsIWebProgressListener::STATE_START |
nsIWebProgressListener::STATE_IS_REQUEST |
nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
NS_ENSURE_SUCCESS(rv, rv);
// note we might not have a listener here if the QI() failed, or if
// there is no nsITransfer object, but we still call
// SetWebProgressListener() to make sure our progress state is sane
// NOTE: This will set up a reference cycle (this nsITransfer has us set up as
// its observer). This cycle will be broken in Cancel, CloseProgressWindow or
// OnStopRequest.
SetWebProgressListener(tr);
// While we were bringing up the progress dialog, we actually finished
// processing the url. If that's the case then mStopRequestIssued will be
// true and OnSaveComplete has been called.
if (mStopRequestIssued && !mSaver) {
return NotifyTransfer();
}
mRequest = nullptr;
@ -2049,68 +2022,9 @@ void nsExternalAppHandler::RequestSaveDestination(const nsAFlatString &aDefaultF
}
}
nsresult nsExternalAppHandler::MoveFile(nsIFile * aNewFileLocation)
{
nsresult rv = NS_OK;
NS_ASSERTION(mStopRequestIssued, "uhoh, how did we get here if we aren't done getting data?");
// if the on stop request was actually issued then it's now time to actually perform the file move....
if (mStopRequestIssued && aNewFileLocation)
{
// Unfortunately, MoveTo will fail if a file already exists at the user specified location....
// but the user has told us, this is where they want the file! (when we threw up the save to file dialog,
// it told them the file already exists and do they wish to over write it. So it should be okay to delete
// fileToUse if it already exists.
bool equalToTempFile = false;
bool filetoUseAlreadyExists = false;
aNewFileLocation->Equals(mTempFile, &equalToTempFile);
aNewFileLocation->Exists(&filetoUseAlreadyExists);
if (filetoUseAlreadyExists && !equalToTempFile)
aNewFileLocation->Remove(false);
// extract the new leaf name from the file location
nsAutoString fileName;
aNewFileLocation->GetLeafName(fileName);
nsCOMPtr<nsIFile> directoryLocation;
rv = aNewFileLocation->GetParent(getter_AddRefs(directoryLocation));
if (directoryLocation)
{
rv = mTempFile->MoveTo(directoryLocation, fileName);
}
if (NS_FAILED(rv))
{
// Send error notification.
nsAutoString path;
aNewFileLocation->GetPath(path);
SendStatusChange(kWriteError, rv, nullptr, path);
Cancel(rv); // Cancel (and clean up temp file).
}
#if defined(XP_OS2)
else
{
// tag the file with its source URI
nsCOMPtr<nsILocalFileOS2> localFileOS2 = do_QueryInterface(aNewFileLocation);
if (localFileOS2)
{
nsAutoCString url;
mSourceUrl->GetSpec(url);
localFileOS2->SetFileSource(url);
}
}
#endif
}
return rv;
}
// SaveToDisk should only be called by the helper app dialog which allows
// the user to say launch with application or save to disk. It doesn't actually
// perform the save, it just prompts for the destination file name. The actual save
// won't happen until we are done downloading the content and are sure we've
// shown a progress dialog. This was done to simplify the
// logic that was showing up in this method. Internal callers who actually want
// to preform the save should call ::MoveFile
// perform the save, it just prompts for the destination file name.
NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool aRememberThisPreference)
{
if (mCanceled)
@ -2118,9 +2032,6 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool
mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
// The helper app dialog has told us what to do.
mReceivedDispositionInfo = true;
if (!aNewFileLocation) {
if (mSuggestedFileName.IsEmpty())
RequestSaveDestination(mTempLeafName, mTempFileExtension);
@ -2143,6 +2054,9 @@ NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool
}
nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
{
if (mCanceled)
return NS_OK;
NS_PRECONDITION(aNewFileLocation, "Must be called with a non-null file");
nsresult rv = NS_OK;
@ -2150,9 +2064,10 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
mFinalFileDestination = do_QueryInterface(fileToUse);
// Move what we have in the final directory, but append .part
// to it, to indicate that it's unfinished.
// do not do that if we're already done
if (mFinalFileDestination && !mStopRequestIssued)
// to it, to indicate that it's unfinished. Do not call SetTarget on the
// saver if we are done (Finish has been called) but OnSaverComplete has not
// been called.
if (mFinalFileDestination && mSaver && !mStopRequestIssued)
{
nsCOMPtr<nsIFile> movedFile;
mFinalFileDestination->Clone(getter_AddRefs(movedFile));
@ -2163,24 +2078,22 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
name.AppendLiteral(".part");
movedFile->SetLeafName(name);
if (mSaver)
{
rv = mSaver->SetTarget(movedFile, true);
if (NS_FAILED(rv)) {
nsAutoString path;
mTempFile->GetPath(path);
SendStatusChange(kWriteError, rv, nullptr, path);
Cancel(rv);
return NS_OK;
}
rv = mSaver->SetTarget(movedFile, true);
if (NS_FAILED(rv)) {
nsAutoString path;
mTempFile->GetPath(path);
SendStatusChange(kWriteError, rv, nullptr, path);
Cancel(rv);
return NS_OK;
}
mTempFile = movedFile;
}
}
if (!mProgressListenerInitialized)
CreateProgressListener();
// The helper app dialog has told us what to do and we have a final file
// destination.
CreateTransfer();
// now that the user has chosen the file location to save to, it's okay to fire the refresh tag
// if there is one. We don't want to do this before the save as dialog goes away because this dialog
@ -2192,68 +2105,9 @@ nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
}
nsresult nsExternalAppHandler::OpenWithApplication()
{
nsresult rv = NS_OK;
if (mCanceled)
return NS_OK;
// we only should have gotten here if the on stop request had been fired already.
NS_ASSERTION(mStopRequestIssued, "uhoh, how did we get here if we aren't done getting data?");
// if a stop request was already issued then proceed with launching the application.
if (mStopRequestIssued)
{
// Note for the default value:
// Mac users have been very verbal about temp files being deleted on
// app exit - they don't like it - but we'll continue to do this on
// other platforms for now.
bool deleteTempFileOnExit =
Preferences::GetBool("browser.helperApps.deleteTempFileOnExit",
#if !defined(XP_MACOSX)
true);
#else
false);
#endif
// See whether the channel has been opened in private browsing mode
NS_ASSERTION(mRequest, "This should never be called with a null request");
nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
bool inPrivateBrowsing = channel && NS_UsePrivateBrowsing(channel);
// make the tmp file readonly so users won't edit it and lose the changes
// only if we're going to delete the file
if (deleteTempFileOnExit || inPrivateBrowsing)
mFinalFileDestination->SetPermissions(0400);
rv = mMimeInfo->LaunchWithFile(mFinalFileDestination);
if (NS_FAILED(rv))
{
// Send error notification.
nsAutoString path;
mFinalFileDestination->GetPath(path);
SendStatusChange(kLaunchError, rv, nullptr, path);
Cancel(rv); // Cancel, and clean up temp file.
}
// Always schedule files to be deleted at the end of the private browsing
// mode, regardless of the value of the pref.
else if (deleteTempFileOnExit) {
mExtProtSvc->DeleteTemporaryFileOnExit(mFinalFileDestination);
}
else if (inPrivateBrowsing) {
mExtProtSvc->DeleteTemporaryPrivateFileWhenPossible(mFinalFileDestination);
}
}
return rv;
}
// LaunchWithApplication should only be called by the helper app dialog which allows
// the user to say launch with application or save to disk. It doesn't actually
// perform launch with application. That won't happen until we are done downloading
// the content and are sure we've shown a progress dialog. This was done to simplify the
// logic that was showing up in this method.
// LaunchWithApplication should only be called by the helper app dialog which
// allows the user to say launch with application or save to disk. It doesn't
// actually perform launch with application.
NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication, bool aRememberThisPreference)
{
if (mCanceled)
@ -2262,7 +2116,6 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
// user has chosen to launch using an application, fire any refresh tags now...
ProcessAnyRefreshTags();
mReceivedDispositionInfo = true;
if (mMimeInfo && aApplication) {
PlatformLocalHandlerApp_t *handlerApp =
new PlatformLocalHandlerApp_t(EmptyString(), aApplication);
@ -2292,12 +2145,13 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
return rv;
}
// Now that the user has elected to launch the downloaded file with a helper app, we're justified in
// removing the 'salted' name. We'll rename to what was specified in mSuggestedFileName after the
// download is done prior to launching the helper app. So that any existing file of that name won't
// be overwritten we call CreateUnique() before calling MoveFile(). Also note that we use the same
// directory as originally downloaded to so that MoveFile() just does an in place rename.
// Now that the user has elected to launch the downloaded file with a helper
// app, we're justified in removing the 'salted' name. We'll rename to what
// was specified in mSuggestedFileName after the download is done prior to
// launching the helper app. So that any existing file of that name won't be
// overwritten we call CreateUnique(). Also note that we use the same
// directory as originally downloaded so nsDownload can rename in place
// later.
nsCOMPtr<nsIFile> fileToUse;
(void) GetDownloadDirectory(getter_AddRefs(fileToUse));
@ -2318,8 +2172,7 @@ NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication
{
mFinalFileDestination = do_QueryInterface(fileToUse);
// launch the progress window now that the user has picked the desired action.
if (!mProgressListenerInitialized)
CreateProgressListener();
CreateTransfer();
}
else
{
@ -2340,8 +2193,10 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason)
// XXX should not ignore the reason
mCanceled = true;
if (mSaver)
if (mSaver) {
mSaver->Finish(aReason);
mSaver = nullptr;
}
// Break our reference cycle with the helper app dialog (set up in
// OnStartRequest)
@ -2351,7 +2206,8 @@ NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason)
// Release the listener, to break the reference cycle with it (we are the
// observer of the listener).
mWebProgressListener = nullptr;
mDialogProgressListener = nullptr;
mTransfer = nullptr;
return NS_OK;
}

View File

@ -282,12 +282,9 @@ protected:
bool mShouldCloseWindow;
/**
* have we received information from the user about how they want to
* dispose of this content
* True if a stop request has been issued.
*/
bool mReceivedDispositionInfo;
bool mStopRequestIssued;
bool mProgressListenerInitialized;
bool mIsFileChannel;
@ -323,6 +320,11 @@ protected:
*/
nsCOMPtr<nsIBackgroundFileSaver> mSaver;
/**
* Stores the SHA-256 hash associated with the file that we downloaded.
*/
nsAutoCString mHash;
/**
* Creates the temporary file for the download and an output stream for it.
* Upon successful return, both mTempFile and mSaver will be valid.
@ -336,12 +338,10 @@ protected:
*/
void RetargetLoadNotifications(nsIRequest *request);
/**
* If the user tells us how they want to dispose of the content and
* we still haven't finished downloading while they were deciding,
* then create a progress listener of some kind so they know
* what's going on...
* Once the user tells us how they want to dispose of the content
* create an nsITransfer so they know what's going on...
*/
nsresult CreateProgressListener();
nsresult CreateTransfer();
/*
@ -381,22 +381,10 @@ protected:
void ProcessAnyRefreshTags();
/**
* An internal method used to actually move the temp file to the final
* destination once we done receiving data AND have showed the progress dialog
* Notify our nsITransfer object that we are done with the download.
*/
nsresult MoveFile(nsIFile * aNewFileLocation);
/**
* An internal method used to actually launch a helper app given the temp file
* once we are done receiving data AND have showed the progress dialog.
* Uses the application specified in the mime info.
*/
nsresult OpenWithApplication();
nsresult NotifyTransfer();
/**
* Helper routine which peaks at the mime action specified by mMimeInfo
* and calls either MoveFile or OpenWithApplication
*/
nsresult ExecuteDesiredAction();
/**
* Helper routine that searches a pref string for a given mime type
*/
@ -427,7 +415,17 @@ protected:
*/
nsresult MaybeCloseWindow();
nsCOMPtr<nsIWebProgressListener2> mWebProgressListener;
/**
* Set in nsHelperDlgApp.js. This is always null after the user has chosen an
* action.
*/
nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
/**
* Set once the user has chosen an action. This is null after the download
* has been canceled or completes.
*/
nsCOMPtr<nsITransfer> mTransfer;
nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;

View File

@ -96,14 +96,16 @@ interface nsIHelperAppLauncher : nsICancelable
readonly attribute AString suggestedFileName;
/**
* Called when we want to just save the content to a particular file.
* NOTE: This will release the reference to the nsIHelperAppLauncherDialog.
* @param aNewFileLocation Location where the content should be saved
* Saves the final destination of the file. Does not actually perform the
* save.
* NOTE: This will release the reference to the
* nsIHelperAppLauncherDialog.
*/
void saveToDisk(in nsIFile aNewFileLocation, in boolean aRememberThisPreference);
/**
* Use aApplication to launch with this content.
* Remembers that aApplication should be used to launch this content. Does
* not actually launch the application.
* NOTE: This will release the reference to the nsIHelperAppLauncherDialog.
* @param aApplication nsIFile corresponding to the location of the application to use.
* @param aRememberThisPreference TRUE if we should remember this choice.
@ -125,12 +127,6 @@ interface nsIHelperAppLauncher : nsICancelable
*/
void setWebProgressListener(in nsIWebProgressListener2 aWebProgressListener);
/**
* when the stand alone progress window actually closes, it calls this method
* so we can release any local state...
*/
void closeProgressWindow();
/**
* The file we are saving to
*/