This commit is contained in:
Rob Campbell 2011-10-04 09:10:55 -03:00
Родитель 4dc1c09386 9a6d70c1b6
Коммит be2df34164
122 изменённых файлов: 2927 добавлений и 696 удалений

Просмотреть файл

@ -324,6 +324,16 @@ private:
};
/**
* Accessible widget selection change event.
*/
class AccSelectionChangeEvent : public AccEvent
{
public:
};
/**
* Accessible table change event.
*/

Просмотреть файл

@ -278,8 +278,8 @@ nsHTMLButtonAccessible::NativeState()
{
PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::submit, eIgnoreCase))
nsEventStates elmState = mContent->AsElement()->State();
if (elmState.HasState(NS_EVENT_STATE_DEFAULT))
state |= states::DEFAULT;
return state;
@ -382,8 +382,8 @@ nsHTML4ButtonAccessible::NativeState()
state |= states::FOCUSABLE;
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::submit, eIgnoreCase))
nsEventStates elmState = mContent->AsElement()->State();
if (elmState.HasState(NS_EVENT_STATE_DEFAULT))
state |= states::DEFAULT;
return state;

Просмотреть файл

@ -817,22 +817,17 @@ Relation
nsXULTreeItemAccessibleBase::RelationByType(PRUint32 aType)
{
if (aType != nsIAccessibleRelation::RELATION_NODE_CHILD_OF)
return nsAccessible::RelationByType(aType);
return Relation();
Relation rel;
PRInt32 parentIndex;
if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
return rel;
return Relation();
if (parentIndex == -1) {
rel.AppendTarget(mParent);
return rel;
}
if (parentIndex == -1)
return Relation(mParent);
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(mParent);
rel.AppendTarget(treeAcc->GetTreeItemAccessible(parentIndex));
return rel;
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
}
PRUint8

Просмотреть файл

@ -43,6 +43,7 @@
#include "nsAccUtils.h"
#include "nsDocAccessible.h"
#include "nsEventShell.h"
#include "Relation.h"
#include "States.h"
#include "nsITreeSelection.h"
@ -1237,6 +1238,12 @@ nsXULTreeGridCellAccessible::IndexInParent() const
return GetColumnIndex();
}
Relation
nsXULTreeGridCellAccessible::RelationByType(PRUint32 aType)
{
return Relation();
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: public implementation

Просмотреть файл

@ -163,6 +163,7 @@ public:
virtual nsAccessible* FocusedChild();
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
virtual PRInt32 IndexInParent() const;
virtual Relation RelationByType(PRUint32 aType);
virtual PRUint32 NativeRole();
virtual PRUint64 NativeState();

Просмотреть файл

@ -76,6 +76,7 @@ _TEST_FILES =\
test_focus_menu.xul \
test_focus_name.html \
test_focus_selects.html \
test_focus_tabbox.xul \
test_focus_tree.xul \
test_menu.xul \
test_mutation.html \

Просмотреть файл

@ -106,9 +106,10 @@
gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
todo(false, "shift+tab doesn't issue the focus, see bug 684818");
//gQuee.push(new synthShiftTab("link", new focusChecker("link")));
if (LINUX)
todo(false, "shift tab from editable document Fails on linux!");
else
gQueue.push(new synthShiftTab("link", new focusChecker("link")));
gQueue.invoke(); // Will call SimpleTest.finish();
}

Просмотреть файл

@ -0,0 +1,94 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tabbox focus testing">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="../common.js" />
<script type="application/javascript"
src="../states.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript">
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTests()
{
// Test focus events.
gQueue = new eventQueue();
gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
new focusChecker("textbox")));
gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
new focusChecker("tab3")));
gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },
new focusChecker("tab1")));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=370396"
title="Control+Tab to an empty tab panel in a tabbox causes focus to leave the tabbox">
Mozilla Bug 370396
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
<vbox flex="1">
<tabbox>
<tabs>
<tab id="tab1" label="Tab1" selected="true"/>
<tab id="tab2" label="Tab2" />
<tab id="tab3" label="Tab3" />
</tabs>
<tabpanels>
<tabpanel orient="vertical">
<groupbox orient="vertical">
<checkbox id="checkbox1" label="Monday" width="75"/>
<checkbox label="Tuesday" width="75"/>
<checkbox label="Wednesday" width="75"/>
<checkbox label="Thursday" width="75"/>
<checkbox label="Friday" width="75"/>
<checkbox label="Saturday" width="75"/>
<checkbox label="Sunday" width="75"/>
</groupbox>
<spacer style="height: 10px" />
<label value="Label After checkboxes" />
</tabpanel>
<tabpanel orient="vertical">
<textbox id="textbox" />
</tabpanel>
<tabpanel orient="vertical">
<description>Tab 3 content</description>
</tabpanel>
</tabpanels>
</tabbox>
<vbox id="eventdump"/>
</vbox>
</hbox>
</window>

Просмотреть файл

@ -47,6 +47,10 @@
var treeitem6 = treeitem5.nextSibling;
testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
// treeitems and treecells shouldn't pick up relations from tree
testRelation(treeitem1, RELATION_LABELLED_BY, null);
testRelation(treeitem1.firstChild, RELATION_LABELLED_BY, null);
SimpleTest.finish();
}
@ -68,7 +72,12 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
title="Reorganize implementation of XUL tree accessibility">
Mozilla Bug 503727
</a><br/>
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=691248"
title="XUL tree items shouldn't pick up relations from XUL tree">
Mozilla Bug 691248
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
@ -77,9 +86,11 @@
</body>
<vbox flex="1">
<label control="tree" value="It's a tree"/>
<tree id="tree" flex="1">
<treecols>
<treecol id="col" flex="1" primary="true" label="column"/>
<treecol id="col2" flex="1" label="column2"/>
</treecols>
<treechildren/>
</tree>

Просмотреть файл

@ -13,6 +13,7 @@
const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT;
const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;

Просмотреть файл

@ -49,6 +49,7 @@ _TEST_FILES =\
test_aria.html \
test_aria_imgmap.html \
test_aria_tabs.html \
test_buttons.html \
test_doc.html \
test_docarticle.html \
test_editablebody.html \

Просмотреть файл

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML button accessible states</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript">
function doTest()
{
// Default state.
testStates("f1_image", STATE_DEFAULT);
testStates("f2_submit", STATE_DEFAULT);
testStates("f3_submitbutton", STATE_DEFAULT);
testStates("f4_button", 0, 0, STATE_DEFAULT);
testStates("f4_image1", STATE_DEFAULT);
testStates("f4_image2", 0, 0, STATE_DEFAULT);
testStates("f4_submit", 0, 0, STATE_DEFAULT);
testStates("f4_submitbutton", 0, 0, STATE_DEFAULT);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=664142"
title="DEFAULT state exposed incorrectly for HTML">
Mozilla Bug 664142
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<p>A form with an image button</p>
<form name="form1" method="get">
<input type="text" name="hi">
<input id="f1_image" type="image" value="image-button">
</form>
<p>A form with a submit button:</p>
<form name="form2" method="get">
<input type="text" name="hi">
<input id="f2_submit" type="submit">
</form>
<p>A form with a HTML4 submit button:</p>
<form name="form3" method="get">
<input type="text" name="hi">
<button id="f3_submitbutton" type="submit">submit</button>
</form>
<p>A form with normal button, two image buttons, submit button,
HTML4 submit button:</p>
<form name="form4" method="get">
<input type="text" name="hi">
<input id="f4_button" type="button" value="normal" name="normal-button">
<input id="f4_image1" type="image" value="image-button1" name="image-button1">
<input id="f4_image2" type="image" value="image-button2" name="image-button2">
<input id="f4_submit" type="submit" value="real-submit" name="real-submit">
<button id="f4_submitbutton" type="submit">submit</button>
</form>
</body>
</html>

Просмотреть файл

@ -763,15 +763,21 @@ BrowserGlue.prototype = {
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
const PREF_TELEMETRY_INFOURL = "toolkit.telemetry.infoURL";
const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
// This is used to reprompt users when privacy message changes
const TELEMETRY_PROMPT_REV = 2;
var telemetryPrompted = null;
try {
// If the user hasn't already been prompted, ask if they want to
// send telemetry data.
if (Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED) ||
Services.prefs.getBoolPref(PREF_TELEMETRY_PROMPTED))
return;
telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
} catch(e) {}
// If the user has seen the latest telemetry prompt, do not prompt again
// else clear old prefs and reprompt
if (telemetryPrompted === TELEMETRY_PROMPT_REV)
return;
Services.prefs.clearUserPref(PREF_TELEMETRY_PROMPTED);
Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED);
// Stick the notification onto the selected tab of the active browser window.
var win = this.getMostRecentBrowserWindow();
var browser = win.gBrowser; // for closure in notification bar callback
@ -802,7 +808,7 @@ BrowserGlue.prototype = {
];
// Set pref to indicate we've shown the notification.
Services.prefs.setBoolPref(PREF_TELEMETRY_PROMPTED, true);
Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit

Просмотреть файл

@ -42,6 +42,7 @@ interface nsIChannel;
interface nsIStreamListener;
interface nsIURI;
interface nsIDocument;
interface nsIFrame;
/**
* This interface represents a content node that loads images. The interface
@ -65,7 +66,7 @@ interface nsIDocument;
* sufficient, when combined with the imageBlockingStatus information.)
*/
[scriptable, uuid(95c74255-df9a-4060-b5a0-0d111fcafe08)]
[scriptable, uuid(f7debb84-2854-4731-a57b-1bd752ad71f8)]
interface nsIImageLoadingContent : imgIDecoderObserver
{
/**
@ -128,6 +129,18 @@ interface nsIImageLoadingContent : imgIDecoderObserver
*/
imgIRequest getRequest(in long aRequestType);
/**
* Used to notify the image loading content node that a frame has been
* created.
*/
[notxpcom] void frameCreated(in nsIFrame aFrame);
/**
* Used to notify the image loading content node that a frame has been
* destroyed.
*/
[notxpcom] void frameDestroyed(in nsIFrame aFrame);
/**
* Used to find out what type of request one is dealing with (eg
* which request got passed through to the imgIDecoderObserver

Просмотреть файл

@ -3220,6 +3220,7 @@ nsDocument::DeleteShell()
if (IsEventHandlingEnabled()) {
RevokeAnimationFrameNotifications();
}
mPresShell = nsnull;
}

Просмотреть файл

@ -72,6 +72,7 @@
#include "nsIDOMNode.h"
#include "nsContentUtils.h"
#include "nsLayoutUtils.h"
#include "nsIContentPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsEventDispatcher.h"
@ -116,7 +117,9 @@ nsImageLoadingContent::nsImageLoadingContent()
mNewRequestsWillNeedAnimationReset(PR_FALSE),
mPendingRequestNeedsResetAnimation(PR_FALSE),
mCurrentRequestNeedsResetAnimation(PR_FALSE),
mStateChangerDepth(0)
mStateChangerDepth(0),
mCurrentRequestRegistered(false),
mPendingRequestRegistered(false)
{
if (!nsContentUtils::GetImgLoader()) {
mLoadingEnabled = PR_FALSE;
@ -330,6 +333,13 @@ nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
nsIPresShell* shell = doc ? doc->GetShell() : nsnull;
if (shell) {
// Make sure that our image requests are deregistered from the refresh
// driver if they aren't animated. Note that this must be mCurrentRequest,
// or we would have aborted up above.
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(GetFramePresContext(),
mCurrentRequest,
&mCurrentRequestRegistered);
// We need to figure out whether to kick off decoding
bool doRequestDecode = false;
@ -502,6 +512,44 @@ nsImageLoadingContent::GetRequest(PRInt32 aRequestType,
return NS_OK;
}
NS_IMETHODIMP_(void)
nsImageLoadingContent::FrameCreated(nsIFrame* aFrame)
{
NS_ASSERTION(aFrame, "aFrame is null");
// We need to make sure that our image request is registered.
nsPresContext* presContext = aFrame->PresContext();
if (mCurrentRequest) {
nsLayoutUtils::RegisterImageRequest(presContext, mCurrentRequest,
&mCurrentRequestRegistered);
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext,
mCurrentRequest,
&mCurrentRequestRegistered);
} else if (mPendingRequest) {
// We don't need to do the same check for animation, because this will be
// done when decoding is finished.
nsLayoutUtils::RegisterImageRequest(presContext, mPendingRequest,
&mPendingRequestRegistered);
}
}
NS_IMETHODIMP_(void)
nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame)
{
NS_ASSERTION(aFrame, "aFrame is null");
// We need to make sure that our image request is deregistered.
if (mCurrentRequest) {
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
mCurrentRequest,
&mCurrentRequestRegistered);
} else if (mPendingRequest) {
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(),
mPendingRequest,
&mPendingRequestRegistered);
}
}
NS_IMETHODIMP
nsImageLoadingContent::GetRequestType(imgIRequest* aRequest,
@ -868,6 +916,23 @@ nsImageLoadingContent::GetOurDocument()
return thisContent->GetOwnerDoc();
}
nsIFrame*
nsImageLoadingContent::GetOurPrimaryFrame()
{
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(this);
return thisContent->GetPrimaryFrame();
}
nsPresContext* nsImageLoadingContent::GetFramePresContext()
{
nsIFrame* frame = GetOurPrimaryFrame();
if (!frame) {
return nsnull;
}
return frame->PresContext();
}
nsresult
nsImageLoadingContent::StringToURI(const nsAString& aSpec,
nsIDocument* aDocument,
@ -987,6 +1052,11 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason)
NS_ABORT_IF_FALSE(!mCurrentURI,
"Shouldn't have both mCurrentRequest and mCurrentURI!");
// Deregister this image from the refresh driver so it no longer receives
// notifications.
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mCurrentRequest,
&mCurrentRequestRegistered);
// Clean up the request.
UntrackImage(mCurrentRequest);
mCurrentRequest->CancelAndForgetObserver(aReason);
@ -1010,12 +1080,29 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason)
nsCxPusher pusher;
pusher.PushNull();
// Deregister this image from the refresh driver so it no longer receives
// notifications.
nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest,
&mPendingRequestRegistered);
UntrackImage(mPendingRequest);
mPendingRequest->CancelAndForgetObserver(aReason);
mPendingRequest = nsnull;
mPendingRequestNeedsResetAnimation = PR_FALSE;
}
bool*
nsImageLoadingContent::GetRegisteredFlagForRequest(imgIRequest* aRequest)
{
if (aRequest == mCurrentRequest) {
return &mCurrentRequestRegistered;
} else if (aRequest == mPendingRequest) {
return &mPendingRequestRegistered;
} else {
return nsnull;
}
}
bool
nsImageLoadingContent::HaveSize(imgIRequest *aImage)
{

Просмотреть файл

@ -146,6 +146,24 @@ protected:
*/
nsIDocument* GetOurDocument();
/**
* Helper function to get the frame associated with this content. Not named
* GetPrimaryFrame to prevent ambiguous method names in subclasses.
*
* @return The frame which we belong to, or nsnull if it doesn't exist.
*/
nsIFrame* GetOurPrimaryFrame();
/**
* Helper function to get the PresContext associated with this content's
* frame. Not named GetPresContext to prevent ambiguous method names in
* subclasses.
*
* @return The nsPresContext associated with our frame, or nsnull if either
* the frame doesn't exist, or the frame's prescontext doesn't exist.
*/
nsPresContext* GetFramePresContext();
/**
* CancelImageRequests is called by subclasses when they want to
* cancel all image requests (for example when the subclass is
@ -302,6 +320,16 @@ protected:
void ClearCurrentRequest(nsresult aReason);
void ClearPendingRequest(nsresult aReason);
/**
* Retrieve a pointer to the 'registered with the refresh driver' flag for
* which a particular image request corresponds.
*
* @returns A pointer to the boolean flag for a given image request, or
* |nsnull| if the request is not either |mPendingRequest| or
* |mCurrentRequest|.
*/
bool* GetRegisteredFlagForRequest(imgIRequest* aRequest);
/**
* Static helper method to tell us if we have the size of a request. The
* image may be null.
@ -382,6 +410,11 @@ private:
/* The number of nested AutoStateChangers currently tracking our state. */
PRUint8 mStateChangerDepth;
// Flags to indicate whether each of the current and pending requests are
// registered with the refresh driver.
bool mCurrentRequestRegistered;
bool mPendingRequestRegistered;
};
#endif // nsImageLoadingContent_h__

Просмотреть файл

