mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-10 13:54:27 +00:00
merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
d4f6be647f
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "3014a433e3b78fa04aacb919c853fa220d309d70",
|
||||
"revision": "e3d925b497f5b996c9c397c200805fbcc62bc823",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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');">
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
@ -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
|
@ -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
|
||||
|
@ -19,3 +19,4 @@
|
||||
|
||||
<!ENTITY showTabsInTaskbar.label "Show tab previews in the Windows taskbar">
|
||||
<!ENTITY showTabsInTaskbar.accesskey "k">
|
||||
<!ENTITY tabsGroup.label "Tabs">
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
141
browser/metro/base/tests/mochitest/browser_urlbar_trimURLs.js
Normal file
141
browser/metro/base/tests/mochitest/browser_urlbar_trimURLs.js
Normal 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");
|
||||
}
|
||||
});
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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"})
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
26
webapprt/RemoteDebugger.jsm
Normal file
26
webapprt/RemoteDebugger.jsm
Normal 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);
|
||||
}
|
||||
}
|
126
webapprt/content/dbg-webapp-actors.js
Normal file
126
webapprt/content/dbg-webapp-actors.js
Normal 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
|
||||
});
|
@ -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);
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -22,6 +22,7 @@ EXTRA_COMPONENTS += [
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'RemoteDebugger.jsm',
|
||||
'Startup.jsm',
|
||||
'WebappRT.jsm',
|
||||
'WebappsHandler.jsm',
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
39
webapprt/test/chrome/browser_debugger.js
Normal file
39
webapprt/test/chrome/browser_debugger.js
Normal 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();
|
||||
});
|
||||
}
|
9
webapprt/test/chrome/debugger.html
Normal file
9
webapprt/test/chrome/debugger.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<p>This is the test webapp.</p>
|
||||
</body>
|
||||
</html>
|
1
webapprt/test/chrome/debugger.webapp
Normal file
1
webapprt/test/chrome/debugger.webapp
Normal file
@ -0,0 +1 @@
|
||||
{"name": "Debugger Test Webapp", "description": "A debugger test app.", "launch_path": "/webapprtChrome/webapprt/test/chrome/debugger.html" }
|
1
webapprt/test/chrome/debugger.webapp^headers^
Normal file
1
webapprt/test/chrome/debugger.webapp^headers^
Normal file
@ -0,0 +1 @@
|
||||
Content-Type: application/x-web-app-manifest+json
|
Loading…
Reference in New Issue
Block a user