merge fx-team to mozilla-central a=merge
@ -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)
|
||||
|
@ -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
|
||||
|
@ -42,7 +42,9 @@
|
||||
#endif
|
||||
|
||||
[xpcom]
|
||||
#ifndef XP_MACOSX
|
||||
@BINPATH@/dependentlibs.list
|
||||
#endif
|
||||
#ifdef GKMEDIAS_SHARED_LIBRARY
|
||||
@BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@
|
||||
#endif
|
||||
|
@ -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@
|
||||
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
_CodeSignature/CodeResources
|
@ -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>
|
||||
|
9
browser/app/macbuild/Contents/MacOS-files.in
Normal file
@ -0,0 +1,9 @@
|
||||
/*.app/***
|
||||
/*.dylib
|
||||
/certutil
|
||||
/firefox-bin
|
||||
/pk12util
|
||||
/ssltunnel
|
||||
/webapprt-stub
|
||||
/xpcshell
|
||||
/XUL
|
@ -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>
|
@ -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);
|
||||
|
@ -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/. */
|
||||
|
@ -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/");
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
},
|
||||
|
@ -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 = "";
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@
|
||||
transition: background .5s;
|
||||
}
|
||||
|
||||
.tag-line .open, .tag-line .close, .comment {
|
||||
.tag-line {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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"><<!--
|
||||
--><span save="${tag}" class="tag theme-fg-color3" tabindex="0"></span><!--
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
});
|
@ -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);
|
||||
}
|
||||
});
|
@ -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);
|
||||
});
|
31
browser/devtools/markupview/test/doc_markup_anonymous.html
Normal 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>
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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@
|
||||
|
@ -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/
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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"];
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
@ -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);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -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
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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")) {
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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,
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|