Merge m-c to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2014-09-30 15:35:59 +02:00
commit b9a248bfae
272 changed files with 5977 additions and 3610 deletions

View File

@ -64,6 +64,7 @@ tools repackage:: $(libs-preqs)
sed -e 's/%MOZ_APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MOZ_APP_NAME%/$(MOZ_APP_NAME)/' -e 's/%APP_VERSION%/$(APP_VERSION)/' -e 's/%APP_NAME%/$(APP_NAME)/' -e 's/%APP_BINARY%/$(APP_BINARY)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist sed -e 's/%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 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) 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 $(RM) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/mangle $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/shlibsign
ifdef LIBXUL_SDK ifdef LIBXUL_SDK
cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(APP_BINARY) cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(APP_BINARY)

View File

@ -42,6 +42,15 @@ endif
MOZ_PACKAGER_MINIFY=1 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 include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
# Note that JS_BINARY can be defined in packager.mk, so this test must come after # Note that JS_BINARY can be defined in packager.mk, so this test must come after

View File

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

View File

@ -1,24 +1,45 @@
README.txt # Due to Apple Mac OS X packaging requirements files that are in the same
@DLL_PREFIX@mozutils@DLL_SUFFIX@ # directory on other platforms must be located in different directories on
jssubloader/ # 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 #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 #endif
#ifdef XP_WIN #ifdef XP_WIN
mozcrt19.dll mozcrt19.dll
mozcpp19.dll mozcpp19.dll
#endif #endif
defaults/preferences/services-sync.js @DIR_MACOS@defaults/preferences/services-sync.js
defaults/preferences/healthreport-prefs.js @DIR_MACOS@defaults/preferences/healthreport-prefs.js
components/dom_sms.xpt @DIR_MACOS@components/dom_sms.xpt
components/dom_webspeech.xpt @DIR_MACOS@components/dom_webspeech.xpt
#ifdef MOZ_FOLD_LIBS #ifdef MOZ_FOLD_LIBS
@DLL_PREFIX@nspr4@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@nspr4@DLL_SUFFIX@
@DLL_PREFIX@plds4@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@plds4@DLL_SUFFIX@
@DLL_PREFIX@plc4@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@plc4@DLL_SUFFIX@
@DLL_PREFIX@ssl3@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@ssl3@DLL_SUFFIX@
@DLL_PREFIX@smime3@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@smime3@DLL_SUFFIX@
@DLL_PREFIX@nssutil3@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@nssutil3@DLL_SUFFIX@
@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
#endif #endif
@DLL_PREFIX@xpcom@DLL_SUFFIX@ @DIR_MACOS@@DLL_PREFIX@xpcom@DLL_SUFFIX@

View File

@ -76,7 +76,7 @@ ifndef LIBXUL_SDK
# channel-prefs.js is handled separate from other prefs due to bug 756325 # channel-prefs.js is handled separate from other prefs due to bug 756325
libs:: $(srcdir)/profile/channel-prefs.js libs:: $(srcdir)/profile/channel-prefs.js
$(NSINSTALL) -D $(DIST)/bin/defaults/pref $(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 endif
libs:: $(srcdir)/blocklist.xml libs:: $(srcdir)/blocklist.xml
@ -97,12 +97,6 @@ AB := $(firstword $(subst -, ,$(AB_CD)))
clean clobber repackage:: clean clobber repackage::
$(RM) -r $(dist_dest) $(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) MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/config/buildid)
.PHONY: repackage .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 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/%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 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) $(RM) $(dist_dest)/Contents/MacOS/$(PROGRAM)
rsync -aL $(PROGRAM) $(dist_dest)/Contents/MacOS rsync -aL $(PROGRAM) $(dist_dest)/Contents/MacOS
cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
#filter substitution
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

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

View File

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

View File

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

View File

@ -201,6 +201,11 @@ function test_multiple_windows() {
ok(true, "Advanced preferences opened on info bar button press."); ok(true, "Advanced preferences opened on info bar button press.");
executeSoon(function soon() { executeSoon(function soon() {
prefWindowOpened = true; 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(); maybeFinish();
}); });
}, "advanced-pane-loaded", false); }, "advanced-pane-loaded", false);

View File

