Merge fx-team to m-c.

This commit is contained in:
Ryan VanderMeulen 2014-01-13 17:37:49 -05:00
commit 212df74e79
35 changed files with 517 additions and 120 deletions

View File

@ -1102,6 +1102,9 @@ let RemoteDebugger = {
"/data/local/debugger-socket";
try {
DebuggerServer.openListener(path);
// Temporary event, until bug 942756 lands and offer a way to know
// when the server is up and running
Services.obs.notifyObservers(null, 'debugger-server-started', null);
this._running = true;
} catch (e) {
dump('Unable to start debugger server: ' + e + '\n');

View File

@ -4631,6 +4631,7 @@
role="presentation"
layer="true" />
<xul:image xbl:inherits="src=image,fadein,pinned,selected"
anonid="tab-icon-image"
class="tab-icon-image"
validate="never"
role="presentation"/>

View File

@ -52,7 +52,7 @@ add_task(function() {
ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
ok(CustomizableUI.inDefaultState, "Should start in default state.");
window.resizeTo(480, window.outerHeight);
window.resizeTo(390, window.outerHeight);
yield waitForCondition(() => navbar.hasAttribute("overflowing"));
ok(!navbar.querySelector("#search-container"), "Search container should be overflowing");
let searchbar = document.getElementById("searchbar");

View File

@ -13,7 +13,7 @@
<overlay id="editBookmarkOverlay"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<vbox id="editBookmarkPanelContent">
<vbox id="editBookmarkPanelContent" flex="1">
<broadcaster id="paneElementsBroadcaster"/>
<hbox id="editBMPanel_selectionCount" hidden="true" pack="center">
@ -107,13 +107,15 @@
</hbox>
</row>
<row align="center" id="editBMPanel_folderTreeRow" collapsed="true">
<row id="editBMPanel_folderTreeRow" collapsed="true" flex="1">
<spacer/>
<vbox flex="1">
<tree id="editBMPanel_folderTree"
flex="1"
class="placesTree"
type="places"
height="150"
minheight="150"
editable="true"
onselect="gEditItemOverlay.onFolderTreeSelect();"
hidecolumnpicker="true"

View File

@ -56,8 +56,10 @@ BrowserToolboxProcess.prototype = {
// Create a separate loader instance, so that we can be sure to receive a
// separate instance of the DebuggingServer from the rest of the devtools.
// This allows us to safely use the tools against even the actors and
// DebuggingServer itself.
// DebuggingServer itself, especially since we can mark this loader as
// invisible to the debugger (unlike the usual loader settings).
this.loader = new DevToolsLoader();
this.loader.invisibleToDebugger = true;
this.loader.main("devtools/server/main");
this.debuggerServer = this.loader.DebuggerServer;
dumpn("Created a separate loader instance for the DebuggerServer.");

View File

@ -1268,9 +1268,6 @@ function MarkupContainer(aMarkupView, aNode, aInspector) {
this._onMouseDown = this._onMouseDown.bind(this);
this.elt.addEventListener("mousedown", this._onMouseDown, false);
this._onClick = this._onClick.bind(this);
this.elt.addEventListener("click", this._onClick, false);
// Prepare the image preview tooltip data if any
this._prepareImagePreview();
}
@ -1394,16 +1391,9 @@ MarkupContainer.prototype = {
},
_onMouseDown: function(event) {
if (event.target.nodeName !== "a") {
this.hovered = false;
this.markup.navigate(this);
event.stopPropagation();
}
},
_onClick: function(event) {
let target = event.target;
// Target may be a resource link (generated by the output-parser)
if (target.nodeName === "a") {
event.stopPropagation();
event.preventDefault();
@ -1411,6 +1401,13 @@ MarkupContainer.prototype = {
.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(target.href, "tab");
}
// Or it may be the "show more nodes" button (which already has its onclick)
// Else, it's the container itself
else if (target.nodeName !== "button") {
this.hovered = false;
this.markup.navigate(this);
event.stopPropagation();
}
},
/**
@ -1544,7 +1541,11 @@ MarkupContainer.prototype = {
// Recursively destroy children containers
let firstChild;
while (firstChild = this.children.firstChild) {
firstChild.container.destroy();
// Not all children of a container are containers themselves
// ("show more nodes" button is one example)
if (firstChild.container) {
firstChild.container.destroy();
}
this.children.removeChild(firstChild);
}
@ -1553,7 +1554,6 @@ MarkupContainer.prototype = {
this.elt.removeEventListener("mouseover", this._onMouseOver, false);
this.elt.removeEventListener("mouseout", this._onMouseOut, false);
this.elt.removeEventListener("mousedown", this._onMouseDown, false);
this.elt.removeEventListener("click", this._onClick, false);
this.expander.removeEventListener("click", this._onToggle, false);
// Destroy my editor

View File

@ -6,6 +6,7 @@ support-files =
browser_inspector_markup_navigation.html
browser_inspector_markup_subset.html
browser_inspector_markup_765105_tooltip.png
browser_inspector_markup_950732.html
head.js
[browser_bug896181_css_mixed_completion_new_attribute.js]
@ -19,3 +20,4 @@ skip-if = true
[browser_inspector_markup_navigation.js]
[browser_inspector_markup_subset.js]
[browser_inspector_markup_765105_tooltip.js]
[browser_inspector_markup_950732.js]

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html class="html">
<body class="body">
<ul>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
<li>some content</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,105 @@
/* Any copyright", " is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that the markup view loads only as many nodes as specified
* by the devtools.markup.pagesize preference.
*/
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
let promise = devtools.require("sdk/core/promise");
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
// Make sure nodes are hidden when there are more than 5 in a row
registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.markup.pagesize");
});
Services.prefs.setIntPref("devtools.markup.pagesize", 5);
function test() {
waitForExplicitFinish();
let doc;
let inspector;
let markup;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(runTests, content);
}, true);
content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_950732.html";
function runTests() {
Task.spawn(function() {
yield openMarkupView();
yield selectUL();
yield reloadPage();
yield showAllNodes();
assertAllNodesAreVisible();
finishUp();
}).then(null, Cu.reportError);
}
function openMarkupView() {
let deferred = promise.defer();
var target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
inspector = toolbox.getCurrentPanel();
markup = inspector.markup;
inspector.once("inspector-updated", deferred.resolve);
});
return deferred.promise;
}
function selectUL() {
let deferred = promise.defer();
let container = getContainerForRawNode(markup, doc.querySelector("ul"));
let win = container.elt.ownerDocument.defaultView;
EventUtils.sendMouseEvent({type: "mousedown"}, container.elt, win);
inspector.once("inspector-updated", deferred.resolve);
return deferred.promise;
}
function reloadPage() {
let deferred = promise.defer();
inspector.once("new-root", () => {
doc = content.document;
markup = inspector.markup;
markup._waitForChildren().then(deferred.resolve);
});
content.location.reload();
return deferred.promise;
}
function showAllNodes() {
let container = getContainerForRawNode(markup, doc.querySelector("ul"));
let button = container.elt.querySelector("button");
let win = button.ownerDocument.defaultView;
EventUtils.sendMouseEvent({type: "click"}, button, win);
return markup._waitForChildren();
}
function assertAllNodesAreVisible() {
let ul = doc.querySelector("ul");
let container = getContainerForRawNode(markup, ul);
ok(!container.elt.querySelector("button"), "All nodes button isn't here");
is(container.children.childNodes.length, ul.children.length);
}
function finishUp() {
doc = inspector = markup = null;
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -132,7 +132,10 @@ function test() {
// Make sure that clicking the "more" button loads all the nodes.
let container = getContainerForRawNode(markup, doc.querySelector("body"));
let button = container.elt.querySelector("button");
button.click();
let win = button.ownerDocument.defaultView;
EventUtils.sendMouseEvent({type: "click"}, button, win);
markup._waitForChildren().then(() => {
assertChildren("abcdefghijklmnopqrstuvwxyz");
finishUp();

View File

@ -26,9 +26,10 @@ var StartUI = {
document.getElementById("bcast_preciseInput").setAttribute("input",
this.chromeWin.InputSourceHelper.isPrecise ? "precise" : "imprecise");
// NOTE: location.search doesn't work for about: pages
if (location.href.indexOf("?firstrun") > 0) {
let firstRunCount = Services.prefs.getIntPref("browser.firstrun.count");
if (firstRunCount > 0) {
document.loadOverlay("chrome://browser/content/FirstRunOverlay.xul", null);
Services.prefs.setIntPref("browser.firstrun.count", firstRunCount - 1);
}
this._adjustDOMforViewState(this.chromeWin.ContentAreaObserver.viewstate);

View File

@ -222,11 +222,6 @@ BrowserCLH.prototype = {
// Default to the saved homepage
let defaultURL = getHomePage();
// Show page for first run or upgrade.
if (needHomepageOverride() == "new profile") {
defaultURL = 'about:newtab?firstrun';
}
// Override the default if we have a URL passed on command line
if (uris.length > 0) {
defaultURL = uris[0].spec;

View File

@ -121,6 +121,9 @@ pref("browser.display.history.maxresults", 100);
/* max items per section of the startui */
pref("browser.display.startUI.maxresults", 16);
// Number of times to display firstrun instructions on new tab page
pref("browser.firstrun.count", 3);
// Backspace and Shift+Backspace behavior
// 0 goes Back/Forward
// 1 act like PgUp/PgDown

View File

@ -57,7 +57,8 @@
#tabs > .tabs-scrollbox > .scrollbutton-up {
list-style-image: url("images/tab-arrows.png") !important;
-moz-image-region: rect(15px 58px 63px 14px) !important;
padding-right: 13px;
padding-right: 15px;
width: @tabs_scrollarrow_width@;
}
#tabs > .tabs-scrollbox > .scrollbutton-up:hover {
-moz-image-region: rect(14px 102px 62px 58px) !important;
@ -72,7 +73,8 @@
#tabs > .tabs-scrollbox > .scrollbutton-down {
list-style-image: url("images/tab-arrows.png") !important;
-moz-image-region: rect(73px 58px 121px 14px) !important;
padding-left: 16px;
padding-left: 15px;
width: @tabs_scrollarrow_width@;
}
#tabs > .tabs-scrollbox > .scrollbutton-down:hover {
-moz-image-region: rect(72px 102px 120px 58px) !important;
@ -84,6 +86,30 @@
-moz-image-region: rect(73px 196px 121px 152px) !important;
}
.tabs-scrollbox > .scrollbutton-up:not([disabled]):not([collapsed])::after {
content: "";
visibility: visible;
display: block;
background-color: rgb(90, 91, 95);
position: absolute;
top: 0;
left: calc(@tabs_scrollarrow_width@ + @metro_spacing_normal@); /* .scrollbutton-up width + #tabs-container left padding */
width: 1px;
height: @tabs_height@;
}
.tabs-scrollbox > .scrollbutton-down:not([disabled]):not([collapsed])::before {
content: "";
visibility: visible;
display: block;
background-color: rgb(90, 91, 95);
position: absolute;
top: 0;
right: calc(@tabs_scrollarrow_width@ + @newtab_button_width@); /* .scrollbutton-down width + #newtab-button width */
width: 1px;
height: @tabs_height@;
}
#tabs-container[viewstate="snapped"] {
visibility: hidden;
}
@ -185,6 +211,7 @@ documenttab[selected] .documenttab-selection {
/* Add some extra padding for a larger target */
padding: 18px 20px 30px 20px;
width: @newtab_button_width@;
}
/* Start UI ----------------------------------------------------------------- */

View File

@ -27,6 +27,8 @@
%define toolbar_height 69px
%define labelled_toolbar_height 90px
%define tabs_height 178px
%define newtab_button_width 63px
%define tabs_scrollarrow_width 64px
%define findbar_height 54px
%define progress_height 5px

View File

@ -64,6 +64,18 @@ this.UITour = {
},
widgetName: "search-container",
}],
["selectedTabIcon", {
query: (aDocument) => {
let selectedtab = aDocument.defaultView.gBrowser.selectedTab;
let element = aDocument.getAnonymousElementByAttribute(selectedtab,
"anonid",
"tab-icon-image");
if (!element || !_isElementVisible(element)) {
return null;
}
return element;
},
}],
["urlbar", {
query: "#urlbar",
widgetName: "urlbar-container",
@ -489,6 +501,10 @@ this.UITour = {
highlighter.parentElement.openPopup(aTargetEl, "overlap", offsetX, offsetY);
}
// Prevent showing a panel at an undefined position.
if (!_isElementVisible(aTarget.node))
return;
this._setAppMenuStateForAnnotation(aTarget.node.ownerDocument.defaultView, "highlight",
this.targetIsInAppMenu(aTarget),
showHighlightPanel.bind(this, aTarget.node));
@ -527,6 +543,10 @@ this.UITour = {
tooltip.openPopup(aAnchorEl, alignment);
}
// Prevent showing a panel at an undefined position.
if (!_isElementVisible(aAnchor.node))
return;
this._setAppMenuStateForAnnotation(aAnchor.node.ownerDocument.defaultView, "info",
this.targetIsInAppMenu(aAnchor),
showInfoPanel.bind(this, aAnchor.node));
@ -615,3 +635,8 @@ this.UITour = {
aWindow.gBrowser.selectedTab = tab;
},
};
function _isElementVisible(aElement) {
let targetStyle = aElement.ownerDocument.defaultView.getComputedStyle(aElement);
return (targetStyle.display != "none" && targetStyle.visibility == "visible");
}

View File

@ -46,6 +46,7 @@
#include "nsString.h"
#include "nsAutoPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozJSComponentLoader.h"
@ -91,6 +92,21 @@ struct Paths {
* the same as homeDir.
*/
nsString desktopDir;
/**
* The user's 'application data' directory.
* Windows:
* HOME = Documents and Settings\$USER\Application Data
* UAppData = $HOME[\$vendor]\$name
*
* Unix:
* HOME = ~
* UAppData = $HOME/.[$vendor/]$name
*
* Mac:
* HOME = ~
* UAppData = $HOME/Library/Application Support/$name
*/
nsString userApplicationDataDir;
#if defined(XP_WIN)
/**
@ -123,6 +139,7 @@ struct Paths {
localProfileDir.SetIsVoid(true);
homeDir.SetIsVoid(true);
desktopDir.SetIsVoid(true);
userApplicationDataDir.SetIsVoid(true);
#if defined(XP_WIN)
winAppDataDir.SetIsVoid(true);
@ -218,7 +235,7 @@ nsresult InitOSFileConstants()
// Initialize paths->libDir
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory("XpcomLib", getter_AddRefs(file));
nsresult rv = NS_GetSpecialDirectory(NS_XPCOM_LIBRARY_FILE, getter_AddRefs(file));
if (NS_FAILED(rv)) {
return rv;
}
@ -262,6 +279,7 @@ nsresult InitOSFileConstants()
GetPathToSpecialDir(NS_OS_TEMP_DIR, paths->tmpDir);
GetPathToSpecialDir(NS_OS_HOME_DIR, paths->homeDir);
GetPathToSpecialDir(NS_OS_DESKTOP_DIR, paths->desktopDir);
GetPathToSpecialDir(XRE_USER_APP_DATA_DIR, paths->userApplicationDataDir);
#if defined(XP_WIN)
GetPathToSpecialDir(NS_WIN_APPDATA_DIR, paths->winAppDataDir);
@ -857,6 +875,10 @@ bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
return false;
}
if (!SetStringProperty(cx, objPath, "userApplicationDataDir", gPaths->userApplicationDataDir)) {
return false;
}
#if defined(XP_WIN)
if (!SetStringProperty(cx, objPath, "winAppDataDir", gPaths->winAppDataDir)) {
return false;

View File

@ -64,6 +64,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
private RenderContext mLastPageContext;
private int mMaxTextureSize;
private int mBackgroundColor;
private int mOverscrollColor;
private long mLastFrameTime;
private final CopyOnWriteArrayList<RenderTask> mTasks;
@ -134,6 +135,7 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
public LayerRenderer(LayerView view) {
mView = view;
mOverscrollColor = view.getContext().getResources().getColor(R.color.background_normal);
Bitmap scrollbarImage = view.getScrollbarImage();
IntSize size = new IntSize(scrollbarImage.getWidth(), scrollbarImage.getHeight());
@ -587,6 +589,9 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
// Draw the overscroll background area as a solid color
clear(mOverscrollColor);
// Update background color.
mBackgroundColor = mView.getBackgroundColor();

View File

@ -62,6 +62,23 @@ import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
/**
* {@code BrowserToolbar} is single entry point for users of the toolbar
* subsystem i.e. this should be the only import outside the 'toolbar'
* package.
*
* {@code BrowserToolbar} serves at the single event bus for all
* sub-components in the toolbar. It tracks tab events and gecko messages
* and update the state of its inner components accordingly.
*
* It has two states, display and edit, which are controlled by
* ToolbarEditLayout and ToolbarDisplayLayout. In display state, the toolbar
* displays the current state for the selected tab. In edit state, it shows
* a text entry for searching bookmarks/history. {@code BrowserToolbar}
* provides public API to enter, cancel, and commit the edit state as well
* as a set of listeners to allow {@code BrowserToolbar} users to react
* to state changes accordingly.
*/
public class BrowserToolbar extends GeckoRelativeLayout
implements Tabs.OnTabsChangedListener,
GeckoMenu.ActionItemBarPresenter,

View File

@ -47,17 +47,38 @@ import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
/**
* {@code ToolbarDisplayLayout} is the UI for when the toolbar is in
* display state. It's used to display the state of the currently selected
* tab. It should always be updated through a single entry point
* (updateFromTab) and should never track any tab events or gecko messages
* on its own to keep it as dumb as possible.
*
* The UI has two possible modes: progress and display which are triggered
* when UpdateFlags.PROGRESS is used depending on the current tab state.
* The progress mode is triggered when the tab is loading a page. Display mode
* is used otherwise.
*
* {@code ToolbarDisplayLayout} is meant to be owned by {@code BrowserToolbar}
* which is the main event bus for the toolbar subsystem.
*/
public class ToolbarDisplayLayout extends GeckoLinearLayout
implements Animation.AnimationListener {
private static final String LOGTAG = "GeckoToolbarDisplayLayout";
// To be used with updateFromTab() to allow the caller
// to give enough context for the requested state change.
enum UpdateFlags {
TITLE,
FAVICON,
PROGRESS,
SITE_IDENTITY,
PRIVATE_MODE,
// Disable any animation that might be
// triggered from this state change. Mostly
// used on tab switches, see BrowserToolbar.
DISABLE_ANIMATIONS
}

View File

@ -25,6 +25,12 @@ import android.view.View.OnFocusChangeListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
/**
* {@code ToolbarEditLayout} is the UI for when the toolbar is in
* edit state. It controls a text entry ({@code ToolbarEditText})
* and its matching 'go' button which changes depending on the
* current type of text in the entry.
*/
public class ToolbarEditLayout extends GeckoLinearLayout {
private final ToolbarEditText mEditText;

View File

@ -30,11 +30,20 @@ import android.view.View.OnKeyListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
/**
* {@code ToolbarEditText} is the text entry used when the toolbar
* is in edit state. It handles all the necessary input method machinery
* as well as the tracking of different text types (empty, search, or url).
* It's meant to be owned by {@code ToolbarEditLayout}.
*/
public class ToolbarEditText extends CustomEditText
implements AutocompleteHandler {
private static final String LOGTAG = "GeckoToolbarEditText";
// Used to track the current type of content in the
// text entry so that ToolbarEditLayout can update its
// state accordingly.
enum TextType {
EMPTY,
SEARCH_QUERY,

View File

@ -676,7 +676,6 @@
"layout/generic/test/test_plugin_clipping.xhtml": "Bug 931116, b2g desktop specific, initial triage",
"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",
"toolkit/devtools/apps/tests/test_webapps_actor.html": "Bug 931116, b2g desktop specific, initial triage"
"layout/generic/test/test_plugin_clipping_transformed.xhtml": "Bug 931116, b2g desktop specific, initial triage"
}
}

View File

@ -154,3 +154,7 @@ user_pref("browser.pagethumbnails.capturing_disabled", true);
// Indicate that the download panel has been shown once so that whichever
// download test runs first doesn't show the popup inconsistently.
user_pref("browser.download.panel.shown", true);
// prefs for firefox metro.
// Disable first-tun tab
user_pref("browser.firstrun.count", 0);

View File

@ -37,6 +37,8 @@ add_task(function() {
do_check_true(!!OS.Constants.Path.desktopDir);
do_check_eq(OS.Constants.Path.desktopDir, Services.dirsvc.get("Desk", Components.interfaces.nsIFile).path);
compare_paths(OS.Constants.Path.userApplicationDataDir, "UAppData");
compare_paths(OS.Constants.Path.winAppDataDir, "AppData");
compare_paths(OS.Constants.Path.winStartMenuProgsDir, "Progs");

View File

@ -35,6 +35,7 @@ let loaderGlobals = {
btoa: btoa,
console: console,
_Iterator: Iterator,
ChromeWorker: ChromeWorker,
loader: {
lazyGetter: XPCOMUtils.defineLazyGetter.bind(XPCOMUtils),
lazyImporter: XPCOMUtils.defineLazyModuleGetter.bind(XPCOMUtils),
@ -43,7 +44,8 @@ let loaderGlobals = {
};
// Used when the tools should be loaded from the Firefox package itself (the default)
var BuiltinProvider = {
function BuiltinProvider() {}
BuiltinProvider.prototype = {
load: function() {
this.loader = new loader.Loader({
modules: {
@ -72,7 +74,8 @@ var BuiltinProvider = {
// Allow access to xpcshell test items from the loader.
"xpcshell-test": "resource://test"
},
globals: loaderGlobals
globals: loaderGlobals,
invisibleToDebugger: this.invisibleToDebugger
});
return promise.resolve(undefined);
@ -87,7 +90,8 @@ var BuiltinProvider = {
// Used when the tools should be loaded from a mozilla-central checkout. In addition
// to different paths, it needs to write chrome.manifest files to override chrome urls
// from the builtin tools.
var SrcdirProvider = {
function SrcdirProvider() {}
SrcdirProvider.prototype = {
fileURI: function(path) {
let file = new FileUtils.File(path);
return Services.io.newFileURI(file).spec;
@ -134,7 +138,8 @@ var SrcdirProvider = {
"acorn": acornURI,
"acorn_loose": acornLoosseURI
},
globals: loaderGlobals
globals: loaderGlobals,
invisibleToDebugger: this.invisibleToDebugger
});
return this._writeManifest(devtoolsDir).then(null, Cu.reportError);
@ -218,12 +223,29 @@ var SrcdirProvider = {
* then a new one can also be created.
*/
this.DevToolsLoader = function DevToolsLoader() {
this._chooseProvider();
this.require = this.require.bind(this);
};
DevToolsLoader.prototype = {
get provider() {
if (!this._provider) {
this._chooseProvider();
}
return this._provider;
},
_provider: null,
/**
* A dummy version of require, in case a provider hasn't been chosen yet when
* this is first called. This will then be replaced by the real version.
* @see setProvider
*/
require: function() {
this._chooseProvider();
return this.require.apply(this, arguments);
},
/**
* Add a URI to the loader.
* @param string id
@ -234,7 +256,7 @@ DevToolsLoader.prototype = {
*/
loadURI: function(id, uri) {
let module = loader.Module(id, uri);
return loader.load(this._provider.loader, module).exports;
return loader.load(this.provider.loader, module).exports;
},
/**
@ -248,7 +270,7 @@ DevToolsLoader.prototype = {
*/
main: function(id) {
this._mainid = id;
this._main = loader.main(this._provider.loader, id);
this._main = loader.main(this.provider.loader, id);
// Mirror the main module's exports on this object.
Object.getOwnPropertyNames(this._main).forEach(key => {
@ -271,6 +293,7 @@ DevToolsLoader.prototype = {
this._provider.unload("newprovider");
}
this._provider = provider;
this._provider.invisibleToDebugger = this.invisibleToDebugger;
this._provider.load();
this.require = loader.Require(this._provider.loader, { id: "devtools" });
@ -284,9 +307,9 @@ DevToolsLoader.prototype = {
*/
_chooseProvider: function() {
if (Services.prefs.prefHasUserValue("devtools.loader.srcdir")) {
this.setProvider(SrcdirProvider);
this.setProvider(new SrcdirProvider());
} else {
this.setProvider(BuiltinProvider);
this.setProvider(new BuiltinProvider());
}
},
@ -302,6 +325,17 @@ DevToolsLoader.prototype = {
delete this._provider;
this._chooseProvider();
},
/**
* Sets whether the compartments loaded by this instance should be invisible
* to the debugger. Invisibility is needed for loaders that support debugging
* of chrome code. This is true of remote target environments, like Fennec or
* B2G. It is not the default case for desktop Firefox because we offer the
* Browser Toolbox for chrome debugging there, which uses its own, separate
* loader instance.
* @see browser/devtools/framework/ToolboxProcess.jsm
*/
invisibleToDebugger: Services.appinfo.name !== "Firefox"
};
// Export the standard instance of DevToolsLoader used by the tools.

View File

@ -14,20 +14,36 @@ const { Services } = Cu.import("resource://gre/modules/Services.jsm");
let gClient, gActor;
function connect(onDone) {
// Initialize a loopback remote protocol connection
DebuggerServer.init(function () { return true; });
// We need to register browser actors to have `listTabs` working
// and also have a root actor
if (Services.appinfo.name == "B2G") {
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
DebuggerServer.addActors("resource://gre/modules/devtools/server/actors/webapps.js");
} else {
// On b2g, we try to exercice the code that launches the production debugger server
let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
settingsService.createLock().set("devtools.debugger.remote-enabled", true, null);
// We can't use `set` callback as it is fired before shell.js code listening for this setting
// is actually called. Same thing applies to mozsettings-changed obs notification.
// So listen to a custom event until bug 942756 lands
let observer = {
observe: function (subject, topic, data) {
Services.obs.removeObserver(observer, "debugger-server-started");
let transport = debuggerSocketConnect("127.0.0.1", 6000);
startClient(transport, onDone);
}
};
Services.obs.addObserver(observer, "debugger-server-started", false);
} else {
// Initialize a loopback remote protocol connection
DebuggerServer.init(function () { return true; });
// We need to register browser actors to have `listTabs` working
// and also have a root actor
DebuggerServer.addBrowserActors();
}
let transport = DebuggerServer.connectPipe();
startClient(transport, onDone);
}
}
function startClient(transport, onDone) {
// Setup client and actor used in all tests
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient = new DebuggerClient(transport);
gClient.connect(function onConnect() {
gClient.listTabs(function onListTabs(aResponse) {
gActor = aResponse.webappsActor;

View File

@ -75,7 +75,9 @@ var steps = [
SpecialPowers.pushPrefEnv({
set: [["dom.mozBrowserFramesEnabled", true],
["security.apps.privileged.CSP.default",
"default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"]
"default-src *; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'"],
["devtools.debugger.unix-domain-socket", 6000],
["devtools.debugger.prompt-connection", false]
]
}, next);
},

View File

@ -5,10 +5,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
const Cr = Components.results;
var Ci = Components.interfaces;
var Cc = Components.classes;
var Cu = Components.utils;
var Cr = Components.results;
// On B2G scope object misbehaves and we have to bind globals to `this`
// in order to ensure theses variable to be visible in transport.js
this.Ci = Ci;
this.Cc = Cc;
this.Cu = Cu;
this.Cr = Cr;
this.EXPORTED_SYMBOLS = ["DebuggerTransport",
"DebuggerClient",

View File

@ -15,31 +15,11 @@ const Ci = Components.interfaces;
const Cc = Components.classes;
const Cu = Components.utils;
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
this.EXPORTED_SYMBOLS = ["DebuggerServer", "ActorPool"];
var loadSubScript =
"function loadSubScript(aURL)\n" +
"{\n" +
"const Ci = Components.interfaces;\n" +
"const Cc = Components.classes;\n" +
" try {\n" +
" let loader = Cc[\"@mozilla.org/moz/jssubscript-loader;1\"]\n" +
" .getService(Ci.mozIJSSubScriptLoader);\n" +
" loader.loadSubScript(aURL, this);\n" +
" } catch(e) {\n" +
" dump(\"Error loading: \" + aURL + \": \" + e + \" - \" + e.stack + \"\\n\");\n" +
" throw e;\n" +
" }\n" +
"}";
let server = devtools.require("devtools/server/main");
// Load the debugging server in a sandbox with its own compartment.
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
var gGlobal = Cu.Sandbox(systemPrincipal);
gGlobal.ChromeWorker = ChromeWorker;
Cu.evalInSandbox(loadSubScript, gGlobal, "1.8");
gGlobal.loadSubScript("resource://gre/modules/devtools/server/main.js");
this.DebuggerServer = gGlobal.DebuggerServer;
this.ActorPool = gGlobal.ActorPool;
this.DebuggerServer = server.DebuggerServer;
this.ActorPool = server.ActorPool;

View File

@ -10,36 +10,22 @@
* debugging global.
*/
// |this.require| is used to test if this file was loaded via the devtools
// loader (as it is in DebuggerProcess.jsm) or via loadSubScript (as it is from
// dbg-server.jsm). Note that testing |require| is not safe in either
// situation, as it causes a ReferenceError.
var Ci, Cc, CC, Cu, Cr, Components;
if (this.require) {
({ Ci, Cc, CC, Cu, Cr, components: Components }) = require("chrome");
} else {
({
interfaces: Ci,
classes: Cc,
Constructor: CC,
utils: Cu,
results: Cr
}) = Components;
}
// On B2G, if |this.require| is undefined at this point, it remains undefined
// later on when |DebuggerServer.registerModule| is called. On desktop (and
// perhaps other places), if |this.require| starts out undefined, it ends up
// being set to some native code by the time we get to |registerModule|. Here
// we perform a test early on, and then cache the correct require function for
// later use.
var localRequire;
if (this.require) {
localRequire = id => require(id);
} else {
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
localRequire = id => devtools.require(id);
}
// Until all Debugger server code is converted to SDK modules,
// imports Components.* alias from chrome module.
var { Ci, Cc, CC, Cu, Cr } = require("chrome");
// On B2G, `this` != Global scope, so `Ci` won't be binded on `this`
// (i.e. this.Ci is undefined) Then later, when using loadSubScript,
// Ci,... won't be defined for sub scripts.
this.Ci = Ci;
this.Cc = Cc;
this.CC = CC;
this.Cu = Cu;
this.Cr = Cr;
// Overload `Components` to prevent SDK loader exception on Components
// object usage
Object.defineProperty(this, "Components", {
get: function () require("chrome").components
});
const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
@ -49,7 +35,6 @@ Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let wantLogging = Services.prefs.getBoolPref("devtools.debugger.log");
const promptConnections = Services.prefs.getBoolPref("devtools.debugger.prompt-connection");
Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
@ -68,10 +53,12 @@ function loadSubScript(aURL)
}
}
let loaderRequire = this.require;
this.require = null;
loadSubScript.call(this, "resource://gre/modules/commonjs/sdk/core/promise.js");
this.require = loaderRequire;
let {defer, resolve, reject, promised, all} = require("sdk/core/promise");
this.defer = defer;
this.resolve = resolve;
this.reject = reject;
this.promised = promised;
this.all = all;
Cu.import("resource://gre/modules/devtools/SourceMap.jsm");
@ -82,12 +69,14 @@ function dumpn(str) {
dump("DBG-SERVER: " + str + "\n");
}
}
this.dumpn = dumpn;
function dbg_assert(cond, e) {
if (!cond) {
return e;
}
}
this.dbg_assert = dbg_assert;
loadSubScript.call(this, "resource://gre/modules/devtools/server/transport.js");
@ -324,7 +313,7 @@ var DebuggerServer = {
}
let moduleAPI = ModuleAPI();
let mod = localRequire(id);
let mod = require(id);
mod.register(moduleAPI);
gRegisteredModules[id] = { module: mod, api: moduleAPI };
},
@ -523,7 +512,7 @@ var DebuggerServer = {
onSocketAccepted:
makeInfallible(function DS_onSocketAccepted(aSocket, aTransport) {
if (promptConnections && !this._allowConnection()) {
if (Services.prefs.getBoolPref("devtools.debugger.prompt-connection") && !this._allowConnection()) {
return;
}
dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
@ -690,6 +679,8 @@ var DebuggerServer = {
if (this.exports) {
exports.DebuggerServer = DebuggerServer;
}
// Needed on B2G (See header note)
this.DebuggerServer = DebuggerServer;
/**
* Construct an ActorPool.
@ -708,6 +699,8 @@ function ActorPool(aConnection)
if (this.exports) {
exports.ActorPool = ActorPool;
}
// Needed on B2G (See header note)
this.ActorPool = ActorPool;
ActorPool.prototype = {
/**

View File

@ -37,11 +37,13 @@
const { BuiltinProvider, SrcdirProvider } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
BuiltinProvider.load();
SrcdirProvider.load();
let builtin = new BuiltinProvider();
builtin.load();
let srcdir = new SrcdirProvider();
srcdir.load();
is(BuiltinProvider.loader.mapping.length,
SrcdirProvider.loader.mapping.length + 1,
is(builtin.loader.mapping.length,
srcdir.loader.mapping.length + 1,
"The built-in loader should have only one more mapping for testing.");
Services.prefs.clearUserPref(SRCDIR_PREF);

View File

@ -0,0 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { DevToolsLoader } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
/**
* Ensure that each instance of the Dev Tools loader contains its own loader
* instance, and also returns unique objects. This ensures there is no sharing
* in place between loaders.
*/
function run_test() {
let loader1 = new DevToolsLoader();
let loader2 = new DevToolsLoader();
let color1 = loader1.require("devtools/css-color");
let color2 = loader2.require("devtools/css-color");
do_check_true(color1 !== color2);
do_check_true(loader1._provider !== loader2._provider);
do_check_true(loader1._provider.loader !== loader2._provider.loader);
}

View File

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const { DevToolsLoader } =
Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
const COLOR_URI = "resource://gre/modules/devtools/css-color.js";
/**
* Ensure that sandboxes created via the Dev Tools loader respect the
* invisibleToDebugger flag.
*/
function run_test() {
visible_loader();
invisible_loader();
}
function visible_loader() {
let loader = new DevToolsLoader();
loader.invisibleToDebugger = false;
loader.require("devtools/css-color");
let dbg = new Debugger();
let sandbox = loader._provider.loader.sandboxes[COLOR_URI];
try {
dbg.addDebuggee(sandbox);
do_check_true(true);
} catch(e) {
do_throw("debugger could not add visible value");
}
}
function invisible_loader() {
let loader = new DevToolsLoader();
loader.invisibleToDebugger = true;
loader.require("devtools/css-color");
let dbg = new Debugger();
let sandbox = loader._provider.loader.sandboxes[COLOR_URI];
try {
dbg.addDebuggee(sandbox);
do_throw("debugger added invisible value");
} catch(e) {
do_check_true(true);
}
}

View File

@ -2,5 +2,7 @@
head = head_devtools.js
tail =
[test_independent_loaders.js]
[test_invisible_loader.js]
[test_safeErrorString.js]
[test_defineLazyPrototypeGetter.js]