mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to fx-team a=merge
This commit is contained in:
commit
53b2bacf3e
@ -17,8 +17,6 @@ class AccessibleWrap;
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
struct MaiUtilClass;
|
||||
|
||||
extern "C" {
|
||||
void actionInterfaceInitCB(AtkActionIface* aIface);
|
||||
void componentInterfaceInitCB(AtkComponentIface* aIface);
|
||||
|
@ -23,7 +23,6 @@ struct nsRect;
|
||||
class nsIFrame;
|
||||
class nsIAtom;
|
||||
class nsIPersistentProperties;
|
||||
class nsView;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
@ -39,7 +38,6 @@ class HTMLLIAccessible;
|
||||
class HyperTextAccessible;
|
||||
class ImageAccessible;
|
||||
class KeyBinding;
|
||||
class MathMLAccessible;
|
||||
class ProxyAccessible;
|
||||
class Relation;
|
||||
class RootAccessible;
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
class nsAccessiblePivot;
|
||||
|
||||
class nsIScrollableView;
|
||||
|
||||
const uint32_t kDefaultCacheLength = 128;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
#include "BaseAccessibles.h"
|
||||
|
||||
class nsGenericHTMLElement;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
|
@ -8,8 +8,6 @@
|
||||
|
||||
#include "HTMLFormControlAccessible.h"
|
||||
|
||||
class nsIMutableArray;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "TableAccessible.h"
|
||||
#include "TableCellAccessible.h"
|
||||
|
||||
class nsITableLayout;
|
||||
class nsITableCellLayout;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -20,11 +20,7 @@ class Accessible;
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
class nsINode;
|
||||
class nsIContent;
|
||||
class nsIFrame;
|
||||
class nsIPresShell;
|
||||
class nsPluginFrame;
|
||||
|
||||
// 0e7e6879-854b-4260-bc6e-525b5fb5cf34
|
||||
#define NS_IACCESSIBILITYSERVICE_IID \
|
||||
|
@ -191,6 +191,12 @@ this.EventManager.prototype = {
|
||||
let acc = aEvent.accessible;
|
||||
if (acc === this.contentControl.vc.position) {
|
||||
this.present(Presentation.nameChanged(acc));
|
||||
} else {
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['text', 'all']);
|
||||
if (liveRegion) {
|
||||
this.present(Presentation.nameChanged(acc, isPolite));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -293,6 +299,12 @@ this.EventManager.prototype = {
|
||||
if (position === target ||
|
||||
Utils.getEmbeddedControl(position) === target) {
|
||||
this.present(Presentation.valueChanged(target));
|
||||
} else {
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['text', 'all']);
|
||||
if (liveRegion) {
|
||||
this.present(Presentation.valueChanged(target, isPolite));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -523,18 +523,19 @@ B2GPresenter.prototype.pivotChanged =
|
||||
};
|
||||
|
||||
B2GPresenter.prototype.nameChanged =
|
||||
function B2GPresenter_nameChanged(aAccessible) {
|
||||
function B2GPresenter_nameChanged(aAccessible, aIsPolite = true) {
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
eventType: 'name-change',
|
||||
data: aAccessible.name
|
||||
data: aAccessible.name,
|
||||
options: {enqueue: aIsPolite}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
B2GPresenter.prototype.valueChanged =
|
||||
function B2GPresenter_valueChanged(aAccessible) {
|
||||
function B2GPresenter_valueChanged(aAccessible, aIsPolite = true) {
|
||||
|
||||
// the editable value changes are handled in the text changed presenter
|
||||
if (Utils.getState(aAccessible).contains(States.EDITABLE)) {
|
||||
@ -545,7 +546,8 @@ B2GPresenter.prototype.valueChanged =
|
||||
type: this.type,
|
||||
details: {
|
||||
eventType: 'value-change',
|
||||
data: aAccessible.value
|
||||
data: aAccessible.value,
|
||||
options: {enqueue: aIsPolite}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -15,8 +15,6 @@
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
struct objc_class;
|
||||
|
||||
class RootAccessibleWrap : public RootAccessible
|
||||
{
|
||||
public:
|
||||
|
@ -44,6 +44,17 @@
|
||||
document.getElementById('fruit').setAttribute('aria-label', 'banana');
|
||||
}
|
||||
|
||||
function renameSlider() {
|
||||
document.getElementById('slider').setAttribute(
|
||||
'aria-label', 'mover');
|
||||
}
|
||||
|
||||
function changeSliderValue() {
|
||||
document.getElementById('slider').setAttribute('aria-valuenow', '5');
|
||||
document.getElementById('slider').setAttribute(
|
||||
'aria-valuetext', 'medium');
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#windows {
|
||||
@ -89,5 +100,9 @@
|
||||
</div>
|
||||
<button id="home">Home</button>
|
||||
<button id="fruit" aria-label="apple"></button>
|
||||
<div id="live" aria-live="polite" aria-label="live">
|
||||
<div id="slider" role="slider" aria-label="slider" aria-valuemin="0"
|
||||
aria-valuemax="10" aria-valuenow="0"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -629,7 +629,7 @@ ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
|
||||
function ExpectedValueChange(aValue, aOptions) {
|
||||
ExpectedPresent.call(this, {
|
||||
eventType: 'value-change',
|
||||
data: [aValue]
|
||||
data: aValue
|
||||
}, null, aOptions);
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,12 @@
|
||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||
[ContentMessages.simpleMoveNext,
|
||||
new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
|
||||
[ContentMessages.simpleMoveNext,
|
||||
new ExpectedCursorChange(['slider', '0', {'string': 'slider'}])],
|
||||
|
||||
// Simple traversal backward
|
||||
[ContentMessages.simpleMovePrevious,
|
||||
new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
|
||||
[ContentMessages.simpleMovePrevious,
|
||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||
[ContentMessages.simpleMovePrevious,
|
||||
@ -92,7 +96,7 @@
|
||||
// Move from an inner frame to the last element in the parent doc
|
||||
[ContentMessages.simpleMoveLast,
|
||||
new ExpectedCursorChange(
|
||||
['apple', {'string': 'pushbutton'}], { b2g_todo: true })],
|
||||
['slider', '0', {'string': 'slider'}], { b2g_todo: true })],
|
||||
|
||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||
|
||||
@ -147,6 +151,12 @@
|
||||
new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
|
||||
[doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
|
||||
|
||||
// Name and value changes inside a live-region (no cursor present)
|
||||
[doc.defaultView.renameSlider,
|
||||
new ExpectedNameChange('mover')],
|
||||
[doc.defaultView.changeSliderValue,
|
||||
new ExpectedValueChange('medium')],
|
||||
|
||||
// Blur button and reset cursor
|
||||
[ContentMessages.focusSelector('button#fruit', true), null],
|
||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||
@ -222,14 +232,14 @@
|
||||
|
||||
// Open dialog in outer doc, while cursor is also in outer doc
|
||||
[ContentMessages.simpleMoveLast,
|
||||
new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
|
||||
new ExpectedCursorChange(['mover'])],
|
||||
[doc.defaultView.showAlert,
|
||||
new ExpectedCursorChange(['This is an alert!',
|
||||
{'string': 'headingLevel', 'args': [1]},
|
||||
{'string': 'dialog'}])],
|
||||
|
||||
[doc.defaultView.hideAlert,
|
||||
new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
|
||||
new ExpectedCursorChange(['mover'])],
|
||||
|
||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
|
||||
class nsIArray;
|
||||
class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include "XULMenuAccessible.h"
|
||||
#include "XULSelectControlAccessible.h"
|
||||
|
||||
class nsIWeakReference;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
|
@ -323,7 +323,7 @@ pref("media.fragmented-mp4.gonk.enabled", true);
|
||||
pref("media.video-queue.default-size", 3);
|
||||
|
||||
// optimize images' memory usage
|
||||
pref("image.decode-only-on-draw.enabled", true);
|
||||
pref("image.decode-only-on-draw.enabled", false);
|
||||
pref("image.mem.allow_locking_in_content_processes", true);
|
||||
// Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
|
||||
// Almost everything that was factored into 'max_decoded_image_kb' is now stored
|
||||
@ -1124,8 +1124,5 @@ pref("dom.mozSettings.allowForceReadOnly", false);
|
||||
// RequestSync API is enabled by default on B2G.
|
||||
pref("dom.requestSync.enabled", true);
|
||||
|
||||
// Use vsync aligned rendering
|
||||
pref("gfx.vsync.hw-vsync.enabled", true);
|
||||
pref("gfx.vsync.compositor", true);
|
||||
// Resample touch events on b2g
|
||||
pref("gfx.touch.resample", true);
|
||||
pref("gfx.vsync.refreshdriver", true);
|
||||
|
@ -30,14 +30,19 @@ html xul|scrollbar {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Scrollbar code will reset the margin to the correct side depending on
|
||||
layout.scrollbar.side pref */
|
||||
xul|scrollbar[orient="vertical"] {
|
||||
margin-left: -8px;
|
||||
-moz-margin-start: -8px;
|
||||
min-width: 8px;
|
||||
max-width: 8px;
|
||||
}
|
||||
|
||||
/* workaround for bug 1119057: as -moz-margin-start may not work as expected,
|
||||
* force a right margin value in RTL mode. */
|
||||
[dir="rtl"] xul|scrollbar[root="true"][orient="vertical"] {
|
||||
-moz-margin-start: unset;
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
xul|scrollbar[orient="vertical"] xul|thumb {
|
||||
max-width: 6px !important;
|
||||
min-width: 6px !important;
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "a7dcc5fb595030dab140d5ff0e7eb5ef04017d51",
|
||||
"git_revision": "9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "5e00921aa36301b179b4912d352e6bcc0de89edc",
|
||||
"revision": "6238380e300e25c4ec8aea2a8804d0ca3675f7fb",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="90f848a40efad820ab00fa52bec52dff37255b12"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a7dcc5fb595030dab140d5ff0e7eb5ef04017d51"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="9d4f756aa35cb7f030a92f3c1f65fb55254ddd1d"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
|
@ -209,6 +209,7 @@
|
||||
@RESPATH@/components/dom_json.xpt
|
||||
@RESPATH@/components/dom_messages.xpt
|
||||
@RESPATH@/components/dom_power.xpt
|
||||
@RESPATH@/components/dom_push.xpt
|
||||
@RESPATH@/components/dom_quota.xpt
|
||||
@RESPATH@/components/dom_range.xpt
|
||||
@RESPATH@/components/dom_security.xpt
|
||||
@ -631,7 +632,7 @@
|
||||
@RESPATH@/components/AppsService.manifest
|
||||
@RESPATH@/components/Push.js
|
||||
@RESPATH@/components/Push.manifest
|
||||
@RESPATH@/components/PushServiceLauncher.js
|
||||
@RESPATH@/components/PushNotificationService.js
|
||||
|
||||
@RESPATH@/components/InterAppComm.manifest
|
||||
@RESPATH@/components/InterAppCommService.js
|
||||
|
@ -30,6 +30,7 @@ externalProtocolUnknown=<Unknown>
|
||||
externalProtocolChkMsg=Remember my choice for all links of this type.
|
||||
externalProtocolLaunchBtn=Launch application
|
||||
malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
|
||||
unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
|
||||
phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information.
|
||||
cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
|
||||
corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
|
||||
|
@ -374,6 +374,7 @@
|
||||
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
|
||||
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
|
||||
<h1 id="et_malwareBlocked">&malwareBlocked.title;</h1>
|
||||
<h1 id="et_unwantedBlocked">&unwantedBlocked.title;</h1>
|
||||
<h1 id="et_cspBlocked">&cspBlocked.title;</h1>
|
||||
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
|
||||
<h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
|
||||
@ -401,6 +402,7 @@
|
||||
<div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
|
||||
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
|
||||
<div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div>
|
||||
<div id="ed_unwantedBlocked">&unwantedBlocked.longDesc;</div>
|
||||
<div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
|
||||
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
|
||||
<div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
|
||||
|
@ -79,6 +79,9 @@
|
||||
case "phishingBlocked" :
|
||||
initPage_phishing();
|
||||
break;
|
||||
case "unwantedBlocked" :
|
||||
initPage_unwanted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +90,7 @@
|
||||
*/
|
||||
function initPage_malware()
|
||||
{
|
||||
// Remove phishing strings
|
||||
// Remove phishing and unwanted strings
|
||||
var el = document.getElementById("errorTitleText_phishing");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
@ -97,18 +100,57 @@
|
||||
el = document.getElementById("errorLongDescText_phishing");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorTitleText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorShortDescText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorLongDescText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
// Set sitename
|
||||
document.getElementById("malware_sitename").textContent = getHostString();
|
||||
document.title = document.getElementById("errorTitleText_malware")
|
||||
.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize custom strings and functionality for blocked malware case
|
||||
*/
|
||||
function initPage_unwanted()
|
||||
{
|
||||
// Remove phishing and malware strings
|
||||
var el = document.getElementById("errorTitleText_phishing");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorShortDescText_phishing");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorLongDescText_phishing");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorTitleText_malware");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorShortDescText_malware");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorLongDescText_malware");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
// Set sitename
|
||||
document.getElementById("unwanted_sitename").textContent = getHostString();
|
||||
document.title = document.getElementById("errorTitleText_unwanted")
|
||||
.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize custom strings and functionality for blocked phishing case
|
||||
*/
|
||||
function initPage_phishing()
|
||||
{
|
||||
// Remove malware strings
|
||||
// Remove malware and unwanted strings
|
||||
var el = document.getElementById("errorTitleText_malware");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
@ -118,6 +160,15 @@
|
||||
el = document.getElementById("errorLongDescText_malware");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorTitleText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorShortDescText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
el = document.getElementById("errorLongDescText_unwanted");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
// Set sitename
|
||||
document.getElementById("phishing_sitename").textContent = getHostString();
|
||||
document.title = document.getElementById("errorTitleText_phishing")
|
||||
@ -161,6 +212,7 @@
|
||||
<div id="errorTitle">
|
||||
<h1 id="errorTitleText_phishing">&safeb.blocked.phishingPage.title;</h1>
|
||||
<h1 id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
|
||||
<h1 id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
|
||||
</div>
|
||||
|
||||
<div id="errorLongContent">
|
||||
@ -169,12 +221,14 @@
|
||||
<div id="errorShortDesc">
|
||||
<p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc;</p>
|
||||
<p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
|
||||
<p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
|
||||
</div>
|
||||
|
||||
<!-- Long Description -->
|
||||
<div id="errorLongDesc">
|
||||
<p id="errorLongDescText_phishing">&safeb.blocked.phishingPage.longDesc;</p>
|
||||
<p id="errorLongDescText_malware">&safeb.blocked.malwarePage.longDesc;</p>
|
||||
<p id="errorLongDescText_unwanted">&safeb.blocked.unwantedPage.longDesc;</p>
|
||||
</div>
|
||||
|
||||
<!-- Action buttons -->
|
||||
|
@ -2659,7 +2659,7 @@ let BrowserOnClick = {
|
||||
msg.data.sslStatusAsString);
|
||||
break;
|
||||
case "Browser:SiteBlockedError":
|
||||
this.onAboutBlocked(msg.data.elementId, msg.data.isMalware,
|
||||
this.onAboutBlocked(msg.data.elementId, msg.data.reason,
|
||||
msg.data.isTopFrame, msg.data.location);
|
||||
break;
|
||||
case "Browser:EnableOnlineMode":
|
||||
@ -2843,10 +2843,15 @@ let BrowserOnClick = {
|
||||
}
|
||||
},
|
||||
|
||||
onAboutBlocked: function (elementId, isMalware, isTopFrame, location) {
|
||||
// Depending on what page we are displaying here (malware/phishing)
|
||||
onAboutBlocked: function (elementId, reason, isTopFrame, location) {
|
||||
// Depending on what page we are displaying here (malware/phishing/unwanted)
|
||||
// use the right strings and links for each.
|
||||
let bucketName = isMalware ? "WARNING_MALWARE_PAGE_":"WARNING_PHISHING_PAGE_";
|
||||
let bucketName = "WARNING_PHISHING_PAGE_";
|
||||
if (reason === 'malware') {
|
||||
bucketName = "WARNING_MALWARE_PAGE_";
|
||||
} else if (reason === 'unwanted') {
|
||||
bucketName = "WARNING_UNWANTED_PAGE_";
|
||||
}
|
||||
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
|
||||
let nsISecTel = Ci.nsISecurityUITelemetry;
|
||||
bucketName += isTopFrame ? "TOP_" : "FRAME_";
|
||||
@ -2857,33 +2862,19 @@ let BrowserOnClick = {
|
||||
break;
|
||||
|
||||
case "reportButton":
|
||||
// This is the "Why is this site blocked" button. For malware,
|
||||
// we can fetch a site-specific report, for phishing, we redirect
|
||||
// to the generic page describing phishing protection.
|
||||
// This is the "Why is this site blocked" button. We redirect
|
||||
// to the generic page describing phishing/malware protection.
|
||||
|
||||
// We log even if malware/phishing info URL couldn't be found:
|
||||
// We log even if malware/phishing/unwanted info URL couldn't be found:
|
||||
// the measurement is for how many users clicked the WHY BLOCKED button
|
||||
secHistogram.add(nsISecTel[bucketName + "WHY_BLOCKED"]);
|
||||
|
||||
if (isMalware) {
|
||||
// Get the stop badware "why is this blocked" report url,
|
||||
// append the current url, and go there.
|
||||
try {
|
||||
let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
|
||||
reportURL += location;
|
||||
gBrowser.loadURI(reportURL);
|
||||
} catch (e) {
|
||||
Components.utils.reportError("Couldn't get malware report URL: " + e);
|
||||
}
|
||||
}
|
||||
else { // It's a phishing site, not malware
|
||||
openHelpLink("phishing-malware", false, "current");
|
||||
}
|
||||
openHelpLink("phishing-malware", false, "current");
|
||||
break;
|
||||
|
||||
case "ignoreWarningButton":
|
||||
secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
|
||||
this.ignoreWarningButton(isMalware);
|
||||
this.ignoreWarningButton(reason);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -2910,7 +2901,7 @@ let BrowserOnClick = {
|
||||
}
|
||||
},
|
||||
|
||||
ignoreWarningButton: function (isMalware) {
|
||||
ignoreWarningButton: function (reason) {
|
||||
// Allow users to override and continue through to the site,
|
||||
// but add a notify bar as a reminder, so that they don't lose
|
||||
// track after, e.g., tab switching.
|
||||
@ -2929,7 +2920,7 @@ let BrowserOnClick = {
|
||||
}];
|
||||
|
||||
let title;
|
||||
if (isMalware) {
|
||||
if (reason === 'malware') {
|
||||
title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
|
||||
buttons[1] = {
|
||||
label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
|
||||
@ -2938,7 +2929,7 @@ let BrowserOnClick = {
|
||||
openUILinkIn(gSafeBrowsing.getReportURL('MalwareError'), 'tab');
|
||||
}
|
||||
};
|
||||
} else {
|
||||
} else if (reason === 'phishing') {
|
||||
title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
|
||||
buttons[1] = {
|
||||
label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
|
||||
@ -2947,6 +2938,10 @@ let BrowserOnClick = {
|
||||
openUILinkIn(gSafeBrowsing.getReportURL('Error'), 'tab');
|
||||
}
|
||||
};
|
||||
} else if (reason === 'unwanted') {
|
||||
title = gNavigatorBundle.getString("safebrowsing.reportedUnwantedSite");
|
||||
// There is no button for reporting errors since Google doesn't currently
|
||||
// provide a URL endpoint for these reports.
|
||||
}
|
||||
|
||||
let notificationBox = gBrowser.getNotificationBox();
|
||||
@ -3994,6 +3989,11 @@ var XULBrowserWindow = {
|
||||
},
|
||||
|
||||
showTooltip: function (x, y, tooltip) {
|
||||
if (Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService).
|
||||
getCurrentSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The x,y coordinates are relative to the <browser> element using
|
||||
// the chrome zoom level.
|
||||
let elt = document.getElementById("remoteBrowserTooltip");
|
||||
|
@ -123,6 +123,13 @@ let handleContentContextMenu = function (event) {
|
||||
InlineSpellCheckerContent.initContextMenu(event, editFlags, this);
|
||||
}
|
||||
|
||||
// Set the event target first as the copy image command needs it to
|
||||
// determine what was context-clicked on. Then, update the state of the
|
||||
// commands on the context menu.
|
||||
docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit)
|
||||
.setCommandNode(event.target);
|
||||
event.target.ownerDocument.defaultView.updateCommands("contentcontextmenu");
|
||||
|
||||
let customMenuItems = PageMenuChild.build(event.target);
|
||||
let principal = doc.nodePrincipal;
|
||||
sendSyncMessage("contextmenu",
|
||||
@ -377,9 +384,15 @@ let ClickEventHandler = {
|
||||
},
|
||||
|
||||
onAboutBlocked: function (targetElement, ownerDoc) {
|
||||
var reason = 'phishing';
|
||||
if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
|
||||
reason = 'malware';
|
||||
} else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
|
||||
reason = 'unwanted';
|
||||
}
|
||||
sendAsyncMessage("Browser:SiteBlockedError", {
|
||||
location: ownerDoc.location.href,
|
||||
isMalware: /e=malwareBlocked/.test(ownerDoc.documentURI),
|
||||
reason: reason,
|
||||
elementId: targetElement.getAttribute("id"),
|
||||
isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
|
||||
});
|
||||
|
@ -628,7 +628,9 @@ nsContextMenu.prototype = {
|
||||
this.inSyntheticDoc = ownerDoc.mozSyntheticDocument;
|
||||
// First, do checks for nodes that never have children.
|
||||
if (this.target.nodeType == Node.ELEMENT_NODE) {
|
||||
// See if the user clicked on an image.
|
||||
// See if the user clicked on an image. This check mirrors
|
||||
// nsDocumentViewer::GetInImage. Make sure to update both if this is
|
||||
// changed.
|
||||
if (this.target instanceof Ci.nsIImageLoadingContent &&
|
||||
this.target.currentURI) {
|
||||
this.onImage = true;
|
||||
|
@ -486,6 +486,11 @@ Sanitizer.prototype = {
|
||||
.getService(Ci.nsISiteSecurityService);
|
||||
sss.clearAll();
|
||||
|
||||
// Clear all push notification subscriptions
|
||||
var push = Cc["@mozilla.org/push/NotificationService;1"]
|
||||
.getService(Ci.nsIPushNotificationService);
|
||||
push.clearAll();
|
||||
|
||||
TelemetryStopwatch.finish("FX_SANITIZE_SITESETTINGS");
|
||||
},
|
||||
|
||||
|
@ -144,7 +144,6 @@ skip-if = e10s # bug 967873 means permitUnload doesn't work in e10s mode
|
||||
[browser_bookmark_titles.js]
|
||||
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
||||
[browser_bug304198.js]
|
||||
skip-if = e10s
|
||||
[browser_bug321000.js]
|
||||
skip-if = true # browser_bug321000.js is disabled because newline handling is shaky (bug 592528)
|
||||
[browser_bug329212.js]
|
||||
@ -215,7 +214,6 @@ skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
|
||||
[browser_bug562649.js]
|
||||
[browser_bug563588.js]
|
||||
[browser_bug565575.js]
|
||||
skip-if = e10s
|
||||
[browser_bug565667.js]
|
||||
skip-if = toolkit != "cocoa"
|
||||
[browser_bug567306.js]
|
||||
@ -250,7 +248,6 @@ skip-if = e10s && debug
|
||||
[browser_bug647886.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1093373 - Relies on browser.sessionHistory
|
||||
[browser_bug655584.js]
|
||||
skip-if = e10s
|
||||
[browser_bug664672.js]
|
||||
[browser_bug676619.js]
|
||||
skip-if = buildapp == 'mulet' || os == "mac" # mac: Intermittent failures, bug 925225
|
||||
@ -337,7 +334,6 @@ skip-if = os == "linux" # Linux: Intermittent failures, bug 917535
|
||||
[browser_menuButtonFitts.js]
|
||||
skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
|
||||
[browser_middleMouse_noJSPaste.js]
|
||||
skip-if = e10s # Bug 921952 - Content:Click event issues
|
||||
[browser_minimize.js]
|
||||
skip-if = e10s # Bug 1100664 - test directly access content docShells (TypeError: gBrowser.docShell is null)
|
||||
[browser_mixedcontent_securityflags.js]
|
||||
@ -388,7 +384,7 @@ skip-if = buildapp == 'mulet' || e10s # e10s: Bug 933103 - mochitest's EventUtil
|
||||
[browser_save_link_when_window_navigates.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
|
||||
[browser_save_video.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1100698 - test uses synthesizeMouse and then does a load of other stuff that breaks in e10s
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_save_video_frame.js]
|
||||
[browser_scope.js]
|
||||
[browser_searchSuggestionUI.js]
|
||||
@ -465,7 +461,6 @@ skip-if = (os == "win" && !debug) || e10s # Bug 1007418
|
||||
[browser_windowopen_reflows.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_wyciwyg_urlbarCopying.js]
|
||||
skip-if = e10s # Bug 1100703 - test directly manipulates content (content.document.getElementById)
|
||||
[browser_zbug569342.js]
|
||||
skip-if = e10s # Bug 1094240 - has findbar-related failures
|
||||
[browser_registerProtocolHandler_notification.js]
|
||||
|
@ -2,9 +2,7 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
add_task(function* () {
|
||||
let charsToDelete, deletedURLTab, fullURLTab, partialURLTab, testPartialURL, testURL;
|
||||
|
||||
charsToDelete = 5;
|
||||
@ -13,112 +11,96 @@ function test() {
|
||||
partialURLTab = gBrowser.addTab();
|
||||
testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
|
||||
|
||||
let loaded1 = BrowserTestUtils.browserLoaded(deletedURLTab.linkedBrowser, testURL);
|
||||
let loaded2 = BrowserTestUtils.browserLoaded(fullURLTab.linkedBrowser, testURL);
|
||||
let loaded3 = BrowserTestUtils.browserLoaded(partialURLTab.linkedBrowser, testURL);
|
||||
deletedURLTab.linkedBrowser.loadURI(testURL);
|
||||
fullURLTab.linkedBrowser.loadURI(testURL);
|
||||
partialURLTab.linkedBrowser.loadURI(testURL);
|
||||
yield Promise.all([loaded1, loaded2, loaded3]);
|
||||
|
||||
testURL = gURLBar.trimValue(testURL);
|
||||
testPartialURL = testURL.substr(0, (testURL.length - charsToDelete));
|
||||
|
||||
function cleanUp() {
|
||||
gBrowser.removeTab(fullURLTab);
|
||||
gBrowser.removeTab(partialURLTab);
|
||||
gBrowser.removeTab(deletedURLTab);
|
||||
}
|
||||
|
||||
function cycleTabs() {
|
||||
gBrowser.selectedTab = fullURLTab;
|
||||
function* cycleTabs() {
|
||||
yield BrowserTestUtils.switchTab(gBrowser, fullURLTab);
|
||||
is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after switching back to fullURLTab');
|
||||
|
||||
gBrowser.selectedTab = partialURLTab;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, partialURLTab);
|
||||
is(gURLBar.textValue, testPartialURL, 'gURLBar.textValue should be testPartialURL after switching back to partialURLTab');
|
||||
gBrowser.selectedTab = deletedURLTab;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, deletedURLTab);
|
||||
is(gURLBar.textValue, '', 'gURLBar.textValue should be "" after switching back to deletedURLTab');
|
||||
|
||||
gBrowser.selectedTab = fullURLTab;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, fullURLTab);
|
||||
is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after switching back to fullURLTab');
|
||||
}
|
||||
|
||||
// function borrowed from browser_bug386835.js
|
||||
function load(tab, url, cb) {
|
||||
tab.linkedBrowser.addEventListener("load", function (event) {
|
||||
event.currentTarget.removeEventListener("load", arguments.callee, true);
|
||||
cb();
|
||||
}, true);
|
||||
tab.linkedBrowser.loadURI(url);
|
||||
}
|
||||
|
||||
function urlbarBackspace(cb) {
|
||||
gBrowser.selectedBrowser.focus();
|
||||
gURLBar.addEventListener("focus", function () {
|
||||
gURLBar.removeEventListener("focus", arguments.callee, false);
|
||||
function urlbarBackspace() {
|
||||
return new Promise((resolve, reject) => {
|
||||
gBrowser.selectedBrowser.focus();
|
||||
gURLBar.addEventListener("input", function () {
|
||||
gURLBar.removeEventListener("input", arguments.callee, false);
|
||||
cb();
|
||||
resolve();
|
||||
}, false);
|
||||
executeSoon(function () {
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
});
|
||||
}, false);
|
||||
gURLBar.focus();
|
||||
gURLBar.focus();
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
});
|
||||
}
|
||||
|
||||
function prepareDeletedURLTab(cb) {
|
||||
gBrowser.selectedTab = deletedURLTab;
|
||||
function* prepareDeletedURLTab() {
|
||||
yield BrowserTestUtils.switchTab(gBrowser, deletedURLTab);
|
||||
is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to deletedURLTab');
|
||||
|
||||
// simulate the user removing the whole url from the location bar
|
||||
gPrefService.setBoolPref("browser.urlbar.clickSelectsAll", true);
|
||||
|
||||
urlbarBackspace(function () {
|
||||
is(gURLBar.textValue, "", 'gURLBar.textValue should be "" (just set)');
|
||||
if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll"))
|
||||
gPrefService.clearUserPref("browser.urlbar.clickSelectsAll");
|
||||
cb();
|
||||
});
|
||||
yield urlbarBackspace();
|
||||
is(gURLBar.textValue, "", 'gURLBar.textValue should be "" (just set)');
|
||||
if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll")) {
|
||||
gPrefService.clearUserPref("browser.urlbar.clickSelectsAll");
|
||||
}
|
||||
}
|
||||
|
||||
function prepareFullURLTab(cb) {
|
||||
gBrowser.selectedTab = fullURLTab;
|
||||
function* prepareFullURLTab() {
|
||||
yield BrowserTestUtils.switchTab(gBrowser, fullURLTab);
|
||||
is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to fullURLTab');
|
||||
cb();
|
||||
}
|
||||
|
||||
function preparePartialURLTab(cb) {
|
||||
gBrowser.selectedTab = partialURLTab;
|
||||
function* preparePartialURLTab() {
|
||||
yield BrowserTestUtils.switchTab(gBrowser, partialURLTab);
|
||||
is(gURLBar.textValue, testURL, 'gURLBar.textValue should be testURL after initial switch to partialURLTab');
|
||||
|
||||
// simulate the user removing part of the url from the location bar
|
||||
gPrefService.setBoolPref("browser.urlbar.clickSelectsAll", false);
|
||||
|
||||
var deleted = 0;
|
||||
urlbarBackspace(function () {
|
||||
let deleted = 0;
|
||||
while (deleted < charsToDelete) {
|
||||
yield urlbarBackspace(arguments.callee);
|
||||
deleted++;
|
||||
if (deleted < charsToDelete) {
|
||||
urlbarBackspace(arguments.callee);
|
||||
} else {
|
||||
is(gURLBar.textValue, testPartialURL, "gURLBar.textValue should be testPartialURL (just set)");
|
||||
if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll"))
|
||||
gPrefService.clearUserPref("browser.urlbar.clickSelectsAll");
|
||||
cb();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
is(gURLBar.textValue, testPartialURL, "gURLBar.textValue should be testPartialURL (just set)");
|
||||
if (gPrefService.prefHasUserValue("browser.urlbar.clickSelectsAll")) {
|
||||
gPrefService.clearUserPref("browser.urlbar.clickSelectsAll");
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
testURL = gURLBar.trimValue(testURL);
|
||||
testPartialURL = testURL.substr(0, (testURL.length - charsToDelete));
|
||||
// prepare the three tabs required by this test
|
||||
|
||||
// prepare the three tabs required by this test
|
||||
prepareFullURLTab(function () {
|
||||
preparePartialURLTab(function () {
|
||||
prepareDeletedURLTab(function () {
|
||||
// now cycle the tabs and make sure everything looks good
|
||||
cycleTabs();
|
||||
cleanUp();
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// First tab
|
||||
yield* prepareFullURLTab();
|
||||
yield* preparePartialURLTab();
|
||||
yield* prepareDeletedURLTab();
|
||||
|
||||
// now cycle the tabs and make sure everything looks good
|
||||
yield* cycleTabs();
|
||||
cleanUp();
|
||||
});
|
||||
|
||||
load(deletedURLTab, testURL, function() {
|
||||
load(fullURLTab, testURL, function() {
|
||||
load(partialURLTab, testURL, runTests);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
function test() {
|
||||
add_task(function* () {
|
||||
gBrowser.selectedBrowser.focus();
|
||||
BrowserOpenTab();
|
||||
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => BrowserOpenTab(), false);
|
||||
ok(gURLBar.focused, "location bar is focused for a new tab");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
yield BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[0]);
|
||||
ok(!gURLBar.focused, "location bar isn't focused for the previously selected tab");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[1];
|
||||
yield BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[1]);
|
||||
ok(gURLBar.focused, "location bar is re-focused when selecting the new tab");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
@ -4,20 +4,20 @@
|
||||
|
||||
// Bug 655584 - awesomebar suggestions don't update after tab is closed
|
||||
|
||||
function test() {
|
||||
add_task(function* () {
|
||||
var tab1 = gBrowser.addTab();
|
||||
var tab2 = gBrowser.addTab();
|
||||
|
||||
// When urlbar in a new tab is focused, and a tab switch occurs,
|
||||
// the urlbar popup should be closed
|
||||
gBrowser.selectedTab = tab2;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, tab2);
|
||||
gURLBar.focus(); // focus the urlbar in the tab we will switch to
|
||||
gBrowser.selectedTab = tab1;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, tab1);
|
||||
gURLBar.openPopup();
|
||||
gBrowser.selectedTab = tab2;
|
||||
yield BrowserTestUtils.switchTab(gBrowser, tab2);
|
||||
ok(!gURLBar.popupOpen, "urlbar focused in tab to switch to, close popup");
|
||||
|
||||
// cleanup
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
@ -1,7 +1,9 @@
|
||||
// This test is used to check copy and paste in editable areas to ensure that non-text
|
||||
// types (html and images) are copied to and pasted from the clipboard properly.
|
||||
|
||||
let testPage = "<div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>";
|
||||
let testPage = "<body style='margin: 0'><img id='img' tabindex='1' src='http://example.org/browser/browser/base/content/test/general/moz.png'>" +
|
||||
" <div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>" +
|
||||
"</body>";
|
||||
|
||||
add_task(function*() {
|
||||
let tab = gBrowser.addTab();
|
||||
@ -12,7 +14,12 @@ add_task(function*() {
|
||||
yield promiseTabLoadEvent(tab, "data:text/html," + escape(testPage));
|
||||
yield SimpleTest.promiseFocus(browser.contentWindowAsCPOW);
|
||||
|
||||
let results = yield ContentTask.spawn(browser, {}, function* () {
|
||||
const modifier = (content.navigator.platform.indexOf("Mac") >= 0) ?
|
||||
Components.interfaces.nsIDOMWindowUtils.MODIFIER_META :
|
||||
Components.interfaces.nsIDOMWindowUtils.MODIFIER_CONTROL;
|
||||
|
||||
let results = yield ContentTask.spawn(browser, { modifier: modifier },
|
||||
function* (arg) {
|
||||
var doc = content.document;
|
||||
var main = doc.getElementById("main");
|
||||
main.focus();
|
||||
@ -20,10 +27,7 @@ add_task(function*() {
|
||||
const utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
const modifier = (content.navigator.platform.indexOf("Mac") >= 0) ?
|
||||
Components.interfaces.nsIDOMWindowUtils.MODIFIER_META :
|
||||
Components.interfaces.nsIDOMWindowUtils.MODIFIER_CONTROL;
|
||||
|
||||
const modifier = arg.modifier;
|
||||
function sendKey(key)
|
||||
{
|
||||
if (utils.sendKeyEvent("keydown", key, 0, modifier)) {
|
||||
@ -46,7 +50,7 @@ add_task(function*() {
|
||||
selection.modify("extend", "right", "word");
|
||||
selection.modify("extend", "right", "word");
|
||||
|
||||
yield new content.Promise((resolve, reject) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
addEventListener("copy", function copyEvent(event) {
|
||||
removeEventListener("copy", copyEvent, true);
|
||||
// The data is empty as the selection is copied during the event default phase.
|
||||
@ -59,7 +63,7 @@ add_task(function*() {
|
||||
|
||||
selection.modify("move", "right", "line");
|
||||
|
||||
yield new content.Promise((resolve, reject) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
addEventListener("paste", function copyEvent(event) {
|
||||
removeEventListener("paste", copyEvent, true);
|
||||
let clipboardData = event.clipboardData;
|
||||
@ -80,7 +84,7 @@ add_task(function*() {
|
||||
selection.modify("extend", "left", "word");
|
||||
selection.modify("extend", "left", "character");
|
||||
|
||||
yield new content.Promise((resolve, reject) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
addEventListener("cut", function copyEvent(event) {
|
||||
removeEventListener("cut", copyEvent, true);
|
||||
event.clipboardData.setData("text/plain", "Some text");
|
||||
@ -94,7 +98,7 @@ add_task(function*() {
|
||||
|
||||
selection.modify("move", "left", "line");
|
||||
|
||||
yield new content.Promise((resolve, reject) => {
|
||||
yield new Promise((resolve, reject) => {
|
||||
addEventListener("paste", function copyEvent(event) {
|
||||
removeEventListener("paste", copyEvent, true);
|
||||
let clipboardData = event.clipboardData;
|
||||
@ -110,6 +114,7 @@ add_task(function*() {
|
||||
});
|
||||
|
||||
is(main.innerHTML, "<i>Italic</i> Test <b>Bold</b> After<b></b>", "Copy and paste html 2");
|
||||
|
||||
return results;
|
||||
});
|
||||
|
||||
@ -118,6 +123,61 @@ add_task(function*() {
|
||||
ok(results[t].startsWith("PASSED"), results[t]);
|
||||
}
|
||||
|
||||
// Next, check that the Copy Image command works.
|
||||
|
||||
// The context menu needs to be opened to properly initialize for the copy
|
||||
// image command to run.
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let contextMenuShown = promisePopupShown(contextMenu);
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#img", { type: "contextmenu", button: 2 }, gBrowser.selectedBrowser);
|
||||
yield contextMenuShown;
|
||||
|
||||
document.getElementById("context-copyimage-contents").doCommand();
|
||||
|
||||
contextMenu.hidePopup();
|
||||
yield promisePopupHidden(contextMenu);
|
||||
|
||||
// Focus the content again
|
||||
yield SimpleTest.promiseFocus(browser.contentWindowAsCPOW);
|
||||
|
||||
let expectedContent = yield ContentTask.spawn(browser, { modifier: modifier },
|
||||
function* (arg) {
|
||||
var doc = content.document;
|
||||
var main = doc.getElementById("main");
|
||||
main.focus();
|
||||
|
||||
yield new Promise((resolve, reject) => {
|
||||
addEventListener("paste", function copyEvent(event) {
|
||||
removeEventListener("paste", copyEvent, true);
|
||||
let clipboardData = event.clipboardData;
|
||||
|
||||
// DataTransfer doesn't support the image types yet, so only text/html
|
||||
// will be present.
|
||||
if (clipboardData.getData("text/html") !=
|
||||
'<img id="img" tabindex="1" src="http://example.org/browser/browser/base/content/test/general/moz.png">') {
|
||||
reject();
|
||||
}
|
||||
resolve();
|
||||
}, true)
|
||||
|
||||
const utils = content.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
const modifier = arg.modifier;
|
||||
if (utils.sendKeyEvent("keydown", "v", 0, modifier)) {
|
||||
utils.sendKeyEvent("keypress", "v", "v".charCodeAt(0), modifier);
|
||||
}
|
||||
utils.sendKeyEvent("keyup", "v", 0, modifier);
|
||||
});
|
||||
|
||||
// Return the new content which should now include an image.
|
||||
return main.innerHTML;
|
||||
});
|
||||
|
||||
is(expectedContent, '<i>Italic</i> <img id="img" tabindex="1" ' +
|
||||
'src="http://example.org/browser/browser/base/content/test/general/moz.png">' +
|
||||
'Test <b>Bold</b> After<b></b>', "Paste after copy image");
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
|
@ -3,51 +3,32 @@
|
||||
|
||||
const middleMousePastePref = "middlemouse.contentLoadURL";
|
||||
const autoScrollPref = "general.autoScroll";
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Services.prefs.setBoolPref(middleMousePastePref, true);
|
||||
Services.prefs.setBoolPref(autoScrollPref, false);
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab();
|
||||
add_task(function* () {
|
||||
yield pushPrefs([middleMousePastePref, true], [autoScrollPref, false]);
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.clearUserPref(middleMousePastePref);
|
||||
Services.prefs.clearUserPref(autoScrollPref);
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser);
|
||||
|
||||
addPageShowListener(function () {
|
||||
let pagePrincipal = gBrowser.contentPrincipal;
|
||||
|
||||
// copy javascript URI to the clipboard
|
||||
let url = "javascript:http://www.example.com/";
|
||||
waitForClipboard(url,
|
||||
function() {
|
||||
Components.classes["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Components.interfaces.nsIClipboardHelper)
|
||||
.copyString(url, document);
|
||||
},
|
||||
function () {
|
||||
// Middle click on the content area
|
||||
info("Middle clicking");
|
||||
EventUtils.sendMouseEvent({type: "click", button: 1}, gBrowser);
|
||||
},
|
||||
function() {
|
||||
ok(false, "Failed to copy URL to the clipboard");
|
||||
finish();
|
||||
}
|
||||
);
|
||||
|
||||
addPageShowListener(function () {
|
||||
is(gBrowser.currentURI.spec, url.replace(/^javascript:/, ""), "url loaded by middle click doesn't include JS");
|
||||
finish();
|
||||
let url = "javascript:http://www.example.com/";
|
||||
yield new Promise((resolve, reject) => {
|
||||
SimpleTest.waitForClipboard(url, () => {
|
||||
Components.classes["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Components.interfaces.nsIClipboardHelper)
|
||||
.copyString(url, document);
|
||||
}, resolve, () => {
|
||||
ok(false, "Clipboard copy failed");
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addPageShowListener(func) {
|
||||
gBrowser.selectedBrowser.addEventListener("pageshow", function loadListener() {
|
||||
gBrowser.selectedBrowser.removeEventListener("pageshow", loadListener, false);
|
||||
func();
|
||||
});
|
||||
}
|
||||
let middlePagePromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
|
||||
// Middle click on the content area
|
||||
info("Middle clicking");
|
||||
yield BrowserTestUtils.synthesizeMouse(null, 10, 10, {button: 1}, gBrowser.selectedBrowser);
|
||||
yield middlePagePromise;
|
||||
|
||||
is(gBrowser.currentURI.spec, url.replace(/^javascript:/, ""), "url loaded by middle click doesn't include JS");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
@ -8,70 +8,68 @@ MockFilePicker.init(window);
|
||||
* TestCase for bug 564387
|
||||
* <https://bugzilla.mozilla.org/show_bug.cgi?id=564387>
|
||||
*/
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
add_task(function* () {
|
||||
var fileName;
|
||||
|
||||
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html");
|
||||
yield loadPromise;
|
||||
|
||||
gBrowser.addEventListener("pageshow", function pageShown(event) {
|
||||
if (event.target.location == "about:blank")
|
||||
return;
|
||||
gBrowser.removeEventListener("pageshow", pageShown);
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
|
||||
|
||||
executeSoon(function () {
|
||||
document.addEventListener("popupshown", contextMenuOpened);
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#video1",
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser);
|
||||
info("context menu click on video1");
|
||||
|
||||
var video1 = gBrowser.contentDocument.getElementById("video1");
|
||||
EventUtils.synthesizeMouseAtCenter(video1,
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.contentWindow);
|
||||
info("context menu click on video1");
|
||||
});
|
||||
});
|
||||
yield popupShownPromise;
|
||||
|
||||
function contextMenuOpened(event) {
|
||||
event.currentTarget.removeEventListener("popupshown", contextMenuOpened);
|
||||
info("context menu opened on video1");
|
||||
info("context menu opened on video1");
|
||||
|
||||
// Create the folder the video will be saved into.
|
||||
var destDir = createTemporarySaveDirectory();
|
||||
var destFile = destDir.clone();
|
||||
// Create the folder the video will be saved into.
|
||||
var destDir = createTemporarySaveDirectory();
|
||||
var destFile = destDir.clone();
|
||||
|
||||
MockFilePicker.displayDirectory = destDir;
|
||||
MockFilePicker.showCallback = function(fp) {
|
||||
fileName = fp.defaultString;
|
||||
destFile.append (fileName);
|
||||
MockFilePicker.returnFiles = [destFile];
|
||||
MockFilePicker.filterIndex = 1; // kSaveAsType_URL
|
||||
};
|
||||
MockFilePicker.displayDirectory = destDir;
|
||||
MockFilePicker.showCallback = function(fp) {
|
||||
fileName = fp.defaultString;
|
||||
destFile.append(fileName);
|
||||
MockFilePicker.returnFiles = [destFile];
|
||||
MockFilePicker.filterIndex = 1; // kSaveAsType_URL
|
||||
};
|
||||
|
||||
let transferCompletePromise = new Promise((resolve) => {
|
||||
function onTransferComplete(downloadSuccess) {
|
||||
ok(downloadSuccess, "Video file should have been downloaded successfully");
|
||||
|
||||
is(fileName, "web-video1-expectedName.ogv",
|
||||
"Video file name is correctly retrieved from Content-Disposition http header");
|
||||
resolve();
|
||||
}
|
||||
|
||||
mockTransferCallback = onTransferComplete;
|
||||
mockTransferRegisterer.register();
|
||||
});
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
mockTransferRegisterer.unregister();
|
||||
MockFilePicker.cleanup();
|
||||
destDir.remove(true);
|
||||
});
|
||||
registerCleanupFunction(function () {
|
||||
mockTransferRegisterer.unregister();
|
||||
MockFilePicker.cleanup();
|
||||
destDir.remove(true);
|
||||
});
|
||||
|
||||
// Select "Save Video As" option from context menu
|
||||
var saveVideoCommand = document.getElementById("context-savevideo");
|
||||
saveVideoCommand.doCommand();
|
||||
info("context-savevideo command executed");
|
||||
// Select "Save Video As" option from context menu
|
||||
var saveVideoCommand = document.getElementById("context-savevideo");
|
||||
saveVideoCommand.doCommand();
|
||||
info("context-savevideo command executed");
|
||||
|
||||
event.target.hidePopup();
|
||||
}
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
|
||||
contextMenu.hidePopup();
|
||||
yield popupHiddenPromise;
|
||||
|
||||
function onTransferComplete(downloadSuccess) {
|
||||
ok(downloadSuccess, "Video file should have been downloaded successfully");
|
||||
yield transferCompletePromise;
|
||||
});
|
||||
|
||||
is(fileName, "web-video1-expectedName.ogv",
|
||||
"Video file name is correctly retrieved from Content-Disposition http header");
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
|
@ -1,39 +1,31 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let url = "http://mochi.test:8888/browser/browser/base/content/test/general/test_wyciwyg_copying.html";
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
tab.linkedBrowser.addEventListener("pageshow", function () {
|
||||
let btn = content.document.getElementById("btn");
|
||||
executeSoon(function () {
|
||||
EventUtils.synthesizeMouseAtCenter(btn, {}, content);
|
||||
let currentURL = gBrowser.currentURI.spec;
|
||||
ok(/^wyciwyg:\/\//i.test(currentURL), currentURL + " is a wyciwyg URI");
|
||||
|
||||
executeSoon(function () {
|
||||
testURLBarCopy(url, endTest);
|
||||
});
|
||||
});
|
||||
}, false);
|
||||
|
||||
function endTest() {
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function testURLBarCopy(targetValue, cb) {
|
||||
function testURLBarCopy(targetValue) {
|
||||
return new Promise((resolve, reject) => {
|
||||
info("Expecting copy of: " + targetValue);
|
||||
waitForClipboard(targetValue, function () {
|
||||
gURLBar.focus();
|
||||
gURLBar.select();
|
||||
|
||||
goDoCommand("cmd_copy");
|
||||
}, cb, cb);
|
||||
}
|
||||
}, resolve, () => {
|
||||
ok(false, "Clipboard copy failed");
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const url = "http://mochi.test:8888/browser/browser/base/content/test/general/test_wyciwyg_copying.html";
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#btn", {}, tab.linkedBrowser);
|
||||
let currentURL = gBrowser.currentURI.spec;
|
||||
ok(/^wyciwyg:\/\//i.test(currentURL), currentURL + " is a wyciwyg URI");
|
||||
|
||||
yield testURLBarCopy(url);
|
||||
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
@ -34,6 +34,25 @@ function testMalware(event) {
|
||||
var style = content.getComputedStyle(el, null);
|
||||
is(style.display, "inline-block", "Ignore Warning button should be display:inline-block for malware");
|
||||
|
||||
// Now launch the unwanted software test
|
||||
window.addEventListener("DOMContentLoaded", testUnwanted, true);
|
||||
content.location = "http://www.itisatrap.org/firefox/unwanted.html";
|
||||
}
|
||||
|
||||
function testUnwanted(event) {
|
||||
if (event.target != gBrowser.selectedBrowser.contentDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.removeEventListener("DOMContentLoaded", testUnwanted, true);
|
||||
|
||||
// Confirm that "Ignore this warning" is visible - bug 422410
|
||||
var el = content.document.getElementById("ignoreWarningButton");
|
||||
ok(el, "Ignore warning button should be present for unwanted software");
|
||||
|
||||
var style = content.getComputedStyle(el, null);
|
||||
is(style.display, "inline-block", "Ignore Warning button should be display:inline-block for unwanted software");
|
||||
|
||||
// Now launch the phishing test
|
||||
window.addEventListener("DOMContentLoaded", testPhishing, true);
|
||||
content.location = "http://www.itisatrap.org/firefox/its-a-trap.html";
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Force SafeBrowsing to be initialized for the tests
|
||||
Services.prefs.setCharPref("urlclassifier.malwareTable", "test-malware-simple");
|
||||
Services.prefs.setCharPref("urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple");
|
||||
Services.prefs.setCharPref("urlclassifier.phishTable", "test-phish-simple");
|
||||
SafeBrowsing.init();
|
||||
|
||||
|
@ -218,6 +218,7 @@
|
||||
@RESPATH@/components/dom_offline.xpt
|
||||
@RESPATH@/components/dom_json.xpt
|
||||
@RESPATH@/components/dom_power.xpt
|
||||
@RESPATH@/components/dom_push.xpt
|
||||
@RESPATH@/components/dom_quota.xpt
|
||||
@RESPATH@/components/dom_range.xpt
|
||||
@RESPATH@/components/dom_security.xpt
|
||||
@ -558,7 +559,7 @@
|
||||
@RESPATH@/components/AlarmsManager.manifest
|
||||
@RESPATH@/components/Push.js
|
||||
@RESPATH@/components/Push.manifest
|
||||
@RESPATH@/components/PushServiceLauncher.js
|
||||
@RESPATH@/components/PushNotificationService.js
|
||||
|
||||
@RESPATH@/components/SlowScriptDebug.manifest
|
||||
@RESPATH@/components/SlowScriptDebug.js
|
||||
|
@ -400,6 +400,7 @@ safebrowsing.notAForgeryButton.accessKey=F
|
||||
safebrowsing.reportedAttackSite=Reported Attack Site!
|
||||
safebrowsing.notAnAttackButton.label=This isn't an attack site…
|
||||
safebrowsing.notAnAttackButton.accessKey=A
|
||||
safebrowsing.reportedUnwantedSite=Reported Unwanted Software Site!
|
||||
|
||||
# Ctrl-Tab
|
||||
# LOCALIZATION NOTE (ctrlTab.listAllTabs.label): #1 represents the number
|
||||
|
@ -12,6 +12,11 @@
|
||||
<!ENTITY safeb.blocked.malwarePage.shortDesc "This web page at <span id='malware_sitename'/> has been reported as an attack page and has been blocked based on your security preferences.">
|
||||
<!ENTITY safeb.blocked.malwarePage.longDesc "<p>Attack pages try to install programs that steal private information, use your computer to attack others, or damage your system.</p><p>Some attack pages intentionally distribute harmful software, but many are compromised without the knowledge or permission of their owners.</p>">
|
||||
|
||||
<!ENTITY safeb.blocked.unwantedPage.title "Reported Unwanted Software Page!">
|
||||
<!-- Localization note (safeb.blocked.malware.shortDesc) - Please don't translate the contents of the <span id="unwanted_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.unwantedPage.shortDesc "This web page at <span id='unwanted_sitename'/> has been reported to contain unwanted software and has been blocked based on your security preferences.">
|
||||
<!ENTITY safeb.blocked.unwantedPage.longDesc "<p>Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.</p>">
|
||||
|
||||
<!ENTITY safeb.blocked.phishingPage.title "Reported Web Forgery!">
|
||||
<!-- Localization note (safeb.blocked.phishing.shortDesc) - Please don't translate the contents of the <span id="phishing_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.phishingPage.shortDesc "This web page at <span id='phishing_sitename'/> has been reported as a web forgery and has been blocked based on your security preferences.">
|
||||
|
@ -30,6 +30,7 @@ externalProtocolUnknown=<Unknown>
|
||||
externalProtocolChkMsg=Remember my choice for all links of this type.
|
||||
externalProtocolLaunchBtn=Launch application
|
||||
malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
|
||||
unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
|
||||
phishingBlocked=The website at %S has been reported as a web forgery designed to trick users into sharing personal or financial information.
|
||||
cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
|
||||
corruptedContentError=The page you are trying to view cannot be shown because an error in the data transmission was detected.
|
||||
|
@ -164,6 +164,11 @@ be temporary, and you can try again later.</li>
|
||||
<p>Website owners who believe their site has been reported as an attack site in error may <a href='http://www.stopbadware.org/home/reviewinfo' >request a review</a>.</p>
|
||||
">
|
||||
|
||||
<!ENTITY unwantedBlocked.title "Suspected Unwanted Software Site!">
|
||||
<!ENTITY unwantedBlocked.longDesc "
|
||||
<p>Unwanted software pages try to install software that can be deceptive and affect your system in unexpected ways.</p>
|
||||
">
|
||||
|
||||
<!ENTITY phishingBlocked.title "Suspected Web Forgery!">
|
||||
<!ENTITY phishingBlocked.longDesc "
|
||||
<p>Entering any personal information on this page may result in identity theft or other fraud.</p>
|
||||
|
@ -341,20 +341,15 @@ let Content = {
|
||||
}
|
||||
} else if (/^about:blocked/.test(errorDoc.documentURI)) {
|
||||
// The event came from a button on a malware/phishing block page
|
||||
// First check whether it's malware or phishing, so that we can
|
||||
// use the right strings/links.
|
||||
let isMalware = /e=malwareBlocked/.test(errorDoc.documentURI);
|
||||
|
||||
if (ot == errorDoc.getElementById("getMeOutButton")) {
|
||||
sendAsyncMessage("Browser:BlockedSite",
|
||||
{ url: errorDoc.location.href, action: "leave" });
|
||||
} else if (ot == errorDoc.getElementById("reportButton")) {
|
||||
// This is the "Why is this site blocked" button. For malware,
|
||||
// we can fetch a site-specific report, for phishing, we redirect
|
||||
// to the generic page describing phishing protection.
|
||||
let action = isMalware ? "report-malware" : "report-phishing";
|
||||
// This is the "Why is this site blocked" button. We redirect
|
||||
// to the generic page describing phishing/malware protection.
|
||||
sendAsyncMessage("Browser:BlockedSite",
|
||||
{ url: errorDoc.location.href, action: action });
|
||||
{ url: errorDoc.location.href, action: "report-phishing" });
|
||||
} else if (ot == errorDoc.getElementById("ignoreWarningButton")) {
|
||||
// Allow users to override and continue through to the site,
|
||||
// but add a notify bar as a reminder, so that they don't lose
|
||||
|
@ -9,10 +9,8 @@ scrollbar {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Scrollbar code will reset the margin to the correct side depending on
|
||||
layout.scrollbar.side pref */
|
||||
scrollbar[orient="vertical"] {
|
||||
margin-left: -10px;
|
||||
-moz-margin-start: -10px;
|
||||
min-width: 10px;
|
||||
max-width: 10px;
|
||||
}
|
||||
|
@ -10,10 +10,8 @@ scrollbar {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Scrollbar code will reset the margin to the correct side depending on
|
||||
layout.scrollbar.side pref */
|
||||
scrollbar[orient="vertical"] {
|
||||
margin-left: -8px;
|
||||
-moz-margin-start: -8px;
|
||||
min-width: 8px;
|
||||
max-width: 8px;
|
||||
}
|
||||
|
@ -9,10 +9,8 @@ scrollbar {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/* Scrollbar code will reset the margin to the correct side depending on
|
||||
layout.scrollbar.side pref */
|
||||
scrollbar[orient="vertical"] {
|
||||
margin-left: -10px;
|
||||
-moz-margin-start: -10px;
|
||||
min-width: 10px;
|
||||
max-width: 10px;
|
||||
}
|
||||
|
@ -101,12 +101,15 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis
|
||||
"""Process a single leak log.
|
||||
"""
|
||||
|
||||
# Per-Inst Leaked Total Rem ...
|
||||
# 0 TOTAL 17 192 419115886 2 ...
|
||||
# 833 nsTimerImpl 60 120 24726 2 ...
|
||||
lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+"
|
||||
r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+"
|
||||
r"-?\d+\s+(?P<numLeaked>-?\d+)")
|
||||
# | |Per-Inst Leaked| Total Rem|
|
||||
# 0 |TOTAL | 17 192| 419115886 2|
|
||||
# 833 |nsTimerImpl | 60 120| 24726 2|
|
||||
# 930 |Foo<Bar, Bar> | 32 8| 100 1|
|
||||
lineRe = re.compile(r"^\s*\d+ \|"
|
||||
r"(?P<name>[^|]+)\|"
|
||||
r"\s*(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s*\|"
|
||||
r"\s*-?\d+\s+(?P<numLeaked>-?\d+)")
|
||||
# The class name can contain spaces. We remove trailing whitespace later.
|
||||
|
||||
processString = "%s process:" % processType
|
||||
crashedOnPurpose = False
|
||||
@ -125,7 +128,7 @@ def processSingleLeakFile(leakLogFileName, processType, leakThreshold, ignoreMis
|
||||
# eg: the leak table header row
|
||||
log.info(line.rstrip())
|
||||
continue
|
||||
name = matches.group("name")
|
||||
name = matches.group("name").rstrip()
|
||||
size = int(matches.group("size"))
|
||||
bytesLeaked = int(matches.group("bytesLeaked"))
|
||||
numLeaked = int(matches.group("numLeaked"))
|
||||
|
@ -84,6 +84,11 @@ private:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback {
|
||||
public:
|
||||
virtual void run(const MatchFinder::MatchResult &Result);
|
||||
};
|
||||
|
||||
ScopeChecker stackClassChecker;
|
||||
ScopeChecker globalClassChecker;
|
||||
NonHeapClassChecker nonheapClassChecker;
|
||||
@ -92,16 +97,17 @@ private:
|
||||
NaNExprChecker nanExprChecker;
|
||||
NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
|
||||
RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
|
||||
ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
|
||||
MatchFinder astMatcher;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
bool isInIgnoredNamespace(const Decl *decl) {
|
||||
std::string getDeclarationNamespace(const Decl *decl) {
|
||||
const DeclContext *DC = decl->getDeclContext()->getEnclosingNamespaceContext();
|
||||
const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
|
||||
if (!ND) {
|
||||
return false;
|
||||
return "";
|
||||
}
|
||||
|
||||
while (const DeclContext *ParentDC = ND->getParent()) {
|
||||
@ -112,8 +118,15 @@ bool isInIgnoredNamespace(const Decl *decl) {
|
||||
}
|
||||
|
||||
const auto& name = ND->getName();
|
||||
return name;
|
||||
}
|
||||
|
||||
bool isInIgnoredNamespaceForImplicitCtor(const Decl *decl) {
|
||||
std::string name = getDeclarationNamespace(decl);
|
||||
if (name == "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// namespace std and icu are ignored for now
|
||||
return name == "std" || // standard C++ lib
|
||||
name == "__gnu_cxx" || // gnu C++ lib
|
||||
name == "boost" || // boost
|
||||
@ -129,7 +142,19 @@ bool isInIgnoredNamespace(const Decl *decl) {
|
||||
name == "testing"; // gtest
|
||||
}
|
||||
|
||||
bool isIgnoredPath(const Decl *decl) {
|
||||
bool isInIgnoredNamespaceForImplicitConversion(const Decl *decl) {
|
||||
std::string name = getDeclarationNamespace(decl);
|
||||
if (name == "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return name == "std" || // standard C++ lib
|
||||
name == "__gnu_cxx" || // gnu C++ lib
|
||||
name == "google_breakpad" || // breakpad
|
||||
name == "testing"; // gtest
|
||||
}
|
||||
|
||||
bool isIgnoredPathForImplicitCtor(const Decl *decl) {
|
||||
decl = decl->getCanonicalDecl();
|
||||
SourceLocation Loc = decl->getLocation();
|
||||
const SourceManager &SM = decl->getASTContext().getSourceManager();
|
||||
@ -150,9 +175,30 @@ bool isIgnoredPath(const Decl *decl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInterestingDecl(const Decl *decl) {
|
||||
return !isInIgnoredNamespace(decl) &&
|
||||
!isIgnoredPath(decl);
|
||||
bool isIgnoredPathForImplicitConversion(const Decl *decl) {
|
||||
decl = decl->getCanonicalDecl();
|
||||
SourceLocation Loc = decl->getLocation();
|
||||
const SourceManager &SM = decl->getASTContext().getSourceManager();
|
||||
SmallString<1024> FileName = SM.getFilename(Loc);
|
||||
llvm::sys::fs::make_absolute(FileName);
|
||||
llvm::sys::path::reverse_iterator begin = llvm::sys::path::rbegin(FileName),
|
||||
end = llvm::sys::path::rend(FileName);
|
||||
for (; begin != end; ++begin) {
|
||||
if (begin->compare_lower(StringRef("graphite2")) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInterestingDeclForImplicitCtor(const Decl *decl) {
|
||||
return !isInIgnoredNamespaceForImplicitCtor(decl) &&
|
||||
!isIgnoredPathForImplicitCtor(decl);
|
||||
}
|
||||
|
||||
bool isInterestingDeclForImplicitConversion(const Decl *decl) {
|
||||
return !isInIgnoredNamespaceForImplicitConversion(decl) &&
|
||||
!isIgnoredPathForImplicitConversion(decl);
|
||||
}
|
||||
|
||||
}
|
||||
@ -232,7 +278,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!d->isAbstract() && isInterestingDecl(d)) {
|
||||
if (!d->isAbstract() && isInterestingDeclForImplicitCtor(d)) {
|
||||
for (CXXRecordDecl::ctor_iterator ctor = d->ctor_begin(),
|
||||
e = d->ctor_end(); ctor != e; ++ctor) {
|
||||
// Ignore non-converting ctors
|
||||
@ -416,6 +462,16 @@ bool isClassRefCounted(QualType T) {
|
||||
return clazz ? isClassRefCounted(clazz) : RegularClass;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool IsInSystemHeader(const ASTContext &AC, const T &D) {
|
||||
auto &SourceManager = AC.getSourceManager();
|
||||
auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
|
||||
if (ExpansionLoc.isInvalid()) {
|
||||
return false;
|
||||
}
|
||||
return SourceManager.isInSystemHeader(ExpansionLoc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
@ -515,12 +571,7 @@ AST_MATCHER(QualType, isFloat) {
|
||||
/// isExpansionInSystemHeader in newer clangs, but modified in order to work
|
||||
/// with old clangs that we use on infra.
|
||||
AST_MATCHER(BinaryOperator, isInSystemHeader) {
|
||||
auto &SourceManager = Finder->getASTContext().getSourceManager();
|
||||
auto ExpansionLoc = SourceManager.getExpansionLoc(Node.getLocStart());
|
||||
if (ExpansionLoc.isInvalid()) {
|
||||
return false;
|
||||
}
|
||||
return SourceManager.isInSystemHeader(ExpansionLoc);
|
||||
return IsInSystemHeader(Finder->getASTContext(), Node);
|
||||
}
|
||||
|
||||
/// This matcher will match locations in SkScalar.h. This header contains a
|
||||
@ -649,6 +700,13 @@ DiagnosticsMatcher::DiagnosticsMatcher()
|
||||
hasDescendant(declRefExpr(hasType(pointerType(pointee(isRefCounted())))).bind("node"))
|
||||
),
|
||||
&refCountedInsideLambdaChecker);
|
||||
|
||||
// Older clang versions such as the ones used on the infra recognize these
|
||||
// conversions as 'operator _Bool', but newer clang versions recognize these
|
||||
// as 'operator bool'.
|
||||
astMatcher.addMatcher(methodDecl(anyOf(hasName("operator bool"),
|
||||
hasName("operator _Bool"))).bind("node"),
|
||||
&explicitOperatorBoolChecker);
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::ScopeChecker::run(
|
||||
@ -861,6 +919,25 @@ void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run(
|
||||
Diag.Report(node->getLocStart(), noteID);
|
||||
}
|
||||
|
||||
void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run(
|
||||
const MatchFinder::MatchResult &Result) {
|
||||
DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
|
||||
unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Error, "bad implicit conversion operator for %0");
|
||||
unsigned noteID = Diag.getDiagnosticIDs()->getCustomDiagID(
|
||||
DiagnosticIDs::Note, "consider adding the explicit keyword to %0");
|
||||
const CXXConversionDecl *method = Result.Nodes.getNodeAs<CXXConversionDecl>("node");
|
||||
const CXXRecordDecl *clazz = method->getParent();
|
||||
|
||||
if (!method->isExplicitSpecified() &&
|
||||
!MozChecker::hasCustomAnnotation(method, "moz_implicit") &&
|
||||
!IsInSystemHeader(method->getASTContext(), *method) &&
|
||||
isInterestingDeclForImplicitConversion(method)) {
|
||||
Diag.Report(method->getLocStart(), errorID) << clazz;
|
||||
Diag.Report(method->getLocStart(), noteID) << "'operator bool'";
|
||||
}
|
||||
}
|
||||
|
||||
class MozCheckAction : public PluginASTAction {
|
||||
public:
|
||||
ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, StringRef fileName) override {
|
||||
|
11
build/clang-plugin/tests/TestExplicitOperatorBool.cpp
Normal file
11
build/clang-plugin/tests/TestExplicitOperatorBool.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
|
||||
|
||||
struct Bad {
|
||||
operator bool(); // expected-error {{bad implicit conversion operator for 'Bad'}} expected-note {{consider adding the explicit keyword to 'operator bool'}}
|
||||
};
|
||||
struct Good {
|
||||
explicit operator bool();
|
||||
};
|
||||
struct Okay {
|
||||
MOZ_IMPLICIT operator bool();
|
||||
};
|
@ -7,6 +7,7 @@
|
||||
SOURCES += [
|
||||
'TestBadImplicitConversionCtor.cpp',
|
||||
'TestCustomHeap.cpp',
|
||||
'TestExplicitOperatorBool.cpp',
|
||||
'TestGlobalClass.cpp',
|
||||
'TestMustOverride.cpp',
|
||||
'TestNANTestingExpr.cpp',
|
||||
|
@ -180,7 +180,13 @@ class RemoteAutomation(Automation):
|
||||
self.checkForANRs()
|
||||
self.checkForTombstones()
|
||||
|
||||
logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters)
|
||||
try:
|
||||
logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters)
|
||||
except DMError:
|
||||
print "getLogcat threw DMError; re-trying just once..."
|
||||
time.sleep(1)
|
||||
logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters)
|
||||
|
||||
javaException = mozcrash.check_for_java_exception(logcat)
|
||||
if javaException:
|
||||
return True
|
||||
|
@ -8,7 +8,7 @@
|
||||
# It is referred to by the following page, so if this file moves, that page must
|
||||
# be modified accordingly:
|
||||
#
|
||||
# http://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F
|
||||
# https://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F
|
||||
#
|
||||
# Empty lines and lines which begin with "#" are ignored and may be used for
|
||||
# storing comments. All other lines consist of an origin followed by whitespace
|
||||
|
@ -13,10 +13,6 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class nsIContentParent;
|
||||
};
|
||||
|
||||
namespace ipc {
|
||||
class URIParams;
|
||||
};
|
||||
|
@ -9,8 +9,6 @@
|
||||
#include "jsapi.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
||||
class nsCString;
|
||||
|
||||
struct nsJSPrincipals : nsIPrincipal, JSPrincipals
|
||||
{
|
||||
static bool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
|
||||
|
@ -15,9 +15,6 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
|
||||
class nsIObjectInputStream;
|
||||
class nsIObjectOutputStream;
|
||||
|
||||
class nsBasePrincipal : public nsJSPrincipals
|
||||
{
|
||||
public:
|
||||
|
@ -17,13 +17,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class nsIDocShell;
|
||||
class nsCString;
|
||||
class nsIClassInfo;
|
||||
class nsIIOService;
|
||||
class nsIStringBundle;
|
||||
class nsSystemPrincipal;
|
||||
class ClassInfoData;
|
||||
|
||||
/////////////////////////////
|
||||
// nsScriptSecurityManager //
|
||||
|
@ -3017,6 +3017,7 @@ then
|
||||
esac
|
||||
LDFLAGS="${_PTHREAD_LDFLAGS} ${LDFLAGS}"
|
||||
AC_SUBST(MOZ_USE_PTHREADS)
|
||||
MOZ_CHECK_HEADERS(pthread.h)
|
||||
fi
|
||||
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "nsISupports.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsString;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -14,8 +14,6 @@
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsILoadContext.h"
|
||||
|
||||
class mozIApplication;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
|
@ -5087,7 +5087,8 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
||||
}
|
||||
}
|
||||
} else if (NS_ERROR_PHISHING_URI == aError ||
|
||||
NS_ERROR_MALWARE_URI == aError) {
|
||||
NS_ERROR_MALWARE_URI == aError ||
|
||||
NS_ERROR_UNWANTED_URI == aError) {
|
||||
nsAutoCString host;
|
||||
aURI->GetHost(host);
|
||||
CopyUTF8toUTF16(host, formatStrs[0]);
|
||||
@ -5106,14 +5107,19 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
||||
error.AssignLiteral("phishingBlocked");
|
||||
bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_PHISHING_PAGE_FRAME
|
||||
: nsISecurityUITelemetry::WARNING_PHISHING_PAGE_TOP;
|
||||
} else {
|
||||
} else if (NS_ERROR_MALWARE_URI == aError) {
|
||||
error.AssignLiteral("malwareBlocked");
|
||||
bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_MALWARE_PAGE_FRAME
|
||||
: nsISecurityUITelemetry::WARNING_MALWARE_PAGE_TOP;
|
||||
} else {
|
||||
error.AssignLiteral("unwantedBlocked");
|
||||
bucketId = IsFrame() ? nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_FRAME
|
||||
: nsISecurityUITelemetry::WARNING_UNWANTED_PAGE_TOP;
|
||||
}
|
||||
|
||||
if (errorPage.EqualsIgnoreCase("blocked"))
|
||||
if (errorPage.EqualsIgnoreCase("blocked")) {
|
||||
Telemetry::Accumulate(Telemetry::SECURITY_UI, bucketId);
|
||||
}
|
||||
|
||||
cssClass.AssignLiteral("blacklist");
|
||||
} else if (NS_ERROR_CONTENT_CRASHED == aError) {
|
||||
@ -7824,6 +7830,7 @@ nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
|
||||
aStatus == NS_ERROR_OFFLINE ||
|
||||
aStatus == NS_ERROR_MALWARE_URI ||
|
||||
aStatus == NS_ERROR_PHISHING_URI ||
|
||||
aStatus == NS_ERROR_UNWANTED_URI ||
|
||||
aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
|
||||
aStatus == NS_ERROR_REMOTE_XUL ||
|
||||
aStatus == NS_ERROR_OFFLINE ||
|
||||
|
@ -88,7 +88,6 @@ class nsIURIFixup;
|
||||
class nsIURILoader;
|
||||
class nsIWebBrowserFind;
|
||||
class nsIWidget;
|
||||
class ProfilerMarkerTracing;
|
||||
|
||||
/* load commands were moved to nsIDocShell.h */
|
||||
/* load types were moved to nsDocShellLoadTypes.h */
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(AF13EA3A-D488-4308-B843-526E055AB943)]
|
||||
interface nsIDOMNode;
|
||||
|
||||
[scriptable, uuid(35BE2D7E-F29B-48EC-BF7E-80A30A724DE3)]
|
||||
interface nsIContentViewerEdit : nsISupports
|
||||
{
|
||||
void clearSelection();
|
||||
@ -27,4 +29,8 @@ interface nsIContentViewerEdit : nsISupports
|
||||
|
||||
AString getContents(in string aMimeType, in boolean aSelectionOnly);
|
||||
readonly attribute boolean canGetContents;
|
||||
|
||||
// Set the node that will be the subject of the editing commands above.
|
||||
// Usually this will be the node that was context-clicked.
|
||||
void setCommandNode(in nsIDOMNode aNode);
|
||||
};
|
||||
|
@ -13,7 +13,6 @@ class nsIContent;
|
||||
class nsIDocShell;
|
||||
class nsIInputStream;
|
||||
class nsIRequest;
|
||||
class nsString;
|
||||
|
||||
// Interface ID for nsILinkHandler
|
||||
#define NS_ILINKHANDLER_IID \
|
||||
|
@ -291,6 +291,7 @@
|
||||
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
|
||||
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
|
||||
<h1 id="et_malwareBlocked">&malwareBlocked.title;</h1>
|
||||
<h1 id="et_unwantedBlocked">&unwantedBlocked.title;</h1>
|
||||
<h1 id="et_cspBlocked">&cspBlocked.title;</h1>
|
||||
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
|
||||
<h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
|
||||
@ -317,6 +318,7 @@
|
||||
<div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
|
||||
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
|
||||
<div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div>
|
||||
<div id="ed_unwantedBlocked">&unwantedBlocked.longDesc;</div>
|
||||
<div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
|
||||
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
|
||||
<div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
|
||||
|
@ -217,6 +217,32 @@ Animation::GetFinished(ErrorResult& aRv)
|
||||
return mFinished;
|
||||
}
|
||||
|
||||
void
|
||||
Animation::Finish(ErrorResult& aRv)
|
||||
{
|
||||
// https://w3c.github.io/web-animations/#finish-an-animation
|
||||
|
||||
if (mPlaybackRate == 0 ||
|
||||
(mPlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
TimeDuration limit =
|
||||
mPlaybackRate > 0 ? TimeDuration(EffectEnd()) : TimeDuration(0);
|
||||
|
||||
SetCurrentTime(limit);
|
||||
|
||||
if (mPendingState == PendingState::PlayPending) {
|
||||
CancelPendingTasks();
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
UpdateFinishedState(true);
|
||||
PostUpdate();
|
||||
}
|
||||
|
||||
void
|
||||
Animation::Play(LimitBehavior aLimitBehavior)
|
||||
{
|
||||
|
@ -94,6 +94,7 @@ public:
|
||||
AnimationPlayState PlayState() const;
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
virtual Promise* GetFinished(ErrorResult& aRv);
|
||||
virtual void Finish(ErrorResult& aRv);
|
||||
virtual void Play(LimitBehavior aLimitBehavior);
|
||||
virtual void Pause();
|
||||
bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
|
||||
|
@ -83,112 +83,6 @@ const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
/**
|
||||
* CSS animation events fire asynchronously after we set 'startTime'. This
|
||||
* helper class allows us to handle such events using Promises.
|
||||
*
|
||||
* To use this class:
|
||||
*
|
||||
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
|
||||
* eventWatcher.waitForEvent(eventType).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkStuff();
|
||||
* makeSomeChanges();
|
||||
* return eventWatcher.waitForEvent(nextEventType);
|
||||
* }).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkMoreStuff();
|
||||
* eventWatcher.stopWatching(); // all done - stop listening for events
|
||||
* });
|
||||
*
|
||||
* This class will assert_unreached() if an event occurs when there is no
|
||||
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
|
||||
* event is of a different type to the type passed to waitForEvent. This helps
|
||||
* provide test coverage to ensure that only events that are expected occur, in
|
||||
* the correct order and with the correct timing. It also helps vastly simplify
|
||||
* the already complex code below by avoiding lots of gnarly error handling
|
||||
* code.
|
||||
*/
|
||||
function EventWatcher(watchedNode, eventTypes)
|
||||
{
|
||||
if (typeof eventTypes == 'string') {
|
||||
eventTypes = [eventTypes];
|
||||
}
|
||||
|
||||
var waitingFor = null;
|
||||
|
||||
function eventHandler(evt) {
|
||||
if (!waitingFor) {
|
||||
assert_unreached('Not expecting event, but got: ' + evt.type +
|
||||
' targeting element #' + evt.target.getAttribute('id'));
|
||||
return;
|
||||
}
|
||||
if (evt.type != waitingFor.types[0]) {
|
||||
assert_unreached('Expected ' + waitingFor.types[0] + ' event but got ' +
|
||||
evt.type + ' event');
|
||||
return;
|
||||
}
|
||||
if (waitingFor.types.length > 1) {
|
||||
// Pop first event from array
|
||||
waitingFor.types.shift();
|
||||
return;
|
||||
}
|
||||
// We need to null out waitingFor before calling the resolve function since
|
||||
// the Promise's resolve handlers may call waitForEvent() which will need
|
||||
// to set waitingFor.
|
||||
var resolveFunc = waitingFor.resolve;
|
||||
waitingFor = null;
|
||||
resolveFunc(evt);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.addEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
|
||||
this.waitForEvent = function(type) {
|
||||
if (typeof type != 'string') {
|
||||
return Promise.reject('Event type not a string');
|
||||
}
|
||||
return this.waitForEvents([type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful when two events are expected to fire one immediately after
|
||||
* the other. This happens when we skip over the entire active interval for
|
||||
* instance. In this case an 'animationstart' and an 'animationend' are fired
|
||||
* and due to the asynchronous nature of Promise callbacks this won't work:
|
||||
*
|
||||
* eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
* return waitForEvent('animationend');
|
||||
* }).then(...);
|
||||
*
|
||||
* It doesn't work because the 'animationend' listener is added too late,
|
||||
* because the resolve handler for the first Promise is called asynchronously
|
||||
* some time after the 'animationstart' event is called, rather than at the
|
||||
* time the event reaches the watched element.
|
||||
*/
|
||||
this.waitForEvents = function(types) {
|
||||
if (waitingFor) {
|
||||
return Promise.reject('Already waiting for an event');
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitingFor = {
|
||||
types: types,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.stopWatching = function() {
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.removeEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
@ -336,7 +230,7 @@ test(function(t)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
@ -347,7 +241,7 @@ async_test(function(t) {
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.waitForEvent('animationstart');
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
@ -357,11 +251,9 @@ async_test(function(t) {
|
||||
|
||||
animation.currentTime =
|
||||
currentTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -372,7 +264,7 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
@ -389,8 +281,8 @@ async_test(function(t) {
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
@ -410,9 +302,9 @@ async_test(function(t) {
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.waitForEvent('animationstart');
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
@ -424,11 +316,9 @@ async_test(function(t) {
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -454,7 +344,7 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
@ -462,14 +352,13 @@ async_test(function(t) {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
@ -477,23 +366,21 @@ async_test(function(t) {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -503,16 +390,15 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -522,17 +408,16 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForBeforePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -542,17 +427,16 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.currentTime = currentTimeForActivePhase(animation.timeline);
|
||||
animation.currentTime = currentTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
|
160
dom/animation/test/css-animations/test_animation-finish.html
Normal file
160
dom/animation/test/css-animations/test_animation-finish.html
Normal file
@ -0,0 +1,160 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@keyframes anim {
|
||||
from { margin-left: 100px; }
|
||||
to { margin-left: 200px; }
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
'use strict';
|
||||
|
||||
const ANIM_PROP_VAL = 'anim 100s';
|
||||
const ANIM_DURATION = 100000; // ms
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.playbackRate = 0;
|
||||
|
||||
var threw = false;
|
||||
try {
|
||||
animation.finish();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert_equals(e.name, 'InvalidStateError',
|
||||
'Exception should be an InvalidStateError exception when ' +
|
||||
'trying to finish an animation with playbackRate == 0');
|
||||
}
|
||||
assert_true(threw,
|
||||
'Expect InvalidStateError exception trying to finish an ' +
|
||||
'animation with playbackRate == 0');
|
||||
}, 'Test exceptions when finishing non-running animation');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
div.style.animationIterationCount = 'infinite';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var threw = false;
|
||||
try {
|
||||
animation.finish();
|
||||
} catch (e) {
|
||||
threw = true;
|
||||
assert_equals(e.name, 'InvalidStateError',
|
||||
'Exception should be an InvalidStateError exception when ' +
|
||||
'trying to finish an infinite animation');
|
||||
}
|
||||
assert_true(threw,
|
||||
'Expect InvalidStateError exception trying to finish an ' +
|
||||
'infinite animation');
|
||||
}, 'Test exceptions when finishing infinite animation');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.finish();
|
||||
assert_equals(animation.currentTime, ANIM_DURATION,
|
||||
'After finishing, the currentTime should be set to the end ' +
|
||||
'of the active duration');
|
||||
}, 'Test finishing of animation');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = ANIM_DURATION + 1000; // 1s past effect end
|
||||
|
||||
animation.finish();
|
||||
assert_equals(animation.currentTime, ANIM_DURATION,
|
||||
'After finishing, the currentTime should be set back to the ' +
|
||||
'end of the active duration');
|
||||
}, 'Test finishing of animation with a current time past the effect end');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.playbackRate = -1;
|
||||
animation.finish();
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'After finishing a reversed animation the currentTime ' +
|
||||
'should be set to zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finishing of reversed animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.playbackRate = -1;
|
||||
|
||||
animation.currentTime = -1000;
|
||||
|
||||
animation.finish();
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'After finishing a reversed animation the currentTime ' +
|
||||
'should be set back to zero');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test finishing of reversed animation with with a current time less ' +
|
||||
'than zero');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.pause();
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
assert_equals(animation.playState, 'paused',
|
||||
'The play state of a paused animation should remain ' +
|
||||
'"paused" even after finish() is called');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test paused state after finishing of animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
div.style.animation = ANIM_PROP_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(t.step_func(function() {
|
||||
animation.finish();
|
||||
var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
|
||||
assert_equals(marginLeft, 10,
|
||||
'The computed style should be reset when finish() is ' +
|
||||
'called');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test resetting of computed style');
|
||||
|
||||
</script>
|
@ -77,7 +77,7 @@ async_test(function(t) {
|
||||
|
||||
animation.currentTime = ANIM_DURATION;
|
||||
|
||||
animation.finished.then(function() {
|
||||
animation.finished.then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.playbackRate = -1;
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
@ -85,7 +85,7 @@ async_test(function(t) {
|
||||
'finished promise');
|
||||
animation.currentTime = 0;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
})).then(t.step_func(function() {
|
||||
previousFinishedPromise = animation.finished;
|
||||
animation.play();
|
||||
assert_not_equals(animation.finished, previousFinishedPromise,
|
||||
|
@ -44,13 +44,13 @@ async_test(function(t) {
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
var originalReadyPromise = animation.ready;
|
||||
animation.ready.then(function() {
|
||||
animation.ready.then(t.step_func(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_not_equals(animation.ready, originalReadyPromise,
|
||||
'After updating animation-play-state a new ready promise'
|
||||
+ ' object is created');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||
|
||||
async_test(function(t) {
|
||||
@ -58,14 +58,14 @@ async_test(function(t) {
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.ready.then(t.step_func(function() {
|
||||
var promiseBeforeCallingPlay = animation.ready;
|
||||
animation.play();
|
||||
assert_equals(animation.ready, promiseBeforeCallingPlay,
|
||||
'Ready promise has same object identity after redundant call'
|
||||
+ ' to play()');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'Redundant calls to play() do not generate new ready promise objects');
|
||||
|
||||
async_test(function(t) {
|
||||
@ -73,12 +73,12 @@ async_test(function(t) {
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function(resolvedAnimation) {
|
||||
animation.ready.then(t.step_func(function(resolvedAnimation) {
|
||||
assert_equals(resolvedAnimation, animation,
|
||||
'Object identity of Animation passed to Promise callback'
|
||||
+ ' matches the Animation object owning the Promise');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'The ready promise is fulfilled with its Animation');
|
||||
|
||||
async_test(function(t) {
|
||||
|
@ -83,112 +83,6 @@ const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
/**
|
||||
* CSS animation events fire asynchronously after we set 'startTime'. This
|
||||
* helper class allows us to handle such events using Promises.
|
||||
*
|
||||
* To use this class:
|
||||
*
|
||||
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
|
||||
* eventWatcher.waitForEvent(eventType).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkStuff();
|
||||
* makeSomeChanges();
|
||||
* return eventWatcher.waitForEvent(nextEventType);
|
||||
* }).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkMoreStuff();
|
||||
* eventWatcher.stopWatching(); // all done - stop listening for events
|
||||
* });
|
||||
*
|
||||
* This class will assert_unreached() if an event occurs when there is no
|
||||
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
|
||||
* event is of a different type to the type passed to waitForEvent. This helps
|
||||
* provide test coverage to ensure that only events that are expected occur, in
|
||||
* the correct order and with the correct timing. It also helps vastly simplify
|
||||
* the already complex code below by avoiding lots of gnarly error handling
|
||||
* code.
|
||||
*/
|
||||
function EventWatcher(watchedNode, eventTypes)
|
||||
{
|
||||
if (typeof eventTypes == 'string') {
|
||||
eventTypes = [eventTypes];
|
||||
}
|
||||
|
||||
var waitingFor = null;
|
||||
|
||||
function eventHandler(evt) {
|
||||
if (!waitingFor) {
|
||||
assert_unreached('Not expecting event, but got: ' + evt.type +
|
||||
' targeting element #' + evt.target.getAttribute('id'));
|
||||
return;
|
||||
}
|
||||
if (evt.type != waitingFor.types[0]) {
|
||||
assert_unreached('Expected ' + waitingFor.types[0] + ' event but got ' +
|
||||
evt.type + ' event');
|
||||
return;
|
||||
}
|
||||
if (waitingFor.types.length > 1) {
|
||||
// Pop first event from array
|
||||
waitingFor.types.shift();
|
||||
return;
|
||||
}
|
||||
// We need to null out waitingFor before calling the resolve function since
|
||||
// the Promise's resolve handlers may call waitForEvent() which will need
|
||||
// to set waitingFor.
|
||||
var resolveFunc = waitingFor.resolve;
|
||||
waitingFor = null;
|
||||
resolveFunc(evt);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.addEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
|
||||
this.waitForEvent = function(type) {
|
||||
if (typeof type != 'string') {
|
||||
return Promise.reject('Event type not a string');
|
||||
}
|
||||
return this.waitForEvents([type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful when two events are expected to fire one immediately after
|
||||
* the other. This happens when we skip over the entire active interval for
|
||||
* instance. In this case an 'animationstart' and an 'animationend' are fired
|
||||
* and due to the asynchronous nature of Promise callbacks this won't work:
|
||||
*
|
||||
* eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
* return waitForEvent('animationend');
|
||||
* }).then(...);
|
||||
*
|
||||
* It doesn't work because the 'animationend' listener is added too late,
|
||||
* because the resolve handler for the first Promise is called asynchronously
|
||||
* some time after the 'animationstart' event is called, rather than at the
|
||||
* time the event reaches the watched element.
|
||||
*/
|
||||
this.waitForEvents = function(types) {
|
||||
if (waitingFor) {
|
||||
return Promise.reject('Already waiting for an event');
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitingFor = {
|
||||
types: types,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.stopWatching = function() {
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.removeEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
@ -390,7 +284,7 @@ test(function(t)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
@ -400,7 +294,7 @@ async_test(function(t) {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.waitForEvent('animationstart');
|
||||
return eventWatcher.wait_for('animationstart');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
||||
@ -409,11 +303,9 @@ async_test(function(t) {
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -424,7 +316,7 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
|
||||
@ -438,8 +330,8 @@ async_test(function(t) {
|
||||
// an 'animationend' event. We need to wait for these events before we start
|
||||
// testing going backwards since EventWatcher will fail the test if it gets
|
||||
// an event that we haven't told it about.
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(t.step_func(function() {
|
||||
assert_true(document.timeline.currentTime - previousTimelineTime <
|
||||
ANIM_DUR_MS,
|
||||
'Sanity check that seeking worked rather than the events ' +
|
||||
@ -459,9 +351,9 @@ async_test(function(t) {
|
||||
//
|
||||
// Calling checkStateAtFiftyPctOfActiveInterval will check computed style,
|
||||
// causing computed style to be updated and the 'animationstart' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
var promise = eventWatcher.waitForEvent('animationstart');
|
||||
var promise = eventWatcher.wait_for('animationstart');
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
return promise;
|
||||
})).then(t.step_func(function() {
|
||||
@ -472,11 +364,9 @@ async_test(function(t) {
|
||||
// Despite going backwards from just after the active interval starts to
|
||||
// the animation start time, we now expect an animationend event
|
||||
// because we went from inside to outside the active interval.
|
||||
return eventWatcher.waitForEvent('animationend');
|
||||
return eventWatcher.wait_for('animationend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateOnReadyPromiseResolved(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -502,7 +392,7 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
@ -510,14 +400,13 @@ async_test(function(t) {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> active, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
@ -525,23 +414,21 @@ async_test(function(t) {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant change, before -> after, then back');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -551,16 +438,15 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
eventWatcher.wait_for('animationstart').then(function() {
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -570,17 +456,16 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForBeforePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -590,17 +475,16 @@ async_test(function(t) {
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, CSS_ANIM_EVENTS);
|
||||
var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS);
|
||||
div.style.animation = ANIM_PROPERTY_VAL;
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
eventWatcher.waitForEvents(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
eventWatcher.wait_for(['animationstart',
|
||||
'animationend']).then(function() {
|
||||
animation.startTime = startTimeForActivePhase(animation.timeline);
|
||||
animation.startTime = startTimeForAfterPhase(animation.timeline);
|
||||
|
||||
waitForAnimationFrames(2).then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
@ -623,11 +507,11 @@ async_test(function(t) {
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
|
@ -73,111 +73,6 @@ const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
/**
|
||||
* CSS animation events fire asynchronously after we set 'startTime'. This
|
||||
* helper class allows us to handle such events using Promises.
|
||||
*
|
||||
* To use this class:
|
||||
*
|
||||
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
|
||||
* eventWatcher.waitForEvent(eventType).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkStuff();
|
||||
* makeSomeChanges();
|
||||
* return eventWatcher.waitForEvent(nextEventType);
|
||||
* }).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkMoreStuff();
|
||||
* eventWatcher.stopWatching(); // all done - stop listening for events
|
||||
* });
|
||||
*
|
||||
* This class will assert_unreached() if an event occurs when there is no
|
||||
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
|
||||
* event is of a different type to the type passed to waitForEvent. This helps
|
||||
* provide test coverage to ensure that only events that are expected occur, in
|
||||
* the correct order and with the correct timing. It also helps vastly simplify
|
||||
* the already complex code below by avoiding lots of gnarly error handling
|
||||
* code.
|
||||
*/
|
||||
function EventWatcher(watchedNode, eventTypes)
|
||||
{
|
||||
if (typeof eventTypes == 'string') {
|
||||
eventTypes = [eventTypes];
|
||||
}
|
||||
|
||||
var waitingFor = null;
|
||||
|
||||
function eventHandler(evt) {
|
||||
if (!waitingFor) {
|
||||
assert_unreached('Not expecting event, but got: ' + evt.type +
|
||||
' targeting element #' + evt.target.getAttribute('id'));
|
||||
return;
|
||||
}
|
||||
if (evt.type != waitingFor.types[0]) {
|
||||
assert_unreached('Expected ' + waitingFor.types[0] + ' event but got ' +
|
||||
evt.type + ' event');
|
||||
return;
|
||||
}
|
||||
if (waitingFor.types.length > 1) {
|
||||
// Pop first event from array
|
||||
waitingFor.types.shift();
|
||||
return;
|
||||
}
|
||||
// We need to null out waitingFor before calling the resolve function since
|
||||
// the Promise's resolve handlers may call waitForEvent() which will need
|
||||
// to set waitingFor.
|
||||
var resolveFunc = waitingFor.resolve;
|
||||
waitingFor = null;
|
||||
resolveFunc(evt);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.addEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
|
||||
this.waitForEvent = function(type) {
|
||||
if (typeof type != 'string') {
|
||||
return Promise.reject('Event type not a string');
|
||||
}
|
||||
return this.waitForEvents([type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful when two events are expected to fire one immediately after
|
||||
* the other. This happens when we skip over the entire active interval for
|
||||
* instance. In this case an 'animationstart' and an 'animationend' are fired
|
||||
* and due to the asynchronous nature of Promise callbacks this won't work:
|
||||
*
|
||||
* eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
* return waitForEvent('animationend');
|
||||
* }).then(...);
|
||||
*
|
||||
* It doesn't work because the 'animationend' listener is added too late,
|
||||
* because the resolve handler for the first Promise is called asynchronously
|
||||
* some time after the 'animationstart' event is called, rather than at the
|
||||
* time the event reaches the watched element.
|
||||
*/
|
||||
this.waitForEvents = function(types) {
|
||||
if (waitingFor) {
|
||||
return Promise.reject('Already waiting for an event');
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitingFor = {
|
||||
types: types,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.stopWatching = function() {
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.removeEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
@ -300,7 +195,7 @@ test(function(t)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, 'transitionend');
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
@ -317,11 +212,9 @@ async_test(function(t) {
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.currentTime = currentTimeForEndOfActiveInterval();
|
||||
return eventWatcher.waitForEvent('transitionend');
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -332,7 +225,7 @@ async_test(function(t) {
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, 'transitionend');
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
@ -356,10 +249,9 @@ test(function(t) {
|
||||
//
|
||||
// Calling checkStateAtActiveIntervalStartTime will check computed style,
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// be dispatched synchronously. We need to call wait_for first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.waitForEvent('transitionend').then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
|
@ -0,0 +1,61 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
|
||||
.animated-div {
|
||||
margin-left: 100px;
|
||||
transition: margin-left 1000s linear 1000s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
'use strict';
|
||||
|
||||
const ANIM_DELAY_MS = 1000000; // 1000s
|
||||
const ANIM_DUR_MS = 1000000; // 1000s
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.finish();
|
||||
|
||||
animation.finished.then(t.step_func(function() {
|
||||
animation.play();
|
||||
assert_equals(animation.currentTime, 0,
|
||||
'Replaying a finished transition should reset its ' +
|
||||
'currentTime');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a finished transition');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
animation.ready.then(function() {
|
||||
animation.playbackRate = -1;
|
||||
return animation.finished;
|
||||
}).then(t.step_func(function() {
|
||||
animation.play();
|
||||
// FIXME: once animation.effect.computedTiming.endTime is available (bug
|
||||
// 1108055) we should use that here.
|
||||
assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
|
||||
'Replaying a finished reversed transition should reset ' +
|
||||
'its currentTime to the end of the effect');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Test restarting a reversed finished transition');
|
||||
|
||||
</script>
|
@ -73,112 +73,6 @@ const TEN_PCT_POSITION = 110;
|
||||
const FIFTY_PCT_POSITION = 150;
|
||||
const END_POSITION = 200;
|
||||
|
||||
/**
|
||||
* CSS animation events fire asynchronously after we set 'startTime'. This
|
||||
* helper class allows us to handle such events using Promises.
|
||||
*
|
||||
* To use this class:
|
||||
*
|
||||
* var eventWatcher = new EventWatcher(watchedNode, eventTypes);
|
||||
* eventWatcher.waitForEvent(eventType).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkStuff();
|
||||
* makeSomeChanges();
|
||||
* return eventWatcher.waitForEvent(nextEventType);
|
||||
* }).then(function() {
|
||||
* // Promise fulfilled
|
||||
* checkMoreStuff();
|
||||
* eventWatcher.stopWatching(); // all done - stop listening for events
|
||||
* });
|
||||
*
|
||||
* This class will assert_unreached() if an event occurs when there is no
|
||||
* Promise created by a waitForEvent() call waiting to be fulfilled, or if the
|
||||
* event is of a different type to the type passed to waitForEvent. This helps
|
||||
* provide test coverage to ensure that only events that are expected occur, in
|
||||
* the correct order and with the correct timing. It also helps vastly simplify
|
||||
* the already complex code below by avoiding lots of gnarly error handling
|
||||
* code.
|
||||
*/
|
||||
function EventWatcher(watchedNode, eventTypes)
|
||||
{
|
||||
if (typeof eventTypes == 'string') {
|
||||
eventTypes = [eventTypes];
|
||||
}
|
||||
|
||||
var waitingFor = null;
|
||||
|
||||
function eventHandler(evt) {
|
||||
if (!waitingFor) {
|
||||
assert_unreached('Not expecting event, but got: ' + evt.type +
|
||||
' targeting element #' + evt.target.getAttribute('id'));
|
||||
return;
|
||||
}
|
||||
if (evt.type != waitingFor.types[0]) {
|
||||
assert_unreached('Expected ' + waitingFor.types[0] + ' event but got ' +
|
||||
evt.type + ' event');
|
||||
return;
|
||||
}
|
||||
if (waitingFor.types.length > 1) {
|
||||
// Pop first event from array
|
||||
waitingFor.types.shift();
|
||||
return;
|
||||
}
|
||||
// We need to null out waitingFor before calling the resolve function since
|
||||
// the Promise's resolve handlers may call waitForEvent() which will need
|
||||
// to set waitingFor.
|
||||
var resolveFunc = waitingFor.resolve;
|
||||
waitingFor = null;
|
||||
resolveFunc(evt);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.addEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
|
||||
this.waitForEvent = function(type) {
|
||||
if (typeof type != 'string') {
|
||||
return Promise.reject('Event type not a string');
|
||||
}
|
||||
return this.waitForEvents([type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is useful when two events are expected to fire one immediately after
|
||||
* the other. This happens when we skip over the entire active interval for
|
||||
* instance. In this case an 'animationstart' and an 'animationend' are fired
|
||||
* and due to the asynchronous nature of Promise callbacks this won't work:
|
||||
*
|
||||
* eventWatcher.waitForEvent('animationstart').then(function() {
|
||||
* return waitForEvent('animationend');
|
||||
* }).then(...);
|
||||
*
|
||||
* It doesn't work because the 'animationend' listener is added too late,
|
||||
* because the resolve handler for the first Promise is called asynchronously
|
||||
* some time after the 'animationstart' event is called, rather than at the
|
||||
* time the event reaches the watched element.
|
||||
*/
|
||||
this.waitForEvents = function(types) {
|
||||
if (waitingFor) {
|
||||
return Promise.reject('Already waiting for an event');
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitingFor = {
|
||||
types: types,
|
||||
resolve: resolve,
|
||||
reject: reject
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
this.stopWatching = function() {
|
||||
for (var i = 0; i < eventTypes.length; i++) {
|
||||
watchedNode.removeEventListener(eventTypes[i], eventHandler);
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// The terms used for the naming of the following helper functions refer to
|
||||
// terms used in the Web Animations specification for specific phases of an
|
||||
// animation. The terms can be found here:
|
||||
@ -277,7 +171,7 @@ test(function(t)
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, 'transitionend');
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
@ -295,11 +189,9 @@ async_test(function(t) {
|
||||
checkStateAtFiftyPctOfActiveInterval(animation);
|
||||
|
||||
animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
|
||||
return eventWatcher.waitForEvent('transitionend');
|
||||
return eventWatcher.wait_for('transitionend');
|
||||
})).then(t.step_func(function() {
|
||||
checkStateAtActiveIntervalEndTime(animation);
|
||||
|
||||
eventWatcher.stopWatching();
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
@ -310,7 +202,7 @@ async_test(function(t) {
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, {'class': 'animated-div'});
|
||||
var eventWatcher = new EventWatcher(div, 'transitionend');
|
||||
var eventWatcher = new EventWatcher(t, div, 'transitionend');
|
||||
|
||||
flushComputedStyle(div);
|
||||
div.style.marginLeft = '200px'; // initiate transition
|
||||
@ -336,8 +228,7 @@ test(function(t) {
|
||||
// causing computed style to be updated and the 'transitionend' event to
|
||||
// be dispatched synchronously. We need to call waitForEvent first
|
||||
// otherwise eventWatcher will assert that the event was unexpected.
|
||||
eventWatcher.waitForEvent('transitionend').then(function() {
|
||||
eventWatcher.stopWatching();
|
||||
eventWatcher.wait_for('transitionend').then(function() {
|
||||
t.done();
|
||||
});
|
||||
checkStateAtActiveIntervalStartTime(animation);
|
||||
@ -360,11 +251,11 @@ async_test(function(t) {
|
||||
return animation.ready;
|
||||
})).catch(t.step_func(function(reason) {
|
||||
assert_unreached(reason);
|
||||
})).then(function() {
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(animation.currentTime, storedCurrentTime,
|
||||
'Test that hold time is correct');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'Setting startTime to null');
|
||||
|
||||
|
||||
|
@ -3,9 +3,10 @@ support-files =
|
||||
testcommon.js
|
||||
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
[css-animations/test_animation-pausing.html]
|
||||
[css-animations/test_animation-currenttime.html]
|
||||
[css-animations/test_animation-finish.html]
|
||||
[css-animations/test_animation-finished.html]
|
||||
[css-animations/test_animation-pausing.html]
|
||||
[css-animations/test_animation-playstate.html]
|
||||
[css-animations/test_animation-ready.html]
|
||||
[css-animations/test_animation-starttime.html]
|
||||
@ -13,8 +14,9 @@ support-files =
|
||||
[css-animations/test_effect-target.html]
|
||||
[css-animations/test_element-get-animations.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-transitions/test_animation-pausing.html]
|
||||
[css-transitions/test_animation-currenttime.html]
|
||||
[css-transitions/test_animation-finished.html]
|
||||
[css-transitions/test_animation-pausing.html]
|
||||
[css-transitions/test_animation-ready.html]
|
||||
[css-transitions/test_animation-starttime.html]
|
||||
[css-transitions/test_effect-name.html]
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "nscore.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsIDocument;
|
||||
class nsINode;
|
||||
class nsAString;
|
||||
class nsAttrValue;
|
||||
class nsTextNode;
|
||||
|
@ -37,16 +37,11 @@
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "Units.h"
|
||||
|
||||
class nsIDOMEventListener;
|
||||
class nsIFrame;
|
||||
class nsIDOMMozNamedAttrMap;
|
||||
class nsIDOMCSSStyleDeclaration;
|
||||
class nsIURI;
|
||||
class nsIControllers;
|
||||
class nsEventChainVisitor;
|
||||
class nsIScrollableFrame;
|
||||
class nsAttrValueOrString;
|
||||
class ContentUnbinder;
|
||||
class nsContentList;
|
||||
class nsDOMSettableTokenList;
|
||||
class nsDOMTokenList;
|
||||
|
@ -29,10 +29,8 @@
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
class nsDOMMultipartFile;
|
||||
class nsIFile;
|
||||
class nsIInputStream;
|
||||
class nsIClassInfo;
|
||||
|
||||
#define FILEIMPL_IID \
|
||||
{ 0xbccb3275, 0x6778, 0x4ac5, \
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include "nsURIHashKey.h"
|
||||
|
||||
class nsIDocument;
|
||||
class nsIChannel;
|
||||
class nsIPrincipal;
|
||||
class nsINode;
|
||||
class AutoError;
|
||||
|
@ -15,10 +15,7 @@
|
||||
|
||||
class nsIAtom;
|
||||
class nsIContent;
|
||||
class nsIDocument;
|
||||
class nsPIDOMWindow;
|
||||
class nsXBLPrototypeBinding;
|
||||
class nsTagNameMapEntry;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -12,8 +12,6 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsIChannel;
|
||||
class nsIDOMWindow;
|
||||
|
||||
class ThirdPartyUtil final : public mozIThirdPartyUtil
|
||||
{
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIDOMBlob;
|
||||
class nsIPrincipal;
|
||||
class nsISupports;
|
||||
class nsIURI;
|
||||
|
||||
|
@ -28,10 +28,6 @@ class nsIInputStream;
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
}
|
||||
|
||||
class File;
|
||||
|
||||
class WebSocketImpl;
|
||||
|
@ -12,15 +12,11 @@
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsITransferable.h"
|
||||
|
||||
class nsIDOMNode;
|
||||
class nsPIDOMWindow;
|
||||
class nsIDOMDragEvent;
|
||||
class nsISelection;
|
||||
class nsITransferable;
|
||||
class nsIContent;
|
||||
class nsIURI;
|
||||
class nsIFile;
|
||||
class nsISimpleEnumerator;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -15,10 +15,8 @@
|
||||
* (mozilla/content and mozilla/layout).
|
||||
*/
|
||||
|
||||
class nsAString;
|
||||
class nsIContent;
|
||||
class imgRequestProxy;
|
||||
class nsNodeInfoManager;
|
||||
class nsGenericHTMLElement;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -31,11 +31,9 @@ class nsIDocument;
|
||||
class nsIURI;
|
||||
class nsIChannel;
|
||||
class nsIDocShell;
|
||||
class nsIParser;
|
||||
class nsIAtom;
|
||||
class nsIChannel;
|
||||
class nsIContent;
|
||||
class nsViewManager;
|
||||
class nsNodeInfoManager;
|
||||
class nsScriptLoader;
|
||||
class nsIApplicationCache;
|
||||
|
@ -7303,24 +7303,25 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
|
||||
RefPtr<mozilla::gfx::SourceSurface> surface =
|
||||
image->GetFrame(imgIContainer::FRAME_CURRENT,
|
||||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
if (surface) {
|
||||
mozilla::RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
|
||||
surface->GetDataSurface();
|
||||
size_t length;
|
||||
int32_t stride;
|
||||
mozilla::UniquePtr<char[]> surfaceData =
|
||||
nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
|
||||
|
||||
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
|
||||
item->flavor() = nsCString(flavorStr);
|
||||
item->data() = nsCString(surfaceData.get(), length);
|
||||
|
||||
mozilla::RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
|
||||
surface->GetDataSurface();
|
||||
size_t length;
|
||||
int32_t stride;
|
||||
mozilla::UniquePtr<char[]> surfaceData =
|
||||
nsContentUtils::GetSurfaceData(dataSurface, &length, &stride);
|
||||
|
||||
IPCDataTransferItem* item = aIPCDataTransfer->items().AppendElement();
|
||||
item->flavor() = nsCString(flavorStr);
|
||||
item->data() = nsCString(surfaceData.get(), length);
|
||||
|
||||
IPCDataTransferImage& imageDetails = item->imageDetails();
|
||||
mozilla::gfx::IntSize size = dataSurface->GetSize();
|
||||
imageDetails.width() = size.width;
|
||||
imageDetails.height() = size.height;
|
||||
imageDetails.stride() = stride;
|
||||
imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat());
|
||||
IPCDataTransferImage& imageDetails = item->imageDetails();
|
||||
mozilla::gfx::IntSize size = dataSurface->GetSize();
|
||||
imageDetails.width() = size.width;
|
||||
imageDetails.height() = size.height;
|
||||
imageDetails.stride() = stride;
|
||||
imageDetails.format() = static_cast<uint8_t>(dataSurface->GetFormat());
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
@ -51,18 +51,14 @@ class nsIConsoleService;
|
||||
class nsIContent;
|
||||
class nsIContentPolicy;
|
||||
class nsIContentSecurityPolicy;
|
||||
class nsIDocShell;
|
||||
class nsIDocument;
|
||||
class nsIDocumentLoaderFactory;
|
||||
class nsIDocumentObserver;
|
||||
class nsIDOMDocument;
|
||||
class nsIDOMDocumentFragment;
|
||||
class nsIDOMEvent;
|
||||
class nsIDOMHTMLFormElement;
|
||||
class nsIDOMHTMLInputElement;
|
||||
class nsIDOMKeyEvent;
|
||||
class nsIDOMNode;
|
||||
class nsIDOMScriptObjectFactory;
|
||||
class nsIDOMWindow;
|
||||
class nsIDragSession;
|
||||
class nsIEditor;
|
||||
@ -71,7 +67,6 @@ class nsIFrame;
|
||||
class nsIImageLoadingContent;
|
||||
class nsIInterfaceRequestor;
|
||||
class nsIIOService;
|
||||
class nsIJSRuntimeService;
|
||||
class nsILineBreaker;
|
||||
class nsIMessageBroadcaster;
|
||||
class nsNameSpaceManager;
|
||||
@ -83,7 +78,6 @@ class nsIPrincipal;
|
||||
class nsIRequest;
|
||||
class nsIRunnable;
|
||||
class nsIScriptContext;
|
||||
class nsIScriptGlobalObject;
|
||||
class nsIScriptSecurityManager;
|
||||
class nsIStringBundle;
|
||||
class nsIStringBundleService;
|
||||
@ -96,7 +90,6 @@ class nsIXPConnect;
|
||||
class nsNodeInfoManager;
|
||||
class nsPIDOMWindow;
|
||||
class nsPresContext;
|
||||
class nsScriptObjectTracer;
|
||||
class nsStringBuffer;
|
||||
class nsStringHashKey;
|
||||
class nsTextFragment;
|
||||
|
@ -18,11 +18,8 @@
|
||||
#undef GetClassName
|
||||
#endif
|
||||
|
||||
class nsContentList;
|
||||
class nsDocument;
|
||||
struct nsGlobalNameStruct;
|
||||
class nsGlobalWindow;
|
||||
class nsIScriptSecurityManager;
|
||||
|
||||
struct nsDOMClassInfoData;
|
||||
|
||||
|
@ -64,6 +64,13 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMMutationRecord,
|
||||
|
||||
// Observer
|
||||
|
||||
bool
|
||||
nsMutationReceiverBase::IsObservable(nsIContent* aContent)
|
||||
{
|
||||
return !aContent->ChromeOnlyAccess() &&
|
||||
(Observer()->IsChrome() || !aContent->IsInAnonymousSubtree());
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(nsMutationReceiver)
|
||||
NS_IMPL_RELEASE(nsMutationReceiver)
|
||||
|
||||
@ -111,8 +118,7 @@ nsMutationReceiver::AttributeWillChange(nsIDocument* aDocument,
|
||||
int32_t aModType)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!ObservesAttr(aElement, aNameSpaceID, aAttribute) ||
|
||||
aElement->ChromeOnlyAccess()) {
|
||||
!ObservesAttr(RegisterTarget(), aElement, aNameSpaceID, aAttribute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -147,14 +153,16 @@ nsMutationReceiver::CharacterDataWillChange(nsIDocument *aDocument,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
{
|
||||
if (nsAutoMutationBatch::IsBatching() ||
|
||||
!CharacterData() || !(Subtree() || aContent == Target()) ||
|
||||
aContent->ChromeOnlyAccess()) {
|
||||
!CharacterData() ||
|
||||
(!Subtree() && aContent != Target()) ||
|
||||
(Subtree() && RegisterTarget()->SubtreeRoot() != aContent->SubtreeRoot()) ||
|
||||
!IsObservable(aContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
nsDOMMutationRecord* m =
|
||||
Observer()->CurrentRecord(nsGkAtoms::characterData);
|
||||
|
||||
|
||||
NS_ASSERTION(!m->mTarget || m->mTarget == aContent,
|
||||
"Wrong target!");
|
||||
|
||||
@ -173,8 +181,11 @@ nsMutationReceiver::ContentAppended(nsIDocument* aDocument,
|
||||
int32_t aNewIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aFirstNewContent->ChromeOnlyAccess()) {
|
||||
bool wantsChildList =
|
||||
ChildList() &&
|
||||
((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
|
||||
parent == Target());
|
||||
if (!wantsChildList || !IsObservable(aFirstNewContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -211,8 +222,11 @@ nsMutationReceiver::ContentInserted(nsIDocument* aDocument,
|
||||
int32_t aIndexInContainer)
|
||||
{
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
bool wantsChildList = ChildList() && (Subtree() || parent == Target());
|
||||
if (!wantsChildList || aChild->ChromeOnlyAccess()) {
|
||||
bool wantsChildList =
|
||||
ChildList() &&
|
||||
((Subtree() && RegisterTarget()->SubtreeRoot() == parent->SubtreeRoot()) ||
|
||||
parent == Target());
|
||||
if (!wantsChildList || !IsObservable(aChild)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,11 +257,14 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
|
||||
int32_t aIndexInContainer,
|
||||
nsIContent* aPreviousSibling)
|
||||
{
|
||||
if (aChild->ChromeOnlyAccess()) {
|
||||
if (!IsObservable(aChild)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsINode* parent = NODE_FROM(aContainer, aDocument);
|
||||
if (Subtree() && parent->SubtreeRoot() != RegisterTarget()->SubtreeRoot()) {
|
||||
return;
|
||||
}
|
||||
if (nsAutoMutationBatch::IsBatching()) {
|
||||
if (nsAutoMutationBatch::IsRemovalDone()) {
|
||||
// This can happen for example if HTML parser parses to
|
||||
@ -265,7 +282,7 @@ nsMutationReceiver::ContentRemoved(nsIDocument* aDocument,
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Subtree()) {
|
||||
// Try to avoid creating transient observer if the node
|
||||
@ -714,8 +731,9 @@ nsDOMMutationObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(window->IsInnerWindow());
|
||||
bool isChrome = nsContentUtils::IsChromeDoc(window->GetExtantDoc());
|
||||
nsRefPtr<nsDOMMutationObserver> observer =
|
||||
new nsDOMMutationObserver(window.forget(), aCb);
|
||||
new nsDOMMutationObserver(window.forget(), aCb, isChrome);
|
||||
return observer.forget();
|
||||
}
|
||||
|
||||
|
@ -248,14 +248,20 @@ protected:
|
||||
mRegisterTarget->OwnerDoc()->SetMayHaveDOMMutationObservers();
|
||||
}
|
||||
|
||||
bool ObservesAttr(mozilla::dom::Element* aElement,
|
||||
bool IsObservable(nsIContent* aContent);
|
||||
|
||||
bool ObservesAttr(nsINode* aRegisterTarget,
|
||||
mozilla::dom::Element* aElement,
|
||||
int32_t aNameSpaceID,
|
||||
nsIAtom* aAttr)
|
||||
{
|
||||
if (mParent) {
|
||||
return mParent->ObservesAttr(aElement, aNameSpaceID, aAttr);
|
||||
return mParent->ObservesAttr(aRegisterTarget, aElement, aNameSpaceID, aAttr);
|
||||
}
|
||||
if (!Attributes() || (!Subtree() && aElement != Target())) {
|
||||
if (!Attributes() ||
|
||||
(!Subtree() && aElement != Target()) ||
|
||||
(Subtree() && aRegisterTarget->SubtreeRoot() != aElement->SubtreeRoot()) ||
|
||||
!IsObservable(aElement)) {
|
||||
return false;
|
||||
}
|
||||
if (AllAttributes()) {
|
||||
@ -447,9 +453,10 @@ class nsDOMMutationObserver final : public nsISupports,
|
||||
{
|
||||
public:
|
||||
nsDOMMutationObserver(already_AddRefed<nsPIDOMWindow>&& aOwner,
|
||||
mozilla::dom::MutationCallback& aCb)
|
||||
mozilla::dom::MutationCallback& aCb,
|
||||
bool aChrome)
|
||||
: mOwner(aOwner), mLastPendingMutation(nullptr), mPendingMutationCount(0),
|
||||
mCallback(&aCb), mWaitingForRun(false), mId(++sCount)
|
||||
mCallback(&aCb), mWaitingForRun(false), mIsChrome(aChrome), mId(++sCount)
|
||||
{
|
||||
}
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -471,6 +478,11 @@ public:
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
bool IsChrome()
|
||||
{
|
||||
return mIsChrome;
|
||||
}
|
||||
|
||||
void Observe(nsINode& aTarget,
|
||||
const mozilla::dom::MutationObserverInit& aOptions,
|
||||
mozilla::ErrorResult& aRv);
|
||||
@ -571,6 +583,7 @@ protected:
|
||||
nsRefPtr<mozilla::dom::MutationCallback> mCallback;
|
||||
|
||||
bool mWaitingForRun;
|
||||
bool mIsChrome;
|
||||
|
||||
uint64_t mId;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user