@ -77,7 +77,7 @@ class WebGLRenderbuffer;
class WebGLUniformLocation;
class WebGLExtension;
class WebGLZeroingObject;
template<int PreallocatedOwnersCapacity> class WebGLZeroingObject;
class WebGLContextBoundObject;
enum FakeBlackStatus { DoNotNeedFakeBlack, DoNeedFakeBlack, DontKnowIfNeedFakeBlack };
@ -109,6 +109,7 @@ inline bool is_pot_assuming_nonnegative(WebGLsizei x)
class WebGLObjectBaseRefPtr
{
protected:
template<int PreallocatedOwnersCapacity>
friend class WebGLZeroingObject;
WebGLObjectBaseRefPtr()
@ -681,6 +682,12 @@ public:
// by WebGLObjectRefPtr to tell the object who holds references, so that
// we can zero them out appropriately when the object is deleted, because
// it will be unbound in the GL.
//
// PreallocatedOwnersCapacity is the preallocated capacity for the array of refptrs to owners.
// Having some minimal preallocated capacity is an important optimization, see bug 522193. In this
// bug, a benchmark was using WebGLBuffer with a number of owners oscillating between 0 and 2.
// At this time mRefOwners was a nsTArray, and the too frequent reallocations were slowing us down.
template<int PreallocatedOwnersCapacity>
class WebGLZeroingObject
{
public:
@ -706,7 +713,7 @@ public:
}
protected:
nsTArray<WebGLObjectBaseRefPtr *> mRefOwners;
nsAutoTArray<WebGLObjectBaseRefPtr *, PreallocatedOwnersCapacity> mRefOwners;
};
// this class is a mixin for GL objects that have dimensions
@ -773,7 +780,7 @@ protected:
{0xd69f22e9, 0x6f98, 0x48bd, {0xb6, 0x94, 0x34, 0x17, 0xed, 0x06, 0x11, 0xab}}
class WebGLBuffer :
public nsIWebGLBuffer,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // almost never has more than 8 owners
public WebGLContextBoundObject
{
public:
@ -909,7 +916,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLBuffer, WEBGLBUFFER_PRIVATE_IID)
{0x4c19f189, 0x1f86, 0x4e61, {0x96, 0x21, 0x0a, 0x11, 0xda, 0x28, 0x10, 0xdd}}
class WebGLTexture :
public nsIWebGLTexture,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // almost never has more than 8 owners
public WebGLContextBoundObject
{
public:
@ -1348,7 +1355,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLTexture, WEBGLTEXTURE_PRIVATE_IID)
{0x48cce975, 0xd459, 0x4689, {0x83, 0x82, 0x37, 0x82, 0x6e, 0xac, 0xe0, 0xa7}}
class WebGLShader :
public nsIWebGLShader,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // almost never has more than 8 owners
public WebGLContextBoundObject
{
public:
@ -1414,7 +1421,9 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLShader, WEBGLSHADER_PRIVATE_IID)
{0xb3084a5b, 0xa5b4, 0x4ee0, {0xa0, 0xf0, 0xfb, 0xdd, 0x64, 0xaf, 0x8e, 0x82}}
class WebGLProgram :
public nsIWebGLProgram,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // can actually have many more owners (WebGLUniformLocations),
// but that shouldn't be performance-critical as references to the uniformlocations are stored
// in mMapUniformLocations, limiting the churning
public WebGLContextBoundObject
{
public:
@ -1541,7 +1550,7 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLProgram, WEBGLPROGRAM_PRIVATE_IID)
{0x3cbc2067, 0x5831, 0x4e3f, {0xac, 0x52, 0x7e, 0xf4, 0x5c, 0x04, 0xff, 0xae}}
class WebGLRenderbuffer :
public nsIWebGLRenderbuffer,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // almost never has more than 8 owners
public WebGLRectangleObject,
public WebGLContextBoundObject
{
@ -1712,7 +1721,7 @@ public:
{0x0052a16f, 0x4bc9, 0x4a55, {0x9d, 0xa3, 0x54, 0x95, 0xaa, 0x4e, 0x80, 0xb9}}
class WebGLFramebuffer :
public nsIWebGLFramebuffer,
public WebGLZeroingObject,
public WebGLZeroingObject<8>, // almost never has more than 8 owners
public WebGLContextBoundObject
{
public:
@ -1987,7 +1996,9 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLFramebuffer, WEBGLFRAMEBUFFER_PRIVATE_IID)
{0x01a8a614, 0xb109, 0x42f1, {0xb4, 0x40, 0x8d, 0x8b, 0x87, 0x0b, 0x43, 0xa7}}
class WebGLUniformLocation :
public nsIWebGLUniformLocation,
public WebGLZeroingObject,
public WebGLZeroingObject<2>, // never saw a WebGLUniformLocation have more than 2 owners, and since these
// are small objects and there are many of them, it's worth saving some memory
// by using a small value such as 2 here.
public WebGLContextBoundObject
{
public:
@ -2054,7 +2065,9 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WebGLActiveInfo, WEBGLACTIVEINFO_PRIVATE_IID)
class WebGLExtension :
public nsIWebGLExtension,
public WebGLContextBoundObject,
public WebGLZeroingObject
public WebGLZeroingObject<2> // WebGLExtensions probably won't have many owers and
// can be very small objects. Also, we have a static array of those. So, saving some memory
// by using a small value such as 2 here.
{
public:
WebGLExtension(WebGLContext *baseContext)

Просмотреть файл

@ -852,7 +852,7 @@ nsCanvasRenderingContext2D::Reset()
// only do this for non-docshell created contexts,
// since those are the ones that we created a surface for
if (mValid && !mDocShell)
if (mValid && !mDocShell && mSurface)
gCanvasMemoryUsed -= mWidth * mHeight * 4;
mSurface = nsnull;

Просмотреть файл

@ -198,7 +198,7 @@ nsXMLPrettyPrinter::MaybeUnhook(nsIContent* aContent)
{
// If there either aContent is null (the document-node was modified) or
// there isn't a binding parent we know it's non-anonymous content.
if (!aContent || !aContent->GetBindingParent() && !mUnhookPending) {
if ((!aContent || !aContent->GetBindingParent()) && !mUnhookPending) {
// Can't blindly to mUnhookPending after AddScriptRunner,
// since AddScriptRunner _could_ in theory run us
// synchronously

Просмотреть файл

@ -54,6 +54,7 @@ _TEST_FILES = test_bug232004.xhtml \
test_bug293347.html \
file_bug293347.xml \
file_bug293347xslt.xml \
test_bug691215.html \
$(NULL)
libs:: $(_TEST_FILES)

Просмотреть файл

@ -0,0 +1,52 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=691215
-->
<head>
<title>Test for Bug 691215</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=691215">Mozilla Bug 691215</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 691215 **/
SimpleTest.waitForExplicitFinish();
var url = "data:text/xml,<root/>";
var w;
addLoadEvent(function() {
// Need a separate window because we do not prettyprint in iframes. This is
// why this test can't be a crashtest.
w = window.open(url);
// Need to poll for load completion, sadly
setTimeout(checker, 0);
});
function checker() {
if (w.location.href != url ||
w.document.readyState != "complete") {
setTimeout(checker, 0);
return;
}
var doc = w.document;
var n = doc.createElement("span");
doc.replaceChild(n, doc.documentElement);
w.close();
ok(1, "Hey, we got here, that's good");
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -10450,9 +10450,9 @@ nsDocShell::SetHistoryEntry(nsCOMPtr<nsISHEntry> *aPtr, nsISHEntry *aEntry)
// SetChildHistoryEntry() does SwapHistoryEntries() (bug 304639).
nsCOMPtr<nsISHEntry> oldRootEntry = GetRootSHEntry(*aPtr);
if (oldRootEntry) {
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
GetSameTypeParent(getter_AddRefs(parentAsItem));
nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(parentAsItem);
nsCOMPtr<nsIDocShellTreeItem> rootAsItem;
GetSameTypeRootTreeItem(getter_AddRefs(rootAsItem));
nsCOMPtr<nsIDocShell> rootShell = do_QueryInterface(rootAsItem);
if (rootShell) { // if we're the root just set it, nothing to swap
SwapEntriesData data = { this, newRootEntry };
nsIDocShell *rootIDocShell =

Просмотреть файл

@ -798,13 +798,7 @@ nsSHistory::GetCanGoBack(bool * aCanGoBack)
NS_ENSURE_ARG_POINTER(aCanGoBack);
*aCanGoBack = PR_FALSE;
// If there is already a pending navigation, we cannot go back.
PRInt32 index = -1;
NS_ENSURE_SUCCESS(GetRequestedIndex(&index), NS_ERROR_FAILURE);
if(index != -1)
return NS_OK;
NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
if(index > 0)
*aCanGoBack = PR_TRUE;
@ -818,15 +812,9 @@ nsSHistory::GetCanGoForward(bool * aCanGoForward)
NS_ENSURE_ARG_POINTER(aCanGoForward);
*aCanGoForward = PR_FALSE;
// If there is already a pending navigation, we cannot go forward.
PRInt32 index = -1;
PRInt32 count = -1;
NS_ENSURE_SUCCESS(GetRequestedIndex(&index), NS_ERROR_FAILURE);
if(index != -1)
return NS_OK;
NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
NS_ENSURE_SUCCESS(GetCount(&count), NS_ERROR_FAILURE);

Просмотреть файл

@ -461,13 +461,16 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
SetTimeoutTimer();
}
void
bool
nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
{
if (!mAllowed)
return false;
nsCOMPtr<nsIRunnable> ev =
new RequestSendLocationEvent(aPosition, this,
mIsWatchPositionRequest ? nsnull : mLocator);
NS_DispatchToMainThread(ev);
return true;
}
void
@ -901,10 +904,10 @@ nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere)
if (!WindowOwnerStillExists())
return Shutdown();
for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++) {
mPendingCallbacks[i]->Update(aSomewhere);
for (PRUint32 i = mPendingCallbacks.Length(); i> 0; i--) {
if (mPendingCallbacks[i-1]->Update(aSomewhere))
mPendingCallbacks.RemoveElementAt(i-1);
}
mPendingCallbacks.Clear();
// notify everyone that is watching
for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++) {

Просмотреть файл

@ -91,7 +91,7 @@ class nsGeolocationRequest
void Shutdown();
// Called by the geolocation device to notify that a location has changed.
void Update(nsIDOMGeoPosition* aPosition);
bool Update(nsIDOMGeoPosition* aPosition);
void SendLocation(nsIDOMGeoPosition* location);
void MarkCleared();

Просмотреть файл

@ -16,7 +16,6 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<uses-permission android:name="android.permission.READ_LOGS"/>

Просмотреть файл

@ -62,7 +62,6 @@ import android.util.*;
import android.net.*;
import android.database.*;
import android.provider.*;
import android.telephony.*;
import android.content.pm.*;
import android.content.pm.PackageManager.*;
import dalvik.system.*;
@ -87,7 +86,6 @@ abstract public class GeckoApp
public Handler mMainHandler;
private IntentFilter mConnectivityFilter;
private BroadcastReceiver mConnectivityReceiver;
private PhoneStateListener mPhoneStateListener;
enum LaunchState {PreLaunch, Launching, WaitButton,
Launched, GeckoRunning, GeckoExiting};
@ -398,8 +396,6 @@ abstract public class GeckoApp
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mConnectivityReceiver = new GeckoConnectivityReceiver();
mPhoneStateListener = new GeckoPhoneStateListener();
if (!checkAndSetLaunchState(LaunchState.PreLaunch,
LaunchState.Launching))
return;
@ -485,10 +481,6 @@ abstract public class GeckoApp
super.onPause();
unregisterReceiver(mConnectivityReceiver);
TelephonyManager tm = (TelephonyManager)
GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
@Override
@ -507,13 +499,6 @@ abstract public class GeckoApp
onNewIntent(getIntent());
registerReceiver(mConnectivityReceiver, mConnectivityFilter);
TelephonyManager tm = (TelephonyManager)
GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
// Notify if network state changed since we paused
GeckoAppShell.onNetworkStateChange(true);
}
@Override

Просмотреть файл

@ -58,7 +58,6 @@ import android.graphics.*;
import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.telephony.*;
import android.webkit.MimeTypeMap;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
@ -96,10 +95,6 @@ public class GeckoAppShell
static private File sCacheFile = null;
static private int sFreeSpace = -1;
static private String sNetworkState = "unknown";
static private String sNetworkType = "unknown";
static private int sNetworkTypeCode = 0;
/* The Android-side API: API methods that Android calls */
// Initialization methods
@ -114,7 +109,7 @@ public class GeckoAppShell
public static native void callObserver(String observerKey, String topic, String data);
public static native void removeObserver(String observerKey);
public static native void loadLibs(String apkName, boolean shouldExtract);
public static native void onChangeNetworkLinkStatus(String status, String type);
public static native void onChangeNetworkLinkStatus(String status);
public static native void reportJavaCrash(String stack);
public static native void processNextNativeEvent();
@ -681,9 +676,6 @@ public class GeckoAppShell
// mLaunchState can only be Launched at this point
GeckoApp.setLaunchState(GeckoApp.LaunchState.GeckoRunning);
sendPendingEventsToGecko();
// Refresh the network connectivity state
onNetworkStateChange(false);
}
static void onXreExit() {
@ -1067,92 +1059,20 @@ public class GeckoAppShell
}
public static boolean isNetworkLinkUp() {
if (sNetworkState == "up")
return true;
return false;
}
public static boolean isNetworkLinkKnown() {
if (sNetworkState == "unknown")
ConnectivityManager cm = (ConnectivityManager)
GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info == null || !info.isConnected())
return false;
return true;
}
public static int getNetworkLinkType() {
return sNetworkTypeCode;
}
public static void onNetworkStateChange(boolean notifyChanged) {
String state;
String type;
int typeCode;
public static boolean isNetworkLinkKnown() {
ConnectivityManager cm = (ConnectivityManager)
GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
// Note, these strings and codes correspond to those specified in
// nsINetworkLinkService. Make sure to keep them in sync!
type = "unknown";
typeCode = 0;
if (info == null) {
state = "unknown";
} else if (!info.isConnected()) {
state = "down";
} else {
state = "up";
int androidType = info.getType();
if (androidType == ConnectivityManager.TYPE_WIFI) {
type = "wifi";
typeCode = 3;
} else if (androidType == ConnectivityManager.TYPE_WIMAX) {
type = "wimax";
typeCode = 4;
} else if (androidType == ConnectivityManager.TYPE_MOBILE) {
TelephonyManager tm = (TelephonyManager)
GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
typeCode = tm.getNetworkType();
// Note that the value of some of these constants are used due
// to not all of these existing in API level 8.
//
// In particular, EVDO_B appears at level 9, and EHRPD and LTE
// appear at level 11.
if (androidType == TelephonyManager.NETWORK_TYPE_GPRS ||
androidType == TelephonyManager.NETWORK_TYPE_EDGE ||
androidType == TelephonyManager.NETWORK_TYPE_CDMA ||
androidType == TelephonyManager.NETWORK_TYPE_IDEN ||
androidType == TelephonyManager.NETWORK_TYPE_1xRTT) {
type = "2g";
typeCode = 5;
} else if (androidType == TelephonyManager.NETWORK_TYPE_UMTS ||
androidType == TelephonyManager.NETWORK_TYPE_HSDPA ||
androidType == TelephonyManager.NETWORK_TYPE_HSUPA ||
androidType == TelephonyManager.NETWORK_TYPE_HSPA ||
androidType == TelephonyManager.NETWORK_TYPE_EVDO_0 ||
androidType == TelephonyManager.NETWORK_TYPE_EVDO_A ||
androidType == 12 || // TelephonyManager.NETWORK_TYPE_EVDO_B
androidType == 14) { // TelephonyManager.NETWORK_TYPE_EHRPD
type = "3g";
typeCode = 6;
} else if (androidType == 13) { // TelephonyManager.NETWORK_TYPE_LTE
type = "4g";
typeCode = 7;
}
}
}
// If the network state has changed, notify Gecko
if (notifyChanged && (state != sNetworkState || typeCode != sNetworkTypeCode)) {
Log.i(LOG_FILE_NAME, "Network state changed: (" + state + ", " + type + ") ");
sNetworkState = state;
sNetworkType = type;
sNetworkTypeCode = typeCode;
if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
onChangeNetworkLinkStatus(sNetworkState, sNetworkType);
}
if (cm.getActiveNetworkInfo() == null)
return false;
return true;
}
public static void setSelectedLocale(String localeCode) {

Просмотреть файл

@ -38,12 +38,25 @@
package org.mozilla.gecko;
import android.content.*;
import android.net.*;
public class GeckoConnectivityReceiver
extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
GeckoAppShell.onNetworkStateChange(true);
String status;
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info == null)
status = "unknown";
else if (!info.isConnected())
status = "down";
else
status = "up";
if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
GeckoAppShell.onChangeNetworkLinkStatus(status);
}
}

Просмотреть файл

@ -52,7 +52,6 @@ JAVAFILES = \
GeckoEvent.java \
GeckoSurfaceView.java \
GeckoInputConnection.java \
GeckoPhoneStateListener.java \
AlertNotification.java \
SurfaceLockInfo.java \
$(NULL)

Просмотреть файл

@ -128,6 +128,10 @@ CSRCS = \
DEFINES += -DANGLE_USE_NSPR -DANGLE_BUILD
#these defines are from ANGLE's build_angle.gyp
DEFINES += -DANGLE_DISABLE_TRACE
DEFINES += -DANGLE_COMPILE_OPTIMIZATION_LEVEL=D3DCOMPILE_OPTIMIZATION_LEVEL0
EXTRA_DSO_LDOPTS = $(MOZALLOC_LIB)
ifdef MOZ_ANGLE

Просмотреть файл

@ -131,6 +131,10 @@ CSRCS = \
DEFINES += -DANGLE_BUILD -DNOMINMAX -DLIBEGL_EXPORTS -D_CRT_SECURE_NO_DEPRECATE
#these defines are from ANGLE's build_angle.gyp
DEFINES += -DANGLE_DISABLE_TRACE
DEFINES += -DANGLE_COMPILE_OPTIMIZATION_LEVEL=D3DCOMPILE_OPTIMIZATION_LEVEL0
ifndef MOZ_DEBUG
DEFINES += -D_SECURE_SCL=0
endif

Просмотреть файл

@ -131,6 +131,10 @@ CSRCS = \
DEFINES += -DANGLE_BUILD -DNOMINMAX -DLIBGLESV2_EXPORTS -D_CRT_SECURE_NO_DEPRECATE
#these defines are from ANGLE's build_angle.gyp
DEFINES += -DANGLE_DISABLE_TRACE
DEFINES += -DANGLE_COMPILE_OPTIMIZATION_LEVEL=D3DCOMPILE_OPTIMIZATION_LEVEL0
ifndef MOZ_DEBUG
DEFINES += -D_SECURE_SCL=0
endif

Просмотреть файл

@ -65,7 +65,7 @@ AlignPtr(void *orig)
tl::FloorLog2<LIFO_ALLOC_ALIGN>::result == tl::CeilingLog2<LIFO_ALLOC_ALIGN>::result
>::result _;
char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & -LIFO_ALLOC_ALIGN);
char *result = (char *) ((uintptr_t(orig) + (LIFO_ALLOC_ALIGN - 1)) & (~LIFO_ALLOC_ALIGN + 1));
JS_ASSERT(uintptr_t(result) % LIFO_ALLOC_ALIGN == 0);
return result;
}

Просмотреть файл

