merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2013-09-11 13:50:28 +02:00
commit d4f6be647f
49 changed files with 1151 additions and 445 deletions

View File

@ -1,4 +1,4 @@
{
"revision": "3014a433e3b78fa04aacb919c853fa220d309d70",
"revision": "e3d925b497f5b996c9c397c200805fbcc62bc823",
"repo_path": "/integration/gaia-central"
}

View File

@ -3,13 +3,11 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
browser.jar:
* content/browser/preferences/in-content/preferences.js
content/browser/preferences/in-content/preferences.js
content/browser/preferences/in-content/landing.xul
* content/browser/preferences/in-content/preferences.xul
* content/browser/preferences/in-content/main.xul
content/browser/preferences/in-content/main.js
* content/browser/preferences/in-content/tabs.xul
* content/browser/preferences/in-content/tabs.js
* content/browser/preferences/in-content/main.js
content/browser/preferences/in-content/privacy.xul
* content/browser/preferences/in-content/privacy.js
* content/browser/preferences/in-content/advanced.xul

View File

@ -13,11 +13,7 @@
<label class="landingButton-label">&paneGeneral.title;</label>
</button>
<button label="&paneTabs.title;" class="landingButton"
oncommand="gotoPref('paneTabs');">
<image class="landingButton-icon" type="tabs"/>
<label class="landingButton-label">&paneTabs.title;</label>
</button>
<button label="&paneContent.title;" class="landingButton"
oncommand="gotoPref('paneContent');">

View File

@ -6,15 +6,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm");
var gMainPane = {
_pane: null,
/**
* Initialization of this.
*/
init: function ()
{
this._pane = document.getElementById("paneMain");
// set up the "use current page" label-changing listener
this._updateUseCurrentButton();
window.addEventListener("focus", this._updateUseCurrentButton.bind(this), false);
@ -27,6 +23,20 @@ var gMainPane = {
Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService)
.notifyObservers(window, "main-pane-loaded", null);
//Functionality for "Show tabs in taskbar" on Win XP
#ifdef XP_WIN
try {
let sysInfo = Cc["@mozilla.org/system-info;1"].
getService(Ci.nsIPropertyBag2);
let ver = parseFloat(sysInfo.getProperty("version"));
let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
showTabsInTaskbar.hidden = ver < 6.1 || (ver >= 6.1 && history.state != "tabs");
} catch (ex) {}
#endif
},
setupDownloadsWindowOptions: function ()
@ -475,5 +485,28 @@ var gMainPane = {
option.removeAttribute("disabled");
startupPref.updateElements(); // select the correct index in the startup menulist
}
},
// TABS
/**
* Determines where a link which opens a new window will open.
*
* @returns |true| if such links should be opened in new tabs
*/
readLinkTarget: function() {
var openNewWindow = document.getElementById("browser.link.open_newwindow");
return openNewWindow.value != 2;
},
/**
* Determines where a link which opens a new window will open.
*
* @returns 2 if such links should be opened in new windows,
* 3 if such links should be opened in new tabs
*/
writeLinkTarget: function() {
var linkTargeting = document.getElementById("linkTargeting");
return linkTargeting.checked ? 3 : 2;
}
};

View File

@ -49,6 +49,47 @@
name="browser.download.folderList"
type="int"/>
<!-- Tab preferences
Preferences:
browser.link.open_newwindow
1 opens such links in the most recent window or tab,
2 opens such links in a new window,
3 opens such links in a new tab
browser.tabs.loadInBackground
- true if display should switch to a new tab which has been opened from a
link, false if display shouldn't switch
browser.tabs.warnOnClose
- true if when closing a window with multiple tabs the user is warned and
allowed to cancel the action, false to just close the window
browser.tabs.warnOnOpen
- true if the user should be warned if he attempts to open a lot of tabs at
once (e.g. a large folder of bookmarks), false otherwise
browser.taskbar.previews.enable
- true if tabs are to be shown in the Windows 7 taskbar
-->
<preference id="browser.link.open_newwindow"
name="browser.link.open_newwindow"
type="int"/>
<preference id="browser.tabs.loadInBackground"
name="browser.tabs.loadInBackground"
type="bool"
inverted="true"/>
<preference id="browser.tabs.warnOnClose"
name="browser.tabs.warnOnClose"
type="bool"/>
<preference id="browser.tabs.warnOnOpen"
name="browser.tabs.warnOnOpen"
type="bool"/>
<preference id="browser.sessionstore.restore_on_demand"
name="browser.sessionstore.restore_on_demand"
type="bool"/>
#ifdef XP_WIN
<preference id="browser.taskbar.previews.enable"
name="browser.taskbar.previews.enable"
type="bool"/>
#endif
</preferences>
<hbox class="heading" data-category="paneGeneral" hidden="true">
@ -166,3 +207,42 @@
accesskey="&alwaysAsk.accesskey;"/>
</radiogroup>
</groupbox>
<!-- Tab preferences -->
<groupbox data-category="paneGeneral" hidden="true">
<caption label="&tabsGroup.label;"/>
<checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
accesskey="&newWindowsAsTabs.accesskey;"
preference="browser.link.open_newwindow"
onsyncfrompreference="return gMainPane.readLinkTarget();"
onsynctopreference="return gMainPane.writeLinkTarget();"
class="indent"/>
<checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
accesskey="&warnCloseMultipleTabs.accesskey;"
preference="browser.tabs.warnOnClose"
class="indent"/>
<checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
accesskey="&warnOpenManyTabs.accesskey;"
preference="browser.tabs.warnOnOpen"
class="indent"/>
<checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
accesskey="&restoreTabsOnDemand.accesskey;"
preference="browser.sessionstore.restore_on_demand"
class="indent"/>
<checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
accesskey="&switchToNewTabs.accesskey;"
preference="browser.tabs.loadInBackground"
class="indent"/>
#ifdef XP_WIN
<checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
accesskey="&showTabsInTaskbar.accesskey;"
preference="browser.taskbar.previews.enable"
class="indent"/>
#endif
</groupbox>

View File

