merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-09-30 15:10:47 +02:00
commit 1f82b2fb58
270 changed files with 5977 additions and 3593 deletions

View File

@ -64,6 +64,7 @@ tools repackage:: $(libs-preqs)
sed -e 's/%MOZ_APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MOZ_APP_NAME%/$(MOZ_APP_NAME)/' -e 's/%APP_VERSION%/$(APP_VERSION)/' -e 's/%APP_NAME%/$(APP_NAME)/' -e 's/%APP_BINARY%/$(APP_BINARY)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist
sed -e 's/%APP_VERSION%/$(APP_VERSION)/' -e 's/%APP_NAME%/$(APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj/InfoPlist.strings
rsync -a $(DIST)/bin/ $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
mv $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/dependentlibs.list $(DIST)/$(APP_NAME).app/Contents/Resources
$(RM) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/mangle $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/shlibsign
ifdef LIBXUL_SDK
cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(APP_BINARY)

View File

@ -42,6 +42,15 @@ endif
MOZ_PACKAGER_MINIFY=1
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
ifndef _APPNAME
_APPNAME = $(MOZ_MACBUNDLE_NAME)
endif
ifndef _BINPATH
_BINPATH = /$(_APPNAME)/Contents/MacOS
endif
endif
include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
# Note that JS_BINARY can be defined in packager.mk, so this test must come after

View File

@ -42,7 +42,9 @@
#endif
[xpcom]
#ifndef XP_MACOSX
@BINPATH@/dependentlibs.list
#endif
#ifdef GKMEDIAS_SHARED_LIBRARY
@BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@
#endif

View File