@ -1095,6 +1095,9 @@ class ScriptAnalysis
return getValueTypes(poppedValue(pc, which));
}
/* Whether an arithmetic operation is operating on integers, with an integer result. */
bool integerOperation(JSContext *cx, jsbytecode *pc);
bool trackUseChain(const SSAValue &v) {
JS_ASSERT_IF(v.kind() == SSAValue::VAR, trackSlot(v.varSlot()));
return v.kind() != SSAValue::EMPTY &&

Просмотреть файл

@ -4273,6 +4273,48 @@ ScriptAnalysis::followEscapingArguments(JSContext *cx, SSAUseChain *use, Vector<
return false;
}
bool
ScriptAnalysis::integerOperation(JSContext *cx, jsbytecode *pc)
{
JS_ASSERT(uint32(pc - script->code) < script->length);
switch (JSOp(*pc)) {
case JSOP_INCARG:
case JSOP_DECARG:
case JSOP_ARGINC:
case JSOP_ARGDEC:
case JSOP_INCLOCAL:
case JSOP_DECLOCAL:
case JSOP_LOCALINC:
case JSOP_LOCALDEC: {
if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
return false;
uint32 slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot)) {
if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
return false;
}
return true;
}
case JSOP_ADD:
case JSOP_SUB:
case JSOP_MUL:
case JSOP_DIV:
if (pushedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
return false;
if (poppedTypes(pc, 0)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
return false;
if (poppedTypes(pc, 1)->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
return false;
return true;
default:
return true;
}
}
/*
* Persistent constraint clearing out newScript and definite properties from
* an object should a property on another object get a setter.

Просмотреть файл

@ -2216,8 +2216,10 @@ mjit::Compiler::jsop_stricteq(JSOp op)
if (frame.haveSameBacking(lhs, rhs)) {
/* False iff NaN. */
frame.pop();
if (lhs->isTypeKnown() && lhs->isNotType(JSVAL_TYPE_DOUBLE)) {
frame.popn(2);
frame.pop();
frame.push(BooleanValue(op == JSOP_STRICTEQ));
return;
}
@ -2256,7 +2258,7 @@ mjit::Compiler::jsop_stricteq(JSOp op)
#endif
frame.freeReg(treg);
frame.popn(2);
frame.pop();
frame.pushTypedPayload(JSVAL_TYPE_BOOLEAN, result);
return;
}

Просмотреть файл

@ -989,6 +989,9 @@ LoopState::cannotIntegerOverflow(const CrossSSAValue &pushed)
jsbytecode *PC = ssa->getFrame(pushed.frame).script->code + pushed.v.pushedOffset();
ScriptAnalysis *analysis = ssa->getFrame(pushed.frame).script->analysis();
if (!analysis->integerOperation(cx, PC))
return false;
uint32 baseSlot = UNASSIGNED;
int32 baseConstant = 0;
JSOp op = JSOp(*PC);
@ -1531,6 +1534,8 @@ LoopState::getLoopTestAccess(const SSAValue &v, uint32 *pslot, int32 *pconstant)
case JSOP_DECARG:
case JSOP_ARGINC:
case JSOP_ARGDEC: {
if (!outerAnalysis->integerOperation(cx, pc))
return false;
uint32 slot = GetBytecodeSlot(outerScript, pc);
if (outerAnalysis->slotEscapes(slot))
return false;
@ -1668,11 +1673,11 @@ LoopState::analyzeLoopIncrements()
if (offset == uint32(-1) || offset < lifetime->lastBlock)
continue;
JSOp op = JSOp(outerScript->code[offset]);
jsbytecode *pc = outerScript->code + offset;
JSOp op = JSOp(*pc);
const JSCodeSpec *cs = &js_CodeSpec[op];
if (cs->format & (JOF_INC | JOF_DEC)) {
TypeSet *types = outerAnalysis->pushedTypes(offset);
if (types->getKnownTypeTag(cx) != JSVAL_TYPE_INT32)
if (!outerAnalysis->integerOperation(cx, pc))
continue;
Increment inc;
@ -2137,7 +2142,7 @@ LoopState::getEntryValue(const CrossSSAValue &iv, uint32 *pslot, int32 *pconstan
case JSOP_GETARG:
case JSOP_ARGINC:
case JSOP_INCARG: {
if (cv.frame != CrossScriptSSA::OUTER_FRAME)
if (cv.frame != CrossScriptSSA::OUTER_FRAME || !analysis->integerOperation(cx, pc))
return false;
uint32 slot = GetBytecodeSlot(outerScript, pc);
if (outerAnalysis->slotEscapes(slot))

Просмотреть файл

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<script>
function boom()
{
document.getElementById("s").appendChild(document.createElement("div"));
var marq = document.getElementById("f").contentDocument.documentElement;
marq.behavior = "alternate";
}
</script>
</head>
<body onload="boom();"><span id="s"></span><iframe src="data:text/xml,%3Cmarquee%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxhtml%22%3EX%3C%2Fmarquee%3E" id="f"></iframe></body>
</html>

Просмотреть файл

@ -321,6 +321,7 @@ load 579655.html
load 580494-1.html
load 580834-1.xhtml
load 595039-1.html
load 597924-1.html
load 606432-1.html
load 609821-1.xhtml
load 615146-1.html

Просмотреть файл

@ -57,6 +57,7 @@
#include "nsStyleContext.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
// Paint forcing
#include "prenv.h"
@ -67,7 +68,8 @@ nsImageLoader::nsImageLoader(nsIFrame *aFrame, PRUint32 aActions,
nsImageLoader *aNextLoader)
: mFrame(aFrame),
mActions(aActions),
mNextLoader(aNextLoader)
mNextLoader(aNextLoader),
mRequestRegistered(false)
{
}
@ -105,12 +107,15 @@ nsImageLoader::Destroy()
todestroy->Destroy();
}
mFrame = nsnull;
if (mRequest) {
nsPresContext* presContext = mFrame->PresContext();
nsLayoutUtils::DeregisterImageRequest(presContext, mRequest,
&mRequestRegistered);
mRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
}
mFrame = nsnull;
mRequest = nsnull;
}
@ -269,3 +274,31 @@ nsImageLoader::DoRedraw(const nsRect* aDamageRect)
mFrame->Invalidate(bounds);
}
}
NS_IMETHODIMP
nsImageLoader::OnStartDecode(imgIRequest *aRequest)
{
// Register our image request with the refresh driver.
nsPresContext* presContext = mFrame->PresContext();
if (!presContext) {
return NS_OK;
}
nsLayoutUtils::RegisterImageRequest(presContext, aRequest,
&mRequestRegistered);
return NS_OK;
}
NS_IMETHODIMP
nsImageLoader::OnStopDecode(imgIRequest *aRequest, nsresult status,
const PRUnichar *statusArg)
{
// Deregister the imgIRequest with the refresh driver if the
// image is not animated.
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(mFrame->PresContext(),
mRequest,
&mRequestRegistered);
return NS_OK;
}

Просмотреть файл

@ -82,6 +82,10 @@ public:
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
nsresult status,
const PRUnichar *statusArg);
NS_IMETHOD OnStopFrame(imgIRequest *aRequest, PRUint32 aFrame);
NS_IMETHOD OnStopRequest(imgIRequest *aRequest, bool aLastPart);
// Do not override OnDataAvailable since background images are not
@ -109,4 +113,8 @@ private:
nsCOMPtr<imgIRequest> mRequest;
PRUint32 mActions;
nsRefPtr<nsImageLoader> mNextLoader;
// This is a boolean flag indicating whether or not the current image request
// has been registered with the refresh driver.
bool mRequestRegistered;
};

Просмотреть файл

@ -4292,6 +4292,102 @@ nsLayoutUtils::Shutdown()
}
}
/* static */
void
nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered)
{
if (!aPresContext) {
return;
}
if (aRequestRegistered && *aRequestRegistered) {
// Our request is already registered with the refresh driver, so
// no need to register it again.
return;
}
if (aRequest) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
if (image) {
if (!aPresContext->RefreshDriver()->AddImageRequest(aRequest)) {
NS_WARNING("Unable to add image request");
return;
}
if (aRequestRegistered) {
*aRequestRegistered = true;
}
}
}
}
/* static */
void
nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered)
{
if (!aPresContext) {
return;
}
// Deregister our imgIRequest with the refresh driver to
// complete tear-down, but only if it has been registered
if (aRequestRegistered && !*aRequestRegistered) {
return;
}
if (aRequest) {
nsCOMPtr<imgIContainer> image;
aRequest->GetImage(getter_AddRefs(image));
if (image) {
aPresContext->RefreshDriver()->RemoveImageRequest(aRequest);
if (aRequestRegistered) {
*aRequestRegistered = false;
}
}
}
}
/* static */
void
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered)
{
if (!aPresContext) {
return;
}
if (aRequestRegistered && !*aRequestRegistered) {
// Image request isn't registered with the refresh driver - no need
// to try and deregister it.
return;
}
// Deregister the imgIRequest with the refresh driver if the
// image is not animated
nsCOMPtr<imgIContainer> imageContainer;
if (aRequest) {
aRequest->GetImage(getter_AddRefs(imageContainer));
bool animated;
if (!imageContainer) {
return;
}
nsresult rv = imageContainer->GetAnimated(&animated);
if (NS_SUCCEEDED(rv) && !animated) {
nsLayoutUtils::DeregisterImageRequest(aPresContext, aRequest,
aRequestRegistered);
}
}
}
nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
const nsAString& aValue)
: mContent(aContent),

Просмотреть файл

@ -1440,6 +1440,63 @@ public:
static void Shutdown();
/**
* Register an imgIRequest object with a refresh driver.
*
* @param aPresContext The nsPresContext whose refresh driver we want to
* register with.
* @param aRequest A pointer to the imgIRequest object which the client wants
* to register with the refresh driver.
* @param aRequestRegistered A pointer to a boolean value which indicates
* whether the given image request is registered. If
* *aRequestRegistered is true, then this request will not be
* registered again. If the request is registered by this function,
* then *aRequestRegistered will be set to true upon the completion of
* this function.
*
*/
static void RegisterImageRequest(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered);
/**
* Deregister an imgIRequest object from a refresh driver.
*
* @param aPresContext The nsPresContext whose refresh driver we want to
* deregister from.
* @param aRequest A pointer to the imgIRequest object with which the client
* previously registered and now wants to deregister from the refresh
* driver.
* @param aRequestRegistered A pointer to a boolean value which indicates
* whether the given image request is registered. If
* *aRequestRegistered is false, then this request will not be
* deregistered. If the request is deregistered by this function,
* then *aRequestRegistered will be set to false upon the completion of
* this function.
*/
static void DeregisterImageRequest(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered);
/**
* Deregister an imgIRequest object from a refresh driver, if the
* imgIRequest object represents a static (i.e. not animated) image.
*
* @param aPresContext The nsPresContext whose refresh driver we want to
* deregister from.
* @param aRequest A pointer to the imgIRequest object with which the client
* previously registered and now wants to deregister from the refresh
* driver.
* @param aRequestRegistered A pointer to a boolean value which indicates
* whether the given image request is registered. If
* *aRequestRegistered is false, then this request will not be
* deregistered. If the request is deregistered by this function,
* then *aRequestRegistered will be set to false upon the completion of
* this function.
*/
static void DeregisterImageRequestIfNotAnimated(nsPresContext* aPresContext,
imgIRequest* aRequest,
bool* aRequestRegistered);
#ifdef DEBUG
/**
* Assert that there are no duplicate continuations of the same frame

Просмотреть файл

@ -112,6 +112,7 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
mTimerIsPrecise(false),
mLastTimerInterval(0)
{
mRequests.Init();
}
nsRefreshDriver::~nsRefreshDriver()
@ -183,6 +184,29 @@ nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver *aObserver,
return array.RemoveElement(aObserver);
}
bool
nsRefreshDriver::AddImageRequest(imgIRequest* aRequest)
{
if (!mRequests.PutEntry(aRequest)) {
return false;
}
EnsureTimerStarted(false);
return true;
}
void
nsRefreshDriver::RemoveImageRequest(imgIRequest* aRequest)
{
mRequests.RemoveEntry(aRequest);
}
void nsRefreshDriver::ClearAllImageRequests()
{
mRequests.Clear();
}
void
nsRefreshDriver::EnsureTimerStarted(bool aAdjustingTimer)
{
@ -236,6 +260,7 @@ nsRefreshDriver::ObserverCount() const
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mObservers); ++i) {
sum += mObservers[i].Length();
}
// Even while throttled, we need to process layout and style changes. Style
// changes can trigger transitions which fire events when they complete, and
// layout changes can affect media queries on child documents, triggering
@ -247,6 +272,12 @@ nsRefreshDriver::ObserverCount() const
return sum;
}
PRUint32
nsRefreshDriver::ImageRequestCount() const
{
return mRequests.Count();
}
void
nsRefreshDriver::UpdateMostRecentRefresh()
{
@ -301,7 +332,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
UpdateMostRecentRefresh();
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (!presShell || ObserverCount() == 0) {
if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0)) {
// Things are being destroyed, or we no longer have any observers.
// We don't want to stop the timer when observers are initially
// removed, because sometimes observers can be added and removed
@ -330,6 +361,7 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
return NS_OK;
}
}
if (i == 0) {
// Don't just loop while we have things in mBeforePaintTargets,
// the whole point is that event handlers should readd the
@ -402,6 +434,17 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
}
}
/*
* Perform notification to imgIRequests subscribed to listen
* for refresh events.
*/
ImageRequestParameters parms = {mMostRecentRefresh};
if (mRequests.Count()) {
mRequests.EnumerateEntries(nsRefreshDriver::ImageRequestEnumerator, &parms);
EnsureTimerStarted(false);
}
if (mThrottled ||
(mTimerIsPrecise !=
(GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {
@ -422,6 +465,24 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
return NS_OK;
}
PLDHashOperator
nsRefreshDriver::ImageRequestEnumerator(nsISupportsHashKey* aEntry,
void* aUserArg)
{
ImageRequestParameters* parms =
static_cast<ImageRequestParameters*> (aUserArg);
mozilla::TimeStamp mostRecentRefresh = parms->ts;
imgIRequest* req = static_cast<imgIRequest*>(aEntry->GetKey());
NS_ABORT_IF_FALSE(req, "Unable to retrieve the image request");
nsCOMPtr<imgIContainer> image;
req->GetImage(getter_AddRefs(image));
if (image) {
image->RequestRefresh(mostRecentRefresh);
}
return PL_DHASH_NEXT;
}
void
nsRefreshDriver::Freeze()
{
@ -435,7 +496,7 @@ nsRefreshDriver::Thaw()
{
NS_ASSERTION(mFrozen, "Thaw called on an unfrozen refresh driver");
mFrozen = false;
if (ObserverCount()) {
if (ObserverCount() || ImageRequestCount()) {
// FIXME: This isn't quite right, since our EnsureTimerStarted call
// updates our mMostRecentRefresh, but the DoRefresh call won't run
// and notify our observers until we get back to the event loop.

Просмотреть файл

@ -50,10 +50,13 @@
#include "nsTObserverArray.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
class nsPresContext;
class nsIPresShell;
class nsIDocument;
class imgIRequest;
/**
* An abstract base class to be implemented by callers wanting to be
@ -128,6 +131,24 @@ public:
bool RemoveRefreshObserver(nsARefreshObserver *aObserver,
mozFlushType aFlushType);
/**
* Add/Remove imgIRequest versions of observers.
*
* These are used for hooking into the refresh driver for
* controlling animated images.
*
* @note The refresh driver owns a reference to these listeners.
*
* @note Technically, imgIRequest objects are not nsARefreshObservers, but
* for controlling animated image repaint events, we subscribe the
* imgIRequests to the nsRefreshDriver for notification of paint events.
*
* @returns whether the operation succeeded, or void in the case of removal.
*/
bool AddImageRequest(imgIRequest* aRequest);
void RemoveImageRequest(imgIRequest* aRequest);
void ClearAllImageRequests();
/**
* Add / remove presshells that we should flush style and layout on
*/
@ -218,10 +239,15 @@ public:
private:
typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
typedef nsTHashtable<nsISupportsHashKey> RequestTable;
void EnsureTimerStarted(bool aAdjustingTimer);
void StopTimer();
PRUint32 ObserverCount() const;
PRUint32 ImageRequestCount() const;
static PLDHashOperator ImageRequestEnumerator(nsISupportsHashKey* aEntry,
void* aUserArg);
void UpdateMostRecentRefresh();
ObserverArray& ArrayFor(mozFlushType aFlushType);
// Trigger a refresh immediately, if haven't been disconnected or frozen.
@ -252,6 +278,8 @@ private:
// separate arrays for each flush type we support
ObserverArray mObservers[3];
RequestTable mRequests;
nsAutoTArray<nsIPresShell*, 16> mStyleFlushObservers;
nsAutoTArray<nsIPresShell*, 16> mLayoutFlushObservers;
// nsTArray on purpose, because we want to be able to swap.
@ -262,6 +290,11 @@ private:
// This is the last interval we used for our timer. May be 0 if we
// haven't computed a timer interval yet.
mutable PRInt32 mLastTimerInterval;
// Helper struct for processing image requests
struct ImageRequestParameters {
mozilla::TimeStamp ts;
};
};
#endif /* !defined(nsRefreshDriver_h_) */

Просмотреть файл

@ -78,6 +78,7 @@ public:
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
const nsIntRect *aRect);
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *aRequest, nsresult status,
const PRUnichar *statusArg);
// imgIContainerObserver (override nsStubImageDecoderObserver)
@ -101,6 +102,10 @@ nsBulletFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
// Stop image loading first
if (mImageRequest) {
// Deregister our image request from the refresh driver
nsLayoutUtils::DeregisterImageRequest(PresContext(),
mImageRequest,
&mRequestRegistered);
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
@ -170,6 +175,8 @@ nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
if (same) {
needNewRequest = PR_FALSE;
} else {
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
@ -178,10 +185,15 @@ nsBulletFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
if (needNewRequest) {
newRequest->Clone(mListener, getter_AddRefs(mImageRequest));
nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
}
} else {
// No image request on the new style context
if (mImageRequest) {
nsLayoutUtils::DeregisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
mImageRequest->Cancel(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
@ -1521,6 +1533,17 @@ NS_IMETHODIMP nsBulletFrame::OnDataAvailable(imgIRequest *aRequest,
return NS_OK;
}
NS_IMETHODIMP nsBulletFrame::OnStartDecode(imgIRequest* aRequest)
{
// Register the image request with the refresh driver.
if (aRequest == mImageRequest) {
nsLayoutUtils::RegisterImageRequest(PresContext(), mImageRequest,
&mRequestRegistered);
}
return NS_OK;
}
NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg)
@ -1537,6 +1560,14 @@ NS_IMETHODIMP nsBulletFrame::OnStopDecode(imgIRequest *aRequest,
}
#endif
// Deregister the imgIRequest with the refresh driver if the
// image is not animated.
if (aRequest == mImageRequest) {
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(PresContext(),
mImageRequest,
&mRequestRegistered);
}
return NS_OK;
}
@ -1634,17 +1665,25 @@ NS_IMETHODIMP nsBulletListener::OnDataAvailable(imgIRequest *aRequest,
const nsIntRect *aRect)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->OnDataAvailable(aRequest, aCurrentFrame, aRect);
}
NS_IMETHODIMP nsBulletListener::OnStartDecode(imgIRequest *aRequest)
{
if (!mFrame)
return NS_OK;
return mFrame->OnStartDecode(aRequest);
}
NS_IMETHODIMP nsBulletListener::OnStopDecode(imgIRequest *aRequest,
nsresult status,
const PRUnichar *statusArg)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->OnStopDecode(aRequest, status, statusArg);
}
@ -1653,7 +1692,7 @@ NS_IMETHODIMP nsBulletListener::FrameChanged(imgIContainer *aContainer,
const nsIntRect *aDirtyRect)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->FrameChanged(aContainer, aDirtyRect);
}

Просмотреть файл

@ -84,6 +84,7 @@ public:
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest,
bool aCurrentFrame,
const nsIntRect *aRect);
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg);
@ -121,6 +122,12 @@ protected:
nsSize mComputedSize;
PRInt32 mOrdinal;
bool mTextIsRTL;
private:
// This is a boolean flag indicating whether or not the current image request
// has been registered with the refresh driver.
bool mRequestRegistered;
};
#endif /* nsBulletFrame_h___ */