@ -18,9 +18,6 @@ function init_all() {
window.addEventListener("popstate", onStatePopped, true);
updateCommands();
gMainPane.init();
#ifdef XP_WIN
gTabsPane.init();
#endif
gPrivacyPane.init();
gAdvancedPane.init();
gApplicationsPane.init();

View File

@ -91,7 +91,6 @@
<prefpane flex="1" id="mainPrefPane">
#include landing.xul
#include main.xul
#include tabs.xul
#include privacy.xul
#include advanced.xul
#include applications.xul

View File

@ -1,63 +0,0 @@
/* 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/. */
var gTabsPane = {
/*
* Preferences:
*
* browser.link.open_newwindow
* - determines where pages which would open in a new window are opened:
* 1 opens such links in the most recent window or tab,
* 2 opens such links in a new window,
* 3 opens such links in a new tab
* browser.tabs.loadInBackground
* - true if display should switch to a new tab which has been opened from a
* link, false if display shouldn't switch
* browser.tabs.warnOnClose
* - true if when closing a window with multiple tabs the user is warned and
* allowed to cancel the action, false to just close the window
* browser.tabs.warnOnOpen
* - true if the user should be warned if he attempts to open a lot of tabs at
* once (e.g. a large folder of bookmarks), false otherwise
* browser.taskbar.previews.enable
* - true if tabs are to be shown in the Windows 7 taskbar
*/
#ifdef XP_WIN
/**
* Initialize any platform-specific UI.
*/
init: function () {
try {
let sysInfo = Cc["@mozilla.org/system-info;1"].
getService(Ci.nsIPropertyBag2);
let ver = parseFloat(sysInfo.getProperty("version"));
let showTabsInTaskbar = document.getElementById("showTabsInTaskbar");
showTabsInTaskbar.hidden = ver < 6.1 || (ver >= 6.1 && history.state != "tabs");
} catch (ex) {}
},
#endif
/**
* Determines where a link which opens a new window will open.
*
* @returns |true| if such links should be opened in new tabs
*/
readLinkTarget: function() {
var openNewWindow = document.getElementById("browser.link.open_newwindow");
return openNewWindow.value != 2;
},
/**
* Determines where a link which opens a new window will open.
*
* @returns 2 if such links should be opened in new windows,
* 3 if such links should be opened in new tabs
*/
writeLinkTarget: function() {
var linkTargeting = document.getElementById("linkTargeting");
return linkTargeting.checked ? 3 : 2;
}
};

View File

@ -1,76 +0,0 @@
<!-- 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/. -->
<script type="application/javascript"
src="chrome://browser/content/preferences/in-content/tabs.js"/>
<preferences id="tabsPreferences">
<preference id="browser.link.open_newwindow"
name="browser.link.open_newwindow"
type="int"/>
<preference id="browser.tabs.loadInBackground"
name="browser.tabs.loadInBackground"
type="bool"
inverted="true"/>
<preference id="browser.tabs.warnOnClose"
name="browser.tabs.warnOnClose"
type="bool"/>
<preference id="browser.tabs.warnOnOpen"
name="browser.tabs.warnOnOpen"
type="bool"/>
<preference id="browser.sessionstore.restore_on_demand"
name="browser.sessionstore.restore_on_demand"
type="bool"/>
#ifdef XP_WIN
<preference id="browser.taskbar.previews.enable"
name="browser.taskbar.previews.enable"
type="bool"/>
#endif
</preferences>
<hbox class="heading" data-category="paneTabs" hidden="true">
<image class="preference-icon" type="tabs"/>
<html:h1>&paneTabs.title;</html:h1>
</hbox>
<checkbox id="linkTargeting" label="&newWindowsAsTabs.label;"
data-category="paneTabs" hidden="true"
accesskey="&newWindowsAsTabs.accesskey;"
preference="browser.link.open_newwindow"
onsyncfrompreference="return gTabsPane.readLinkTarget();"
onsynctopreference="return gTabsPane.writeLinkTarget();"
class="indent"/>
<checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
data-category="paneTabs" hidden="true"
accesskey="&warnCloseMultipleTabs.accesskey;"
preference="browser.tabs.warnOnClose"
class="indent"/>
<checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
data-category="paneTabs" hidden="true"
accesskey="&warnOpenManyTabs.accesskey;"
preference="browser.tabs.warnOnOpen"
class="indent"/>
<checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
data-category="paneTabs" hidden="true"
accesskey="&restoreTabsOnDemand.accesskey;"
preference="browser.sessionstore.restore_on_demand"
class="indent"/>
<checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
data-category="paneTabs" hidden="true"
accesskey="&switchToNewTabs.accesskey;"
preference="browser.tabs.loadInBackground"
class="indent"/>
#ifdef XP_WIN
<checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
data-category="paneTabs" hidden="true"
accesskey="&showTabsInTaskbar.accesskey;"
preference="browser.taskbar.previews.enable"
class="indent"/>
#endif

View File

@ -765,6 +765,7 @@ bin/libfreebl_32int64_3.so
@BINPATH@/webapprt/modules/Startup.jsm
@BINPATH@/webapprt/modules/WebappRT.jsm
@BINPATH@/webapprt/modules/WebappsHandler.jsm
@BINPATH@/webapprt/modules/RemoteDebugger.jsm
#endif
#ifdef MOZ_METRO

View File

@ -19,3 +19,4 @@
<!ENTITY showTabsInTaskbar.label "Show tab previews in the Windows taskbar">
<!ENTITY showTabsInTaskbar.accesskey "k">
<!ENTITY tabsGroup.label "Tabs">

View File

@ -67,9 +67,6 @@
<method name="_clearFormatting">
<body>
<![CDATA[
if (!this._mayFormat)
return;
let controller = this.editor.selectionController;
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
@ -417,6 +414,7 @@
switch (aData) {
case "browser.urlbar.formatting.enabled":
this._mayFormat = Services.prefs.getBoolPref(aData);
if (!this._mayFormat) this._clearFormatting();
break;
case "browser.urlbar.trimURLs":
this._mayTrimURLs = Services.prefs.getBoolPref(aData);
@ -433,6 +431,12 @@
<handlers>
<!-- Entering editing mode -->
<handler event="focus" phase="capturing">
<![CDATA[
this.beginEditing();
]]>
</handler>
<handler event="input" phase="capturing">
<![CDATA[
// Ensures that paste-and-go actually brings the URL bar into editing mode

View File

@ -36,6 +36,8 @@ MOCHITEST_METRO_FILES = \
browser_tilegrid.xul \
browser_topsites.js \
browser_urlbar.js \
browser_urlbar_highlightURLs.js \
browser_urlbar_trimURLs.js \
$(NULL)
ifndef MOZ_DEBUG

View File

@ -0,0 +1,171 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const kHighlightPref = "browser.urlbar.formatting.enabled";
var gHighlightPrefValue;
function test() {
runTests();
}
function setUp() {
gHighlightPrefValue = SpecialPowers.getBoolPref(kHighlightPref);
SpecialPowers.setBoolPref(kHighlightPref, true);
yield addTab("about:blank");
}
function tearDown() {
SpecialPowers.setBoolPref(kHighlightPref, gHighlightPrefValue);
Browser.closeTab(Browser.selectedTab, { forceClose: true });
}
function testHighlight(aExpected) {
let urlbar = BrowserUI._edit;
urlbar.value = aExpected.replace(/[<>]/g, "");
let selectionController = urlbar.editor.selectionController;
let selection = selectionController.getSelection(selectionController.SELECTION_URLSECONDARY);
let value = urlbar.editor.rootElement.textContent;
let result = "";
for (let i = 0; i < selection.rangeCount; i++) {
let range = selection.getRangeAt(i).toString();
let pos = value.indexOf(range);
result += value.substring(0, pos) + "<" + range + ">";
value = value.substring(pos + range.length);
}
result += value;
is(result, aExpected, "test highight");
}
gTests.push({
desc: "Domain-based URIs (not in editing mode)",
setUp: setUp,
tearDown: tearDown,
run: function () {
let testcases = [
"<https://>mozilla.org",
"<https://>mözilla.org",
"<https://>mozilla.imaginatory",
"<https://www.>mozilla.org",
"<https://sub.>mozilla.org",
"<https://sub1.sub2.sub3.>mozilla.org",
"<www.>mozilla.org",
"<sub.>mozilla.org",
"<sub1.sub2.sub3.>mozilla.org",
"<http://ftp.>mozilla.org",
"<ftp://ftp.>mozilla.org",
"<https://sub.>mozilla.org",
"<https://sub1.sub2.sub3.>mozilla.org",
"<https://user:pass@sub1.sub2.sub3.>mozilla.org",
"<https://user:pass@>mozilla.org",
"<https://>mozilla.org</file.ext>",
"<https://>mozilla.org</sub/file.ext>",
"<https://>mozilla.org</sub/file.ext?foo>",
"<https://>mozilla.org</sub/file.ext?foo&bar>",
"<https://>mozilla.org</sub/file.ext?foo&bar#top>",
"<https://>mozilla.org</sub/file.ext?foo&bar#top>",
"<https://sub.>mozilla.org<:666/file.ext>",
"<sub.>mozilla.org<:666/file.ext>",
"localhost<:666/file.ext>",
"mailto:admin@mozilla.org",
"gopher://mozilla.org/",
"about:config",
"jar:http://mozilla.org/example.jar!/",
"view-source:http://mozilla.org/",
"foo9://mozilla.org/",
"foo+://mozilla.org/",
"foo.://mozilla.org/",
"foo-://mozilla.org/"
];
testcases.forEach(testHighlight);
}
});
gTests.push({
desc: "IP-based URIs (not in editing mode)",
setUp: setUp,
tearDown: tearDown,
run: function () {
let ips = [
"192.168.1.1",
"[::]",
"[::1]",
"[1::]",
"[::]",
"[::1]",
"[1::]",
"[1:2:3:4:5:6:7::]",
"[::1:2:3:4:5:6:7]",
"[1:2:a:B:c:D:e:F]",
"[1::8]",
"[1:2::8]",
"[fe80::222:19ff:fe11:8c76]",
"[0000:0123:4567:89AB:CDEF:abcd:ef00:0000]",
"[::192.168.1.1]",
"[1::0.0.0.0]",
"[1:2::255.255.255.255]",
"[1:2:3::255.255.255.255]",
"[1:2:3:4::255.255.255.255]",
"[1:2:3:4:5::255.255.255.255]",
"[1:2:3:4:5:6:255.255.255.255]"
];
let formats = [
"{ip}</file.ext>",
"{ip}<:666/file.ext>",
"<https://>{ip}",
"<https://>{ip}</file.ext>",
"<https://user:pass@>{ip}<:666/file.ext>",
"<http://user:pass@>{ip}<:666/file.ext>"
];
function testHighlightAllFormats(aIP) {
formats.forEach((aFormat) => testHighlight(aFormat.replace("{ip}", aIP)));
}
ips.forEach(testHighlightAllFormats);
}
});
gTests.push({
desc: "no highlighting (in editing mode)",
setUp: setUp,
tearDown: tearDown,
run: function () {
testHighlight("<https://>mozilla.org");
BrowserUI._edit.focus();
testHighlight("https://mozilla.org");
Browser.selectedBrowser.focus();
testHighlight("<https://>mozilla.org");
}
});
gTests.push({
desc: "no higlighting (pref disabled)",
setUp: setUp,
tearDown: tearDown,
run: function () {
testHighlight("<https://>mozilla.org");
SpecialPowers.setBoolPref(kHighlightPref, false);
testHighlight("https://mozilla.org");
SpecialPowers.setBoolPref(kHighlightPref, true);
testHighlight("<https://>mozilla.org");
}
});

View File

@ -0,0 +1,141 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const kTrimPref = "browser.urlbar.trimURLs";
var gTrimPrefValue;
function test() {
runTests();
}
function setUp() {
gTrimPrefValue = SpecialPowers.getBoolPref(kTrimPref);
SpecialPowers.setBoolPref(kTrimPref, true);
yield addTab("about:blank");
}
function tearDown() {
SpecialPowers.setBoolPref(kTrimPref, gTrimPrefValue);
Browser.closeTab(Browser.selectedTab, { forceClose: true });
}
function testTrim(aOriginal, aTarget) {
let urlbar = BrowserUI._edit;
urlbar.value = aOriginal;
urlbar.valueIsTyped = false;
is(urlbar.value, aTarget || aOriginal, "url bar value set");
}
gTests.push({
desc: "URIs - trimming (pref enabled)",
setUp: setUp,
tearDown: tearDown,
run: function () {
let testcases = [
["http://mozilla.org/", "mozilla.org"],
["https://mozilla.org/", "https://mozilla.org"],
["http://mözilla.org/", "mözilla.org"],
["http://mozilla.imaginatory/", "mozilla.imaginatory"],
["http://www.mozilla.org/", "www.mozilla.org"],
["http://sub.mozilla.org/", "sub.mozilla.org"],
["http://sub1.sub2.sub3.mozilla.org/", "sub1.sub2.sub3.mozilla.org"],
["http://mozilla.org/file.ext", "mozilla.org/file.ext"],
["http://mozilla.org/sub/", "mozilla.org/sub/"],
["http://ftp.mozilla.org/", "http://ftp.mozilla.org"],
["http://ftp1.mozilla.org/", "http://ftp1.mozilla.org"],
["http://ftp42.mozilla.org/", "http://ftp42.mozilla.org"],
["http://ftpx.mozilla.org/", "ftpx.mozilla.org"],
["ftp://ftp.mozilla.org/", "ftp://ftp.mozilla.org"],
["ftp://ftp1.mozilla.org/", "ftp://ftp1.mozilla.org"],
["ftp://ftp42.mozilla.org/", "ftp://ftp42.mozilla.org"],
["ftp://ftpx.mozilla.org/", "ftp://ftpx.mozilla.org"],
["https://user:pass@mozilla.org/", "https://user:pass@mozilla.org"],
["http://user:pass@mozilla.org/", "http://user:pass@mozilla.org"],
["http://sub.mozilla.org:666/", "sub.mozilla.org:666"],
["https://[fe80::222:19ff:fe11:8c76]/file.ext"],
["http://[fe80::222:19ff:fe11:8c76]/", "[fe80::222:19ff:fe11:8c76]"],
["https://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"],
["http://user:pass@[fe80::222:19ff:fe11:8c76]:666/file.ext"],
["mailto:admin@mozilla.org"],
["gopher://mozilla.org/"],
["about:config"],
["jar:http://mozilla.org/example.jar!/"],
["view-source:http://mozilla.org/"]
];
for (let [original, target] of testcases)
testTrim(original, target);
}
});
gTests.push({
desc: "URIs - no trimming (pref disabled)",
setUp: setUp,
tearDown: tearDown,
run: function () {
SpecialPowers.setBoolPref(kTrimPref, false);
testTrim("http://mozilla.org/");
SpecialPowers.setBoolPref(kTrimPref, true);
testTrim("http://mozilla.org/", "mozilla.org");
}
});
gTests.push({
desc: "Loaded URI - copy/paste behavior",
setUp: setUp,
tearDown: tearDown,
run: function () {
let urlbar = BrowserUI._edit;
BrowserUI.goToURI("http://example.com/");
let pageLoaded = yield waitForCondition(
() => Browser.selectedBrowser.currentURI.spec == "http://example.com/");
ok(pageLoaded, "expected page should have loaded");
is(urlbar.value, "example.com", "trimmed value set");
yield showNavBar();
function clipboardCondition(aExpected) {
return () => aExpected == SpecialPowers.getClipboardData("text/unicode");
}
// Value set by browser -- should copy entire url (w/ scheme) on full select
urlbar.focus();
urlbar.select();
CommandUpdater.doCommand("cmd_copy");
let copy = yield waitForCondition(clipboardCondition("http://example.com/"));
ok(copy, "should copy entire url (w/ scheme) on full select");
// Value set by browser -- should copy selected text on partial select
urlbar.focus();
urlbar.select();
urlbar.selectionStart = 2;
CommandUpdater.doCommand("cmd_copy");
copy = yield waitForCondition(clipboardCondition("ample.com"));
ok(copy, "should copy selected text on partial select");
// Value set by user -- should not copy full string
urlbar.valueIsTyped = true;
urlbar.focus();
urlbar.select();
CommandUpdater.doCommand("cmd_copy");
copy = yield waitForCondition(clipboardCondition("example.com"));
ok(copy, "should not copy full string");
}
});

View File

@ -49,11 +49,6 @@
background-position: 0 0;
}
.preference-icon[type="tabs"],
.landingButton-icon[type="tabs"] {
background-position: -32px 0;
}
.preference-icon[type="content"],
.landingButton-icon[type="content"] {
background-position: -64px 0;

View File

@ -52,11 +52,6 @@
background-position: 0 0;
}
.preference-icon[type="tabs"],
.landingButton-icon[type="tabs"] {
background-position: -32px 0;
}
.preference-icon[type="content"],
.landingButton-icon[type="content"] {
background-position: -64px 0;

View File

@ -52,11 +52,6 @@
background-position: 0 0;
}
.preference-icon[type="tabs"],
.landingButton-icon[type="tabs"] {
background-position: -32px 0;
}
.preference-icon[type="content"],
.landingButton-icon[type="content"] {
background-position: -64px 0;

View File

@ -606,7 +606,7 @@ NetworkManager.prototype = {
dns2_str: this.active.dns2
};
this.worker.postMessage(options);
this.setNetworkProxy();
this.setNetworkProxy(this.active);
},
removeDefaultRoute: function removeDefaultRoute(ifname) {
@ -689,9 +689,9 @@ NetworkManager.prototype = {
this.worker.postMessage(options);
},
setNetworkProxy: function setNetworkProxy() {
setNetworkProxy: function setNetworkProxy(network) {
try {
if (!this.active.httpProxyHost || this.active.httpProxyHost == "") {
if (!network.httpProxyHost || network.httpProxyHost == "") {
// Sets direct connection to internet.
Services.prefs.clearUserPref("network.proxy.type");
Services.prefs.clearUserPref("network.proxy.share_proxy_settings");
@ -699,23 +699,23 @@ NetworkManager.prototype = {
Services.prefs.clearUserPref("network.proxy.http_port");
Services.prefs.clearUserPref("network.proxy.ssl");
Services.prefs.clearUserPref("network.proxy.ssl_port");
debug("No proxy support for " + this.active.name + " network interface.");
debug("No proxy support for " + network.name + " network interface.");
return;
}
debug("Going to set proxy settings for " + this.active.name + " network interface.");
debug("Going to set proxy settings for " + network.name + " network interface.");
// Sets manual proxy configuration.
Services.prefs.setIntPref("network.proxy.type", MANUAL_PROXY_CONFIGURATION);
// Do not use this proxy server for all protocols.
Services.prefs.setBoolPref("network.proxy.share_proxy_settings", false);
Services.prefs.setCharPref("network.proxy.http", this.active.httpProxyHost);
Services.prefs.setCharPref("network.proxy.ssl", this.active.httpProxyHost);
let port = this.active.httpProxyPort == "" ? 8080 : this.active.httpProxyPort;
Services.prefs.setCharPref("network.proxy.http", network.httpProxyHost);
Services.prefs.setCharPref("network.proxy.ssl", network.httpProxyHost);
let port = network.httpProxyPort == 0 ? 8080 : network.httpProxyPort;
Services.prefs.setIntPref("network.proxy.http_port", port);
Services.prefs.setIntPref("network.proxy.ssl_port", port);
} catch (ex) {
debug("Exception " + ex + ". Unable to set proxy setting for "
+ this.active.name + " network interface.");
+ network.name + " network interface.");
return;
}
},

View File

@ -119,7 +119,7 @@ interface nsIWifiOperationModeCallback : nsISupports
/**
* Manage network interfaces.
*/
[scriptable, uuid(5b22c620-f8b9-11e2-b778-0800200c9a66)]
[scriptable, uuid(fad3fb08-664f-48e3-bba3-423186988c61)]
interface nsINetworkManager : nsISupports
{
/**
@ -231,4 +231,12 @@ interface nsINetworkManager : nsISupports
* Callback to notify Wifi firmware reload result.
*/
void setWifiOperationMode(in DOMString interfaceName, in DOMString mode, in nsIWifiOperationModeCallback callback);
/**
* Set http proxy for specific network
*
* @param network
* Network interface to register.
*/
void setNetworkProxy(in nsINetworkInterface network);
};

View File

@ -87,6 +87,7 @@ DOMWifiManager.prototype = {
"WifiManager:forget:Return:OK", "WifiManager:forget:Return:NO",
"WifiManager:wps:Return:OK", "WifiManager:wps:Return:NO",
"WifiManager:setPowerSavingMode:Return:OK", "WifiManager:setPowerSavingMode:Return:NO",
"WifiManager:setHttpProxy:Return:OK", "WifiManager:setHttpProxy:Return:NO",
"WifiManager:setStaticIpMode:Return:OK", "WifiManager:setStaticIpMode:Return:NO",
"WifiManager:wifiDown", "WifiManager:wifiUp",
"WifiManager:onconnecting", "WifiManager:onassociate",
@ -194,6 +195,16 @@ DOMWifiManager.prototype = {
Services.DOMRequest.fireError(request, msg.data);
break;
case "WifiManager:setHttpProxy:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
break;
case "WifiManager:setHttpProxy:Return:NO":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireError(request, msg.data);
break;
case "WifiManager:setStaticIpMode:Return:OK":
request = this.takeRequest(msg.rid);
Services.DOMRequest.fireSuccess(request, exposeReadOnly(msg.data));
@ -360,6 +371,14 @@ DOMWifiManager.prototype = {
return request;
},
setHttpProxy: function nsIDOMWifiManager_setHttpProxy(network, info) {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);
var request = this.createRequest();
this._sendMessageForRequest("WifiManager:setHttpProxy", {network:network, info:info}, request);
return request;
},
setStaticIpMode: function nsIDOMWifiManager_setStaticIpMode(network, info) {
if (!this._hasPrivileges)
throw new Components.Exception("Denied", Cr.NS_ERROR_FAILURE);

View File

@ -647,6 +647,48 @@ var WifiManager = (function() {
});
}
var httpProxyConfig = Object.create(null);
/**
* Given a network, configure http proxy when using wifi.
* @param network A network object to update http proxy
* @param info Info should have following field:
* - httpProxyHost ip address of http proxy.
* - httpProxyPort port of http proxy, set 0 to use default port 8080.
* @param callback callback function.
*/
function configureHttpProxy(network, info, callback) {
if (!network)
return;
let networkKey = getNetworkKey(network);
if (!info || info.httpProxyHost === "") {
delete httpProxyConfig[networkKey];
} else {
httpProxyConfig[networkKey] = network;
httpProxyConfig[networkKey].httpProxyHost = info.httpProxyHost;
httpProxyConfig[networkKey].httpProxyPort = info.httpProxyPort;
}
callback(true);
}
function getHttpProxyNetwork(network) {
if (!network)
return null;
let networkKey = getNetworkKey(network);
return ((networkKey in httpProxyConfig) ? httpProxyConfig : null);
}
function setHttpProxy(network) {
if (!network)
return;
gNetworkManager.setNetworkProxy(network);
}
var staticIpConfig = Object.create(null);
function setStaticIpMode(network, info, callback) {
let setNetworkKey = getNetworkKey(network);
@ -1548,6 +1590,9 @@ var WifiManager = (function() {
manager.setPowerMode = (sdkVersion >= 16)
? setPowerModeCommandJB
: setPowerModeCommandICS;
manager.getHttpProxyNetwork = getHttpProxyNetwork;
manager.setHttpProxy = setHttpProxy;
manager.configureHttpProxy = configureHttpProxy;
manager.setSuspendOptimizations = setSuspendOptimizationsCommand;
manager.setStaticIpMode = setStaticIpMode;
manager.getRssiApprox = getRssiApproxCommand;
@ -1851,6 +1896,7 @@ function WifiWorker() {
"WifiManager:associate", "WifiManager:forget",
"WifiManager:wps", "WifiManager:getState",
"WifiManager:setPowerSavingMode",
"WifiManager:setHttpProxy",
"WifiManager:setStaticIpMode",
"child-process-shutdown"];
@ -2160,6 +2206,11 @@ function WifiWorker() {
WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
}
// Update http proxy when connected to network.
let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
if (netConnect)
WifiManager.setHttpProxy(netConnect);
// The full authentication process is completed, reset the count.
WifiManager.authenticationFailuresCount = 0;
WifiManager.loopDetectionCount = 0;
@ -2182,6 +2233,23 @@ function WifiWorker() {
}
self._fireEvent("ondisconnect", {});
// When disconnected, clear the http proxy setting if it exists.
// Temporarily set http proxy to empty and restore user setting after setHttpProxy.
let netDisconnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
if (netDisconnect) {
let prehttpProxyHostSetting = netDisconnect.httpProxyHost;
let prehttpProxyPortSetting = netDisconnect.httpProxyPort;
netDisconnect.httpProxyHost = "";
netDisconnect.httpProxyPort = 0;
WifiManager.setHttpProxy(netDisconnect);
netDisconnect.httpProxyHost = prehttpProxyHostSetting;
netDisconnect.httpProxyPort = prehttpProxyPortSetting;
}
self.currentNetwork = null;
self.ipAddress = "";
@ -2716,6 +2784,9 @@ WifiWorker.prototype = {
case "WifiManager:setPowerSavingMode":
this.setPowerSavingMode(msg);
break;
case "WifiManager:setHttpProxy":
this.setHttpProxy(msg);
break;
case "WifiManager:setStaticIpMode":
this.setStaticIpMode(msg);
break;
@ -3210,6 +3281,30 @@ WifiWorker.prototype = {
});
},
setHttpProxy: function(msg) {
const message = "WifiManager:setHttpProxy:Return";
let self = this;
let network = msg.data.network;
let info = msg.data.info;
netFromDOM(network, null);
WifiManager.configureHttpProxy(network, info, function(ok) {
if (ok) {
// If configured network is current connected network
// need update http proxy immediately.
let setNetworkKey = getNetworkKey(network);
let curNetworkKey = self.currentNetwork ? getNetworkKey(self.currentNetwork) : null;
if (setNetworkKey === curNetworkKey)
WifiManager.setHttpProxy(network);
self._sendMessage(message, true, true, msg);
} else {
self._sendMessage(message, false, "Set http proxy failed", msg);
}
});
},
setStaticIpMode: function(msg) {
const message = "WifiManager:setStaticMode:Return";
let self = this;

View File

@ -59,7 +59,7 @@ interface nsIWifi : nsISupports
void getWifiScanResults(in nsIWifiScanResultsReady callback);
};
[scriptable, uuid(3f21012d-6e75-4632-b87c-acdd7c57fbf3)]
[scriptable, uuid(e5a72295-1c5f-4848-9cbb-f1d3785c16c1)]
interface nsIDOMWifiManager : nsISupports
{
/**
@ -145,6 +145,19 @@ interface nsIDOMWifiManager : nsISupports
nsIDOMDOMRequest setStaticIpMode(in jsval network,
in jsval info);
/**
* Given a network, configure http proxy when using wifi.
* @param network A network object with the SSID of the network to set http proxy.
* @param info info should have following field:
* - httpProxyHost ip address of http proxy.
* - httpProxyPort port of http proxy, set 0 to use default port 8080.
* set info to null to clear http proxy.
* onsuccess: We have successfully configure http proxy.
* onerror: We have failed to configure http proxy.
*/
nsIDOMDOMRequest setHttpProxy(in jsval network,
in jsval info);
/**
* Returns whether or not wifi is currently enabled.
*/

View File

@ -1,6 +1,7 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import com.jayway.android.robotium.solo.Condition;
import @ANDROID_PACKAGE_NAME@.*;
import android.content.ContentResolver;
@ -79,12 +80,12 @@ abstract class AboutHomeTest extends BaseTest {
/**
* Waits for the given ListView to have a non-empty adapter.
*
* This method will fail if the given ListView or its adapter are null.
* This method will return false if the given ListView or its adapter are null.
*/
protected boolean waitForListToLoad(final ListView listView) {
boolean result = waitForTest(new BooleanTest() {
Condition listWaitCondition = new Condition() {
@Override
public boolean test() {
public boolean isSatisfied() {
if (listView == null) {
return false;
}
@ -96,9 +97,8 @@ abstract class AboutHomeTest extends BaseTest {
return (adapter.getCount() > 0);
}
}, MAX_WAIT_MS);
return result;
};
return waitForCondition(listWaitCondition, MAX_WAIT_MS);
}
/**
@ -331,9 +331,9 @@ abstract class AboutHomeTest extends BaseTest {
// A wait in order for the about:home tab to be rendered after drag/tab selection
private void waitForAboutHomeTab(final int tabIndex) {
boolean correctTab = waitForTest(new BooleanTest() {
boolean correctTab = waitForCondition(new Condition() {
@Override
public boolean test() {
public boolean isSatisfied() {
ViewPager pager = (ViewPager)mSolo.getView(ViewPager.class, 0);
return (pager.getCurrentItem() == tabIndex);
}

View File

@ -1,6 +1,7 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import com.jayway.android.robotium.solo.Condition;
import com.jayway.android.robotium.solo.Solo;
import @ANDROID_PACKAGE_NAME@.*;
@ -196,12 +197,13 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
*/
protected final void focusUrlBar() {
// Click on the browser toolbar to enter editing mode
mDriver.findElement(mActivity, BROWSER_TOOLBAR_ID).click();
final View toolbarView = mSolo.getView(BROWSER_TOOLBAR_ID);
mSolo.clickOnView(toolbarView);
// Wait for highlighed text to gain focus
boolean success = waitForTest(new BooleanTest() {
boolean success = waitForCondition(new Condition() {
@Override
public boolean test() {
public boolean isSatisfied() {
EditText urlEditText = mSolo.getEditText(0);
if (urlEditText.isInputMethodTarget()) {
return true;
@ -222,7 +224,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
mActions.sendKeys(url);
// Get the URL text from the URL bar EditText view
String urlBarText = mDriver.findElement(mActivity, URL_EDIT_TEXT_ID).getText();
String urlBarText = ((EditText) mSolo.getView(URL_EDIT_TEXT_ID)).getText().toString();
mAsserter.is(url, urlBarText, "URL typed properly");
}
@ -272,32 +274,30 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
}
public final void verifyUrl(String url) {
Element urlEditText = mDriver.findElement(mActivity, URL_EDIT_TEXT_ID);
final EditText urlEditText = (EditText) mSolo.getView(URL_EDIT_TEXT_ID);
String urlBarText = null;
if (urlEditText != null) {
// wait for a short time for the expected text, in case there is a delay
// in updating the view
waitForTest(new VerifyUrlTest(urlEditText, url), VERIFY_URL_TIMEOUT);
urlBarText = urlEditText.getText();
waitForCondition(new VerifyTextViewText(urlEditText, url), VERIFY_URL_TIMEOUT);
urlBarText = urlEditText.getText().toString();
}
mAsserter.is(urlBarText, url, "Browser toolbar URL stayed the same");
}
class VerifyUrlTest implements BooleanTest {
private Element mUrlEditText;
private String mUrl;
public VerifyUrlTest(Element urlEditText, String url) {
mUrlEditText = urlEditText;
mUrl = url;
class VerifyTextViewText implements Condition {
private TextView mTextView;
private String mExpected;
public VerifyTextViewText(TextView textView, String expected) {
mTextView = textView;
mExpected = expected;
}
@Override
public boolean test() {
String urlbarText = mUrlEditText.getText();
if (urlbarText.equals(mUrl)) {
return true;
}
return false;
public boolean isSatisfied() {
String textValue = mTextView.getText().toString();
return mExpected.equals(textValue);
}
}
@ -309,6 +309,21 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
return mRawBaseUrl + "/" + url.replaceAll("(^/)", "");
}
/*
* Wrapper method for mSolo.waitForCondition with additional logging.
*/
protected final boolean waitForCondition(Condition condition, int timeout) {
boolean result = mSolo.waitForCondition(condition, timeout);
if (!result) {
// Log timeout failure for diagnostic purposes only; a failed wait may
// be normal and does not necessarily warrant a test asssertion/failure.
mAsserter.dumpLog("waitForCondition timeout after " + timeout + " ms.");
}
return result;
}
// TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
// Future boolean tests should not use this method.
protected final boolean waitForTest(BooleanTest t, int timeout) {
long end = SystemClock.uptimeMillis() + timeout;
while (SystemClock.uptimeMillis() < end) {
@ -324,6 +339,8 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
return false;
}
// TODO: With Robotium 4.2, we should use Condition and waitForCondition instead.
// Future boolean tests should not implement this interface.
protected interface BooleanTest {
public boolean test();
}
@ -413,9 +430,9 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
*/
public boolean waitForEnabledText(String text) {
final String testText = text;
boolean rc = waitForTest(new BooleanTest() {
boolean rc = waitForCondition(new Condition() {
@Override
public boolean test() {
public boolean isSatisfied() {
// Solo.getText() could be used here, except that it sometimes
// hits an assertion when the requested text is not found.
ArrayList<View> views = mSolo.getCurrentViews();
@ -485,12 +502,11 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
}
public final void verifyHomePagerHidden() {
final Element homePagerElement = mDriver.findElement(mActivity, "home_pager");
final View homePagerView = mActivity.findViewById(homePagerElement.getId());
final View homePagerView = mSolo.getView("home_pager");
boolean rc = waitForTest(new BooleanTest() {
boolean rc = waitForCondition(new Condition() {
@Override
public boolean test() {
public boolean isSatisfied() {
return homePagerView.getVisibility() != View.VISIBLE;
}
}, MAX_WAIT_HOME_PAGER_HIDDEN_MS);
@ -501,35 +517,17 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
}
public final void verifyPageTitle(String title) {
Element urlBarTitle = mDriver.findElement(getActivity(), URL_BAR_TITLE_ID);
final TextView urlBarTitle = (TextView) mSolo.getView(URL_BAR_TITLE_ID);
String pageTitle = null;
if (urlBarTitle != null) {
// Wait for the title to make sure it has been displayed in case the view
// does not update fast enough
waitForTest(new VerifyTitle(urlBarTitle, title), MAX_WAIT_MS);
pageTitle = urlBarTitle.getText();
waitForCondition(new VerifyTextViewText(urlBarTitle, title), MAX_WAIT_MS);
pageTitle = urlBarTitle.getText().toString();
}
mAsserter.is(pageTitle, title, "Page title is correct");
}
class VerifyTitle implements BooleanTest {
private Element mUrlBarTitle;
private String mTitle;
public VerifyTitle(Element urlBarTitle, String title) {
mUrlBarTitle = urlBarTitle;
mTitle = title;
}
@Override
public boolean test() {
String pageTitle = mUrlBarTitle.getText();
if (pageTitle.equals(mTitle)) {
return true;
}
return false;
}
}
public final void verifyTabCount(int expectedTabCount) {
Activity activity = getActivity();
Element tabCount = mDriver.findElement(activity, "tabs_counter");
@ -560,18 +558,12 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
}
public void addTab(String url) {
Element tabs = null;
Element addTab = null;
Activity activity = getActivity();
tabs = mDriver.findElement(activity, "tabs");
addTab = mDriver.findElement(activity, "add_tab");
final int addTabId = addTab.getId();
mAsserter.ok(tabs.click(), "checking that tabs clicked", "tabs element clicked");
mSolo.clickOnView(mSolo.getView("tabs"));
// wait for addTab to appear (this is usually immediate)
boolean success = waitForTest(new BooleanTest() {
boolean success = waitForCondition(new Condition() {
@Override
public boolean test() {
View addTabView = getActivity().findViewById(addTabId);
public boolean isSatisfied() {
View addTabView = mSolo.getView("add_tab");
if (addTabView == null) {
return false;
}
@ -579,7 +571,8 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
}
}, MAX_WAIT_MS);
mAsserter.ok(success, "waiting for add tab view", "add tab view available");
mAsserter.ok(addTab.click(), "checking that add_tab clicked", "add_tab element clicked");
final View addTabView = mSolo.getView("add_tab");
mSolo.clickOnView(mSolo.getView("add_tab"));
// Adding a new tab opens about:home, so now we just need to load the url in it.
inputAndLoadUrl(url);

View File

@ -82,17 +82,6 @@ function _Worker(browserPromise, options) {
}
_Worker.prototype = {
reload: function() {
// In the future, it would be nice to just throw away the browser element
// and re-create from scratch. The complication there would be the need
// to reconnect existing ports - but even that might be managable.
// However, bug 899908 calls for 'reload' to be dropped, so let's do that
// instead!
this.browserPromise.then(browser => {
browser.messageManager.sendAsyncMessage("frameworker:reload");
});
},
// Message handler.
receiveMessage: function(msg) {
switch (msg.name) {

View File

@ -45,7 +45,6 @@ function FrameWorker(url, name, origin, exposeLocalStorage) {
this.name = name || url;
this.ports = new Map(); // all unclosed ports, including ones yet to be entangled
this.loaded = false;
this.reloading = false;
this.origin = origin;
this._injectController = null;
this.exposeLocalStorage = exposeLocalStorage;
@ -78,22 +77,6 @@ FrameWorker.prototype = {
}
},
reload: function FrameWorker_reloadWorker() {
// reset all ports as not entangled; they will then be re-entangled during
// the call to createSandbox after the document is reloaded
for (let [, port] of this.ports) {
port._entangled = false;
}
// Mark the provider as unloaded now, so that any new ports created after
// this point but before the unload has fired are properly queued up.
this.loaded = false;
// reset the content to about:blank - this will fire the unload event
// but not remove our browser from the DOM. Our unload handler will
// see this.reloading is true and reload for us.
this.reloading = true;
navigate("about:blank");
},
createSandbox: function createSandbox() {
let workerWindow = content;
let sandbox = new Cu.Sandbox(workerWindow);
@ -246,30 +229,14 @@ FrameWorker.prototype = {
workerWindow.addEventListener("unload", function unloadListener() {
workerWindow.removeEventListener("unload", unloadListener);
worker.loaded = false;
// If the worker is reloading, when we don't actually close any ports as
// they need to be re-entangled.
// If content is already null, we can't send a close message, so skip it.
if (!worker.reloading && content) {
for (let [, port] of worker.ports) {
try {
port.close();
} catch (ex) {
Cu.reportError("FrameWorker: failed to close port. " + ex);
}
}
worker.ports.clear();
}
// No need to close ports - the worker probably wont see a
// social.port-closing message and certainly isn't going to have time to
// do anything if it did see it.
worker.ports.clear();
if (sandbox) {
Cu.nukeSandbox(sandbox);
sandbox = null;
}
if (worker.reloading) {
Services.tm.mainThread.dispatch(function doReload() {
worker.reloading = false;
worker.load();
}, Ci.nsIThread.DISPATCH_NORMAL);
}
});
},
};
@ -285,7 +252,6 @@ const FrameWorkerManager = {
addMessageListener("frameworker:init", this._onInit);
addMessageListener("frameworker:connect", this._onConnect);
addMessageListener("frameworker:reload", this._onReload);
addMessageListener("frameworker:port-message", this._onPortMessage);
addMessageListener("frameworker:cookie-get", this._onCookieGet);
},
@ -304,10 +270,6 @@ const FrameWorkerManager = {
port._createWorkerAndEntangle(frameworker);
},
_onReload: function(msg) {
frameworker.reload();
},
// A message related to a port.
_onPortMessage: function(msg) {
// find the "client" port for this message and have it post it into

View File

@ -60,11 +60,7 @@ WorkerAPI.prototype = {
SocialService.updateProvider(origin, data);
},
"social.reload-worker": function(data) {
getFrameWorkerHandle(this._provider.workerURL, null)._worker.reload();
// the frameworker is going to be reloaded, send the initialization
// so it can have the same startup sequence as if it were loaded
// the first time. This will be queued until the frameworker is ready.
this._port.postMessage({topic: "social.initialize"});
this._provider.reload();
},
"social.user-profile": function (data) {
this._provider.updateUserProfile(data);

View File

@ -559,33 +559,6 @@ let tests = {
}
},
testReloadAndNewPort: function(cbnext) {
let run = function () {
onconnect = function(e) {
e.ports[0].postMessage({topic: "ready"});
}
}
let doneReload = false;
let worker = getFrameWorkerHandle(makeWorkerUrl(run),
undefined, "testReloadAndNewPort");
worker.port.onmessage = function(e) {
if (e.data.topic == "ready" && !doneReload) {
// do the "reload"
doneReload = true;
worker._worker.reload();
let worker2 = getFrameWorkerHandle(makeWorkerUrl(run),
undefined, "testReloadAndNewPort");
worker2.port.onmessage = function(e) {
if (e.data.topic == "ready") {
// "worker" and "worker2" are handles to the same worker
worker2.terminate();
cbnext();
}
}
}
}
},
// This will create the worker, then send a message to the port, then close
// the port - all before the worker has actually initialized.
testCloseFirstSend: function(cbnext) {

View File

@ -128,103 +128,41 @@ let tests = {
let fw = {};
Cu.import("resource://gre/modules/FrameWorker.jsm", fw);
// get a real handle to the worker so we can watch the unload event
// we watch for the unload of the worker to know it is infact being
// unloaded, after that if we get worker.connected we know that
// the worker was loaded again and ports reconnected.
// Given our <browser remote="true"> model, we have to some of this in a
// content script. We turn this function into a data: URL...
// (and note that this code only makes sense in the context of
// FrameWorkerContent.js...)
let contentScript = function() {
// the content script may be executed while there are pending messages,
// such as ports from the previous test being closed, which screws us up.
// By only doing this work on a message, we ensure previous messages have
// all been delivered.
addMessageListener("frameworker-test:ready", function onready() {
removeMessageListener("frameworker-test:ready", onready);
// first, find our test's port - it will be the last one.
let port, id
for ([id, port] of frameworker.ports) {
; // nothing to do - we just want to get the last...
}
let unloadHasFired = false,
haveNoPendingMessagesBefore = true,
havePendingMessagesAfter = false;
content.addEventListener("unload", function workerUnload(e) {
content.removeEventListener("unload", workerUnload);
// There should be no "pending" messages with one subtle exception -
// there *might* be a social.initialize message - the reload process
// posts a message to do the reload, then posts the init message - it
// may or maynot have arrived here by now - but there certainly should
// be no other messages.
for (let [temp_id, temp_port] of frameworker.ports) {
if (temp_port._pendingMessagesOutgoing.length == 0)
continue;
if (temp_port._pendingMessagesOutgoing.length == 1 &&
temp_port._pendingMessagesOutgoing[0].data &&
JSON.parse(temp_port._pendingMessagesOutgoing[0].data).topic == "social.initialize")
continue;
// we found something we aren't expecting...
haveNoPendingMessagesBefore = false;
}
unloadHasFired = true; // at the end, so errors above aren't masked.
});
addEventListener("DOMWindowCreated", function workerLoaded(e) {
removeEventListener("DOMWindowCreated", workerLoaded);
// send a message which should end up pending
port.postMessage({topic: "test-pending-msg"});
for (let [temp_id, temp_port] of frameworker.ports) {
if (temp_port._pendingMessagesOutgoing.length >= 0) {
havePendingMessagesAfter = true;
}
}
let ok = unloadHasFired && haveNoPendingMessagesBefore && havePendingMessagesAfter;
sendAsyncMessage("test-result", {ok: ok});
});
});
};
let reloading = false;
let worker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload");
let port = provider.getWorkerPort();
worker._worker.browserPromise.then(browser => {
browser.messageManager.loadFrameScript("data:," + encodeURI(contentScript.toSource()) + "();", false);
browser.messageManager.sendAsyncMessage("frameworker-test:ready");
let seenTestResult = false;
browser.messageManager.addMessageListener("test-result", function _onTestResult(msg) {
browser.messageManager.removeMessageListener("test-result", _onTestResult);
ok(msg.json.ok, "test result from content-script is ok");
seenTestResult = true;
});
port.onmessage = function (e) {
// this observer will be fired when the worker reloads - it ensures the
// old port was closed and the new worker is functioning.
Services.obs.addObserver(function reloadObserver() {
Services.obs.removeObserver(reloadObserver, "social:provider-reload");
ok(port._closed, "old port was closed by the reload");
let newWorker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload - worker2");
let newPort = provider.getWorkerPort();
newPort.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-initialization-complete":
// tell the worker to send the reload msg - that will trigger the
// frameworker to unload and for our content script's unload
// handler to post a "test-pending" message, which in-turn causes
// the worker to send the test-pending-response.
// All of which goes to prove that if a message is delivered while
// the frameworker is unloaded due to a reload, that message still
// gets delivered.
port.postMessage({topic: "test-reload-init"});
break;
case "test-pending-response":
ok(e.data.data.seenInit, "worker has seen the social.initialize message");
// now we've been reloaded, check that our worker is still the same
let newWorker = fw.getFrameWorkerHandle(provider.workerURL, undefined, "testWorkerReload");
is(worker._worker, newWorker._worker, "worker is the same after reload");
ok(true, "worker reloaded and testPort was reconnected");
ok(seenTestResult, "saw result message from content");
// and we are done.
newPort.close();
next();
break;
}
}
port.postMessage({topic: "test-initialization"});
});
newPort.postMessage({topic: "test-initialization"});
}, "social:provider-reload", false);
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-initialization-complete":
// tell the worker to send the reload msg - that will trigger the
// frameworker to unload and for our content script's unload
// handler to post a "test-result" message.
port.postMessage({topic: "test-reload-init"});
// and the "social:provider-reload" observer should fire...
break;
}
}
port.postMessage({topic: "test-initialization"});
},
testNotificationLinks: function(next) {

View File

@ -25,11 +25,6 @@ onconnect = function(e) {
case "test-profile":
apiPort.postMessage({topic: "social.user-profile", data: data});
break;
case "test-pending-msg":
// we also want to check we have seen a social.initialize message before
// this one, so send that back in the response.
port.postMessage({topic: "test-pending-response", data: {seenInit: !!apiPort}});
break;
case "test-ambient":
apiPort.postMessage({topic: "social.ambient-notification", data: data});
break;
@ -40,8 +35,6 @@ onconnect = function(e) {
testerPort.postMessage({topic: "test.cookies-get-response", data: data});
break;
case "test-reload-init":
// browser_social_sidebar.js started test, tell the sidebar to
// start
apiPort.postMessage({topic: 'social.reload-worker'});
break;
case "test-notification-create":
@ -63,9 +56,4 @@ onconnect = function(e) {
break;
}
}
// used for "test-reload-worker"
if (apiPort && apiPort != port) {
port.postMessage({topic: "worker.connected"})
}
}

View File

@ -195,6 +195,26 @@ const BackgroundPageThumbs = {
this._parentWin.document.documentElement.appendChild(browser);
// an event that is sent if the remote process crashes - no need to remove
// it as we want it to be there as long as the browser itself lives.
browser.addEventListener("oop-browser-crashed", () => {
Cu.reportError("BackgroundThumbnails remote process crashed - recovering");
this._destroyBrowser();
let curCapture = this._captureQueue.length ? this._captureQueue[0] : null;
// we could retry the pending capture, but it's possible the crash
// was due directly to it, so trying again might just crash again.
// We could keep a flag to indicate if it previously crashed, but
// "resetting" the capture requires more work - so for now, we just
// discard it.
if (curCapture && curCapture.pending) {
curCapture._done(null);
// _done automatically continues queue processing.
}
// else: we must have been idle and not currently doing a capture (eg,
// maybe a GC or similar crashed) - so there's no need to attempt a
// queue restart - the next capture request will set everything up.
});
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
this._thumbBrowser = browser;
},
@ -226,7 +246,8 @@ const BackgroundPageThumbs = {
},
/**
* Called when the current capture completes or times out.
* Called when the current capture completes or fails (eg, times out, remote
* process crashes.)
*/
_onCaptureOrTimeout: function (capture) {
// Since timeouts start as an item is being processed, only the first

View File

@ -22,8 +22,10 @@ MOCHITEST_BROWSER_FILES := \
ifndef RELEASE_BUILD
MOCHITEST_BROWSER_FILES += \
browser_thumbnails_background.js \
browser_thumbnails_background_crash.js \
browser_thumbnails_update.js \
thumbnails_background.sjs \
thumbnails_crash_content_helper.js \
thumbnails_update.sjs \
$(NULL)
endif

View File

@ -0,0 +1,162 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const TEST_PAGE_URL = "http://mochi.test:8888/browser/toolkit/components/thumbnails/test/thumbnails_background.sjs";
const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/toolkit/components/thumbnails/test/thumbnails_crash_content_helper.js";
const imports = {};
Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm", imports);
Cu.import("resource://gre/modules/PageThumbs.jsm", imports);
Cu.import("resource://gre/modules/Task.jsm", imports);
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", imports);
function test() {
waitForExplicitFinish();
Services.obs.addObserver(crashObserver, 'ipc:content-shutdown', false);
spawnNextTest();
}
function spawnNextTest() {
if (!tests.length) {
Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
is(numCrashes, 2, "saw 2 crashes from this test");
finish();
return;
}
let test = tests.shift();
info("Running sub-test " + test.name);
imports.Task.spawn(test).then(spawnNextTest, function onError(err) {
ok(false, err);
spawnNextTest();
});
}
let tests = [
function crashDuringCapture() {
// make a good capture first - this ensures we have the <browser>
let goodUrl = testPageURL();
yield capture(goodUrl);
let goodFile = fileForURL(goodUrl);
ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
goodFile.remove(false);
// inject our content script.
let mm = injectContentScript();
// queue up 2 captures - the first has a wait, so this is the one that
// will die. The second one should immediately capture after the crash.
let waitUrl = testPageURL({ wait: 30000 });
let deferred1 = capture(waitUrl);
let deferred2 = capture(goodUrl);
info("Crashing the thumbnail content process.");
mm.sendAsyncMessage("thumbnails-test:crash");
yield deferred1;
let waitFile = fileForURL(waitUrl);
ok(!waitFile.exists(), "Thumbnail should not have been saved due to the crash: " + waitFile.path);
yield deferred2;
ok(goodFile.exists(), "We should have recovered and completed the 2nd capture after the crash: " + goodFile.path);
goodFile.remove(false);
},
function crashWhileIdle() {
// make a good capture first - this ensures we have the <browser>
let goodUrl = testPageURL();
yield capture(goodUrl);
let goodFile = fileForURL(goodUrl);
ok(goodFile.exists(), "Thumbnail should be cached after capture: " + goodFile.path);
goodFile.remove(false);
// inject our content script.
let mm = injectContentScript();
// the observer for the crashing process is basically async, so it's hard
// to know when the <browser> has actually seen it. Easist is to just add
// our own observer.
let deferred = imports.Promise.defer();
Services.obs.addObserver(function crashObserver() {
Services.obs.removeObserver(crashObserver, "oop-frameloader-crashed");
// spin the event loop to ensure the BPT observer was called.
executeSoon(function() {
// Now queue another capture and ensure it recovers.
capture(goodUrl).then(() => {
ok(goodFile.exists(), "We should have recovered and handled new capture requests: " + goodFile.path);
goodFile.remove(false);
deferred.resolve();
});
});
} , "oop-frameloader-crashed", false);
// Nothing is pending - crash the process.
info("Crashing the thumbnail content process.");
mm.sendAsyncMessage("thumbnails-test:crash");
yield deferred.promise;
},
];
function injectContentScript() {
let thumbnailBrowser = imports.BackgroundPageThumbs._thumbBrowser;
let mm = thumbnailBrowser.messageManager;
mm.loadFrameScript(TEST_CONTENT_HELPER, false);
return mm;
}
function capture(url, options) {
let deferred = imports.Promise.defer();
options = options || {};
options.onDone = function onDone(capturedURL) {
deferred.resolve(capturedURL);
};
imports.BackgroundPageThumbs.capture(url, options);
return deferred.promise;
}
function fileForURL(url) {
let path = imports.PageThumbsStorage.getFilePathForURL(url);
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
return file;
}
function testPageURL(opts) {
return TEST_PAGE_URL + "?" + encodeURIComponent(JSON.stringify(opts || {}));
}
// This observer is needed so we can clean up all evidence of the crash so
// the testrunner thinks things are peachy.
let numCrashes = 0;
function crashObserver(subject, topic, data) {
is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
ok(subject instanceof Components.interfaces.nsIPropertyBag2,
'Subject implements nsIPropertyBag2.');
// we might see this called as the process terminates due to previous tests.
// We are only looking for "abnormal" exits...
if (!subject.hasKey("abnormal")) {
info("This is a normal termination and isn't the one we are looking for...");
return;
}
numCrashes++;
var dumpID;
if ('nsICrashReporter' in Components.interfaces) {
dumpID = subject.getPropertyAsAString('dumpID');
ok(dumpID, "dumpID is present and not an empty string");
}
if (dumpID) {
var minidumpDirectory = getMinidumpDirectory();
removeFile(minidumpDirectory, dumpID + '.dmp');
removeFile(minidumpDirectory, dumpID + '.extra');
}
}
function getMinidumpDirectory() {
var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
dir.append("minidumps");
return dir;
}
function removeFile(directory, filename) {
var file = directory.clone();
file.append(filename);
if (file.exists()) {
file.remove(false);
}
}

View File

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Ideally we would use CrashTestUtils.jsm, but that's only available for
// xpcshell tests - so we just copy a ctypes crasher from it.
Cu.import("resource://gre/modules/ctypes.jsm");
let crash = function() { // this will crash when called.
let zero = new ctypes.intptr_t(8);
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
badptr.contents
};
TestHelper = {
init: function() {
addMessageListener("thumbnails-test:crash", this);
},
receiveMessage: function(msg) {
switch (msg.name) {
case "thumbnails-test:crash":
privateNoteIntentionalCrash();
crash();
break;
}
},
}
TestHelper.init();

View File

@ -37,7 +37,7 @@ function appShellDOMWindowType(aWindow) {
* Send Debugger:Shutdown events to all "navigator:browser" windows.
*/
function sendShutdownEvent() {
for (let win of allAppShellDOMWindows("navigator:browser")) {
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
let evt = win.document.createEvent("Event");
evt.initEvent("Debugger:Shutdown", true, false);
win.document.documentElement.dispatchEvent(evt);
@ -196,7 +196,7 @@ BrowserTabList.prototype._getChildren = function(aWindow) {
};
BrowserTabList.prototype.getList = function() {
let topXULWindow = windowMediator.getMostRecentWindow("navigator:browser");
let topXULWindow = windowMediator.getMostRecentWindow(DebuggerServer.chromeWindowType);
// As a sanity check, make sure all the actors presently in our map get
// picked up when we iterate over all windows' tabs.
@ -209,7 +209,7 @@ BrowserTabList.prototype.getList = function() {
// actors that were live when we began the iteration.
// Iterate over all navigator:browser XUL windows.
for (let win of allAppShellDOMWindows("navigator:browser")) {
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
let selectedBrowser = this._getSelectedBrowser(win);
// For each tab in this XUL window, ensure that we have an actor for
@ -333,7 +333,7 @@ BrowserTabList.prototype._checkListening = function() {
BrowserTabList.prototype._listenForEventsIf = function(aShouldListen, aGuard, aEventNames) {
if (!aShouldListen !== !this[aGuard]) {
let op = aShouldListen ? "addEventListener" : "removeEventListener";
for (let win of allAppShellDOMWindows("navigator:browser")) {
for (let win of allAppShellDOMWindows(DebuggerServer.chromeWindowType)) {
for (let name of aEventNames) {
win[op](name, this, false);
}
@ -391,7 +391,7 @@ BrowserTabList.prototype.onOpenWindow = makeInfallible(function(aWindow) {
/* We don't want any further load events from this window. */
aWindow.removeEventListener("load", handleLoad, false);
if (appShellDOMWindowType(aWindow) !== "navigator:browser")
if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType)
return;
// Listen for future tab activity.
@ -425,7 +425,7 @@ BrowserTabList.prototype.onCloseWindow = makeInfallible(function(aWindow) {
aWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
if (appShellDOMWindowType(aWindow) !== "navigator:browser")
if (appShellDOMWindowType(aWindow) !== DebuggerServer.chromeWindowType)
return;
/*

View File

@ -350,8 +350,8 @@ var DebuggerServer = {
/**
* Install Firefox-specific actors.
*/
addBrowserActors: function DS_addBrowserActors() {
this.chromeWindowType = "navigator:browser";
addBrowserActors: function(aWindowType) {
this.chromeWindowType = aWindowType ? aWindowType : "navigator:browser";
this.addActors("resource://gre/modules/devtools/server/actors/webbrowser.js");
this.addActors("resource://gre/modules/devtools/server/actors/script.js");
this.addGlobalActor(this.ChromeDebuggerActor, "chromeDebugger");

View File

@ -21,6 +21,12 @@ CommandLineHandler.prototype = {
createInstance(Ci.nsIWritablePropertyBag);
let inTestMode = this._handleTestMode(cmdLine, args);
let debugPort = this._handleDebugMode(cmdLine);
if (!isNaN(debugPort)) {
Cu.import("resource://webapprt/modules/RemoteDebugger.jsm");
RemoteDebugger.init(debugPort);
}
if (inTestMode) {
// Open the mochitest shim window, which configures the runtime for tests.
Services.ww.openWindow(null,
@ -41,6 +47,34 @@ CommandLineHandler.prototype = {
}
},
/**
* Handle debug command line option.
*
* @param cmdLine A nsICommandLine object.
*
* @returns the port number if it's specified, the default port number if
* the debug option is specified, NaN if the debug option isn't
* specified or the port number isn't valid.
*/
_handleDebugMode: function(cmdLine) {
// -debug [port]
let idx = cmdLine.findFlag("debug", true);
if (idx < 0) {
return NaN;
}
let port;
let portIdx = idx + 1;
if (portIdx < cmdLine.length) {
port = parseInt(cmdLine.getArgument(portIdx));
if (port != NaN) {
return port;
}
}
return Services.prefs.getIntPref('devtools.debugger.remote-port');
},
_handleTestMode: function _handleTestMode(cmdLine, args) {
// -test-mode [url]
let idx = cmdLine.findFlag("test-mode", true);

View File

@ -0,0 +1,26 @@
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["RemoteDebugger"];
let Cc = Components.classes;
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
this.RemoteDebugger = {
init: function(port) {
if (!DebuggerServer.initialized) {
DebuggerServer.init();
DebuggerServer.addBrowserActors("webapprt:webapp");
DebuggerServer.addActors("chrome://webapprt/content/dbg-webapp-actors.js");
}
DebuggerServer.openListener(port);
}
}

View File

@ -0,0 +1,126 @@
/* 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/. */
'use strict';
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
/**
* WebappRT-specific actors.
*/
/**
* Construct a root actor appropriate for use in a server running in the webapp
* runtime. The returned root actor:
* - respects the factories registered with DebuggerServer.addGlobalActor,
* - uses a WebappTabList to supply tab actors,
* - sends all webapprt:webapp window documents a Debugger:Shutdown event
* when it exits.
*
* * @param connection DebuggerServerConnection
* The conection to the client.
*/
function createRootActor(connection)
{
let parameters = {
tabList: new WebappTabList(connection),
globalActorFactories: DebuggerServer.globalActorFactories,
onShutdown: sendShutdownEvent
};
return new RootActor(connection, parameters);
}
/**
* A live list of BrowserTabActors representing the current webapp windows,
* to be provided to the root actor to answer 'listTabs' requests. In the
* webapp runtime, only a single tab per window is ever present.
*
* @param connection DebuggerServerConnection
* The connection in which this list's tab actors may participate.
*
* @see BrowserTabList for more a extensive description of how tab list objects
* work.
*/
function WebappTabList(connection)
{
BrowserTabList.call(this, connection);
}
WebappTabList.prototype = Object.create(BrowserTabList.prototype);
WebappTabList.prototype.constructor = WebappTabList;
WebappTabList.prototype.getList = function() {
let topXULWindow = windowMediator.getMostRecentWindow(this._windowType);
// As a sanity check, make sure all the actors presently in our map get
// picked up when we iterate over all windows.
let initialMapSize = this._actorByBrowser.size;
let foundCount = 0;
// To avoid mysterious behavior if windows are closed or opened mid-iteration,
// we update the map first, and then make a second pass over it to yield
// the actors. Thus, the sequence yielded is always a snapshot of the
// actors that were live when we began the iteration.
// Iterate over all webapprt:webapp XUL windows.
for (let win of allAppShellDOMWindows(this._windowType)) {
let browser = win.document.getElementById("content");
if (!browser) {
continue;
}
// Do we have an existing actor for this browser? If not, create one.
let actor = this._actorByBrowser.get(browser);
if (actor) {
foundCount++;
} else {
actor = new WebappTabActor(this._connection, browser);
this._actorByBrowser.set(browser, actor);
}
actor.selected = (win == topXULWindow);
}
if (this._testing && initialMapSize !== foundCount) {
throw Error("_actorByBrowser map contained actors for dead tabs");
}
this._mustNotify = true;
this._checkListening();
return promise.resolve([actor for ([_, actor] of this._actorByBrowser)]);
};
/**
* Creates a tab actor for handling requests to the single tab, like
* attaching and detaching. WebappTabActor respects the actor factories
* registered with DebuggerServer.addTabActor.
*
* We override the title of the XUL window in content/webapp.js so here
* we need to override the title property to avoid confusion to the user.
* We won't return the title of the contained browser, but the title of
* the webapp window.
*
* @param connection DebuggerServerConnection
* The conection to the client.
* @param browser browser
* The browser instance that contains this tab.
*/
function WebappTabActor(connection, browser)
{
BrowserTabActor.call(this, connection, browser);
}
WebappTabActor.prototype.constructor = WebappTabActor;
WebappTabActor.prototype = Object.create(BrowserTabActor.prototype);
Object.defineProperty(WebappTabActor.prototype, "title", {
get: function() {
return this.browser.ownerDocument.defaultView.document.title;
},
enumerable: true,
configurable: false
});

View File

@ -32,6 +32,10 @@ let progressListener = {
let origin = location.prePath;
if (origin != WebappRT.config.app.origin) {
title = origin + " - " + title;
// We should exit fullscreen mode if the user navigates off the app
// origin.
document.mozCancelFullScreen();
}
document.documentElement.setAttribute("title", title);
},

View File

@ -9,3 +9,4 @@ webapprt.jar:
content/mochitest-shared.js (content/mochitest-shared.js)
content/mochitest.js (content/mochitest.js)
content/mochitest.xul (content/mochitest.xul)
content/dbg-webapp-actors.js (content/dbg-webapp-actors.js)

View File

@ -22,6 +22,7 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
'RemoteDebugger.jsm',
'Startup.jsm',
'WebappRT.jsm',
'WebappsHandler.jsm',

View File

@ -51,6 +51,9 @@ pref("dom.always_allow_move_resize_window", true);
pref("plugin.allowed_types", "application/x-shockwave-flash,application/futuresplash");
pref("devtools.debugger.remote-enabled", true);
pref("devtools.debugger.force-local", true);
// The default for this pref reflects whether the build is capable of IPC.
// (Turning it on in a no-IPC build will have no effect.)
#ifdef XP_MACOSX

View File

@ -28,4 +28,8 @@ MOCHITEST_WEBAPPRT_CHROME_FILES = \
geolocation-prompt-noperm.webapp^headers^ \
geolocation-prompt-perm.html \
geolocation-prompt-noperm.html \
browser_debugger.js \
debugger.webapp \
debugger.webapp^headers^ \
debugger.html \
$(NULL)

View File

@ -0,0 +1,39 @@
Cu.import("resource://gre/modules/Services.jsm");
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
let { RemoteDebugger } = Cu.import("resource://webapprt/modules/RemoteDebugger.jsm", {});
function test() {
waitForExplicitFinish();
loadWebapp("debugger.webapp", undefined, () => {
RemoteDebugger.init(Services.prefs.getIntPref('devtools.debugger.remote-port'));
let client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(() => {
client.listTabs((aResponse) => {
is(aResponse.tabs[0].title, "Debugger Test Webapp", "Title correct");
is(aResponse.tabs[0].url, "http://test/webapprtChrome/webapprt/test/chrome/debugger.html", "URL correct");
ok(aResponse.tabs[0].consoleActor, "consoleActor set");
ok(aResponse.tabs[0].gcliActor, "gcliActor set");
ok(aResponse.tabs[0].styleEditorActor, "styleEditorActor set");
ok(aResponse.tabs[0].inspectorActor, "inspectorActor set");
ok(aResponse.tabs[0].traceActor, "traceActor set");
ok(aResponse.chromeDebugger, "chromeDebugger set");
ok(aResponse.consoleActor, "consoleActor set");
ok(aResponse.gcliActor, "gcliActor set");
ok(aResponse.profilerActor, "profilerActor set");
ok(aResponse.gcliActor, "gcliActor set");
ok(aResponse.deviceActor, "deviceActor set");
client.close(() => {
finish();
});
});
});
});
registerCleanupFunction(function() {
DebuggerServer.destroy();
});
}

View File

@ -0,0 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>This is the test webapp.</p>
</body>
</html>

View File

@ -0,0 +1 @@
{"name": "Debugger Test Webapp", "description": "A debugger test app.", "launch_path": "/webapprtChrome/webapprt/test/chrome/debugger.html" }

View File

@ -0,0 +1 @@
Content-Type: application/x-web-app-manifest+json