@ -150,7 +150,8 @@ function clearSubview(aSubview) {
parent.appendChild(aSubview); parent.appendChild(aSubview);
} }
const CustomizableWidgets = [{ const CustomizableWidgets = [
{
id: "history-panelmenu", id: "history-panelmenu",
type: "view", type: "view",
viewId: "PanelUI-history", viewId: "PanelUI-history",
@ -923,6 +924,18 @@ const CustomizableWidgets = [{
}); });
return node; 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 #ifdef XP_WIN

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ const {HTMLEditor} = require("devtools/markupview/html-editor");
const promise = require("devtools/toolkit/deprecated-sync-thenables"); const promise = require("devtools/toolkit/deprecated-sync-thenables");
const {Tooltip} = require("devtools/shared/widgets/Tooltip"); const {Tooltip} = require("devtools/shared/widgets/Tooltip");
const EventEmitter = require("devtools/toolkit/event-emitter"); 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/LayoutHelpers.jsm");
Cu.import("resource://gre/modules/devtools/Templater.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 * MarkupContainer - the structure that holds an editor and its
* immediate children in the markup panel. * 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. * Node - A content node.
* object.elt - A UI element in the markup panel. * object.elt - A UI element in the markup panel.
*/ */
@ -186,7 +190,7 @@ MarkupView.prototype = {
parentNode = parentNode.parentNode; parentNode = parentNode.parentNode;
} }
if (container) { if (container instanceof MarkupElementContainer) {
// With the newly found container, delegate the tooltip content creation // With the newly found container, delegate the tooltip content creation
// and decision to show or not the tooltip // and decision to show or not the tooltip
container._buildEventTooltipContent(event.target, this.tooltip); container._buildEventTooltipContent(event.target, this.tooltip);
@ -301,7 +305,7 @@ MarkupView.prototype = {
* tooltip. * tooltip.
* Delegates the actual decision to the corresponding MarkupContainer instance * Delegates the actual decision to the corresponding MarkupContainer instance
* if one is found. * if one is found.
* @return the promise returned by MarkupContainer._isImagePreviewTarget * @return the promise returned by MarkupElementContainer._isImagePreviewTarget
*/ */
_isImagePreviewTarget: function(target) { _isImagePreviewTarget: function(target) {
// From the target passed here, let's find the parent MarkupContainer // From the target passed here, let's find the parent MarkupContainer
@ -315,10 +319,10 @@ MarkupView.prototype = {
parent = parent.parentNode; parent = parent.parentNode;
} }
if (container) { if (container instanceof MarkupElementContainer) {
// With the newly found container, delegate the tooltip content creation // With the newly found container, delegate the tooltip content creation
// and decision to show or not the tooltip // 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) { deleteNode: function(aNode) {
if (aNode.isDocumentElement || if (aNode.isDocumentElement ||
aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) { aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE ||
aNode.isAnonymous) {
return; return;
} }
@ -568,7 +573,7 @@ MarkupView.prototype = {
/** /**
* Make sure a node is included in the markup tool. * Make sure a node is included in the markup tool.
* *
* @param DOMNode aNode * @param NodeFront aNode
* The node in the content document. * The node in the content document.
* @param boolean aFlashNode * @param boolean aFlashNode
* Whether the newly imported node should be flashed * Whether the newly imported node should be flashed
@ -583,15 +588,23 @@ MarkupView.prototype = {
return this.getContainer(aNode); return this.getContainer(aNode);
} }
let container;
let {nodeType, isPseudoElement} = aNode;
if (aNode === this.walker.rootNode) { if (aNode === this.walker.rootNode) {
var container = new RootContainer(this, aNode); container = new RootContainer(this, aNode);
this._elt.appendChild(container.elt); this._elt.appendChild(container.elt);
this._rootNode = aNode; 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 { } else {
var container = new MarkupContainer(this, aNode, this._inspector); container = new MarkupReadOnlyContainer(this, aNode, this._inspector);
if (aFlashNode) { }
container.flashMutation();
} if (aFlashNode) {
container.flashMutation();
} }
this._containers.set(aNode, container); this._containers.set(aNode, container);
@ -961,7 +974,7 @@ MarkupView.prototype = {
let parentContainer = this.getContainer(parent); let parentContainer = this.getContainer(parent);
if (parentContainer) { if (parentContainer) {
parentContainer.childrenDirty = true; 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 * The main structure for storing a document node in the markup
* tree. Manages creation of the editor for the node and * tree. Manages creation of the editor for the node and
* a <ul> for placing child elements, and expansion/collapsing * a <ul> for placing child elements, and expansion/collapsing
* of the element. * of the element.
* *
* @param MarkupView aMarkupView * This should not be instantiated directly, instead use one of:
* The markup view that owns this container. * MarkupReadOnlyContainer
* @param DOMNode aNode * MarkupTextContainer
* The node to display. * MarkupElementContainer
* @param Inspector aInspector
* The inspector tool container the markup-view
*/ */
function MarkupContainer(aMarkupView, aNode, aInspector) { function MarkupContainer() { }
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;
}
MarkupContainer.prototype = { 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() { toString: function() {
return "[MarkupContainer for " + this.node + "]"; return "[MarkupContainer for " + this.node + "]";
}, },
isPreviewable: function() { isPreviewable: function() {
if (this.node.tagName) { if (this.node.tagName && !this.node.isPseudoElement) {
let tagName = this.node.tagName.toLowerCase(); let tagName = this.node.tagName.toLowerCase();
let srcAttr = this.editor.getAttributeElement("src"); let srcAttr = this.editor.getAttributeElement("src");
let isImage = tagName === "img" && srcAttr; 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 * 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 * True if the current node has children. The MarkupView
* will set this attribute for the MarkupContainer. * will set this attribute for the MarkupContainer.
@ -1492,6 +1415,10 @@ MarkupContainer.prototype = {
set hasChildren(aValue) { set hasChildren(aValue) {
this._hasChildren = aValue; this._hasChildren = aValue;
if (!this.expander) {
return;
}
if (aValue) { if (aValue) {
this.expander.style.visibility = "visible"; this.expander.style.visibility = "visible";
} else { } 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. * True if the node has been visually expanded in the tree.
*/ */
@ -1511,32 +1434,35 @@ MarkupContainer.prototype = {
}, },
set expanded(aValue) { set expanded(aValue) {
if (!this.expander) {
return;
}
if (aValue && this.elt.classList.contains("collapsed")) { if (aValue && this.elt.classList.contains("collapsed")) {
// Expanding a node means cloning its "inline" closing tag into a new // Expanding a node means cloning its "inline" closing tag into a new
// tag-line that the user can interact with and showing the children. // tag-line that the user can interact with and showing the children.
if (this.editor instanceof ElementEditor) { let closingTag = this.elt.querySelector(".close");
let closingTag = this.elt.querySelector(".close"); if (closingTag) {
if (closingTag) { if (!this.closeTagLine) {
if (!this.closeTagLine) { let line = this.markup.doc.createElement("div");
let line = this.markup.doc.createElement("div"); line.classList.add("tag-line");
line.classList.add("tag-line");
let tagState = this.markup.doc.createElement("div"); let tagState = this.markup.doc.createElement("div");
tagState.classList.add("tag-state"); tagState.classList.add("tag-state");
line.appendChild(tagState); line.appendChild(tagState);
line.appendChild(closingTag.cloneNode(true)); line.appendChild(closingTag.cloneNode(true));
this.closeTagLine = line; this.closeTagLine = line;
}
this.elt.appendChild(this.closeTagLine);
} }
this.elt.appendChild(this.closeTagLine);
} }
this.elt.classList.remove("collapsed"); this.elt.classList.remove("collapsed");
this.expander.setAttribute("open", ""); this.expander.setAttribute("open", "");
this.hovered = false; this.hovered = false;
} else if (!aValue) { } else if (!aValue) {
if (this.editor instanceof ElementEditor && this.closeTagLine) { if (this.closeTagLine) {
this.elt.removeChild(this.closeTagLine); this.elt.removeChild(this.closeTagLine);
} }
this.elt.classList.add("collapsed"); this.elt.classList.add("collapsed");
@ -1544,12 +1470,8 @@ MarkupContainer.prototype = {
} }
}, },
_onToggle: function(event) { parentContainer: function() {
this.markup.navigate(this); return this.elt.parentNode ? this.elt.parentNode.container : null;
if(this.hasChildren) {
this.markup.setNodeExpanded(this.node, !this.expanded, event.altKey);
}
event.stopPropagation();
}, },
_onMouseDown: function(event) { _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 * Get rid of event listeners and references, when the container is no longer
* needed * needed
*/ */
destroy: function() { 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 // Recursively destroy children containers
let firstChild; let firstChild;
while (firstChild = this.children.firstChild) { while (firstChild = this.children.firstChild) {
@ -1711,16 +1649,168 @@ MarkupContainer.prototype = {
this.children.removeChild(firstChild); 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(); 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. * 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) { function GenericEditor(aContainer, aNode) {
this.elt = aContainer.doc.createElement("span"); this.container = aContainer;
this.elt.className = "editor"; this.markup = this.container.markup;
this.elt.textContent = aNode.nodeName; 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 = { GenericEditor.prototype = {
destroy: function() {} destroy: function() {
}; this.elt.remove();
}
/**
* 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() {}
}; };
/** /**
@ -1782,10 +1870,13 @@ DoctypeEditor.prototype = {
* @param string aTemplate The template id to use to build the editor. * @param string aTemplate The template id to use to build the editor.
*/ */
function TextEditor(aContainer, aNode, aTemplate) { function TextEditor(aContainer, aNode, aTemplate) {
this.container = aContainer;
this.markup = this.container.markup;
this.node = aNode; this.node = aNode;
this.template = this.markup.template.bind(aTemplate);
this._selected = false; this._selected = false;
aContainer.markup.template(aTemplate, this); this.markup.template(aTemplate, this);
editableField({ editableField({
element: this.value, element: this.value,
@ -1800,13 +1891,13 @@ function TextEditor(aContainer, aNode, aTemplate) {
longstr.string().then(oldValue => { longstr.string().then(oldValue => {
longstr.release().then(null, console.error); longstr.release().then(null, console.error);
aContainer.undo.do(() => { this.container.undo.do(() => {
this.node.setNodeValue(aVal).then(() => { this.node.setNodeValue(aVal).then(() => {
aContainer.markup.nodeChanged(this.node); this.markup.nodeChanged(this.node);
}); });
}, () => { }, () => {
this.node.setNodeValue(oldValue).then(() => { 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. * @param Element aNode The node being edited.
*/ */
function ElementEditor(aContainer, aNode) { function ElementEditor(aContainer, aNode) {
this.doc = aContainer.doc;
this.undo = aContainer.undo;
this.template = aContainer.markup.template.bind(aContainer.markup);
this.container = aContainer; this.container = aContainer;
this.markup = this.container.markup;
this.node = aNode; this.node = aNode;
this.markup = this.container.markup;
this.template = this.markup.template.bind(this.markup);
this.doc = this.markup.doc;
this.attrs = {}; this.attrs = {};
@ -1911,7 +2001,7 @@ function ElementEditor(aContainer, aNode) {
let doMods = this._startModifyingAttributes(); let doMods = this._startModifyingAttributes();
let undoMods = this._startModifyingAttributes(); let undoMods = this._startModifyingAttributes();
this._applyAttributes(aVal, null, doMods, undoMods); this._applyAttributes(aVal, null, doMods, undoMods);
this.undo.do(() => { this.container.undo.do(() => {
doMods.apply(); doMods.apply();
}, function() { }, function() {
undoMods.apply(); undoMods.apply();
@ -2039,7 +2129,7 @@ ElementEditor.prototype = {
this._saveAttribute(aAttr.name, undoMods); this._saveAttribute(aAttr.name, undoMods);
doMods.removeAttribute(aAttr.name); doMods.removeAttribute(aAttr.name);
this._applyAttributes(aVal, attr, doMods, undoMods); this._applyAttributes(aVal, attr, doMods, undoMods);
this.undo.do(() => { this.container.undo.do(() => {
doMods.apply(); doMods.apply();
}, () => { }, () => {
undoMods.apply(); undoMods.apply();
@ -2154,7 +2244,7 @@ ElementEditor.prototype = {
aOld.parentNode.removeChild(aOld); aOld.parentNode.removeChild(aOld);
} }
this.undo.do(() => { this.container.undo.do(() => {
swapNodes(this.rawNode, newElt); swapNodes(this.rawNode, newElt);
this.markup.setNodeExpanded(newFront, this.container.expanded); this.markup.setNodeExpanded(newFront, this.container.expanded);
if (this.container.selected) { if (this.container.selected) {

View File

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

View File

@ -1,6 +1,7 @@
[DEFAULT] [DEFAULT]
subsuite = devtools subsuite = devtools
support-files = support-files =
doc_markup_anonymous.html
doc_markup_edit.html doc_markup_edit.html
doc_markup_events.html doc_markup_events.html
doc_markup_events_jquery.html doc_markup_events_jquery.html
@ -29,6 +30,10 @@ support-files =
lib_jquery_1.11.1_min.js lib_jquery_1.11.1_min.js
lib_jquery_2.1.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_copy_image_data.js]
[browser_markupview_css_completion_style_attribute.js] [browser_markupview_css_completion_style_attribute.js]
[browser_markupview_events.js] [browser_markupview_events.js]

View File

@ -0,0 +1,30 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test native anonymous content in the markupview.
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let pseudo = yield getNodeFront("#pseudo", inspector);
// Markup looks like: <div><::before /><span /><::after /></div>
let children = yield inspector.walker.children(pseudo);
is (children.nodes.length, 3, "Children returned from walker");
info ("Checking the ::before pseudo element");
let before = children.nodes[0];
yield isEditingMenuDisabled(before, inspector);
info ("Checking the normal child element");
let span = children.nodes[1];
yield isEditingMenuEnabled(span, inspector);
info ("Checking the ::after pseudo element");
let after = children.nodes[2];
yield isEditingMenuDisabled(after, inspector);
});

View File

@ -0,0 +1,29 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test XBL anonymous content in the markupview
const TEST_URL = "chrome://browser/content/devtools/scratchpad.xul";
let test = asyncTest(function*() {
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let toolbarbutton = yield getNodeFront("toolbarbutton", inspector);
let children = yield inspector.walker.children(toolbarbutton);
is(toolbarbutton.numChildren, 3, "Correct number of children");
is (children.nodes.length, 3, "Children returned from walker");
is(toolbarbutton.isAnonymous, false, "Toolbarbutton is not anonymous");
yield isEditingMenuEnabled(toolbarbutton, inspector);
for (let node of children.nodes) {
ok (node.isAnonymous, "Child is anonymous");
ok (node._form.isXBLAnonymous, "Child is XBL anonymous");
ok (!node._form.isShadowAnonymous, "Child is not shadow anonymous");
ok (!node._form.isNativeAnonymous, "Child is not native anonymous");
yield isEditingMenuDisabled(node, inspector);
}
});

View File

@ -0,0 +1,34 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test shadow DOM content in the markupview.
// Note that many features are not yet enabled, but basic listing
// of elements should be working.
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
let test = asyncTest(function*() {
Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
let {inspector} = yield addTab(TEST_URL).then(openInspector);
let shadow = yield getNodeFront("#shadow", inspector.markup);
let children = yield inspector.walker.children(shadow);
is (shadow.numChildren, 3, "Children of the shadow root are counted");
is (children.nodes.length, 3, "Children returned from walker");
info ("Checking the ::before pseudo element");
let before = children.nodes[0];
yield isEditingMenuDisabled(before, inspector);
info ("Checking the <h3> shadow element");
let shadowChild1 = children.nodes[1];
yield isEditingMenuDisabled(shadowChild1, inspector);
info ("Checking the <select> shadow element");
let shadowChild2 = children.nodes[2];
yield isEditingMenuDisabled(shadowChild2, inspector);
});

View File

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

View File

@ -8,6 +8,7 @@ let TargetFactory = devtools.TargetFactory;
let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {}); let {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables"); let promise = devtools.require("devtools/toolkit/deprecated-sync-thenables");
let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor"); let {getInplaceEditorForSpan: inplaceEditor} = devtools.require("devtools/shared/inplace-editor");
let clipboard = devtools.require("sdk/clipboard");
// All test are asynchronous // All test are asynchronous
waitForExplicitFinish(); waitForExplicitFinish();
@ -30,6 +31,7 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.inspector.activeSidebar"); Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
Services.prefs.clearUserPref("devtools.dump.emit"); Services.prefs.clearUserPref("devtools.dump.emit");
Services.prefs.clearUserPref("devtools.markup.pagesize"); 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 // 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 * 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 * @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox * loaded in the toolbox
* @return {Promise} Resolves to the NodeFront instance * @return {Promise} Resolves to the NodeFront instance
*/ */
function getNodeFront(selector, {walker}) { function getNodeFront(selector, {walker}) {
if (selector._form) {
return selector;
}
return walker.querySelector(walker.rootNode, 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 * Set the inspector's current selection to the first match of the given css
* selector * selector
* @param {String} selector * @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently * @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox * loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not * @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 * Get the MarkupContainer object instance that corresponds to the given
* selector * selector
* @param {String} selector * @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently * @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox * loaded in the toolbox
* @return {MarkupContainer} * @return {MarkupContainer}
@ -236,7 +241,7 @@ function waitForChildrenUpdated({markup}) {
/** /**
* Simulate a mouse-over on the markup-container (a line in the markup-view) * Simulate a mouse-over on the markup-container (a line in the markup-view)
* that corresponds to the selector passed. * that corresponds to the selector passed.
* @param {String} selector * @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently * @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox * loaded in the toolbox
* @return {Promise} Resolves when the container is hovered and the higlighter * @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) * Simulate a click on the markup-container (a line in the markup-view)
* that corresponds to the selector passed. * that corresponds to the selector passed.
* @param {String} selector * @param {String|NodeFront} selector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently * @param {InspectorPanel} inspector The instance of InspectorPanel currently
* loaded in the toolbox * loaded in the toolbox
* @return {Promise} Resolves when the node has been selected. * @return {Promise} Resolves when the node has been selected.
@ -440,6 +445,124 @@ function wait(ms) {
return def.promise; 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 * Wait for all current promises to be resolved. See this as executeSoon that
* can be used with yield. * can be used with yield.

View File

@ -153,7 +153,9 @@ ElementStyle.prototype = {
// engine, we will set properties on a dummy element and observe // engine, we will set properties on a dummy element and observe
// how their .style attribute reflects them as computed values. // how their .style attribute reflects them as computed values.
return this.dummyElementPromise = createDummyDocument().then(document => { 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); this.element.tagName);
document.documentElement.appendChild(this.dummyElement); document.documentElement.appendChild(this.dummyElement);
return this.dummyElement; return this.dummyElement;
@ -163,9 +165,7 @@ ElementStyle.prototype = {
destroy: function() { destroy: function() {
this.dummyElement = null; this.dummyElement = null;
this.dummyElementPromise.then(dummyElement => { this.dummyElementPromise.then(dummyElement => {
if (dummyElement.parentNode) { dummyElement.remove();
dummyElement.parentNode.removeChild(dummyElement);
}
this.dummyElementPromise = null; this.dummyElementPromise = null;
}, console.error); }, console.error);
}, },
@ -1236,6 +1236,8 @@ CssRuleView.prototype = {
let accessKey = label + ".accessKey"; let accessKey = label + ".accessKey";
this.menuitemSources.setAttribute("accesskey", this.menuitemSources.setAttribute("accesskey",
_strings.GetStringFromName(accessKey)); _strings.GetStringFromName(accessKey));
this.menuitemAddRule.disabled = this.inspector.selection.isAnonymousNode();
}, },
/** /**
@ -1831,10 +1833,14 @@ function RuleEditor(aRuleView, aRule) {
RuleEditor.prototype = { RuleEditor.prototype = {
get isSelectorEditable() { get isSelectorEditable() {
let toolbox = this.ruleView.inspector.toolbox; let toolbox = this.ruleView.inspector.toolbox;
return this.isEditable && let trait = this.isEditable &&
toolbox.target.client.traits.selectorEditable && toolbox.target.client.traits.selectorEditable &&
this.rule.domRule.type !== ELEMENT_STYLE && 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() { _create: function() {

View File

@ -34,6 +34,7 @@ support-files =
[browser_computedview_media-queries.js] [browser_computedview_media-queries.js]
[browser_computedview_no-results-placeholder.js] [browser_computedview_no-results-placeholder.js]
[browser_computedview_original-source-link.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_01.js]
[browser_computedview_refresh-on-style-change_02.js] [browser_computedview_refresh-on-style-change_02.js]
[browser_computedview_search-filter.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_multiple_properties_02.js]
[browser_ruleview_original-source-link.js] [browser_ruleview_original-source-link.js]
[browser_ruleview_override.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_01.js]
[browser_ruleview_refresh-on-attribute-change_02.js] [browser_ruleview_refresh-on-attribute-change_02.js]
[browser_ruleview_refresh-on-style-change.js] [browser_ruleview_refresh-on-style-change.js]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,51 +52,75 @@
# #
# Example: path/to/dir/* # 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 # This is located under the "distribution/" directory and it was added before
# Firefox 27 # 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 # Some users are ending up with unpacked chrome instead of omni.ja. This
# causes updates to break badly, see bug 1063052. Removing the toplevel # causes updates to break badly, see bug 1063052. Removing the toplevel
# chrome.manifest causes us to use the updated omni.ja. # chrome.manifest causes us to use the updated omni.ja.
#ifndef MOZ_GTK #ifndef MOZ_GTK
chrome.manifest @DIR_MACOS@chrome.manifest
#ifdef XP_MACOSX
@DIR_RESOURCES@chrome.manifest
#endif
#endif #endif
# Directory removals # Mac OS X v2 signing removals
chrome/ #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 #ifdef XP_UNIX
#ifndef XP_MACOSX #ifndef XP_MACOSX
chrome/icons/ chrome/icons/
chrome/icons/default/ chrome/icons/default/
#endif #endif
#endif #endif
chrome/overlayinfo/ @DIR_MACOS@chrome/overlayinfo/
components/ @DIR_MACOS@components/
defaults/autoconfig/ @DIR_MACOS@defaults/autoconfig/
defaults/profile/ @DIR_MACOS@defaults/profile/
defaults/profile/chrome/ @DIR_MACOS@defaults/profile/chrome/
defaults/profile/US/* @DIR_MACOS@defaults/profile/US/*
defaults/profile/extensions/ @DIR_MACOS@defaults/profile/extensions/
defaults/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/* @DIR_MACOS@defaults/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
distribution/ @DIR_MACOS@distribution/
distribution/extensions/ @DIR_MACOS@distribution/extensions/
extensions/ @DIR_MACOS@extensions/
extensions/inspector@mozilla.org/* @DIR_MACOS@extensions/inspector@mozilla.org/*
extensions/reporter@mozilla.org/* @DIR_MACOS@extensions/reporter@mozilla.org/*
extensions/talkback@mozilla.org/* @DIR_MACOS@extensions/talkback@mozilla.org/*
extensions/testpilot@labs.mozilla.com/* @DIR_MACOS@extensions/testpilot@labs.mozilla.com/*
extensions/{641d8d09-7dda-4850-8228-ac0ab65e2ac9}/* @DIR_MACOS@extensions/{641d8d09-7dda-4850-8228-ac0ab65e2ac9}/*
extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/* @DIR_MACOS@extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/*
greprefs/ @DIR_MACOS@greprefs/
jssubloader/ @DIR_MACOS@jssubloader/
modules/ @DIR_MACOS@modules/
#ifdef XP_MACOSX #ifdef XP_MACOSX
plugins/Default Plugin.plugin/* @DIR_MACOS@plugins/Default Plugin.plugin/*
plugins/JavaEmbeddingPlugin.bundle/* @DIR_MACOS@plugins/JavaEmbeddingPlugin.bundle/*
plugins/MRJPlugin.plugin/* @DIR_MACOS@plugins/MRJPlugin.plugin/*
../Plug-Ins/PrintPDE.plugin/* Contents/Plug-Ins/PrintPDE.plugin/*
#endif #endif
searchplugins/* @DIR_MACOS@searchplugins/*
webapprt/components/ @DIR_MACOS@webapprt/components/

View File

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

View File

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

View File

@ -23,9 +23,6 @@ const PREF_BRANCH = "browser.newtab.";
// The interval between swapping in a preload docShell and kicking off the // The interval between swapping in a preload docShell and kicking off the
// next preload in the background. // next preload in the background.
const PRELOADER_INTERVAL_MS = 600; 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 // 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 // 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. // kind of a damper when too many events are occuring in quick succession.
@ -52,17 +49,20 @@ function clearTimer(timer) {
this.BrowserNewTabPreloader = { this.BrowserNewTabPreloader = {
init: function Preloader_init() { init: function Preloader_init() {
Initializer.start(); Preferences.init();
}, },
uninit: function Preloader_uninit() { uninit: function Preloader_uninit() {
Initializer.stop();
HostFrame.destroy(); HostFrame.destroy();
Preferences.uninit(); Preferences.uninit();
HiddenBrowsers.uninit(); HiddenBrowsers.uninit();
}, },
newTab: function Preloader_newTab(aTab) { newTab: function Preloader_newTab(aTab) {
if (!Preferences.enabled) {
return false;
}
let win = aTab.ownerDocument.defaultView; let win = aTab.ownerDocument.defaultView;
if (win.gBrowser) { if (win.gBrowser) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor) let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
@ -81,47 +81,6 @@ this.BrowserNewTabPreloader = {
Object.freeze(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 = { let Preferences = {
_enabled: null, _enabled: null,
_branch: null, _branch: null,
@ -153,8 +112,6 @@ let Preferences = {
if (prevEnabled && !this.enabled) { if (prevEnabled && !this.enabled) {
HiddenBrowsers.uninit(); HiddenBrowsers.uninit();
} else if (!prevEnabled && this.enabled) {
HiddenBrowsers.init();
} }
}, },
}; };
@ -168,7 +125,7 @@ let HiddenBrowsers = {
TOPIC_XUL_WINDOW_CLOSED TOPIC_XUL_WINDOW_CLOSED
], ],
init: function () { _init: function () {
this._browsers = new Map(); this._browsers = new Map();
this._updateBrowserSizes(); this._updateBrowserSizes();
this._topics.forEach(t => Services.obs.addObserver(this, t, false)); this._topics.forEach(t => Services.obs.addObserver(this, t, false));
@ -187,9 +144,9 @@ let HiddenBrowsers = {
}, },
get: function (width, height) { get: function (width, height) {
// We haven't been initialized, yet. // Initialize if this is the first call.
if (!this._browsers) { if (!this._browsers) {
return null; this._init();
} }
let key = width + "x" + height; let key = width + "x" + height;

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -1131,6 +1131,13 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
transform: scaleX(-1); 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-icon,
:-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .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); -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 { toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 1664px, 64px, 1600px); -moz-image-region: rect(0, 1664px, 64px, 1600px);
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -2,7 +2,7 @@
% Note that zoom-reset-button is a bit different since it doesn't use an image and thus has the image with display: none. % 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 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 %ifdef XP_MACOSX
% Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen % Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen

View File

@ -156,6 +156,11 @@ toolbarpaletteitem[place="palette"] > #panic-button {
-moz-image-region: rect(32px, 896px, 64px, 864px); -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 { toolbaritem[sdkstylewidget="true"] > toolbarbutton {
-moz-image-region: rect(0, 832px, 32px, 800px); -moz-image-region: rect(0, 832px, 32px, 800px);
} }

View File

@ -190,6 +190,15 @@ toolbar[brighttext] #sync-button[status="active"]:hover:active:not([disabled="tr
transform: scaleX(-1); 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 { #loop-call-button > .toolbarbutton-badge-container {
list-style-image: url(chrome://browser/skin/loop/toolbar.png); list-style-image: url(chrome://browser/skin/loop/toolbar.png);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

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

View File

@ -44,14 +44,17 @@ def generate_precomplete(root_path):
application update instructions. The given directory is used application update instructions. The given directory is used
for the location to enumerate and to create the precomplete file. 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 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, '../../')) 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,rel_path_precomplete)
precomplete_file_path = os.path.join(root_path,"precomplete") # Open the file so it exists before building the list of files and open it
# open in binary mode to prevent OS specific line endings. # in binary mode to prevent OS specific line endings.
precomplete_file = open(precomplete_file_path, "wb") 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: for rel_file_path in rel_file_path_list:
precomplete_file.writelines("remove \""+rel_file_path+"\"\n") precomplete_file.writelines("remove \""+rel_file_path+"\"\n")

View File

@ -135,15 +135,26 @@ GeckoChildProcessHost::GetPathToBinary(FilePath& exePath)
MOZ_ASSERT(gGREPath); MOZ_ASSERT(gGREPath);
#ifdef OS_WIN #ifdef OS_WIN
exePath = FilePath(char16ptr_t(gGREPath)); 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 #else
nsCString path; nsCString path;
NS_CopyUnicodeToNative(nsDependentString(gGREPath), path); NS_CopyUnicodeToNative(nsDependentString(gGREPath), path);
exePath = FilePath(path.get()); 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 #endif
} }
@ -539,7 +550,17 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
if (ShouldHaveDirectoryService()) { if (ShouldHaveDirectoryService()) {
MOZ_ASSERT(gGREPath); MOZ_ASSERT(gGREPath);
nsCString path; 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); NS_CopyUnicodeToNative(nsDependentString(gGREPath), path);
#endif
# if defined(OS_LINUX) || defined(OS_BSD) # if defined(OS_LINUX) || defined(OS_BSD)
# if defined(MOZ_WIDGET_ANDROID) # if defined(MOZ_WIDGET_ANDROID)
path += "/lib"; path += "/lib";

View File

@ -1351,6 +1351,23 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
argc -= 2; argc -= 2;
argv += 2; argv += 2;
} else { } 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; nsAutoString workingDir;
if (!GetCurrentWorkingDirectory(workingDir)) { if (!GetCurrentWorkingDirectory(workingDir)) {
printf("GetCurrentWorkingDirectory failed.\n"); printf("GetCurrentWorkingDirectory failed.\n");
@ -1361,6 +1378,7 @@ XRE_XPCShellMain(int argc, char **argv, char **envp)
printf("NS_NewLocalFile failed.\n"); printf("NS_NewLocalFile failed.\n");
return 1; return 1;
} }
#endif
} }
if (argc > 1 && !strcmp(argv[1], "-a")) { if (argc > 1 && !strcmp(argv[1], "-a")) {

View File

@ -7,9 +7,6 @@
#include "prlog.h" #include "prlog.h"
#include "prenv.h" #include "prenv.h"
#include "webrtc/system_wrappers/interface/trace.h" #include "webrtc/system_wrappers/interface/trace.h"
#ifdef WEBRTC_GONK
#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
#endif
#include "nscore.h" #include "nscore.h"
#ifdef MOZILLA_INTERNAL_API #ifdef MOZILLA_INTERNAL_API
@ -56,10 +53,6 @@ public:
}; };
static WebRtcTraceCallback gWebRtcCallback; static WebRtcTraceCallback gWebRtcCallback;
#ifdef WEBRTC_GONK
static webrtc::LogcatTraceContext gWebRtcLogcatCallback;
#endif
#ifdef MOZILLA_INTERNAL_API #ifdef MOZILLA_INTERNAL_API
void GetWebRtcLogPrefs(uint32_t *aTraceMask, nsACString* aLogFile, nsACString *aAECLogDir, bool *aMultiLog) void GetWebRtcLogPrefs(uint32_t *aTraceMask, nsACString* aLogFile, nsACString *aAECLogDir, bool *aMultiLog)
@ -122,13 +115,8 @@ void ConfigWebRtcLog(uint32_t trace_mask, nsCString &aLogFile, nsCString &aAECLo
logFile.Append(default_log); logFile.Append(default_log);
} }
#elif defined(ANDROID) #elif defined(ANDROID)
#ifdef WEBRTC_GONK
// Special case: use callback to log through logcat.
logFile.Assign("logcat");
#else
// Special case: use callback to pipe to NSPR logging. // Special case: use callback to pipe to NSPR logging.
logFile.Assign("nspr"); logFile.Assign("nspr");
#endif
// for AEC, force the user to specify a directory // for AEC, force the user to specify a directory
aecLogDir.Assign("/dev/null"); aecLogDir.Assign("/dev/null");
#else #else
@ -149,10 +137,6 @@ void ConfigWebRtcLog(uint32_t trace_mask, nsCString &aLogFile, nsCString &aAECLo
if (trace_mask != 0) { if (trace_mask != 0) {
if (aLogFile.EqualsLiteral("nspr")) { if (aLogFile.EqualsLiteral("nspr")) {
webrtc::Trace::SetTraceCallback(&gWebRtcCallback); webrtc::Trace::SetTraceCallback(&gWebRtcCallback);
#ifdef WEBRTC_GONK
} else if (aLogFile.EqualsLiteral("logcat")) {
webrtc::Trace::SetTraceCallback(&gWebRtcLogcatCallback);
#endif
} else { } else {
webrtc::Trace::SetTraceFile(aLogFile.get(), multi_log); webrtc::Trace::SetTraceFile(aLogFile.get(), multi_log);
} }

View File

@ -188,7 +188,6 @@
['moz_widget_toolkit_gonk==1', { ['moz_widget_toolkit_gonk==1', {
'defines' : [ 'defines' : [
'WEBRTC_GONK', 'WEBRTC_GONK',
'WEBRTC_DIRECT_TRACE',
], ],
}], }],
['restrict_webrtc_logging==1', { ['restrict_webrtc_logging==1', {

View File

@ -78,8 +78,8 @@ import org.mozilla.gecko.widget.GeckoActionProvider;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.content.BroadcastReceiver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
@ -521,14 +521,8 @@ public class BrowserApp extends GeckoApp
final String args = intent.getStringExtra("args"); final String args = intent.getStringExtra("args");
if (GuestSession.shouldUse(this, args)) { if (GuestSession.shouldUse(this, args)) {
GuestSession.configureWindow(getWindow());
mProfile = GeckoProfile.createGuestProfile(this); mProfile = GeckoProfile.createGuestProfile(this);
} else { } 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); GeckoProfile.maybeCleanupGuestProfile(this);
} }
@ -757,6 +751,7 @@ public class BrowserApp extends GeckoApp
final String args = getIntent().getStringExtra("args"); final String args = getIntent().getStringExtra("args");
// If an external intent tries to start Fennec in guest mode, and it's not already // 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. // 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 enableGuestSession = GuestSession.shouldUse(this, args);
final boolean inGuestSession = GeckoProfile.get(this).inGuestMode(); final boolean inGuestSession = GeckoProfile.get(this).inGuestMode();
if (enableGuestSession != inGuestSession) { if (enableGuestSession != inGuestSession) {
@ -765,6 +760,19 @@ public class BrowserApp extends GeckoApp
return; 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, EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
"Prompt:ShowTop"); "Prompt:ShowTop");
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -7,8 +7,8 @@
android:color="@color/text_color_primary_inverse"/> android:color="@color/text_color_primary_inverse"/>
<item android:state_checked="true" <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> </selector>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<level-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gecko="http://schemas.android.com/apk/res-auto">
<item android:maxLevel="1">
<selector>
<item gecko:state_private="true" android:drawable="@drawable/menu_pb"/>
<item android:drawable="@drawable/new_tablet_menu"/>
</selector>
</item>
<item android:maxLevel="2" android:drawable="@android:color/transparent"/>
</level-list>

View File

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

View File

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

View File

@ -44,7 +44,7 @@
<color name="text_color_tertiary">#9198A1</color> <color name="text_color_tertiary">#9198A1</color>
<!-- Default inverse colors --> <!-- 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_secondary_inverse">#DDDDDD</color>
<color name="text_color_tertiary_inverse">#A4A7A9</color> <color name="text_color_tertiary_inverse">#A4A7A9</color>

View File

@ -12,7 +12,9 @@
<dimen name="browser_toolbar_button_padding">12dp</dimen> <dimen name="browser_toolbar_button_padding">12dp</dimen>
<dimen name="browser_toolbar_icon_width">48dp</dimen> <dimen name="browser_toolbar_icon_width">48dp</dimen>
<dimen name="browser_toolbar_lock_width">20dp</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="browser_toolbar_shadow_size">2dp</dimen>
<dimen name="new_tablet_tab_strip_height">48dp</dimen> <dimen name="new_tablet_tab_strip_height">48dp</dimen>

View File

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

View File

@ -1,11 +1,13 @@
package org.mozilla.gecko.tests; package org.mozilla.gecko.tests;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.mozilla.gecko.Actions; import org.mozilla.gecko.Actions;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager;
import android.widget.ImageView; 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. // Verify that the number of displayed searchengines is the same as the one received through the SearchEngines:Data event.
verifyDisplayedSearchEnginesCount(initialNumSearchEngines + 1); verifyDisplayedSearchEnginesCount(initialNumSearchEngines + 1);
searchEngineDataEventExpector.unregisterListener(); 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); mActions.sendSpecialKey(Actions.SpecialKey.BACK);
waitForText(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE); 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"); mAsserter.ok(correctNumSearchEnginesDisplayed, expectedCount + " Search Engines should be displayed" , "The correct number of Search Engines has been displayed");
} }
} }

View File

@ -107,6 +107,11 @@ public class UpdateService extends IntentService {
} else if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) { } else if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) {
mCancelDownload = true; mCancelDownload = true;
} else { } 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); super.onStartCommand(intent, flags, startId);
} }
@ -412,8 +417,26 @@ public class UpdateService extends IntentService {
mNotificationManager.notify(NOTIFICATION_ID, notification); 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) { 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()) { 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 // 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) { private void applyUpdate(String updatePath) {
if (updatePath == null) { if (updatePath == null) {
updatePath = mPrefs.getString(KEY_LAST_FILE_NAME, null); updatePath = getLastFileName();
} }
applyUpdate(new File(updatePath)); applyUpdate(new File(updatePath));
} }
@ -569,6 +592,10 @@ public class UpdateService extends IntentService {
return mPrefs.getString(KEY_LAST_HASH_VALUE, null); return mPrefs.getString(KEY_LAST_HASH_VALUE, null);
} }
private String getLastFileName() {
return mPrefs.getString(KEY_LAST_FILE_NAME, null);
}
private Calendar getLastAttemptDate() { private Calendar getLastAttemptDate() {
long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1); long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1);
if (lastAttempt < 0) if (lastAttempt < 0)

View File

@ -98,6 +98,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetErrorHelper",
XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils", XPCOMUtils.defineLazyModuleGetter(this, "PermissionsUtils",
"resource://gre/modules/PermissionsUtils.jsm"); "resource://gre/modules/PermissionsUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SharedPreferences",
"resource://gre/modules/SharedPreferences.jsm");
// Lazily-loaded browser scripts: // Lazily-loaded browser scripts:
[ [
["SelectHelper", "chrome://browser/content/SelectHelper.js"], ["SelectHelper", "chrome://browser/content/SelectHelper.js"],
@ -820,12 +823,32 @@ var BrowserApp = {
Services.obs.notifyObservers(null, "FormHistory:Init", ""); Services.obs.notifyObservers(null, "FormHistory:Init", "");
Services.obs.notifyObservers(null, "Passwords:Init", ""); Services.obs.notifyObservers(null, "Passwords:Init", "");
// Migrate user-set "plugins.click_to_play" pref. See bug 884694. // Migrate the UI if necessary.
// Because the default value is true, a user-set pref means that the pref was set to false. const UI_VERSION = 1;
if (Services.prefs.prefHasUserValue("plugins.click_to_play")) {
Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED); let currentUIVersion = 0;
Services.prefs.clearUserPref("plugins.click_to_play"); 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() { shutdown: function shutdown() {
@ -6764,12 +6787,16 @@ var SearchEngines = {
PREF_SUGGEST_ENABLED: "browser.search.suggest.enabled", PREF_SUGGEST_ENABLED: "browser.search.suggest.enabled",
PREF_SUGGEST_PROMPTED: "browser.search.suggest.prompted", 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() { init: function init() {
Services.obs.addObserver(this, "SearchEngines:Add", false); Services.obs.addObserver(this, "SearchEngines:Add", false);
Services.obs.addObserver(this, "SearchEngines:GetVisible", false); Services.obs.addObserver(this, "SearchEngines:GetVisible", false);
Services.obs.addObserver(this, "SearchEngines:Remove", false); Services.obs.addObserver(this, "SearchEngines:Remove", false);
Services.obs.addObserver(this, "SearchEngines:RestoreDefaults", false); Services.obs.addObserver(this, "SearchEngines:RestoreDefaults", false);
Services.obs.addObserver(this, "SearchEngines:SetDefault", false); Services.obs.addObserver(this, "SearchEngines:SetDefault", false);
Services.obs.addObserver(this, "browser-search-engine-modified", false);
let filter = { let filter = {
matches: function (aElement) { matches: function (aElement) {
@ -6814,6 +6841,7 @@ var SearchEngines = {
Services.obs.removeObserver(this, "SearchEngines:Remove"); Services.obs.removeObserver(this, "SearchEngines:Remove");
Services.obs.removeObserver(this, "SearchEngines:RestoreDefaults"); Services.obs.removeObserver(this, "SearchEngines:RestoreDefaults");
Services.obs.removeObserver(this, "SearchEngines:SetDefault"); Services.obs.removeObserver(this, "SearchEngines:SetDefault");
Services.obs.removeObserver(this, "browser-search-engine-modified");
if (this._contextMenuId != null) if (this._contextMenuId != null)
NativeWindow.contextmenus.remove(this._contextMenuId); NativeWindow.contextmenus.remove(this._contextMenuId);
}, },
@ -6894,13 +6922,53 @@ var SearchEngines = {
Services.search.moveEngine(engine, 0); Services.search.moveEngine(engine, 0);
Services.search.defaultEngine = engine; Services.search.defaultEngine = engine;
break; break;
case "browser-search-engine-modified":
if (aData == "engine-default") {
this._setSearchActivityDefaultPref(aSubject.QueryInterface(Ci.nsISearchEngine));
}
break;
default: default:
dump("Unexpected message type observed: " + aTopic); dump("Unexpected message type observed: " + aTopic);
break; 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. // Display context menu listing names of the search engines available to be added.
displaySearchEnginesList: function displaySearchEnginesList(aData) { displaySearchEnginesList: function displaySearchEnginesList(aData) {
let data = JSON.parse(aData); let data = JSON.parse(aData);

View File

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

View File

@ -6,14 +6,11 @@ package org.mozilla.search;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
@ -22,10 +19,6 @@ import org.mozilla.gecko.LocaleAware;
import org.mozilla.gecko.Telemetry; import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract; import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract; 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. * 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. * TODO: Change this to PreferenceFragment when we stop supporting devices older than SDK 11.
*/ */
public class SearchPreferenceActivity extends PreferenceActivity public class SearchPreferenceActivity extends PreferenceActivity {
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String LOG_TAG = "SearchPreferenceActivity"; 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_CLEAR_HISTORY_KEY = "search.not_a_preference.clear_history";
public static final String PREF_SEARCH_ENGINE_KEY = "search.engines.default";
private SearchEngineManager searchEngineManager;
@Override @Override
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@ -55,8 +44,6 @@ public class SearchPreferenceActivity extends PreferenceActivity
getPreferenceManager().setSharedPreferencesName(GeckoSharedPrefs.APP_PREFS_NAME); getPreferenceManager().setSharedPreferencesName(GeckoSharedPrefs.APP_PREFS_NAME);
searchEngineManager = new SearchEngineManager(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (getActionBar() != null) { if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true); 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 @Override
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
@ -111,40 +80,6 @@ public class SearchPreferenceActivity extends PreferenceActivity
return false; 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() { private void clearHistory() {
@ -169,19 +104,4 @@ public class SearchPreferenceActivity extends PreferenceActivity
}; };
clearHistoryTask.execute(); clearHistoryTask.execute();
} }
/**
* Update summaries when the value of a shared preference changes.
*/
@Override
@SuppressWarnings("deprecation")
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (TextUtils.equals(PREF_SEARCH_ENGINE_KEY, key)) {
final ListPreference searchEnginePref = (ListPreference) findPreference(PREF_SEARCH_ENGINE_KEY);
searchEnginePref.setSummary(searchEnginePref.getEntry());
Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH_SET_DEFAULT,
TelemetryContract.Method.DIALOG,
searchEnginePref.getValue().toLowerCase());
}
}
} }

View File

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

View File

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

View File

@ -6,9 +6,4 @@
<Preference <Preference
android:key="search.not_a_preference.clear_history" android:key="search.not_a_preference.clear_history"
android:title="@string/pref_clearHistory_title"/> 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> </PreferenceScreen>

View File

@ -10,7 +10,7 @@
<Param name="action" value="opensearch"/> <Param name="action" value="opensearch"/>
<Param name="search" value="{searchTerms}"/> <Param name="search" value="{searchTerms}"/>
</Url> </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="search" value="{searchTerms}"/>
<Param name="sourceid" value="Mozilla-search"/> <Param name="sourceid" value="Mozilla-search"/>
</Url> </Url>

View File

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

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