Просмотреть файл

@ -1529,13 +1529,12 @@ WrapPreserve3DListInternal(nsIFrame* aFrame, nsDisplayListBuilder *aBuilder, nsD
nsDisplayList temp;
while (nsDisplayItem *item = aList->RemoveBottom()) {
nsIFrame *childFrame = item->GetUnderlyingFrame();
NS_ASSERTION(childFrame, "All display items to be wrapped must have a frame!");
// We accumulate sequential items that aren't transforms into the 'temp' list
// and then flush this list into newList by wrapping the whole lot with a single
// nsDisplayTransform.
if (childFrame->GetParent()->Preserves3DChildren()) {
if (childFrame && childFrame->GetParent()->Preserves3DChildren()) {
switch (item->GetType()) {
case nsDisplayItem::TYPE_TRANSFORM: {
if (!temp.IsEmpty()) {
@ -1645,7 +1644,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
}
// Mark the display list items for absolutely positioned children
MarkAbsoluteFramesForDisplayList(aBuilder, aDirtyRect);
MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
nsDisplayListCollection set;
nsresult rv;

Просмотреть файл

@ -576,6 +576,10 @@ protected:
/**
* Implements Destroy(). Do not call this directly except from within a
* DestroyFrom() implementation.
*
* @note This will always be called, so it is not necessary to override
* Destroy() in subclasses of nsFrame, just DestroyFrom().
*
* @param aDestructRoot is the root of the subtree being destroyed
*/
virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;

Просмотреть файл

@ -223,6 +223,10 @@ nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
nsCxPusher pusher;
pusher.PushNull();
// Notify our image loading content that we are going away so it can
// deregister with our refresh driver.
imageLoader->FrameDestroyed(this);
imageLoader->RemoveObserver(mListener);
}
@ -268,6 +272,10 @@ nsImageFrame::Init(nsIContent* aContent,
if (!gIconLoad)
LoadIcons(aPresContext);
// We have a PresContext now, so we need to notify the image content node
// that it can register images.
imageLoader->FrameCreated(this);
// Give image loads associated with an image frame a small priority boost!
nsCOMPtr<imgIRequest> currentRequest;
imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,

Просмотреть файл

@ -0,0 +1,12 @@
<!DOCTYPE html>
<div>test</div>
<style>
body {
width: 100px; height: 300px;
position: absolute;
-moz-transform: scale(0.5);
}
div { position: absolute; }
</style>

Просмотреть файл

@ -1655,6 +1655,7 @@ HTTP(..) == 654057-1.html 654057-1-ref.html
fails-if(layersGPUAccelerated&&cocoaWidget) == 654950-1.html 654950-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
== 652775-1.html 652775-1-ref.html
== 655549-1.html 655549-1-ref.html
!= 656875.html about:blank
== 658952.html 658952-ref.html
== 664127-1.xul 664127-1-ref.xul
== 660682-1.html 660682-1-ref.html

Просмотреть файл

@ -1744,9 +1744,12 @@ Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
// Go through and deal with the whole linked list.
SheetLoadData* data = aLoadData;
while (data) {
NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
"should not get marked modified during parsing");
if (!data->mSheetAlreadyComplete) {
// If mSheetAlreadyComplete, then the sheet could well be modified between
// when we posted the async call to SheetComplete and now, since the sheet
// was page-accessible during that whole time.
NS_ABORT_IF_FALSE(!data->mSheet->IsModified(),
"should not get marked modified during parsing");
data->mSheet->SetComplete();
data->ScheduleLoadEventIfNeeded(aStatus);
}

Просмотреть файл

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html class="reftest-wait">
<link id="e" href="data:text/css,.ref { background-color: green; }" rel="stylesheet">
<script>
function boom()
{
document.documentElement.appendChild(document.getElementById("e"));
document.styleSheets[0].cssRules[0];
// Remove reftest-wait async so we give the SheetComplete a chance to run
setTimeout(function() { document.documentElement.className = ""; }, 0);
}
</script>
<body onload="boom();"></body>
</html>

Просмотреть файл

@ -72,3 +72,4 @@ load 611922-1.html
load 665209-1.html
asserts(2) load 671799-1.html
asserts(2) load 671799-2.html
load 690990-1.html

Просмотреть файл

@ -100,6 +100,7 @@ public:
NS_IMETHOD Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow);
virtual void DestroyFrom(nsIFrame* aDestructRoot);
/**
* Get the "type" of the frame
@ -176,6 +177,10 @@ nsSVGImageFrame::Init(nsIContent* aContent,
nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
NS_ENSURE_TRUE(imageLoader, NS_ERROR_UNEXPECTED);
// We should have a PresContext now, so let's notify our image loader that
// we need to register any image animations with the refresh driver.
imageLoader->FrameCreated(this);
// Push a null JSContext on the stack so that code that runs within
// the below code doesn't think it's being called by JS. See bug
// 604262.
@ -187,6 +192,19 @@ nsSVGImageFrame::Init(nsIContent* aContent,
return NS_OK;
}
/* virtual */ void
nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameDestroyed(this);
}
nsFrame::DestroyFrom(aDestructRoot);
}
//----------------------------------------------------------------------
// nsIFrame methods:

Просмотреть файл

@ -36,6 +36,7 @@
#include "nsFrame.h"
#include "nsSVGEffects.h"
#include "nsImageLoadingContent.h"
class nsSVGLeafFrame : public nsFrame
{
@ -47,6 +48,12 @@ protected:
public:
NS_DECL_FRAMEARENA_HELPERS
NS_IMETHOD Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* asPrevInFlow);
virtual void DestroyFrom(nsIFrame* aDestructRoot);
virtual bool IsFrameOfType(PRUint32 aFlags) const
{
return nsFrame::IsFrameOfType(aFlags & ~(nsIFrame::eSVG));
@ -70,6 +77,33 @@ NS_NewSVGLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
NS_IMPL_FRAMEARENA_HELPERS(nsSVGLeafFrame)
NS_IMETHODIMP
nsSVGLeafFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* asPrevInFlow)
{
nsFrame::Init(aContent, aParent, asPrevInFlow);
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameCreated(this);
}
return NS_OK;
}
/* virtual */ void
nsSVGLeafFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
nsCOMPtr<nsIImageLoadingContent> imageLoader =
do_QueryInterface(nsFrame::mContent);
if (imageLoader) {
imageLoader->FrameDestroyed(this);
}
nsFrame::DestroyFrom(aDestructRoot);
}
/* virtual */ void
nsSVGLeafFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
{

Просмотреть файл

@ -72,6 +72,7 @@
#include "nsIDOMDocument.h"
#include "nsTransform2D.h"
#include "nsITheme.h"
#include "nsIImageLoadingContent.h"
#include "nsIServiceManager.h"
#include "nsIURI.h"
@ -178,6 +179,7 @@ nsImageBoxFrame::AttributeChanged(PRInt32 aNameSpaceID,
nsImageBoxFrame::nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
nsLeafBoxFrame(aShell, aContext),
mIntrinsicSize(0,0),
mRequestRegistered(false),
mLoadFlags(nsIRequest::LOAD_NORMAL),
mUseSrcAttr(PR_FALSE),
mSuppressStyleCheck(PR_FALSE)
@ -200,9 +202,16 @@ nsImageBoxFrame::MarkIntrinsicWidthsDirty()
void
nsImageBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
// Release image loader first so that it's refcnt can go to zero
if (mImageRequest)
if (mImageRequest) {
nsPresContext* presContext = PresContext();
NS_ASSERTION(presContext, "No PresContext");
nsLayoutUtils::DeregisterImageRequest(presContext,
mImageRequest,
&mRequestRegistered);
// Release image loader first so that it's refcnt can go to zero
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
}
if (mListener)
reinterpret_cast<nsImageBoxListener*>(mListener.get())->SetFrame(nsnull); // set the frame to null so we don't send messages to a dead object.
@ -237,7 +246,12 @@ nsImageBoxFrame::Init(nsIContent* aContent,
void
nsImageBoxFrame::UpdateImage()
{
nsPresContext* presContext = PresContext();
NS_ASSERTION(presContext, "No PresContext");
if (mImageRequest) {
nsLayoutUtils::DeregisterImageRequest(presContext, mImageRequest,
&mRequestRegistered);
mImageRequest->CancelAndForgetObserver(NS_ERROR_FAILURE);
mImageRequest = nsnull;
}
@ -264,6 +278,12 @@ nsImageBoxFrame::UpdateImage()
nsContentUtils::LoadImage(uri, doc, mContent->NodePrincipal(),
doc->GetDocumentURI(), mListener, mLoadFlags,
getter_AddRefs(mImageRequest));
// Register our imgIRequest with the refresh driver
nsLayoutUtils::RegisterImageRequest(presContext,
mImageRequest,
&mRequestRegistered);
}
} else {
// Only get the list-style-image if we aren't being drawn
@ -588,10 +608,31 @@ NS_IMETHODIMP nsImageBoxFrame::OnStopContainer(imgIRequest *request,
return NS_OK;
}
NS_IMETHODIMP nsImageBoxFrame::OnStartDecode(imgIRequest* aRequest)
{
nsPresContext* presContext = PresContext();
NS_ASSERTION(presContext, "No PresContext");
nsLayoutUtils::RegisterImageRequest(presContext,
mImageRequest,
&mRequestRegistered);
return NS_OK;
}
NS_IMETHODIMP nsImageBoxFrame::OnStopDecode(imgIRequest *request,
nsresult aStatus,
const PRUnichar *statusArg)
{
// If the imgIRequest does not represent an animated image, then we should
// deregister it from our refresh driver.
nsPresContext* presContext = PresContext();
NS_ASSERTION(presContext, "No PresContext");
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(presContext,
mImageRequest,
&mRequestRegistered);
if (NS_SUCCEEDED(aStatus))
// Fire an onload DOM event.
FireImageDOMEvent(mContent, NS_LOAD);
@ -629,7 +670,7 @@ NS_IMETHODIMP nsImageBoxListener::OnStartContainer(imgIRequest *request,
imgIContainer *image)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->OnStartContainer(request, image);
}
@ -638,17 +679,26 @@ NS_IMETHODIMP nsImageBoxListener::OnStopContainer(imgIRequest *request,
imgIContainer *image)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->OnStopContainer(request, image);
}
NS_IMETHODIMP nsImageBoxListener::OnStartDecode(imgIRequest *aRequest)
{
if (!mFrame) {
return NS_OK;
}
return mFrame->OnStartDecode(aRequest);
}
NS_IMETHODIMP nsImageBoxListener::OnStopDecode(imgIRequest *request,
nsresult status,
const PRUnichar *statusArg)
{
if (!mFrame)
return NS_ERROR_FAILURE;
return NS_OK;
return mFrame->OnStopDecode(request, status, statusArg);
}

Просмотреть файл

@ -56,6 +56,7 @@ public:
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *request, nsresult status,
const PRUnichar *statusArg);
// imgIContainerObserver (override nsStubImageDecoderObserver)
@ -119,6 +120,7 @@ public:
NS_IMETHOD OnStartContainer(imgIRequest *request, imgIContainer *image);
NS_IMETHOD OnStopContainer(imgIRequest *request, imgIContainer *image);
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *request,
nsresult status,
const PRUnichar *statusArg);
@ -142,6 +144,10 @@ private:
nsSize mIntrinsicSize;
nsSize mImageSize;
// Boolean variable to determine if the current image request has been
// registered with the refresh driver.
bool mRequestRegistered;
nsCOMPtr<imgIRequest> mImageRequest;
nsCOMPtr<imgIDecoderObserver> mListener;

Просмотреть файл

@ -1,5 +1,4 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
@ -12,14 +11,17 @@
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Lord <chrislord.net@gmail.com>
* Dave Hyatt <hyatt@mozilla.org> (Original Author)
* Jan Varga <varga@ku.sk>
* Scott Johnson <sjohnson@mozilla.com>, Mozilla Corporation
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -35,16 +37,28 @@
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
#ifndef nsITreeImageListener_h__
#define nsITreeImageListener_h__
import android.telephony.*;
// The interface for our image listener.
// {90586540-2D50-403e-8DCE-981CAA778444}
#define NS_ITREEIMAGELISTENER_IID \
{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
public class GeckoPhoneStateListener
extends PhoneStateListener
class nsITreeImageListener : public nsISupports
{
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
GeckoAppShell.onNetworkStateChange(true);
}
}
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
/**
* Clear the internal frame pointer to prevent dereferencing an object
* that no longer exists.
*/
NS_IMETHOD ClearFrame() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
#endif

Просмотреть файл

@ -54,6 +54,7 @@
#include "nsTreeBodyFrame.h"
#include "nsTreeSelection.h"
#include "nsTreeImageListener.h"
#include "nsGkAtoms.h"
#include "nsCSSAnonBoxes.h"
@ -114,6 +115,14 @@ static PLDHashOperator
CancelImageRequest(const nsAString& aKey,
nsTreeImageCacheEntry aEntry, void* aData)
{
// If our imgIRequest object was registered with the refresh driver,
// then we need to deregister it.
nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
nsnull);
aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
return PL_DHASH_NEXT;
}
@ -164,7 +173,8 @@ nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aCont
// Destructor
nsTreeBodyFrame::~nsTreeBodyFrame()
{
mImageCache.EnumerateRead(CancelImageRequest, nsnull);
mImageCache.EnumerateRead(CancelImageRequest, this);
DetachImageListeners();
delete mSlots;
}
@ -197,8 +207,11 @@ nsTreeBodyFrame::Init(nsIContent* aContent,
mIndentation = GetIndentation();
mRowHeight = GetRowHeight();
NS_ENSURE_TRUE(mCreatedListeners.Init(), NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mImageCache.Init(16), NS_ERROR_OUT_OF_MEMORY);
EnsureBoxObject();
return rv;
}
@ -2154,10 +2167,14 @@ nsTreeBodyFrame::GetImage(PRInt32 aRowIndex, nsTreeColumn* aCol, bool aUseContex
if (!*aResult) {
// Create a new nsTreeImageListener object and pass it our row and column
// information.
nsTreeImageListener* listener = new nsTreeImageListener(mTreeBoxObject);
nsTreeImageListener* listener = new nsTreeImageListener(this);
if (!listener)
return NS_ERROR_OUT_OF_MEMORY;
if (!mCreatedListeners.PutEntry(listener)) {
return NS_ERROR_FAILURE;
}
listener->AddCell(aRowIndex, aCol);
nsCOMPtr<imgIDecoderObserver> imgDecoderObserver = listener;
@ -4236,7 +4253,7 @@ nsresult
nsTreeBodyFrame::ClearStyleAndImageCaches()
{
mStyleCache.Clear();
mImageCache.EnumerateRead(CancelImageRequest, nsnull);
mImageCache.EnumerateRead(CancelImageRequest, this);
mImageCache.Clear();
return NS_OK;
}
@ -4468,6 +4485,20 @@ nsTreeBodyFrame::PostScrollEvent()
}
}
void
nsTreeBodyFrame::DetachImageListeners()
{
mCreatedListeners.Clear();
}
void
nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
{
if (aListener) {
mCreatedListeners.RemoveEntry(aListener);
}
}
#ifdef ACCESSIBILITY
void
nsTreeBodyFrame::FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount)
@ -4641,3 +4672,19 @@ nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
nsContentUtils::AddScriptRunner(new nsOverflowChecker(this));
return weakFrame.IsAlive();
}
nsresult
nsTreeBodyFrame::OnStartDecode(imgIRequest* aRequest)
{
nsLayoutUtils::RegisterImageRequest(PresContext(), aRequest, nsnull);
return NS_OK;
}
nsresult
nsTreeBodyFrame::OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
const PRUnichar* aStatusArg)
{
nsLayoutUtils::DeregisterImageRequestIfNotAnimated(PresContext(), aRequest,
nsnull);
return NS_OK;
}

Просмотреть файл

