зеркало из https://github.com/mozilla/gecko-dev.git
merge m-c to fx-team
This commit is contained in:
Коммит
be2df34164
|
@ -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)
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 72 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 146 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче