Merge m-c to inbound

This commit is contained in:
Wes Kocher 2013-11-07 17:56:10 -08:00
commit aa27f88eb0
45 changed files with 1045 additions and 446 deletions

View File

@ -1,4 +1,4 @@
{
"revision": "ab0d6e1927a4fbce5d171555044557c1b7ba25c3",
"revision": "0af41c1c297f1b784fb103e6150910f334036f20",
"repo_path": "/integration/gaia-central"
}

View File

@ -1078,7 +1078,7 @@ pref("devtools.commands.dir", "");
// Enable the app manager
pref("devtools.appmanager.enabled", true);
pref("devtools.appmanager.firstrun", true);
pref("devtools.appmanager.lastTab", "help");
pref("devtools.appmanager.manifestEditor.enabled", false);
// Toolbox preferences

View File

@ -986,16 +986,15 @@ nsContextMenu.prototype = {
},
saveVideoFrameAsImage: function () {
urlSecurityCheck(this.mediaURL,
this._unremotePrincipal(this.browser.contentPrincipal),
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
let name = "";
try {
let uri = makeURI(this.mediaURL);
let url = uri.QueryInterface(Ci.nsIURL);
if (url.fileBaseName)
name = decodeURI(url.fileBaseName) + ".jpg";
} catch (e) { }
if (this.mediaURL) {
try {
let uri = makeURI(this.mediaURL);
let url = uri.QueryInterface(Ci.nsIURL);
if (url.fileBaseName)
name = decodeURI(url.fileBaseName) + ".jpg";
} catch (e) { }
}
if (!name)
name = "snapshot.jpg";
var video = this.target;

View File

@ -162,16 +162,13 @@ let UI = {
if (!this.connected) {
return;
}
let app = this.store.object.apps.all.filter(a => a.manifestURL == manifest)[0];
getTargetForApp(this.connection.client,
this.listTabsResponse.webappsActor,
manifest).then((target) => {
gDevTools.showToolbox(target,
null,
devtools.Toolbox.HostType.WINDOW).then(toolbox => {
this.connection.once(Connection.Events.DISCONNECTED, () => {
toolbox.destroy();
});
});
top.UI.openAndShowToolboxForTarget(target, app.name, app.iconURL);
}, console.error);
},

View File

@ -7,76 +7,176 @@ Cu.import("resource:///modules/devtools/gDevTools.jsm");
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
const {require} = devtools;
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const promise = require("sdk/core/promise");
const prefs = require('sdk/preferences/service');
let connection;
window.addEventListener("message", function(event) {
try {
let json = JSON.parse(event.data);
switch (json.name) {
case "connection":
let cid = +json.cid;
for (let c of ConnectionManager.connections) {
if (c.uid == cid) {
connection = c;
onNewConnection();
break;
}
}
break;
case "closeHelp":
selectTab("projects");
break;
default:
Cu.reportError("Unknown message: " + json.name);
let UI = {
_toolboxTabCursor: 0,
_handledTargets: new Map(),
connection: null,
init: function() {
this.onLoad = this.onLoad.bind(this);
this.onUnload = this.onUnload.bind(this);
this.onMessage = this.onMessage.bind(this);
this.onConnected = this.onConnected.bind(this);
this.onDisconnected = this.onDisconnected.bind(this);
window.addEventListener("load", this.onLoad);
window.addEventListener("unload", this.onUnload);
window.addEventListener("message", this.onMessage);
},
onLoad: function() {
window.removeEventListener("load", this.onLoad);
let defaultPanel = prefs.get("devtools.appmanager.lastTab");
let panelExists = !!document.querySelector("." + defaultPanel + "-panel");
this.selectTab(panelExists ? defaultPanel : "projects");
},
onUnload: function() {
window.removeEventListener("unload", this.onUnload);
window.removeEventListener("message", this.onMessage);
if (this.connection) {
this.connection.off(Connection.Status.CONNECTED, this.onConnected);
this.connection.off(Connection.Status.DISCONNECTED, this.onDisconnected);
}
} catch(e) { Cu.reportError(e); }
},
// Forward message
let panels = document.querySelectorAll(".panel");
for (let frame of panels) {
frame.contentWindow.postMessage(event.data, "*");
}
}, false);
onMessage: function(event) {
try {
let json = JSON.parse(event.data);
switch (json.name) {
case "connection":
let cid = +json.cid;
for (let c of ConnectionManager.connections) {
if (c.uid == cid) {
this.onNewConnection(c);
break;
}
}
break;
case "closeHelp":
this.selectTab("projects");
break;
case "toolbox-raise":
this.selectTab(json.uid);
break;
case "toolbox-close":
this.closeToolboxTab(json.uid);
break;
default:
Cu.reportError("Unknown message: " + json.name);
}
} catch(e) { Cu.reportError(e); }
window.addEventListener("unload", function onUnload() {
window.removeEventListener("unload", onUnload);
if (connection) {
connection.off(Connection.Status.CONNECTED, onConnected);
connection.off(Connection.Status.DISCONNECTED, onDisconnected);
}
});
// Forward message
let panels = document.querySelectorAll(".panel");
for (let frame of panels) {
frame.contentWindow.postMessage(event.data, "*");
}
},
function onNewConnection() {
connection.on(Connection.Status.CONNECTED, onConnected);
connection.on(Connection.Status.DISCONNECTED, onDisconnected);
}
selectTabFromButton: function(button) {
if (!button.hasAttribute("panel"))
return;
this.selectTab(button.getAttribute("panel"));
},
function onConnected() {
document.querySelector("#content").classList.add("connected");
}
selectTab: function(panel) {
let isToolboxTab = false;
for (let type of ["button", "panel"]) {
let oldSelection = document.querySelector("." + type + "[selected]");
let newSelection = document.querySelector("." + panel + "-" + type);
if (oldSelection) oldSelection.removeAttribute("selected");
if (newSelection) {
newSelection.scrollIntoView(false);
newSelection.setAttribute("selected", "true");
if (newSelection.classList.contains("toolbox")) {
isToolboxTab = true;
}
}
}
if (!isToolboxTab) {
prefs.set("devtools.appmanager.lastTab", panel);
}
},
function onDisconnected() {
document.querySelector("#content").classList.remove("connected");
}
onNewConnection: function(connection) {
this.connection = connection;
this.connection.on(Connection.Status.CONNECTED, this.onConnected);
this.connection.on(Connection.Status.DISCONNECTED, this.onDisconnected);
},
function selectTab(id) {
for (let type of ["button", "panel"]) {
let oldSelection = document.querySelector("." + type + "[selected]");
let newSelection = document.querySelector("." + id + "-" + type);
if (oldSelection) oldSelection.removeAttribute("selected");
if (newSelection) newSelection.setAttribute("selected", "true");
}
if (id != "help") {
// Might be the first time the user is accessing the actual app manager
prefs.set("devtools.appmanager.firstrun", false);
onConnected: function() {
document.querySelector("#content").classList.add("connected");
},
onDisconnected: function() {
for (let [,toolbox] of this._handledTargets) {
if (toolbox) {
toolbox.destroy();
}
}
this._handledTargets.clear();
document.querySelector("#content").classList.remove("connected");
},
createToolboxTab: function(name, iconURL, uid) {
let button = document.createElement("button");
button.className = "button toolbox " + uid + "-button";
button.setAttribute("panel", uid);
button.textContent = name;
button.setAttribute("style", "background-image: url(" + iconURL + ")");
let toolboxTabs = document.querySelector("#toolbox-tabs");
toolboxTabs.appendChild(button);
let iframe = document.createElement("iframe");
iframe.setAttribute("flex", "1");
iframe.className = "panel toolbox " + uid + "-panel";
let panels = document.querySelector("#tab-panels");
panels.appendChild(iframe);
this.selectTab(uid);
return iframe;
},
closeToolboxTab: function(uid) {
let buttonToDestroy = document.querySelector("." + uid + "-button");
let panelToDestroy = document.querySelector("." + uid + "-panel");
if (buttonToDestroy.hasAttribute("selected")) {
let lastTab = prefs.get("devtools.appmanager.lastTab");
this.selectTab(lastTab);
}
buttonToDestroy.remove();
panelToDestroy.remove();
},
openAndShowToolboxForTarget: function(target, name, icon) {
let host = devtools.Toolbox.HostType.CUSTOM;
if (!this._handledTargets.has(target)) {
let uid = "uid" + this._toolboxTabCursor++;
let iframe = this.createToolboxTab(name, icon, uid);
let options = { customIframe: iframe , uid: uid };
this._handledTargets.set(target, null);
return gDevTools.showToolbox(target, null, host, options).then(toolbox => {
this._handledTargets.set(target, toolbox);
toolbox.once("destroyed", () => {
this._handledTargets.delete(target)
});
});
} else {
let toolbox = this._handledTargets.get(target);
if (!toolbox) {
// Target is handled, but toolbox is still being
// created.
return promise.resolve(null);
}
return gDevTools.showToolbox(target, null, host);
}
}
}
let firstRun = prefs.get("devtools.appmanager.firstrun");
if (firstRun) {
selectTab("help");
} else {
selectTab("projects");
}
UI.init();

View File

@ -22,11 +22,11 @@
<vbox id="root" flex="1">
<hbox id="content" flex="1">
<vbox id="tabs">
<button class="button projects-button" onclick="selectTab('projects')">&index.projects2;</button>
<button class="button device-button" onclick="selectTab('device')">&index.device2;</button>
<spacer flex="1"/>
<button class="button help-button" onclick="selectTab('help')">&index.help;</button>
<vbox id="tabs" onclick="UI.selectTabFromButton(event.target)">
<button class="button projects-button" panel="projects">&index.projects2;</button>
<button class="button device-button" panel="device">&index.device2;</button>
<vbox id="toolbox-tabs" flex="1"/>
<button class="button help-button" panel="help">&index.help;</button>
</vbox>
<hbox id="tab-panels" flex="1">
<iframe flex="1" class="panel projects-panel" src="chrome://browser/content/devtools/app-manager/projects.xhtml"/>

View File

@ -356,25 +356,15 @@ let UI = {
loop(0);
return deferred.promise;
};
let onTargetReady = (target) => {
// Finally, when it's finally opened, display the toolbox
let deferred = promise.defer();
gDevTools.showToolbox(target,
null,
devtools.Toolbox.HostType.WINDOW).then(toolbox => {
this.connection.once(Connection.Events.DISCONNECTED, () => {
toolbox.destroy();
});
deferred.resolve(toolbox);
});
return deferred.promise;
};
// First try to open the app
this.start(project)
.then(null, onFailedToStart)
.then(onStarted)
.then(onTargetReady)
.then((target) =>
top.UI.openAndShowToolboxForTarget(target,
project.manifest.name,
project.icon))
.then(() => {
// And only when the toolbox is opened, release the button
button.disabled = false;

View File

@ -33,7 +33,7 @@ function test() {
});
}
function testNavigate() {
function testNavigate([aGrip, aResponse]) {
let outstanding = [promise.defer(), promise.defer()];
gClient.addListener("tabNavigated", function onTabNavigated(aEvent, aPacket) {
@ -51,14 +51,16 @@ function testNavigate() {
});
gBrowser.selectedTab.linkedBrowser.loadURI(TAB2_URL);
return promise.all(outstanding.map(e => e.promise));
return promise.all(outstanding.map(e => e.promise))
.then(() => aGrip.actor);
}
function testDetach() {
function testDetach(aActor) {
let deferred = promise.defer();
gClient.addOneTimeListener("tabDetached", () => {
gClient.addOneTimeListener("tabDetached", (aType, aPacket) => {
ok(true, "Got a tab detach notification.");
is(aPacket.from, aActor, "tab detach message comes from the expected actor");
gClient.close(deferred.resolve);
});

View File

@ -206,11 +206,13 @@ DevTools.prototype = {
* The id of the tool to show
* @param {Toolbox.HostType} hostType
* The type of host (bottom, window, side)
* @param {object} hostOptions
* Options for host specifically
*
* @return {Toolbox} toolbox
* The toolbox that was opened
*/
showToolbox: function(target, toolId, hostType) {
showToolbox: function(target, toolId, hostType, hostOptions) {
let deferred = promise.defer();
let toolbox = this._toolboxes.get(target);
@ -233,7 +235,7 @@ DevTools.prototype = {
}
else {
// No toolbox for target, create one
toolbox = new devtools.Toolbox(target, toolId, hostType);
toolbox = new devtools.Toolbox(target, toolId, hostType, hostOptions);
this._toolboxes.set(target, toolbox);

View File

@ -350,7 +350,13 @@ TabTarget.prototype = {
* Setup listeners for remote debugging, updating existing ones as necessary.
*/
_setupRemoteListeners: function TabTarget__setupRemoteListeners() {
this.client.addListener("tabDetached", this.destroy);
this._onTabDetached = (aType, aPacket) => {
// We have to filter message to ensure that this detach is for this tab
if (aPacket.from == this._form.actor) {
this.destroy();
}
};
this.client.addListener("tabDetached", this._onTabDetached);
this._onTabNavigated = function onRemoteTabNavigated(aType, aPacket) {
let event = Object.create(null);
@ -377,7 +383,7 @@ TabTarget.prototype = {
*/
_teardownRemoteListeners: function TabTarget__teardownRemoteListeners() {
this.client.removeListener("tabNavigated", this._onTabNavigated);
this.client.removeListener("tabDetached", this.destroy);
this.client.removeListener("tabDetached", this._onTabDetached);
},
/**

View File

@ -22,3 +22,4 @@ support-files = head.js
[browser_toolbox_window_shortcuts.js]
[browser_toolbox_window_title_changes.js]
[browser_toolbox_zoom.js]
[browser_toolbox_custom_host.js]

View File

@ -0,0 +1,60 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
Cu.import("resource://gre/modules/Services.jsm");
let temp = {}
Cu.import("resource:///modules/devtools/gDevTools.jsm", temp);
let DevTools = temp.DevTools;
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", temp);
let LayoutHelpers = temp.LayoutHelpers;
Cu.import("resource://gre/modules/devtools/Loader.jsm", temp);
let devtools = temp.devtools;
let Toolbox = devtools.Toolbox;
let toolbox, iframe, target, tab;
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
target = TargetFactory.forTab(gBrowser.selectedTab);
window.addEventListener("message", onMessage);
iframe = document.createElement("iframe");
document.documentElement.appendChild(iframe);
gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
let options = {customIframe: iframe};
gDevTools.showToolbox(target, null, Toolbox.HostType.CUSTOM, options)
.then(testCustomHost, console.error)
.then(null, console.error);
}, true);
content.location = "data:text/html,test custom host";
function onMessage(event) {
info("onMessage: " + event.data);
let json = JSON.parse(event.data);
if (json.name == "toolbox-close") {
ok("Got the `toolbox-close` message");
cleanup();
}
}
function testCustomHost(toolbox) {
is(toolbox.doc.defaultView.top, window, "Toolbox is included in browser.xul");
is(toolbox.doc, iframe.contentDocument, "Toolbox is in the custom iframe");
executeSoon(() => gBrowser.removeCurrentTab());
}
function cleanup() {
window.removeEventListener("message", onMessage);
iframe.remove();
finish();
}
}

View File

@ -10,6 +10,7 @@ let promise = require("sdk/core/promise");
let EventEmitter = require("devtools/shared/event-emitter");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
/**
* A toolbox host represents an object that contains a toolbox (e.g. the
@ -23,7 +24,8 @@ Cu.import("resource://gre/modules/Services.jsm");
exports.Hosts = {
"bottom": BottomHost,
"side": SidebarHost,
"window": WindowHost
"window": WindowHost,
"custom": CustomHost
}
/**
@ -61,18 +63,18 @@ BottomHost.prototype = {
this._nbox.appendChild(this.frame);
let frameLoad = function() {
this.frame.removeEventListener("DOMContentLoaded", frameLoad, true);
this.emit("ready", this.frame);
deferred.resolve(this.frame);
}.bind(this);
this.frame.tooltip = "aHTMLTooltip";
this.frame.addEventListener("DOMContentLoaded", frameLoad, true);
// we have to load something so we can switch documents if we have to
this.frame.setAttribute("src", "about:blank");
let domHelper = new DOMHelpers(this.frame.contentWindow);
domHelper.onceDOMReady(frameLoad);
focusTab(this.hostTab);
return deferred.promise;
@ -272,6 +274,59 @@ WindowHost.prototype = {
}
}
/**
* Host object for the toolbox in its own tab
*/
function CustomHost(hostTab, options) {
this.frame = options.customIframe;
this.uid = options.uid;
EventEmitter.decorate(this);
}
CustomHost.prototype = {
type: "custom",
_sendMessageToTopWindow: function CH__sendMessageToTopWindow(msg) {
// It's up to the custom frame owner (parent window) to honor
// "close" or "raise" instructions.
let topWindow = this.frame.ownerDocument.defaultView;
let json = {name:"toolbox-" + msg, uid: this.uid}
topWindow.postMessage(JSON.stringify(json), "*");
},
/**
* Create a new xul window to contain the toolbox.
*/
create: function CH_create() {
return promise.resolve(this.frame);
},
/**
* Raise the host.
*/
raise: function CH_raise() {
this._sendMessageToTopWindow("raise");
},
/**
* Set the toolbox title.
*/
setTitle: function CH_setTitle(title) {
// Not supported
},
/**
* Destroy the window.
*/
destroy: function WH_destroy() {
if (!this._destroyed) {
this._destroyed = true;
this._sendMessageToTopWindow("close");
}
return promise.resolve(null);
}
}
/**
* Switch to the given tab in a browser and focus the browser window
*/

View File

@ -19,6 +19,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
loader.lazyGetter(this, "Hosts", () => require("devtools/framework/toolbox-hosts").Hosts);
@ -55,8 +56,10 @@ loader.lazyGetter(this, "Requisition", () => {
* Tool to select initially
* @param {Toolbox.HostType} hostType
* Type of host that will host the toolbox (e.g. sidebar, window)
* @param {object} hostOptions
* Options for host specifically
*/
function Toolbox(target, selectedTool, hostType) {
function Toolbox(target, selectedTool, hostType, hostOptions) {
this._target = target;
this._toolPanels = new Map();
this._telemetry = new Telemetry();
@ -79,7 +82,7 @@ function Toolbox(target, selectedTool, hostType) {
}
this._defaultToolId = selectedTool;
this._host = this._createHost(hostType);
this._host = this._createHost(hostType, hostOptions);
EventEmitter.decorate(this);
@ -99,7 +102,8 @@ exports.Toolbox = Toolbox;
Toolbox.HostType = {
BOTTOM: "bottom",
SIDE: "side",
WINDOW: "window"
WINDOW: "window",
CUSTOM: "custom"
};
Toolbox.prototype = {
@ -187,8 +191,6 @@ Toolbox.prototype = {
let deferred = promise.defer();
let domReady = () => {
iframe.removeEventListener("DOMContentLoaded", domReady, true);
this.isReady = true;
let closeButton = this.doc.getElementById("toolbox-close");
@ -211,9 +213,11 @@ Toolbox.prototype = {
});
};
iframe.addEventListener("DOMContentLoaded", domReady, true);
iframe.setAttribute("src", this._URL);
let domHelper = new DOMHelpers(iframe.contentWindow);
domHelper.onceDOMReady(domReady);
return deferred.promise;
});
},
@ -387,6 +391,7 @@ Toolbox.prototype = {
for (let type in Toolbox.HostType) {
let position = Toolbox.HostType[type];
if (position == this.hostType ||
position == Toolbox.HostType.CUSTOM ||
(!sideEnabled && position == Toolbox.HostType.SIDE)) {
continue;
}
@ -555,8 +560,6 @@ Toolbox.prototype = {
vbox.appendChild(iframe);
let onLoad = () => {
iframe.removeEventListener("DOMContentLoaded", onLoad, true);
let built = definition.build(iframe.contentWindow, this);
promise.resolve(built).then((panel) => {
this._toolPanels.set(id, panel);
@ -566,8 +569,25 @@ Toolbox.prototype = {
});
};
iframe.addEventListener("DOMContentLoaded", onLoad, true);
iframe.setAttribute("src", definition.url);
// Depending on the host, iframe.contentWindow is not always
// defined at this moment. If it is not defined, we use an
// event listener on the iframe DOM node. If it's defined,
// we use the chromeEventHandler. We can't use a listener
// on the DOM node every time because this won't work
// if the (xul chrome) iframe is loaded in a content docshell.
if (iframe.contentWindow) {
let domHelper = new DOMHelpers(iframe.contentWindow);
domHelper.onceDOMReady(onLoad);
} else {
let callback = () => {
iframe.removeEventListener("DOMContentLoaded", callback);
onLoad();
}
iframe.addEventListener("DOMContentLoaded", callback);
}
return deferred.promise;
},
@ -732,13 +752,13 @@ Toolbox.prototype = {
* @return {Host} host
* The created host object
*/
_createHost: function(hostType) {
_createHost: function(hostType, options) {
if (!Hosts[hostType]) {
throw new Error("Unknown hostType: " + hostType);
}
// clean up the toolbox if its window is closed
let newHost = new Hosts[hostType](this.target.tab);
let newHost = new Hosts[hostType](this.target.tab, options);
newHost.on("window-closed", this.destroy);
return newHost;
},
@ -766,7 +786,9 @@ Toolbox.prototype = {
this._host = newHost;
Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type);
if (this.hostType != Toolbox.HostType.CUSTOM) {
Services.prefs.setCharPref(this._prefs.LAST_HOST, this._host.type);
}
this._buildDockButtons();
this._addKeysToWindow();

View File

@ -181,6 +181,13 @@ function ResponsiveUI(aWindow, aTab)
this.buildUI();
this.checkMenus();
this.docShell = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
this._deviceSizeWasPageSize = this.docShell.deviceSizeIsPageSize;
this.docShell.deviceSizeIsPageSize = true;
try {
if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
this.rotate();
@ -249,6 +256,8 @@ ResponsiveUI.prototype = {
return;
this.closing = true;
this.docShell.deviceSizeIsPageSize = this._deviceSizeWasPageSize;
this.browser.removeEventListener("load", this.bound_onPageLoad, true);
this.browser.removeEventListener("unload", this.bound_onPageUnload, true);
@ -288,6 +297,7 @@ ResponsiveUI.prototype = {
this.container.removeAttribute("responsivemode");
this.stack.removeAttribute("responsivemode");
delete this.docShell;
delete this.tab.__responsiveUI;
if (this.touchEventHandler)
this.touchEventHandler.stop();

View File

@ -9,3 +9,4 @@ support-files =
[browser_responsiveui.js]
[browser_responsiveui_touch.js]
[browser_responsiveuiaddcustompreset.js]
[browser_responsive_devicewidth.js]

View File

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let instance;
let mgr = ResponsiveUI.ResponsiveUIManager;
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
waitForFocus(startTest, content);
}, true);
content.location = "data:text/html,mop";
function startTest() {
mgr.once("on", function() {executeSoon(onUIOpen)});
document.getElementById("Tools:ResponsiveUI").doCommand();
}
function onUIOpen() {
instance = gBrowser.selectedTab.__responsiveUI;
instance.stack.setAttribute("notransition", "true");
ok(instance, "instance of the module is attached to the tab.");
instance.setSize(110, 500);
ok(content.innerWidth, 110, "initial width is valid");
let mql = content.matchMedia("(max-device-width:100px)")
ok(!mql.matches, "media query doesn't match.");
mql.addListener(onMediaChange);
instance.setSize(90, 500);
}
function onMediaChange(mql) {
mql.removeListener(onMediaChange);
ok(mql.matches, "media query matches.");
ok(window.screen.width != content.screen.width, "screen.width is not the size of the screen.");
is(content.screen.width, 90, "screen.width is the width of the page.");
is(content.screen.height, 500, "screen.height is the height of the page.");
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
mql.addListener(onMediaChange2);
docShell.deviceSizeIsPageSize = false;
}
function onMediaChange2(mql) {
mql.removeListener(onMediaChange);
ok(!mql.matches, "media query has been re-evaluated.");
ok(window.screen.width == content.screen.width, "screen.width is not the size of the screen.");
instance.stack.removeAttribute("notransition");
document.getElementById("Tools:ResponsiveUI").doCommand();
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -2,6 +2,10 @@
* 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 Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
this.EXPORTED_SYMBOLS = ["DOMHelpers"];
/**
@ -13,6 +17,9 @@ this.EXPORTED_SYMBOLS = ["DOMHelpers"];
* The content window, owning the document to traverse.
*/
this.DOMHelpers = function DOMHelpers(aWindow) {
if (!aWindow) {
throw new Error("window can't be null or undefined");
}
this.window = aWindow;
};
@ -120,5 +127,30 @@ DOMHelpers.prototype = {
{
delete this.window;
delete this.treeWalker;
},
/**
* A simple way to be notified (once) when a window becomes
* interactive (DOMContentLoaded).
*
* It is based on the chromeEventHandler. This is useful when
* chrome iframes are loaded in content docshells (in Firefox
* tabs for example).
*/
onceDOMReady: function Helpers_onLocationChange(callback) {
let window = this.window;
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let onReady = function(event) {
if (event.target == window.document) {
docShell.chromeEventHandler.removeEventListener("DOMContentLoaded", onReady, false);
// If in `callback` the URL of the window is changed and a listener to DOMContentLoaded
// is attached, the event we just received will be also be caught by the new listener.
// We want to avoid that so we execute the callback in the next queue.
Services.tm.mainThread.dispatch(callback, 0);
}
}
docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady, false);
}
};

View File

@ -12,6 +12,7 @@
#banners-and-logs {
display: flex;
flex-grow: 1;
}
#logs {

View File

@ -14,6 +14,10 @@
background: #252C33;
}
#toolbox-tabs {
overflow-y: auto;
}
.button {
width: 80px;
height: 85px;
@ -54,6 +58,12 @@
display: none;
}
.button.toolbox {
background-repeat: no-repeat;
background-position: center 15px;
background-size: 40px 40px;
}
.projects-button {
background: url('chrome://browser/skin/devtools/app-manager/index-icons.svg') no-repeat;
background-position: left -5px;

View File

@ -755,6 +755,7 @@ nsDocShell::nsDocShell():
mIsAppTab(false),
mUseGlobalHistory(false),
mInPrivateBrowsing(false),
mDeviceSizeIsPageSize(false),
mFiredUnloadEvent(false),
mEODForCurrentDocument(false),
mURIResultedInDocument(false),
@ -3926,6 +3927,27 @@ nsDocShell::GetScriptGlobalObject()
return mScriptGlobal;
}
NS_IMETHODIMP
nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
{
if (mDeviceSizeIsPageSize != aValue) {
mDeviceSizeIsPageSize = aValue;
nsRefPtr<nsPresContext> presContext;
GetPresContext(getter_AddRefs(presContext));
if (presContext) {
presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
{
*aValue = mDeviceSizeIsPageSize;
return NS_OK;
}
void
nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
{
@ -4935,6 +4957,10 @@ nsDocShell::Create()
gAddedPreferencesVarCache = true;
}
mDeviceSizeIsPageSize =
Preferences::GetBool("docshell.device_size_is_page_size",
mDeviceSizeIsPageSize);
nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
if (serv) {
const char* msg = mItemType == typeContent ?

View File

@ -810,6 +810,7 @@ protected:
bool mIsAppTab;
bool mUseGlobalHistory;
bool mInPrivateBrowsing;
bool mDeviceSizeIsPageSize;
// This boolean is set to true right before we fire pagehide and generally
// unset when we embed a new content viewer. While it's true no navigation

View File

@ -44,7 +44,7 @@ interface nsIReflowObserver;
typedef unsigned long nsLoadFlags;
[scriptable, builtinclass, uuid(5c9adf31-8e4a-4d8d-8daf-9999e6002697)]
[scriptable, builtinclass, uuid(55ca6545-7ce4-49ad-8117-8286ca9c61bb)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -913,4 +913,16 @@ interface nsIDocShell : nsIDocShellTreeItem
* Get the script global for the document in this docshell.
*/
[noscript,notxpcom,nostdcall] nsIScriptGlobalObject GetScriptGlobalObject();
/**
* If deviceSizeIsPageSize is set to true, device-width/height media queries
* will be calculated from the page size, not the device size.
*
* Used by the Responsive Design View and B2G Simulator.
*
* Default is False.
* Default value can be overriden with
* docshell.device_size_is_page_size pref.
*/
[infallible] attribute boolean deviceSizeIsPageSize;
};

View File

@ -390,6 +390,19 @@ nsScreen::SlowMozUnlockOrientation()
return NS_OK;
}
bool
nsScreen::IsDeviceSizePageSize()
{
nsPIDOMWindow* owner = GetOwner();
if (owner) {
nsIDocShell* docShell = owner->GetDocShell();
if (docShell) {
return docShell->GetDeviceSizeIsPageSize();
}
}
return false;
}
/* virtual */
JSObject*
nsScreen::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)

View File

@ -51,6 +51,15 @@ public:
int32_t GetWidth(ErrorResult& aRv)
{
nsRect rect;
if (IsDeviceSizePageSize()) {
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
if (owner) {
int32_t innerWidth = 0;
aRv = owner->GetInnerWidth(&innerWidth);
return innerWidth;
}
}
aRv = GetRect(rect);
return rect.width;
}
@ -58,6 +67,15 @@ public:
int32_t GetHeight(ErrorResult& aRv)
{
nsRect rect;
if (IsDeviceSizePageSize()) {
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
if (owner) {
int32_t innerHeight = 0;
aRv = owner->GetInnerHeight(&innerHeight);
return innerHeight;
}
}
aRv = GetRect(rect);
return rect.height;
}
@ -137,6 +155,8 @@ private:
LockPermission GetLockOrientationPermission() const;
bool IsDeviceSizePageSize();
nsRefPtr<FullScreenEventListener> mEventListener;
};

View File

@ -16,6 +16,7 @@
#include "mozilla/MouseEvents.h"
#include "mozilla/mozalloc.h" // for operator new
#include "mozilla/TouchEvents.h"
#include "nsDebug.h" // for NS_WARNING
#include "nsPoint.h" // for nsIntPoint
#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
#include "nsThreadUtils.h" // for NS_IsMainThread
@ -29,7 +30,8 @@ namespace layers {
float APZCTreeManager::sDPI = 72.0;
APZCTreeManager::APZCTreeManager()
: mTreeLock("APZCTreeLock")
: mTreeLock("APZCTreeLock"),
mTouchCount(0)
{
MOZ_ASSERT(NS_IsMainThread());
AsyncPanZoomController::InitializeGlobalState();
@ -238,6 +240,7 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
case MULTITOUCH_INPUT: {
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
mTouchCount++;
mApzcForInputBlock = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
for (size_t i = 1; i < multiTouchInput.mTouches.Length(); i++) {
nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[i].mScreenPoint));
@ -267,10 +270,18 @@ APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
result = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
}
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END) {
if (mTouchCount >= multiTouchInput.mTouches.Length()) {
mTouchCount -= multiTouchInput.mTouches.Length();
} else {
NS_WARNING("Got an unexpected touchend/touchcancel");
mTouchCount = 0;
}
// If we have an mApzcForInputBlock and it's the end of the touch sequence
// then null it out so we don't keep a dangling reference and leak things.
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
(multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END && multiTouchInput.mTouches.Length() == 1)) {
if (mTouchCount == 0) {
mApzcForInputBlock = nullptr;
}
}
@ -333,31 +344,50 @@ nsEventStatus
APZCTreeManager::ProcessTouchEvent(const WidgetTouchEvent& aEvent,
WidgetTouchEvent* aOutEvent)
{
// For computing the input for the APZC, used the cached transform.
// This ensures that the sequence of touch points an APZC sees in an
// input block are all in the same coordinate space.
gfx3DMatrix transformToApzc = mCachedTransformToApzcForInputBlock;
MultiTouchInput inputForApzc(aEvent);
for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
nsEventStatus ret = nsEventStatus_eIgnore;
if (!aEvent.touches.Length()) {
return ret;
}
nsEventStatus ret = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
// For computing the event to pass back to Gecko, use the up-to-date transforms.
// This ensures that transformToApzc and transformToGecko are in sync
// (note that transformToGecko isn't cached).
gfx3DMatrix transformToGecko;
GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
gfx3DMatrix outTransform = transformToApzc * transformToGecko;
for (size_t i = 0; i < aOutEvent->touches.Length(); i++) {
ApplyTransform(&(aOutEvent->touches[i]->mRefPoint), outTransform);
if (aEvent.message == NS_TOUCH_START) {
mTouchCount++;
ScreenPoint point = ScreenPoint(aEvent.touches[0]->mRefPoint.x, aEvent.touches[0]->mRefPoint.y);
mApzcForInputBlock = GetTouchInputBlockAPZC(aEvent, point);
}
if (mApzcForInputBlock) {
// For computing the input for the APZC, used the cached transform.
// This ensures that the sequence of touch points an APZC sees in an
// input block are all in the same coordinate space.
gfx3DMatrix transformToApzc = mCachedTransformToApzcForInputBlock;
MultiTouchInput inputForApzc(aEvent);
for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
}
ret = mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
// For computing the event to pass back to Gecko, use the up-to-date transforms.
// This ensures that transformToApzc and transformToGecko are in sync
// (note that transformToGecko isn't cached).
gfx3DMatrix transformToGecko;
GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
gfx3DMatrix outTransform = transformToApzc * transformToGecko;
for (size_t i = 0; i < aOutEvent->touches.Length(); i++) {
ApplyTransform(&(aOutEvent->touches[i]->mRefPoint), outTransform);
}
}
// If we have an mApzcForInputBlock and it's the end of the touch sequence
// then null it out so we don't keep a dangling reference and leak things.
if (aEvent.message == NS_TOUCH_CANCEL ||
(aEvent.message == NS_TOUCH_END && aEvent.touches.Length() == 1)) {
mApzcForInputBlock = nullptr;
aEvent.message == NS_TOUCH_END) {
if (mTouchCount >= aEvent.touches.Length()) {
mTouchCount -= aEvent.touches.Length();
} else {
NS_WARNING("Got an unexpected touchend/touchcancel");
mTouchCount = 0;
}
if (mTouchCount == 0) {
mApzcForInputBlock = nullptr;
}
}
return ret;
}
@ -406,16 +436,6 @@ APZCTreeManager::ReceiveInputEvent(const WidgetInputEvent& aEvent,
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
const WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
if (!touchEvent.touches.Length()) {
return nsEventStatus_eIgnore;
}
if (touchEvent.message == NS_TOUCH_START) {
ScreenPoint point = ScreenPoint(touchEvent.touches[0]->mRefPoint.x, touchEvent.touches[0]->mRefPoint.y);
mApzcForInputBlock = GetTouchInputBlockAPZC(touchEvent, point);
}
if (!mApzcForInputBlock) {
return nsEventStatus_eIgnore;
}
return ProcessTouchEvent(touchEvent, aOutEvent->AsTouchEvent());
}
case NS_MOUSE_EVENT: {
@ -438,16 +458,6 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent)
switch (aEvent.eventStructType) {
case NS_TOUCH_EVENT: {
WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
if (!touchEvent.touches.Length()) {
return nsEventStatus_eIgnore;
}
if (touchEvent.message == NS_TOUCH_START) {
ScreenPoint point = ScreenPoint(touchEvent.touches[0]->mRefPoint.x, touchEvent.touches[0]->mRefPoint.y);
mApzcForInputBlock = GetTouchInputBlockAPZC(touchEvent, point);
}
if (!mApzcForInputBlock) {
return nsEventStatus_eIgnore;
}
return ProcessTouchEvent(touchEvent, &touchEvent);
}
default: {

View File

@ -321,6 +321,8 @@ private:
* input delivery thread, and so does not require locking.
*/
nsRefPtr<AsyncPanZoomController> mApzcForInputBlock;
/* The number of touch points we are tracking that are currently on the screen. */
uint32_t mTouchCount;
/* The transform from root screen coordinates into mApzcForInputBlock's
* screen coordinates, as returned through the 'aTransformToApzcOut' parameter
* of GetInputTransform(), at the start of the input block. This is cached

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=4 ts=8 et tw=80 : */
/* vim: set sw=2 ts=8 et tw=80 : */
/* 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/. */
@ -73,11 +73,7 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
}
NS_WARN_IF_FALSE(!foundAlreadyExistingTouch, "Tried to add a touch that already exists");
// If we didn't find a touch in our list that matches this, then add it.
// If it already existed, we don't want to add it twice because that
// messes with our touch move/end code.
if (!foundAlreadyExistingTouch) {
mTouches.AppendElement(event.mTouches[i]);
}
@ -137,18 +133,17 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
}
case MultiTouchInput::MULTITOUCH_END:
case MultiTouchInput::MULTITOUCH_LEAVE: {
bool foundAlreadyExistingTouch = false;
for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) {
for (size_t i = 0; i < event.mTouches.Length(); i++) {
bool foundAlreadyExistingTouch = false;
for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) {
if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) {
foundAlreadyExistingTouch = true;
mTouches.RemoveElementAt(j);
}
}
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
}
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
CancelDoubleTapTimeoutTask();
if (mTapStartTime - mLastTapEndTime > MAX_TAP_TIME ||

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=4 ts=8 et tw=80 : */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -2688,6 +2688,17 @@ nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const
return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
}
bool
nsPresContext::IsDeviceSizePageSize()
{
bool isDeviceSizePageSize = false;
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
if (docShell) {
isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize();
}
return isDeviceSizePageSize;
}
nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
nsPresContextType aType)
: nsPresContext(aDocument, aType),

View File

@ -1000,6 +1000,8 @@ public:
mExistThrottledUpdates = aExistThrottledUpdates;
}
bool IsDeviceSizePageSize();
protected:
friend class nsRunnableMethod<nsPresContext>;
NS_HIDDEN_(void) ThemeChangedInternal();

View File

@ -113,14 +113,18 @@ static nsSize
GetDeviceSize(nsPresContext* aPresContext)
{
nsSize size;
if (aPresContext->IsRootPaginatedDocument())
if (aPresContext->IsDeviceSizePageSize()) {
size = GetSize(aPresContext);
} else if (aPresContext->IsRootPaginatedDocument()) {
// We want the page size, including unprintable areas and margins.
// XXX The spec actually says we want the "page sheet size", but
// how is that different?
size = aPresContext->GetPageSize();
else
} else {
GetDeviceContextFor(aPresContext)->
GetDeviceSurfaceDimensions(size.width, size.height);
}
return size;
}

View File

@ -20,16 +20,9 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -49,6 +42,10 @@ public class GeckoView extends LayerView
boolean doInit = a.getBoolean(R.styleable.GeckoView_doinit, true);
a.recycle();
// TODO: Fennec currently takes care of its own initialization, so this
// flag is a hack used in Fennec to prevent GeckoView initialization.
// This should go away once Fennec also uses GeckoView for
// initialization.
if (!doInit)
return;
@ -97,12 +94,17 @@ public class GeckoView extends LayerView
ThreadUtils.setUiThread(Thread.currentThread(), new Handler());
initializeView(GeckoAppShell.getEventDispatcher());
GeckoProfile profile = GeckoProfile.get(context).forceCreate();
BrowserDB.initialize(profile.getName());
if (GeckoThread.checkAndSetLaunchState(GeckoThread.LaunchState.Launching, GeckoThread.LaunchState.Launched)) {
// This is the first launch, so finish initialization and go.
GeckoProfile profile = GeckoProfile.get(context).forceCreate();
BrowserDB.initialize(profile.getName());
GeckoAppShell.setLayerView(this);
GeckoThread.createAndStart();
} else if(GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
// If Gecko is already running, that means the Activity was
// destroyed, so we need to re-attach Gecko to this GeckoView.
connectToGecko();
}
}
@ -195,7 +197,7 @@ public class GeckoView extends LayerView
});
}
private void handleReady(final JSONObject message) {
private void connectToGecko() {
GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning);
Tab selectedTab = Tabs.getInstance().getSelectedTab();
if (selectedTab != null)
@ -203,8 +205,10 @@ public class GeckoView extends LayerView
geckoConnected();
GeckoAppShell.setLayerClient(getLayerClient());
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null));
show();
requestRender();
}
private void handleReady(final JSONObject message) {
connectToGecko();
if (mChromeDelegate != null) {
mChromeDelegate.onReady(this);

View File

@ -3,13 +3,8 @@
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

View File

@ -9,11 +9,32 @@ GECKOVIEW_LIBRARY_FILES := \
.project \
AndroidManifest.xml \
project.properties \
build.xml \
$(NULL)
PP_TARGETS = properties
properties = local.properties.in
properties_PATH = .
properties_deps := $(patsubst %.in,%,$(properties))
GARBAGE = $(GECKOVIEW_LIBRARY_FILES) $(properties_deps)
GARBAGE_DIRS = \
bin \
libs \
src \
.deps \
gen \
res \
$(NULL)
DEFINES += -DANDROID_SDK=$(ANDROID_SDK)
include $(topsrcdir)/config/rules.mk
package:
package: $(properties_deps)
# Make directory for the zips
$(MKDIR) -p $(DIST)/geckoview_library

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="GeckoView" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<!--<property file="ant.properties" />-->
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

View File

@ -0,0 +1,11 @@
#filter substitution
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
# location of the SDK. This is only used by Ant
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=@ANDROID_SDK@/../../

View File

@ -66,7 +66,7 @@ body {
.header > h1 {
font-size: 2.625em;
font-weight: 300;
font-weight: 700;
line-height: 1.1em;
width: 100%;
margin: 0px;
@ -75,10 +75,6 @@ body {
padding: 0px;
}
.serif > .header > h1 {
font-weight: 700;
}
.header > .credits {
padding: 0px;
margin: 0px;

View File

@ -281,12 +281,8 @@ nsJARInputStream::ContinueInflate(char* aBuffer, uint32_t aCount,
inflateEnd(&mZs);
// stop returning valid data as soon as we know we have a bad CRC
if (mOutCrc != mInCrc) {
// asserting because while this rarely happens, you definitely
// want to catch it in debug builds!
NS_NOTREACHED(0);
if (mOutCrc != mInCrc)
return NS_ERROR_FILE_CORRUPTED;
}
}
return NS_OK;

View File

@ -440,31 +440,8 @@
"content/events/test/test_bug322588.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug493251.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug545268.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug650493.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug656379-1.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug656379-2.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug656954.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug659350.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug662678.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug667612.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug698929.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug741666.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug742376.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug812744.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug847597.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug855741.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_bug864040.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_clickevent_on_input.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_dblclick_explicit_original_target.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_dom_keyboard_event.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_dom_mouse_event.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_draggableprop.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_eventctors.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_focus_disabled.html": "Bug 931116, b2g desktop specific, initial triage",
"content/events/test/test_moz_mouse_pixel_scroll_event.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_button_attributes_reflection.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_change_event.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_form_attribute-1.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_input_event.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_input_range_key_events.html": "Bug 931116, b2g desktop specific, initial triage",
"content/html/content/test/forms/test_input_range_rounding.html": "Bug 931116, b2g desktop specific, initial triage",
@ -705,26 +682,14 @@
"dom/tests/mochitest/general/test__content.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_bug628069_1.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_bug631440.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_frameElementWrapping.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_framedhistoryframes.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_interfaces.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_offsets.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_outerHTML.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_outerHTML.xhtml": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_stylesheetPI.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_vibrator.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_windowProperties.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/general/test_windowedhistoryframes.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/sessionstorage/test_sessionStorageClone.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/sessionstorage/test_sessionStorageReplace.html": "Bug 931116, b2g desktop specific, initial triage",
"dom/tests/mochitest/whatwg/test_postMessage_closed.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug607529.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug749186.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug770106.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug842853.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug842853-2.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_bug858459.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_reftests_with_caret.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/base/tests/test_remote_frame.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/generic/test/test_bug735641.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/generic/test/test_bug784410.html": "Bug 931116, b2g desktop specific, initial triage",
@ -732,10 +697,6 @@
"layout/generic/test/test_plugin_clipping2.xhtml": "Bug 931116, b2g desktop specific, initial triage",
"layout/generic/test/test_plugin_clipping_table.xhtml": "Bug 931116, b2g desktop specific, initial triage",
"layout/generic/test/test_plugin_clipping_transformed.xhtml": "Bug 931116, b2g desktop specific, initial triage",
"layout/style/test/test_property_syntax_errors.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/style/test/test_redundant_font_download.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/style/test/test_rem_unit.html": "Bug 931116, b2g desktop specific, initial triage",
"layout/style/test/test_rule_serialization.html": "Bug 931116, b2g desktop specific, initial triage",
"toolkit/devtools/apps/tests/test_webapps_actor.html": "Bug 931116, b2g desktop specific, initial triage"
}
}

View File

@ -1191,8 +1191,8 @@ Engine.prototype = {
_updateURL: null,
// The url to check for a new icon
_iconUpdateURL: null,
// A reference to the timer used for lazily serializing the engine to file
_serializeTimer: null,
/* Deferred serialization task. */
_lazySerializeTask: null,
/**
* Retrieves the data from the engine's file. If the engine's dataType is
@ -2363,28 +2363,15 @@ Engine.prototype = {
return doc;
},
_lazySerializeToFile: function SRCH_ENG_serializeToFile() {
if (this._serializeTimer) {
// Reset the timer
this._serializeTimer.delay = LAZY_SERIALIZE_DELAY;
} else {
this._serializeTimer = Cc["@mozilla.org/timer;1"].
createInstance(Ci.nsITimer);
var timerCallback = {
self: this,
notify: function SRCH_ENG_notify(aTimer) {
try {
this.self._serializeToFile();
} catch (ex) {
LOG("Serialization from timer callback failed:\n" + ex);
}
this.self._serializeTimer = null;
}
};
this._serializeTimer.initWithCallback(timerCallback,
LAZY_SERIALIZE_DELAY,
Ci.nsITimer.TYPE_ONE_SHOT);
get lazySerializeTask() {
if (!this._lazySerializeTask) {
let task = function taskCallback() {
this._serializeToFile();
}.bind(this);
this._lazySerializeTask = new DeferredTask(task, LAZY_SERIALIZE_DELAY);
}
return this._lazySerializeTask;
},
/**
@ -2415,6 +2402,9 @@ Engine.prototype = {
}
closeSafeOutputStream(fos);
Services.obs.notifyObservers(file.clone(), SEARCH_SERVICE_TOPIC,
"write-engine-to-disk-complete");
},
/**
@ -2660,7 +2650,7 @@ Engine.prototype = {
url.addParam(aName, aValue);
// Serialize the changes to file lazily
this._lazySerializeToFile();
this.lazySerializeTask.start();
},
#ifdef ANDROID
@ -3236,25 +3226,16 @@ SearchService.prototype = {
});
},
_batchTimer: null,
_batchCacheInvalidation: function SRCH_SVC__batchCacheInvalidation() {
let callback = {
self: this,
notify: function SRCH_SVC_batchTimerNotify(aTimer) {
LOG("_batchCacheInvalidation: Invalidating engine cache");
this.self._buildCache();
this.self._batchTimer = null;
}
};
if (!this._batchTimer) {
this._batchTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this._batchTimer.initWithCallback(callback, CACHE_INVALIDATION_DELAY,
Ci.nsITimer.TYPE_ONE_SHOT);
} else {
this._batchTimer.delay = CACHE_INVALIDATION_DELAY;
LOG("_batchCacheInvalidation: Batch timer reset");
_batchTask: null,
get batchTask() {
if (!this._batchTask) {
let task = function taskCallback() {
LOG("batchTask: Invalidating engine cache");
this._buildCache();
}.bind(this);
this._batchTask = new DeferredTask(task, CACHE_INVALIDATION_DELAY);
}
return this._batchTask;
},
_addEngineToStore: function SRCH_SVC_addEngineToStore(aEngine) {
@ -3885,7 +3866,7 @@ SearchService.prototype = {
engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
aMethod, aTemplate);
this._addEngineToStore(engine);
this._batchCacheInvalidation();
this.batchTask.start();
},
addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
@ -3948,10 +3929,10 @@ SearchService.prototype = {
engineToRemove.hidden = true;
engineToRemove.alias = null;
} else {
// Cancel the lazy serialization timer if it's running
if (engineToRemove._serializeTimer) {
engineToRemove._serializeTimer.cancel();
engineToRemove._serializeTimer = null;
// Cancel the serialized task if it's running
if (engineToRemove._lazySerializeTask) {
engineToRemove._lazySerializeTask.cancel();
engineToRemove._lazySerializeTask = null;
}
// Remove the engine file from disk (this might throw)
@ -4149,21 +4130,20 @@ SearchService.prototype = {
LOG("nsSearchService::observe: setting current");
this.currentEngine = aEngine;
}
this._batchCacheInvalidation();
this.batchTask.start();
break;
case SEARCH_ENGINE_CHANGED:
case SEARCH_ENGINE_REMOVED:
this._batchCacheInvalidation();
this.batchTask.start();
break;
}
break;
case QUIT_APPLICATION_TOPIC:
this._removeObservers();
if (this._batchTimer) {
if (this._batchTask) {
// Flush to disk immediately
this._batchTimer.cancel();
this._buildCache();
this._batchTask.flush();
}
engineMetadataService.flush();
break;

View File

@ -0,0 +1,92 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* test_serialize_file: Add test engines
*
* Ensure that :
* - File is created.
* - File size is bigger than 0.
* - lazySerializeTask updates the file.
*/
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/NetUtil.jsm");
add_test(function test_batchTask() {
let observer = function(aSubject, aTopic, aData) {
if (aTopic == "browser-search-engine-modified" && aData == "engine-loaded") {
let engine1 = Services.search.getEngineByName("Test search engine");
let engine2 = Services.search.getEngineByName("Sherlock test search engine");
if (engine1 && engine2) {
Services.obs.removeObserver(observer, aTopic);
// Test that files are written correctly.
let engineFile1 = engine1.wrappedJSObject._file;
let engineFile2 = engine2.wrappedJSObject._file;
do_check_true(engineFile1.exists());
do_check_true(engineFile2.exists());
do_check_neq(engineFile1.fileSize, 0);
do_check_neq(engineFile2.fileSize, 0);
run_next_test();
}
}
}
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
Services.search.addEngine("http://localhost:4444/data/engine.xml",
Ci.nsISearchEngine.DATA_XML, null, false);
Services.search.addEngine("http://localhost:4444/data/engine.src",
Ci.nsISearchEngine.DATA_TEXT,
"http://localhost:4444/data/ico-size-16x16-png.ico",
false);
});
add_test(function test_addParam() {
let engine = Services.search.getEngineByName("Test search engine");
engine.addParam("param-name", "param-value", null);
function readAsyncFile(aFile, aCallback) {
NetUtil.asyncFetch(aFile, function(inputStream, status) {
do_check_true(Components.isSuccessCode(status));
let data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
aCallback(data);
});
}
let observer = function(aSubject, aTopic, aData) {
// The sherlock engine file may still be updated because the icon takes
// time be loaded, therefore, the engine name is checked here.
aSubject.QueryInterface(Ci.nsIFile);
if (aTopic == "browser-search-service" &&
aData == "write-engine-to-disk-complete" &&
aSubject.leafName == "test-search-engine.xml") {
Services.obs.removeObserver(observer, aTopic);
let engineFile = engine.wrappedJSObject._file;
readAsyncFile(engineFile, function(engineData) {
do_check_true(engineData.indexOf("param-name") > 0);
run_next_test();
});
}
}
Services.obs.addObserver(observer, "browser-search-service", false);
});
function run_test() {
updateAppInfo();
let httpServer = new HttpServer();
httpServer.start(4444);
httpServer.registerDirectory("/", do_get_cwd());
do_register_cleanup(function cleanup() {
httpServer.stop(function() {});
});
run_next_test();
}

View File

@ -29,6 +29,7 @@ support-files =
[test_notifications.js]
[test_addEngine_callback.js]
[test_multipleIcons.js]
[test_serialize_file.js]
[test_async.js]
[test_sync.js]
[test_sync_fallback.js]

File diff suppressed because it is too large Load Diff

View File

@ -178,7 +178,12 @@ namespace {
{
nsTArray<nsRefPtr<Touch> > *touches =
static_cast<nsTArray<nsRefPtr<Touch> > *>(aTouchList);
touches->AppendElement(aData);
nsRefPtr<Touch> copy = new Touch(aData->mIdentifier,
aData->mRefPoint,
aData->mRadius,
aData->mRotationAngle,
aData->mForce);
touches->AppendElement(copy);
aData->mChanged = false;
return PL_DHASH_NEXT;
}
@ -441,10 +446,6 @@ MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
bool
MetroInput::ShouldDeliverInputToRecognizer()
{
// If the event is destined for chrome deliver all events to the recognizer.
if (mChromeHitTestCacheForTouch) {
return true;
}
return mRecognizerWantsEvents;
}
@ -1150,6 +1151,7 @@ MetroInput::DeliverNextQueuedTouchEvent()
// the touch block for content.
if (mContentConsumingTouch) {
mWidget->ApzContentConsumingTouch();
DispatchTouchCancel(event);
} else {
mWidget->ApzContentIgnoringTouch();
}
@ -1194,8 +1196,7 @@ MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent)
MOZ_ASSERT(aEvent);
// Send a touchcancel for each pointer id we have a corresponding start
// for. Note we can't rely on mTouches here since touchends remove points
// from it. The only time we end up in here is if the apz is consuming
// events, so this array shouldn't be very large.
// from it.
WidgetTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get());
nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
@ -1213,9 +1214,13 @@ MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent)
if (!touchEvent.touches.Length()) {
return;
}
DUMP_TOUCH_IDS("DOM(4)", &touchEvent);
mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
if (mContentConsumingTouch) {
DUMP_TOUCH_IDS("APZC(3)", &touchEvent);
mWidget->ApzReceiveInputEvent(&touchEvent);
} else {
DUMP_TOUCH_IDS("DOM(5)", &touchEvent);
mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
}
}
void