@ -54,7 +54,6 @@
#include "nsTArray.h"
#include "nsTreeStyleCache.h"
#include "nsTreeColumns.h"
#include "nsTreeImageListener.h"
#include "nsAutoPtr.h"
#include "nsDataHashtable.h"
#include "imgIRequest.h"
@ -62,8 +61,10 @@
#include "nsScrollbarFrame.h"
#include "nsThreadUtils.h"
#include "mozilla/LookAndFeel.h"
#include "nsITreeImageListener.h"
class nsOverflowChecker;
class nsTreeImageListener;
// An entry in the tree's image cache
struct nsTreeImageCacheEntry
@ -91,6 +92,13 @@ public:
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS
// Callback handler methods for refresh driver based animations.
// Calls to these functions are forwarded from nsTreeImageListener. These
// mirror how nsImageFrame works.
nsresult OnStartDecode(imgIRequest* aRequest);
nsresult OnStopDecode(imgIRequest* aRequest, nsresult aStatus,
const PRUnichar* aStatusArg);
// non-virtual signatures like nsITreeBodyFrame
nsresult GetColumns(nsITreeColumns **aColumns);
nsresult GetView(nsITreeView **aView);
@ -435,6 +443,15 @@ public:
return col;
}
/**
* Remove an nsITreeImageListener from being tracked by this frame. Only tree
* image listeners that are created by this frame are tracked.
*
* @param aListener A pointer to an nsTreeImageListener to no longer
* track.
*/
void RemoveTreeImageListener(nsTreeImageListener* aListener);
protected:
// Create a new timer. This method is used to delay various actions like
@ -465,6 +482,12 @@ protected:
void PostScrollEvent();
void FireScrollEvent();
/**
* Clear the pointer to this frame for all nsTreeImageListeners that were
* created by this frame.
*/
void DetachImageListeners();
#ifdef ACCESSIBILITY
/**
* Fires 'treeRowCountChanged' event asynchronously. The event supports
@ -602,6 +625,11 @@ protected: // Data Members
bool mHorizontalOverflow;
bool mReflowCallbackPosted;
// Hash table to keep track of which listeners we created and thus
// have pointers to us.
nsTHashtable<nsPtrHashKey<nsTreeImageListener> > mCreatedListeners;
}; // class nsTreeBodyFrame
#endif

Просмотреть файл

@ -44,8 +44,8 @@
NS_IMPL_ISUPPORTS3(nsTreeImageListener, imgIDecoderObserver, imgIContainerObserver, nsITreeImageListener)
nsTreeImageListener::nsTreeImageListener(nsITreeBoxObject* aTree)
: mTree(aTree),
nsTreeImageListener::nsTreeImageListener(nsTreeBodyFrame* aTreeFrame)
: mTreeFrame(aTreeFrame),
mInvalidationSuppressed(PR_TRUE),
mInvalidationArea(nsnull)
{
@ -56,6 +56,29 @@ nsTreeImageListener::~nsTreeImageListener()
delete mInvalidationArea;
}
NS_IMETHODIMP
nsTreeImageListener::OnStartDecode(imgIRequest *aRequest)
{
if (!mTreeFrame) {
return NS_OK;
}
// grab the frame we want to use
return mTreeFrame->OnStartDecode(aRequest);
}
NS_IMETHODIMP
nsTreeImageListener::OnStopDecode(imgIRequest *aRequest,
nsresult aStatus,
const PRUnichar *aStatusArg)
{
if (!mTreeFrame) {
return NS_OK;
}
return mTreeFrame->OnStopDecode(aRequest, aStatus, aStatusArg);
}
NS_IMETHODIMP nsTreeImageListener::OnStartContainer(imgIRequest *aRequest,
imgIContainer *aImage)
{
@ -113,10 +136,16 @@ void
nsTreeImageListener::Invalidate()
{
if (!mInvalidationSuppressed) {
for (InvalidationArea* currArea = mInvalidationArea; currArea; currArea = currArea->GetNext()) {
for (InvalidationArea* currArea = mInvalidationArea; currArea;
currArea = currArea->GetNext()) {
// Loop from min to max, invalidating each cell that was listening for this image.
for (PRInt32 i = currArea->GetMin(); i <= currArea->GetMax(); ++i) {
mTree->InvalidateCell(i, currArea->GetCol());
if (mTreeFrame) {
nsITreeBoxObject* tree = mTreeFrame->GetTreeBoxObject();
if (tree) {
tree->InvalidateCell(i, currArea->GetCol());
}
}
}
}
}
@ -140,3 +169,10 @@ nsTreeImageListener::InvalidationArea::AddRow(PRInt32 aIndex)
else if (aIndex > mMax)
mMax = aIndex;
}
NS_IMETHODIMP
nsTreeImageListener::ClearFrame()
{
mTreeFrame = nsnull;
return NS_OK;
}

Просмотреть файл

@ -44,33 +44,21 @@
#include "nsCOMPtr.h"
#include "nsITreeColumns.h"
#include "nsStubImageDecoderObserver.h"
class nsITreeBoxObject;
// The interface for our image listener.
// {90586540-2D50-403e-8DCE-981CAA778444}
#define NS_ITREEIMAGELISTENER_IID \
{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
class nsITreeImageListener : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsITreeImageListener, NS_ITREEIMAGELISTENER_IID)
#include "nsTreeBodyFrame.h"
#include "nsITreeImageListener.h"
// This class handles image load observation.
class nsTreeImageListener : public nsStubImageDecoderObserver, public nsITreeImageListener
{
public:
nsTreeImageListener(nsITreeBoxObject* aTree);
nsTreeImageListener(nsTreeBodyFrame *aTreeFrame);
~nsTreeImageListener();
NS_DECL_ISUPPORTS
// imgIDecoderObserver (override nsStubImageDecoderObserver)
NS_IMETHOD OnStartDecode(imgIRequest *aRequest);
NS_IMETHOD OnStopDecode(imgIRequest *aRequest,
nsresult aStatus, const PRUnichar *aStatusArg);
NS_IMETHOD OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
NS_IMETHOD OnDataAvailable(imgIRequest *aRequest, bool aCurrentFrame,
const nsIntRect *aRect);
@ -79,6 +67,8 @@ public:
const nsIntRect *aDirtyRect);
NS_IMETHOD AddCell(PRInt32 aIndex, nsITreeColumn* aCol);
NS_IMETHOD ClearFrame();
friend class nsTreeBodyFrame;
@ -87,7 +77,7 @@ protected:
void Invalidate();
private:
nsITreeBoxObject* mTree;
nsTreeBodyFrame* mTreeFrame;
// A guard that prevents us from recursive painting.
bool mInvalidationSuppressed;

Просмотреть файл

@ -0,0 +1,114 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Matt Brubeck <mbrubeck@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var TabletSidebar = {
_grabbed: false, // true while the user is dragging the sidebar
_offset: 0, // tracks how far the sidebar has been dragged
_slideMultiplier: 1, // set greater than 1 to amplify sidebar drags (makes swiping easier)
get visible() {
return Elements.urlbarState.getAttribute("tablet_sidebar") == "true";
},
toggle: function toggle() {
if (this.visible)
this.hide();
else
this.show();
},
show: function show() {
Elements.urlbarState.setAttribute("tablet_sidebar", "true");
ViewableAreaObserver.update();
},
hide: function hide() {
Elements.urlbarState.setAttribute("tablet_sidebar", "false");
ViewableAreaObserver.update();
},
/**
* Call this function in landscape tablet mode to begin dragging the tab sidebar.
* Hiding the sidebar makes the viewable area grow; showing the sidebar makes it shrink.
*/
grab: function grab() {
this._grabbed = true;
ViewableAreaObserver.update();
let ltr = (Util.localeDir == Util.LOCALE_DIR_LTR);
if (this.visible) {
this._setOffset(ltr ? 0 : ViewableAreaObserver.sidebarWidth);
this._slideMultiplier = 3;
} else {
// If the tab bar is hidden, un-collapse it but scroll it offscreen.
this.show();
this._setOffset(ltr ? ViewableAreaObserver.sidebarWidth : 0);
this._slideMultiplier = 6;
}
},
/** Move the tablet sidebar by aX pixels. */
slideBy: function slideBy(aX) {
this._setOffset(this._offset + (aX * this._slideMultiplier));
},
/** Call this when tablet sidebar dragging is finished. */
ungrab: function ungrab() {
if (!this._grabbed)
return;
this._grabbed = false;
let finalOffset = this._offset;
this._setOffset(0);
let rtl = (Util.localeDir == Util.LOCALE_DIR_RTL);
if (finalOffset > (ViewableAreaObserver.sidebarWidth / 2) ^ rtl)
this.hide();
else
// we already called show() in grab; just need to update the width again.
ViewableAreaObserver.update();
},
/** Move the tablet sidebar. */
_setOffset: function _setOffset(aOffset) {
this._offset = aOffset;
let scrollX = Util.clamp(aOffset, 0, ViewableAreaObserver.sidebarWidth);
Browser.controlsScrollboxScroller.scrollTo(scrollX, 0);
}
}

Просмотреть файл

