This commit is contained in:
Ryan VanderMeulen 2013-09-09 16:30:03 -04:00
Родитель 2e71c50348 77e829ca72
Коммит 6f1f0691b8
40 изменённых файлов: 790 добавлений и 253 удалений

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

@ -1747,7 +1747,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppCacheUtils",
width = window.innerWidth;
height = window.innerHeight;
} else {
let rect = LayoutHelpers.getRect(node, window);
let lh = new LayoutHelpers(window);
let rect = lh.getRect(node, window);
top = rect.top;
left = rect.left;
width = rect.width;

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

@ -19,7 +19,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
Cu.import("resource:///modules/devtools/VariablesView.jsm");
@ -40,6 +39,13 @@ Object.defineProperty(this, "NetworkHelper", {
enumerable: true
});
Object.defineProperty(this, "DevtoolsHelpers", {
get: function() {
return devtools.require("devtools/shared/helpers");
},
configurable: true,
enumerable: true
});
/**
* Object defining the debugger controller components.

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

@ -38,10 +38,10 @@ ToolbarView.prototype = {
this._stepOutButton = document.getElementById("step-out");
this._chromeGlobals = document.getElementById("chrome-globals");
let resumeKey = LayoutHelpers.prettyKey(document.getElementById("resumeKey"), true);
let stepOverKey = LayoutHelpers.prettyKey(document.getElementById("stepOverKey"), true);
let stepInKey = LayoutHelpers.prettyKey(document.getElementById("stepInKey"), true);
let stepOutKey = LayoutHelpers.prettyKey(document.getElementById("stepOutKey"), true);
let resumeKey = DevtoolsHelpers.prettyKey(document.getElementById("resumeKey"), true);
let stepOverKey = DevtoolsHelpers.prettyKey(document.getElementById("stepOverKey"), true);
let stepInKey = DevtoolsHelpers.prettyKey(document.getElementById("stepInKey"), true);
let stepOutKey = DevtoolsHelpers.prettyKey(document.getElementById("stepOutKey"), true);
this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
@ -743,12 +743,12 @@ FilterView.prototype = {
this._variableOperatorButton = document.getElementById("variable-operator-button");
this._variableOperatorLabel = document.getElementById("variable-operator-label");
this._fileSearchKey = LayoutHelpers.prettyKey(document.getElementById("fileSearchKey"), true);
this._globalSearchKey = LayoutHelpers.prettyKey(document.getElementById("globalSearchKey"), true);
this._filteredFunctionsKey = LayoutHelpers.prettyKey(document.getElementById("functionSearchKey"), true);
this._tokenSearchKey = LayoutHelpers.prettyKey(document.getElementById("tokenSearchKey"), true);
this._lineSearchKey = LayoutHelpers.prettyKey(document.getElementById("lineSearchKey"), true);
this._variableSearchKey = LayoutHelpers.prettyKey(document.getElementById("variableSearchKey"), true);
this._fileSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("fileSearchKey"), true);
this._globalSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("globalSearchKey"), true);
this._filteredFunctionsKey = DevtoolsHelpers.prettyKey(document.getElementById("functionSearchKey"), true);
this._tokenSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("tokenSearchKey"), true);
this._lineSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("lineSearchKey"), true);
this._variableSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("variableSearchKey"), true);
this._searchbox.addEventListener("click", this._onClick, false);
this._searchbox.addEventListener("select", this._onSearch, false);

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

@ -8,7 +8,7 @@ var gPane = null;
var gTab = null;
var gDebugger = null;
var gView = null;
var gLH = null;
var gDH = null;
var gL10N = null;
function test() {
@ -17,7 +17,7 @@ function test() {
gPane = aPane;
gDebugger = gPane.panelWin;
gView = gDebugger.DebuggerView;
gLH = gDebugger.LayoutHelpers;
gDH = gDebugger.DevtoolsHelpers;
gL10N = gDebugger.L10N;
testPause();
@ -31,7 +31,7 @@ function testPause() {
let button = gDebugger.document.getElementById("resume");
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("pauseButtonTooltip",
gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
"Button tooltip should be pause when running.");
gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
@ -45,7 +45,7 @@ function testPause() {
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("resumeButtonTooltip",
gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
"Button tooltip should be resume when paused.");
is(frames.querySelectorAll(".dbg-stackframe").length, 0,
@ -70,7 +70,7 @@ function testResume() {
let button = gDebugger.document.getElementById("resume");
is(button.getAttribute("tooltiptext"),
gL10N.getFormatStr("pauseButtonTooltip",
gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
"Button tooltip should be pause when running.");
closeDebuggerAndFinish();
@ -88,6 +88,6 @@ registerCleanupFunction(function() {
gTab = null;
gDebugger = null;
gView = null;
gLH = null;
gDH = null;
gL10N = null;
});

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

@ -11,7 +11,6 @@ const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/Services.jsm");
let promise = require("sdk/core/promise");

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

@ -93,6 +93,7 @@ function LocalHighlighter(aTarget, aInspector, aToolbox)
this.chromeDoc = this.tab.ownerDocument;
this.chromeWin = this.chromeDoc.defaultView;
this.inspector = aInspector
this.layoutHelpers = new LayoutHelpers(this.browser.contentWindow);
EventEmitter.decorate(this);
@ -225,7 +226,7 @@ LocalHighlighter.prototype = {
this.invalidateSize();
if (!this._highlighting &&
this.selection.reason != "highlighter") {
LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
}
} else {
this.disabled = true;
@ -255,7 +256,7 @@ LocalHighlighter.prototype = {
}
let clientRect = this.selection.node.getBoundingClientRect();
let rect = LayoutHelpers.getDirtyRect(this.selection.node);
let rect = this.layoutHelpers.getDirtyRect(this.selection.node);
this.highlightRectangle(rect);
this.moveInfobar();
@ -461,7 +462,7 @@ LocalHighlighter.prototype = {
texthbox.addEventListener("mousedown", function(aEvent) {
// On click, show the node:
if (this.selection.isElementNode()) {
LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
}
}.bind(this), true);
@ -514,7 +515,7 @@ LocalHighlighter.prototype = {
return; // same rectangle
}
let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
let aRectScaled = this.layoutHelpers.getZoomedRect(this.win, aRect);
if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
aRectScaled.width > 0 && aRectScaled.height > 0) {
@ -809,7 +810,7 @@ LocalHighlighter.prototype = {
// This should never happen, but just in case, we don't let the
// highlighter highlight browser nodes.
if (doc && doc != this.chromeDoc) {
let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
let element = this.layoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
aEvent.clientX, aEvent.clientY);
if (element && element != this.selection.node) {
this.selection.setNode(element, "highlighter");

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

@ -33,7 +33,8 @@ function test()
let tmp = {};
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
let lh = new tmp.LayoutHelpers(window.content);
ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
// Wait for the inspector to process the mutation
inspector.once("inspector-updated", () => {

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

@ -33,7 +33,8 @@ function test()
let tmp = {};
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
let lh = new tmp.LayoutHelpers(window.content);
ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
ok(!inspector.selection.isConnected(), "Selection considered as disconnected");
finishUp();

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

@ -77,7 +77,8 @@ function getHighlitNode()
// Get midpoint of diagonal line.
let midpoint = midPoint(a, b);
return LayoutHelpers.getElementFromPoint(h.win.document, midpoint.x,
let lh = new LayoutHelpers(window.content);
return lh.getElementFromPoint(h.win.document, midpoint.x,
midpoint.y);
}

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

@ -8,7 +8,6 @@
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/Console.jsm");

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

@ -53,6 +53,8 @@ function MarkupView(aInspector, aFrame, aControllerWindow)
this.doc = this._frame.contentDocument;
this._elt = this.doc.querySelector("#root");
this.layoutHelpers = new LayoutHelpers(this.doc.defaultView);
try {
this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
} catch(ex) {
@ -406,7 +408,7 @@ MarkupView.prototype = {
return this._ensureVisible(aNode);
}).then(() => {
// Why is this not working?
LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
this.layoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
});
},
@ -1231,7 +1233,7 @@ ElementEditor.prototype = {
// Double quotes need to be handled specially to prevent DOMParser failing.
// name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
// name="v'a"l'u"e" when editing -> name="v'a"l'u"e"
let editValueDisplayed = aAttr.value;
let editValueDisplayed = aAttr.value || "";
let hasDoubleQuote = editValueDisplayed.contains('"');
let hasSingleQuote = editValueDisplayed.contains("'");
let initial = aAttr.name + '="' + editValueDisplayed + '"';

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

@ -260,6 +260,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
dumpn("Initializing the RequestsMenuView");
this.widget = new SideMenuWidget($("#requests-menu-contents"), false);
this._splitter = $('#splitter');
this._summary = $("#request-menu-network-summary");
this.allowFocusOnRightClick = true;
@ -267,6 +268,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.widget.autoscrollWithAppendedItems = true;
this.widget.addEventListener("select", this._onSelect, false);
this._splitter.addEventListener("mousemove", this._onResize, false);
window.addEventListener("resize", this._onResize, false);
},
@ -277,6 +279,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
dumpn("Destroying the SourcesView");
this.widget.removeEventListener("select", this._onSelect, false);
this._splitter.removeEventListener("mousemove", this._onResize, false);
window.removeEventListener("resize", this._onResize, false);
},
@ -1312,6 +1315,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
return this._cachedWaterfallWidth;
},
_splitter: null,
_summary: null,
_canvas: null,
_ctx: null,

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

@ -210,7 +210,7 @@
</hbox>
</vbox>
<splitter class="devtools-side-splitter"/>
<splitter id="splitter" class="devtools-side-splitter"/>
<deck id="details-pane"
hidden="true">

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

@ -19,6 +19,7 @@ let require = Components.utils.import("resource://gre/modules/devtools/Loader.js
let { Cc, Ci, Cu } = require("chrome");
let promise = require("sdk/core/promise");
let Telemetry = require("devtools/shared/telemetry");
let DevtoolsHelpers = require("devtools/shared/helpers");
let TargetFactory = require("devtools/framework/target").TargetFactory;
const escodegen = require("escodegen/escodegen");
@ -26,7 +27,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource:///modules/source-editor.jsm");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource://gre/modules/jsdebugger.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
@ -1284,9 +1284,9 @@ var Scratchpad = {
let initialText = this.strings.formatStringFromName(
"scratchpadIntro1",
[LayoutHelpers.prettyKey(document.getElementById("sp-key-run")),
LayoutHelpers.prettyKey(document.getElementById("sp-key-inspect")),
LayoutHelpers.prettyKey(document.getElementById("sp-key-display"))],
[DevtoolsHelpers.prettyKey(document.getElementById("sp-key-run")),
DevtoolsHelpers.prettyKey(document.getElementById("sp-key-inspect")),
DevtoolsHelpers.prettyKey(document.getElementById("sp-key-display"))],
3);
let args = window.arguments;

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

@ -0,0 +1,72 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {Cu} = require("chrome");
Cu.import("resource://gre/modules/Services.jsm");
let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
return Services.strings.createBundle(
"chrome://global-platform/locale/platformKeys.properties");
});
/**
* Prettifies the modifier keys for an element.
*
* @param Node aElemKey
* The key element to get the modifiers from.
* @param boolean aAllowCloverleaf
* Pass true to use the cloverleaf symbol instead of a descriptive string.
* @return string
* A prettified and properly separated modifier keys string.
*/
exports.prettyKey = function Helpers_prettyKey(aElemKey, aAllowCloverleaf) {
let elemString = "";
let elemMod = aElemKey.getAttribute("modifiers");
if (elemMod.match("accel")) {
if (Services.appinfo.OS == "Darwin") {
// XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
// Orion adds variable height lines.
if (!aAllowCloverleaf) {
elemString += "Cmd-";
} else {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
} else {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("access")) {
if (Services.appinfo.OS == "Darwin") {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
} else {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("shift")) {
elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("alt")) {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("ctrl") || elemMod.match("control")) {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("meta")) {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
return elemString +
(aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
aElemKey.getAttribute("key")).toUpperCase();
}

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

@ -25,52 +25,54 @@ function test() {
}
function runTest(win, some) {
let lh = new LayoutHelpers(win);
some.style.top = win.innerHeight + 'px';
some.style.left = win.innerWidth + 'px';
// The tests start with a black 2x2 pixels square below bottom right.
// Do not resize the window during the tests.
win.scroll(win.innerWidth / 2, win.innerHeight + 2); // Above the viewport.
LayoutHelpers.scrollIntoViewIfNeeded(some);
lh.scrollIntoViewIfNeeded(some);
is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
'Element completely hidden above should appear centered.');
win.scroll(win.innerWidth / 2, win.innerHeight + 1); // On the top edge.
LayoutHelpers.scrollIntoViewIfNeeded(some);
lh.scrollIntoViewIfNeeded(some);
is(win.scrollY, win.innerHeight,
'Element partially visible above should appear above.');
win.scroll(win.innerWidth / 2, 0); // Just below the viewport.
LayoutHelpers.scrollIntoViewIfNeeded(some);
lh.scrollIntoViewIfNeeded(some);
is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
'Element completely hidden below should appear centered.');
win.scroll(win.innerWidth / 2, 1); // On the bottom edge.
LayoutHelpers.scrollIntoViewIfNeeded(some);
lh.scrollIntoViewIfNeeded(some);
is(win.scrollY, 2,
'Element partially visible below should appear below.');
win.scroll(win.innerWidth / 2, win.innerHeight + 2); // Above the viewport.
LayoutHelpers.scrollIntoViewIfNeeded(some, false);
lh.scrollIntoViewIfNeeded(some, false);
is(win.scrollY, win.innerHeight,
'Element completely hidden above should appear above ' +
'if parameter is false.');
win.scroll(win.innerWidth / 2, win.innerHeight + 1); // On the top edge.
LayoutHelpers.scrollIntoViewIfNeeded(some, false);
lh.scrollIntoViewIfNeeded(some, false);
is(win.scrollY, win.innerHeight,
'Element partially visible above should appear above ' +
'if parameter is false.');
win.scroll(win.innerWidth / 2, 0); // Below the viewport.
LayoutHelpers.scrollIntoViewIfNeeded(some, false);
lh.scrollIntoViewIfNeeded(some, false);
is(win.scrollY, 2,
'Element completely hidden below should appear below ' +
'if parameter is false.');
win.scroll(win.innerWidth / 2, 1); // On the bottom edge.
LayoutHelpers.scrollIntoViewIfNeeded(some, false);
lh.scrollIntoViewIfNeeded(some, false);
is(win.scrollY, 2,
'Element partially visible below should appear below ' +
'if parameter is false.');
@ -86,7 +88,7 @@ function runTest(win, some) {
fwin.addEventListener('load', function frameLoad() {
let some = fwin.document.getElementById('some');
LayoutHelpers.scrollIntoViewIfNeeded(some);
lh.scrollIntoViewIfNeeded(some);
is(win.scrollX, Math.floor(win.innerWidth / 2) + 20,
'Scrolling from an iframe should center the iframe vertically.');
is(win.scrollY, Math.floor(win.innerHeight / 2) + 20,

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

@ -55,10 +55,11 @@ function test() {
iframe.contentWindow.innerHeight,
"The content window height wasn't calculated correctly.");
let nodeCoordinates = LayoutHelpers.getRect(
let lh = new LayoutHelpers(gBrowser.contentWindow);
let nodeCoordinates = lh.getRect(
iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
let frameOffset = LayoutHelpers.getIframeContentOffset(iframe);
let frameOffset = lh.getIframeContentOffset(iframe);
let frameRect = iframe.getBoundingClientRect();
is(nodeCoordinates.top, frameRect.top + frameOffset[0] + 98,

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

@ -104,10 +104,11 @@ function test() {
iframe.contentWindow.innerHeight,
"The content window height wasn't calculated correctly.");
let nodeCoordinates = LayoutHelpers.getRect(
let lh = new LayoutHelpers(gBrowser.contentWindow);
let nodeCoordinates = lh.getRect(
iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
let frameOffset = LayoutHelpers.getIframeContentOffset(iframe);
let frameOffset = lh.getIframeContentOffset(iframe);
let frameRect = iframe.getBoundingClientRect();
is(nodeCoordinates.top, frameRect.top + frameOffset[0],

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

@ -405,8 +405,9 @@ TiltUtils.DOM = {
*/
getNodePosition: function TUD_getNodePosition(aContentWindow, aNode,
aParentPosition) {
let lh = new LayoutHelpers(aContentWindow);
// get the x, y, width and height coordinates of the node
let coord = LayoutHelpers.getRect(aNode, aContentWindow);
let coord = lh.getRect(aNode, aContentWindow);
if (!coord) {
return null;
}

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

@ -16,6 +16,12 @@ let Cr = Components.results;
var APZCObserver = {
_debugEvents: false,
_enabled: false,
get enabled() {
return this._enabled;
},
init: function() {
this._enabled = Services.prefs.getBoolPref(kAsyncPanZoomEnabled);
if (!this._enabled) {

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

@ -108,6 +108,7 @@ let ScriptContexts = {};
["NavButtonSlider", "chrome://browser/content/NavButtonSlider.js"],
["ContextUI", "chrome://browser/content/ContextUI.js"],
["FlyoutPanelsUI", "chrome://browser/content/flyoutpanels/FlyoutPanelsUI.js"],
["APZCObserver", "chrome://browser/content/apzc.js"],
].forEach(function (aScript) {
let [name, script] = aScript;
XPCOMUtils.defineLazyGetter(window, name, function() {

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

@ -47,7 +47,6 @@
<script type="application/javascript" src="chrome://browser/content/Util.js"/>
<script type="application/javascript" src="chrome://browser/content/input.js"/>
<script type="application/javascript" src="chrome://browser/content/appbar.js"/>
<script type="application/javascript" src="chrome://browser/content/apzc.js"/>
<broadcasterset id="broadcasterset">
<broadcaster id="bcast_contentShowing" disabled="false"/>
<broadcaster id="bcast_urlbarState" mode="editing"/>

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

@ -311,8 +311,15 @@ var TouchModule = {
if (this._isCancellable) {
// only the first touchmove is cancellable.
this._isCancellable = false;
if (aEvent.defaultPrevented)
if (aEvent.defaultPrevented) {
this._isCancelled = true;
}
// Help out chrome ui elements that want input.js vs. apz scrolling: call
// preventDefault when apz is enabled on anything that isn't in the
// browser.
if (APZCObserver.enabled && aEvent.target.ownerDocument == document) {
aEvent.preventDefault();
}
}
if (this._isCancelled)
@ -352,8 +359,6 @@ var TouchModule = {
// Let everyone know when mousemove begins a pan
if (!oldIsPan && dragData.isPan()) {
//this._longClickTimeout.clear();
let event = document.createEvent("Events");
event.initEvent("PanBegin", true, false);
this._targetScrollbox.dispatchEvent(event);

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

@ -18,8 +18,6 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.GridLayoutAnimationController;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.GridView;
@ -50,6 +48,12 @@ public class TopBookmarksView extends GridView {
// Vertical spacing in between the rows.
private final int mVerticalSpacing;
// Measured width of this view.
private int mMeasuredWidth;
// Measured height of this view.
private int mMeasuredHeight;
// On URL open listener.
private OnUrlOpenListener mUrlOpenListener;
@ -114,9 +118,6 @@ public class TopBookmarksView extends GridView {
return showContextMenuForChild(TopBookmarksView.this);
}
});
final GridLayoutAnimationController controller = new GridLayoutAnimationController(AnimationUtils.loadAnimation(getContext(), R.anim.grow_fade_in_center));
setLayoutAnimation(controller);
}
@Override
@ -147,52 +148,45 @@ public class TopBookmarksView extends GridView {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int measuredWidth = getMeasuredWidth();
if (measuredWidth == mMeasuredWidth) {
// Return the cached values as the width is the same.
setMeasuredDimension(mMeasuredWidth, mMeasuredHeight);
return;
}
final int childWidth = getColumnWidth();
int childHeight = 0;
// Set the column width as the thumbnail width.
ThumbnailHelper.getInstance().setThumbnailWidth(childWidth);
// If there's an adapter, use it to calculate the height of this view.
final TopBookmarksAdapter adapter = (TopBookmarksAdapter) getAdapter();
final int count;
// There shouldn't be any inherent size (due to padding) if there are no child views.
if (adapter == null || (count = adapter.getCount()) == 0) {
setMeasuredDimension(0, 0);
return;
}
// Get the first child from the adapter.
final View child = adapter.getView(0, null, this);
if (child != null) {
// Set a default LayoutParams on the child, if it doesn't have one on its own.
AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
if (params == null) {
params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
AbsListView.LayoutParams.WRAP_CONTENT);
child.setLayoutParams(params);
}
final View child = new TopBookmarkItemView(getContext());
// Measure the exact width of the child, and the height based on the width.
// Note: the child (and BookmarkThumbnailView) takes care of calculating its height.
int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(childWidthSpec, childHeightSpec);
childHeight = child.getMeasuredHeight();
// Set a default LayoutParams on the child, if it doesn't have one on its own.
AbsListView.LayoutParams params = (AbsListView.LayoutParams) child.getLayoutParams();
if (params == null) {
params = new AbsListView.LayoutParams(AbsListView.LayoutParams.WRAP_CONTENT,
AbsListView.LayoutParams.WRAP_CONTENT);
child.setLayoutParams(params);
}
// Find the minimum of bookmarks we need to show, and the one given by the cursor.
final int total = Math.min(count > 0 ? count : Integer.MAX_VALUE, mMaxBookmarks);
// Measure the exact width of the child, and the height based on the width.
// Note: the child (and BookmarkThumbnailView) takes care of calculating its height.
int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY);
int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(childWidthSpec, childHeightSpec);
final int childHeight = child.getMeasuredHeight();
// Number of rows required to show these bookmarks.
final int rows = (int) Math.ceil((double) total / mNumColumns);
final int rows = (int) Math.ceil((double) mMaxBookmarks / mNumColumns);
final int childrenHeight = childHeight * rows;
final int totalVerticalSpacing = rows > 0 ? (rows - 1) * mVerticalSpacing : 0;
// Total height of this view.
final int measuredHeight = childrenHeight + getPaddingTop() + getPaddingBottom() + totalVerticalSpacing;
setMeasuredDimension(measuredWidth, measuredHeight);
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
}
@Override

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

@ -10,6 +10,7 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.util.HardwareUtils;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -77,7 +78,16 @@ public class ArrowPopup extends PopupWindow {
// If there's no anchor or the anchor is out of the window bounds,
// just show the popup at the top of the gecko app view.
if (mAnchor == null || anchorLocation[1] < 0) {
showAtLocation(mActivity.getView(), Gravity.TOP, 0, 0);
final View view = mActivity.getView();
// Bug in android code causes the window layout parameters to be ignored
// when using showAtLocation() in Gingerbread phones.
if (Build.VERSION.SDK_INT < 11) {
setWidth(view.getWidth());
setHeight(view.getHeight());
}
showAtLocation(view, Gravity.TOP, 0, 0);
return;
}

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

@ -147,6 +147,13 @@ pref("browser.helperApps.alwaysAsk.force", false);
pref("browser.helperApps.neverAsk.saveToDisk", "");
pref("browser.helperApps.neverAsk.openFile", "");
#ifdef XP_WIN
// By default, security zone information is stored in the Alternate Data Stream
// of downloaded executable files on Windows. This preference allows disabling
// this feature, and thus the associated system-level execution prompts.
pref("browser.download.saveZoneInformation", true);
#endif
// xxxbsmedberg: where should prefs for the toolkit go?
pref("browser.chrome.toolbar_tips", true);
// 0 = Pictures Only, 1 = Text Only, 2 = Pictures and Text

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

@ -67,6 +67,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gDownloadHistory",
"@mozilla.org/browser/download-history;1",
Ci.nsIDownloadHistory);
XPCOMUtils.defineLazyServiceGetter(this, "gExternalHelperAppService",
"@mozilla.org/uriloader/external-helper-app-service;1",
Ci.nsIExternalHelperAppService);
@ -1205,6 +1208,30 @@ DownloadSaver.prototype = {
return Promise.resolve();
},
/**
* This can be called by the saver implementation when the download is already
* started, to add it to the browsing history. This method has no effect if
* the download is private.
*/
addToHistory: function ()
{
if (this.download.source.isPrivate) {
return;
}
let sourceUri = NetUtil.newURI(this.download.source.url);
let referrer = this.download.source.referrer;
let referrerUri = referrer ? NetUtil.newURI(referrer) : null;
let targetUri = NetUtil.newURI(new FileUtils.File(
this.download.target.path));
// The start time is always available when we reach this point.
let startPRTime = this.download.startTime.getTime() * 1000;
gDownloadHistory.addDownload(sourceUri, referrerUri, startPRTime,
targetUri);
},
/**
* Returns a static representation of the current object state.
*
@ -1266,6 +1293,11 @@ DownloadCopySaver.prototype = {
*/
_canceled: false,
/**
* True if the associated download has already been added to browsing history.
*/
alreadyAddedToHistory: false,
/**
* String corresponding to the entityID property of the nsIResumableChannel
* used to execute the download, or null if the channel was not resumable or
@ -1288,6 +1320,16 @@ DownloadCopySaver.prototype = {
let keepPartialData = download.tryToKeepPartialData;
return Task.spawn(function task_DCS_execute() {
// Add the download to history the first time it is started in this
// session. If the download is restarted in a different session, a new
// history visit will be added. We do this just to avoid the complexity
// of serializing this state between sessions, since adding a new visit
// does not have any noticeable side effect.
if (!this.alreadyAddedToHistory) {
this.addToHistory();
this.alreadyAddedToHistory = true;
}
// To reduce the chance that other downloads reuse the same final target
// file name, we should create a placeholder as soon as possible, before
// starting the network request. The placeholder is also required in case
@ -1633,8 +1675,14 @@ DownloadLegacySaver.prototype = {
*
* @param aRequest
* nsIRequest associated to the status update.
* @param aAlreadyAddedToHistory
* Indicates that the nsIExternalHelperAppService component already
* added the download to the browsing history, unless it was started
* from a private browsing window. When this parameter is false, the
* download is added to the browsing history here. Private downloads
* are never added to history even if this parameter is false.
*/
onTransferStarted: function (aRequest)
onTransferStarted: function (aRequest, aAlreadyAddedToHistory)
{
// Store the entity ID to use for resuming if required.
if (this.download.tryToKeepPartialData &&
@ -1645,6 +1693,15 @@ DownloadLegacySaver.prototype = {
} catch (ex if ex instanceof Components.Exception &&
ex.result == Cr.NS_ERROR_NOT_RESUMABLE) { }
}
// For legacy downloads, we must update the referrer at this time.
if (aRequest instanceof Ci.nsIHttpChannel && aRequest.referrer) {
this.download.source.referrer = aRequest.referrer.spec;
}
if (!aAlreadyAddedToHistory) {
this.addToHistory();
}
},
/**
@ -1702,6 +1759,7 @@ DownloadLegacySaver.prototype = {
this.copySaver = new DownloadCopySaver();
this.copySaver.download = this.download;
this.copySaver.entityID = this.entityID;
this.copySaver.alreadyAddedToHistory = true;
}
return this.copySaver.execute.apply(this.copySaver, arguments);
}

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

@ -66,6 +66,14 @@ XPCOMUtils.defineLazyGetter(this, "gParentalControlsService", function() {
return null;
});
/**
* ArrayBufferView representing the bytes to be written to the "Zone.Identifier"
* Alternate Data Stream to mark a file as coming from the Internet zone.
*/
XPCOMUtils.defineLazyGetter(this, "gInternetZoneIdentifier", function() {
return new TextEncoder().encode("[ZoneTransfer]\r\nZoneId=3\r\n");
});
const Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer",
"initWithCallback");
@ -389,15 +397,46 @@ this.DownloadIntegration = {
* @rejects JavaScript exception if any of the operations failed.
*/
downloadDone: function(aDownload) {
try {
return Task.spawn(function () {
#ifdef XP_WIN
// On Windows, we mark any executable file saved to the NTFS file system
// as coming from the Internet security zone. We do this by writing to
// the "Zone.Identifier" Alternate Data Stream directly, because the Save
// method of the IAttachmentExecute interface would trigger operations
// that may cause the application to hang, or other performance issues.
// The stream created in this way is forward-compatible with all the
// current and future versions of Windows.
if (Services.prefs.getBoolPref("browser.download.saveZoneInformation")) {
let file = new FileUtils.File(aDownload.target.path);
if (file.isExecutable()) {
try {
let streamPath = aDownload.target.path + ":Zone.Identifier";
let stream = yield OS.File.open(streamPath, { create: true });
try {
yield stream.write(gInternetZoneIdentifier);
} finally {
yield stream.close();
}
} catch (ex) {
// If writing to the stream fails, we ignore the error and continue.
// The Windows API error 123 (ERROR_INVALID_NAME) is expected to
// occur when working on a file system that does not support
// Alternate Data Streams, like FAT32, thus we don't report this
// specific error.
if (!(ex instanceof OS.File.Error) || ex.winLastError != 123) {
Cu.reportError(ex);
}
}
}
}
#endif
gDownloadPlatform.downloadDone(NetUtil.newURI(aDownload.source.url),
new FileUtils.File(aDownload.target.path),
aDownload.contentType, aDownload.source.isPrivate);
aDownload.contentType,
aDownload.source.isPrivate);
this.downloadDoneCalled = true;
return Promise.resolve();
} catch(ex) {
return Promise.reject(ex);
}
}.bind(this));
},
/**
@ -432,12 +471,14 @@ this.DownloadIntegration = {
let deferred = Task.spawn(function DI_launchDownload_task() {
let file = new FileUtils.File(aDownload.target.path);
// Ask for confirmation if the file is executable. We do this here,
// instead of letting the caller handle the prompt separately in the user
// interface layer, for two reasons. The first is because of its security
// nature, so that add-ons cannot forget to do this check. The second is
// that the system-level security prompt, if enabled, would be displayed
// at launch time in any case.
#ifndef XP_WIN
// Ask for confirmation if the file is executable, except on Windows where
// the operating system will show the prompt based on the security zone.
// We do this here, instead of letting the caller handle the prompt
// separately in the user interface layer, for two reasons. The first is
// because of its security nature, so that add-ons cannot forget to do
// this check. The second is that the system-level security prompt would
// be displayed at launch time in any case.
if (file.isExecutable() && !this.dontOpenFileAndFolder) {
// We don't anchor the prompt to a specific window intentionally, not
// only because this is the same behavior as the system-level prompt,
@ -449,6 +490,7 @@ this.DownloadIntegration = {
return;
}
}
#endif
// In case of a double extension, like ".tar.gz", we only
// consider the last one, because the MIME service cannot

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

@ -91,16 +91,21 @@ DownloadLegacyTransfer.prototype = {
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
// The main request has just started. Wait for the associated Download
// object to be available before notifying.
this._deferDownload.promise.then(function (aDownload) {
aDownload.saver.onTransferStarted(aRequest);
this._deferDownload.promise.then(download => {
download.saver.onTransferStarted(
aRequest,
this._cancelable instanceof Ci.nsIHelperAppLauncher);
}).then(null, Cu.reportError);
} else if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)) {
// The last file has been received, or the download failed. Wait for the
// associated Download object to be available before notifying.
this._deferDownload.promise.then(function DLT_OSC_onDownload(aDownload) {
aDownload.saver.onTransferFinished(aRequest, aStatus);
this._deferDownload.promise.then(download => {
download.saver.onTransferFinished(aRequest, aStatus);
}).then(null, Cu.reportError);
// Release the reference to the component executing the download.
this._cancelable = null;
}
},
@ -164,6 +169,8 @@ DownloadLegacyTransfer.prototype = {
init: function DLT_init(aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime,
aTempFile, aCancelable, aIsPrivate)
{
this._cancelable = aCancelable;
let launchWhenSucceeded = false, contentType = null, launcherPath = null;
if (aMIMEInfo instanceof Ci.nsIMIMEInfo) {
@ -233,6 +240,12 @@ DownloadLegacyTransfer.prototype = {
*/
_deferDownload: null,
/**
* Reference to the component that is executing the download. This component
* allows cancellation through its nsICancelable interface.
*/
_cancelable: null,
/**
* Indicates that the component that executes the download has notified a
* failure condition. In this case, we should never use the component methods

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

@ -1676,3 +1676,60 @@ add_task(function test_platform_integration()
yield promiseVerifyContents(download.target.path, TEST_DATA_SHORT);
}
});
/**
* Checks that downloads are added to browsing history when they start.
*/
add_task(function test_history()
{
mustInterruptResponses();
// We will wait for the visit to be notified during the download.
yield promiseClearHistory();
let promiseVisit = promiseWaitForVisit(httpUrl("interruptible.txt"));
// Start a download that is not allowed to finish yet.
let download = yield promiseStartDownload(httpUrl("interruptible.txt"));
// The history notifications should be received before the download completes.
let [time, transitionType] = yield promiseVisit;
do_check_eq(time, download.startTime.getTime() * 1000);
do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
// Restart and complete the download after clearing history.
yield promiseClearHistory();
download.cancel();
continueResponses();
yield download.start();
// The restart should not have added a new history visit.
do_check_false(yield promiseIsURIVisited(httpUrl("interruptible.txt")));
});
/**
* Checks that downloads started by nsIHelperAppService are added to the
* browsing history when they start.
*/
add_task(function test_history_tryToKeepPartialData()
{
// We will wait for the visit to be notified during the download.
yield promiseClearHistory();
let promiseVisit =
promiseWaitForVisit(httpUrl("interruptible_resumable.txt"));
// Start a download that is not allowed to finish yet.
let beforeStartTimeMs = Date.now();
let download = yield promiseStartDownload_tryToKeepPartialData();
// The history notifications should be received before the download completes.
let [time, transitionType] = yield promiseVisit;
do_check_eq(transitionType, Ci.nsINavHistoryService.TRANSITION_DOWNLOAD);
// The time set by nsIHelperAppService may be different than the start time in
// the download object, thus we only check that it is a meaningful time.
do_check_true(time >= beforeStartTimeMs * 1000);
// Complete the download before finishing the test.
continueResponses();
yield promiseDownloadStopped(download);
});

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

@ -169,6 +169,101 @@ function promiseTimeout(aTime)
return deferred.promise;
}
/**
* Allows waiting for an observer notification once.
*
* @param aTopic
* Notification topic to observe.
*
* @return {Promise}
* @resolves The array [aSubject, aData] from the observed notification.
* @rejects Never.
*/
function promiseTopicObserved(aTopic)
{
let deferred = Promise.defer();
Services.obs.addObserver(
function PTO_observe(aSubject, aTopic, aData) {
Services.obs.removeObserver(PTO_observe, aTopic);
deferred.resolve([aSubject, aData]);
}, aTopic, false);
return deferred.promise;
}
/**
* Clears history asynchronously.
*
* @return {Promise}
* @resolves When history has been cleared.
* @rejects Never.
*/
function promiseClearHistory()
{
let promise = promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
do_execute_soon(function() PlacesUtils.bhistory.removeAllPages());
return promise;
}
/**
* Waits for a new history visit to be notified for the specified URI.
*
* @param aUrl
* String containing the URI that will be visited.
*
* @return {Promise}
* @resolves Array [aTime, aTransitionType] from nsINavHistoryObserver.onVisit.
* @rejects Never.
*/
function promiseWaitForVisit(aUrl)
{
let deferred = Promise.defer();
let uri = NetUtil.newURI(aUrl);
PlacesUtils.history.addObserver({
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver]),
onBeginUpdateBatch: function () {},
onEndUpdateBatch: function () {},
onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID,
aTransitionType, aGUID, aHidden) {
if (aURI.equals(uri)) {
PlacesUtils.history.removeObserver(this);
deferred.resolve([aTime, aTransitionType]);
}
},
onTitleChanged: function () {},
onDeleteURI: function () {},
onClearHistory: function () {},
onPageChanged: function () {},
onDeleteVisits: function () {},
}, false);
return deferred.promise;
}
/**
* Check browsing history to see whether the given URI has been visited.
*
* @param aUrl
* String containing the URI that will be visited.
*
* @return {Promise}
* @resolves Boolean indicating whether the URI has been visited.
* @rejects JavaScript exception.
*/
function promiseIsURIVisited(aUrl) {
let deferred = Promise.defer();
PlacesUtils.asyncHistory.isURIVisited(NetUtil.newURI(aUrl),
function (aURI, aIsVisited) {
deferred.resolve(aIsVisited);
});
return deferred.promise;
}
/**
* Creates a new Download object, setting a temporary file as the target.
*
@ -441,40 +536,6 @@ function promiseVerifyContents(aPath, aExpectedContents)
});
}
/**
* Adds entry for download.
*
* @param aSourceUrl
* String containing the URI for the download source, or null to use
* httpUrl("source.txt").
*
* @return {Promise}
* @rejects JavaScript exception.
*/
function promiseAddDownloadToHistory(aSourceUrl) {
let deferred = Promise.defer();
PlacesUtils.asyncHistory.updatePlaces(
{
uri: NetUtil.newURI(aSourceUrl || httpUrl("source.txt")),
visits: [{
transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
visitDate: Date.now()
}]
},
{
handleError: function handleError(aResultCode, aPlaceInfo) {
let ex = new Components.Exception("Unexpected error in adding visits.",
aResultCode);
deferred.reject(ex);
},
handleResult: function () {},
handleCompletion: function handleCompletion() {
deferred.resolve();
}
});
return deferred.promise;
}
/**
* Starts a socket listener that closes each incoming connection.
*

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

@ -9,6 +9,62 @@
"use strict";
////////////////////////////////////////////////////////////////////////////////
//// Globals
/**
* Returns a PRTime in the past usable to add expirable visits.
*
* @note Expiration ignores any visit added in the last 7 days, but it's
* better be safe against DST issues, by going back one day more.
*/
function getExpirablePRTime()
{
let dateObj = new Date();
// Normalize to midnight
dateObj.setHours(0);
dateObj.setMinutes(0);
dateObj.setSeconds(0);
dateObj.setMilliseconds(0);
dateObj = new Date(dateObj.getTime() - 8 * 86400000);
return dateObj.getTime() * 1000;
}
/**
* Adds an expirable history visit for a download.
*
* @param aSourceUrl
* String containing the URI for the download source, or null to use
* httpUrl("source.txt").
*
* @return {Promise}
* @rejects JavaScript exception.
*/
function promiseExpirableDownloadVisit(aSourceUrl)
{
let deferred = Promise.defer();
PlacesUtils.asyncHistory.updatePlaces(
{
uri: NetUtil.newURI(aSourceUrl || httpUrl("source.txt")),
visits: [{
transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
visitDate: getExpirablePRTime(),
}]
},
{
handleError: function handleError(aResultCode, aPlaceInfo) {
let ex = new Components.Exception("Unexpected error in adding visits.",
aResultCode);
deferred.reject(ex);
},
handleResult: function () {},
handleCompletion: function handleCompletion() {
deferred.resolve();
}
});
return deferred.promise;
}
////////////////////////////////////////////////////////////////////////////////
//// Tests
@ -205,6 +261,8 @@ add_task(function test_notifications_this()
*/
add_task(function test_history_expiration()
{
mustInterruptResponses();
function cleanup() {
Services.prefs.clearUserPref("places.history.expiration.max_pages");
}
@ -213,15 +271,9 @@ add_task(function test_history_expiration()
// Set max pages to 0 to make the download expire.
Services.prefs.setIntPref("places.history.expiration.max_pages", 0);
// Add expirable visit for downloads.
yield promiseAddDownloadToHistory();
yield promiseAddDownloadToHistory(httpUrl("interruptible.txt"));
let list = yield promiseNewDownloadList();
let downloadOne = yield promiseNewDownload();
let downloadTwo = yield promiseNewDownload(httpUrl("interruptible.txt"));
list.add(downloadOne);
list.add(downloadTwo);
let deferred = Promise.defer();
let removeNotifications = 0;
@ -234,18 +286,27 @@ add_task(function test_history_expiration()
};
list.addView(downloadView);
// Start download one.
// Work with one finished download and one canceled download.
yield downloadOne.start();
// Start download two and then cancel it.
downloadTwo.start();
yield downloadTwo.cancel();
// We must replace the visits added while executing the downloads with visits
// that are older than 7 days, otherwise they will not be expired.
yield promiseClearHistory();
yield promiseExpirableDownloadVisit();
yield promiseExpirableDownloadVisit(httpUrl("interruptible.txt"));
// After clearing history, we can add the downloads to be removed to the list.
list.add(downloadOne);
list.add(downloadTwo);
// Force a history expiration.
let expire = Cc["@mozilla.org/places/expiration;1"]
.getService(Ci.nsIObserver);
expire.observe(null, "places-debug-start-expiration", -1);
// Wait for both downloads to be removed.
yield deferred.promise;
cleanup();
@ -256,10 +317,6 @@ add_task(function test_history_expiration()
*/
add_task(function test_history_clear()
{
// Add expirable visit for downloads.
yield promiseAddDownloadToHistory();
yield promiseAddDownloadToHistory();
let list = yield promiseNewDownloadList();
let downloadOne = yield promiseNewDownload();
let downloadTwo = yield promiseNewDownload();
@ -280,8 +337,9 @@ add_task(function test_history_clear()
yield downloadOne.start();
yield downloadTwo.start();
PlacesUtils.history.removeAllPages();
yield promiseClearHistory();
// Wait for the removal notifications that may still be pending.
yield deferred.promise;
});

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

@ -13,14 +13,15 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
return Services.strings.createBundle(
"chrome://global-platform/locale/platformKeys.properties");
});
this.EXPORTED_SYMBOLS = ["LayoutHelpers"];
this.LayoutHelpers = LayoutHelpers = {
this.LayoutHelpers = LayoutHelpers = function(aTopLevelWindow) {
this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
}
LayoutHelpers.prototype = {
/**
* Compute the position and the dimensions for the visible portion
@ -70,22 +71,27 @@ this.LayoutHelpers = LayoutHelpers = {
// Selection has been clipped to fit in its own window.
// Are we in the top-level window?
if (frameWin.parent === frameWin || !frameWin.frameElement) {
if (this.isTopLevelWindow(frameWin)) {
break;
}
let frameElement = this.getFrameElement(frameWin);
if (!frameElement) {
break;
}
// We are in an iframe.
// We take into account the parent iframe position and its
// offset (borders and padding).
let frameRect = frameWin.frameElement.getBoundingClientRect();
let frameRect = frameElement.getBoundingClientRect();
let [offsetTop, offsetLeft] =
this.getIframeContentOffset(frameWin.frameElement);
this.getIframeContentOffset(frameElement);
rect.top += frameRect.top + offsetTop;
rect.left += frameRect.left + offsetLeft;
frameWin = frameWin.parent;
frameWin = this.getParentWindow(frameWin);
}
return rect;
@ -115,22 +121,27 @@ this.LayoutHelpers = LayoutHelpers = {
while (true) {
// Are we in the top-level window?
if (frameWin.parent === frameWin || !frameWin.frameElement) {
if (this.isTopLevelWindow(frameWin)) {
break;
}
let frameElement = this.getFrameElement(frameWin);
if (!frameElement) {
break;
}
// We are in an iframe.
// We take into account the parent iframe position and its
// offset (borders and padding).
let frameRect = frameWin.frameElement.getBoundingClientRect();
let frameRect = frameElement.getBoundingClientRect();
let [offsetTop, offsetLeft] =
this.getIframeContentOffset(frameWin.frameElement);
this.getIframeContentOffset(frameElement);
rect.top += frameRect.top + offsetTop;
rect.left += frameRect.left + offsetLeft;
frameWin = frameWin.parent;
frameWin = this.getParentWindow(frameWin);
}
return rect;
@ -203,7 +214,7 @@ this.LayoutHelpers = LayoutHelpers = {
let rect = node.getBoundingClientRect();
// Gap between the iframe and its content window.
let [offsetTop, offsetLeft] = LayoutHelpers.getIframeContentOffset(node);
let [offsetTop, offsetLeft] = this.getIframeContentOffset(node);
aX -= rect.left + offsetLeft;
aY -= rect.top + offsetTop;
@ -296,9 +307,10 @@ this.LayoutHelpers = LayoutHelpers = {
}
}
if (win.parent !== win) {
if (!this.isTopLevelWindow(win)) {
// We are inside an iframe.
LH_scrollIntoViewIfNeeded(win.frameElement, centered);
let frameElement = this.getFrameElement(win);
this.scrollIntoViewIfNeeded(frameElement, centered);
}
},
@ -322,63 +334,60 @@ this.LayoutHelpers = LayoutHelpers = {
},
/**
* Prettifies the modifier keys for an element.
*
* @param Node aElemKey
* The key element to get the modifiers from.
* @param boolean aAllowCloverleaf
* Pass true to use the cloverleaf symbol instead of a descriptive string.
* @return string
* A prettified and properly separated modifier keys string.
* like win.parent === win, but goes through mozbrowsers and mozapps iframes.
*/
prettyKey: function LH_prettyKey(aElemKey, aAllowCloverleaf)
{
let elemString = "";
let elemMod = aElemKey.getAttribute("modifiers");
isTopLevelWindow: function LH_isTopLevelWindow(win) {
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (elemMod.match("accel")) {
if (Services.appinfo.OS == "Darwin") {
// XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
// Orion adds variable height lines.
if (!aAllowCloverleaf) {
elemString += "Cmd-";
} else {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
return docShell === this._topDocShell;
},
/**
* like win.parent, but goes through mozbrowsers and mozapps iframes.
*/
getParentWindow: function LH_getParentWindow(win) {
if (this.isTopLevelWindow(win)) {
return null;
}
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (docShell.isBrowserOrApp) {
let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
return parentDocShell.contentViewer.DOMDocument.defaultView;
} else {
return win.parent;
}
},
/**
* like win.frameElement, but goes through mozbrowsers and mozapps iframes.
*/
getFrameElement: function LH_getFrameElement(win) {
if (this.isTopLevelWindow(win)) {
return null;
}
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
if (docShell.isBrowserOrApp) {
let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
let parentDoc = parentDocShell.contentViewer.DOMDocument;
let allIframes = parentDoc.querySelectorAll("iframe");
for (let f of allIframes) {
if (f.contentWindow === win) {
return f;
}
} else {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
return null;
} else {
return win.frameElement;
}
if (elemMod.match("access")) {
if (Services.appinfo.OS == "Darwin") {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
} else {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
}
if (elemMod.match("shift")) {
elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("alt")) {
elemString += PlatformKeys.GetStringFromName("VK_ALT") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("ctrl") || elemMod.match("control")) {
elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
if (elemMod.match("meta")) {
elemString += PlatformKeys.GetStringFromName("VK_META") +
PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
}
return elemString +
(aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
aElemKey.getAttribute("key")).toUpperCase();
}
},
};

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

@ -768,11 +768,14 @@ var WalkerActor = protocol.ActorClass({
*/
initialize: function(conn, tabActor, options) {
protocol.Actor.prototype.initialize.call(this, conn);
this.rootDoc = tabActor.window.document;
this.rootWin = tabActor.window;
this.rootDoc = this.rootWin.document;
this._refMap = new Map();
this._pendingMutations = [];
this._activePseudoClassLocks = new Set();
this.layoutHelpers = new LayoutHelpers(this.rootWin);
// Nodes which have been removed from the client's known
// ownership tree are considered "orphaned", and stored in
// this set.
@ -929,7 +932,7 @@ var WalkerActor = protocol.ActorClass({
return;
}
LayoutHelpers.scrollIntoViewIfNeeded(node.rawNode);
this.layoutHelpers.scrollIntoViewIfNeeded(node.rawNode);
DOMUtils.addPseudoClassLock(node.rawNode, HIGHLIGHTED_PSEUDO_CLASS);
this._highlightTimeout = setTimeout(this._unhighlight.bind(this), HIGHLIGHTED_TIMEOUT);
@ -993,7 +996,7 @@ var WalkerActor = protocol.ActorClass({
* document as the node.
*/
parents: method(function(node, options={}) {
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let parents = [];
let cur;
while((cur = walker.parentNode())) {
@ -1014,7 +1017,7 @@ var WalkerActor = protocol.ActorClass({
}),
parentNode: function(node) {
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let parent = walker.parentNode();
if (parent) {
return this._ref(parent);
@ -1075,7 +1078,7 @@ var WalkerActor = protocol.ActorClass({
this._retainedOrphans.delete(node);
}
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let child = walker.firstChild();
while (child) {
@ -1102,7 +1105,7 @@ var WalkerActor = protocol.ActorClass({
if (!node) {
return newParents;
}
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let cur;
while ((cur = walker.parentNode())) {
let parent = this._refMap.get(cur);
@ -1152,8 +1155,8 @@ var WalkerActor = protocol.ActorClass({
// We're going to create a few document walkers with the same filter,
// make it easier.
let filteredWalker = function(node) {
return documentWalker(node, options.whatToShow);
let filteredWalker = (node) => {
return documentWalker(node, this.rootWin, options.whatToShow);
}
// Need to know the first and last child.
@ -1236,7 +1239,7 @@ var WalkerActor = protocol.ActorClass({
* nodes: Child nodes returned by the request.
*/
siblings: method(function(node, options={}) {
let parentNode = documentWalker(node.rawNode).parentNode();
let parentNode = documentWalker(node.rawNode, this.rootWin).parentNode();
if (!parentNode) {
return {
hasFirst: true,
@ -1262,7 +1265,7 @@ var WalkerActor = protocol.ActorClass({
* https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
*/
nextSibling: method(function(node, options={}) {
let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
let walker = documentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
let sibling = walker.nextSibling();
return sibling ? this._ref(sibling) : null;
}, traversalMethod),
@ -1277,7 +1280,7 @@ var WalkerActor = protocol.ActorClass({
* https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
*/
previousSibling: method(function(node, options={}) {
let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
let walker = documentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
let sibling = walker.previousSibling();
return sibling ? this._ref(sibling) : null;
}, traversalMethod),
@ -1382,7 +1385,7 @@ var WalkerActor = protocol.ActorClass({
return;
}
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let cur;
while ((cur = walker.parentNode())) {
let curNode = this._ref(cur);
@ -1463,7 +1466,7 @@ var WalkerActor = protocol.ActorClass({
return;
}
let walker = documentWalker(node.rawNode);
let walker = documentWalker(node.rawNode, this.rootWin);
let cur;
while ((cur = walker.parentNode())) {
let curNode = this._ref(cur);
@ -1731,7 +1734,7 @@ var WalkerActor = protocol.ActorClass({
},
onFrameLoad: function(window) {
let frame = window.frameElement;
let frame = this.layoutHelpers.getFrameElement(window);
if (!frame && !this.rootDoc) {
this.rootDoc = window.document;
this.rootNode = this.document();
@ -1766,7 +1769,7 @@ var WalkerActor = protocol.ActorClass({
if (win === window) {
return true;
}
win = win.frameElement;
win = this.layoutHelpers.getFrameElement(win);
}
return false;
},
@ -1810,7 +1813,7 @@ var WalkerActor = protocol.ActorClass({
target: documentActor.actorID
});
let walker = documentWalker(doc);
let walker = documentWalker(doc, this.rootWin);
let parentNode = walker.parentNode();
if (parentNode) {
// Send a childList mutation on the frame so that clients know
@ -2267,8 +2270,8 @@ var InspectorFront = exports.InspectorFront = protocol.FrontClass(InspectorActor
})
});
function documentWalker(node, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
return new DocumentWalker(node, whatToShow, whitespaceTextFilter, false);
function documentWalker(node, rootWin, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
return new DocumentWalker(node, rootWin, whatToShow, whitespaceTextFilter, false);
}
// Exported for test purposes.
@ -2284,9 +2287,10 @@ function nodeDocument(node) {
*
* See TreeWalker documentation for explanations of the methods.
*/
function DocumentWalker(aNode, aShow, aFilter, aExpandEntityReferences)
function DocumentWalker(aNode, aRootWin, aShow, aFilter, aExpandEntityReferences)
{
let doc = nodeDocument(aNode);
this.layoutHelpers = new LayoutHelpers(aRootWin);
this.walker = doc.createTreeWalker(doc,
aShow, aFilter, aExpandEntityReferences);
this.walker.currentNode = aNode;
@ -2325,9 +2329,11 @@ DocumentWalker.prototype = {
if (!parentNode) {
if (currentNode && currentNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE
&& currentNode.defaultView) {
let embeddingFrame = currentNode.defaultView.frameElement;
if (embeddingFrame) {
return this._reparentWalker(embeddingFrame);
let window = currentNode.defaultView;
let frame = this.layoutHelpers.getFrameElement(window);
if (frame) {
return this._reparentWalker(frame);
}
}
return null;

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

@ -1396,19 +1396,24 @@ ThreadActor.prototype = {
_findClosestOffsetMappings: function TA__findClosestOffsetMappings(aTargetLocation,
aScript,
aScriptsAndOffsetMappings) {
let offsetMappings = aScript.getAllColumnOffsets()
.filter(({ lineNumber }) => lineNumber === aTargetLocation.line);
// If we are given a column, we will try and break only at that location,
// otherwise we will break anytime we get on that line.
if (aTargetLocation.column == null) {
let offsetMappings = aScript.getLineOffsets(aTargetLocation.line)
.map(o => ({
line: aTargetLocation.line,
offset: o
}));
if (offsetMappings.length) {
aScriptsAndOffsetMappings.set(aScript, offsetMappings);
}
return;
}
let offsetMappings = aScript.getAllColumnOffsets()
.filter(({ lineNumber }) => lineNumber === aTargetLocation.line);
// Attempt to find the current closest offset distance from the target
// location by grabbing any offset mapping in the map by doing one iteration
// and then breaking (they all have the same distance from the target

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

@ -101,7 +101,7 @@ function serverOwnershipSubtree(walker, node) {
}
let children = [];
let docwalker = _documentWalker(node);
let docwalker = _documentWalker(node, window);
let child = docwalker.firstChild();
while (child) {
let item = serverOwnershipSubtree(walker, child);

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

@ -65,7 +65,7 @@ addTest(function testRearrange() {
nextNode = response.nodes[13];
return gWalker.insertBefore(nodeA, longlist, nextNode);
}).then(response => {
let sibling = inspector._documentWalker(gInspectee.querySelector("#a")).nextSibling();
let sibling = inspector._documentWalker(gInspectee.querySelector("#a"), window).nextSibling();
is(sibling, nextNode.rawNode(), "Node should match the expected next node.");
return gWalker.children(longlist);
}).then(response => {

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

@ -0,0 +1,73 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check that we only break on offsets that are entry points for the line we are
* breaking on. Bug 907278.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test()
{
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-breakpoints");
gDebuggee.console = { log: x => void x };
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function () {
attachTestTabAndResume(gClient,
"test-breakpoints",
function (aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
setUpCode();
});
});
do_test_pending();
}
const URL = "test.js";
function setUpCode() {
gClient.addOneTimeListener("newSource", setBreakpoint);
Cu.evalInSandbox(
"" + function test() {
console.log("foo bar");
debugger;
},
gDebuggee,
"1.8",
URL
);
}
function setBreakpoint() {
gThreadClient.setBreakpoint({
url: URL,
line: 1
}, ({ error }) => {
do_check_true(!error);
gThreadClient.resume(runCode);
});
}
function runCode() {
gClient.addOneTimeListener("paused", testBPHit);
gDebuggee.test();
}
function testBPHit(event, { why }) {
do_check_eq(why.type, "breakpoint");
gClient.addOneTimeListener("paused", testDbgStatement);
gThreadClient.resume();
}
function testDbgStatement(event, { why }) {
// Should continue to the debugger statement.
do_check_eq(why.type, "debuggerStatement");
// Not break on another offset from the same line (that isn't an entry point
// to the line)
do_check_neq(why.type, "breakpoint");
finishClient(gClient);
}

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

@ -96,6 +96,7 @@ reason = bug 820380
[test_breakpoint-15.js]
[test_breakpoint-16.js]
[test_breakpoint-17.js]
[test_breakpoint-18.js]
[test_listsources-01.js]
[test_listsources-02.js]
[test_listsources-03.js]

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

@ -98,6 +98,38 @@ namespace {
pressure);
}
/**
* Test if a touchpoint position has moved. See Touch.Equals for
* criteria.
*
* @param aTouch previous touch point
* @param aPoint new winrt touch point
* @return true if the point has moved
*/
bool
HasPointMoved(Touch* aTouch, UI::Input::IPointerPoint* aPoint) {
WRL::ComPtr<UI::Input::IPointerPointProperties> props;
Foundation::Point position;
Foundation::Rect contactRect;
float pressure;
aPoint->get_Properties(props.GetAddressOf());
aPoint->get_Position(&position);
props->get_ContactRect(&contactRect);
props->get_Pressure(&pressure);
nsIntPoint touchPoint = MetroUtils::LogToPhys(position);
nsIntPoint touchRadius;
touchRadius.x = MetroUtils::LogToPhys(contactRect.Width) / 2;
touchRadius.y = MetroUtils::LogToPhys(contactRect.Height) / 2;
// from Touch.Equals
return touchPoint != aTouch->mRefPoint ||
pressure != aTouch->Force() ||
/* mRotationAngle == aTouch->RotationAngle() || */
touchRadius.x != aTouch->RadiusX() ||
touchRadius.y != aTouch->RadiusY();
}
/**
* Converts from the Devices::Input::PointerDeviceType enumeration
* to a nsIDOMMouseEvent::MOZ_SOURCE_* value.
@ -468,9 +500,16 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
return S_OK;
}
// If the point hasn't moved, filter it out per the spec. Pres shell does
// this as well, but we need to know when our first touchmove is going to
// get delivered so we can check the result.
if (!HasPointMoved(touch, currentPoint.Get())) {
return S_OK;
}
// If we've accumulated a batch of pointer moves and we're now on a new batch
// at a new position send the previous batch. (perf opt)
if (touch->mChanged) {
if (!mIsFirstTouchMove && touch->mChanged) {
nsTouchEvent* touchEvent =
new nsTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
InitTouchEventTouchList(touchEvent);
@ -1061,7 +1100,8 @@ MetroInput::DeliverNextQueuedTouchEvent()
MOZ_ASSERT(event);
nsEventStatus status;
mWidget->DispatchEvent(event, status);
if (status != nsEventStatus_eConsumeNoDefault && MetroWidget::sAPZC) {
// Deliver to the apz if content has *not* cancelled touchstart or the first touchmove.
if (!mTouchStartDefaultPrevented && !mTouchMoveDefaultPrevented && MetroWidget::sAPZC) {
MultiTouchInput inputData(*event);
MetroWidget::sAPZC->ReceiveInputEvent(inputData);
}