@ -1,24 +1,45 @@
README.txt
@DLL_PREFIX@mozutils@DLL_SUFFIX@
jssubloader/
# Due to Apple Mac OS X packaging requirements files that are in the same
# directory on other platforms must be located in different directories on
# Mac OS X. The following defines allow specifying the Mac OS X bundle
# location which also work on other platforms.
#
# @DIR_MACOS@
# Equals Contents/MacOS/ on Mac OX X and is an empty string on other platforms.
#
# @DIR_RESOURCES@
# Equals Contents/Resources/ on Mac OX X and is an empty string on other
# platforms.
# Mac OS X v2 signing removals
#ifdef XP_MACOSX
run-mozilla.sh
@DIR_MACOS@active-update.xml
@DIR_MACOS@update-settings.ini
@DIR_MACOS@updates.xml
@DIR_MACOS@defaults/*
@DIR_MACOS@updates/*
#endif
@DIR_MACOS@README.txt
@DIR_MACOS@@DLL_PREFIX@mozutils@DLL_SUFFIX@
@DIR_MACOS@jssubloader/
#ifdef XP_MACOSX
@DIR_MACOS@run-mozilla.sh
#endif
#ifdef XP_WIN
mozcrt19.dll
mozcpp19.dll
#endif
defaults/preferences/services-sync.js
defaults/preferences/healthreport-prefs.js
components/dom_sms.xpt
components/dom_webspeech.xpt
@DIR_MACOS@defaults/preferences/services-sync.js
@DIR_MACOS@defaults/preferences/healthreport-prefs.js
@DIR_MACOS@components/dom_sms.xpt
@DIR_MACOS@components/dom_webspeech.xpt
#ifdef MOZ_FOLD_LIBS
@DLL_PREFIX@nspr4@DLL_SUFFIX@
@DLL_PREFIX@plds4@DLL_SUFFIX@
@DLL_PREFIX@plc4@DLL_SUFFIX@
@DLL_PREFIX@ssl3@DLL_SUFFIX@
@DLL_PREFIX@smime3@DLL_SUFFIX@
@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@nspr4@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@plds4@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@plc4@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@ssl3@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@smime3@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
#endif
@DLL_PREFIX@xpcom@DLL_SUFFIX@
@DIR_MACOS@@DLL_PREFIX@xpcom@DLL_SUFFIX@

View File

@ -76,7 +76,7 @@ ifndef LIBXUL_SDK
# channel-prefs.js is handled separate from other prefs due to bug 756325
libs:: $(srcdir)/profile/channel-prefs.js
$(NSINSTALL) -D $(DIST)/bin/defaults/pref
$(call py_action,preprocessor,$(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js)
$(call py_action,preprocessor,-Fsubstitution $(PREF_PPFLAGS) $(ACDEFINES) $^ -o $(DIST)/bin/defaults/pref/channel-prefs.js)
endif
libs:: $(srcdir)/blocklist.xml
@ -97,12 +97,6 @@ AB := $(firstword $(subst -, ,$(AB_CD)))
clean clobber repackage::
$(RM) -r $(dist_dest)
ifdef LIBXUL_SDK
APPFILES = Resources
else
APPFILES = MacOS
endif
MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/config/buildid)
.PHONY: repackage
@ -113,7 +107,8 @@ tools repackage:: $(PROGRAM)
rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/Contents/Resources/$(AB).lproj
sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/Contents/Resources/$(AB).lproj/InfoPlist.strings
rsync -a $(DIST)/bin/ $(dist_dest)/Contents/$(APPFILES)
rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ $(dist_dest)/Contents/Resources
rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ $(dist_dest)/Contents/MacOS
$(RM) $(dist_dest)/Contents/MacOS/$(PROGRAM)
rsync -aL $(PROGRAM) $(dist_dest)/Contents/MacOS
cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns

View File

@ -1 +0,0 @@
_CodeSignature/CodeResources

View File

@ -147,7 +147,7 @@
<key>CFBundleGetInfoString</key>
<string>%MAC_APP_NAME% %APP_VERSION%</string>
<key>CFBundleIconFile</key>
<string>firefox</string>
<string>firefox.icns</string>
<key>CFBundleIdentifier</key>
<string>%MOZ_MACBUNDLE_ID%</string>
<key>CFBundleInfoDictionaryVersion</key>

View File

@ -0,0 +1,9 @@
/*.app/***
/*.dylib
/certutil
/firefox-bin
/pk12util
/ssltunnel
/webapprt-stub
/xpcshell
/XUL

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>rules</key>
<dict>
<key>^Info.plist$</key>
<true/>
<key>^PkgInfo$</key>
<true/>
<key>^MacOS/</key>
<true/>
<key>^Resources/</key>
<true/>
<key>^MacOS/distribution/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/override.ini</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/updates/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/active-update.xml$</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/defaults/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/removed-files$</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^MacOS/updates.xml$</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^Updated.app/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
<key>^updating/.*</key><dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>10</real>
</dict>
</dict>
</dict>
</plist>

View File

@ -50,6 +50,9 @@
using namespace mozilla;
#ifdef XP_MACOSX
#define kOSXResourcesFolder "Resources"
#endif
#define kDesktopFolder "browser"
#define kMetroFolder "metro"
#define kMetroAppIniFilename "metroapp.ini"
@ -270,7 +273,12 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
nsCOMPtr<nsIFile> greDir;
exeFile->GetParent(getter_AddRefs(greDir));
#ifdef XP_MACOSX
nsCOMPtr<nsIFile> parent;
greDir->GetParent(getter_AddRefs(parent));
greDir = parent.forget();
greDir->AppendNative(NS_LITERAL_CSTRING(kOSXResourcesFolder));
#endif
nsCOMPtr<nsIFile> appSubdir;
greDir->Clone(getter_AddRefs(appSubdir));
appSubdir->Append(NS_LITERAL_STRING(kDesktopFolder));
@ -564,6 +572,10 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
// chop XPCOM_DLL off exePath
*lastSlash = '\0';
#ifdef XP_MACOSX
lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
strcpy(lastSlash + 1, kOSXResourcesFolder);
#endif
#ifdef XP_WIN
rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(exePath), false,
xreDirectory);

View File

@ -1,4 +1,3 @@
#filter substitution
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -1730,3 +1730,5 @@ pref("experiments.supported", true);
// Enable the OpenH264 plugin support in the addon manager.
pref("media.gmp-gmpopenh264.provider.enabled", true);
pref("browser.apps.URL", "https://marketplace.firefox.com/discovery/");

View File

@ -470,6 +470,10 @@
accesskey="&addons.accesskey;"
key="key_openAddons"
command="Tools:Addons"/>
<menuitem id="menu_openApps"
label="&webapps.label;"
accesskey="&webapps.accesskey;"
oncommand="BrowserOpenApps();"/>
#ifdef MOZ_SERVICES_SYNC
<!-- only one of sync-setup or sync-menu will be showing at once -->
<menuitem id="sync-setup"

View File

@ -6092,6 +6092,11 @@ function BrowserOpenAddonsMgr(aView) {
}
}
function BrowserOpenApps() {
let appsURL = Services.urlFormatter.formatURLPref("browser.apps.URL");
switchToTabHavingURI(appsURL, true)
}
function GetSearchFieldBookmarkData(node) {
var charset = node.ownerDocument.characterSet;

View File

@ -201,6 +201,11 @@ function test_multiple_windows() {
ok(true, "Advanced preferences opened on info bar button press.");
executeSoon(function soon() {
prefWindowOpened = true;
// If the prefs are being displayed in a dialog we need to close it.
// If in a tab (ie, in-content prefs) it closes with the window.
if (!Services.prefs.getBoolPref("browser.preferences.inContent")) {
prefWin.close();
}
maybeFinish();
});
}, "advanced-pane-loaded", false);

View File

@ -150,7 +150,8 @@ function clearSubview(aSubview) {
parent.appendChild(aSubview);
}
const CustomizableWidgets = [{
const CustomizableWidgets = [
{
id: "history-panelmenu",
type: "view",
viewId: "PanelUI-history",
@ -923,6 +924,18 @@ const CustomizableWidgets = [{
});
return node;
}
}, {
id: "web-apps-button",
label: "web-apps-button.label",
tooltiptext: "web-apps-button.tooltiptext",
onCommand: function(aEvent) {
let win = aEvent.target &&
aEvent.target.ownerDocument &&
aEvent.target.ownerDocument.defaultView;
if (win && typeof win.BrowserOpenApps == "function") {
win.BrowserOpenApps();
}
}
}];
#ifdef XP_WIN

View File

@ -125,12 +125,11 @@ static void
AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array)
{
nsCOMPtr<nsIFile> searchPlugins;
nsresult rv = aDirSvc->Get(XRE_EXECUTABLE_FILE,
nsresult rv = aDirSvc->Get(XRE_APP_DISTRIBUTION_DIR,
NS_GET_IID(nsIFile),
getter_AddRefs(searchPlugins));
if (NS_FAILED(rv))
return;
searchPlugins->SetNativeLeafName(NS_LITERAL_CSTRING("distribution"));
searchPlugins->AppendNative(NS_LITERAL_CSTRING("searchplugins"));
bool exists;

View File

@ -26,12 +26,16 @@ this.DistributionCustomizer = function DistributionCustomizer() {
} catch(ex) {}
let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
let iniFile = loadFromProfile ? dirSvc.get("ProfD", Ci.nsIFile)
: dirSvc.get("XREExeF", Ci.nsIFile);
iniFile.leafName = "distribution";
iniFile.append("distribution.ini");
if (iniFile.exists())
this._iniFile = iniFile;
try {
let iniFile = loadFromProfile ? dirSvc.get("ProfD", Ci.nsIFile)
: dirSvc.get("XREAppDist", Ci.nsIFile);
if (loadFromProfile) {
iniFile.leafName = "distribution";
}
iniFile.append("distribution.ini");
if (iniFile.exists())
this._iniFile = iniFile;
} catch(ex) {}
}
DistributionCustomizer.prototype = {

View File

@ -93,7 +93,7 @@ FontInspector.prototype = {
// We don't get fonts for a node, but for a range
let rng = contentDocument.createRange();
rng.selectNode(node);
rng.selectNodeContents(node);
let fonts = DOMUtils.getUsedFontFaces(rng);
let fontsArray = [];
for (let i = 0; i < fonts.length; i++) {

View File

@ -8,6 +8,7 @@
const {Cu, Ci} = require("chrome");
let EventEmitter = require("devtools/toolkit/event-emitter");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
/**
* API
@ -225,11 +226,18 @@ Selection.prototype = {
if (rawNode) {
try {
let doc = this.document;
return (doc && doc.defaultView && doc.documentElement.contains(rawNode));
if (doc && doc.defaultView) {
let docEl = doc.documentElement;
let bindingParent = LayoutHelpers.getRootBindingParent(rawNode);
if (docEl.contains(bindingParent)) {
return true;
}
}
} catch (e) {
// "can't access dead object" error
return false;
}
return false;
}
while(node) {
@ -252,6 +260,14 @@ Selection.prototype = {
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ELEMENT_NODE;
},
isPseudoElementNode: function() {
return this.isNode() && this.nodeFront.isPseudoElement;
},
isAnonymousNode: function() {
return this.isNode() && this.nodeFront.isAnonymous;
},
isAttributeNode: function() {
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ATTRIBUTE_NODE;
},

View File

@ -156,6 +156,10 @@ HTMLBreadcrumbs.prototype = {
prettyPrintNodeAsText: function BC_prettyPrintNodeText(aNode)
{
let text = aNode.tagName.toLowerCase();
if (aNode.isPseudoElement) {
text = aNode.isBeforePseudoElement ? "::before" : "::after";
}
if (aNode.id) {
text += "#" + aNode.id;
}
@ -201,6 +205,9 @@ HTMLBreadcrumbs.prototype = {
pseudosLabel.className = "breadcrumbs-widget-item-pseudo-classes plain";
let tagText = aNode.tagName.toLowerCase();
if (aNode.isPseudoElement) {
tagText = aNode.isBeforePseudoElement ? "::before" : "::after";
}
let idText = aNode.id ? ("#" + aNode.id) : "";
let classesText = "";

View File

@ -584,7 +584,10 @@ InspectorPanel.prototype = {
* Disable the delete item if needed. Update the pseudo classes.
*/
_setupNodeMenu: function InspectorPanel_setupNodeMenu() {
let isSelectionElement = this.selection.isElementNode();
let isSelectionElement = this.selection.isElementNode() &&
!this.selection.isPseudoElementNode();
let isEditableElement = isSelectionElement &&
!this.selection.isAnonymousNode();
// Set the pseudo classes
for (let name of ["hover", "active", "focus"]) {
@ -601,10 +604,10 @@ InspectorPanel.prototype = {
// Disable delete item if needed
let deleteNode = this.panelDoc.getElementById("node-menu-delete");
if (this.selection.isRoot() || this.selection.isDocumentTypeNode()) {
deleteNode.setAttribute("disabled", "true");
} else {
if (isEditableElement) {
deleteNode.removeAttribute("disabled");
} else {
deleteNode.setAttribute("disabled", "true");
}
// Disable / enable "Copy Unique Selector", "Copy inner HTML" &
@ -625,7 +628,7 @@ InspectorPanel.prototype = {
// Enable the "edit HTML" item if the selection is an element and the root
// actor has the appropriate trait (isOuterHTMLEditable)
let editHTML = this.panelDoc.getElementById("node-menu-edithtml");
if (this.isOuterHTMLEditable && isSelectionElement) {
if (isEditableElement && this.isOuterHTMLEditable) {
editHTML.removeAttribute("disabled");
} else {
editHTML.setAttribute("disabled", "true");
@ -635,7 +638,7 @@ InspectorPanel.prototype = {
// the root actor has the appropriate trait (isOuterHTMLEditable) and if
// the clipbard content is appropriate.
let pasteOuterHTML = this.panelDoc.getElementById("node-menu-pasteouterhtml");
if (this.isOuterHTMLEditable && isSelectionElement &&
if (isEditableElement && this.isOuterHTMLEditable &&
this._getClipboardContentForOuterHTML()) {
pasteOuterHTML.removeAttribute("disabled");
} else {
@ -646,7 +649,7 @@ InspectorPanel.prototype = {
// which essentially checks if it's an image or canvas tag
let copyImageData = this.panelDoc.getElementById("node-menu-copyimagedatauri");
let markupContainer = this.markup.getContainer(this.selection.nodeFront);
if (markupContainer && markupContainer.isPreviewable()) {
if (isSelectionElement && markupContainer && markupContainer.isPreviewable()) {
copyImageData.removeAttribute("disabled");
} else {
copyImageData.setAttribute("disabled", "true");

View File

@ -51,4 +51,26 @@ let test = asyncTest(function*() {
is(labelId.textContent, "#" + id,
"Node #" + node.nodeId + ": selection matches");
}
yield testPseudoElements(inspector, container);
});
function *testPseudoElements(inspector, container) {
info ("Checking for pseudo elements");
let pseudoParent = getNodeFront(getNode("#pseudo-container"));
let children = yield inspector.walker.children(pseudoParent);
is (children.nodes.length, 2, "Pseudo children returned from walker");
let beforeElement = children.nodes[0];
let breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
let nodeSelected = selectNode(beforeElement, inspector);
yield Promise.all([breadcrumbsUpdated, nodeSelected]);
is(container.childNodes[3].textContent, "::before", "::before shows up in breadcrumb");
let afterElement = children.nodes[1];
breadcrumbsUpdated = inspector.once("breadcrumbs-updated");
nodeSelected = selectNode(afterElement, inspector);
yield Promise.all([breadcrumbsUpdated, nodeSelected]);
is(container.childNodes[3].textContent, "::after", "::before shows up in breadcrumb");
}

View File

@ -7,6 +7,12 @@
border: 1px solid red;
margin: 10px;
}
#pseudo-container::before {
content: 'before';
}
#pseudo-container::after {
content: 'after';
}
</style>
</head>
<body>
@ -36,5 +42,6 @@
</div>
</div>
</article>
<div id='pseudo-container'></div>
</body>
</html>

View File

@ -155,7 +155,7 @@ function selectAndHighlightNode(nodeOrSelector, inspector) {
/**
* Set the inspector's current selection to a node or to the first match of the
* given css selector.
* @param {String|DOMNode} nodeOrSelector
* @param {String|DOMNode|NodeFront} nodeOrSelector
* @param {InspectorPanel} inspector
* The instance of InspectorPanel currently loaded in the toolbox
* @param {String} reason
@ -169,7 +169,11 @@ function selectNode(nodeOrSelector, inspector, reason="test") {
let node = getNode(nodeOrSelector);
let updated = inspector.once("inspector-updated");
inspector.selection.setNode(node, reason);
if (node._form) {
inspector.selection.setNodeFront(node, reason);
} else {
inspector.selection.setNode(node, reason);
}
return updated;
}

View File

@ -131,7 +131,7 @@
transition: background .5s;
}
.tag-line .open, .tag-line .close, .comment {
.tag-line {
cursor: default;
}

View File

@ -22,6 +22,7 @@ const {HTMLEditor} = require("devtools/markupview/html-editor");
const promise = require("devtools/toolkit/deprecated-sync-thenables");
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
const EventEmitter = require("devtools/toolkit/event-emitter");
const Heritage = require("sdk/core/heritage");
Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Templater.jsm");
@ -40,6 +41,9 @@ loader.lazyGetter(this, "AutocompletePopup", () => {
*
* MarkupContainer - the structure that holds an editor and its
* immediate children in the markup panel.
* - MarkupElementContainer: markup container for element nodes
* - MarkupTextContainer: markup container for text / comment nodes
* - MarkupReadonlyContainer: markup container for other nodes
* Node - A content node.
* object.elt - A UI element in the markup panel.
*/
@ -186,7 +190,7 @@ MarkupView.prototype = {
parentNode = parentNode.parentNode;
}
if (container) {
if (container instanceof MarkupElementContainer) {
// With the newly found container, delegate the tooltip content creation
// and decision to show or not the tooltip
container._buildEventTooltipContent(event.target, this.tooltip);
@ -301,7 +305,7 @@ MarkupView.prototype = {
* tooltip.
* Delegates the actual decision to the corresponding MarkupContainer instance
* if one is found.
* @return the promise returned by MarkupContainer._isImagePreviewTarget
* @return the promise returned by MarkupElementContainer._isImagePreviewTarget
*/
_isImagePreviewTarget: function(target) {
// From the target passed here, let's find the parent MarkupContainer
@ -315,10 +319,10 @@ MarkupView.prototype = {
parent = parent.parentNode;
}
if (container) {
if (container instanceof MarkupElementContainer) {
// With the newly found container, delegate the tooltip content creation
// and decision to show or not the tooltip
return container._isImagePreviewTarget(target, this.tooltip);
return container.isImagePreviewTarget(target, this.tooltip);
}
},
@ -507,7 +511,8 @@ MarkupView.prototype = {
*/
deleteNode: function(aNode) {
if (aNode.isDocumentElement ||
aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE ||
aNode.isAnonymous) {
return;
}
@ -568,7 +573,7 @@ MarkupView.prototype = {
/**
* Make sure a node is included in the markup tool.
*
* @param DOMNode aNode
* @param NodeFront aNode
* The node in the content document.
* @param boolean aFlashNode
* Whether the newly imported node should be flashed
@ -583,15 +588,23 @@ MarkupView.prototype = {
return this.getContainer(aNode);
}
let container;
let {nodeType, isPseudoElement} = aNode;
if (aNode === this.walker.rootNode) {
var container = new RootContainer(this, aNode);
container = new RootContainer(this, aNode);
this._elt.appendChild(container.elt);
this._rootNode = aNode;
} else if (nodeType == Ci.nsIDOMNode.ELEMENT_NODE && !isPseudoElement) {
container = new MarkupElementContainer(this, aNode, this._inspector);
} else if (nodeType == Ci.nsIDOMNode.COMMENT_NODE ||
nodeType == Ci.nsIDOMNode.TEXT_NODE) {
container = new MarkupTextContainer(this, aNode, this._inspector);
} else {
var container = new MarkupContainer(this, aNode, this._inspector);
if (aFlashNode) {
container.flashMutation();
}
container = new MarkupReadOnlyContainer(this, aNode, this._inspector);
}
if (aFlashNode) {
container.flashMutation();
}
this._containers.set(aNode, container);
@ -961,7 +974,7 @@ MarkupView.prototype = {
let parentContainer = this.getContainer(parent);
if (parentContainer) {
parentContainer.childrenDirty = true;
this._updateChildren(parentContainer, {expand: node});
this._updateChildren(parentContainer, {expand: true});
}
}
@ -1306,74 +1319,69 @@ MarkupView.prototype = {
}
};
/**
* The main structure for storing a document node in the markup
* tree. Manages creation of the editor for the node and
* a <ul> for placing child elements, and expansion/collapsing
* of the element.
*
* @param MarkupView aMarkupView
* The markup view that owns this container.
* @param DOMNode aNode
* The node to display.
* @param Inspector aInspector
* The inspector tool container the markup-view
* This should not be instantiated directly, instead use one of:
* MarkupReadOnlyContainer
* MarkupTextContainer
* MarkupElementContainer
*/
function MarkupContainer(aMarkupView, aNode, aInspector) {
this.markup = aMarkupView;
this.doc = this.markup.doc;
this.undo = this.markup.undo;
this.node = aNode;
this._inspector = aInspector;
if (aNode.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
this.editor = new TextEditor(this, aNode, "text");
} else if (aNode.nodeType == Ci.nsIDOMNode.COMMENT_NODE) {
this.editor = new TextEditor(this, aNode, "comment");
} else if (aNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
this.editor = new ElementEditor(this, aNode);
} else if (aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
this.editor = new DoctypeEditor(this, aNode);
} else {
this.editor = new GenericEditor(this, aNode);
}
// The template will fill the following properties
this.elt = null;
this.expander = null;
this.tagState = null;
this.tagLine = null;
this.children = null;
this.markup.template("container", this);
this.elt.container = this;
this.children.container = this;
// Expanding/collapsing the node on dblclick of the whole tag-line element
this._onToggle = this._onToggle.bind(this);
this.elt.addEventListener("dblclick", this._onToggle, false);
this.expander.addEventListener("click", this._onToggle, false);
// Appending the editor element and attaching event listeners
this.tagLine.appendChild(this.editor.elt);
this._onMouseDown = this._onMouseDown.bind(this);
this.elt.addEventListener("mousedown", this._onMouseDown, false);
// Prepare the image preview tooltip data if any
this._prepareImagePreview();
// Marking the node as shown or hidden
this.isDisplayed = this.node.isDisplayed;
}
function MarkupContainer() { }
MarkupContainer.prototype = {
/*
* Initialize the MarkupContainer. Should be called while one
* of the other contain classes is instantiated.
*
* @param MarkupView markupView
* The markup view that owns this container.
* @param NodeFront node
* The node to display.
* @param string templateID
* Which template to render for this container
*/
initialize: function(markupView, node, templateID) {
this.markup = markupView;
this.node = node;
this.undo = this.markup.undo;
// The template will fill the following properties
this.elt = null;
this.expander = null;
this.tagState = null;
this.tagLine = null;
this.children = null;
this.markup.template(templateID, this);
this.elt.container = this;
// Binding event listeners
this._onMouseDown = this._onMouseDown.bind(this);
this.elt.addEventListener("mousedown", this._onMouseDown, false);
this._onToggle = this._onToggle.bind(this);
// Expanding/collapsing the node on dblclick of the whole tag-line element
this.elt.addEventListener("dblclick", this._onToggle, false);
if (this.expander) {
this.expander.addEventListener("click", this._onToggle, false);
}
// Marking the node as shown or hidden
this.isDisplayed = this.node.isDisplayed;
},
toString: function() {
return "[MarkupContainer for " + this.node + "]";
},
isPreviewable: function() {
if (this.node.tagName) {
if (this.node.tagName && !this.node.isPseudoElement) {
let tagName = this.node.tagName.toLowerCase();
let srcAttr = this.editor.getAttributeElement("src");
let isImage = tagName === "img" && srcAttr;
@ -1385,60 +1393,6 @@ MarkupContainer.prototype = {
}
},
/**
* If the node is an image or canvas (@see isPreviewable), then get the
* image data uri from the server so that it can then later be previewed in
* a tooltip if needed.
* Stores a promise in this.tooltipData.data that resolves when the data has
* been retrieved
*/
_prepareImagePreview: function() {
if (this.isPreviewable()) {
// Get the image data for later so that when the user actually hovers over
// the element, the tooltip does contain the image
let def = promise.defer();
this.tooltipData = {
target: this.editor.getAttributeElement("src") || this.editor.tag,
data: def.promise
};
let maxDim = Services.prefs.getIntPref("devtools.inspector.imagePreviewTooltipSize");
this.node.getImageData(maxDim).then(data => {
data.data.string().then(str => {
let res = {data: str, size: data.size};
// Resolving the data promise and, to always keep tooltipData.data
// as a promise, create a new one that resolves immediately
def.resolve(res);
this.tooltipData.data = promise.resolve(res);
});
}, () => {
this.tooltipData.data = promise.reject();
});
}
},
/**
* Executed by MarkupView._isImagePreviewTarget which is itself called when the
* mouse hovers over a target in the markup-view.
* Checks if the target is indeed something we want to have an image tooltip
* preview over and, if so, inserts content into the tooltip.
* @return a promise that resolves when the content has been inserted or
* rejects if no preview is required. This promise is then used by Tooltip.js
* to decide if/when to show the tooltip
*/
_isImagePreviewTarget: function(target, tooltip) {
if (!this.tooltipData || this.tooltipData.target !== target) {
return promise.reject();
}
return this.tooltipData.data.then(({data, size}) => {
tooltip.setImageContent(data, size);
}, () => {
tooltip.setBrokenImageContent();
});
},
/**
* Show the element has displayed or not
*/
@ -1449,37 +1403,6 @@ MarkupContainer.prototype = {
}
},
copyImageDataUri: function() {
// We need to send again a request to gettooltipData even if one was sent for
// the tooltip, because we want the full-size image
this.node.getImageData().then(data => {
data.data.string().then(str => {
clipboardHelper.copyString(str, this.markup.doc);
});
});
},
_buildEventTooltipContent: function(target, tooltip) {
if (target.hasAttribute("data-event")) {
tooltip.hide(target);
this.node.getEventListenerInfo().then(listenerInfo => {
tooltip.setEventContent({
eventListenerInfos: listenerInfo,
toolbox: this._inspector.toolbox
});
this.markup._makeTooltipPersistent(true);
tooltip.once("hidden", () => {
this.markup._makeTooltipPersistent(false);
});
tooltip.show(target);
});
return true;
}
},
/**
* True if the current node has children. The MarkupView
* will set this attribute for the MarkupContainer.
@ -1492,6 +1415,10 @@ MarkupContainer.prototype = {
set hasChildren(aValue) {
this._hasChildren = aValue;
if (!this.expander) {
return;
}
if (aValue) {
this.expander.style.visibility = "visible";
} else {
@ -1499,10 +1426,6 @@ MarkupContainer.prototype = {
}
},
parentContainer: function() {
return this.elt.parentNode ? this.elt.parentNode.container : null;
},
/**
* True if the node has been visually expanded in the tree.
*/
@ -1511,32 +1434,35 @@ MarkupContainer.prototype = {
},
set expanded(aValue) {
if (!this.expander) {
return;
}
if (aValue && this.elt.classList.contains("collapsed")) {
// Expanding a node means cloning its "inline" closing tag into a new
// tag-line that the user can interact with and showing the children.
if (this.editor instanceof ElementEditor) {
let closingTag = this.elt.querySelector(".close");
if (closingTag) {
if (!this.closeTagLine) {
let line = this.markup.doc.createElement("div");
line.classList.add("tag-line");
let closingTag = this.elt.querySelector(".close");
if (closingTag) {
if (!this.closeTagLine) {
let line = this.markup.doc.createElement("div");
line.classList.add("tag-line");
let tagState = this.markup.doc.createElement("div");
tagState.classList.add("tag-state");
line.appendChild(tagState);
let tagState = this.markup.doc.createElement("div");
tagState.classList.add("tag-state");
line.appendChild(tagState);
line.appendChild(closingTag.cloneNode(true));
line.appendChild(closingTag.cloneNode(true));
this.closeTagLine = line;
}
this.elt.appendChild(this.closeTagLine);
this.closeTagLine = line;
}
this.elt.appendChild(this.closeTagLine);
}
this.elt.classList.remove("collapsed");
this.expander.setAttribute("open", "");
this.hovered = false;
} else if (!aValue) {
if (this.editor instanceof ElementEditor && this.closeTagLine) {
if (this.closeTagLine) {
this.elt.removeChild(this.closeTagLine);
}
this.elt.classList.add("collapsed");
@ -1544,12 +1470,8 @@ MarkupContainer.prototype = {
}
},
_onToggle: function(event) {
this.markup.navigate(this);
if(this.hasChildren) {
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
}
event.stopPropagation();
parentContainer: function() {
return this.elt.parentNode ? this.elt.parentNode.container : null;
},
_onMouseDown: function(event) {
@ -1695,11 +1617,27 @@ MarkupContainer.prototype = {
}
},
_onToggle: function(event) {
this.markup.navigate(this);
if (this.hasChildren) {
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
}
event.stopPropagation();
},
/**
* Get rid of event listeners and references, when the container is no longer
* needed
*/
destroy: function() {
// Remove event listeners
this.elt.removeEventListener("mousedown", this._onMouseDown, false);
this.elt.removeEventListener("dblclick", this._onToggle, false);
if (this.expander) {
this.expander.removeEventListener("click", this._onToggle, false);
}
// Recursively destroy children containers
let firstChild;
while (firstChild = this.children.firstChild) {
@ -1711,16 +1649,168 @@ MarkupContainer.prototype = {
this.children.removeChild(firstChild);
}
// Remove event listeners
this.elt.removeEventListener("dblclick", this._onToggle, false);
this.elt.removeEventListener("mousedown", this._onMouseDown, false);
this.expander.removeEventListener("click", this._onToggle, false);
// Destroy my editor
this.editor.destroy();
}
};
/**
* An implementation of MarkupContainer for Pseudo Elements,
* Doctype nodes, or any other type generic node that doesn't
* fit for other editors.
* Does not allow any editing, just viewing / selecting.
*
* @param MarkupView markupView
* The markup view that owns this container.
* @param NodeFront node
* The node to display.
*/
function MarkupReadOnlyContainer(markupView, node) {
MarkupContainer.prototype.initialize.call(this, markupView, node, "readonlycontainer");
this.editor = new GenericEditor(this, node);
this.tagLine.appendChild(this.editor.elt);
}
MarkupReadOnlyContainer.prototype = Heritage.extend(MarkupContainer.prototype, {});
/**
* An implementation of MarkupContainer for text node and comment nodes.
* Allows basic text editing in a textarea.
*
* @param MarkupView aMarkupView
* The markup view that owns this container.
* @param NodeFront aNode
* The node to display.
* @param Inspector aInspector
* The inspector tool container the markup-view
*/
function MarkupTextContainer(markupView, node) {
MarkupContainer.prototype.initialize.call(this, markupView, node, "textcontainer");
if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE) {
this.editor = new TextEditor(this, node, "text");
} else if (node.nodeType == Ci.nsIDOMNode.COMMENT_NODE) {
this.editor = new TextEditor(this, node, "comment");
} else {
throw "Invalid node for MarkupTextContainer";
}
this.tagLine.appendChild(this.editor.elt);
}
MarkupTextContainer.prototype = Heritage.extend(MarkupContainer.prototype, {});
/**
* An implementation of MarkupContainer for Elements that can contain
* child nodes.
* Allows editing of tag name, attributes, expanding / collapsing.
*
* @param MarkupView markupView
* The markup view that owns this container.
* @param NodeFront node
* The node to display.
*/
function MarkupElementContainer(markupView, node) {
MarkupContainer.prototype.initialize.call(this, markupView, node, "elementcontainer");
if (node.nodeType === Ci.nsIDOMNode.ELEMENT_NODE) {
this.editor = new ElementEditor(this, node);
} else {
throw "Invalid node for MarkupElementContainer";
}
this.tagLine.appendChild(this.editor.elt);
// Prepare the image preview tooltip data if any
this._prepareImagePreview();
}
MarkupElementContainer.prototype = Heritage.extend(MarkupContainer.prototype, {
_buildEventTooltipContent: function(target, tooltip) {
if (target.hasAttribute("data-event")) {
tooltip.hide(target);
this.node.getEventListenerInfo().then(listenerInfo => {
tooltip.setEventContent({
eventListenerInfos: listenerInfo,
toolbox: this.markup._inspector.toolbox
});
this.markup._makeTooltipPersistent(true);
tooltip.once("hidden", () => {
this.markup._makeTooltipPersistent(false);
});
tooltip.show(target);
});
return true;
}
},
/**
* If the node is an image or canvas (@see isPreviewable), then get the
* image data uri from the server so that it can then later be previewed in
* a tooltip if needed.
* Stores a promise in this.tooltipData.data that resolves when the data has
* been retrieved
*/
_prepareImagePreview: function() {
if (this.isPreviewable()) {
// Get the image data for later so that when the user actually hovers over
// the element, the tooltip does contain the image
let def = promise.defer();
this.tooltipData = {
target: this.editor.getAttributeElement("src") || this.editor.tag,
data: def.promise
};
let maxDim = Services.prefs.getIntPref("devtools.inspector.imagePreviewTooltipSize");
this.node.getImageData(maxDim).then(data => {
data.data.string().then(str => {
let res = {data: str, size: data.size};
// Resolving the data promise and, to always keep tooltipData.data
// as a promise, create a new one that resolves immediately
def.resolve(res);
this.tooltipData.data = promise.resolve(res);
});
}, () => {
this.tooltipData.data = promise.reject();
});
}
},
/**
* Executed by MarkupView._isImagePreviewTarget which is itself called when the
* mouse hovers over a target in the markup-view.
* Checks if the target is indeed something we want to have an image tooltip
* preview over and, if so, inserts content into the tooltip.
* @return a promise that resolves when the content has been inserted or
* rejects if no preview is required. This promise is then used by Tooltip.js
* to decide if/when to show the tooltip
*/
isImagePreviewTarget: function(target, tooltip) {
if (!this.tooltipData || this.tooltipData.target !== target) {
return promise.reject();
}
return this.tooltipData.data.then(({data, size}) => {
tooltip.setImageContent(data, size);
}, () => {
tooltip.setBrokenImageContent();
});
},
copyImageDataUri: function() {
// We need to send again a request to gettooltipData even if one was sent for
// the tooltip, because we want the full-size image
this.node.getImageData().then(data => {
data.data.string().then(str => {
clipboardHelper.copyString(str, this.markup.doc);
});
});
}
});
/**
* Dummy container node used for the root document element.
@ -1742,35 +1832,33 @@ RootContainer.prototype = {
};
/**
* Creates an editor for simple nodes.
* Creates an editor for non-editable nodes.
*/
function GenericEditor(aContainer, aNode) {
this.elt = aContainer.doc.createElement("span");
this.elt.className = "editor";
this.elt.textContent = aNode.nodeName;
this.container = aContainer;
this.markup = this.container.markup;
this.template = this.markup.template.bind(this.markup);
this.elt = null;
this.template("generic", this);
if (aNode.isPseudoElement) {
this.tag.classList.add("theme-fg-color5");
this.tag.textContent = aNode.isBeforePseudoElement ? "::before" : "::after";
} else if (aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
this.elt.classList.add("comment");
this.tag.textContent = '<!DOCTYPE ' + aNode.name +
(aNode.publicId ? ' PUBLIC "' + aNode.publicId + '"': '') +
(aNode.systemId ? ' "' + aNode.systemId + '"' : '') +
'>';
} else {
this.tag.textContent = aNode.nodeName;
}
}
GenericEditor.prototype = {
destroy: function() {}
};
/**
* Creates an editor for a DOCTYPE node.
*
* @param MarkupContainer aContainer The container owning this editor.
* @param DOMNode aNode The node being edited.
*/
function DoctypeEditor(aContainer, aNode) {
this.elt = aContainer.doc.createElement("span");
this.elt.className = "editor comment";
this.elt.textContent = '<!DOCTYPE ' + aNode.name +
(aNode.publicId ? ' PUBLIC "' + aNode.publicId + '"': '') +
(aNode.systemId ? ' "' + aNode.systemId + '"' : '') +
'>';
}
DoctypeEditor.prototype = {
destroy: function() {}
destroy: function() {
this.elt.remove();
}
};
/**
@ -1782,10 +1870,13 @@ DoctypeEditor.prototype = {
* @param string aTemplate The template id to use to build the editor.
*/
function TextEditor(aContainer, aNode, aTemplate) {
this.container = aContainer;
this.markup = this.container.markup;
this.node = aNode;
this.template = this.markup.template.bind(aTemplate);
this._selected = false;
aContainer.markup.template(aTemplate, this);
this.markup.template(aTemplate, this);
editableField({
element: this.value,
@ -1800,13 +1891,13 @@ function TextEditor(aContainer, aNode, aTemplate) {
longstr.string().then(oldValue => {
longstr.release().then(null, console.error);
aContainer.undo.do(() => {
this.container.undo.do(() => {
this.node.setNodeValue(aVal).then(() => {
aContainer.markup.nodeChanged(this.node);
this.markup.nodeChanged(this.node);
});
}, () => {
this.node.setNodeValue(oldValue).then(() => {
aContainer.markup.nodeChanged(this.node);
this.markup.nodeChanged(this.node);
})
});
});
@ -1859,12 +1950,11 @@ TextEditor.prototype = {
* @param Element aNode The node being edited.
*/
function ElementEditor(aContainer, aNode) {
this.doc = aContainer.doc;
this.undo = aContainer.undo;
this.template = aContainer.markup.template.bind(aContainer.markup);
this.container = aContainer;
this.markup = this.container.markup;
this.node = aNode;
this.markup = this.container.markup;
this.template = this.markup.template.bind(this.markup);
this.doc = this.markup.doc;
this.attrs = {};
@ -1911,7 +2001,7 @@ function ElementEditor(aContainer, aNode) {
let doMods = this._startModifyingAttributes();
let undoMods = this._startModifyingAttributes();
this._applyAttributes(aVal, null, doMods, undoMods);
this.undo.do(() => {
this.container.undo.do(() => {
doMods.apply();
}, function() {
undoMods.apply();
@ -2039,7 +2129,7 @@ ElementEditor.prototype = {
this._saveAttribute(aAttr.name, undoMods);
doMods.removeAttribute(aAttr.name);
this._applyAttributes(aVal, attr, doMods, undoMods);
this.undo.do(() => {
this.container.undo.do(() => {
doMods.apply();
}, () => {
undoMods.apply();
@ -2154,7 +2244,7 @@ ElementEditor.prototype = {
aOld.parentNode.removeChild(aOld);
}
this.undo.do(() => {
this.container.undo.do(() => {
swapNodes(this.rawNode, newElt);
this.markup.setNodeExpanded(newFront, this.container.expanded);
if (this.container.selected) {

View File

@ -26,7 +26,20 @@
<div id="templates" style="display:none">
<ul class="children">
<li id="template-container" save="${elt}" class="child collapsed">
<li id="template-elementcontainer" save="${elt}" class="child collapsed">
<div save="${tagLine}" class="tag-line"><!--
--><span save="${tagState}" class="tag-state"></span><!--
--><span save="${expander}" class="theme-twisty expander"></span><!--
--></div>
<ul save="${children}" class="children"></ul>
</li>
<li id="template-textcontainer" save="${elt}" class="child collapsed">
<div save="${tagLine}" class="tag-line"><span save="${tagState}" class="tag-state"></span></div>
<ul save="${children}" class="children"></ul>
</li>
<li id="template-readonlycontainer" save="${elt}" class="child collapsed">
<div save="${tagLine}" class="tag-line"><!--
--><span save="${tagState}" class="tag-state"></span><!--
--><span save="${expander}" class="theme-twisty expander"></span><!--
@ -42,6 +55,8 @@
</li>
</ul>
<span id="template-generic" save="${elt}" class="editor"><span save="${tag}" class="tag"></span></span>
<span id="template-element" save="${elt}" class="editor"><!--
--><span class="open">&lt;<!--
--><span save="${tag}" class="tag theme-fg-color3" tabindex="0"></span><!--

View File

@ -1,6 +1,7 @@
[DEFAULT]
subsuite = devtools
support-files =
doc_markup_anonymous.html
doc_markup_edit.html
doc_markup_events.html
doc_markup_events_jquery.html
@ -29,6 +30,10 @@ support-files =
lib_jquery_1.11.1_min.js
lib_jquery_2.1.1_min.js
[browser_markupview_anonymous_01.js]
[browser_markupview_anonymous_02.js]
skip-if = e10s # scratchpad.xul is not loading in e10s window
[browser_markupview_anonymous_03.js]
[browser_markupview_copy_image_data.js]
[browser_markupview_css_completion_style_attribute.js]
[browser_markupview_events.js]

View File

@ -0,0 +1,30 @@
/* 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";
// Test native anonymous content in the markupview.
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let pseudo = yield getNodeFront("#pseudo", inspector);
// Markup looks like: <div><::before /><span /><::after /></div>
let children = yield inspector.walker.children(pseudo);
is (children.nodes.length, 3, "Children returned from walker");
info ("Checking the ::before pseudo element");
let before = children.nodes[0];
yield isEditingMenuDisabled(before, inspector);
info ("Checking the normal child element");
let span = children.nodes[1];
yield isEditingMenuEnabled(span, inspector);
info ("Checking the ::after pseudo element");
let after = children.nodes[2];
yield isEditingMenuDisabled(after, inspector);
});

View File

@ -0,0 +1,29 @@
/* 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";
// Test XBL anonymous content in the markupview
const TEST_URL = "chrome://browser/content/devtools/scratchpad.xul";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let toolbarbutton = yield getNodeFront("toolbarbutton", inspector);
let children = yield inspector.walker.children(toolbarbutton);
is(toolbarbutton.numChildren, 3, "Correct number of children");
is (children.nodes.length, 3, "Children returned from walker");
is(toolbarbutton.isAnonymous, false, "Toolbarbutton is not anonymous");
yield isEditingMenuEnabled(toolbarbutton, inspector);
for (let node of children.nodes) {
ok (node.isAnonymous, "Child is anonymous");
ok (node._form.isXBLAnonymous, "Child is XBL anonymous");
ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
ok (!node._form.isNativeAnonymous, "Child is not native anonymous");
yield isEditingMenuDisabled(node, inspector);
}
});

View File

@ -0,0 +1,34 @@
/* 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";
// Test shadow DOM content in the markupview.
// Note that many features are not yet enabled, but basic listing
// of elements should be working.
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
let test = asyncTest(function*() {
Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let shadow = yield getNodeFront("#shadow", inspector.markup);
let children = yield inspector.walker.children(shadow);
is (shadow.numChildren, 3, "Children of the shadow root are counted");
is (children.nodes.length, 3, "Children returned from walker");
info ("Checking the ::before pseudo element");
let before = children.nodes[0];
yield isEditingMenuDisabled(before, inspector);
info ("Checking the <h3> shadow element");
let shadowChild1 = children.nodes[1];
yield isEditingMenuDisabled(shadowChild1, inspector);
info ("Checking the <select> shadow element");
let shadowChild2 = children.nodes[2];
yield isEditingMenuDisabled(shadowChild2, inspector);
});

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Anonymous content test</title>
<style type="text/css">
#pseudo::before {
content: "before";
}
#pseudo::after {
content: "after";
}
#shadow::before {
content: "Testing ::before on a shadow host";
}
</style>
</head>
<body>
<div id="pseudo"><span>middle</span></div>
<div id="shadow">light dom</div>
<script>
var host = document.querySelector('#shadow');
if (host.createShadowRoot) {
var root = host.createShadowRoot();
root.innerHTML = '<h3>Shadow DOM</h3><select multiple></select>';
}
</script>
</body>
</html>

View File

@ -8,6 +8,7 @@ let TargetFactory = devtools.TargetFactory;
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
let clipboard = devtools.require("sdk/clipboard");
// All test are asynchronous
waitForExplicitFinish();
@ -30,6 +31,7 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
Services.prefs.clearUserPref("devtools.dump.emit");
Services.prefs.clearUserPref("devtools.markup.pagesize");
Services.prefs.clearUserPref("dom.webcomponents.enabled");
});
// Auto close the toolbox and close the test tabs when the test ends
@ -143,12 +145,15 @@ function getNode(nodeOrSelector) {
/**
* Get the NodeFront for a given css selector, via the protocol
* @param {String} selector
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return {Promise} Resolves to the NodeFront instance
*/
function getNodeFront(selector, {walker}) {
if (selector._form) {
return selector;
}
return walker.querySelector(walker.rootNode, selector);
}
@ -173,7 +178,7 @@ function selectAndHighlightNode(nodeOrSelector, inspector) {
/**
* Set the inspector's current selection to the first match of the given css
* selector
* @param {String} selector
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not
@ -203,7 +208,7 @@ function getContainerForNodeFront(nodeFront, {markup}) {
/**
* Get the MarkupContainer object instance that corresponds to the given
* selector
* @param {String} selector
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return {MarkupContainer}
@ -236,7 +241,7 @@ function waitForChildrenUpdated({markup}) {
/**
* Simulate a mouse-over on the markup-container (a line in the markup-view)
* that corresponds to the selector passed.
* @param {String} selector
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return {Promise} Resolves when the container is hovered and the higlighter
@ -257,7 +262,7 @@ let hoverContainer = Task.async(function*(selector, inspector) {
/**
* Simulate a click on the markup-container (a line in the markup-view)
* that corresponds to the selector passed.
* @param {String} selector
* @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox
* @return {Promise} Resolves when the node has been selected.
@ -440,6 +445,124 @@ function wait(ms) {
return def.promise;
}
/**
* Wait for eventName on target.
* @param {Object} target An observable object that either supports on/off or
* addEventListener/removeEventListener
* @param {String} eventName
* @param {Boolean} useCapture Optional, for addEventListener/removeEventListener
* @return A promise that resolves when the event has been handled
*/
function once(target, eventName, useCapture=false) {
info("Waiting for event: '" + eventName + "' on " + target + ".");
let deferred = promise.defer();
for (let [add, remove] of [
["addEventListener", "removeEventListener"],
["addListener", "removeListener"],
["on", "off"]
]) {
if ((add in target) && (remove in target)) {
target[add](eventName, function onEvent(...aArgs) {
info("Got event: '" + eventName + "' on " + target + ".");
target[remove](eventName, onEvent, useCapture);
deferred.resolve.apply(deferred, aArgs);
}, useCapture);
break;
}
}
return deferred.promise;
}
/**
* Check to see if the inspector menu items for editing are disabled.
* Things like Edit As HTML, Delete Node, etc.
* @param {NodeFront} nodeFront
* @param {InspectorPanel} inspector
* @param {Boolean} assert Should this function run assertions inline.
* @return A promise that resolves with a boolean indicating whether
* the menu items are disabled once the menu has been checked.
*/
let isEditingMenuDisabled = Task.async(function*(nodeFront, inspector, assert=true) {
let deleteMenuItem = inspector.panelDoc.getElementById("node-menu-delete");
let editHTMLMenuItem = inspector.panelDoc.getElementById("node-menu-edithtml");
let pasteHTMLMenuItem = inspector.panelDoc.getElementById("node-menu-pasteouterhtml");
// To ensure clipboard contains something to paste.
clipboard.set("<p>test</p>", "html");
let menu = inspector.nodemenu;
yield selectNode(nodeFront, inspector);
yield reopenMenu(menu);
let isDeleteMenuDisabled = deleteMenuItem.hasAttribute("disabled");
let isEditHTMLMenuDisabled = editHTMLMenuItem.hasAttribute("disabled");
let isPasteHTMLMenuDisabled = pasteHTMLMenuItem.hasAttribute("disabled");
if (assert) {
ok(isDeleteMenuDisabled, "Delete menu item is disabled");
ok(isEditHTMLMenuDisabled, "Edit HTML menu item is disabled");
ok(isPasteHTMLMenuDisabled, "Paste HTML menu item is disabled");
}
return isDeleteMenuDisabled && isEditHTMLMenuDisabled && isPasteHTMLMenuDisabled;
});
/**
* Check to see if the inspector menu items for editing are enabled.
* Things like Edit As HTML, Delete Node, etc.
* @param {NodeFront} nodeFront
* @param {InspectorPanel} inspector
* @param {Boolean} assert Should this function run assertions inline.
* @return A promise that resolves with a boolean indicating whether
* the menu items are enabled once the menu has been checked.
*/
let isEditingMenuEnabled = Task.async(function*(nodeFront, inspector, assert=true) {
let deleteMenuItem = inspector.panelDoc.getElementById("node-menu-delete");
let editHTMLMenuItem = inspector.panelDoc.getElementById("node-menu-edithtml");
let pasteHTMLMenuItem = inspector.panelDoc.getElementById("node-menu-pasteouterhtml");
// To ensure clipboard contains something to paste.
clipboard.set("<p>test</p>", "html");
let menu = inspector.nodemenu;
yield selectNode(nodeFront, inspector);
yield reopenMenu(menu);
let isDeleteMenuDisabled = deleteMenuItem.hasAttribute("disabled");
let isEditHTMLMenuDisabled = editHTMLMenuItem.hasAttribute("disabled");
let isPasteHTMLMenuDisabled = pasteHTMLMenuItem.hasAttribute("disabled");
if (assert) {
ok(!isDeleteMenuDisabled, "Delete menu item is enabled");
ok(!isEditHTMLMenuDisabled, "Edit HTML menu item is enabled");
ok(!isPasteHTMLMenuDisabled, "Paste HTML menu item is enabled");
}
return !isDeleteMenuDisabled && !isEditHTMLMenuDisabled && !isPasteHTMLMenuDisabled;
});
/**
* Open a menu (closing it first if necessary).
* @param {DOMNode} menu A menu that implements hidePopup/openPopup
* @return a promise that resolves once the menu is opened.
*/
let reopenMenu = Task.async(function*(menu) {
// First close it is if it is already opened.
if (menu.state == "closing" || menu.state == "open") {
let popuphidden = once(menu, "popuphidden", true);
menu.hidePopup();
yield popuphidden;
}
// Then open it and return once
let popupshown = once(menu, "popupshown", true);
menu.openPopup();
yield popupshown;
});
/**
* Wait for all current promises to be resolved. See this as executeSoon that
* can be used with yield.

View File

@ -153,7 +153,9 @@ ElementStyle.prototype = {
// engine, we will set properties on a dummy element and observe
// how their .style attribute reflects them as computed values.
return this.dummyElementPromise = createDummyDocument().then(document => {
this.dummyElement = document.createElementNS(this.element.namespaceURI,
// ::before and ::after do not have a namespaceURI
let namespaceURI = this.element.namespaceURI || document.documentElement.namespaceURI;
this.dummyElement = document.createElementNS(namespaceURI,
this.element.tagName);
document.documentElement.appendChild(this.dummyElement);
return this.dummyElement;
@ -163,9 +165,7 @@ ElementStyle.prototype = {
destroy: function() {
this.dummyElement = null;
this.dummyElementPromise.then(dummyElement => {
if (dummyElement.parentNode) {
dummyElement.parentNode.removeChild(dummyElement);
}
dummyElement.remove();
this.dummyElementPromise = null;
}, console.error);
},
@ -1236,6 +1236,8 @@ CssRuleView.prototype = {
let accessKey = label + ".accessKey";
this.menuitemSources.setAttribute("accesskey",
_strings.GetStringFromName(accessKey));
this.menuitemAddRule.disabled = this.inspector.selection.isAnonymousNode();
},
/**
@ -1831,10 +1833,14 @@ function RuleEditor(aRuleView, aRule) {
RuleEditor.prototype = {
get isSelectorEditable() {
let toolbox = this.ruleView.inspector.toolbox;
return this.isEditable &&
let trait = this.isEditable &&
toolbox.target.client.traits.selectorEditable &&
this.rule.domRule.type !== ELEMENT_STYLE &&
this.rule.domRule.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE
this.rule.domRule.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE;
// Do not allow editing anonymousselectors until we can
// detect mutations on pseudo elements in Bug 1034110.
return trait && !this.rule.elementStyle.element.isAnonymous;
},
_create: function() {

View File

@ -34,6 +34,7 @@ support-files =
[browser_computedview_media-queries.js]
[browser_computedview_no-results-placeholder.js]
[browser_computedview_original-source-link.js]
[browser_computedview_pseudo-element_01.js]
[browser_computedview_refresh-on-style-change_01.js]
[browser_computedview_refresh-on-style-change_02.js]
[browser_computedview_search-filter.js]
@ -92,7 +93,8 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
[browser_ruleview_multiple_properties_02.js]
[browser_ruleview_original-source-link.js]
[browser_ruleview_override.js]
[browser_ruleview_pseudo-element.js]
[browser_ruleview_pseudo-element_01.js]
[browser_ruleview_pseudo-element_02.js]
[browser_ruleview_refresh-on-attribute-change_01.js]
[browser_ruleview_refresh-on-attribute-change_02.js]
[browser_ruleview_refresh-on-style-change.js]

View File

@ -0,0 +1,41 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that pseudoelements are displayed correctly in the rule view
const TEST_URI = TEST_URL_ROOT + "doc_pseudoelement.html";
let test = asyncTest(function*() {
yield addTab(TEST_URI);
let {toolbox, inspector, view} = yield openComputedView();
yield testTopLeft(inspector, view);
});
function* testTopLeft(inspector, view) {
let node = yield getNodeFront("#topleft", inspector.markup);
yield selectNode(node, inspector);
let float = getComputedViewPropertyValue(view, "float");
is(float, "left", "The computed view shows the correct float");
let children = yield inspector.markup.walker.children(node);
is (children.nodes.length, 3, "Element has correct number of children");
let beforeElement = children.nodes[0];
yield selectNode(beforeElement, inspector);
let top = getComputedViewPropertyValue(view, "top");
is(top, "0px", "The computed view shows the correct top");
let left = getComputedViewPropertyValue(view, "left");
is(left, "0px", "The computed view shows the correct left");
let afterElement = children.nodes[children.nodes.length-1];
yield selectNode(afterElement, inspector);
top = getComputedViewPropertyValue(view, "top");
is(top, "50%", "The computed view shows the correct top");
left = getComputedViewPropertyValue(view, "left");
is(left, "50%", "The computed view shows the correct left");
}

View File

@ -5,15 +5,15 @@
"use strict";
// Testing selector inplace-editor behaviors in the rule-view with pseudo
// classes and elements
// classes.
let PAGE_CONTENT = [
'<style type="text/css">',
' .testclass {',
' text-align: center;',
' }',
' #testid3:after {',
' content: "+"',
' #testid3:first-letter {',
' text-decoration: "italic"',
' }',
'</style>',
'<div id="testid">Styled Node</div>',
@ -41,11 +41,11 @@ let test = asyncTest(function*() {
info("Selecting the test element");
yield selectNode("#testid3", inspector);
yield testEditSelector(view, ".testclass2:after");
yield testEditSelector(view, ".testclass2:first-letter");
info("Selecting the modified element");
yield selectNode(".testclass2", inspector);
yield checkModifiedElement(view, ".testclass2:after");
yield checkModifiedElement(view, ".testclass2:first-letter");
});
function* testEditSelector(view, name) {

View File

@ -28,10 +28,8 @@ function* testTopLeft(inspector, view) {
elementStyle
} = yield assertPseudoElementRulesNumbers(selector, inspector, view, {
elementRulesNb: 4,
afterRulesNb: 1,
beforeRulesNb: 2,
firstLineRulesNb: 0,
firstLetterRulesNb: 0,
firstLineRulesNb: 2,
firstLetterRulesNb: 1,
selectionRulesNb: 0
});
@ -50,74 +48,62 @@ function* testTopLeft(inspector, view) {
ok (!view.element.firstChild.classList.contains("show-expandable-container"), "Pseudo Elements are collapsed by dblclicking");
let defaultView = element.ownerDocument.defaultView;
let elementRule = rules.elementRules[0];
let elementRuleView = getRuleViewRuleEditor(view, 3);
let elementAfterRule = rules.afterRules[0];
let elementAfterRuleView = [].filter.call(view.element.children[1].children, (e) => {
return e._ruleEditor && e._ruleEditor.rule === elementAfterRule;
let elementFirstLineRule = rules.firstLineRules[0];
let elementFirstLineRuleView = [].filter.call(view.element.children[1].children, (e) => {
return e._ruleEditor && e._ruleEditor.rule === elementFirstLineRule;
})[0]._ruleEditor;
is
(
convertTextPropsToString(elementAfterRule.textProps),
"background: none repeat scroll 0% 0% red; content: \" \"; position: absolute; " +
"border-radius: 50%; height: 32px; width: 32px; top: 50%; left: 50%; margin-top: -16px; margin-left: -16px",
"TopLeft after properties are correct"
convertTextPropsToString(elementFirstLineRule.textProps),
"color: orange",
"TopLeft firstLine properties are correct"
);
let elementBeforeRule = rules.beforeRules[0];
let elementBeforeRuleView = [].filter.call(view.element.children[1].children, (e) => {
return e._ruleEditor && e._ruleEditor.rule === elementBeforeRule;
})[0]._ruleEditor;
let firstProp = elementFirstLineRuleView.addProperty("background-color", "rgb(0, 255, 0)", "");
let secondProp = elementFirstLineRuleView.addProperty("font-style", "italic", "");
is
(
convertTextPropsToString(elementBeforeRule.textProps),
"top: 0px; left: 0px",
"TopLeft before properties are correct"
);
let firstProp = elementAfterRuleView.addProperty("background-color", "rgb(0, 255, 0)", "");
let secondProp = elementAfterRuleView.addProperty("padding", "100px", "");
is (firstProp, elementAfterRule.textProps[elementAfterRule.textProps.length - 2],
is (firstProp, elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 2],
"First added property is on back of array");
is (secondProp, elementAfterRule.textProps[elementAfterRule.textProps.length - 1],
is (secondProp, elementFirstLineRule.textProps[elementFirstLineRule.textProps.length - 1],
"Second added property is on back of array");
yield elementAfterRule._applyingModifications;
yield elementFirstLineRule._applyingModifications;
is((yield getComputedStyleProperty(selector, ":after", "background-color")),
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
"rgb(0, 255, 0)", "Added property should have been used.");
is((yield getComputedStyleProperty(selector, ":after", "padding-top")),
"100px", "Added property should have been used.");
is((yield getComputedStyleProperty(selector, null, "padding-top")),
"32px", "Added property should not apply to element");
is((yield getComputedStyleProperty(selector, ":first-line", "font-style")),
"italic", "Added property should have been used.");
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
"none", "Added property should not apply to element");
secondProp.setEnabled(false);
yield elementAfterRule._applyingModifications;
firstProp.setEnabled(false);
yield elementFirstLineRule._applyingModifications;
is((yield getComputedStyleProperty(selector, ":after", "padding-top")), "0px",
"Disabled property should have been used.");
is((yield getComputedStyleProperty(selector, null, "padding-top")), "32px",
"Added property should not apply to element");
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
"rgb(255, 0, 0)", "Disabled property should now have been used.");
is((yield getComputedStyleProperty(selector, null, "background-color")),
"rgb(221, 221, 221)", "Added property should not apply to element");
secondProp.setEnabled(true);
yield elementAfterRule._applyingModifications;
firstProp.setEnabled(true);
yield elementFirstLineRule._applyingModifications;
is((yield getComputedStyleProperty(selector, ":after", "padding-top")), "100px",
"Enabled property should have been used.");
is((yield getComputedStyleProperty(selector, null, "padding-top")), "32px",
"Added property should not apply to element");
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
"rgb(0, 255, 0)", "Added property should have been used.");
is((yield getComputedStyleProperty(selector, null, "text-decoration")),
"none", "Added property should not apply to element");
firstProp = elementRuleView.addProperty("background-color", "rgb(0, 0, 255)", "");
yield elementRule._applyingModifications;
is((yield getComputedStyleProperty(selector, null, "background-color")), "rgb(0, 0, 255)",
"Added property should have been used.");
is((yield getComputedStyleProperty(selector, ":after", "background-color")), "rgb(0, 255, 0)",
"Added prop does not apply to pseudo");
is((yield getComputedStyleProperty(selector, null, "background-color")),
"rgb(0, 0, 255)", "Added property should have been used.");
is((yield getComputedStyleProperty(selector, ":first-line", "background-color")),
"rgb(0, 255, 0)", "Added prop does not apply to pseudo");
}
function* testTopRight(inspector, view) {
@ -127,10 +113,8 @@ function* testTopRight(inspector, view) {
elementStyle
} = yield assertPseudoElementRulesNumbers("#topright", inspector, view, {
elementRulesNb: 4,
afterRulesNb: 1,
beforeRulesNb: 2,
firstLineRulesNb: 0,
firstLetterRulesNb: 0,
firstLineRulesNb: 1,
firstLetterRulesNb: 1,
selectionRulesNb: 0
});
@ -146,10 +130,8 @@ function* testTopRight(inspector, view) {
function* testBottomRight(inspector, view) {
yield assertPseudoElementRulesNumbers("#bottomright", inspector, view, {
elementRulesNb: 4,
afterRulesNb: 1,
beforeRulesNb: 3,
firstLineRulesNb: 0,
firstLetterRulesNb: 0,
firstLineRulesNb: 1,
firstLetterRulesNb: 1,
selectionRulesNb: 0
});
}
@ -157,10 +139,8 @@ function* testBottomRight(inspector, view) {
function* testBottomLeft(inspector, view) {
yield assertPseudoElementRulesNumbers("#bottomleft", inspector, view, {
elementRulesNb: 4,
afterRulesNb: 1,
beforeRulesNb: 2,
firstLineRulesNb: 0,
firstLetterRulesNb: 0,
firstLineRulesNb: 1,
firstLetterRulesNb: 1,
selectionRulesNb: 0
});
}
@ -172,8 +152,6 @@ function* testParagraph(inspector, view) {
elementStyle
} = yield assertPseudoElementRulesNumbers("#bottomleft p", inspector, view, {
elementRulesNb: 3,
afterRulesNb: 0,
beforeRulesNb: 0,
firstLineRulesNb: 1,
firstLetterRulesNb: 1,
selectionRulesNb: 1
@ -241,8 +219,6 @@ function* assertPseudoElementRulesNumbers(selector, inspector, view, ruleNbs) {
let rules = {
elementRules: elementStyle.rules.filter(rule => !rule.pseudoElement),
afterRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":after"),
beforeRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":before"),
firstLineRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":first-line"),
firstLetterRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":first-letter"),
selectionRules: elementStyle.rules.filter(rule => rule.pseudoElement === ":-moz-selection")
@ -250,10 +226,6 @@ function* assertPseudoElementRulesNumbers(selector, inspector, view, ruleNbs) {
is(rules.elementRules.length, ruleNbs.elementRulesNb, selector +
" has the correct number of non pseudo element rules");
is(rules.afterRules.length, ruleNbs.afterRulesNb, selector +
" has the correct number of :after rules");
is(rules.beforeRules.length, ruleNbs.beforeRulesNb, selector +
" has the correct number of :before rules");
is(rules.firstLineRules.length, ruleNbs.firstLineRulesNb, selector +
" has the correct number of :first-line rules");
is(rules.firstLetterRules.length, ruleNbs.firstLetterRulesNb, selector +
@ -270,5 +242,6 @@ function assertGutters(view) {
is (gutters[0].textContent, "Pseudo-elements", "Gutter heading is correct");
is (gutters[1].textContent, "This Element", "Gutter heading is correct");
is (gutters[2].textContent, "Inherited from body", "Gutter heading is correct");
return gutters;
}
}

View File

@ -0,0 +1,32 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that pseudoelements are displayed correctly in the rule view
const TEST_URI = TEST_URL_ROOT + "doc_pseudoelement.html";
let test = asyncTest(function*() {
yield addTab(TEST_URI);
let {toolbox, inspector, view} = yield openRuleView();
yield testTopLeft(inspector, view);
});
function* testTopLeft(inspector, view) {
let node = inspector.markup.walker.frontForRawNode(getNode("#topleft"));
let children = yield inspector.markup.walker.children(node);
is (children.nodes.length, 3, "Element has correct number of children");
let beforeElement = children.nodes[0];
is (beforeElement.tagName, "_moz_generated_content_before", "tag name is correct");
yield selectNode(beforeElement, inspector);
let afterElement = children.nodes[children.nodes.length-1];
is (afterElement.tagName, "_moz_generated_content_after", "tag name is correct");
yield selectNode(afterElement, inspector);
}

View File

@ -18,6 +18,15 @@ body {
position:relative;
}
.box:first-line {
color: orange;
background: red;
}
.box:first-letter {
color: green;
}
* {
cursor: default;
}
@ -69,6 +78,13 @@ p:first-letter {
left:0;
}
.topleft:first-line {
color: orange;
}
.topleft::selection {
color: orange;
}
.topright:before {
top:0;
right:0;

View File

@ -163,19 +163,25 @@ let selectAndHighlightNode = Task.async(function*(selector, inspector) {
yield updated;
});
/**
* Set the inspector's current selection to a node that matches the given css
* selector.
* @param {String} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
/*
* Set the inspector's current selection to a node or to the first match of the
* given css selector.
* @param {String|NodeFront}
* data The node to select
* @param {InspectorPanel} inspector
* The instance of InspectorPanel currently
* loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not
* to highlight the node upon selection
* @param {String} reason
* Defaults to "test" which instructs the inspector not
* to highlight the node upon selection
* @return {Promise} Resolves when the inspector is updated with the new node
*/
let selectNode = Task.async(function*(selector, inspector, reason="test") {
info("Selecting the node for '" + selector + "'");
let nodeFront = yield getNodeFront(selector, inspector);
let selectNode = Task.async(function*(data, inspector, reason="test") {
info("Selecting the node for '" + data + "'");
let nodeFront = data;
if (!data._form) {
nodeFront = yield getNodeFront(data, inspector);
}
let updated = inspector.once("inspector-updated");
inspector.selection.setNodeFront(nodeFront, reason);
yield updated;

View File

@ -23,10 +23,9 @@
; Mac bundle stuff
@APPNAME@/Contents/Info.plist
@APPNAME@/Contents/PkgInfo
@APPNAME@/Contents/Resources/
#ifdef MOZ_SIGNING
@APPNAME@/Contents/_CodeSignature/CodeResources
#endif
@APPNAME@/Contents/Resources/firefox.icns
@APPNAME@/Contents/Resources/document.icns
@APPNAME@/Contents/Resources/en.lproj/*
#endif
[@AB_CD@]
@ -55,16 +54,32 @@
#ifdef GKMEDIAS_SHARED_LIBRARY
@BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@
#endif
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@
#endif
#ifdef MOZ_SHARED_MOZGLUE
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@mozglue@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@mozglue@DLL_SUFFIX@
#endif
#endif
#ifndef MOZ_STATIC_JS
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@mozjs@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@mozjs@DLL_SUFFIX@
#endif
#endif
#ifdef MOZ_DMD
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@dmd@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@dmd@DLL_SUFFIX@
#endif
#endif
#ifndef MOZ_NATIVE_NSPR
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
@ -73,13 +88,13 @@
#endif
#endif
#ifdef XP_MACOSX
@BINPATH@/XUL
@APPNAME@/Contents/MacOS/XUL
#else
@BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
#endif
#ifdef XP_MACOSX
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
@BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
@APPNAME@/Contents/MacOS/@MOZ_CHILD_PROCESS_NAME@.app/
@APPNAME@/Contents/MacOS/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
#else
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@
#endif
@ -109,9 +124,13 @@
#endif
#ifdef MOZ_REPLACE_MALLOC
#ifndef MOZ_JEMALLOC3
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@replace_jemalloc@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@replace_jemalloc@DLL_SUFFIX@
#endif
#endif
#endif
#ifdef MOZ_GTK3
@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
@ -121,6 +140,9 @@
; [Base Browser Files]
#ifndef XP_UNIX
@BINPATH@/@MOZ_APP_NAME@.exe
#elif XP_MACOSX
@APPNAME@/Contents/MacOS/@MOZ_APP_NAME@-bin
@APPNAME@/Contents/MacOS/@MOZ_APP_NAME@
#else
@BINPATH@/@MOZ_APP_NAME@-bin
@BINPATH@/@MOZ_APP_NAME@
@ -741,19 +763,33 @@
; meaning their .chk files are created there directly.
;
#ifndef MOZ_NATIVE_NSS
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@freebl3@DLL_SUFFIX@
@APPNAME@/Contents/MacOS/@DLL_PREFIX@nss3@DLL_SUFFIX@
@APPNAME@/Contents/MacOS/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@freebl3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
#endif
#ifndef NSS_DISABLE_DBM
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@nssdbm3@DLL_SUFFIX@
#endif
#endif
#ifndef MOZ_FOLD_LIBS
@BINPATH@/@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@smime3@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@ssl3@DLL_SUFFIX@
#endif
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/@DLL_PREFIX@softokn3@DLL_SUFFIX@
#else
@BINPATH@/@DLL_PREFIX@softokn3@DLL_SUFFIX@
#endif
#endif
@BINPATH@/chrome/pippki@JAREXT@
@BINPATH@/chrome/pippki.manifest
@BINPATH@/components/pipboot.xpt
@ -780,7 +816,7 @@ bin/libfreebl_32int64_3.so
;
#ifdef MOZ_UPDATER
#ifdef XP_MACOSX
@BINPATH@/updater.app/
@APPNAME@/Contents/MacOS/updater.app/
#else
@BINPATH@/updater@BIN_SUFFIX@
#endif
@ -800,7 +836,7 @@ bin/libfreebl_32int64_3.so
@BINPATH@/components/CrashService.js
@BINPATH@/components/toolkit_crashservice.xpt
#ifdef XP_MACOSX
@BINPATH@/crashreporter.app/
@APPNAME@/Contents/MacOS/crashreporter.app/
#else
@BINPATH@/crashreporter@BIN_SUFFIX@
@BINPATH@/crashreporter.ini
@ -819,7 +855,11 @@ bin/libfreebl_32int64_3.so
#ifdef XP_WIN
@BINPATH@/webapp-uninstaller@BIN_SUFFIX@
#endif
#ifdef XP_MACOSX
@APPNAME@/Contents/MacOS/webapprt-stub@BIN_SUFFIX@
#else
@BINPATH@/webapprt-stub@BIN_SUFFIX@
#endif
@BINPATH@/webapprt/webapprt.ini
@BINPATH@/webapprt/chrome.manifest
@BINPATH@/webapprt/chrome/webapprt@JAREXT@

View File

@ -52,51 +52,75 @@
#
# Example: path/to/dir/*
# File Removals
# Due to Apple Mac OS X packaging requirements files that are in the same
# directory on other platforms must be located in different directories on
# Mac OS X. The following defines allow specifying the Mac OS X bundle
# location which also work on other platforms.
#
# @DIR_MACOS@
# Equals Contents/MacOS/ on Mac OX X and is an empty string on other platforms.
#
# @DIR_RESOURCES@
# Equals Contents/Resources/ on Mac OX X and is an empty string on other
# platforms.
# Common File Removals
# This is located under the "distribution/" directory and it was added before
# Firefox 27
distribution/extensions/testpilot@labs.mozilla.com.xpi
@DIR_MACOS@distribution/extensions/testpilot@labs.mozilla.com.xpi
# Some users are ending up with unpacked chrome instead of omni.ja. This
# causes updates to break badly, see bug 1063052. Removing the toplevel
# chrome.manifest causes us to use the updated omni.ja.
#ifndef MOZ_GTK
chrome.manifest
@DIR_MACOS@chrome.manifest
#ifdef XP_MACOSX
@DIR_RESOURCES@chrome.manifest
#endif
#endif
# Directory removals
chrome/
# Mac OS X v2 signing removals
#ifdef XP_MACOSX
@DIR_MACOS@active-update.xml
@DIR_MACOS@update-settings.ini
@DIR_MACOS@updates.xml
@DIR_MACOS@defaults/*
@DIR_MACOS@updates/*
#endif
# Common Directory removals
@DIR_MACOS@chrome/
#ifdef XP_UNIX
#ifndef XP_MACOSX
chrome/icons/
chrome/icons/default/
#endif
#endif
chrome/overlayinfo/
components/
defaults/autoconfig/
defaults/profile/
defaults/profile/chrome/
defaults/profile/US/*
defaults/profile/extensions/
defaults/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
distribution/
distribution/extensions/
extensions/
extensions/inspector@mozilla.org/*
extensions/reporter@mozilla.org/*
extensions/talkback@mozilla.org/*
extensions/testpilot@labs.mozilla.com/*
extensions/{641d8d09-7dda-4850-8228-ac0ab65e2ac9}/*
extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
greprefs/
jssubloader/
modules/
@DIR_MACOS@chrome/overlayinfo/
@DIR_MACOS@components/
@DIR_MACOS@defaults/autoconfig/
@DIR_MACOS@defaults/profile/
@DIR_MACOS@defaults/profile/chrome/
@DIR_MACOS@defaults/profile/US/*
@DIR_MACOS@defaults/profile/extensions/
@DIR_MACOS@defaults/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
@DIR_MACOS@distribution/
@DIR_MACOS@distribution/extensions/
@DIR_MACOS@extensions/
@DIR_MACOS@extensions/inspector@mozilla.org/*
@DIR_MACOS@extensions/reporter@mozilla.org/*
@DIR_MACOS@extensions/talkback@mozilla.org/*
@DIR_MACOS@extensions/testpilot@labs.mozilla.com/*
@DIR_MACOS@extensions/{641d8d09-7dda-4850-8228-ac0ab65e2ac9}/*
@DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
@DIR_MACOS@greprefs/
@DIR_MACOS@jssubloader/
@DIR_MACOS@modules/
#ifdef XP_MACOSX
plugins/Default Plugin.plugin/*
plugins/JavaEmbeddingPlugin.bundle/*
plugins/MRJPlugin.plugin/*
../Plug-Ins/PrintPDE.plugin/*
@DIR_MACOS@plugins/Default Plugin.plugin/*
@DIR_MACOS@plugins/JavaEmbeddingPlugin.bundle/*
@DIR_MACOS@plugins/MRJPlugin.plugin/*
Contents/Plug-Ins/PrintPDE.plugin/*
#endif
searchplugins/*
webapprt/components/
@DIR_MACOS@searchplugins/*
@DIR_MACOS@webapprt/components/

View File

@ -212,6 +212,8 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY addons.label "Add-ons">
<!ENTITY addons.accesskey "A">
<!ENTITY addons.commandkey "A">
<!ENTITY webapps.label "Apps">
<!ENTITY webapps.accesskey "p">
<!ENTITY webDeveloperMenu.label "Web Developer">
<!ENTITY webDeveloperMenu.accesskey "W">

View File

@ -102,3 +102,6 @@ loop-call-button.tooltiptext = Invite someone to talk
panic-button.label = Forget
panic-button.tooltiptext = Forget about some browsing history
web-apps-button.label = Apps
web-apps-button.tooltiptext = Discover Apps

View File

@ -23,9 +23,6 @@ const PREF_BRANCH = "browser.newtab.";
// The interval between swapping in a preload docShell and kicking off the
// next preload in the background.
const PRELOADER_INTERVAL_MS = 600;
// The initial delay before we start preloading our first new tab page. The
// timer is started after the first 'browser-delayed-startup' has been sent.
const PRELOADER_INIT_DELAY_MS = 5000;
// The number of miliseconds we'll wait after we received a notification that
// causes us to update our list of browsers and tabbrowser sizes. This acts as
// kind of a damper when too many events are occuring in quick succession.
@ -52,17 +49,20 @@ function clearTimer(timer) {
this.BrowserNewTabPreloader = {
init: function Preloader_init() {
Initializer.start();
Preferences.init();
},
uninit: function Preloader_uninit() {
Initializer.stop();
HostFrame.destroy();
Preferences.uninit();
HiddenBrowsers.uninit();
},
newTab: function Preloader_newTab(aTab) {
if (!Preferences.enabled) {
return false;
}
let win = aTab.ownerDocument.defaultView;
if (win.gBrowser) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
@ -81,47 +81,6 @@ this.BrowserNewTabPreloader = {
Object.freeze(BrowserNewTabPreloader);
let Initializer = {
_timer: null,
_observing: false,
start: function Initializer_start() {
Services.obs.addObserver(this, TOPIC_DELAYED_STARTUP, false);
this._observing = true;
},
stop: function Initializer_stop() {
this._timer = clearTimer(this._timer);
if (this._observing) {
Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
this._observing = false;
}
},
observe: function Initializer_observe(aSubject, aTopic, aData) {
if (aTopic == TOPIC_DELAYED_STARTUP) {
Services.obs.removeObserver(this, TOPIC_DELAYED_STARTUP);
this._observing = false;
this._startTimer();
} else if (aTopic == TOPIC_TIMER_CALLBACK) {
this._timer = null;
this._startPreloader();
}
},
_startTimer: function Initializer_startTimer() {
this._timer = createTimer(this, PRELOADER_INIT_DELAY_MS);
},
_startPreloader: function Initializer_startPreloader() {
Preferences.init();
if (Preferences.enabled) {
HiddenBrowsers.init();
}
}
};
let Preferences = {
_enabled: null,
_branch: null,
@ -153,8 +112,6 @@ let Preferences = {
if (prevEnabled && !this.enabled) {
HiddenBrowsers.uninit();
} else if (!prevEnabled && this.enabled) {
HiddenBrowsers.init();
}
},
};
@ -168,7 +125,7 @@ let HiddenBrowsers = {
TOPIC_XUL_WINDOW_CLOSED
],
init: function () {
_init: function () {
this._browsers = new Map();
this._updateBrowserSizes();
this._topics.forEach(t => Services.obs.addObserver(this, t, false));
@ -187,9 +144,9 @@ let HiddenBrowsers = {
},
get: function (width, height) {
// We haven't been initialized, yet.
// Initialize if this is the first call.
if (!this._browsers) {
return null;
this._init();
}
let key = width + "x" + height;

View File

@ -96,6 +96,7 @@ XPCOMUtils.defineLazyGetter(this, "PALETTE_ITEMS", function() {
"email-link-button",
"sync-button",
"tabview-button",
"web-apps-button",
];
let panelPlacements = DEFAULT_AREA_PLACEMENTS["PanelUI-contents"];

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -1131,6 +1131,13 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
transform: scaleX(-1);
}
#web-apps-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 1440px, 36px, 1404px);
}
#web-apps-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(36px, 1440px, 72px, 1404px);
}
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon,
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
@ -1290,6 +1297,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
-moz-image-region: rect(64px, 1792px, 128px, 1728px);
}
#web-apps-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #web-apps-button {
-moz-image-region: rect(0, 1856px, 64px, 1792px);
}
toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 1664px, 64px, 1600px);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -2,7 +2,7 @@
% Note that zoom-reset-button is a bit different since it doesn't use an image and thus has the image with display: none.
%define nestedButtons #zoom-out-button, #zoom-reset-button, #zoom-in-button, #cut-button, #copy-button, #paste-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button, #panic-button
%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #switch-to-metro-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button, #panic-button, #web-apps-button
%ifdef XP_MACOSX
% Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen

View File

@ -156,6 +156,11 @@ toolbarpaletteitem[place="palette"] > #panic-button {
-moz-image-region: rect(32px, 896px, 64px, 864px);
}
#web-apps-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #web-apps-button {
-moz-image-region: rect(0, 928px, 32px, 896px);
}
toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 832px, 32px, 800px);
}

View File

@ -190,6 +190,15 @@ toolbar[brighttext] #sync-button[status="active"]:hover:active:not([disabled="tr
transform: scaleX(-1);
}
#web-apps-button[cui-areatype="toolbar"] {
-moz-image-region: rect(0, 720px, 18px, 702px);
}
%ifdef XP_MACOSX
#web-apps-button[cui-areatype="toolbar"]:hover:active:not([disabled="true"]) {
-moz-image-region: rect(18px, 720px, 36px, 702px);
}
%endif
#loop-call-button > .toolbarbutton-badge-container {
list-style-image: url(chrome://browser/skin/loop/toolbar.png);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -390,7 +390,10 @@ def environment(xrePath, env=None, crashreporter=True, debugger=False, dmdPath=N
assert os.path.isabs(xrePath)
ldLibraryPath = xrePath
if mozinfo.isMac:
ldLibraryPath = os.path.join(os.path.dirname(xrePath), "MacOS")
else:
ldLibraryPath = xrePath
envVar = None
dmdLibrary = None

View File

@ -44,14 +44,17 @@ def generate_precomplete(root_path):
application update instructions. The given directory is used
for the location to enumerate and to create the precomplete file.
"""
rel_path_precomplete = "precomplete"
# If inside a Mac bundle use the root of the bundle for the path.
if os.path.basename(root_path) == "MacOS":
if os.path.basename(root_path) == "Resources":
root_path = os.path.abspath(os.path.join(root_path, '../../'))
rel_path_precomplete = "Contents/Resources/precomplete"
rel_file_path_list, rel_dir_path_list = get_build_entries(root_path)
precomplete_file_path = os.path.join(root_path,"precomplete")
# open in binary mode to prevent OS specific line endings.
precomplete_file_path = os.path.join(root_path,rel_path_precomplete)
# Open the file so it exists before building the list of files and open it
# in binary mode to prevent OS specific line endings.
precomplete_file = open(precomplete_file_path, "wb")
rel_file_path_list, rel_dir_path_list = get_build_entries(root_path)
for rel_file_path in rel_file_path_list:
precomplete_file.writelines("remove \""+rel_file_path+"\"\n")

View File

@ -135,15 +135,26 @@ GeckoChildProcessHost::GetPathToBinary(FilePath& exePath)
MOZ_ASSERT(gGREPath);
#ifdef OS_WIN
exePath = FilePath(char16ptr_t(gGREPath));
#elif MOZ_WIDGET_COCOA
nsCOMPtr<nsIFile> childProcPath;
NS_NewLocalFile(nsDependentString(gGREPath), false,
getter_AddRefs(childProcPath));
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225.
nsCOMPtr<nsIFile> tempPath;
childProcPath->GetParent(getter_AddRefs(tempPath));
childProcPath = tempPath.forget();
childProcPath->AppendNative(NS_LITERAL_CSTRING("MacOS"));
childProcPath->AppendNative(NS_LITERAL_CSTRING("plugin-container.app"));
childProcPath->AppendNative(NS_LITERAL_CSTRING("Contents"));
childProcPath->AppendNative(NS_LITERAL_CSTRING("MacOS"));
nsCString tempCPath;
childProcPath->GetNativePath(tempCPath);
exePath = FilePath(tempCPath.get());
#else
nsCString path;
NS_CopyUnicodeToNative(nsDependentString(gGREPath), path);
exePath = FilePath(path.get());
#endif
#ifdef MOZ_WIDGET_COCOA
// We need to use an App Bundle on OS X so that we can hide
// the dock icon. See Bug 557225.
exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_BUNDLE);
#endif
}
@ -539,7 +550,17 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
if (ShouldHaveDirectoryService()) {
MOZ_ASSERT(gGREPath);
nsCString path;
#ifdef MOZ_WIDGET_COCOA
nsCOMPtr<nsIFile> grePath;
nsCOMPtr<nsIFile> tempPath;
NS_NewLocalFile(nsDependentString(gGREPath), false,
getter_AddRefs(grePath));
grePath->GetParent(getter_AddRefs(tempPath));
tempPath->AppendNative(NS_LITERAL_CSTRING("MacOS"));
tempPath->GetNativePath(path);
#else
NS_CopyUnicodeToNative(nsDependentString(gGREPath), path);
#endif
# if defined(OS_LINUX) || defined(OS_BSD)
# if defined(MOZ_WIDGET_ANDROID)
path += "/lib";

View File

@ -1351,6 +1351,23 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
argc -= 2;
argv += 2;
} else {
#ifdef XP_MACOSX
// On OSX, the GreD needs to point to Contents/Resources in the .app
// bundle. Libraries will be loaded at a relative path to GreD, i.e.
// ../MacOS.
XRE_GetFileFromPath(argv[0], getter_AddRefs(greDir));
nsCOMPtr<nsIFile> parentDir;
greDir->GetParent(getter_AddRefs(parentDir));
parentDir->GetParent(getter_AddRefs(greDir));
greDir->AppendNative(NS_LITERAL_CSTRING("Resources"));
bool dirExists = false;
greDir->Exists(&dirExists);
if (!dirExists) {
printf("Setting GreD failed.\n");
return 1;
}
dirprovider.SetGREDir(greDir);
#else
nsAutoString workingDir;
if (!GetCurrentWorkingDirectory(workingDir)) {
printf("GetCurrentWorkingDirectory failed.\n");
@ -1361,6 +1378,7 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
printf("NS_NewLocalFile failed.\n");
return 1;
}
#endif
}
if (argc > 1 && !strcmp(argv[1], "-a")) {

View File

@ -78,8 +78,8 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
@ -521,14 +521,8 @@ public class BrowserApp extends GeckoApp
final String args = intent.getStringExtra("args");
if (GuestSession.shouldUse(this, args)) {
GuestSession.configureWindow(getWindow());
mProfile = GeckoProfile.createGuestProfile(this);
} else {
// We also allow non-guest sessions if the keyguard isn't a secure one.
final KeyguardManager manager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (Versions.feature16Plus && !manager.isKeyguardSecure()) {
GuestSession.configureWindow(getWindow());
}
GeckoProfile.maybeCleanupGuestProfile(this);
}
@ -757,6 +751,7 @@ public class BrowserApp extends GeckoApp
final String args = getIntent().getStringExtra("args");
// If an external intent tries to start Fennec in guest mode, and it's not already
// in guest mode, this will change modes before opening the url.
// NOTE: OnResume is called twice sometimes when showing on the lock screen.
final boolean enableGuestSession = GuestSession.shouldUse(this, args);
final boolean inGuestSession = GeckoProfile.get(this).inGuestMode();
if (enableGuestSession != inGuestSession) {
@ -765,6 +760,19 @@ public class BrowserApp extends GeckoApp
return;
}
final KeyguardManager manager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
// The test machines return null for the KeyguardService, despite running Android 4.2.
if (Versions.feature11Plus && manager != null) {
// If the keyguard is showing AND we're either in guest mode or the keyguard is insecure,
// allow showing this window. We do this in onResume so that we can avoid setting these flags if the keyguard
// is not showing since it affects Android's layout of the window.
if (manager.isKeyguardLocked() && (GeckoProfile.get(this).inGuestMode() || !manager.isKeyguardSecure())) {
GuestSession.configureWindow(getWindow());
} else {
GuestSession.unconfigureWindow(getWindow());
}
}
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
"Prompt:ShowTop");
}

View File

@ -34,6 +34,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import java.util.Arrays;
@ -60,12 +61,14 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
}
private PopupType mPopupType;
private static final int MAX_VISIBLE_ROWS = 5;
private static int sAutoCompleteMinWidth;
private static int sAutoCompleteRowHeight;
private static int sValidationMessageHeight;
private static int sValidationTextMarginTop;
private static RelativeLayout.LayoutParams sValidationTextLayoutNormal;
private static RelativeLayout.LayoutParams sValidationTextLayoutInverted;
private static LayoutParams sValidationTextLayoutNormal;
private static LayoutParams sValidationTextLayoutInverted;
private static final String LOGTAG = "GeckoFormAssistPopup";
@ -214,10 +217,10 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
sValidationTextMarginTop = (int) (mContext.getResources().getDimension(R.dimen.validation_message_margin_top));
sValidationTextLayoutNormal = new RelativeLayout.LayoutParams(mValidationMessageText.getLayoutParams());
sValidationTextLayoutNormal = new LayoutParams(mValidationMessageText.getLayoutParams());
sValidationTextLayoutNormal.setMargins(0, sValidationTextMarginTop, 0, 0);
sValidationTextLayoutInverted = new RelativeLayout.LayoutParams((ViewGroup.MarginLayoutParams) sValidationTextLayoutNormal);
sValidationTextLayoutInverted = new LayoutParams((ViewGroup.MarginLayoutParams) sValidationTextLayoutNormal);
sValidationTextLayoutInverted.setMargins(0, 0, 0, 0);
mValidationMessageArrow = (ImageView) mValidationMessage.findViewById(R.id.validation_message_arrow);
@ -260,15 +263,18 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
// Don't show the form assist popup when using fullscreen VKB
InputMethodManager imm =
(InputMethodManager) GeckoAppShell.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isFullscreenMode())
(InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isFullscreenMode()) {
return;
}
// Hide/show the appropriate popup contents
if (mAutoCompleteList != null)
if (mAutoCompleteList != null) {
mAutoCompleteList.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? VISIBLE : GONE);
if (mValidationMessage != null)
}
if (mValidationMessage != null) {
mValidationMessage.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? GONE : VISIBLE);
}
if (sAutoCompleteMinWidth == 0) {
Resources res = mContext.getResources();
@ -287,7 +293,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
int width = (int) (mW * zoom);
int height = (int) (mH * zoom);
int popupWidth = RelativeLayout.LayoutParams.MATCH_PARENT;
int popupWidth = LayoutParams.MATCH_PARENT;
int popupLeft = left < 0 ? 0 : left;
FloatSize viewport = aMetrics.getSize();
@ -302,16 +308,24 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
popupWidth = sAutoCompleteMinWidth;
// Move the popup to the left if there isn't enough room for it.
if ((popupLeft + popupWidth) > viewport.width)
if ((popupLeft + popupWidth) > viewport.width) {
popupLeft = (int) (viewport.width - popupWidth);
}
}
}
int popupHeight;
if (mPopupType == PopupType.AUTOCOMPLETE)
popupHeight = sAutoCompleteRowHeight * mAutoCompleteList.getAdapter().getCount();
else
if (mPopupType == PopupType.AUTOCOMPLETE) {
// Limit the amount of visible rows.
int rows = mAutoCompleteList.getAdapter().getCount();
if (rows > MAX_VISIBLE_ROWS) {
rows = MAX_VISIBLE_ROWS;
}
popupHeight = sAutoCompleteRowHeight * rows;
} else {
popupHeight = sValidationMessageHeight;
}
int popupTop = top + height;
@ -346,8 +360,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
}
}
RelativeLayout.LayoutParams layoutParams =
new RelativeLayout.LayoutParams(popupWidth, popupHeight);
LayoutParams layoutParams = new LayoutParams(popupWidth, popupHeight);
layoutParams.setMargins(popupLeft, popupTop, 0, 0);
setLayoutParams(layoutParams);
requestLayout();

View File

@ -73,9 +73,14 @@ public class GuestSession {
public static void configureWindow(Window window) {
// In guest sessions we allow showing over the keyguard.
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
public static void unconfigureWindow(Window window) {
// In guest sessions we allow showing over the keyguard.
window.clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
}
private static PendingIntent getNotificationIntent(Context context) {
Intent intent = new Intent(NOTIFICATION_INTENT);
intent.setClass(context, BrowserApp.class);

View File

@ -102,7 +102,7 @@ public class RemoteClientsDialogFragment extends DialogFragment {
final AlertDialog dialog = (AlertDialog) dialogInterface;
final SparseBooleanArray checkedItemPositions = dialog.getListView().getCheckedItemPositions();
final ArrayList<RemoteClient> checked = new ArrayList<RemoteClient>();
for (int i = 0; i < checkedItemPositions.size(); i++) {
for (int i = 0; i < clients.size(); i++) {
if (checkedItemPositions.get(i)) {
checked.add(clients.get(i));
}

View File

@ -275,6 +275,11 @@ class JavaPanZoomController
} else if (MESSAGE_TOUCH_LISTENER.equals(event)) {
int tabId = message.getInt("tabID");
final Tab tab = Tabs.getInstance().getTab(tabId);
// Make sure we still have a Tab
if (tab == null) {
return;
}
tab.setHasTouchListeners(true);
mTarget.post(new Runnable() {
@Override

View File

@ -7,8 +7,8 @@
android:color="@color/text_color_primary_inverse"/>
<item android:state_checked="true"
android:color="@color/text_color_primary"/>
android:color="@color/text_color_secondary"/>
<item android:color="@color/text_color_tertiary_inverse"/>
<item android:color="@color/text_color_secondary"/>
</selector>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<level-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<item android:maxLevel="1">
<selector>
<item gecko:state_private="true" android:drawable="@drawable/menu_pb"/>
<item android:drawable="@drawable/new_tablet_menu"/>
</selector>
</item>
<item android:maxLevel="2" android:drawable="@android:color/transparent"/>
</level-list>

View File

@ -91,7 +91,7 @@
style="@style/UrlBar.ImageButton"
android:layout_alignLeft="@id/menu"
android:layout_alignRight="@id/menu"
android:src="@drawable/new_tablet_menu_level"
android:src="@drawable/new_tablet_menu"
android:visibility="gone"/>
<!-- We draw after the menu items so when they are hidden, the cancel button,

View File

@ -10,7 +10,7 @@
android:paddingLeft="4dip"
android:background="@drawable/new_tablet_tabs_count_foreground"
android:textAppearance="@style/TextAppearance.Micro"
android:textColor="#eeeeee"
android:textColor="@color/text_color_primary_inverse"
android:textStyle="bold"
android:duplicateParentState="true"
android:gravity="center"/>

View File

@ -44,7 +44,7 @@
<color name="text_color_tertiary">#9198A1</color>
<!-- Default inverse colors -->
<color name="text_color_primary_inverse">#FFFFFF</color>
<color name="text_color_primary_inverse">#F5F5F5</color>
<color name="text_color_secondary_inverse">#DDDDDD</color>
<color name="text_color_tertiary_inverse">#A4A7A9</color>

View File

@ -12,7 +12,9 @@
<dimen name="browser_toolbar_button_padding">12dp</dimen>
<dimen name="browser_toolbar_icon_width">48dp</dimen>
<dimen name="browser_toolbar_lock_width">20dp</dimen>
<dimen name="browser_toolbar_favicon_size">25.33dip</dimen>
<!-- favicon_size includes 4dp of right padding. We can't use margin (which would allow us to
specify the actual size) because that would decrease the size of our hit target. -->
<dimen name="browser_toolbar_favicon_size">21.33dip</dimen>
<dimen name="browser_toolbar_shadow_size">2dp</dimen>
<dimen name="new_tablet_tab_strip_height">48dp</dimen>

View File

@ -13,7 +13,7 @@
<org.mozilla.gecko.preferences.SyncPreference android:key="android.not_a_preference.sync"
android:title="@string/pref_sync"
android:summary="@string/pref_category_customize_summary"
android:summary="@string/pref_sync_summary"
android:persistent="false" />
<PreferenceScreen android:title="@string/pref_category_customize"

View File

@ -1,11 +1,13 @@
package org.mozilla.gecko.tests;
import java.io.File;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.home.HomePager;
import android.widget.ImageView;
@ -101,6 +103,11 @@ public class testAddSearchEngine extends AboutHomeTest {
// Verify that the number of displayed searchengines is the same as the one received through the SearchEngines:Data event.
verifyDisplayedSearchEnginesCount(initialNumSearchEngines + 1);
searchEngineDataEventExpector.unregisterListener();
// Verify that the search plugin XML file for the new engine ended up where we expected it to.
// This file name is created in nsSearchService.js based on the name of the new engine.
final File f = GeckoProfile.get(getActivity()).getFile("searchplugins/robocop-search-engine.xml");
mAsserter.ok(f.exists(), "Checking that new search plugin file exists", "");
}
/**
@ -149,5 +156,5 @@ public class testAddSearchEngine extends AboutHomeTest {
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
mAsserter.ok(correctNumSearchEnginesDisplayed, expectedCount + " Search Engines should be displayed" , "The correct number of Search Engines has been displayed");
}
}
}

View File

@ -107,6 +107,11 @@ public class UpdateService extends IntentService {
} else if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) {
mCancelDownload = true;
} else {
if (!UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
// Delete the update package used to install the current version.
deleteUpdatePackage(getLastFileName());
}
super.onStartCommand(intent, flags, startId);
}
@ -412,8 +417,26 @@ public class UpdateService extends IntentService {
mNotificationManager.notify(NOTIFICATION_ID, notification);
}
private boolean deleteUpdatePackage(String path) {
if (path == null) {
return false;
}
File pkg = new File(path);
if (!pkg.exists()) {
return false;
}
pkg.delete();
Log.i(LOGTAG, "deleted update package: " + path);
return true;
}
private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
File downloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), new File(info.url.getFile()).getName());
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String fileName = new File(info.url.getFile()).getName();
File downloadFile = new File(path, fileName);
if (!overwriteExisting && info.buildID.equals(getLastBuildID()) && downloadFile.exists()) {
// The last saved buildID is the same as the one for the current update. We also have a file
@ -533,7 +556,7 @@ public class UpdateService extends IntentService {
private void applyUpdate(String updatePath) {
if (updatePath == null) {
updatePath = mPrefs.getString(KEY_LAST_FILE_NAME, null);
updatePath = getLastFileName();
}
applyUpdate(new File(updatePath));
}
@ -569,6 +592,10 @@ public class UpdateService extends IntentService {
return mPrefs.getString(KEY_LAST_HASH_VALUE, null);
}
private String getLastFileName() {
return mPrefs.getString(KEY_LAST_FILE_NAME, null);
}
private Calendar getLastAttemptDate() {
long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1);
if (lastAttempt < 0)

View File

@ -98,6 +98,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetErrorHelper",
XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
"resource://gre/modules/PermissionsUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences",
"resource://gre/modules/SharedPreferences.jsm");
// Lazily-loaded browser scripts:
[
["SelectHelper", "chrome://browser/content/SelectHelper.js"],
@ -820,12 +823,32 @@ var BrowserApp = {
Services.obs.notifyObservers(null, "FormHistory:Init", "");
Services.obs.notifyObservers(null, "Passwords:Init", "");
// Migrate user-set "plugins.click_to_play" pref. See bug 884694.
// Because the default value is true, a user-set pref means that the pref was set to false.
if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
Services.prefs.clearUserPref("plugins.click_to_play");
// Migrate the UI if necessary.
const UI_VERSION = 1;
let currentUIVersion = 0;
try {
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
} catch(ex) {}
if (currentUIVersion >= UI_VERSION) {
return;
}
if (currentUIVersion < 1) {
// Migrate user-set "plugins.click_to_play" pref. See bug 884694.
// Because the default value is true, a user-set pref means that the pref was set to false.
if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED);
Services.prefs.clearUserPref("plugins.click_to_play");
}
// Set the default search engine for the search activity.
SearchEngines.migrateSearchActivityDefaultPref();
}
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},
shutdown: function shutdown() {
@ -6764,12 +6787,16 @@ var SearchEngines = {
PREF_SUGGEST_ENABLED: "browser.search.suggest.enabled",
PREF_SUGGEST_PROMPTED: "browser.search.suggest.prompted",
// Shared preference key used for search activity default engine.
PREF_SEARCH_ACTIVITY_ENGINE_KEY: "search.engines.default",
init: function init() {
Services.obs.addObserver(this, "SearchEngines:Add", false);
Services.obs.addObserver(this, "SearchEngines:GetVisible", false);
Services.obs.addObserver(this, "SearchEngines:Remove", false);
Services.obs.addObserver(this, "SearchEngines:RestoreDefaults", false);
Services.obs.addObserver(this, "SearchEngines:SetDefault", false);
Services.obs.addObserver(this, "browser-search-engine-modified", false);
let filter = {
matches: function (aElement) {
@ -6814,6 +6841,7 @@ var SearchEngines = {
Services.obs.removeObserver(this, "SearchEngines:Remove");
Services.obs.removeObserver(this, "SearchEngines:RestoreDefaults");
Services.obs.removeObserver(this, "SearchEngines:SetDefault");
Services.obs.removeObserver(this, "browser-search-engine-modified");
if (this._contextMenuId != null)
NativeWindow.contextmenus.remove(this._contextMenuId);
},
@ -6894,13 +6922,53 @@ var SearchEngines = {
Services.search.moveEngine(engine, 0);
Services.search.defaultEngine = engine;
break;
case "browser-search-engine-modified":
if (aData == "engine-default") {
this._setSearchActivityDefaultPref(aSubject.QueryInterface(Ci.nsISearchEngine));
}
break;
default:
dump("Unexpected message type observed: " + aTopic);
break;
}
},
migrateSearchActivityDefaultPref: function migrateSearchActivityDefaultPref() {
Services.search.init(() => this._setSearchActivityDefaultPref(Services.search.defaultEngine));
},
// Updates the search activity pref when the default engine changes.
_setSearchActivityDefaultPref: function _setSearchActivityDefaultPref(engine) {
// Helper function copied from nsSearchService.js. This is the logic that is used
// to create file names for search plugin XML serialized to disk.
function sanitizeName(aName) {
const maxLength = 60;
const minLength = 1;
let name = aName.toLowerCase();
name = name.replace(/\s+/g, "-");
name = name.replace(/[^-a-z0-9]/g, "");
if (name.length < minLength) {
// Well, in this case, we're kinda screwed. In this case, the search service
// generates a random file name, so to do this the right way, we'd need
// to open up search.json and see what file name is stored.
Cu.reportError("Couldn't create search plugin file name from engine name: " + aName);
return null;
}
// Force max length.
return name.substring(0, maxLength);
}
let identifier = engine.identifier;
if (identifier === null) {
// The identifier will be null for non-built-in engines. In this case, we need to
// figure out an identifier to store from the engine name.
identifier = sanitizeName(engine.name);
}
SharedPreferences.forApp().setCharPref(this.PREF_SEARCH_ACTIVITY_ENGINE_KEY, identifier);
},
// Display context menu listing names of the search engines available to be added.
displaySearchEnginesList: function displaySearchEnginesList(aData) {
let data = JSON.parse(aData);

View File

@ -20,4 +20,6 @@ public class Constants {
// TODO: Localize this with region.properties (or a similar solution). See bug 1065306.
public static final String DEFAULT_ENGINE_IDENTIFIER = "yahoo";
public static final String PREF_SEARCH_ENGINE_KEY = "search.engines.default";
}

View File

@ -6,14 +6,11 @@ package org.mozilla.search;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
@ -22,10 +19,6 @@ import org.mozilla.gecko.LocaleAware;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.search.providers.SearchEngine;
import org.mozilla.search.providers.SearchEngineManager;
import java.util.List;
/**
* This activity allows users to modify the settings for the search activity.
@ -37,15 +30,11 @@ import java.util.List;
*
* TODO: Change this to PreferenceFragment when we stop supporting devices older than SDK 11.
*/
public class SearchPreferenceActivity extends PreferenceActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
public class SearchPreferenceActivity extends PreferenceActivity {
private static final String LOG_TAG = "SearchPreferenceActivity";
public static final String PREF_CLEAR_HISTORY_KEY = "search.not_a_preference.clear_history";
public static final String PREF_SEARCH_ENGINE_KEY = "search.engines.default";
private SearchEngineManager searchEngineManager;
@Override
@SuppressWarnings("deprecation")
@ -55,8 +44,6 @@ public class SearchPreferenceActivity extends PreferenceActivity
getPreferenceManager().setSharedPreferencesName(GeckoSharedPrefs.APP_PREFS_NAME);
searchEngineManager = new SearchEngineManager(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
@ -64,24 +51,6 @@ public class SearchPreferenceActivity extends PreferenceActivity
}
}
@Override
protected void onDestroy() {
super.onDestroy();
searchEngineManager.destroy();
}
@Override
protected void onResume() {
super.onResume();
GeckoSharedPrefs.forApp(this).registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPause() {
super.onPause();
GeckoSharedPrefs.forApp(this).unregisterOnSharedPreferenceChangeListener(this);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
@ -111,40 +80,6 @@ public class SearchPreferenceActivity extends PreferenceActivity
return false;
}
});
setUpSearchEnginePref();
}
@SuppressWarnings("deprecation")
private void setUpSearchEnginePref() {
final AsyncTask<Void, Void, List<SearchEngine>> task = new AsyncTask<Void, Void, List<SearchEngine>>() {
@Override
protected List<SearchEngine> doInBackground(Void... params) {
return searchEngineManager.getAllEngines();
}
@Override
protected void onPostExecute(List<SearchEngine> engines) {
final CharSequence[] entries = new CharSequence[engines.size()];
final CharSequence[] entryValues = new CharSequence[engines.size()];
for (int i = 0; i < engines.size(); i++) {
final SearchEngine engine = engines.get(i);
entries[i] = engine.getName();
entryValues[i] = engine.getIdentifier();
}
final ListPreference searchEnginePref = (ListPreference) findPreference(PREF_SEARCH_ENGINE_KEY);
searchEnginePref.setEntries(entries);
searchEnginePref.setEntryValues(entryValues);
if (searchEnginePref.getValue() == null) {
searchEnginePref.setValue(Constants.DEFAULT_ENGINE_IDENTIFIER);
}
searchEnginePref.setSummary(searchEnginePref.getEntry());
}
};
task.execute();
}
private void clearHistory() {
@ -169,19 +104,4 @@ public class SearchPreferenceActivity extends PreferenceActivity
};
clearHistoryTask.execute();
}
/**
* Update summaries when the value of a shared preference changes.
*/
@Override
@SuppressWarnings("deprecation")
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (TextUtils.equals(PREF_SEARCH_ENGINE_KEY, key)) {
final ListPreference searchEnginePref = (ListPreference) findPreference(PREF_SEARCH_ENGINE_KEY);
searchEnginePref.setSummary(searchEnginePref.getEntry());
Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH_SET_DEFAULT,
TelemetryContract.Method.DIALOG,
searchEnginePref.getValue().toLowerCase());
}
}
}

View File

@ -12,14 +12,16 @@ import android.util.Log;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.BrowserLocaleManager;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.search.Constants;
import org.mozilla.search.R;
import org.mozilla.search.SearchPreferenceActivity;
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -71,7 +73,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
if (!TextUtils.equals(SearchPreferenceActivity.PREF_SEARCH_ENGINE_KEY, key)) {
if (!TextUtils.equals(Constants.PREF_SEARCH_ENGINE_KEY, key)) {
return;
}
getEngineFromPrefs(changeCallback);
@ -88,7 +90,7 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
final AsyncTask<Void, Void, SearchEngine> task = new AsyncTask<Void, Void, SearchEngine>() {
@Override
protected SearchEngine doInBackground(Void... params) {
String identifier = GeckoSharedPrefs.forApp(context).getString(SearchPreferenceActivity.PREF_SEARCH_ENGINE_KEY, null);
String identifier = GeckoSharedPrefs.forApp(context).getString(Constants.PREF_SEARCH_ENGINE_KEY, null);
if (!TextUtils.isEmpty(identifier)) {
try {
return createEngine(identifier);
@ -179,6 +181,10 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
private SearchEngine createEngine(String identifier) {
InputStream in = getInputStreamFromJar(identifier + ".xml");
if (in == null) {
in = getEngineFromProfile(identifier);
}
// Fallback for standalone search activity.
if (in == null) {
in = getEngineFromAssets(identifier);
@ -264,4 +270,22 @@ public class SearchEngineManager implements SharedPreferences.OnSharedPreference
final String path = "!/chrome/" + locale + "/locale/" + locale + "/browser/searchplugins/" + fileName;
return "jar:jar:file://" + context.getPackageResourcePath() + "!/" + AppConstants.OMNIJAR_NAME + path;
}
/**
* Opens the search plugin XML file from the searchplugins directory in the Gecko profile.
*
* @param identifier
* @return InputStream for search plugin file
*/
private InputStream getEngineFromProfile(String identifier) {
final File f = GeckoProfile.get(context).getFile("searchplugins/" + identifier + ".xml");
if (f.exists()) {
try {
return new FileInputStream(f);
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, "Exception getting search engine from profile", e);
}
}
return null;
}
}

View File

@ -6,9 +6,9 @@
<!-- Make sure the border only appears at the bottom of the background -->
<item
android:top="-2dp"
android:right="-2dp"
android:left="-2dp">
android:top="-3dp"
android:right="-3dp"
android:left="-3dp">
<shape>
<!-- Padding creates vertical space between the text and the underline -->
<padding

View File

@ -6,9 +6,4 @@
<Preference
android:key="search.not_a_preference.clear_history"
android:title="@string/pref_clearHistory_title"/>
<ListPreference
android:key="search.engines.default"
android:title="@string/pref_searchProvider_title"
android:dialogTitle="@string/pref_searchProvider_title" />
</PreferenceScreen>

View File

@ -10,7 +10,7 @@
<Param name="action" value="opensearch"/>
<Param name="search" value="{searchTerms}"/>
</Url>
<Url type="text/html" method="GET" template="https://en.wikipedia.org/wiki/Special:Search">
<Url type="text/html" method="GET" template="https://en.m.wikipedia.org/wiki/Special:Search">
<Param name="search" value="{searchTerms}"/>
<Param name="sourceid" value="Mozilla-search"/>
</Url>

View File

@ -30,16 +30,10 @@ function getAvailableBytes(input)
function runScriptSubprocess(script, args)
{
// logic copied from ted's crashreporter unit test
var ds = new DirectoryService();
var bin = ds.get("CurProcD", Ci.nsILocalFile);
bin.append("xpcshell");
var bin = ds.get("XREExeF", Ci.nsILocalFile);
if (!bin.exists()) {
bin.leafName = "xpcshell.exe";
do_check_true(bin.exists());
if (!bin.exists())
do_throw("Can't find xpcshell binary");
do_throw("Can't find xpcshell binary");
}
var script = do_get_file(script);

View File

@ -274,6 +274,9 @@ class MozbuildObject(ProcessExecutionMixin):
@property
def bindir(self):
import mozinfo
if mozinfo.os == "mac":
return os.path.join(self.topobjdir, 'dist', self.substs['MOZ_MACBUNDLE_NAME'], 'Contents', 'Resources')
return os.path.join(self.topobjdir, 'dist', 'bin')
@property

View File

@ -775,7 +775,7 @@ class Install(MachCommandBase):
return self._run_make(directory=".", target='install', ensure_exit_code=False)
def get_run_args(mach_command, params, remote, background):
def get_run_args(mach_command, params, remote, background, noprofile):
"""
Parses the given options to create an args array for running firefox.
Creates a scratch profile and uses that if one is not specified.
@ -794,7 +794,7 @@ def get_run_args(mach_command, params, remote, background):
if not background and sys.platform == 'darwin':
args.append('-foreground')
if '-profile' not in params and '-P' not in params:
if '-profile' not in params and '-P' not in params and not noprofile:
path = os.path.join(mach_command.topobjdir, 'tmp', 'scratch_user')
if not os.path.isdir(path):
os.makedirs(path)
@ -821,8 +821,10 @@ class RunProgram(MachCommandBase):
help='Do not pass the -no-remote argument by default.')
@CommandArgument('+background', '+b', action='store_true',
help='Do not pass the -foreground argument by default on Mac')
def run(self, params, remote, background):
args = get_run_args(self, params, remote, background)
@CommandArgument('+noprofile', '+n', action='store_true',
help='Do not pass the -profile argument by default.')
def run(self, params, remote, background, noprofile):
args = get_run_args(self, params, remote, background, noprofile)
if not args:
return 1
@ -852,7 +854,9 @@ class DebugProgram(MachCommandBase):
# automatic resuming; see the bug.
@CommandArgument('+slowscript', action='store_true',
help='Do not set the JS_DISABLE_SLOW_SCRIPT_SIGNALS env variable; when not set, recoverable but misleading SIGSEGV instances may occur in Ion/Odin JIT code')
def debug(self, params, remote, background, debugger, debugparams, slowscript):
@CommandArgument('+noprofile', '+n', action='store_true',
help='Do not pass the -profile argument by default.')
def debug(self, params, remote, background, debugger, debugparams, slowscript, noprofile):
# Parameters come from the CLI. We need to convert them before their use.
if debugparams:
import pymake.process
@ -898,7 +902,7 @@ class DebugProgram(MachCommandBase):
args.append('-foreground')
if params:
args.extend(params)
if '-profile' not in params and '-P' not in params:
if '-profile' not in params and '-P' not in params and not noprofile:
path = os.path.join(self.topobjdir, 'tmp', 'scratch_user')
if not os.path.isdir(path):
os.makedirs(path)

Some files were not shown because too many files have changed in this diff Show More