@ -61,22 +61,18 @@ var TabsPopup = {
return this.button = document.getElementById("tool-tabs");
},
hide: function hide() {
this._hidePortraitMenu();
get visible() {
return !this.box.hidden;
},
if (!Util.isPortrait()) {
Elements.urlbarState.removeAttribute("tablet_sidebar");
ViewableAreaObserver.update();
}
toggle: function toggle() {
if (this.visible)
this.hide();
else
this.show();
},
show: function show() {
if (!Util.isPortrait()) {
Elements.urlbarState.setAttribute("tablet_sidebar", "true");
ViewableAreaObserver.update();
return;
}
while(this.list.firstChild)
this.list.removeChild(this.list.firstChild);
@ -123,18 +119,7 @@ var TabsPopup = {
window.addEventListener("resize", this.resizeHandler, false);
},
toggle: function toggle() {
if (this.visible)
this.hide();
else
this.show();
},
get visible() {
return Util.isPortrait() ? !this.box.hidden : Elements.urlbarState.hasAttribute("tablet_sidebar");
},
_hidePortraitMenu: function _hidePortraitMenu() {
hide: function hide() {
if (!this.box.hidden) {
this.box.hidden = true;
BrowserUI.popPopup(this);
@ -174,7 +159,7 @@ var TabsPopup = {
if (aEvent.target != window)
return;
if (!Util.isPortrait())
this._hidePortraitMenu();
this.hide();
},
handleEvent: function handleEvent(aEvent) {

Просмотреть файл

@ -127,7 +127,9 @@
<script type="application/javascript;version=1.8"><![CDATA[
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
let gChromeWin = null;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
function openLink(aElement) {
try {
@ -177,23 +179,21 @@
uninitAddons();
}
function _readFile(aFile) {
try {
let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
stream.init(aFile, 0x01, 0, 0);
let cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
function _readFile(aFile, aCallback) {
let channel = NetUtil.newChannel(aFile);
channel.contentType = "application/json";
NetUtil.asyncFetch(channel, function(aStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
Cu.reportError("AboutHome: Could not read from " + aFile.leafName);
aCallback(null);
return;
}
let fileSize = stream.available();
cvstream.init(stream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
let data = {};
cvstream.readString(fileSize, data);
let content = data.value;
cvstream.close();
return content.replace(/\r\n?/g, "\n");
}
catch (ex) { Cu.reportError(ex); }
let content = NetUtil.readInputStreamToString(aStream, aStream.available()) || "";
aStream.close();
return null;
aCallback(content.replace(/\r\n?/g, "\n"));
});
}
function openTabs(aURLs) {
@ -218,75 +218,77 @@
return;
}
let data = JSON.parse(_readFile(session));
if (!data || data.windows.length == 0) {
noRecentTabs();
return;
}
let chromeWin = getChromeWin();
let allPageURLs = [];
let list = document.getElementById("recentTabs");
let tabs = data.windows[0].tabs;
for (let i=0; i<tabs.length; i++) {
let tabData = tabs[i];
let tabEntry = tabData.entries[tabData.index - 1];
let url = tabEntry.url;
if (url.indexOf("about:") == 0)
continue;
let title = tabEntry.title;
if (!title)
continue;
let uri = chromeWin.Util.makeURI(url);
let favicon = chromeWin.gFaviconService.getFaviconImageForPage(uri).spec;
let outer = document.createElement("a");
outer.setAttribute("role", "button");
outer.setAttribute("href", url);
allPageURLs.push(url);
let img = document.createElement("img");
img.className = "favicon";
img.setAttribute("src", favicon);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let titlePart = document.createElement("div");
titlePart.textContent = title;
titlePart.className = "title";
inner.appendChild(titlePart);
outer.appendChild(inner);
list.appendChild(outer);
}
if (allPageURLs.length > 0) {
let loading = document.getElementById("loadingTabs");
loading.parentNode.removeChild(loading);
if (allPageURLs.length > 1) {
let outer = document.createElement("div");
outer.className = "openall";
outer.textContent = document.getElementById("text-openalltabs").textContent;
_readFile(session, function(aContent) {
let data = JSON.parse(aContent);
if (!data || data.windows.length == 0) {
noRecentTabs();
return;
}
let chromeWin = getChromeWin();
let allPageURLs = [];
let list = document.getElementById("recentTabs");
let tabs = data.windows[0].tabs;
for (let i=0; i<tabs.length; i++) {
let tabData = tabs[i];
let tabEntry = tabData.entries[tabData.index - 1];
let url = tabEntry.url;
if (url.indexOf("about:") == 0)
continue;
let title = tabEntry.title;
if (!title)
continue;
let uri = chromeWin.Util.makeURI(url);
let favicon = chromeWin.gFaviconService.getFaviconImageForPage(uri).spec;
let outer = document.createElement("a");
outer.setAttribute("role", "button");
outer.addEventListener("click", function() {
openTabs(allPageURLs);
}, false);
outer.setAttribute("href", url);
allPageURLs.push(url);
let img = document.createElement("img");
img.className = "favicon";
img.setAttribute("src", favicon);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let titlePart = document.createElement("div");
titlePart.textContent = title;
titlePart.className = "title";
inner.appendChild(titlePart);
outer.appendChild(inner);
list.appendChild(outer);
}
} else {
noRecentTabs();
}
if (allPageURLs.length > 0) {
let loading = document.getElementById("loadingTabs");
loading.parentNode.removeChild(loading);
if (allPageURLs.length > 1) {
let outer = document.createElement("div");
outer.className = "openall";
outer.textContent = document.getElementById("text-openalltabs").textContent;
outer.setAttribute("role", "button");
outer.addEventListener("click", function() {
openTabs(allPageURLs);
}, false);
list.appendChild(outer);
}
} else {
noRecentTabs();
}
});
}
function openRemoteTabs() {
@ -315,26 +317,6 @@
return file;
},
_readFile: function(aFile) {
try {
let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
stream.init(aFile, 0x01, 0, 0);
let cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
let fileSize = stream.available();
cvstream.init(stream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
let data = {};
cvstream.readString(fileSize, data);
let content = data.value;
cvstream.close();
return content.replace(/\r\n?/g, "\n");
}
catch (ex) {
Cu.reportError(ex);
}
return null;
},
_loadAddons: function(aAddons, aAddonCount, aTotalResults) {
let list = document.getElementById("newAddons");
let loading = document.getElementById("loadingAddons");
@ -384,23 +366,29 @@
},
loadFromCacheOrScheduleUpdate: function(aDelay) {
let self = this;
let file = this._getFile();
if (file.exists()) {
let json = JSON.parse(this._readFile(file));
// Ignore addons already installed
let self = this;
let addonsCache = json.addons;
AddonManager.getAllAddons(function(aAddons) {
let addons = addonsCache.filter(function(addon) {
for (let i =0; i < aAddons.length; i++)
if (addon.id == aAddons[i].id)
return false;
return true;
_readFile(file, function(aContent) {
let json = JSON.parse(aContent);
if (!json || json.addons.length == 0) {
self._loadAddons([], 0, 0);
return;
}
// Ignore addons already installed
let addonsCache = json.addons;
AddonManager.getAllAddons(function(aAddons) {
let addons = addonsCache.filter(function(addon) {
for (let i =0; i < aAddons.length; i++)
if (addon.id == aAddons[i].id)
return false;
return true;
});
self._loadAddons(addons, addons.length, json.totalResults);
});
self._loadAddons(addons, addons.length, json.totalResults);
});
} else {
setTimeout(function() {

Просмотреть файл

@ -108,6 +108,7 @@ XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
["SelectionHelper", "chrome://browser/content/SelectionHelper.js"],
["ContentPopupHelper", "chrome://browser/content/ContentPopupHelper.js"],
["SharingUI", "chrome://browser/content/SharingUI.js"],
["TabletSidebar", "chrome://browser/content/TabletSidebar.js"],
["TabsPopup", "chrome://browser/content/TabsPopup.js"],
["MasterPasswordUI", "chrome://browser/content/MasterPasswordUI.js"],
#ifdef MOZ_SERVICES_SYNC

Просмотреть файл

@ -428,7 +428,7 @@ var BrowserUI = {
window.addEventListener("mousedown", this, true);
// listening escape to dismiss dialog on VK_ESCAPE
window.addEventListener("keypress", this, false);
window.addEventListener("keypress", this, true);
// listening AppCommand to handle special keys
window.addEventListener("AppCommand", this, true);
@ -945,6 +945,10 @@ var BrowserUI = {
break;
// Window events
case "keypress":
// Ignore events headed toward the browser; they will be
// re-dispatched after content has a chance to handle them.
if (aEvent.target.localName == "browser")
break;
if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
this.handleEscape(aEvent);
break;
@ -1246,7 +1250,10 @@ var BrowserUI = {
AppMenu.toggle();
break;
case "cmd_showTabs":
TabsPopup.toggle();
if (Util.isPortrait())
TabsPopup.toggle();
else
TabletSidebar.toggle();
break;
case "cmd_newTab":
this.newTab();

Просмотреть файл

@ -1276,60 +1276,6 @@ var Browser = {
this._handleErrorPage(aMessage);
break;
}
},
_grabbedSidebar: false, // true while the user is dragging the sidebar
_sidebarOffset: 0, // tracks how far the sidebar has been dragged
_slideMultiplier: 1, // set greater than 1 to amplify sidebar drags (makes swiping easier)
/**
* Call this function in landscape tablet mode to begin dragging the tab sidebar.
* Hiding the sidebar makes the viewable area grow; showing the sidebar makes it shrink.
*/
grabSidebar: function grabSidebar() {
this._grabbedSidebar = true;
ViewableAreaObserver.update();
let ltr = (Util.localeDir == Util.LOCALE_DIR_LTR);
if (TabsPopup.visible) {
this._setSidebarOffset(ltr ? 0 : ViewableAreaObserver.sidebarWidth);
this._slideMultiplier = 3;
} else {
// If the tab bar is hidden, un-collapse it but scroll it offscreen.
TabsPopup.show();
this._setSidebarOffset(ltr ? ViewableAreaObserver.sidebarWidth : 0);
this._slideMultiplier = 6;
}
},
/** Move the tablet sidebar by aX pixels. */
slideSidebarBy: function slideSidebarBy(aX) {
this._setSidebarOffset(this._sidebarOffset + (aX * this._slideMultiplier));
},
/** Call this when tablet sidebar dragging is finished. */
ungrabSidebar: function ungrabSidebar() {
if (!this._grabbedSidebar)
return;
this._grabbedSidebar = false;
let finalOffset = this._sidebarOffset;
this._setSidebarOffset(0);
let rtl = (Util.localeDir == Util.LOCALE_DIR_RTL);
if (finalOffset > (ViewableAreaObserver.sidebarWidth / 2) ^ rtl)
TabsPopup.hide();
else
// we already called TabsPopup.show() in grabSidebar; just need to update the width again.
ViewableAreaObserver.update();
},
/** Move the tablet sidebar. */
_setSidebarOffset: function _setSidebarOffset(aOffset) {
this._sidebarOffset = aOffset;
let scrollX = Util.clamp(aOffset, 0, ViewableAreaObserver.sidebarWidth);
Browser.controlsScrollboxScroller.scrollTo(scrollX, 0);
}
};
@ -1365,7 +1311,7 @@ Browser.MainDragger.prototype = {
this._canGrabSidebar = false;
// In landscape portrait mode, swiping from the left margin drags the tab sidebar.
if (isTablet && !Util.isPortrait()) {
let grabSidebarMargin = TabsPopup.visible ? 30 : 5;
let grabSidebarMargin = TabletSidebar.visible ? 30 : 5;
// Don't actually grab until we see whether the swipe is horizontal in dragMove.
this._canGrabSidebar = ((Util.localeDir == Util.LOCALE_DIR_LTR)
? (clientX - bcr.left < 30)
@ -1380,7 +1326,7 @@ Browser.MainDragger.prototype = {
dragStop: function dragStop(dx, dy, scroller) {
if (this._grabSidebar) {
Browser.ungrabSidebar();
TabletSidebar.ungrab();
return;
}
@ -1394,10 +1340,10 @@ Browser.MainDragger.prototype = {
dragMove: function dragMove(dx, dy, scroller, aIsKinetic) {
if (this._canGrabSidebar && !this._grabSidebar && dx) {
this._grabSidebar = true;
Browser.grabSidebar();
TabletSidebar.grab();
}
if (this._grabSidebar) {
Browser.slideSidebarBy(dx);
TabletSidebar.slideBy(dx);
return;
}
@ -2084,7 +2030,7 @@ KeyFilter.prototype = {
return;
let browser = getBrowser();
if (browser && browser.active && browser.getAttribute("remote") == "true") {
if (browser && browser.active) {
aEvent.stopPropagation();
document.getElementById("mainKeyset").setAttribute("disabled", "true");
}
@ -3236,7 +3182,7 @@ function rendererFactory(aBrowser, aCanvas) {
var ViewableAreaObserver = {
get width() {
let width = this._width || window.innerWidth;
if (!Browser._grabbedSidebar && Util.isTablet())
if (!TabletSidebar._grabbed && Util.isTablet())
width -= this.sidebarWidth;
return width;
},

Просмотреть файл

@ -84,7 +84,7 @@
<broadcasterset id="broadcasterset">
<broadcaster id="bcast_contentShowing" disabled="false"/>
<broadcaster id="bcast_urlbarState" mode="view" tablet_sidebar="true"/>
<broadcaster id="bcast_urlbarState" mode="view" tablet_sidebar="true" persist="tablet_sidebar"/>
<broadcaster id="bcast_uidiscovery"/>
</broadcasterset>

Просмотреть файл

@ -13,7 +13,7 @@ let stringPrefs = [
{ selector: "#picker-title", pref: "chooseLanguage", data: null },
{ selector: "#continue-button", pref: "continue", data: null },
{ selector: "#cancel-button", pref: "cancel", data: null },
{ selector: "#intalling-message", pref: "installing", data: ["CURRENT_LOCALE"] },
{ selector: "#installing-message", pref: "installing", data: ["CURRENT_LOCALE"] },
{ selector: "#cancel-install-button", pref: "cancel", data: null },
{ selector: "#installing-error", pref: "installerror", data: null },
{ selector: "#install-continue", pref: "continue", data: null },
@ -88,7 +88,7 @@ let LocaleUI = {
return this.list = document.getElementById("language-list");
},
_createItem: function(aId, aText, aLocale) {
_createItem: function lp__createItem(aId, aText, aLocale) {
let item = document.createElement("richlistitem");
item.setAttribute("id", aId);
@ -109,7 +109,7 @@ let LocaleUI = {
return item;
},
addLocales: function(aLocales) {
addLocales: function lp_addLocales(aLocales) {
let fragment = document.createDocumentFragment();
let selectedItem = null;
let bestMatch = NO_MATCH;
@ -158,9 +158,6 @@ let LocaleUI = {
_locale: "",
set locale(aVal) {
if (aVal == this._locale)
return;
Services.prefs.setBoolPref("intl.locale.matchOS", false);
Services.prefs.setCharPref("general.useragent.locale", aVal);
this._locale = aVal;
@ -194,7 +191,7 @@ let LocaleUI = {
}
},
installAddon: function() {
installAddon: function lp_installAddon() {
let locale = LocaleUI.list.selectedItem.locale;
if (locale.install) {
@ -210,9 +207,9 @@ let LocaleUI = {
cancelPicker: function() {
if (this.pendingInstall)
this.pendingInstall = null;
// restore the last known "good" locale
this.locale = this.defaultLocale;
this.updateStrings();
this.closePicker();
},
@ -252,8 +249,6 @@ let LocaleUI = {
try { addonInstall.cancel(); }
catch(ex) { }
LocaleUI.pendingInstall = null;
this.locale = this.defaultLocale;
}
},

Просмотреть файл

@ -23,14 +23,14 @@
<description id="picker-title"/>
<richlistbox id="language-list" onclick="LocaleUI.selectLocale(event);" flex="1" class="window-width"/>
<hbox class="footer">
<button id="cancel-button" class="cancel" onclick="LocaleUI.cancelPicker();" crop="center"/>
<button id="continue-button" class="continue" onclick="LocaleUI.installAddon();" crop="center"/>
<button id="cancel-button" class="cancel" onclick="LocaleUI.cancelPicker();" crop="center"/>
</hbox>
</vbox>
<deck id="installer-page" class="pane" flex="1">
<vbox id="installer-page-installing" flex="1" pack="center" align="center">
<description id="intalling-message" class="install-message"/>
<description id="installing-message" class="install-message"/>
<button id="cancel-install-button" class="cancel" onclick="LocaleUI.cancelInstall();" crop="center"/>
</vbox>
<vbox id="installer-page-error" flex="1" pack="center" align="center">

Просмотреть файл

@ -104,11 +104,17 @@ var PreferencesView = {
WeaveGlue.init();
Services.prefs.addObserver("general.useragent.locale", this, false);
let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
chrome.QueryInterface(Ci.nsIToolkitChromeRegistry);
this._currentLocale = chrome.getSelectedLocale("browser");
},
observe: function(aSubject, aTopic, aData) {
if (aData == "general.useragent.locale") {
this.showRestart();
if (Services.prefs.getCharPref("general.useragent.locale") != this._currentLocale)
this.showRestart();
else
this.hideRestart();
this._loadLocales();
}
},
@ -140,7 +146,6 @@ var PreferencesView = {
}
if (locale == selectedLocale) {
selectedLabel = label;
this._currentLocale = locale;
break;
}
}

Просмотреть файл

@ -201,7 +201,7 @@
dragStop: function dragStop(dx, dy) {
if (this._grabSidebar)
Browser.ungrabSidebar();
TabletSidebar.ungrab();
dragger.dragStop(dx, dy, scroller);
},
@ -211,10 +211,10 @@
if (!this._grabSidebar && hiddingPan) {
this._grabSidebar = dx && Util.isTablet() && !Util.isPortrait();
if (this._grabSidebar)
Browser.grabSidebar();
TabletSidebar.grab();
}
if (this._grabSidebar)
Browser.slideSidebarBy(dx);
TabletSidebar.slideBy(dx);
return dragger.dragMove(dx, dy, scroller);
}
};

Просмотреть файл

@ -33,6 +33,7 @@ chrome.jar:
content/SelectHelperUI.js (content/SelectHelperUI.js)
content/SelectionHelper.js (content/SelectionHelper.js)
content/SharingUI.js (content/SharingUI.js)
content/TabletSidebar.js (content/TabletSidebar.js)
content/TabsPopup.js (content/TabsPopup.js)
content/MasterPasswordUI.js (content/MasterPasswordUI.js)
* content/content.js (content/content.js)

Просмотреть файл

@ -74,6 +74,7 @@ _BROWSER_FILES = \
browser_formsZoom.html \
$(warning browser_formsZoom.js disabled due to failures) \
browser_history.js \
browser_localepicker.js \
browser_mainui.js \
browser_preferences_text.js \
browser_preferences_fulltoggle.js \

Просмотреть файл

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function install(data, reason) {}
function startup(data, reason) {}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>locale1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>8</em:type>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Test Locale</em:name>
</Description>
</RDF>

Просмотреть файл

@ -61,8 +61,7 @@ gTests.push({
waitForNavigationPanel(gCurrentTest.onPopupHidden, true);
// Need to use sendKey instead of synthesizeKey here (bug 684558).
EventUtils.sendKey("ESCAPE", window);
EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window);
},
onPopupHidden: function() {
@ -110,7 +109,7 @@ gTests.push({
runNextTest();
}, true);
EventUtils.sendKey("ESCAPE", window);
EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window);
}
});
@ -139,7 +138,7 @@ gTests.push({
is(BrowserUI._edit.readOnly, false, "urlbar should not be readonly after an input");
waitForNavigationPanel(gCurrentTest.onPopupHidden, true);
EventUtils.sendKey("ESCAPE", window);
EventUtils.synthesizeKey("VK_ESCAPE", {type: "keypress"}, window);
},
onPopupHidden: function() {

Просмотреть файл

@ -0,0 +1,282 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
const RELATIVE_DIR = "browser/mobile/chrome/tests/";
const TESTROOT = "http://example.com/" + RELATIVE_DIR;
const PREF_GET_LOCALES = "extensions.getLocales.get.url";
var gAvailable = [];
var restartObserver = {
observe: function(aSubject, aTopic, aData) {
// cancel restart requests
aSubject.QueryInterface(Ci.nsISupportsPRBool);
aSubject.data = true;
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
}
function test() {
Services.obs.addObserver(restartObserver, "quit-application-requested", false);
waitForExplicitFinish();
Services.prefs.setCharPref(PREF_GET_LOCALES, TESTROOT + "locales_list.sjs?numvalid=2");
let chromeReg = Components.classes["@mozilla.org/chrome/chrome-registry;1"].getService(Components.interfaces.nsIXULChromeRegistry);
chromeReg.QueryInterface(Ci.nsIToolkitChromeRegistry);
let availableLocales = chromeReg.getLocalesForPackage("browser");
while (availableLocales.hasMore())
gAvailable.push( availableLocales.getNext() );
// in order to test restart notifications being shown, we much open the settings panel once
let settingsButton = document.getElementById("tool-panel-open");
settingsButton.click();
waitForAndContinue(runNextTest, function() {
return !document.getElementById("panel-container").hidden;
});
}
function end_test() {
BrowserUI.hidePanel();
Services.prefs.clearUserPref(PREF_GET_LOCALES);
Services.obs.removeObserver(restartObserver, "quit-application-requested");
}
registerCleanupFunction(end_test);
function CheckListLoad(aList, aLength) {
return function() {
return aList.childNodes.length == aLength;
}
}
function CheckDeck(aDeck, aPanel) {
return function() {
return aDeck.selectedPanel == aPanel;
}
}
function LocaleTest(aName, aOptions) {
var install = null;
return {
desc: aName,
win: null,
run: function lt_run() {
this.loadedWindow = this.loadedWindow.bind(this);
this.windowClosed = this.windowClosed.bind(this);
this.win = Services.ww.openWindow(aOptions.opener, "chrome://browser/content/localePicker.xul", "_browser", "chrome,dialog=no,all", null);
this.win.addEventListener("load", this.loadedWindow, false);
},
loadedWindow: function lt_loadedWindow() {
this.win.removeEventListener("load", this.loadedWindow, false);
if (aOptions.opener)
setTimeout(this.delayedLoadPicker.bind(this), 0);
else
setTimeout(this.delayedLoadMain.bind(this), 0);
},
delayedLoadMain: function lt_delayedLoadMain() {
let deck = this.win.document.getElementById("language-deck");
let mainPage = this.win.document.getElementById("main-page");
is(deck.selectedPanel, mainPage, "Deck is showing the main page");
if (aOptions.loadLocalesList) {
// load the locales list
let changeButton = this.win.document.getElementById("change-language");
changeButton.click();
this.delayedLoadPicker();
} else {
// click the "Continue in English" button
let continueButton = this.win.document.getElementById("continue-in-button");
ok(/english/i.test(continueButton.textContent), "Continue button says English");
this.win.addEventListener("unload", this.windowClosed, false);
continueButton.click();
}
},
delayedLoadPicker: function lt_delayedLoadPicker() {
let deck = this.win.document.getElementById("language-deck");
let pickerPage = this.win.document.getElementById("picker-page");
is(deck.selectedPanel, pickerPage, "Deck is showing the picker page");
let list = this.win.document.getElementById("language-list");
// wait till the list shows the number of locales bundled with this build + the 2 from the downloaded list
waitForAndContinue(this.listLoaded.bind(this), CheckListLoad(list, gAvailable.length + 2));
},
listLoaded: function() {
let continueButton = this.win.document.getElementById("continue-button");
let cancelButton = this.win.document.getElementById("cancel-button");
ok(/continue/i.test(continueButton.textContent), "Continue button has correct text");
ok(/cancel/i.test(cancelButton.textContent), "Cancel button has correct text");
let list = this.win.document.getElementById("language-list");
is(list.childNodes.length, gAvailable.length + 2, "List has correct number of children");
let nextSelected = null;
let selectedItem = null;
for(var i = 0; i < list.childNodes.length; i++) {
let child = list.childNodes[i];
if (/english/i.test(child.textContent)) {
ok(child.hasAttribute("selected"), "English is initially selected");
selectedItem = child;
} else {
ok(!child.hasAttribute("selected"), "Language is not selected");
if (aOptions.selectAddon && child.textContent == aOptions.selectAddon.name)
nextSelected = child;
}
}
this.testInstallingItem(nextSelected);
},
testInstallingItem: function lt_testInstallingItem(aSelect) {
let continueButton = this.win.document.getElementById("continue-button");
let cancelButton = this.win.document.getElementById("cancel-button");
if (aSelect) {
aSelect.click();
is(continueButton.textContent, aOptions.selectAddon.continueButton, "Continue button says " + aOptions.selectAddon.continueButton);
is(cancelButton.textContent, aOptions.selectAddon.cancelButton, "Cancel button says " + aOptions.selectAddon.cancelButton);
let title = this.win.document.getElementById("picker-title");
is(title.textContent, aOptions.selectAddon.title, "Title says " + aOptions.selectAddon.title);
continueButton.click();
let deck = this.win.document.getElementById("language-deck");
let installerPage = this.win.document.getElementById("installer-page");
is(deck.selectedPanel, installerPage, "Deck is showing the installer page");
let installingPage = this.win.document.getElementById("installer-page-installing");
is(installerPage.selectedPanel, installingPage, "Installer is showing installing page");
let installMsg = this.win.document.getElementById("installing-message");
is(installMsg.textContent, aOptions.selectAddon.installMessage, "Installer is showing correct message");
if (aOptions.selectAddon.willFail) {
let failedPage = this.win.document.getElementById("installer-page-error");
waitForAndContinue(this.installFailed.bind(this), CheckDeck(installerPage, failedPage));
} else {
let install = aSelect.locale;
this.win.addEventListener("unload", this.windowClosed, false);
}
} else {
this.cancelList();
}
},
installFailed: function lt_installFailed() {
let continueButton = this.win.document.getElementById("install-continue");
is(continueButton.textContent, aOptions.selectAddon.installFailed, "Install failed button has correct label");
continueButton.click();
let deck = this.win.document.getElementById("language-deck");
let pickerPage = this.win.document.getElementById("picker-page");
is(deck.selectedPanel, pickerPage, "Deck is showing the picker page");
this.cancelList();
},
cancelList: function lt_cancelList() {
this.win.addEventListener("unload", this.windowClosed, false);
let cancelButton = this.win.document.getElementById("cancel-button");
cancelButton.click();
if (!aOptions.opener) {
// canceling out of the list, should revert back to english ui
let deck = this.win.document.getElementById("language-deck");
let mainPage = this.win.document.getElementById("main-page");
is(deck.selectedPanel, mainPage, "Deck is showing the main page again");
let continueButton = this.win.document.getElementById("continue-in-button");
ok(/english/i.test(continueButton.textContent), "Cancelling returned the UI to English");
continueButton.click();
}
},
windowClosed: function lt_windowClosed(aEvent) {
this.checkMainUI(aOptions.selectAddon);
Services.prefs.clearUserPref("intl.locale.matchOS");
Services.prefs.clearUserPref("general.useragent.locale");
window.PreferencesView.hideRestart();
if (install)
install.uninstall();
runNextTest();
},
checkMainUI: function(aAddon) {
let systemPref = "";
let userAgentPref = "";
try {
systemPref = Services.prefs.getBoolPref("intl.locale.matchOS");
userAgentPref = Services.prefs.getCharPref("general.useragent.locale")
} catch(ex) { }
let notification = document.getElementById("prefs-messages").getNotificationWithValue("restart-app");
let showRestart = aAddon ? !aAddon.willFail : false;
is(!!notification, showRestart, "Restart message is " + (showRestart ? "" : "not ") + "shown");
// check that locale pref has been updated
let localeName = aAddon ? aAddon.locale : "en-US";
is(systemPref, false, "Match system locale is false");
is(userAgentPref, localeName, "User agent locale is " + localeName);
let buttonLabel = aAddon ? aAddon.localeName : "English (US)";
is(document.getElementById("prefs-uilanguage-button").getAttribute("label"), buttonLabel, "Locale button says " + buttonLabel);
}
}
}
let invalidInstall = {
name: "Test Locale 0",
installMessage: "INSTALLINGTest Locale 0",
continueButton: "CONTINUE",
cancelButton: "CANCEL",
title: "CHOOSELANGUAGE",
installFailed: "CONTINUE",
locale: "en-US",
localeName: "English (US)",
willFail: true
};
let validInstall = {
name: "Test Locale 1",
installMessage: "INSTALLINGTest Locale 1",
continueButton: "CONTINUE",
cancelButton: "CANCEL",
title: "CHOOSELANGUAGE",
locale: "test1",
localeName: "test1",
willFail: false
}
gTests.push(new LocaleTest("Load locale picker with no opener and continue",
{ opener: null,
loadLocalesList: false,
selectAddon: null
}));
gTests.push(new LocaleTest("Load locale picker with no opener and try to install an invalid language",
{ opener: null,
loadLocalesList: true,
selectAddon: invalidInstall
}));
gTests.push(new LocaleTest("Load locale picker with no opener and try to install a valid language",
{ opener: null,
loadLocalesList: true,
selectAddon: validInstall
}));
gTests.push(new LocaleTest("Load locale picker with opener and try to install an invalid language",
{ opener: this.window,
loadLocalesList: true,
selectAddon: invalidInstall
}));
gTests.push(new LocaleTest("Load locale picker with opener and try to install a valid language",
{ opener: this.window,
loadLocalesList: true,
selectAddon: validInstall
}));

Просмотреть файл

@ -25,14 +25,14 @@ gTests.push({
listLoaded: function(aLocales) {
is(aLocales.length, 1, "Correct number of locales were found");
isnot(aLocales[0].addon, null, "Locale has an addon");
is(aLocales[0].xpiURL, "http://www.example.com/mylocale.xpi", "Locale has correct xpi url");
is(aLocales[0].xpiURL, "http://www.example.com/browser/mobile/chrome/tests/addons/browser_locale1.xpi", "Locale has correct xpi url");
is(aLocales[0].xpiHash, null, "Locale has correct hash");
is(aLocales[0].addon.id, "langpack-test-1@firefox-mobile.mozilla.org", "Locale has correct id");
is(aLocales[0].addon.name, "Test Locale", "Locale has correct name");
is(aLocales[0].addon.id, "langpack-test1@firefox-mobile.mozilla.org", "Locale has correct id");
is(aLocales[0].addon.name, "Test Locale 1", "Locale has correct name");
is(aLocales[0].addon.type, "language", "Locale has correct type");
is(aLocales[0].addon.targetLocale, "test", "Locale has correct target locale");
is(aLocales[0].addon.targetLocale, "test1", "Locale has correct target locale");
is(aLocales[0].addon.version, "1.0", "Locale has correct version");
runNextTest();
}

Просмотреть файл

@ -3,10 +3,10 @@ Components.utils.import("resource://gre/modules/Services.jsm");
function getLocale(aLocaleParams, aAppParams) {
let l = {
TARGETLOCALE: "test",
NAME: "Test Locale",
TARGETLOCALE: "test{IDNUMBER}",
NAME: "Test Locale {IDNUMBER}",
VERSION: "1.0",
INSTALL: "http://www.example.com/mylocale.xpi",
INSTALL: "http://www.example.com/browser/mobile/chrome/tests/addons/browser_locale{IDNUMBER}.xpi",
TYPENUMBER: 5,
TYPENAME: "Language Pack (Application)",
IDNUMBER: "",
@ -44,20 +44,20 @@ let template = "<addon>"+
"<target_locale>{TARGETLOCALE}</target_locale>" +
"<name>{NAME}</name>"+
"<type id=\"{TYPENUMBER}\">{TYPENAME}</type>"+
"<guid>langpack-{TARGETLOCALE}-{IDNUMBER}@firefox-mobile.mozilla.org</guid>"+
"<guid>langpack-{TARGETLOCALE}@firefox-mobile.mozilla.org</guid>"+
"<version>{VERSION}</version>"+
"<status id=\"4\">Public</status>"+
"<compatible_applications>{APPS}</compatible_applications>"+
"<all_compatible_os><os>ALL</os></all_compatible_os>"+
"<install os=\"ALL\">{INSTALL}</install><strings>\n" +
"title=TITLE\n" +
"continueIn=CONTINUEIN\n" +
"continueIn=CONTINUEIN%S\n" +
"name=NAME\n" +
"choose=CHOOSE\n" +
"chooseLanguage=CHOOSELANGUAGE\n" +
"cancel=CANCEL\n" +
"continue=CONTINUE\n" +
"installing=INSTALLING\n" +
"installing=INSTALLING%S\n" +
"installerror=INSTALLERROR\n" +
"loading=LOADING" +
"</strings>"+
@ -105,7 +105,7 @@ function handleRequest(request, response) {
t = t.replace(/{INSTALL}/, locales[i].INSTALL);
t = t.replace(/{TYPENUMBER}/, locales[i].TYPENUMBER);
t = t.replace(/{TYPENAME}/, locales[i].TYPENAME);
t = t.replace(/{IDNUMBER}/, locales[i].IDNUMBER)
t = t.replace(/{IDNUMBER}/g, locales[i].IDNUMBER)
let a = appTemplate;
a = a.replace(/{APPNAME}/, locales[i].app.APPNAME);

Просмотреть файл

@ -16,7 +16,7 @@
#toolbar-main[tablet] > .button-actionbar {
visibility: visible;
}
#toolbar-main[tablet][tablet_sidebar] > #tool-tabs {
#toolbar-main[tablet][tablet_sidebar="true"] > #tool-tabs {
visibility: collapse;
}
@ -132,12 +132,12 @@ documenttab[selected="true"] > vbox > stack > hbox > .documenttab-close[tablet]
text-align: start;
}
#controls-scrollbox[tablet]:not([tablet_sidebar]) > #tabs-sidebar {
#controls-scrollbox[tablet][tablet_sidebar="false"] > #tabs-sidebar {
visibility: collapse;
}
@media (@orientation@: portrait) {
#toolbar-main[tablet][tablet_sidebar] > #tool-tabs {
#toolbar-main[tablet][tablet_sidebar="true"] > #tool-tabs {
visibility: visible;
}
@ -147,7 +147,7 @@ documenttab[selected="true"] > vbox > stack > hbox > .documenttab-close[tablet]
}
@media (@orientation@: landscape) {
#toolbar-main[tablet][tablet_sidebar] {
#toolbar-main[tablet][tablet_sidebar="true"] {
-moz-padding-start: @padding_xxnormal@;
}
}

Просмотреть файл

@ -56,6 +56,10 @@ interface imgIDecoderObserver;
#include "limits.h"
class nsIFrame;
namespace mozilla {
class TimeStamp;
}
%}
[ptr] native gfxImageSurface(gfxImageSurface);
@ -68,6 +72,7 @@ native gfxGraphicsFilter(gfxPattern::GraphicsFilter);
[ref] native nsIntRect(nsIntRect);
[ref] native nsIntSize(nsIntSize);
[ptr] native nsIFrame(nsIFrame);
[ref] native TimeStamp(mozilla::TimeStamp);
/**
* imgIContainer is the interface that represents an image. It allows
@ -261,6 +266,13 @@ interface imgIContainer : nsISupports
*/
void unlockImage();
/**
* Indicates that this imgIContainer has been triggered to update
* its internal animation state. Likely this should only be called
* from within nsImageFrame or objects of similar type.
*/
[notxpcom] void requestRefresh([const] in TimeStamp aTime);
/**
* Animation mode Constants
* 0 = normal

Просмотреть файл

@ -173,10 +173,10 @@ namespace mozilla {
namespace imagelib {
#ifndef DEBUG
NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
nsISupportsWeakReference)
#else
NS_IMPL_ISUPPORTS5(RasterImage, imgIContainer, nsITimerCallback, nsIProperties,
NS_IMPL_ISUPPORTS4(RasterImage, imgIContainer, nsIProperties,
imgIContainerDebug, nsISupportsWeakReference)
#endif
@ -313,6 +313,159 @@ RasterImage::Init(imgIDecoderObserver *aObserver,
return NS_OK;
}
bool
RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
{
NS_ASSERTION(aTime <= TimeStamp::Now(),
"Given time appears to be in the future");
imgFrame* nextFrame = nsnull;
PRUint32 currentFrameIndex = mAnim->currentAnimationFrameIndex;
PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
PRUint32 timeout = 0;
// Figure out if we have the next full frame. This is more complicated than
// just checking for mFrames.Length() because decoders append their frames
// before they're filled in.
NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
"How did we get 2 indices too far by incrementing?");
// If we don't have a decoder, we know we've got everything we're going to
// get. If we do, we only display fully-downloaded frames; everything else
// gets delayed.
bool haveFullNextFrame = !mDecoder ||
nextFrameIndex < mDecoder->GetCompleteFrameCount();
// If we're done decoding the next frame, go ahead and display it now and
// reinit with the next frame's delay time.
if (haveFullNextFrame) {
if (mFrames.Length() == nextFrameIndex) {
// End of Animation, unless we are looping forever
// If animation mode is "loop once", it's time to stop animating
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
mAnimationFinished = PR_TRUE;
EvaluateAnimation();
}
// We may have used compositingFrame to build a frame, and then copied
// it back into mFrames[..]. If so, delete composite to save memory
if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1) {
mAnim->compositingFrame = nsnull;
}
nextFrameIndex = 0;
if (mLoopCount > 0) {
mLoopCount--;
}
if (!mAnimating) {
// break out early if we are actually done animating
return false;
}
}
if (!(nextFrame = mFrames[nextFrameIndex])) {
// something wrong with the next frame, skip it
mAnim->currentAnimationFrameIndex = nextFrameIndex;
return false;
}
timeout = nextFrame->GetTimeout();
} else {
// Uh oh, the frame we want to show is currently being decoded (partial)
// Wait until the next refresh driver tick and try again
return false;
}
if (!(timeout > 0)) {
mAnimationFinished = PR_TRUE;
EvaluateAnimation();
}
imgFrame *frameToUse = nsnull;
if (nextFrameIndex == 0) {
frameToUse = nextFrame;
aDirtyRect = &(mAnim->firstFrameRefreshArea);
} else {
imgFrame *curFrame = mFrames[currentFrameIndex];
if (!curFrame) {
return false;
}
// Change frame
if (NS_FAILED(DoComposite(&frameToUse, aDirtyRect, curFrame,
nextFrame, nextFrameIndex))) {
// something went wrong, move on to next
NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
nextFrame->SetCompositingFailed(PR_TRUE);
mAnim->currentAnimationFrameIndex = nextFrameIndex;
mAnim->currentAnimationFrameTime = aTime;
return false;
}
nextFrame->SetCompositingFailed(PR_FALSE);
}
// Set currentAnimationFrameIndex at the last possible moment
mAnim->currentAnimationFrameIndex = nextFrameIndex;
mAnim->currentAnimationFrameTime = aTime;
return true;
}
//******************************************************************************
// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
NS_IMETHODIMP_(void)
RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
{
if (!mAnimating || !ShouldAnimate()) {
return;
}
EnsureAnimExists();
// only advance the frame if the current time is greater than or
// equal to the current frame's end time.
TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
bool frameAdvanced = false;
nsIntRect dirtyRect;
while (currentFrameEndTime <= aTime) {
TimeStamp oldFrameEndTime = currentFrameEndTime;
bool didAdvance = AdvanceFrame(aTime, &dirtyRect);
frameAdvanced = frameAdvanced || didAdvance;
currentFrameEndTime = GetCurrentImgFrameEndTime();
// if we didn't advance a frame, and our frame end time didn't change,
// then we need to break out of this loop & wait for the frame(s)
// to finish downloading
if (!frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
break;
}
}
if (frameAdvanced) {
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
if (!observer) {
NS_ERROR("Refreshing image after its imgRequest is gone");
StopAnimation();
return;
}
// Notify listeners that our frame has actually changed
#ifdef DEBUG
mFramesNotified++;
#endif
observer->FrameChanged(this, &dirtyRect);
}
}
//******************************************************************************
/* [noscript] imgIContainer extractFrame(PRUint32 aWhichFrame,
* [const] in nsIntRect aRegion,
@ -495,6 +648,27 @@ RasterImage::GetCurrentImgFrameIndex() const
return 0;
}
TimeStamp
RasterImage::GetCurrentImgFrameEndTime() const
{
imgFrame* currentFrame = mFrames[mAnim->currentAnimationFrameIndex];
TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
PRInt64 timeout = currentFrame->GetTimeout();
if (timeout < 0) {
// We need to return a sentinel value in this case, because our logic
// doesn't work correctly if we have a negative timeout value. The reason
// this positive infinity was chosen was because it works with the loop in
// RequestRefresh() above.
return TimeStamp() + TimeDuration::FromMilliseconds(UINT64_MAX_VAL);
}
TimeDuration durationOfTimeout = TimeDuration::FromMilliseconds(timeout);
TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
return currentFrameEndTime;
}
imgFrame*
RasterImage::GetCurrentImgFrame()
{
@ -1140,25 +1314,19 @@ RasterImage::StartAnimation()
EnsureAnimExists();
NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
// Default timeout to 100: the timer notify code will do the right
// thing, so just get that started.
PRInt32 timeout = 100;
imgFrame *currentFrame = GetCurrentImgFrame();
imgFrame* currentFrame = GetCurrentImgFrame();
if (currentFrame) {
timeout = currentFrame->GetTimeout();
if (timeout < 0) { // -1 means display this frame forever
if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
mAnimationFinished = PR_TRUE;
return NS_ERROR_ABORT;
}
// We need to set the time that this initial frame was first displayed, as
// this is used in AdvanceFrame().
mAnim->currentAnimationFrameTime = TimeStamp::Now();
}
mAnim->timer = do_CreateInstance("@mozilla.org/timer;1");
NS_ENSURE_TRUE(mAnim->timer, NS_ERROR_OUT_OF_MEMORY);
mAnim->timer->InitWithCallback(static_cast<nsITimerCallback*>(this),
timeout, nsITimer::TYPE_REPEATING_SLACK);
return NS_OK;
}
@ -1172,11 +1340,6 @@ RasterImage::StopAnimation()
if (mError)
return NS_ERROR_FAILURE;
if (mAnim->timer) {
mAnim->timer->Cancel();
mAnim->timer = nsnull;
}
return NS_OK;
}
@ -1432,129 +1595,6 @@ RasterImage::SetSourceSizeHint(PRUint32 sizeHint)
return NS_OK;
}
//******************************************************************************
/* void notify(in nsITimer timer); */
NS_IMETHODIMP
RasterImage::Notify(nsITimer *timer)
{
#ifdef DEBUG
mFramesNotified++;
#endif
// This should never happen since the timer is only set up in StartAnimation()
// after mAnim is checked to exist.
NS_ABORT_IF_FALSE(mAnim, "Need anim for Notify()");
NS_ABORT_IF_FALSE(timer, "Need timer for Notify()");
NS_ABORT_IF_FALSE(mAnim->timer == timer,
"RasterImage::Notify() called with incorrect timer");
if (!mAnimating || !ShouldAnimate())
return NS_OK;
nsCOMPtr<imgIContainerObserver> observer(do_QueryReferent(mObserver));
if (!observer) {
// the imgRequest that owns us is dead, we should die now too.
NS_ABORT_IF_FALSE(mAnimationConsumers == 0,
"If no observer, should have no consumers");
if (mAnimating)
StopAnimation();
return NS_OK;
}
if (mFrames.Length() == 0)
return NS_OK;
imgFrame *nextFrame = nsnull;
PRInt32 previousFrameIndex = mAnim->currentAnimationFrameIndex;
PRUint32 nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
PRInt32 timeout = 0;
// Figure out if we have the next full frame. This is more complicated than
// just checking for mFrames.Length() because decoders append their frames
// before they're filled in.
NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= mFrames.Length(),
"How did we get 2 indicies too far by incrementing?");
// If we don't have a decoder, we know we've got everything we're going to get.
// If we do, we only display fully-downloaded frames; everything else gets delayed.
bool haveFullNextFrame = !mDecoder || nextFrameIndex < mDecoder->GetCompleteFrameCount();
// If we're done decoding the next frame, go ahead and display it now and
// reinit the timer with the next frame's delay time.
if (haveFullNextFrame) {
if (mFrames.Length() == nextFrameIndex) {
// End of Animation
// If animation mode is "loop once", it's time to stop animating
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
mAnimationFinished = PR_TRUE;
EvaluateAnimation();
return NS_OK;
} else {
// We may have used compositingFrame to build a frame, and then copied
// it back into mFrames[..]. If so, delete composite to save memory
if (mAnim->compositingFrame && mAnim->lastCompositedFrameIndex == -1)
mAnim->compositingFrame = nsnull;
}
nextFrameIndex = 0;
if (mLoopCount > 0)
mLoopCount--;
}
if (!(nextFrame = mFrames[nextFrameIndex])) {
// something wrong with the next frame, skip it
mAnim->currentAnimationFrameIndex = nextFrameIndex;
mAnim->timer->SetDelay(100);
return NS_OK;
}
timeout = nextFrame->GetTimeout();
} else {
// Uh oh, the frame we want to show is currently being decoded (partial)
// Wait a bit and try again
mAnim->timer->SetDelay(100);
return NS_OK;
}
if (timeout > 0)
mAnim->timer->SetDelay(timeout);
else {
mAnimationFinished = PR_TRUE;
EvaluateAnimation();
}
nsIntRect dirtyRect;
imgFrame *frameToUse = nsnull;
if (nextFrameIndex == 0) {
frameToUse = nextFrame;
dirtyRect = mAnim->firstFrameRefreshArea;
} else {
imgFrame *prevFrame = mFrames[previousFrameIndex];
if (!prevFrame)
return NS_OK;
// Change frame and announce it
if (NS_FAILED(DoComposite(&frameToUse, &dirtyRect, prevFrame,
nextFrame, nextFrameIndex))) {
// something went wrong, move on to next
NS_WARNING("RasterImage::Notify(): Composing Frame Failed\n");
nextFrame->SetCompositingFailed(PR_TRUE);
mAnim->currentAnimationFrameIndex = nextFrameIndex;
return NS_OK;
} else {
nextFrame->SetCompositingFailed(PR_FALSE);
}
}
// Set currentAnimationFrameIndex at the last possible moment
mAnim->currentAnimationFrameIndex = nextFrameIndex;
// Refreshes the screen
observer->FrameChanged(this, &dirtyRect);
return NS_OK;
}
//******************************************************************************
// DoComposite gets called when the timer for animation get fired and we have to
// update the composited frame of the animation.

Просмотреть файл

@ -71,6 +71,7 @@
#endif
class imgIDecoder;
class imgIContainerObserver;
class nsIInputStream;
#define NS_RASTERIMAGE_CID \
@ -81,6 +82,12 @@ class nsIInputStream;
{0xb1, 0x43, 0x33, 0x40, 0xc0, 0x01, 0x12, 0xf7} \
}
/**
* It would be nice if we had a macro for this in prtypes.h.
* TODO: Place this macro in prtypes.h as PR_UINT64_MAX.
*/
#define UINT64_MAX_VAL PRUint64(-1)
/**
* Handles static and animated image containers.
*
@ -88,15 +95,27 @@ class nsIInputStream;
* @par A Quick Walk Through
* The decoder initializes this class and calls AppendFrame() to add a frame.
* Once RasterImage detects more than one frame, it starts the animation
* with StartAnimation().
* with StartAnimation(). Note that the invalidation events for RasterImage are
* generated automatically using nsRefreshDriver.
*
* @par
* StartAnimation() creates a timer. The timer calls Notify when the
* specified frame delay time is up.
* StartAnimation() initializes the animation helper object and sets the time
* the first frame was displayed to the current clock time.
*
* @par
* Notify() moves on to the next frame, sets up the new timer delay, destroys
* the old frame, and forces a redraw via observer->FrameChanged().
* When the refresh driver corresponding to the imgIContainer that this image is
* a part of notifies the RasterImage that it's time to invalidate,
* RequestRefresh() is called with a given TimeStamp to advance to. As long as
* the timeout of the given frame (the frame's "delay") plus the time that frame
* was first displayed is less than or equal to the TimeStamp given,
* RequestRefresh() calls AdvanceFrame().
*
* @par
* AdvanceFrame() is responsible for advancing a single frame of the animation.
* It can return true, meaning that the frame advanced, or false, meaning that
* the frame failed to advance (usually because the next frame hasn't been
* decoded yet). It is also responsible for performing the final animation stop
* procedure if the final frame of a non-looping animation is reached.
*
* @par
* Each frame can have a different method of removing itself. These are
@ -151,7 +170,6 @@ class imgDecodeWorker;
class Decoder;
class RasterImage : public Image
, public nsITimerCallback
, public nsIProperties
, public nsSupportsWeakReference
#ifdef DEBUG
@ -160,7 +178,6 @@ class RasterImage : public Image
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSIPROPERTIES
#ifdef DEBUG
NS_DECL_IMGICONTAINERDEBUG
@ -183,6 +200,7 @@ public:
NS_SCRIPTABLE NS_IMETHOD LockImage(void);
NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
// END NS_DECL_IMGICONTAINER
RasterImage(imgStatusTracker* aStatusTracker = nsnull);
@ -334,6 +352,10 @@ private:
//! Area of the first frame that needs to be redrawn on subsequent loops.
nsIntRect firstFrameRefreshArea;
PRUint32 currentAnimationFrameIndex; // 0 to numFrames-1
// the time that the animation advanced to the current frame
TimeStamp currentAnimationFrameTime;
//! Track the last composited frame for Optimizations (See DoComposite code)
PRInt32 lastCompositedFrameIndex;
/** For managing blending of frames
@ -352,23 +374,33 @@ private:
* when it's done with the current frame.
*/
nsAutoPtr<imgFrame> compositingPrevFrame;
//! Timer to animate multiframed images
nsCOMPtr<nsITimer> timer;
Anim() :
firstFrameRefreshArea(),
currentAnimationFrameIndex(0),
lastCompositedFrameIndex(-1)
{
;
}
~Anim()
{
if (timer)
timer->Cancel();
}
lastCompositedFrameIndex(-1) {}
~Anim() {}
};
/**
* Advances the animation. Typically, this will advance a single frame, but it
* may advance multiple frames. This may happen if we have infrequently
* "ticking" refresh drivers (e.g. in background tabs), or extremely short-
* lived animation frames.
*
* @param aTime the time that the animation should advance to. This will
* typically be <= TimeStamp::Now().
*
* @param [out] aDirtyRect a pointer to an nsIntRect which encapsulates the
* area to be repainted after the frame is advanced.
*
* @returns true, if the frame was successfully advanced, false if it was not
* able to be advanced (e.g. the frame to which we want to advance is
* still decoding). Note: If false is returned, then aDirtyRect will
* remain unmodified.
*/
bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect);
/**
* Deletes and nulls out the frame in mFrames[framenum].
*
@ -385,6 +417,7 @@ private:
imgFrame* GetCurrentImgFrame();
imgFrame* GetCurrentDrawableImgFrame();
PRUint32 GetCurrentImgFrameIndex() const;
mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
inline void EnsureAnimExists()
{
@ -405,7 +438,7 @@ private:
LockImage();
}
}
/** Function for doing the frame compositing of animations
*
* @param aFrameToUse Set by DoComposite

Просмотреть файл

@ -171,7 +171,6 @@ SVGDrawingCallback::operator()(gfxContext* aContext,
gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext);
aContext->Multiply(gfxMatrix(aTransform).Invert());
nsPresContext* presContext = presShell->GetPresContext();
NS_ABORT_IF_FALSE(presContext, "pres shell w/out pres context");
@ -329,6 +328,14 @@ VectorImage::GetWidth(PRInt32* aWidth)
return NS_OK;
}
//******************************************************************************
/* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */
NS_IMETHODIMP_(void)
VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime)
{
// TODO: Implement for b666446.
}
//******************************************************************************
/* readonly attribute PRInt32 height; */
NS_IMETHODIMP

Просмотреть файл

@ -42,6 +42,7 @@
#include "Image.h"
#include "nsIStreamListener.h"
#include "nsWeakReference.h"
#include "mozilla/TimeStamp.h"
class imgIDecoderObserver;
@ -76,6 +77,7 @@ public:
NS_SCRIPTABLE NS_IMETHOD LockImage(void);
NS_SCRIPTABLE NS_IMETHOD UnlockImage(void);
NS_SCRIPTABLE NS_IMETHOD ResetAnimation(void);
NS_IMETHOD_(void) RequestRefresh(const mozilla::TimeStamp& aTime);
// END NS_DECL_IMGICONTAINER
VectorImage(imgStatusTracker* aStatusTracker = nsnull);

Просмотреть файл

@ -93,8 +93,25 @@ _TEST_FILES = imgutils.js \
# test_bug478398.html disabled - See bug 579139
_CHROME_FILES = imgutils.js \
animationPolling.js \
lime-anim-100x100.svg \
animation.svg \
test_animSVGImage.html \
test_animation.html \
animated-gif-finalframe.gif \
animated-gif.gif \
animated-gif2.gif \
test_svg_animatedGIF.html \
test_bullet_animation.html \
test_background_image_anim.html \
filter.svg \
filter-final.svg \
test_svg_filter_animation.html \
test_xultree_animation.xhtml \
test_changeOfSource.html \
test_undisplayed_iframe.html \
iframe.html \
ref-iframe.html \
$(NULL)
libs:: $(_TEST_FILES)

Двоичные данные
modules/libpr0n/test/mochitest/animated-gif-finalframe.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 72 B

Двоичные данные
modules/libpr0n/test/mochitest/animated-gif.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 146 B

Двоичные данные
modules/libpr0n/test/mochitest/animated-gif2.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 165 B

Просмотреть файл

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<image id="anim" xlink:href="animated-gif.gif" width="40" height="40"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 170 B

Просмотреть файл

@ -0,0 +1,307 @@
var currentTest;
var gIsImageLoaded = false;
function pollForSuccess ()
{
if (!currentTest.isTestFinished) {
currentTest.checkImage();
setTimeout(pollForSuccess, currentTest.pollFreq);
}
};
function imageLoadCallback()
{
gIsImageLoaded = true;
}
function referencePoller()
{
currentTest.takeReferenceSnapshot();
}
function failTest ()
{
if (currentTest.isTestFinished || currentTest.closeFunc) {
return;
}
ok(false, "timing out after " + currentTest.timeout + "ms. "
+ "Animated image still doesn't look correct, " + "after call #"
+ currentTest.onStopFrameCounter + " to onStopFrame");
currentTest.wereFailures = true;
currentTest.enableDisplay(document.getElementById(currentTest.debugElementId));
currentTest.cleanUpAndFinish();
};
/**
* Create a new AnimationTest object.
*
* @param pollFreq The amount of time (in ms) to wait between consecutive
* snapshots if the reference image and the test image don't match.
* @param timeout The total amount of time (in ms) to wait before declaring the
* test as failed.
* @param referenceElementId The id attribute of the reference image element.
* @param imageElementId The id attribute of the test image element.
* @param debugElementId The id attribute of the div where links should be
* appended if the test fails.
* @param cleanId The id attribute of the div or element to use as the 'clean'
* test. This element is only enabled when we are testing to verify that
* the reference image has been loaded. It can be undefined.
* @param srcAttr The location of the source of the image, for preloading. This
* is usually not required, but it useful for preloading reference
* images.
* @param xulTest A boolean value indicating whether or not this is a XUL test
* (uses hidden=true/false rather than display: none to hide/show
* elements).
* @param closeFunc A function that should be called when this test is finished.
* If null, then cleanUpAndFinish() will be called. This can be used to
* chain tests together, so they are all finished exactly once.
* @returns {AnimationTest}
*/
function AnimationTest(pollFreq, timeout, referenceElementId, imageElementId,
debugElementId, cleanId, srcAttr, xulTest, closeFunc)
{
// We want to test the cold loading behavior, so clear cache in case an
// earlier test got our image in there already.
clearImageCache();
this.wereFailures = false;
this.pollFreq = pollFreq;
this.timeout = timeout;
this.imageElementId = imageElementId;
this.referenceElementId = referenceElementId;
this.srcAttr = srcAttr;
this.debugElementId = debugElementId;
this.referenceSnapshot = ""; // value will be set in takeReferenceSnapshot()
this.onStopFrameCounter = 0;
this.isTestFinished = false;
this.numRefsTaken = 0;
this.blankWaitTime = 0;
this.cleanId = cleanId ? cleanId : '';
this.xulTest = xulTest ? xulTest : '';
this.closeFunc = closeFunc ? closeFunc : '';
if (this.srcAttr) {
this.myImage = new Image();
this.myImage.onload = imageLoadCallback;
this.myImage.src = this.srcAttr;
} else {
gIsImageLoaded = true;
}
}
AnimationTest.prototype.outputDebugInfo = function(message, id, dataUri)
{
var debugElement = document.getElementById(this.debugElementId);
var newDataUriElement = document.createElement("a");
newDataUriElement.setAttribute("id", id);
newDataUriElement.setAttribute("href", dataUri);
newDataUriElement.appendChild(document.createTextNode(message));
debugElement.appendChild(newDataUriElement);
var brElement = document.createElement("br");
debugElement.appendChild(brElement);
};
AnimationTest.prototype.isFinished = function()
{
return this.isTestFinished;
};
AnimationTest.prototype.takeCleanSnapshot = function()
{
var cleanElement;
if (this.cleanId) {
cleanElement = document.getElementById(this.cleanId);
}
// Enable clean page comparison element
if (cleanElement) {
this.enableDisplay(cleanElement);
}
// Take a snapshot of the initial (clean) page
this.cleanSnapshot = snapshotWindow(window, false);
// Disable the clean page comparison element
if (cleanElement) {
this.disableDisplay(cleanElement);
}
var dataString1 = "Clean Snapshot";
this.outputDebugInfo(dataString1, 'cleanSnap',
this.cleanSnapshot.toDataURL());
};
AnimationTest.prototype.takeBlankSnapshot = function()
{
// Take a snapshot of the initial (essentially blank) page
this.blankSnapshot = snapshotWindow(window, false);
var dataString1 = "Initial Blank Snapshot";
this.outputDebugInfo(dataString1, 'blank1Snap',
this.blankSnapshot.toDataURL());
};
/**
* Begin the AnimationTest. This will utilize the information provided in the
* constructor to invoke a mochitest on animated images. It will automatically
* fail if allowed to run past the timeout.
*/
AnimationTest.prototype.beginTest = function ()
{
SimpleTest.waitForExplicitFinish();
currentTest = this;
this.takeReferenceSnapshot();
// In case something goes wrong, fail earlier than mochitest timeout,
// and with more information.
setTimeout(failTest, this.timeout);
this.disableDisplay(document.getElementById(this.imageElementId));
setTimeout(pollForSuccess, 10);
};
AnimationTest.prototype.checkImage = function ()
{
if (this.isTestFinished) {
return;
}
this.onStopFrameCounter++;
// Make sure the image is visible
this.enableDisplay(document.getElementById(this.imageElementId));
var currentSnapshot = snapshotWindow(window, false);
var result = compareSnapshots(currentSnapshot, this.referenceSnapshot, true);
var dataString = "Snapshot #" + this.onStopFrameCounter;
this.outputDebugInfo(dataString, 'snap' + this.onStopFrameCounter,
currentSnapshot.toDataURL());
if (result[0]) {
// SUCCESS!
ok(true, "Animated image looks correct, " + "at call #"
+ this.onStopFrameCounter + " to onStopFrame");
this.cleanUpAndFinish();
}
};
AnimationTest.prototype.takeReferenceSnapshot = function ()
{
this.numRefsTaken++;
// Test to make sure the reference image doesn't match a clean snapshot
if (!this.cleanSnapshot) {
this.takeCleanSnapshot();
}
// Used later to verify that the reference div disappeared
if (!this.blankSnapshot) {
this.takeBlankSnapshot();
}
// Make sure the animation section is hidden
this.disableDisplay(document.getElementById(this.imageElementId));
// Show reference div, & take a snapshot
var referenceDiv = document.getElementById(this.referenceElementId);
this.enableDisplay(referenceDiv);
this.referenceSnapshot = snapshotWindow(window, false);
var snapResult = compareSnapshots(this.cleanSnapshot, this.referenceSnapshot, false);
if (!snapResult[0]) {
if (this.blankWaitTime > 2000) {
// if it took longer than two seconds to load the image, we probably
// have a problem.
this.wereFailures = true;
ok(snapResult[0],
"Reference snapshot shouldn't match clean (non-image) snapshot");
} else {
this.blankWaitTime += 20;
// let's wait a bit and see if it clears up
setTimeout(referencePoller, 20);
return;
}
}
ok(snapResult[0],
"Reference snapshot shouldn't match clean (non-image) snapshot");
var dataString = "Reference Snapshot #" + this.numRefsTaken;
this.outputDebugInfo(dataString, 'refSnapId',
this.referenceSnapshot.toDataURL());
// Re-hide reference div, and take another snapshot to be sure it's gone
this.disableDisplay(referenceDiv);
this.testBlankCameBack();
};
AnimationTest.prototype.enableDisplay = function(element)
{
if (!element) {
return;
}
if (!this.xulTest) {
element.style.display = '';
} else {
element.setAttribute('hidden', 'false');
}
};
AnimationTest.prototype.disableDisplay = function(element)
{
if (!element) {
return;
}
if (!this.xulTest) {
element.style.display = 'none';
} else {
element.setAttribute('hidden', 'true');
}
};
AnimationTest.prototype.testBlankCameBack = function()
{
var blankSnapshot2 = snapshotWindow(window, false);
var result = compareSnapshots(this.blankSnapshot, blankSnapshot2, true);
ok(result[0], "Reference image should disappear when it becomes display:none");
if (!result[0]) {
this.wereFailures = true;
var dataString = "Second Blank Snapshot";
this.outputDebugInfo(dataString, 'blank2SnapId', result[2]);
}
};
AnimationTest.prototype.cleanUpAndFinish = function ()
{
// On the off chance that failTest and checkImage are triggered
// back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
if (this.isTestFinished) {
return;
}
this.isTestFinished = true;
// Call our closing function, if one exists
if (this.closeFunc) {
this.closeFunc();
return;
}
if (this.wereFailures) {
document.getElementById(this.debugElementId).style.display = 'block';
}
SimpleTest.finish();
document.getElementById(this.debugElementId).style.display = "";
};

Просмотреть файл

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
<feImage xlink:href="animated-gif-finalframe.gif"/>
</filter>
<g>
<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 299 B

Просмотреть файл

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="filter1" x="0%" y="0%" width="100%" height="100%">
<feImage xlink:href="animated-gif.gif"/>
</filter>
<g>
<rect x="0" y="0" width="100%" height="100%" filter="url(#filter1)"/>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 288 B

Просмотреть файл

@ -0,0 +1,5 @@
<html>
<body bgcolor="gray">
<img src="animated-gif.gif">
</body>
</html>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше