Merge m-c to inbound.
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
@ -102,7 +102,7 @@
|
||||
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="f19ba22ba6973804628781606bc072d18f3f79c2"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="acba00cdb4596c6dcb61ed06f14cf4ec89623539"/>
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
|
||||
</manifest>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="65fba428f8d76336b33ddd9e15900357953600ba">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="ca283b9db2b151d465cfd2e19346cf58fe89e413"/>
|
||||
@ -102,7 +102,7 @@
|
||||
<project name="platform/external/iproute2" path="external/iproute2" revision="c66c5716d5335e450f7a7b71ccc6a604fb2f41d2"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="d2685281e2e54ca14d1df304867aa82c37b27162"/>
|
||||
<project name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="627f9b20fc518937b93747a7ff1ed4f5ed46e06f"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="f19ba22ba6973804628781606bc072d18f3f79c2"/>
|
||||
<project name="platform/prebuilts/tools" path="prebuilts/tools" revision="acba00cdb4596c6dcb61ed06f14cf4ec89623539"/>
|
||||
<project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="431afac2ebfdd9c1c8402b413ff5914ed448e961"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="4f46930827957afbce500a4a920755a218bf3155"/>
|
||||
</manifest>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
|
||||
@ -118,7 +118,7 @@
|
||||
<default remote="caf" revision="jb_3.2" sync-j="4"/>
|
||||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="e8a318f7690092e639ba88891606f4183e846d3f"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="66715ba03ef9dd277ebd65ef3c17231c4dcdf0ed"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="34ed8345250bb97262d70a052217a92e83444ede"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="ecc08b2f0efea93c778e3525553bcd4bd5f2cf49"/>
|
||||
<project name="kernel/msm" path="kernel" revision="7158567fc83e7475f08db3adedc5df1ad6f54abd"/>
|
||||
<project name="platform/bootable/recovery" path="bootable/recovery" revision="f2914eacee9120680a41463708bb6ee8291749fc"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "d37aa6da234abefd5227511661ec6a365c1cfb84",
|
||||
"revision": "5bd5379f114f542314571a0361089cfd5beb5827",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="3512d982f336887a73283bb8d1147a8f7b822077"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="de1c755411949b50ae395b42e124af215ed9b702"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="461ea9a09563c2caf1fe5195227d126dadb83dc5"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="30f87e602509415be4f69493c23cba1912f91ef5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
@ -1239,6 +1239,13 @@ pref("devtools.appmanager.enabled", true);
|
||||
pref("devtools.appmanager.lastTab", "help");
|
||||
pref("devtools.appmanager.manifestEditor.enabled", true);
|
||||
|
||||
// Enable devtools webide
|
||||
#ifdef MOZ_DEVTOOLS_WEBIDE
|
||||
pref("devtools.webide.enabled", true);
|
||||
#else
|
||||
pref("devtools.webide.enabled", false);
|
||||
#endif
|
||||
|
||||
// Toolbox preferences
|
||||
pref("devtools.toolbox.footer.height", 250);
|
||||
pref("devtools.toolbox.sidebar.width", 500);
|
||||
@ -1556,6 +1563,7 @@ pref("browser.cache.auto_delete_cache_version", 1);
|
||||
pref("browser.cache.frecency_experiment", 0);
|
||||
|
||||
pref("browser.translation.detectLanguage", false);
|
||||
pref("browser.translation.neverForLanguages", "");
|
||||
|
||||
// Telemetry experiments settings.
|
||||
pref("experiments.enabled", true);
|
||||
|
@ -942,11 +942,11 @@ chatbox:-moz-full-screen-ancestor > .chat-titlebar {
|
||||
%ifndef MOZ_WIDGET_GTK
|
||||
|
||||
#BMB_bookmarksPopup {
|
||||
transform: scale(.7);
|
||||
transform: scale(.4);
|
||||
opacity: 0;
|
||||
transition-property: transform, opacity;
|
||||
transition-duration: 0.15s;
|
||||
transition-timing-function: ease;
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
#BMB_bookmarksPopup[animate="open"] {
|
||||
@ -972,12 +972,12 @@ chatbox:-moz-full-screen-ancestor > .chat-titlebar {
|
||||
|
||||
#BMB_bookmarksPopup[arrowposition="after_start"][animate="cancel"],
|
||||
#BMB_bookmarksPopup[arrowposition="before_end"][animate="cancel"] {
|
||||
transform: scale(.7) skew(30deg, 20deg);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
#BMB_bookmarksPopup[arrowposition="after_end"][animate="cancel"],
|
||||
#BMB_bookmarksPopup[arrowposition="before_start"][animate="cancel"] {
|
||||
transform: scale(.7) skew(-30deg, -20deg);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
%endif
|
||||
|
@ -906,6 +906,8 @@ let CustomizableUIInternal = {
|
||||
let anchor = props.get("anchor");
|
||||
if (anchor) {
|
||||
aNode.setAttribute("cui-anchorid", anchor);
|
||||
} else {
|
||||
aNode.removeAttribute("cui-anchorid");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -108,5 +108,6 @@ skip-if = os == "linux"
|
||||
[browser_993322_widget_notoolbar.js]
|
||||
[browser_995164_registerArea_during_customize_mode.js]
|
||||
[browser_996364_registerArea_different_properties.js]
|
||||
[browser_1008559_anchor_undo_restore.js]
|
||||
[browser_bootstrapped_custom_toolbar.js]
|
||||
[browser_panel_toggle.js]
|
||||
|
@ -0,0 +1,71 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const kAnchorAttribute = "cui-anchorid";
|
||||
|
||||
/**
|
||||
* Check that anchor gets set correctly when moving an item from the panel to the toolbar
|
||||
* using 'undo'
|
||||
*/
|
||||
add_task(function*() {
|
||||
yield startCustomizing();
|
||||
let button = document.getElementById("history-panelmenu");
|
||||
is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
|
||||
"Button (" + button.id + ") starts out with correct anchor");
|
||||
|
||||
let navbar = document.getElementById("nav-bar").customizationTarget;
|
||||
simulateItemDrag(button, navbar);
|
||||
is(CustomizableUI.getPlacementOfWidget(button.id).area, "nav-bar",
|
||||
"Button (" + button.id + ") ends up in nav-bar");
|
||||
|
||||
ok(!button.hasAttribute(kAnchorAttribute),
|
||||
"Button (" + button.id + ") has no anchor in toolbar");
|
||||
|
||||
let resetButton = document.getElementById("customization-reset-button");
|
||||
ok(!resetButton.hasAttribute("disabled"), "Should be able to reset now.");
|
||||
yield gCustomizeMode.reset();
|
||||
|
||||
is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
|
||||
"Button (" + button.id + ") has anchor again");
|
||||
|
||||
let undoButton = document.getElementById("customization-undo-reset-button");
|
||||
ok(!undoButton.hasAttribute("disabled"), "Should be able to undo now.");
|
||||
yield gCustomizeMode.undoReset();
|
||||
|
||||
ok(!button.hasAttribute(kAnchorAttribute),
|
||||
"Button (" + button.id + ") once again has no anchor in toolbar");
|
||||
|
||||
yield gCustomizeMode.reset();
|
||||
|
||||
yield endCustomizing();
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Check that anchor gets set correctly when moving an item from the panel to the toolbar
|
||||
* using 'reset'
|
||||
*/
|
||||
add_task(function*() {
|
||||
yield startCustomizing();
|
||||
let button = document.getElementById("bookmarks-menu-button");
|
||||
ok(!button.hasAttribute(kAnchorAttribute),
|
||||
"Button (" + button.id + ") has no anchor in toolbar");
|
||||
|
||||
let panel = document.getElementById("PanelUI-contents");
|
||||
simulateItemDrag(button, panel);
|
||||
is(CustomizableUI.getPlacementOfWidget(button.id).area, "PanelUI-contents",
|
||||
"Button (" + button.id + ") ends up in panel");
|
||||
is(button.getAttribute(kAnchorAttribute), "PanelUI-menu-button",
|
||||
"Button (" + button.id + ") has correct anchor in the panel");
|
||||
|
||||
let resetButton = document.getElementById("customization-reset-button");
|
||||
ok(!resetButton.hasAttribute("disabled"), "Should be able to reset now.");
|
||||
yield gCustomizeMode.reset();
|
||||
|
||||
ok(!button.hasAttribute(kAnchorAttribute),
|
||||
"Button (" + button.id + ") once again has no anchor in toolbar");
|
||||
|
||||
yield endCustomizing();
|
||||
});
|
@ -170,5 +170,16 @@ var gContentPane = {
|
||||
{
|
||||
document.documentElement.openSubDialog("chrome://browser/content/preferences/languages.xul",
|
||||
"", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the translation exceptions dialog where specific site and language
|
||||
* translation preferences can be set.
|
||||
*/
|
||||
showTranslationExceptions: function ()
|
||||
{
|
||||
document.documentElement.openWindow("Browser:TranslationExceptions",
|
||||
"chrome://browser/content/preferences/translation.xul",
|
||||
"", null);
|
||||
}
|
||||
};
|
||||
|
@ -30,6 +30,11 @@
|
||||
name="font.language.group"
|
||||
type="wstring"
|
||||
onchange="gContentPane._rebuildFonts();"/>
|
||||
|
||||
<!-- LANGUAGES -->
|
||||
<preference id="browser.translation.detectLanguage"
|
||||
name="browser.translation.detectLanguage"
|
||||
type="bool"/>
|
||||
</preferences>
|
||||
|
||||
<script type="application/javascript" src="chrome://mozapps/content/preferences/fontbuilder.js"/>
|
||||
@ -125,13 +130,31 @@
|
||||
<groupbox id="languagesGroup">
|
||||
<caption label="&languages.label;"/>
|
||||
|
||||
<hbox id="languagesBox" align="center">
|
||||
<description flex="1" control="chooseLanguage">&chooseLanguage.label;</description>
|
||||
<button id="chooseLanguage"
|
||||
label="&chooseButton.label;"
|
||||
accesskey="&chooseButton.accesskey;"
|
||||
oncommand="gContentPane.showLanguages();"/>
|
||||
</hbox>
|
||||
<grid id="languagesGrid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows id="languagesRows">
|
||||
<row id="preferredLanguageRow">
|
||||
<label flex="1" control="chooseLanguage">&chooseLanguage.label;</label>
|
||||
<button id="chooseLanguage"
|
||||
label="&chooseButton.label;"
|
||||
accesskey="&chooseButton.accesskey;"
|
||||
oncommand="gContentPane.showLanguages();"/>
|
||||
</row>
|
||||
<row hidden="true">
|
||||
<checkbox id="translate" preference="browser.translation.detectLanguage" flex="1"
|
||||
label="&translateWebPages.label;" accesskey="&translateWebPages.accesskey;"
|
||||
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
||||
'browser.translation.detectLanguage');"/>
|
||||
<button id="translateButton" label="&translateExceptions.label;"
|
||||
oncommand="gContentPane.showTranslationExceptions();"
|
||||
accesskey="&translateExceptions.accesskey;"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
||||
</groupbox>
|
||||
|
||||
</prefpane>
|
||||
|
@ -169,5 +169,15 @@ var gContentPane = {
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/languages.xul",
|
||||
"Browser:LanguagePreferences", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the translation exceptions dialog where specific site and language
|
||||
* translation preferences can be set.
|
||||
*/
|
||||
showTranslationExceptions: function ()
|
||||
{
|
||||
openDialog("chrome://browser/content/preferences/translation.xul",
|
||||
"Browser:TranslationExceptions", null);
|
||||
}
|
||||
};
|
||||
|
@ -14,6 +14,11 @@
|
||||
name="font.language.group"
|
||||
type="wstring"
|
||||
onchange="gContentPane._rebuildFonts();"/>
|
||||
|
||||
<!-- Languages -->
|
||||
<preference id="browser.translation.detectLanguage"
|
||||
name="browser.translation.detectLanguage"
|
||||
type="bool"/>
|
||||
</preferences>
|
||||
|
||||
<script type="application/javascript"
|
||||
@ -126,4 +131,14 @@
|
||||
accesskey="&chooseButton.accesskey;"
|
||||
oncommand="gContentPane.showLanguages();"/>
|
||||
</hbox>
|
||||
|
||||
<hbox id="translationBox" hidden="true">
|
||||
<checkbox id="translate" preference="browser.translation.detectLanguage" flex="1"
|
||||
label="&translateWebPages.label;" accesskey="&translateWebPages.accesskey;"
|
||||
onsyncfrompreference="return gContentPane.updateButtons('translateButton',
|
||||
'browser.translation.detectLanguage');"/>
|
||||
<button id="translateButton" label="&translateExceptions.label;"
|
||||
oncommand="gContentPane.showTranslationExceptions();"
|
||||
accesskey="&translateExceptions.accesskey;"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
@ -45,3 +45,5 @@ browser.jar:
|
||||
#endif
|
||||
* content/browser/preferences/tabs.xul
|
||||
* content/browser/preferences/tabs.js
|
||||
* content/browser/preferences/translation.xul
|
||||
content/browser/preferences/translation.js
|
||||
|
227
browser/components/preferences/translation.js
Normal file
@ -0,0 +1,227 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gLangBundle", () =>
|
||||
Services.strings.createBundle("chrome://global/locale/languageNames.properties"));
|
||||
|
||||
const kPermissionType = "translate";
|
||||
const kLanguagesPref = "browser.translation.neverForLanguages";
|
||||
|
||||
function Tree(aId, aData)
|
||||
{
|
||||
this._data = aData;
|
||||
this._tree = document.getElementById(aId);
|
||||
this._tree.treeBoxObject.view = this;
|
||||
}
|
||||
|
||||
Tree.prototype = {
|
||||
get boxObject() this._tree.treeBoxObject,
|
||||
get isEmpty() !this._data.length,
|
||||
get hasSelection() this.selection.count > 0,
|
||||
getSelectedItems: function() {
|
||||
let result = [];
|
||||
|
||||
let rc = this.selection.getRangeCount();
|
||||
for (let i = 0; i < rc; ++i) {
|
||||
let min = {}, max = {};
|
||||
this.selection.getRangeAt(i, min, max);
|
||||
for (let j = min.value; j <= max.value; ++j)
|
||||
result.push(this._data[j]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// nsITreeView implementation
|
||||
get rowCount() this._data.length,
|
||||
getCellText: function (aRow, aColumn) this._data[aRow],
|
||||
isSeparator: function(aIndex) false,
|
||||
isSorted: function() false,
|
||||
isContainer: function(aIndex) false,
|
||||
setTree: function(aTree) {},
|
||||
getImageSrc: function(aRow, aColumn) {},
|
||||
getProgressMode: function(aRow, aColumn) {},
|
||||
getCellValue: function(aRow, aColumn) {},
|
||||
cycleHeader: function(column) {},
|
||||
getRowProperties: function(row) "",
|
||||
getColumnProperties: function(column) "",
|
||||
getCellProperties: function(row, column) "",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView])
|
||||
};
|
||||
|
||||
function Lang(aCode)
|
||||
{
|
||||
this.langCode = aCode;
|
||||
this._label = gLangBundle.GetStringFromName(aCode);
|
||||
}
|
||||
|
||||
Lang.prototype = {
|
||||
toString: function() this._label
|
||||
}
|
||||
|
||||
let gTranslationExceptions = {
|
||||
onLoad: function() {
|
||||
if (this._siteTree) {
|
||||
// Re-using an open dialog, clear the old observers.
|
||||
this.uninit();
|
||||
}
|
||||
|
||||
// Load site permissions into an array.
|
||||
this._sites = [];
|
||||
let enumerator = Services.perms.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
|
||||
|
||||
if (perm.type == kPermissionType &&
|
||||
perm.capability == Services.perms.DENY_ACTION) {
|
||||
this._sites.push(perm.host);
|
||||
}
|
||||
}
|
||||
Services.obs.addObserver(this, "perm-changed", false);
|
||||
this._sites.sort();
|
||||
|
||||
this._siteTree = new Tree("sitesTree", this._sites);
|
||||
this.onSiteSelected();
|
||||
|
||||
this._langs = this.getLanguageExceptions();
|
||||
Services.prefs.addObserver(kLanguagesPref, this, false);
|
||||
this._langTree = new Tree("languagesTree", this._langs);
|
||||
this.onLanguageSelected();
|
||||
},
|
||||
|
||||
// Get the list of languages we don't translate as an array.
|
||||
getLanguageExceptions: function() {
|
||||
let langs = Services.prefs.getCharPref(kLanguagesPref);
|
||||
if (!langs)
|
||||
return [];
|
||||
|
||||
let result = langs.split(",").map(code => new Lang(code));
|
||||
result.sort();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "perm-changed") {
|
||||
if (aData == "cleared") {
|
||||
if (!this._sites.length)
|
||||
return;
|
||||
let removed = this._sites.splice(0, this._sites.length);
|
||||
this._siteTree.boxObject.rowCountChanged(0, - removed.length);
|
||||
}
|
||||
else {
|
||||
let perm = aSubject.QueryInterface(Ci.nsIPermission);
|
||||
if (perm.type != kPermissionType)
|
||||
return;
|
||||
|
||||
if (aData == "added") {
|
||||
if (perm.capability != Services.perms.DENY_ACTION)
|
||||
return;
|
||||
this._sites.push(perm.host);
|
||||
this._sites.sort();
|
||||
let boxObject = this._siteTree.boxObject;
|
||||
boxObject.rowCountChanged(0, 1);
|
||||
boxObject.invalidate();
|
||||
}
|
||||
else if (aData == "deleted") {
|
||||
let index = this._sites.indexOf(perm.host);
|
||||
if (index == -1)
|
||||
return;
|
||||
this._sites.splice(index, 1);
|
||||
this._siteTree.boxObject.rowCountChanged(index, -1);
|
||||
this.onSiteSelected();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.onSiteSelected();
|
||||
}
|
||||
else if (aTopic == "nsPref:changed") {
|
||||
this._langs = this.getLanguageExceptions();
|
||||
let change = this._langs.length - this._langTree.rowCount;
|
||||
this._langTree._data = this._langs;
|
||||
let boxObject = this._langTree.boxObject;
|
||||
if (change)
|
||||
boxObject.rowCountChanged(0, change);
|
||||
boxObject.invalidate();
|
||||
this.onLanguageSelected();
|
||||
}
|
||||
},
|
||||
|
||||
_handleButtonDisabling: function(aTree, aIdPart) {
|
||||
let empty = aTree.isEmpty;
|
||||
document.getElementById("removeAll" + aIdPart + "s").disabled = empty;
|
||||
document.getElementById("remove" + aIdPart).disabled =
|
||||
empty || !aTree.hasSelection;
|
||||
},
|
||||
|
||||
onLanguageSelected: function() {
|
||||
this._handleButtonDisabling(this._langTree, "Language");
|
||||
},
|
||||
|
||||
onSiteSelected: function() {
|
||||
this._handleButtonDisabling(this._siteTree, "Site");
|
||||
},
|
||||
|
||||
onLanguageDeleted: function() {
|
||||
let langs = Services.prefs.getCharPref(kLanguagesPref);
|
||||
if (!langs)
|
||||
return;
|
||||
|
||||
let removed = this._langTree.getSelectedItems().map(l => l.langCode);
|
||||
|
||||
langs = langs.split(",").filter(l => removed.indexOf(l) == -1);
|
||||
Services.prefs.setCharPref(kLanguagesPref, langs.join(","));
|
||||
},
|
||||
|
||||
onAllLanguagesDeleted: function() {
|
||||
Services.prefs.setCharPref(kLanguagesPref, "");
|
||||
},
|
||||
|
||||
onSiteDeleted: function() {
|
||||
let removedSites = this._siteTree.getSelectedItems();
|
||||
for (let host of removedSites)
|
||||
Services.perms.remove(host, kPermissionType);
|
||||
},
|
||||
|
||||
onAllSitesDeleted: function() {
|
||||
if (this._siteTree.isEmpty)
|
||||
return;
|
||||
|
||||
let removedSites = this._sites.splice(0, this._sites.length);
|
||||
this._siteTree.boxObject.rowCountChanged(0, -removedSites.length);
|
||||
|
||||
for (let host of removedSites)
|
||||
Services.perms.remove(host, kPermissionType);
|
||||
|
||||
this.onSiteSelected();
|
||||
},
|
||||
|
||||
onSiteKeyPress: function(aEvent) {
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE)
|
||||
this.onSiteDeleted();
|
||||
},
|
||||
|
||||
onLanguageKeyPress: function() {
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE)
|
||||
this.onLanguageDeleted();
|
||||
},
|
||||
|
||||
onWindowKeyPress: function(aEvent) {
|
||||
if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
|
||||
window.close();
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
Services.obs.removeObserver(this, "perm-changed");
|
||||
Services.prefs.removeObserver(kLanguagesPref, this);
|
||||
}
|
||||
};
|
90
browser/components/preferences/translation.xul
Normal file
@ -0,0 +1,90 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/translation.dtd">
|
||||
|
||||
<window id="TranslationDialog" class="windowDialog"
|
||||
windowtype="Browser:TranslationExceptions"
|
||||
title="&window.title;"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
style="width: &window.width;;"
|
||||
onload="gTranslationExceptions.onLoad();"
|
||||
onunload="gTranslationExceptions.uninit();"
|
||||
persist="screenX screenY width height"
|
||||
onkeypress="gTranslationExceptions.onWindowKeyPress(event);">
|
||||
|
||||
<script src="chrome://browser/content/preferences/translation.js"/>
|
||||
|
||||
<stringbundle id="bundlePreferences"
|
||||
src="chrome://browser/locale/preferences/preferences.properties"/>
|
||||
|
||||
<keyset>
|
||||
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForLanguages.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="languagesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onLanguageKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onLanguageSelected();">
|
||||
<treecols>
|
||||
<treecol id="languageCol" label="&treehead.languageName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<button id="removeLanguage" disabled="true"
|
||||
accesskey="&removeLanguage.accesskey;"
|
||||
icon="remove" label="&removeLanguage.label;"
|
||||
oncommand="gTranslationExceptions.onLanguageDeleted();"/>
|
||||
<button id="removeAllLanguages"
|
||||
icon="clear" label="&removeAllLanguages.label;"
|
||||
accesskey="&removeAllLanguages.accesskey;"
|
||||
oncommand="gTranslationExceptions.onAllLanguagesDeleted();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<separator/>
|
||||
<vbox class="contentPane" flex="1">
|
||||
<label id="languagesLabel" control="permissionsTree">&noTranslationForSites.label;</label>
|
||||
<separator class="thin"/>
|
||||
<tree id="sitesTree" flex="1" style="height: 12em;"
|
||||
hidecolumnpicker="true"
|
||||
onkeypress="gTranslationExceptions.onSiteKeyPress(event)"
|
||||
onselect="gTranslationExceptions.onSiteSelected();">
|
||||
<treecols>
|
||||
<treecol id="siteCol" label="&treehead.siteName.label;" flex="1"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<button id="removeSite" disabled="true"
|
||||
accesskey="&removeSite.accesskey;"
|
||||
icon="remove" label="&removeSite.label;"
|
||||
oncommand="gTranslationExceptions.onSiteDeleted();"/>
|
||||
<button id="removeAllSites"
|
||||
icon="clear" label="&removeAllSites.label;"
|
||||
accesskey="&removeAllSites.accesskey;"
|
||||
oncommand="gTranslationExceptions.onAllSitesDeleted();"/>
|
||||
<spacer flex="1"/>
|
||||
#ifndef XP_MACOSX
|
||||
<button oncommand="close();" icon="close"
|
||||
label="&button.close.label;" accesskey="&button.close.accesskey;"/>
|
||||
#endif
|
||||
</hbox>
|
||||
<resizer type="window" dir="bottomend"/>
|
||||
</hbox>
|
||||
</window>
|
@ -64,8 +64,6 @@ TranslationUI.prototype = {
|
||||
STATE_TRANSLATED: 2,
|
||||
STATE_ERROR: 3,
|
||||
|
||||
get doc() this.browser.contentDocument,
|
||||
|
||||
translate: function(aFrom, aTo) {
|
||||
this.state = this.STATE_TRANSLATING;
|
||||
this.translatedFrom = aFrom;
|
||||
@ -125,6 +123,18 @@ TranslationUI.prototype = {
|
||||
return notif;
|
||||
},
|
||||
|
||||
shouldShowInfoBar: function(aURI, aDetectedLanguage) {
|
||||
// Check if we should never show the infobar for this language.
|
||||
let neverForLangs =
|
||||
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
||||
if (neverForLangs.split(",").indexOf(aDetectedLanguage) != -1)
|
||||
return false;
|
||||
|
||||
// or if we should never show the infobar for this domain.
|
||||
let perms = Services.perms;
|
||||
return perms.testExactPermission(aURI, "translate") != perms.DENY_ACTION;
|
||||
},
|
||||
|
||||
showTranslationUI: function(aDetectedLanguage) {
|
||||
this.detectedLanguage = aDetectedLanguage;
|
||||
|
||||
@ -135,6 +145,10 @@ TranslationUI.prototype = {
|
||||
this.originalShown = true;
|
||||
|
||||
this.showURLBarIcon();
|
||||
|
||||
if (!this.shouldShowInfoBar(this.browser.currentURI, aDetectedLanguage))
|
||||
return null;
|
||||
|
||||
return this.showTranslationInfoBar();
|
||||
}
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
[DEFAULT]
|
||||
|
||||
[browser_translation_infobar.js]
|
||||
[browser_translation_exceptions.js]
|
||||
|
@ -0,0 +1,301 @@
|
||||
/* 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/. */
|
||||
|
||||
// tests the translation infobar, using a fake 'Translation' implementation.
|
||||
|
||||
Components.utils.import("resource:///modules/translation/Translation.jsm");
|
||||
|
||||
const kLanguagesPref = "browser.translation.neverForLanguages";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
registerCleanupFunction(function () {
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
tab.linkedBrowser.addEventListener("load", function onload() {
|
||||
tab.linkedBrowser.removeEventListener("load", onload, true);
|
||||
Task.spawn(function* () {
|
||||
for (let test of gTests) {
|
||||
info(test.desc);
|
||||
yield test.run();
|
||||
}
|
||||
}).then(finish, ex => {
|
||||
ok(false, "Unexpected Exception: " + ex);
|
||||
finish();
|
||||
});
|
||||
}, true);
|
||||
|
||||
content.location = "http://example.com/";
|
||||
}
|
||||
|
||||
function getLanguageExceptions() {
|
||||
let langs = Services.prefs.getCharPref(kLanguagesPref);
|
||||
return langs ? langs.split(",") : [];
|
||||
}
|
||||
|
||||
function getDomainExceptions() {
|
||||
let results = [];
|
||||
let enumerator = Services.perms.enumerator;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
|
||||
|
||||
if (perm.type == "translate" &&
|
||||
perm.capability == Services.perms.DENY_ACTION)
|
||||
results.push(perm.host);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getInfoBar() {
|
||||
return gBrowser.getNotificationBox().getNotificationWithValue("translation");
|
||||
}
|
||||
|
||||
function openPopup(aPopup) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
aPopup.addEventListener("popupshown", function popupShown() {
|
||||
aPopup.removeEventListener("popupshown", popupShown);
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
aPopup.focus();
|
||||
// One down event to open the popup.
|
||||
EventUtils.synthesizeKey("VK_DOWN",
|
||||
{ altKey: !navigator.platform.contains("Mac") });
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForWindowLoad(aWin) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
aWin.addEventListener("load", function onload() {
|
||||
aWin.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
||||
let gTests = [
|
||||
|
||||
{
|
||||
desc: "clean exception lists at startup",
|
||||
run: function checkNeverForLanguage() {
|
||||
is(getLanguageExceptions().length, 0,
|
||||
"we start with an empty list of languages to never translate");
|
||||
is(getDomainExceptions().length, 0,
|
||||
"we start with an empty list of sites to never translate");
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "never for language",
|
||||
run: function* checkNeverForLanguage() {
|
||||
// Show the infobar for example.com and fr.
|
||||
Translation.languageDetected(gBrowser.selectedBrowser, "fr");
|
||||
let notif = getInfoBar();
|
||||
ok(notif, "the infobar is visible");
|
||||
let ui = gBrowser.selectedBrowser.translationUI;
|
||||
let uri = gBrowser.selectedBrowser.currentURI;
|
||||
ok(ui.shouldShowInfoBar(uri, "fr"),
|
||||
"check shouldShowInfoBar initially returns true");
|
||||
|
||||
// Open the "options" drop down.
|
||||
yield openPopup(notif._getAnonElt("options"));
|
||||
ok(notif._getAnonElt("options").getAttribute("open"),
|
||||
"the options menu is open");
|
||||
|
||||
// Check that the item is not disabled.
|
||||
ok(!notif._getAnonElt("neverForLanguage").disabled,
|
||||
"The 'Never translate <language>' item isn't disabled");
|
||||
|
||||
// Click the 'Never for French' item.
|
||||
notif._getAnonElt("neverForLanguage").click();
|
||||
ok(!getInfoBar(), "infobar hidden");
|
||||
|
||||
// Check this has been saved to the exceptions list.
|
||||
let langs = getLanguageExceptions();
|
||||
is(langs.length, 1, "one language in the exception list");
|
||||
is(langs[0], "fr", "correct language in the exception list");
|
||||
ok(!ui.shouldShowInfoBar(uri, "fr"),
|
||||
"the infobar wouldn't be shown anymore");
|
||||
|
||||
// Reopen the infobar.
|
||||
PopupNotifications.getNotification("translate").anchorElement.click();
|
||||
notif = getInfoBar();
|
||||
// Open the "options" drop down.
|
||||
yield openPopup(notif._getAnonElt("options"));
|
||||
ok(notif._getAnonElt("neverForLanguage").disabled,
|
||||
"The 'Never translate French' item is disabled");
|
||||
|
||||
// Cleanup.
|
||||
Services.prefs.setCharPref(kLanguagesPref, "");
|
||||
notif.close();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "never for site",
|
||||
run: function* checkNeverForSite() {
|
||||
// Show the infobar for example.com and fr.
|
||||
Translation.languageDetected(gBrowser.selectedBrowser, "fr");
|
||||
let notif = getInfoBar();
|
||||
ok(notif, "the infobar is visible");
|
||||
let ui = gBrowser.selectedBrowser.translationUI;
|
||||
let uri = gBrowser.selectedBrowser.currentURI;
|
||||
ok(ui.shouldShowInfoBar(uri, "fr"),
|
||||
"check shouldShowInfoBar initially returns true");
|
||||
|
||||
// Open the "options" drop down.
|
||||
yield openPopup(notif._getAnonElt("options"));
|
||||
ok(notif._getAnonElt("options").getAttribute("open"),
|
||||
"the options menu is open");
|
||||
|
||||
// Check that the item is not disabled.
|
||||
ok(!notif._getAnonElt("neverForSite").disabled,
|
||||
"The 'Never translate site' item isn't disabled");
|
||||
|
||||
// Click the 'Never for French' item.
|
||||
notif._getAnonElt("neverForSite").click();
|
||||
ok(!getInfoBar(), "infobar hidden");
|
||||
|
||||
// Check this has been saved to the exceptions list.
|
||||
let sites = getDomainExceptions();
|
||||
is(sites.length, 1, "one site in the exception list");
|
||||
is(sites[0], "example.com", "correct site in the exception list");
|
||||
ok(!ui.shouldShowInfoBar(uri, "fr"),
|
||||
"the infobar wouldn't be shown anymore");
|
||||
|
||||
// Reopen the infobar.
|
||||
PopupNotifications.getNotification("translate").anchorElement.click();
|
||||
notif = getInfoBar();
|
||||
// Open the "options" drop down.
|
||||
yield openPopup(notif._getAnonElt("options"));
|
||||
ok(notif._getAnonElt("neverForSite").disabled,
|
||||
"The 'Never translate French' item is disabled");
|
||||
|
||||
// Cleanup.
|
||||
Services.perms.remove("example.com", "translate");
|
||||
notif.close();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "language exception list",
|
||||
run: function* checkLanguageExceptions() {
|
||||
// Put 2 languages in the pref before opening the window to check
|
||||
// the list is displayed on load.
|
||||
Services.prefs.setCharPref(kLanguagesPref, "fr,de");
|
||||
|
||||
// Open the translation exceptions dialog.
|
||||
let win = openDialog("chrome://browser/content/preferences/translation.xul",
|
||||
"Browser:TranslationExceptions",
|
||||
"", null);
|
||||
yield waitForWindowLoad(win);
|
||||
|
||||
// Check that the list of language exceptions is loaded.
|
||||
let getById = win.document.getElementById.bind(win.document);
|
||||
let tree = getById("languagesTree");
|
||||
let remove = getById("removeLanguage");
|
||||
let removeAll = getById("removeAllLanguages");
|
||||
is(tree.view.rowCount, 2, "The language exceptions list has 2 items");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
// Select the first item.
|
||||
tree.view.selection.select(0);
|
||||
ok(!remove.disabled, "The 'Remove Language' button is enabled");
|
||||
|
||||
// Click the 'Remove' button.
|
||||
remove.click();
|
||||
is(tree.view.rowCount, 1, "The language exceptions now contains 1 item");
|
||||
is(getLanguageExceptions().length, 1, "One exception in the pref");
|
||||
|
||||
// Clear the pref, and check the last item is removed from the display.
|
||||
Services.prefs.setCharPref(kLanguagesPref, "");
|
||||
is(tree.view.rowCount, 0, "The language exceptions list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
|
||||
// Add an item and check it appears.
|
||||
Services.prefs.setCharPref(kLanguagesPref, "fr");
|
||||
is(tree.view.rowCount, 1, "The language exceptions list has 1 item");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Languages' button is enabled");
|
||||
|
||||
// Click the 'Remove All' button.
|
||||
removeAll.click();
|
||||
is(tree.view.rowCount, 0, "The language exceptions list is empty");
|
||||
ok(remove.disabled, "The 'Remove Language' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Languages' button is disabled");
|
||||
is(Services.prefs.getCharPref(kLanguagesPref), "", "The pref is empty");
|
||||
|
||||
win.close();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "domains exception list",
|
||||
run: function* checkDomainExceptions() {
|
||||
// Put 2 exceptions before opening the window to check the list is
|
||||
// displayed on load.
|
||||
let perms = Services.perms;
|
||||
perms.add(makeURI("http://example.org"), "translate", perms.DENY_ACTION);
|
||||
perms.add(makeURI("http://example.com"), "translate", perms.DENY_ACTION);
|
||||
|
||||
// Open the translation exceptions dialog.
|
||||
let win = openDialog("chrome://browser/content/preferences/translation.xul",
|
||||
"Browser:TranslationExceptions",
|
||||
"", null);
|
||||
yield waitForWindowLoad(win);
|
||||
|
||||
// Check that the list of language exceptions is loaded.
|
||||
let getById = win.document.getElementById.bind(win.document);
|
||||
let tree = getById("sitesTree");
|
||||
let remove = getById("removeSite");
|
||||
let removeAll = getById("removeAllSites");
|
||||
is(tree.view.rowCount, 2, "The sites exceptions list has 2 items");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Sites' button is enabled");
|
||||
|
||||
// Select the first item.
|
||||
tree.view.selection.select(0);
|
||||
ok(!remove.disabled, "The 'Remove Site' button is enabled");
|
||||
|
||||
// Click the 'Remove' button.
|
||||
remove.click();
|
||||
is(tree.view.rowCount, 1, "The site exceptions now contains 1 item");
|
||||
is(getDomainExceptions().length, 1, "One exception in the permissions");
|
||||
|
||||
// Clear the permissions, and check the last item is removed from the display.
|
||||
perms.remove("example.org", "translate");
|
||||
perms.remove("example.com", "translate");
|
||||
is(tree.view.rowCount, 0, "The site exceptions list is empty");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Site' button is disabled");
|
||||
|
||||
// Add an item and check it appears.
|
||||
perms.add(makeURI("http://example.com"), "translate", perms.DENY_ACTION);
|
||||
is(tree.view.rowCount, 1, "The site exceptions list has 1 item");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(!removeAll.disabled, "The 'Remove All Sites' button is enabled");
|
||||
|
||||
// Click the 'Remove All' button.
|
||||
removeAll.click();
|
||||
is(tree.view.rowCount, 0, "The site exceptions list is empty");
|
||||
ok(remove.disabled, "The 'Remove Site' button is disabled");
|
||||
ok(removeAll.disabled, "The 'Remove All Sites' button is disabled");
|
||||
is(getDomainExceptions().length, 0, "No exceptions in the permissions");
|
||||
|
||||
win.close();
|
||||
}
|
||||
}
|
||||
|
||||
];
|
@ -73,8 +73,19 @@
|
||||
</xul:deck>
|
||||
<xul:spacer flex="1"/>
|
||||
|
||||
<xul:button type="menu" label="&translation.options.menu;">
|
||||
<xul:menupopup/>
|
||||
<xul:button type="menu" anonid="options" label="&translation.options.menu;">
|
||||
<xul:menupopup onpopupshowing="document.getBindingParent(this).optionsShowing();">
|
||||
<xul:menuitem anonid="neverForLanguage"
|
||||
oncommand="document.getBindingParent(this).neverForLanguage();"/>
|
||||
<xul:menuitem anonid="neverForSite"
|
||||
oncommand="document.getBindingParent(this).neverForSite();"
|
||||
label="&translation.options.neverForSite.label;"
|
||||
accesskey="&translation.options.neverForSite.accesskey;"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem oncommand="openPreferences('paneContent');"
|
||||
label="&translation.options.preferences.label;"
|
||||
accesskey="&translation.options.preferences.accesskey;"/>
|
||||
</xul:menupopup>
|
||||
</xul:button>
|
||||
|
||||
</xul:hbox>
|
||||
@ -181,6 +192,80 @@
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="optionsShowing">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Get the source language name.
|
||||
let lang;
|
||||
if (this.state == this.translation.STATE_OFFER)
|
||||
lang = this._getAnonElt("detectedLanguage").value;
|
||||
else
|
||||
lang = this._getAnonElt("fromLanguage").value;
|
||||
let langBundle =
|
||||
Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://global/locale/languageNames.properties");
|
||||
let langName = langBundle.GetStringFromName(lang);
|
||||
|
||||
// Set the label and accesskey on the menuitem.
|
||||
let bundle =
|
||||
Cc["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Ci.nsIStringBundleService)
|
||||
.createBundle("chrome://browser/locale/translation.properties");
|
||||
let item = this._getAnonElt("neverForLanguage");
|
||||
const kStrId = "translation.options.neverForLanguage";
|
||||
item.setAttribute("label",
|
||||
bundle.formatStringFromName(kStrId + ".label",
|
||||
[langName], 1));
|
||||
item.setAttribute("accesskey",
|
||||
bundle.GetStringFromName(kStrId + ".accesskey"));
|
||||
item.langCode = lang;
|
||||
|
||||
// We may need to disable the menuitems if they have already been used.
|
||||
// Check if translation is already disabled for this language:
|
||||
let neverForLangs =
|
||||
Services.prefs.getCharPref("browser.translation.neverForLanguages");
|
||||
item.disabled = neverForLangs.split(",").indexOf(lang) != -1;
|
||||
|
||||
// Check if translation is disabled for the domain:
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
item = this._getAnonElt("neverForSite");
|
||||
item.disabled =
|
||||
perms.testExactPermission(uri, "translate") == perms.DENY_ACTION;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="neverForLanguage">
|
||||
<body>
|
||||
<![CDATA[
|
||||
const kPrefName = "browser.translation.neverForLanguages";
|
||||
|
||||
let val = Services.prefs.getCharPref(kPrefName);
|
||||
if (val)
|
||||
val += ",";
|
||||
val += this._getAnonElt("neverForLanguage").langCode;
|
||||
|
||||
Services.prefs.setCharPref(kPrefName, val);
|
||||
|
||||
this.close();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="neverForSite">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let uri = this.translation.browser.currentURI;
|
||||
let perms = Services.perms;
|
||||
perms.add(uri, "translate", perms.DENY_ACTION);
|
||||
|
||||
this.close();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
@ -1,9 +1,10 @@
|
||||
const {Cc,Ci,Cu} = require("chrome");
|
||||
const {Cc,Ci,Cu,Cr} = require("chrome");
|
||||
const ObservableObject = require("devtools/shared/observable-object");
|
||||
const promise = require("devtools/toolkit/deprecated-sync-thenables");
|
||||
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
const {generateUUID} = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
|
||||
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
/**
|
||||
* IndexedDB wrapper that just save project objects
|
||||
@ -37,20 +38,45 @@ const IDB = {
|
||||
let db = IDB._db = request.result;
|
||||
let objectStore = db.transaction("projects").objectStore("projects");
|
||||
let projects = []
|
||||
let toRemove = [];
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
if (cursor.value.location) {
|
||||
|
||||
// We need to make sure this object has a `.location` property.
|
||||
// The UI depends on this property.
|
||||
// This should not be needed as we make sure to register valid
|
||||
// projects, but in the past (before bug 924568), we might have
|
||||
// registered invalid objects.
|
||||
projects.push(cursor.value);
|
||||
|
||||
|
||||
// We also want to make sure the location is valid.
|
||||
// If the location doesn't exist, we remove the project.
|
||||
|
||||
try {
|
||||
let file = FileUtils.File(cursor.value.location);
|
||||
if (file.exists()) {
|
||||
projects.push(cursor.value);
|
||||
} else {
|
||||
toRemove.push(cursor.value.location);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.result == Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH) {
|
||||
// A URL
|
||||
projects.push(cursor.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor.continue();
|
||||
} else {
|
||||
deferred.resolve(projects);
|
||||
let removePromises = [];
|
||||
for (let location of toRemove) {
|
||||
removePromises.push(IDB.remove(location));
|
||||
}
|
||||
promise.all(removePromises).then(() => {
|
||||
deferred.resolve(projects);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -83,6 +109,12 @@ const IDB = {
|
||||
update: function(project) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// Clone object to make it storable by IndexedDB.
|
||||
// Projects are proxified objects (for the template
|
||||
// mechanismn in the first version of the App Manager).
|
||||
// This will change in the future.
|
||||
project = JSON.parse(JSON.stringify(project));
|
||||
|
||||
var transaction = IDB._db.transaction(["projects"], "readwrite");
|
||||
var objectStore = transaction.objectStore("projects");
|
||||
var request = objectStore.put(project);
|
||||
@ -131,6 +163,14 @@ const AppProjects = {
|
||||
},
|
||||
|
||||
addPackaged: function(folder) {
|
||||
let file = FileUtils.File(folder.path);
|
||||
if (!file.exists()) {
|
||||
return promise.reject("path doesn't exist");
|
||||
}
|
||||
let existingProject = this.get(folder.path);
|
||||
if (existingProject) {
|
||||
return promise.reject("Already added");
|
||||
}
|
||||
let project = {
|
||||
type: "packaged",
|
||||
location: folder.path,
|
||||
@ -151,6 +191,10 @@ const AppProjects = {
|
||||
},
|
||||
|
||||
addHosted: function(manifestURL) {
|
||||
let existingProject = this.get(manifestURL);
|
||||
if (existingProject) {
|
||||
return promise.reject("Already added");
|
||||
}
|
||||
let project = {
|
||||
type: "hosted",
|
||||
location: manifestURL
|
||||
@ -163,11 +207,7 @@ const AppProjects = {
|
||||
},
|
||||
|
||||
update: function (project) {
|
||||
return IDB.update({
|
||||
type: project.type,
|
||||
location: project.location,
|
||||
packagedAppOrigin: project.packagedAppOrigin
|
||||
}).then(() => project);
|
||||
return IDB.update(project);
|
||||
},
|
||||
|
||||
remove: function(location) {
|
||||
|
@ -56,6 +56,7 @@ AppValidator.prototype._fetchManifest = function (manifestURL) {
|
||||
this.manifestURL = manifestURL;
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.overrideMimeType('text/plain');
|
||||
try {
|
||||
req.open("GET", manifestURL, true);
|
||||
} catch(e) {
|
||||
@ -155,6 +156,7 @@ AppValidator.prototype.validateLaunchPath = function (manifest) {
|
||||
}
|
||||
|
||||
let req = new XMLHttpRequest();
|
||||
req.overrideMimeType('text/plain');
|
||||
try {
|
||||
req.open("HEAD", indexURL, true);
|
||||
} catch(e) {
|
||||
|
@ -522,7 +522,17 @@ let gDevToolsBrowser = {
|
||||
* Open the App Manager
|
||||
*/
|
||||
openAppManager: function(gBrowser) {
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
|
||||
if (Services.prefs.getBoolPref("devtools.webide.enabled")) {
|
||||
let win = Services.wm.getMostRecentWindow("devtools:webide");
|
||||
if (win) {
|
||||
win.focus();
|
||||
} else {
|
||||
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
|
||||
ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
|
||||
}
|
||||
} else {
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:app-manager");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -29,6 +29,9 @@ DIRS += [
|
||||
'webconsole',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DEVTOOLS_WEBIDE']:
|
||||
DIRS += ['webide']
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'devtools-clhandler.js',
|
||||
'devtools-clhandler.manifest',
|
||||
|
2
browser/devtools/webconsole/test/browser.ini
Executable file → Normal file
@ -61,6 +61,7 @@ support-files =
|
||||
test-bug-859170-longstring-hang.html
|
||||
test-bug-869003-iframe.html
|
||||
test-bug-869003-top-window.html
|
||||
test-closure-optimized-out.html
|
||||
test-closures.html
|
||||
test-console-assert.html
|
||||
test-console-count.html
|
||||
@ -137,6 +138,7 @@ support-files =
|
||||
[browser_console_native_getters.js]
|
||||
[browser_console_navigation_marker.js]
|
||||
[browser_console_nsiconsolemessage.js]
|
||||
[browser_console_optimized_out_vars.js]
|
||||
[browser_console_private_browsing.js]
|
||||
[browser_console_variables_view.js]
|
||||
[browser_console_variables_view_while_debugging.js]
|
||||
|
@ -0,0 +1,82 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Check that inspecting an optimized out variable works when execution is
|
||||
// paused.
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-closure-optimized-out.html";
|
||||
let {tab} = yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole(tab);
|
||||
let { toolbox, panel, panelWin } = yield openDebugger();
|
||||
|
||||
yield waitForThreadEvents(panel, "resumed");
|
||||
ok(true, "Debugger resumed");
|
||||
|
||||
let sources = panelWin.DebuggerView.Sources;
|
||||
yield panel.addBreakpoint({ url: sources.values[0], line: 18 });
|
||||
yield ensureThreadClientState(panel, "resumed");
|
||||
|
||||
let fetchedScopes = panelWin.once(panelWin.EVENTS.FETCHED_SCOPES);
|
||||
let button = content.document.querySelector("button");
|
||||
ok(button, "Button element found");
|
||||
// Spin the event loop before causing the debuggee to pause, to allow
|
||||
// this function to return first.
|
||||
executeSoon(() => button.click());
|
||||
|
||||
let packet = yield fetchedScopes;
|
||||
ok(true, "Scopes were fetched");
|
||||
|
||||
yield toolbox.selectTool("webconsole");
|
||||
|
||||
// This is the meat of the test: evaluate the optimized out variable.
|
||||
hud.jsterm.execute("upvar");
|
||||
yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
text: "optimized out",
|
||||
category: CATEGORY_OUTPUT,
|
||||
}]
|
||||
});
|
||||
|
||||
finishTest();
|
||||
}).then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
}
|
||||
|
||||
// Debugger helper functions stolen from browser/devtools/debugger/test/head.js.
|
||||
|
||||
function ensureThreadClientState(aPanel, aState) {
|
||||
let thread = aPanel.panelWin.gThreadClient;
|
||||
let state = thread.state;
|
||||
|
||||
info("Thread is: '" + state + "'.");
|
||||
|
||||
if (state == aState) {
|
||||
return promise.resolve(null);
|
||||
} else {
|
||||
return waitForThreadEvents(aPanel, aState);
|
||||
}
|
||||
}
|
||||
|
||||
function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) {
|
||||
info("Waiting for thread event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s).");
|
||||
|
||||
let deferred = promise.defer();
|
||||
let thread = aPanel.panelWin.gThreadClient;
|
||||
let count = 0;
|
||||
|
||||
thread.addListener(aEventName, function onEvent(aEventName, ...aArgs) {
|
||||
info("Thread event '" + aEventName + "' fired: " + (++count) + " time(s).");
|
||||
|
||||
if (count == aEventRepeat) {
|
||||
ok(true, "Enough '" + aEventName + "' thread events have been fired.");
|
||||
thread.removeListener(aEventName, onEvent);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
0
browser/devtools/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js
Executable file → Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Debugger Test for Inspecting Optimized-Out Variables</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function onload() {
|
||||
window.removeEventListener("load", onload);
|
||||
function clickHandler(event) {
|
||||
button.removeEventListener("click", clickHandler, false);
|
||||
function outer(arg) {
|
||||
var upvar = arg * 2;
|
||||
// The inner lambda only aliases arg, so the frontend alias analysis decides
|
||||
// that upvar is not aliased and is not in the CallObject.
|
||||
return function () {
|
||||
arg += 2;
|
||||
};
|
||||
}
|
||||
|
||||
var f = outer(42);
|
||||
f();
|
||||
}
|
||||
var button = document.querySelector("button");
|
||||
button.addEventListener("click", clickHandler, false);
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<button>Click me!</button>
|
||||
</body>
|
||||
</html>
|
@ -104,7 +104,7 @@ function goUpdateConsoleCommands() {
|
||||
</toolbarbutton>
|
||||
<toolbarbutton label="&btnPageCSS.label;" type="menu-button"
|
||||
category="css" class="devtools-toolbarbutton webconsole-filter-button"
|
||||
tooltiptext="&btnPageCSS.tooltip;"
|
||||
tooltiptext="&btnPageCSS.tooltip2;"
|
||||
accesskey="&btnPageCSS.accesskey;"
|
||||
tabindex="4">
|
||||
<menupopup>
|
||||
|
10
browser/devtools/webide/Makefile.in
Normal file
@ -0,0 +1,10 @@
|
||||
# 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/.
|
||||
|
||||
PREF_JS_EXPORTS = $(srcdir)/webide-prefs.js
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
$(NSINSTALL) $(srcdir)/modules/*.js $(FINAL_TARGET)/modules/devtools
|
122
browser/devtools/webide/content/details.js
Normal file
@ -0,0 +1,122 @@
|
||||
/* 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/. */
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const {AppValidator} = require("devtools/app-manager/app-validator");
|
||||
const {AppManager} = require("devtools/app-manager");
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
document.addEventListener("visibilitychange", updateUI, true);
|
||||
AppManager.on("app-manager-update", onAppManagerUpdate);
|
||||
updateUI();
|
||||
}, true);
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
AppManager.off("app-manager-update", onAppManagerUpdate);
|
||||
}, true);
|
||||
|
||||
function onAppManagerUpdate(event, what, details) {
|
||||
if (what == "project" ||
|
||||
what == "project-validated") {
|
||||
updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
function resetUI() {
|
||||
document.querySelector("#toolbar").classList.add("hidden");
|
||||
document.querySelector("#type").classList.add("hidden");
|
||||
document.querySelector("#descriptionHeader").classList.add("hidden");
|
||||
document.querySelector("#manifestURLHeader").classList.add("hidden");
|
||||
document.querySelector("#locationHeader").classList.add("hidden");
|
||||
|
||||
document.body.className = "";
|
||||
document.querySelector("#icon").src = "";
|
||||
document.querySelector("h1").textContent = "";
|
||||
document.querySelector("#description").textContent = "";
|
||||
document.querySelector("#type").textContent = "";
|
||||
document.querySelector("#manifestURL").textContent = "";
|
||||
document.querySelector("#location").textContent = "";
|
||||
|
||||
document.querySelector("#errorslist").innerHTML = "";
|
||||
document.querySelector("#warningslist").innerHTML = "";
|
||||
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
resetUI();
|
||||
|
||||
let project = AppManager.selectedProject;
|
||||
if (!project) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.type != "runtimeApp") {
|
||||
document.querySelector("#toolbar").classList.remove("hidden");
|
||||
document.querySelector("#locationHeader").classList.remove("hidden");
|
||||
document.querySelector("#location").textContent = project.location;
|
||||
}
|
||||
|
||||
document.body.className = project.validationStatus;
|
||||
document.querySelector("#icon").src = project.icon;
|
||||
document.querySelector("h1").textContent = project.name;
|
||||
|
||||
let manifest;
|
||||
if (project.type == "runtimeApp") {
|
||||
manifest = project.app.manifest;
|
||||
} else {
|
||||
manifest = project.manifest;
|
||||
}
|
||||
|
||||
if (manifest) {
|
||||
if (manifest.description) {
|
||||
document.querySelector("#descriptionHeader").classList.remove("hidden");
|
||||
document.querySelector("#description").textContent = manifest.description;
|
||||
}
|
||||
|
||||
document.querySelector("#type").classList.remove("hidden");
|
||||
|
||||
if (project.type == "runtimeApp") {
|
||||
document.querySelector("#type").textContent = manifest.type || "web";
|
||||
} else {
|
||||
document.querySelector("#type").textContent = project.type + " " + (manifest.type || "web");
|
||||
}
|
||||
|
||||
if (project.type == "packaged") {
|
||||
let manifest = AppManager.getProjectManifestURL(project);
|
||||
if (manifest) {
|
||||
document.querySelector("#manifestURLHeader").classList.remove("hidden");
|
||||
document.querySelector("#manifestURL").textContent = manifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let errorsNode = document.querySelector("#errorslist");
|
||||
let warningsNode = document.querySelector("#warningslist");
|
||||
|
||||
if (project.errors) {
|
||||
for (let e of project.errors) {
|
||||
let li = document.createElement("li");
|
||||
li.textContent = e;
|
||||
errorsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
if (project.warnings) {
|
||||
for (let w of project.warnings) {
|
||||
let li = document.createElement("li");
|
||||
li.textContent = w;
|
||||
warningsNode.appendChild(li);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeProject() {
|
||||
AppManager.removeSelectedProject();
|
||||
}
|
52
browser/devtools/webide/content/details.xhtml
Normal file
@ -0,0 +1,52 @@
|
||||
<?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/. -->
|
||||
|
||||
<!DOCTYPE html [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf8"/>
|
||||
<link rel="stylesheet" href="chrome://webide/skin/details.css" type="text/css"/>
|
||||
<script type="application/javascript;version=1.8" src="chrome://webide/content/details.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="toolbar">
|
||||
<button onclick="removeProject()">&details_removeProject_button;</button>
|
||||
<p id="validation_status">
|
||||
<span class="valid">&details_valid_header;</span>
|
||||
<span class="warning">&details_warning_header;</span>
|
||||
<span class="error">&details_error_header;</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<img id="icon"></img>
|
||||
<div>
|
||||
<h1></h1>
|
||||
<p id="type"></p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<h3 id="descriptionHeader">&details_description;</h3>
|
||||
<p id="description"></p>
|
||||
|
||||
<h3 id="locationHeader">&details_location;</h3>
|
||||
<p id="location"></p>
|
||||
|
||||
<h3 id="manifestURLHeader">&details_manifestURL;</h3>
|
||||
<p id="manifestURL"></p>
|
||||
</main>
|
||||
|
||||
<ul class="validation_messages" id="errorslist"></ul>
|
||||
<ul class="validation_messages" id="warningslist"></ul>
|
||||
|
||||
</body>
|
||||
</html>
|
12
browser/devtools/webide/content/jar.mn
Normal file
@ -0,0 +1,12 @@
|
||||
# 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/.
|
||||
|
||||
webide.jar:
|
||||
% content webide %content/
|
||||
content/webide.xul (webide.xul)
|
||||
content/webide.js (webide.js)
|
||||
content/newapp.xul (newapp.xul)
|
||||
content/newapp.js (newapp.js)
|
||||
content/details.xhtml (details.xhtml)
|
||||
content/details.js (details.js)
|
7
browser/devtools/webide/content/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
161
browser/devtools/webide/content/newapp.js
Normal file
@ -0,0 +1,161 @@
|
||||
/* 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/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ZipUtils", "resource://gre/modules/ZipUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
|
||||
|
||||
const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
||||
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const APP_CREATOR_LIST = "devtools.webide.templatesURL";
|
||||
const {AppManager} = require("devtools/app-manager");
|
||||
|
||||
let gTemplateList = null;
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
let projectNameNode = document.querySelector("#project-name");
|
||||
projectNameNode.addEventListener("input", canValidate, true);
|
||||
getJSON();
|
||||
}, true);
|
||||
|
||||
function getJSON() {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType('text/plain');
|
||||
xhr.onload = function() {
|
||||
let list;
|
||||
try {
|
||||
list = JSON.parse(this.responseText);
|
||||
if (!Array.isArray(list)) {
|
||||
throw new Error("JSON response not an array");
|
||||
}
|
||||
if (list.length == 0) {
|
||||
throw new Error("JSON response is an empty array");
|
||||
}
|
||||
} catch(e) {
|
||||
return failAndBail("Invalid response from server");
|
||||
}
|
||||
gTemplateList = list;
|
||||
let templatelistNode = document.querySelector("#templatelist");
|
||||
templatelistNode.innerHTML = "";
|
||||
for (let template of list) {
|
||||
let richlistitemNode = document.createElement("richlistitem");
|
||||
let imageNode = document.createElement("image");
|
||||
imageNode.setAttribute("src", template.icon);
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.setAttribute("value", template.name);
|
||||
let descriptionNode = document.createElement("description");
|
||||
descriptionNode.textContent = template.description;
|
||||
let vboxNode = document.createElement("vbox");
|
||||
vboxNode.setAttribute("flex", "1");
|
||||
richlistitemNode.appendChild(imageNode);
|
||||
vboxNode.appendChild(labelNode);
|
||||
vboxNode.appendChild(descriptionNode);
|
||||
richlistitemNode.appendChild(vboxNode);
|
||||
templatelistNode.appendChild(richlistitemNode);
|
||||
}
|
||||
templatelistNode.selectedIndex = 0;
|
||||
};
|
||||
xhr.onerror = function() {
|
||||
failAndBail("Can't download app templates");
|
||||
};
|
||||
let url = Services.prefs.getCharPref(APP_CREATOR_LIST);
|
||||
xhr.open("get", url);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function failAndBail(msg) {
|
||||
let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
|
||||
promptService.alert(window, "error", msg);
|
||||
window.close();
|
||||
}
|
||||
|
||||
function canValidate() {
|
||||
let projectNameNode = document.querySelector("#project-name");
|
||||
let dialogNode = document.querySelector("dialog");
|
||||
if (projectNameNode.value.length > 0) {
|
||||
dialogNode.removeAttribute("buttondisabledaccept");
|
||||
} else {
|
||||
dialogNode.setAttribute("buttondisabledaccept", "true");
|
||||
}
|
||||
}
|
||||
|
||||
function doOK() {
|
||||
let projectName = document.querySelector("#project-name").value;
|
||||
|
||||
if (!projectName) {
|
||||
AppManager.console.error("No project name");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!gTemplateList) {
|
||||
AppManager.console.error("No template index");
|
||||
return false;
|
||||
}
|
||||
|
||||
let templatelistNode = document.querySelector("#templatelist");
|
||||
if (templatelistNode.selectedIndex < 0) {
|
||||
AppManager.console.error("No template selected");
|
||||
return false;
|
||||
}
|
||||
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, "Select directory where to create app directory", Ci.nsIFilePicker.modeGetFolder);
|
||||
let res = fp.show();
|
||||
if (res == Ci.nsIFilePicker.returnCancel) {
|
||||
AppManager.console.error("No directory selected");
|
||||
return false;
|
||||
}
|
||||
let folder = fp.file;
|
||||
|
||||
// Create subfolder with fs-friendly name of project
|
||||
let subfolder = projectName.replace(/\W/g, '').toLowerCase();
|
||||
folder.append(subfolder);
|
||||
|
||||
try {
|
||||
folder.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
} catch(e) {
|
||||
AppManager.console.error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Download boilerplate zip
|
||||
let template = gTemplateList[templatelistNode.selectedIndex];
|
||||
let source = template.file;
|
||||
let target = folder.clone();
|
||||
target.append(subfolder + ".zip");
|
||||
|
||||
let bail = (e) => {
|
||||
AppManager.console.error(e);
|
||||
window.close();
|
||||
};
|
||||
|
||||
Downloads.fetch(source, target).then(() => {
|
||||
ZipUtils.extractFiles(target, folder);
|
||||
target.remove(false);
|
||||
AppProjects.addPackaged(folder).then((project) => {
|
||||
window.arguments[0].location = project.location;
|
||||
AppManager.validateProject(project).then(() => {
|
||||
if (project.manifest) {
|
||||
project.manifest.name = projectName;
|
||||
AppManager.writeManifest(project).then(() => {
|
||||
AppManager.validateProject(project).then(
|
||||
() => {window.close()}, bail)
|
||||
}, bail)
|
||||
} else {
|
||||
bail("Manifest not found");
|
||||
}
|
||||
}, bail)
|
||||
}, bail)
|
||||
}, bail);
|
||||
|
||||
return false;
|
||||
}
|
33
browser/devtools/webide/content/newapp.xul
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://webide/skin/newapp.css"?>
|
||||
|
||||
<dialog id="webide:newapp" title="&newAppWindowTitle;"
|
||||
width="600" height="400"
|
||||
buttons="accept,cancel"
|
||||
ondialogaccept="return doOK();"
|
||||
buttondisabledaccept="true"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="newapp.js"></script>
|
||||
<label class="header-name" value="&newAppHeader;"/>
|
||||
|
||||
<richlistbox id="templatelist" flex="1">
|
||||
<description>&newAppLoadingTemplate;</description>
|
||||
</richlistbox>
|
||||
<vbox>
|
||||
<label class="header-name" control="project-name" value="&newAppProjectName;"/>
|
||||
<textbox id="project-name"/>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
735
browser/devtools/webide/content/webide.js
Normal file
@ -0,0 +1,735 @@
|
||||
/* 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/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
|
||||
const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const {require} = devtools;
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const {Connection} = require("devtools/client/connection-manager");
|
||||
const {AppManager} = require("devtools/app-manager");
|
||||
|
||||
const Strings = Services.strings.createBundle("chrome://webide/locale/webide.properties");
|
||||
|
||||
const HTML = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
window.addEventListener("load", function onLoad() {
|
||||
window.removeEventListener("load", onLoad);
|
||||
UI.init();
|
||||
});
|
||||
|
||||
window.addEventListener("unload", function onUnload() {
|
||||
window.removeEventListener("unload", onUnload);
|
||||
UI.uninit();
|
||||
});
|
||||
|
||||
let UI = {
|
||||
init: function() {
|
||||
AppManager.init();
|
||||
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
window.addEventListener("message", this.onMessage);
|
||||
|
||||
this.appManagerUpdate = this.appManagerUpdate.bind(this);
|
||||
AppManager.on("app-manager-update", this.appManagerUpdate);
|
||||
|
||||
this.logNode = document.querySelector("#logs");
|
||||
|
||||
this.updateCommands();
|
||||
this.updateRuntimeList();
|
||||
|
||||
this.onfocus = this.onfocus.bind(this);
|
||||
window.addEventListener("focus", this.onfocus, true);
|
||||
|
||||
try {
|
||||
let lastProjectLocation = Services.prefs.getCharPref("devtools.webide.lastprojectlocation");
|
||||
AppProjects.load().then(() => {
|
||||
let lastProject = AppProjects.get(lastProjectLocation);
|
||||
if (lastProject) {
|
||||
AppManager.selectedProject = lastProject;
|
||||
} else {
|
||||
AppManager.selectedProject = null;
|
||||
}
|
||||
});
|
||||
} catch(e) {
|
||||
AppManager.selectedProject = null;
|
||||
}
|
||||
|
||||
document.querySelector("#toggle-logs").addEventListener("click", function() {
|
||||
document.querySelector("#logs").classList.toggle("expand");
|
||||
UI.logNode.scrollTop = UI.logNode.scrollTopMax;
|
||||
});
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
window.removeEventListener("focus", this.onfocus, true);
|
||||
AppManager.off("app-manager-update", this.appManagerUpdate);
|
||||
AppManager.uninit();
|
||||
window.removeEventListener("message", this.onMessage);
|
||||
},
|
||||
|
||||
onfocus: function() {
|
||||
// Because we can't track the activity in the folder project,
|
||||
// we need to validate the project regularly. Let's assume that
|
||||
// if a modification happened, it happened when the window was
|
||||
// not focused.
|
||||
if (AppManager.selectedProject &&
|
||||
AppManager.selectedProject.type != "runtimeApp") {
|
||||
AppManager.validateProject(AppManager.selectedProject);
|
||||
}
|
||||
},
|
||||
|
||||
appManagerUpdate: function(event, what, details) {
|
||||
// Got a message from app-manager.js
|
||||
switch (what) {
|
||||
case "console":
|
||||
if (details.level == "log") this.console.log(details.message);
|
||||
if (details.level == "warning") this.console.warning(details.message);
|
||||
if (details.level == "error") this.console.error(details.message);
|
||||
if (details.level == "success") this.console.success(details.message);
|
||||
break;
|
||||
case "runtimelist":
|
||||
this.updateRuntimeList();
|
||||
break;
|
||||
case "connection":
|
||||
this.updateRuntimeButton();
|
||||
this.updateCommands();
|
||||
break;
|
||||
case "project":
|
||||
this.updateTitle();
|
||||
this.closeToolbox();
|
||||
this.updateCommands();
|
||||
this.updateProjectButton();
|
||||
this.openProject();
|
||||
break;
|
||||
case "project-is-not-running":
|
||||
case "project-is-running":
|
||||
this.updateCommands();
|
||||
break;
|
||||
case "runtime":
|
||||
this.updateRuntimeButton();
|
||||
break;
|
||||
case "project-validated":
|
||||
this.updateTitle();
|
||||
this.updateCommands();
|
||||
this.updateProjectButton();
|
||||
break;
|
||||
};
|
||||
},
|
||||
|
||||
openInBrowser: function(url) {
|
||||
// Open a URL in a Firefox window
|
||||
let browserWin = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWin) {
|
||||
let gBrowser = browserWin.gBrowser;
|
||||
gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
browserWin.focus();
|
||||
} else {
|
||||
window.open(url);
|
||||
}
|
||||
},
|
||||
|
||||
updateTitle: function() {
|
||||
let project = AppManager.selectedProject;
|
||||
if (project) {
|
||||
window.document.title = Strings.formatStringFromName("title_app", [project.name], 1);
|
||||
} else {
|
||||
window.document.title = Strings.GetStringFromName("title_noApp");
|
||||
}
|
||||
},
|
||||
|
||||
hidePanels: function() {
|
||||
let panels = document.querySelectorAll("panel");
|
||||
for (let p of panels) {
|
||||
p.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
busy: function() {
|
||||
document.querySelector("window").classList.add("busy")
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
unbusy: function() {
|
||||
document.querySelector("window").classList.remove("busy")
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
busyUntil: function(promise, operationDescription) {
|
||||
// Freeze the UI until the promise is resolved. A 30s timeout
|
||||
// will unfreeze the UI, just in case the promise never gets
|
||||
// resolved.
|
||||
let timeout = setTimeout(() => {
|
||||
this.unbusy();
|
||||
this.console.error("Operation timeout: " + operationDescription);
|
||||
}, 30000);
|
||||
this.busy();
|
||||
promise.then(() => {
|
||||
clearTimeout(timeout);
|
||||
this.unbusy();
|
||||
}, () => {
|
||||
clearTimeout(timeout);
|
||||
this.unbusy();
|
||||
});
|
||||
},
|
||||
|
||||
/********** RUNTIME **********/
|
||||
|
||||
updateRuntimeList: function() {
|
||||
let USBListNode = document.querySelector("#runtime-panel-usbruntime");
|
||||
let simulatorListNode = document.querySelector("#runtime-panel-simulators");
|
||||
while (USBListNode.hasChildNodes()) {
|
||||
USBListNode.firstChild.remove();
|
||||
}
|
||||
|
||||
this.console.log("Found " + AppManager.runtimeList.usb.length + " USB devices.");
|
||||
this.console.log("Found " + AppManager.runtimeList.simulators.length + " simulators.");
|
||||
for (let runtime of AppManager.runtimeList.usb) {
|
||||
let panelItemNode = document.createElement("toolbarbutton");
|
||||
panelItemNode.className = "panel-item runtime-panel-item-usbruntime";
|
||||
panelItemNode.setAttribute("label", runtime.getName());
|
||||
USBListNode.appendChild(panelItemNode);
|
||||
let r = runtime;
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
this.hidePanels();
|
||||
this.connectToRuntime(r);
|
||||
}, true);
|
||||
}
|
||||
|
||||
while (simulatorListNode.hasChildNodes()) {
|
||||
simulatorListNode.firstChild.remove();
|
||||
}
|
||||
for (let runtime of AppManager.runtimeList.simulators) {
|
||||
let panelItemNode = document.createElement("toolbarbutton");
|
||||
panelItemNode.className = "panel-item runtime-panel-item-simulator";
|
||||
panelItemNode.setAttribute("label", runtime.getName());
|
||||
simulatorListNode.appendChild(panelItemNode);
|
||||
let r = runtime;
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
this.hidePanels();
|
||||
this.connectToRuntime(r);
|
||||
}, true);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
connectToRuntime: function(runtime) {
|
||||
let name = runtime.getName();
|
||||
let promise = AppManager.connectToRuntime(runtime);
|
||||
this.busyUntil(promise, "connecting to runtime");
|
||||
promise.then(
|
||||
() => {this.console.success("Connected to " + name)},
|
||||
() => {this.console.error("Can't connect to " + name)});
|
||||
},
|
||||
|
||||
updateRuntimeButton: function() {
|
||||
let buttonNode = document.querySelector("#runtime-panel-button");
|
||||
let labelNode = buttonNode.querySelector(".panel-button-label");
|
||||
if (!AppManager.selectedRuntime) {
|
||||
labelNode.setAttribute("value", Strings.GetStringFromName("runtimeButton_label"));
|
||||
} else {
|
||||
let name = AppManager.selectedRuntime.getName();
|
||||
labelNode.setAttribute("value", name);
|
||||
}
|
||||
},
|
||||
|
||||
/********** PROJECTS **********/
|
||||
|
||||
// Panel & button
|
||||
|
||||
updateProjectButton: function() {
|
||||
let buttonNode = document.querySelector("#project-panel-button");
|
||||
let labelNode = buttonNode.querySelector(".panel-button-label");
|
||||
let imageNode = buttonNode.querySelector(".panel-button-image");
|
||||
|
||||
let project = AppManager.selectedProject;
|
||||
|
||||
if (!project) {
|
||||
buttonNode.classList.add("no-project");
|
||||
labelNode.setAttribute("value", Strings.GetStringFromName("projectButton_label"));
|
||||
imageNode.removeAttribute("src");
|
||||
} else {
|
||||
buttonNode.classList.remove("no-project");
|
||||
labelNode.setAttribute("value", project.name);
|
||||
imageNode.setAttribute("src", project.icon);
|
||||
}
|
||||
},
|
||||
|
||||
// details.xhtml
|
||||
|
||||
openProject: function() {
|
||||
let details = document.querySelector("#details");
|
||||
let project = AppManager.selectedProject;
|
||||
|
||||
if (!project) {
|
||||
details.setAttribute("hidden", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
if (project.location) {
|
||||
Services.prefs.setCharPref("devtools.webide.lastprojectlocation", project.location);
|
||||
}
|
||||
|
||||
details.removeAttribute("hidden");
|
||||
},
|
||||
|
||||
/********** COMMANDS **********/
|
||||
|
||||
updateCommands: function() {
|
||||
|
||||
if (document.querySelector("window").classList.contains("busy")) {
|
||||
document.querySelector("#cmd_newApp").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_importPackagedApp").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_importHostedApp").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_showProjectPanel").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_showRuntimePanel").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_removeProject").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_disconnectRuntime").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_showPermissionsTable").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_takeScreenshot").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_showRuntimeDetails").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_play").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_stop").setAttribute("disabled", "true");
|
||||
document.querySelector("#cmd_toggleToolbox").setAttribute("disabled", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
document.querySelector("#cmd_newApp").removeAttribute("disabled");
|
||||
document.querySelector("#cmd_importPackagedApp").removeAttribute("disabled");
|
||||
document.querySelector("#cmd_importHostedApp").removeAttribute("disabled");
|
||||
document.querySelector("#cmd_showProjectPanel").removeAttribute("disabled");
|
||||
document.querySelector("#cmd_showRuntimePanel").removeAttribute("disabled");
|
||||
|
||||
document.querySelector("#runtime-panel-button").removeAttribute("active");
|
||||
|
||||
// Action commands
|
||||
let playCmd = document.querySelector("#cmd_play");
|
||||
let stopCmd = document.querySelector("#cmd_stop");
|
||||
let debugCmd = document.querySelector("#cmd_toggleToolbox");
|
||||
|
||||
if (!AppManager.selectedProject || AppManager.connection.status != Connection.Status.CONNECTED) {
|
||||
playCmd.setAttribute("disabled", "true");
|
||||
stopCmd.setAttribute("disabled", "true");
|
||||
debugCmd.setAttribute("disabled", "true");
|
||||
} else {
|
||||
let isProjectRunning = AppManager.isProjectRunning();
|
||||
if (isProjectRunning) {
|
||||
stopCmd.removeAttribute("disabled");
|
||||
debugCmd.removeAttribute("disabled");
|
||||
} else {
|
||||
stopCmd.setAttribute("disabled", "true");
|
||||
debugCmd.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
// If connected and a project is selected
|
||||
if (AppManager.selectedProject.type == "runtimeApp") {
|
||||
if (isProjectRunning) {
|
||||
playCmd.setAttribute("disabled", "true");
|
||||
} else {
|
||||
playCmd.removeAttribute("disabled");
|
||||
}
|
||||
} else {
|
||||
if (AppManager.selectedProject.errorsCount == 0) {
|
||||
playCmd.removeAttribute("disabled");
|
||||
} else {
|
||||
playCmd.setAttribute("disabled", "true");
|
||||
}
|
||||
}
|
||||
document.querySelector("#runtime-panel-button").setAttribute("active", "true");
|
||||
}
|
||||
|
||||
// Remove command
|
||||
let removeCmdNode = document.querySelector("#cmd_removeProject");
|
||||
if (AppManager.selectedProject) {
|
||||
removeCmdNode.removeAttribute("disabled");
|
||||
} else {
|
||||
removeCmdNode.setAttribute("disabled", "true");
|
||||
}
|
||||
|
||||
// Runtime commands
|
||||
let screenshotCmd = document.querySelector("#cmd_takeScreenshot");
|
||||
let permissionsCmd = document.querySelector("#cmd_showPermissionsTable");
|
||||
let detailsCmd = document.querySelector("#cmd_showRuntimeDetails");
|
||||
let disconnectCmd = document.querySelector("#cmd_disconnectRuntime");
|
||||
|
||||
let box = document.querySelector("#runtime-actions");
|
||||
|
||||
if (AppManager.connection.status == Connection.Status.CONNECTED) {
|
||||
screenshotCmd.removeAttribute("disabled");
|
||||
permissionsCmd.removeAttribute("disabled");
|
||||
disconnectCmd.removeAttribute("disabled");
|
||||
detailsCmd.removeAttribute("disabled");
|
||||
box.removeAttribute("hidden");
|
||||
} else {
|
||||
screenshotCmd.setAttribute("disabled", "true");
|
||||
permissionsCmd.setAttribute("disabled", "true");
|
||||
disconnectCmd.setAttribute("disabled", "true");
|
||||
detailsCmd.setAttribute("disabled", "true");
|
||||
box.setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/********** TOOLBOX **********/
|
||||
|
||||
onMessage: function(event) {
|
||||
// The custom toolbox sends a message to its parent
|
||||
// window.
|
||||
try {
|
||||
let json = JSON.parse(event.data);
|
||||
switch (json.name) {
|
||||
case "toolbox-close":
|
||||
this.closeToolboxUI();
|
||||
break;
|
||||
}
|
||||
} catch(e) { Cu.reportError(e); }
|
||||
},
|
||||
|
||||
closeToolbox: function() {
|
||||
if (this.toolboxPromise) {
|
||||
this.toolboxPromise.then(toolbox => {
|
||||
toolbox.destroy();
|
||||
document.querySelector("#action-button-debug").removeAttribute("active");
|
||||
this.toolboxPromise = null;
|
||||
}, this.console.error);
|
||||
}
|
||||
},
|
||||
|
||||
showToolbox: function(target) {
|
||||
if (this.toolboxIframe) {
|
||||
return;
|
||||
}
|
||||
|
||||
let splitter = document.querySelector(".devtools-horizontal-splitter");
|
||||
splitter.removeAttribute("hidden");
|
||||
|
||||
let iframe = document.createElement("iframe");
|
||||
document.querySelector("window").insertBefore(iframe, splitter.nextSibling);
|
||||
let host = devtools.Toolbox.HostType.CUSTOM;
|
||||
let options = { customIframe: iframe };
|
||||
this.toolboxIframe = iframe;
|
||||
|
||||
let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");
|
||||
iframe.height = height;
|
||||
|
||||
document.querySelector("#action-button-debug").setAttribute("active", "true");
|
||||
|
||||
return gDevTools.showToolbox(target, null, host, options);
|
||||
},
|
||||
|
||||
closeToolboxUI: function() {
|
||||
let body = document.querySelector("#body");
|
||||
body.removeAttribute("hidden");
|
||||
|
||||
Services.prefs.setIntPref("devtools.toolbox.footer.height", this.toolboxIframe.height);
|
||||
|
||||
// We have to destroy the iframe, otherwise, the keybindings of webide don't work
|
||||
// properly anymore.
|
||||
this.toolboxIframe.remove();
|
||||
this.toolboxIframe = null;
|
||||
|
||||
let splitter = document.querySelector(".devtools-horizontal-splitter");
|
||||
splitter.setAttribute("hidden", "true");
|
||||
},
|
||||
|
||||
console: {
|
||||
_log: function(msg, classname) {
|
||||
let li = document.createElementNS(HTML, "p");
|
||||
li.textContent = msg;
|
||||
li.className = classname;
|
||||
UI.logNode.appendChild(li);
|
||||
UI.logNode.scrollTop = UI.logNode.scrollTopMax;
|
||||
},
|
||||
log: function(msg) {
|
||||
UI.console._log(msg, "log");
|
||||
console.log(msg);
|
||||
},
|
||||
warning: function(msg) {
|
||||
UI.console._log(msg, "warning");
|
||||
console.warning(msg);
|
||||
},
|
||||
error: function(msg) {
|
||||
UI.console._log(msg, "error");
|
||||
console.error(msg);
|
||||
},
|
||||
success: function(msg) {
|
||||
UI.console._log(msg, "success");
|
||||
console.log(msg);
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
let Cmds = {
|
||||
quit: function() {
|
||||
window.close();
|
||||
},
|
||||
|
||||
newApp: function() {
|
||||
UI.hidePanels();
|
||||
let ret = {location:null};
|
||||
window.openDialog("chrome://webide/content/newapp.xul", "newapp", "chrome,modal", ret);
|
||||
if (!ret.location)
|
||||
return;
|
||||
let project = AppProjects.get(ret.location);
|
||||
UI.busyUntil(AppManager.validateProject(project).then(() => {
|
||||
UI.console.success("New project created at " + ret.location);
|
||||
AppManager.selectedProject = project;
|
||||
}, (e) => UI.console.error("Error while create new app: " + e)), "creating new app");;
|
||||
},
|
||||
|
||||
importPackagedApp: function() {
|
||||
UI.hidePanels();
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, Strings.GetStringFromName("importPackagedApp_title"), Ci.nsIFilePicker.modeGetFolder);
|
||||
let res = fp.show();
|
||||
if (res == Ci.nsIFilePicker.returnCancel)
|
||||
return;
|
||||
UI.busyUntil(AppProjects.addPackaged(fp.file)
|
||||
.then(project => AppManager.validateProject(project))
|
||||
.then(project => AppManager.selectedProject = project)
|
||||
.then(( ) => { UI.console.log("New project successfuly added") },
|
||||
(e) => { UI.console.error("Error while importing project: " + e) }),
|
||||
"importing packaged app");
|
||||
},
|
||||
|
||||
|
||||
importHostedApp: function() {
|
||||
UI.hidePanels();
|
||||
let promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
|
||||
let ret = {value:null};
|
||||
promptService.prompt(window,
|
||||
Strings.GetStringFromName("importHostedApp_title"),
|
||||
Strings.GetStringFromName("importHostedApp_header"),
|
||||
ret, null, {});
|
||||
let url = ret.value;
|
||||
if (!url)
|
||||
return;
|
||||
UI.busyUntil(AppProjects.addHosted(url)
|
||||
.then(project => AppManager.validateProject(project))
|
||||
.then(project => AppManager.selectedProject = project)
|
||||
.then(( ) => { UI.console.log("New project successfuly added") },
|
||||
(e) => { UI.console.error("Error while importing project: " + e) }),
|
||||
"importing hosted app");
|
||||
},
|
||||
|
||||
|
||||
showProjectPanel: function() {
|
||||
let panelNode = document.querySelector("#project-panel");
|
||||
let panelVboxNode = document.querySelector("#project-panel > vbox");
|
||||
let anchorNode = document.querySelector("#project-panel-button > .panel-button-anchor");
|
||||
let projectsNode = document.querySelector("#project-panel-projects");
|
||||
|
||||
while (projectsNode.hasChildNodes()) {
|
||||
projectsNode.firstChild.remove();
|
||||
}
|
||||
|
||||
AppProjects.load().then(() => {
|
||||
let projects = AppProjects.store.object.projects;
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
let project = projects[i];
|
||||
let panelItemNode = document.createElement("toolbarbutton");
|
||||
panelItemNode.className = "panel-item";
|
||||
projectsNode.appendChild(panelItemNode);
|
||||
panelItemNode.setAttribute("label", project.name || AppManager.DEFAULT_PROJECT_NAME);
|
||||
panelItemNode.setAttribute("image", project.icon || AppManager.DEFAULT_PROJECT_ICON);
|
||||
if (!project.validationStatus) {
|
||||
// The result of the validation process (storing names, icons, …) has never been
|
||||
// stored in the IndexedDB database. This happens when the project has been created
|
||||
// from the old app manager. We need to run the validation again and update the name
|
||||
// and icon of the app
|
||||
AppManager.validateProject(project).then(() => {
|
||||
panelItemNode.setAttribute("label", project.name);
|
||||
panelItemNode.setAttribute("image", project.icon);
|
||||
});
|
||||
}
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
UI.hidePanels();
|
||||
AppManager.selectedProject = project;
|
||||
}, true);
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
// Open the popup only when the projects are added.
|
||||
// Not doing it in the next tick can cause mis-calculations
|
||||
// of the size of the panel.
|
||||
panelNode.openPopup(anchorNode);
|
||||
panelVboxNode.scrollTop = 0;
|
||||
}, 0);
|
||||
}, UI.console.error);
|
||||
|
||||
|
||||
let runtimeappsHeaderNode = document.querySelector("#panel-header-runtimeapps");
|
||||
if (AppManager.connection.status == Connection.Status.CONNECTED) {
|
||||
runtimeappsHeaderNode.removeAttribute("hidden");
|
||||
} else {
|
||||
runtimeappsHeaderNode.setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
let runtimeAppsNode = document.querySelector("#project-panel-runtimeapps");
|
||||
while (runtimeAppsNode.hasChildNodes()) {
|
||||
runtimeAppsNode.firstChild.remove();
|
||||
}
|
||||
|
||||
UI.console.log("Found " + AppManager.webAppsStore.object.all.length + " apps");
|
||||
|
||||
for (let i = 0; i < AppManager.webAppsStore.object.all.length; i++) {
|
||||
let app = AppManager.webAppsStore.object.all[i];
|
||||
let panelItemNode = document.createElement("toolbarbutton");
|
||||
panelItemNode.className = "panel-item";
|
||||
panelItemNode.setAttribute("label", app.name);
|
||||
panelItemNode.setAttribute("image", app.iconURL);
|
||||
runtimeAppsNode.appendChild(panelItemNode);
|
||||
panelItemNode.addEventListener("click", () => {
|
||||
UI.hidePanels();
|
||||
AppManager.selectedProject = {
|
||||
type: "runtimeApp",
|
||||
app: app,
|
||||
icon: app.iconURL,
|
||||
name: app.name
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
},
|
||||
|
||||
showRuntimePanel: function() {
|
||||
let panel = document.querySelector("#runtime-panel");
|
||||
let anchor = document.querySelector("#runtime-panel-button > .panel-button-anchor");
|
||||
panel.openPopup(anchor);
|
||||
},
|
||||
|
||||
disconnectRuntime: function() {
|
||||
UI.busyUntil(AppManager.disconnectRuntime());
|
||||
},
|
||||
|
||||
takeScreenshot: function() {
|
||||
UI.hidePanels();
|
||||
UI.busyUntil(AppManager.deviceFront.screenshotToDataURL().then(longstr => {
|
||||
return longstr.string().then(dataURL => {
|
||||
longstr.release().then(null, UI.console.error);
|
||||
UI.openInBrowser(dataURL);
|
||||
});
|
||||
}));
|
||||
},
|
||||
|
||||
showPermissionsTable: function() {
|
||||
UI.hidePanels();
|
||||
UI.busyUntil(AppManager.deviceFront.getRawPermissionsTable().then(json => {
|
||||
let styleContent = "";
|
||||
styleContent += "body {background:white; font-family: monospace}";
|
||||
styleContent += "table {border-collapse: collapse}";
|
||||
styleContent += "th, td {padding: 5px; border: 1px solid #EEE}";
|
||||
styleContent += "th {min-width: 130px}";
|
||||
styleContent += "td {text-align: center}";
|
||||
styleContent += "th:first-of-type, td:first-of-type {text-align:left}";
|
||||
styleContent += ".permallow {color:rgb(152, 207, 57)}";
|
||||
styleContent += ".permprompt {color:rgb(0,158,237)}";
|
||||
styleContent += ".permdeny {color:rgb(204,73,8)}";
|
||||
let style = document.createElementNS(HTML, "style");
|
||||
style.textContent = styleContent;
|
||||
let table = document.createElementNS(HTML, "table");
|
||||
table.innerHTML = "<tr><th>Name</th><th>type:web</th><th>type:privileged</th><th>type:certified</th></tr>";
|
||||
let permissionsTable = json.rawPermissionsTable;
|
||||
for (let name in permissionsTable) {
|
||||
let tr = document.createElementNS(HTML, "tr");
|
||||
let td = document.createElementNS(HTML, "td");
|
||||
td.textContent = name;
|
||||
tr.appendChild(td);
|
||||
for (let type of ["app","privileged","certified"]) {
|
||||
let td = document.createElementNS(HTML, "td");
|
||||
if (permissionsTable[name][type] == json.ALLOW_ACTION) {
|
||||
td.textContent = "✓";
|
||||
td.className = "permallow";
|
||||
}
|
||||
if (permissionsTable[name][type] == json.PROMPT_ACTION) {
|
||||
td.textContent = "!";
|
||||
td.className = "permprompt";
|
||||
}
|
||||
if (permissionsTable[name][type] == json.DENY_ACTION) {
|
||||
td.textContent = "✕";
|
||||
td.className = "permdeny"
|
||||
}
|
||||
tr.appendChild(td);
|
||||
}
|
||||
table.appendChild(tr);
|
||||
}
|
||||
let body = document.createElementNS(HTML, "body");
|
||||
body.appendChild(style);
|
||||
body.appendChild(table);
|
||||
let url = "data:text/html;charset=utf-8,";
|
||||
url += encodeURIComponent(body.outerHTML);
|
||||
UI.openInBrowser(url);
|
||||
}), "showing permission table");
|
||||
},
|
||||
|
||||
showRuntimeDetails: function() {
|
||||
UI.hidePanels();
|
||||
UI.busyUntil(AppManager.deviceFront.getDescription().then(json => {
|
||||
let styleContent = "";
|
||||
styleContent += "body {background:white; font-family: monospace}";
|
||||
styleContent += "table {border-collapse: collapse}";
|
||||
styleContent += "th, td {padding: 5px; border: 1px solid #EEE}";
|
||||
let style = document.createElementNS(HTML, "style");
|
||||
style.textContent = styleContent;
|
||||
let table = document.createElementNS(HTML, "table");
|
||||
for (let name in json) {
|
||||
let tr = document.createElementNS(HTML, "tr");
|
||||
let td = document.createElementNS(HTML, "td");
|
||||
td.textContent = name;
|
||||
tr.appendChild(td);
|
||||
td = document.createElementNS(HTML, "td");
|
||||
td.textContent = json[name];
|
||||
tr.appendChild(td);
|
||||
table.appendChild(tr);
|
||||
}
|
||||
let body = document.createElementNS(HTML, "body");
|
||||
body.appendChild(style);
|
||||
body.appendChild(table);
|
||||
let url = "data:text/html;charset=utf-8,";
|
||||
url += encodeURIComponent(body.outerHTML);
|
||||
UI.openInBrowser(url);
|
||||
}), "showing runtime details");
|
||||
|
||||
},
|
||||
|
||||
play: function() {
|
||||
switch(AppManager.selectedProject.type) {
|
||||
case "packaged":
|
||||
case "hosted":
|
||||
UI.busyUntil(AppManager.installAndRunProject(), "installing and running app");
|
||||
break;
|
||||
case "runtimeApp":
|
||||
UI.busyUntil(AppManager.runRuntimeApp(), "running app");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
UI.busyUntil(AppManager.stopRunningApp(), "stopping app");
|
||||
},
|
||||
|
||||
toggleToolbox: function() {
|
||||
if (UI.toolboxIframe) {
|
||||
UI.closeToolbox();
|
||||
} else {
|
||||
UI.toolboxPromise = AppManager.getTarget().then((target) => {
|
||||
return UI.showToolbox(target);
|
||||
}, UI.console.error);
|
||||
UI.busyUntil(UI.toolboxPromise, "opening toolbox");
|
||||
}
|
||||
},
|
||||
|
||||
removeProject: function() {
|
||||
AppManager.removeSelectedProject();
|
||||
},
|
||||
|
||||
toggleEditors: function() {
|
||||
// Toggle Itchpad
|
||||
},
|
||||
}
|
164
browser/devtools/webide/content/webide.xul
Normal file
@ -0,0 +1,164 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % webideDTD SYSTEM "chrome://webide/locale/webide.dtd" >
|
||||
%webideDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css"?>
|
||||
<?xml-stylesheet href="chrome://webide/skin/webide.css"?>
|
||||
|
||||
<window id="webide"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
title="&windowTitle;"
|
||||
windowtype="devtools:webide"
|
||||
macanimationtype="document"
|
||||
fullscreenbutton="true"
|
||||
screenX="4" screenY="4"
|
||||
width="640" height="480"
|
||||
persist="screenX screenY width height">
|
||||
|
||||
<script type="application/javascript" src="webide.js"></script>
|
||||
|
||||
<commandset id="mainCommandSet">
|
||||
<commandset id="editMenuCommands"/>
|
||||
<commandset id="webideCommands">
|
||||
<command id="cmd_quit" oncommand="Cmds.quit()"/>
|
||||
<command id="cmd_newApp" oncommand="Cmds.newApp()" label="&projectMenu_newApp_label;"/>
|
||||
<command id="cmd_importPackagedApp" oncommand="Cmds.importPackagedApp()" label="&projectMenu_importPackagedApp_label;"/>
|
||||
<command id="cmd_importHostedApp" oncommand="Cmds.importHostedApp()" label="&projectMenu_importHostedApp_label;"/>
|
||||
<command id="cmd_removeProject" oncommand="Cmds.removeProject()" label="&projectMenu_remove_label;"/>
|
||||
<command id="cmd_showProjectPanel" oncommand="Cmds.showProjectPanel()"/>
|
||||
<command id="cmd_showRuntimePanel" oncommand="Cmds.showRuntimePanel()"/>
|
||||
<command id="cmd_disconnectRuntime" oncommand="Cmds.disconnectRuntime()" label="&runtimeMenu_disconnect_label;"/>
|
||||
<command id="cmd_showPermissionsTable" oncommand="Cmds.showPermissionsTable()" label="&runtimeMenu_showPermissionTable_label;"/>
|
||||
<command id="cmd_showRuntimeDetails" oncommand="Cmds.showRuntimeDetails()" label="&runtimeMenu_showDetails_label;"/>
|
||||
<command id="cmd_takeScreenshot" oncommand="Cmds.takeScreenshot()" label="&runtimeMenu_takeScreenshot_label;"/>
|
||||
<command id="cmd_toggleEditor" oncommand="Cmds.toggleEditors()" label="&viewMenu_toggleEditor_label;"/>
|
||||
<command id="cmd_play" oncommand="Cmds.play()"/>
|
||||
<command id="cmd_stop" oncommand="Cmds.stop()"/>
|
||||
<command id="cmd_toggleToolbox" oncommand="Cmds.toggleToolbox()"/>
|
||||
</commandset>
|
||||
</commandset>
|
||||
|
||||
<menubar id="main-menubar">
|
||||
<menu id="menu-project" label="&projectMenu_label;" accesskey="&projectMenu_accesskey;">
|
||||
<menupopup id="menu-project-popup">
|
||||
<menuitem command="cmd_newApp" accesskey="&projectMenu_newApp_accesskey;"/>
|
||||
<menuitem command="cmd_importPackagedApp" accesskey="&projectMenu_importPackagedApp_accesskey;"/>
|
||||
<menuitem command="cmd_importHostedApp" accesskey="&projectMenu_importHostedApp_accesskey;"/>
|
||||
<menuitem command="cmd_showProjectPanel" key="key_showProjectPanel" label="&projectMenu_selectApp_label;" accesskey="&projectMenu_selectApp_accessley;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_play" key="key_play" label="&projectMenu_play_label;" accesskey="&projectMenu_play_accesskey;"/>
|
||||
<menuitem command="cmd_stop" key="key_stop" label="&projectMenu_stop_label;" accesskey="&projectMenu_stop_accesskey;"/>
|
||||
<menuitem command="cmd_toggleToolbox" key="key_toggleToolbox" label="&projectMenu_debug_label;" accesskey="&projectMenu_debug_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_removeProject" accesskey="&projectMenu_remove_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
<menu id="menu-runtime" label="&runtimeMenu_label;" accesskey="&runtimeMenu_accesskey;">
|
||||
<menupopup id="menu-runtime-popup">
|
||||
<menuitem command="cmd_takeScreenshot" accesskey="&runtimeMenu_takeScreenshot_accesskey;"/>
|
||||
<menuitem command="cmd_showPermissionsTable" accesskey="&runtimeMenu_showPermissionTable_accesskey;"/>
|
||||
<menuitem command="cmd_showRuntimeDetails" accesskey="&runtimeMenu_showDetails_accesskey;"/>
|
||||
<menuseparator/>
|
||||
<menuitem command="cmd_disconnectRuntime" accesskey="&runtimeMenu_disconnect_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
<menu id="menu-view" label="&viewMenu_label;" accesskey="&viewMenu_accesskey;">
|
||||
<menupopup id="menu-ViewPopup">
|
||||
<menuitem command="cmd_toggleEditor" key="key_toggleEditor" accesskey="&viewMenu_toggleEditor_accesskey;"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
</menubar>
|
||||
|
||||
<keyset id="mainKeyset">
|
||||
<key key="&key_quit;" id="key_quit" command="cmd_quit" modifiers="accel"/>
|
||||
<key key="&key_showProjectPanel;" id="key_showProjectPanel" command="cmd_showProjectPanel" modifiers="accel"/>
|
||||
<key key="&key_play;" id="key_play" command="cmd_play" modifiers="accel"/>
|
||||
<key key="&key_stop;" id="key_stop" command="cmd_stop" modifiers="accel"/>
|
||||
<key key="&key_toggleEditor;" id="key_toggleEditor" command="cmd_toggleEditor" modifiers="accel"/>
|
||||
<key keycode="&key_toggleToolbox;" id="key_toggleToolbox" command="cmd_toggleToolbox"/>
|
||||
</keyset>
|
||||
|
||||
<toolbar id="main-toolbar">
|
||||
|
||||
<vbox flex="1">
|
||||
<hbox id="action-buttons-container" class="busy">
|
||||
<toolbarbutton id="action-button-play" class="action-button" command="cmd_play" tooltiptext="&projectMenu_play_label;"/>
|
||||
<toolbarbutton id="action-button-stop" class="action-button" command="cmd_stop" tooltiptext="&projectMenu_stop_label;"/>
|
||||
<toolbarbutton id="action-button-debug" class="action-button" command="cmd_toggleToolbox" tooltiptext="&projectMenu_debug_label;"/>
|
||||
<html:img id="action-busy" src="chrome://webide/skin/throbber.svg"/>
|
||||
</hbox>
|
||||
|
||||
<hbox id="panel-buttons-container">
|
||||
<toolbarbutton id="project-panel-button" class="panel-button no-project" command="cmd_showProjectPanel">
|
||||
<image class="panel-button-image"/>
|
||||
<label class="panel-button-label" value="&projectButton_label;"/>
|
||||
<image class="panel-button-anchor"/>
|
||||
</toolbarbutton>
|
||||
<spacer flex="1"/>
|
||||
<toolbarbutton id="runtime-panel-button" class="panel-button" command="cmd_showRuntimePanel">
|
||||
<image class="panel-button-image"/>
|
||||
<label class="panel-button-label" value="&runtimeButton_label;"/>
|
||||
<image class="panel-button-anchor"/>
|
||||
</toolbarbutton>
|
||||
</hbox>
|
||||
|
||||
</vbox>
|
||||
</toolbar>
|
||||
|
||||
<popupset>
|
||||
|
||||
<!-- App panel -->
|
||||
<panel id="project-panel" type="arrow" position="bottomcenter topleft" consumeoutsideclicks="true">
|
||||
<vbox flex="1">
|
||||
<toolbarbutton class="panel-item project-panel-item-newapp" command="cmd_newApp"/>
|
||||
<toolbarbutton class="panel-item project-panel-item-openpackaged" command="cmd_importPackagedApp"/>
|
||||
<toolbarbutton class="panel-item project-panel-item-openhosted" command="cmd_importHostedApp"/>
|
||||
<label class="panel-header">&projectPanel_myProjects;</label>
|
||||
<vbox id="project-panel-projects"></vbox>
|
||||
<label class="panel-header" id="panel-header-runtimeapps" hidden="true">&projectPanel_runtimeApps;</label>
|
||||
<vbox flex="1" id="project-panel-runtimeapps"/>
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
<!-- Runtime panel -->
|
||||
<panel id="runtime-panel" type="arrow" position="bottomcenter topright" consumeoutsideclicks="true">
|
||||
<vbox flex="1">
|
||||
<label class="panel-header">&runtimePanel_USBDevices;</label>
|
||||
<vbox id="runtime-panel-usbruntime"></vbox>
|
||||
<label class="panel-header">&runtimePanel_simulators;</label>
|
||||
<vbox id="runtime-panel-simulators"></vbox>
|
||||
<vbox flex="1" id="runtime-actions" hidden="true">
|
||||
<toolbarbutton class="panel-item" id="runtime-details" command="cmd_showRuntimeDetails"/>
|
||||
<toolbarbutton class="panel-item" id="runtime-permissions" command="cmd_showPermissionsTable"/>
|
||||
<toolbarbutton class="panel-item" id="runtime-screenshot" command="cmd_takeScreenshot"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</panel>
|
||||
|
||||
</popupset>
|
||||
|
||||
<vbox flex="1" id="body">
|
||||
<iframe id="details" flex="1" hidden="true" src="details.xhtml"/>
|
||||
</vbox>
|
||||
|
||||
<splitter hidden="true" class="devtools-horizontal-splitter" orient="vertical"/>
|
||||
|
||||
<!-- toolbox iframe will be inserted here -->
|
||||
|
||||
<html:div id="logs-container">
|
||||
<html:pre id="logs"></html:pre>
|
||||
<html:button id="toggle-logs">&logs;</html:button>
|
||||
</html:div>
|
||||
|
||||
</window>
|
5
browser/devtools/webide/locales/Makefile.in
Normal file
@ -0,0 +1,5 @@
|
||||
# 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/.
|
||||
|
||||
DEFINES += -DAB_CD=$(AB_CD)
|
80
browser/devtools/webide/locales/en-US/webide.dtd
Normal file
@ -0,0 +1,80 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<!ENTITY windowTitle "Firefox App Manager">
|
||||
|
||||
<!ENTITY projectMenu_label "Project">
|
||||
<!ENTITY projectMenu_accesskey "P">
|
||||
<!ENTITY projectMenu_newApp_label "New App…">
|
||||
<!ENTITY projectMenu_newApp_accesskey "N">
|
||||
<!ENTITY projectMenu_importPackagedApp_label "Open Packaged App…">
|
||||
<!ENTITY projectMenu_importPackagedApp_accesskey "P">
|
||||
<!ENTITY projectMenu_importHostedApp_label "Open Hosted App…">
|
||||
<!ENTITY projectMenu_importHostedApp_accesskey "H">
|
||||
<!ENTITY projectMenu_selectApp_label "Open App…">
|
||||
<!ENTITY projectMenu_selectApp_accessley "S">
|
||||
<!ENTITY projectMenu_play_label "Install and run">
|
||||
<!ENTITY projectMenu_play_accesskey "I">
|
||||
<!ENTITY projectMenu_stop_label "Stop App">
|
||||
<!ENTITY projectMenu_stop_accesskey "S">
|
||||
<!ENTITY projectMenu_debug_label "Debug App">
|
||||
<!ENTITY projectMenu_debug_accesskey "D">
|
||||
<!ENTITY projectMenu_remove_label "Remove Project">
|
||||
<!ENTITY projectMenu_remove_accesskey "R">
|
||||
|
||||
<!ENTITY runtimeMenu_label "Runtime">
|
||||
<!ENTITY runtimeMenu_accesskey "R">
|
||||
<!ENTITY runtimeMenu_disconnect_label "Disconnect">
|
||||
<!ENTITY runtimeMenu_disconnect_accesskey "D">
|
||||
<!ENTITY runtimeMenu_showPermissionTable_label "Permissions Table">
|
||||
<!ENTITY runtimeMenu_showPermissionTable_accesskey "P">
|
||||
<!ENTITY runtimeMenu_takeScreenshot_label "Screenshot">
|
||||
<!ENTITY runtimeMenu_takeScreenshot_accesskey "S">
|
||||
<!ENTITY runtimeMenu_showDetails_label "Runtime Info">
|
||||
<!ENTITY runtimeMenu_showDetails_accesskey "E">
|
||||
|
||||
<!ENTITY viewMenu_label "View">
|
||||
<!ENTITY viewMenu_accesskey "V">
|
||||
<!ENTITY viewMenu_toggleEditor_label "Toggle Editor">
|
||||
<!ENTITY viewMenu_toggleEditor_accesskey "E">
|
||||
|
||||
<!ENTITY projectButton_label "Open App">
|
||||
<!ENTITY runtimeButton_label "Select Runtime">
|
||||
|
||||
<!-- We try to repicate Firefox' bindings: -->
|
||||
<!-- quit app -->
|
||||
<!ENTITY key_quit "Q">
|
||||
<!-- open menu -->
|
||||
<!ENTITY key_showProjectPanel "O">
|
||||
<!-- reload app -->
|
||||
<!ENTITY key_play "R">
|
||||
<!-- close app -->
|
||||
<!ENTITY key_stop "W">
|
||||
<!-- show toolbox -->
|
||||
<!ENTITY key_toggleToolbox "VK_F12">
|
||||
<!-- toggle sidebar -->
|
||||
<!ENTITY key_toggleEditor "B">
|
||||
|
||||
<!ENTITY projectPanel_myProjects "My Projects">
|
||||
<!ENTITY projectPanel_runtimeApps "Runtime Apps">
|
||||
<!ENTITY runtimePanel_USBDevices "USB Devices">
|
||||
<!ENTITY runtimePanel_simulators "Simulators">
|
||||
|
||||
<!ENTITY logs "Logs">
|
||||
|
||||
|
||||
<!-- Lense -->
|
||||
<!ENTITY details_valid_header "valid">
|
||||
<!ENTITY details_warning_header "warnings">
|
||||
<!ENTITY details_error_header "errors">
|
||||
<!ENTITY details_description "Description">
|
||||
<!ENTITY details_location "Location">
|
||||
<!ENTITY details_manifestURL "App ID">
|
||||
<!ENTITY details_removeProject_button "Remove Project">
|
||||
|
||||
<!-- New App -->
|
||||
<!ENTITY newAppWindowTitle "New App">
|
||||
<!ENTITY newAppHeader "Select template">
|
||||
<!ENTITY newAppLoadingTemplate "Loading templates…">
|
||||
<!ENTITY newAppProjectName "Project Name:">
|
13
browser/devtools/webide/locales/en-US/webide.properties
Normal file
@ -0,0 +1,13 @@
|
||||
# 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/.
|
||||
|
||||
title_noApp=Firefox App Manager
|
||||
title_app=Firefox App Manager: %S
|
||||
|
||||
runtimeButton_label=Select Runtime
|
||||
projectButton_label=Open App
|
||||
|
||||
importPackagedApp_title=Select directory
|
||||
importHostedApp_title=Open Hosted App
|
||||
importHostedApp_header=Enter Manifest URL
|
10
browser/devtools/webide/locales/jar.mn
Normal file
@ -0,0 +1,10 @@
|
||||
#filter substitution
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
webide.jar:
|
||||
% locale webide @AB_CD@ %locale/
|
||||
locale/webide.dtd (%webide.dtd)
|
||||
locale/webide.properties (%webide.properties)
|
7
browser/devtools/webide/locales/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
540
browser/devtools/webide/modules/app-manager.js
Normal file
@ -0,0 +1,540 @@
|
||||
/* 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/. */
|
||||
|
||||
const {Cu} = require("chrome");
|
||||
|
||||
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
|
||||
const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm");
|
||||
const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
|
||||
const {TextEncoder, OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
const {AppProjects} = require("devtools/app-manager/app-projects");
|
||||
const WebappsStore = require("devtools/app-manager/webapps-store");
|
||||
const {AppValidator} = require("devtools/app-manager/app-validator");
|
||||
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
|
||||
const AppActorFront = require("devtools/app-actor-front");
|
||||
const {getDeviceFront} = require("devtools/server/actors/device");
|
||||
|
||||
exports.AppManager = AppManager = {
|
||||
|
||||
// FIXME: will break when devtools/app-manager will be removed:
|
||||
DEFAULT_PROJECT_ICON: "chrome://browser/skin/devtools/app-manager/default-app-icon.png",
|
||||
DEFAULT_PROJECT_NAME: "--",
|
||||
|
||||
init: function() {
|
||||
let host = Services.prefs.getCharPref("devtools.debugger.remote-host");
|
||||
let port = Services.prefs.getIntPref("devtools.debugger.remote-port");
|
||||
|
||||
this.connection = ConnectionManager.createConnection("localhost", port);
|
||||
this.onConnectionChanged = this.onConnectionChanged.bind(this);
|
||||
this.connection.on(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
|
||||
this.webAppsStore = new WebappsStore(this.connection);
|
||||
|
||||
this.runtimeList = {usb:[], simulators:[]};
|
||||
this.trackUSBRuntimes();
|
||||
this.trackSimulatorRuntimes();
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
this._unlistenToApps();
|
||||
this.selectedProject = null;
|
||||
this.selectedRuntime = null;
|
||||
this.untrackUSBRuntimes();
|
||||
this.untrackSimulatorRuntimes();
|
||||
this._runningApps.clear();
|
||||
this.runtimeList = null;
|
||||
this.connection.off(Connection.Events.STATUS_CHANGED, this.onConnectionChanged);
|
||||
this.webAppsStore.destroy();
|
||||
this._listTabsResponse = null;
|
||||
this.connection.disconnect();
|
||||
this.connection = null;
|
||||
},
|
||||
|
||||
console: {
|
||||
// Forward console.* calls to the UI
|
||||
log: function(msg) { AppManager.update("console", {level: "log", message: msg}); },
|
||||
warning: function(msg) { AppManager.update("console", {level: "warning", message: msg}); },
|
||||
error: function(msg) { AppManager.update("console", {level: "error", message: msg}); },
|
||||
success: function(msg) { AppManager.update("console", {level: "success", message: msg}); },
|
||||
},
|
||||
|
||||
update: function(what, details) {
|
||||
// Anything we want to forward to the UI
|
||||
this.emit("app-manager-update", what, details);
|
||||
},
|
||||
|
||||
onConnectionChanged: function() {
|
||||
if (this.connection.status == Connection.Status.DISCONNECTED) {
|
||||
this.selectedRuntime = null;
|
||||
}
|
||||
|
||||
if (this.connection.status != Connection.Status.CONNECTED) {
|
||||
AppManager.console.log("Connection status changed: " + this.connection.status);
|
||||
this._runningApps.clear();
|
||||
this._unlistenToApps();
|
||||
this._listTabsResponse = null;
|
||||
} else {
|
||||
this.connection.client.listTabs((response) => {
|
||||
this._listenToApps();
|
||||
this._listTabsResponse = response;
|
||||
this._getRunningApps();
|
||||
});
|
||||
}
|
||||
|
||||
this.update("connection");
|
||||
},
|
||||
|
||||
_runningApps: new Set(),
|
||||
_getRunningApps: function() {
|
||||
let client = this.connection.client;
|
||||
let request = {
|
||||
to: this._listTabsResponse.webappsActor,
|
||||
type: "listRunningApps"
|
||||
};
|
||||
client.request(request, (res) => {
|
||||
if (res.error) {
|
||||
AppManager.console.error("listRunningApps error: " + res.error);
|
||||
}
|
||||
for (let m of res.apps) {
|
||||
this._runningApps.add(m);
|
||||
}
|
||||
});
|
||||
this.checkIfProjectIsRunning();
|
||||
},
|
||||
_listenToApps: function() {
|
||||
let client = this.connection.client;
|
||||
client.addListener("appOpen", (type, { manifestURL }) => {
|
||||
AppManager.console.log("App open: " + manifestURL);
|
||||
this._runningApps.add(manifestURL);
|
||||
this.checkIfProjectIsRunning();
|
||||
});
|
||||
|
||||
client.addListener("appClose", (type, { manifestURL }) => {
|
||||
AppManager.console.log("App close: " + manifestURL);
|
||||
this._runningApps.delete(manifestURL);
|
||||
this.checkIfProjectIsRunning();
|
||||
});
|
||||
},
|
||||
_unlistenToApps: function() {
|
||||
// Is that even possible?
|
||||
// connection.client is null now.
|
||||
},
|
||||
|
||||
isProjectRunning: function() {
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return manifest && this._runningApps.has(manifest);
|
||||
},
|
||||
|
||||
checkIfProjectIsRunning: function() {
|
||||
if (this.selectedProject) {
|
||||
if (this.isProjectRunning()) {
|
||||
AppManager.console.log("Project is running on " + this.selectedRuntime.getName());
|
||||
this.update("project-is-running");
|
||||
this._notRunningLogged = false;
|
||||
} else {
|
||||
this.update("project-is-not-running");
|
||||
if (!this._notRunningLogged) {
|
||||
this._notRunningLogged = true;
|
||||
AppManager.console.log("Project is not running");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getTarget: function() {
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
let name = this.selectedProject.name;
|
||||
if (manifest) {
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
|
||||
let promise = AppActorFront.getTargetForApp(client, actor, manifest);
|
||||
promise.then(( ) => { AppManager.console.log("Connected to app: " + name) },
|
||||
(e) => { AppManager.console.error("Can't connect to app: " + e) });
|
||||
return promise;
|
||||
|
||||
}
|
||||
AppManager.console.error("Can't find manifestURL for selected project");
|
||||
return promise.reject();
|
||||
},
|
||||
|
||||
|
||||
getProjectManifestURL: function(project) {
|
||||
let manifest = null;
|
||||
if (project.type == "runtimeApp") {
|
||||
manifest = project.app.manifestURL;
|
||||
}
|
||||
|
||||
if (project.type == "hosted") {
|
||||
manifest = project.location;
|
||||
}
|
||||
|
||||
if (project.type == "packaged" && project.packagedAppOrigin) {
|
||||
manifest = "app://" + project.packagedAppOrigin + "/manifest.webapp";
|
||||
}
|
||||
|
||||
return manifest;
|
||||
},
|
||||
|
||||
_selectedProject: null,
|
||||
set selectedProject(value) {
|
||||
if (value != this.selectedProject) {
|
||||
this._selectedProject = value;
|
||||
|
||||
if (this.selectedProject) {
|
||||
AppManager.console.log("New project selected: " + this.selectedProject.name);
|
||||
if (this.selectedProject.type == "runtimeApp") {
|
||||
this.runRuntimeApp();
|
||||
} else {
|
||||
this.validateProject(this.selectedProject);
|
||||
}
|
||||
} else {
|
||||
AppManager.console.log("No project selected");
|
||||
}
|
||||
|
||||
this.update("project");
|
||||
|
||||
this.checkIfProjectIsRunning();
|
||||
}
|
||||
},
|
||||
get selectedProject() {
|
||||
return this._selectedProject;
|
||||
},
|
||||
|
||||
removeSelectedProject: function() {
|
||||
let location = this.selectedProject.location;
|
||||
AppManager.selectedProject = null;
|
||||
AppProjects.remove(location);
|
||||
},
|
||||
|
||||
_selectedRuntime: null,
|
||||
set selectedRuntime(value) {
|
||||
this._selectedRuntime = value;
|
||||
if (!value &&
|
||||
this.selectedProject &&
|
||||
this.selectedProject.type == "runtimeApp") {
|
||||
this.selectedProject = null;
|
||||
}
|
||||
this.update("runtime");
|
||||
},
|
||||
|
||||
get selectedRuntime() {
|
||||
return this._selectedRuntime;
|
||||
},
|
||||
|
||||
connectToRuntime: function(runtime) {
|
||||
if (this.connection.status == Connection.Status.CONNECTED) {
|
||||
return promise.reject("Already connected");
|
||||
}
|
||||
this.selectedRuntime = runtime;
|
||||
let deferred = promise.defer();
|
||||
|
||||
AppManager.console.log("Connecting to " + runtime.getName());
|
||||
let onConnectedOrDisconnected = () => {
|
||||
this.connection.off(Connection.Events.CONNECTED, onConnectedOrDisconnected);
|
||||
this.connection.off(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
|
||||
if (this.connection.status == Connection.Status.CONNECTED) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject();
|
||||
}
|
||||
}
|
||||
this.connection.on(Connection.Events.CONNECTED, onConnectedOrDisconnected);
|
||||
this.connection.on(Connection.Events.DISCONNECTED, onConnectedOrDisconnected);
|
||||
this.selectedRuntime.connect(this.connection).then(
|
||||
() => {},
|
||||
() => {deferred.reject()});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
get deviceFront() {
|
||||
if (!this._listTabsResponse) {
|
||||
return null;
|
||||
}
|
||||
return getDeviceFront(this.connection.client, this._listTabsResponse);
|
||||
},
|
||||
|
||||
disconnectRuntime: function() {
|
||||
if (this.connection.status != Connection.Status.CONNECTED) {
|
||||
return promise.reject("Already disconnected");
|
||||
}
|
||||
let deferred = promise.defer();
|
||||
this.connection.once(Connection.Events.DISCONNECTED, () => deferred.resolve());
|
||||
this.connection.disconnect();
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
runRuntimeApp: function() {
|
||||
if (this.selectedProject && this.selectedProject.type != "runtimeApp") {
|
||||
return promise.reject("attempting to run a non-runtime app");
|
||||
}
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return AppActorFront.launchApp(client, actor, manifest);
|
||||
},
|
||||
|
||||
installAndRunProject: function() {
|
||||
let project = this.selectedProject;
|
||||
|
||||
if (!project ||
|
||||
!this._listTabsResponse ||
|
||||
(project.type != "packaged" && project.type != "hosted")) {
|
||||
AppManager.console.error("Can't install project. Unknown type of project.");
|
||||
return promise.reject("Can't install");
|
||||
}
|
||||
|
||||
return this.validateProject(project).then(() => {
|
||||
|
||||
if (project.errorsCount > 0) {
|
||||
AppManager.console.error("Can't install project. Validation errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let installPromise;
|
||||
|
||||
if (project.type == "packaged") {
|
||||
installPromise = AppActorFront.installPackaged(client, actor, project.location, project.packagedAppOrigin)
|
||||
.then(({ appId }) => {
|
||||
// If the packaged app specified a custom origin override,
|
||||
// we need to update the local project origin
|
||||
project.packagedAppOrigin = appId;
|
||||
// And ensure the indexed db on disk is also updated
|
||||
AppProjects.update(project);
|
||||
});
|
||||
}
|
||||
|
||||
if (project.type == "hosted") {
|
||||
let manifestURLObject = Services.io.newURI(project.location, null, null);
|
||||
let origin = Services.io.newURI(manifestURLObject.prePath, null, null);
|
||||
let appId = origin.host;
|
||||
let metadata = {
|
||||
origin: origin.spec,
|
||||
manifestURL: project.location
|
||||
};
|
||||
installPromise = AppActorFront.installHosted(client, actor, appId, metadata, project.manifest);
|
||||
}
|
||||
|
||||
if (!installPromise) {
|
||||
return promise.reject("Can't install");
|
||||
}
|
||||
|
||||
return installPromise.then(() => {
|
||||
let manifest = this.getProjectManifestURL(project);
|
||||
if (!this._runningApps.has(manifest)) {
|
||||
AppManager.console.log("Launching app: " + project.name);
|
||||
AppActorFront.launchApp(client, actor, manifest);
|
||||
} else {
|
||||
AppManager.console.log("Reloading app: " + project.name);
|
||||
AppActorFront.reloadApp(client, actor, manifest);
|
||||
}
|
||||
});
|
||||
|
||||
}, AppManager.console.error);
|
||||
},
|
||||
|
||||
stopRunningApp: function() {
|
||||
let client = this.connection.client;
|
||||
let actor = this._listTabsResponse.webappsActor;
|
||||
let manifest = this.getProjectManifestURL(this.selectedProject);
|
||||
return AppActorFront.closeApp(client, actor, manifest);
|
||||
},
|
||||
|
||||
/* PROJECT VALIDATION */
|
||||
|
||||
validateProject: function(project) {
|
||||
if (!project) {
|
||||
return promise.reject();
|
||||
}
|
||||
|
||||
let validation = new AppValidator(project);
|
||||
return validation.validate()
|
||||
.then(() => {
|
||||
if (validation.manifest) {
|
||||
let manifest = validation.manifest;
|
||||
let iconPath;
|
||||
if (manifest.icons) {
|
||||
let size = Object.keys(manifest.icons).sort(function(a, b) b - a)[0];
|
||||
if (size) {
|
||||
iconPath = manifest.icons[size];
|
||||
}
|
||||
}
|
||||
if (!iconPath) {
|
||||
project.icon = AppManager.DEFAULT_PROJECT_ICON;
|
||||
} else {
|
||||
if (project.type == "hosted") {
|
||||
let manifestURL = Services.io.newURI(project.location, null, null);
|
||||
let origin = Services.io.newURI(manifestURL.prePath, null, null);
|
||||
project.icon = Services.io.newURI(iconPath, null, origin).spec;
|
||||
} else if (project.type == "packaged") {
|
||||
let projectFolder = FileUtils.File(project.location);
|
||||
let folderURI = Services.io.newFileURI(projectFolder).spec;
|
||||
project.icon = folderURI + iconPath.replace(/^\/|\\/, "");
|
||||
}
|
||||
}
|
||||
project.manifest = validation.manifest;
|
||||
|
||||
if ("name" in project.manifest) {
|
||||
project.name = project.manifest.name;
|
||||
} else {
|
||||
project.name = AppManager.DEFAULT_PROJECT_NAME;
|
||||
}
|
||||
} else {
|
||||
project.manifest = null;
|
||||
project.icon = AppManager.DEFAULT_PROJECT_ICON;
|
||||
project.name = AppManager.DEFAULT_PROJECT_NAME;
|
||||
}
|
||||
|
||||
project.validationStatus = "valid";
|
||||
|
||||
if (validation.warnings.length > 0) {
|
||||
project.warningsCount = validation.warnings.length;
|
||||
project.warnings = validation.warnings;
|
||||
project.validationStatus = "warning";
|
||||
AppManager.console.warning("Validation (" + project.name + "): found " + validation.warnings.length + " warnings.");
|
||||
} else {
|
||||
project.warnings = "";
|
||||
project.warningsCount = 0;
|
||||
AppManager.console.log("Validation (" + project.name + "): no warnings found.");
|
||||
}
|
||||
|
||||
if (validation.errors.length > 0) {
|
||||
project.errorsCount = validation.errors.length;
|
||||
project.errors = validation.errors;
|
||||
project.validationStatus = "error";
|
||||
AppManager.console.error("Validation (" + project.name + "): found " + validation.errors.length + " errors.");
|
||||
} else {
|
||||
project.errors = "";
|
||||
project.errorsCount = 0;
|
||||
AppManager.console.log("Validation (" + project.name + "): no errors found.");
|
||||
}
|
||||
|
||||
if (project.warningsCount && project.errorsCount) {
|
||||
project.validationStatus = "error warning";
|
||||
}
|
||||
|
||||
if (this.selectedProject === project) {
|
||||
this.update("project-validated");
|
||||
}
|
||||
|
||||
if (AppProjects.get(project.location)) {
|
||||
AppProjects.update(project);
|
||||
}
|
||||
|
||||
return project;
|
||||
}, AppManager.console.error);
|
||||
},
|
||||
|
||||
/* RUNTIME LIST */
|
||||
|
||||
trackUSBRuntimes: function() {
|
||||
this._updateUSBRuntimes = this._updateUSBRuntimes.bind(this);
|
||||
Devices.on("register", this._updateUSBRuntimes);
|
||||
Devices.on("unregister", this._updateUSBRuntimes);
|
||||
Devices.on("addon-status-updated", this._updateUSBRuntimes);
|
||||
this._updateUSBRuntimes();
|
||||
},
|
||||
untrackUSBRuntimes: function() {
|
||||
Devices.off("register", this._updateUSBRuntimes);
|
||||
Devices.off("unregister", this._updateUSBRuntimes);
|
||||
Devices.off("addon-status-updated", this._updateUSBRuntimes);
|
||||
},
|
||||
_updateUSBRuntimes: function() {
|
||||
this.runtimeList.usb = [];
|
||||
for (let id of Devices.available()) {
|
||||
this.runtimeList.usb.push(new USBRuntime(id));
|
||||
}
|
||||
this.update("runtimelist");
|
||||
},
|
||||
|
||||
trackSimulatorRuntimes: function() {
|
||||
this._updateSimulatorRuntimes = this._updateSimulatorRuntimes.bind(this);
|
||||
Simulator.on("register", this._updateSimulatorRuntimes);
|
||||
Simulator.on("unregister", this._updateSimulatorRuntimes);
|
||||
this._updateSimulatorRuntimes();
|
||||
},
|
||||
untrackSimulatorRuntimes: function() {
|
||||
Simulator.off("register", this._updateSimulatorRuntimes);
|
||||
Simulator.off("unregister", this._updateSimulatorRuntimes);
|
||||
},
|
||||
_updateSimulatorRuntimes: function() {
|
||||
this.runtimeList.simulators = [];
|
||||
for (let version of Simulator.availableVersions()) {
|
||||
this.runtimeList.simulators.push(new SimulatorRuntime(version));
|
||||
}
|
||||
this.update("runtimelist");
|
||||
},
|
||||
|
||||
writeManifest: function(project) {
|
||||
if (project.type != "packaged") {
|
||||
return promise.reject("Not a packaged app");
|
||||
}
|
||||
|
||||
if (!project.manifest) {
|
||||
project.manifest = {};
|
||||
}
|
||||
|
||||
let folder = project.location;
|
||||
let manifestPath = OS.Path.join(folder, "manifest.webapp");
|
||||
let text = JSON.stringify(project.manifest, null, 2);
|
||||
let encoder = new TextEncoder();
|
||||
let array = encoder.encode(text);
|
||||
return OS.File.writeAtomic(manifestPath, array, {tmpPath: manifestPath + ".tmp"});
|
||||
},
|
||||
}
|
||||
|
||||
EventEmitter.decorate(AppManager);
|
||||
|
||||
/* RUNTIMES */
|
||||
|
||||
function USBRuntime(id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
USBRuntime.prototype = {
|
||||
connect: function(connection) {
|
||||
let device = Devices.getByName(this.id);
|
||||
if (!device) {
|
||||
AppManager.console.error("Can't find device: " + id);
|
||||
return promise.reject();
|
||||
}
|
||||
return device.connect().then((port) => {
|
||||
connection.host = "localhost";
|
||||
connection.port = port;
|
||||
connection.connect();
|
||||
});
|
||||
},
|
||||
getName: function() {
|
||||
return this.id;
|
||||
},
|
||||
}
|
||||
|
||||
function SimulatorRuntime(version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
SimulatorRuntime.prototype = {
|
||||
connect: function(connection) {
|
||||
let port = ConnectionManager.getFreeTCPPort();
|
||||
let simulator = Simulator.getByVersion(this.version);
|
||||
if (!simulator || !simulator.launch) {
|
||||
AppManager.console.error("Can't find simulator: " + this.version);
|
||||
return promise.reject();
|
||||
}
|
||||
return simulator.launch({port: port}).then(() => {
|
||||
connection.port = port;
|
||||
connection.keepConnecting = true;
|
||||
connection.connect();
|
||||
});
|
||||
},
|
||||
getName: function() {
|
||||
return this.version;
|
||||
},
|
||||
}
|
12
browser/devtools/webide/moz.build
Normal file
@ -0,0 +1,12 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
PARALLEL_DIRS += [
|
||||
'content',
|
||||
'locales',
|
||||
'themes',
|
||||
]
|
||||
|
135
browser/devtools/webide/themes/details.css
Normal file
@ -0,0 +1,135 @@
|
||||
/* 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/. */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background-color: white;
|
||||
font-family: Lucida Grande, Helvetica, Helvetica Neue, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
h1, h3, p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#toolbar {
|
||||
background-color: #D8D8D8;
|
||||
border-bottom: 1px solid #AAA;
|
||||
}
|
||||
|
||||
#toolbar > button {
|
||||
-moz-appearance: none;
|
||||
background-color: transparent;
|
||||
border-width: 0 1px 0 0;
|
||||
border-color: #AAA;
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
font-weight: bold;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#toolbar > button:hover {
|
||||
background-color: #CCC;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#validation_status {
|
||||
float: right;
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
line-height: 24px;
|
||||
padding: 0 12px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
header {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
header > div {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#icon {
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
text-align:top;
|
||||
float: left;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
h1, #type {
|
||||
line-height: 24px;
|
||||
height: 24px; /* avoid collapsing if empty */
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
#type {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
main {
|
||||
padding-left: 88px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #999;
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
main > p {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.validation_messages {
|
||||
margin-left: 74px;
|
||||
list-style: none;
|
||||
border-left: 4px solid transparent;
|
||||
padding: 0 10px;;
|
||||
}
|
||||
|
||||
|
||||
body.valid #validation_status {
|
||||
background-color: #81D135;
|
||||
}
|
||||
|
||||
body.warning #validation_status {
|
||||
background-color: #FFAC00;
|
||||
}
|
||||
|
||||
body.error #validation_status {
|
||||
background-color: #ED4C62;
|
||||
}
|
||||
|
||||
#warningslist {
|
||||
border-color: #FFAC00
|
||||
}
|
||||
|
||||
#errorslist {
|
||||
border-color: #ED4C62;
|
||||
}
|
||||
|
||||
#validation_status > span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.valid #validation_status > .valid,
|
||||
body.warning #validation_status > .warning,
|
||||
body.error #validation_status > .error {
|
||||
display: inline;
|
||||
}
|
BIN
browser/devtools/webide/themes/icons.png
Normal file
After Width: | Height: | Size: 27 KiB |
11
browser/devtools/webide/themes/jar.mn
Normal file
@ -0,0 +1,11 @@
|
||||
# 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/.
|
||||
|
||||
webide.jar:
|
||||
% skin webide classic/1.0 %skin/
|
||||
* skin/webide.css (webide.css)
|
||||
skin/icons.png (icons.png)
|
||||
skin/details.css (details.css)
|
||||
skin/newapp.css (newapp.css)
|
||||
skin/throbber.svg (throbber.svg)
|
7
browser/devtools/webide/themes/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
58
browser/devtools/webide/themes/newapp.css
Normal file
@ -0,0 +1,58 @@
|
||||
/* 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/. */
|
||||
|
||||
dialog {
|
||||
-moz-appearance: none;
|
||||
background-image: linear-gradient(rgb(255, 255, 255), rgb(237, 237, 237) 100px);
|
||||
font-family: "Clear Sans", sans-serif;
|
||||
color: #424E5A;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
font-size: 1.5rem;
|
||||
font-weight: normal;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
richlistbox {
|
||||
-moz-appearance: none;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #424E5A;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
richlistitem:not([selected="true"]):hover {
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
richlistitem > vbox > label {
|
||||
margin: 0;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
richlistbox > description {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
-moz-box-align: start;
|
||||
}
|
||||
|
||||
richlistitem:nth-child(odd) {
|
||||
background-color:
|
||||
}
|
||||
|
||||
richlistitem > image {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
textbox {
|
||||
font-size: 1.2rem;
|
||||
}
|
22
browser/devtools/webide/themes/throbber.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="24" height="24" viewBox="0 0 64 64">
|
||||
<g>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(0, 32, 32)" fill="#BBB"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(30, 32, 32)" fill="#AAA"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(60, 32, 32)" fill="#999"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(90, 32, 32)" fill="#888"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(120, 32, 32)" fill="#777"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(150, 32, 32)" fill="#666"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(180, 32, 32)" fill="#555"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(210, 32, 32)" fill="#444"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(240, 32, 32)" fill="#333"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(270, 32, 32)" fill="#222"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(300, 32, 32)" fill="#111"/>
|
||||
<rect x="30" y="4" width="4" height="15" transform="rotate(330, 32, 32)" fill="#000"/>
|
||||
<animateTransform attributeName="transform" type="rotate" calcMode="discrete" values="0 32 32;30 32 32;60 32 32;90 32 32;120 32 32;150 32 32;180 32 32;210 32 32;240 32 32;270 32 32;300 32 32;330 32 32" dur="0.8s" repeatCount="indefinite"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
279
browser/devtools/webide/themes/webide.css
Normal file
@ -0,0 +1,279 @@
|
||||
/* 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/. */
|
||||
|
||||
#main-toolbar {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
#action-buttons-container {
|
||||
-moz-box-pack: center;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
#panel-buttons-container {
|
||||
height: 50px;
|
||||
margin-top: -50px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#panel-buttons-container > .panel-button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#action-busy {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
window.busy .action-button,
|
||||
window:not(.busy) #action-busy {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Panel buttons */
|
||||
|
||||
.panel-button {
|
||||
-moz-box-align: center;
|
||||
}
|
||||
|
||||
.panel-button-anchor {
|
||||
list-style-image: url('icons.png');
|
||||
-moz-image-region: rect(43px, 563px, 61px, 535px);
|
||||
width: 12;
|
||||
height: 7px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.panel-button:hover > .panel-button-anchor {
|
||||
-moz-image-region: rect(243px, 563px, 261px, 535px);
|
||||
}
|
||||
|
||||
/* Panel buttons - projects */
|
||||
|
||||
#project-panel-button > .panel-button-image {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
#project-panel-button.no-project > .panel-button-image {
|
||||
list-style-image: url("icons.png");
|
||||
-moz-image-region: rect(0px, 740px, 40px, 700px);
|
||||
}
|
||||
|
||||
/* Panel buttons - runtime */
|
||||
|
||||
#runtime-panel-button > .panel-button-image {
|
||||
list-style-image: url('icons.png');
|
||||
-moz-image-region: rect(25px, 475px, 75px, 425px);
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
}
|
||||
|
||||
#runtime-panel-button[active="true"] > .panel-button-image {
|
||||
-moz-image-region: rect(125px, 475px, 175px, 425px);
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
|
||||
.action-button {
|
||||
-moz-appearance: none;
|
||||
border-width: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-image: url('icons.png');
|
||||
}
|
||||
|
||||
.action-button[disabled="true"] {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.action-button > .toolbarbutton-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.action-button > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#action-button-play { -moz-image-region: rect(0,100px,100px,0) }
|
||||
#action-button-stop { -moz-image-region: rect(0,200px,100px,100px) }
|
||||
#action-button-debug { -moz-image-region: rect(0,400px,100px,300px) }
|
||||
|
||||
#action-button-play:not([disabled="true"]):hover { -moz-image-region: rect(200px,100px,300px,0) }
|
||||
#action-button-stop:not([disabled="true"]):hover { -moz-image-region: rect(200px,200px,300px,100px) }
|
||||
#action-button-debug:not([disabled="true"]):not([active="true"]):hover { -moz-image-region: rect(200px,400px,300px,300px) }
|
||||
|
||||
#action-button-debug[active="true"] { -moz-image-region: rect(100px,400px,200px,300px) }
|
||||
|
||||
/* Panels */
|
||||
|
||||
panel > vbox {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
panel > .panel-arrowcontainer > .panel-arrowcontent {
|
||||
padding: 12px 0;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.panel-item {
|
||||
padding: 3px 12px;
|
||||
margin: 0;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
.panel-item:hover {
|
||||
background: #CBF0FE;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
/* We can't use borders or vertical padding here because
|
||||
* panels don't take these into account when calculated the
|
||||
* height of the panel.
|
||||
*/
|
||||
background-color: #EEE;
|
||||
outline-width: 1px;
|
||||
outline-color: #D5D5D5;
|
||||
outline-style: solid;
|
||||
color: #ACACAC;
|
||||
text-transform: uppercase;
|
||||
padding: 0 16px;
|
||||
line-height: 200%;
|
||||
margin: 5px 0;
|
||||
font-size: 90%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.panel-item > .toolbarbutton-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.panel-item > .toolbarbutton-text {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
/* project panel */
|
||||
|
||||
.project-panel-item-newapp,
|
||||
.project-panel-item-openpackaged,
|
||||
.project-panel-item-openhosted {
|
||||
list-style-image: url("icons.png");
|
||||
}
|
||||
|
||||
.project-panel-item-newapp { -moz-image-region: rect(0px, 640px, 40px, 600px) }
|
||||
.project-panel-item-openpackaged { -moz-image-region: rect(0px, 740px, 40px, 700px) }
|
||||
.project-panel-item-openhosted { -moz-image-region: rect(0px, 840px, 40px, 800px) }
|
||||
|
||||
/* runtime panel */
|
||||
|
||||
#runtime-panel .panel-arrowcontent {
|
||||
padding: 12px 0 0;
|
||||
}
|
||||
|
||||
#runtime-panel-simulators {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#runtime-permissions,
|
||||
#runtime-screenshot,
|
||||
.runtime-panel-item-usbruntime,
|
||||
.runtime-panel-item-simulator {
|
||||
list-style-image: url("icons.png");
|
||||
}
|
||||
|
||||
#runtime-screenshot { -moz-image-region: rect(200px, 640px, 240px, 600px) }
|
||||
#runtime-permissions { -moz-image-region: rect(100px, 840px, 140px, 800px) }
|
||||
.runtime-panel-item-usbruntime { -moz-image-region: rect(100px, 640px, 140px, 600px) }
|
||||
.runtime-panel-item-simulator { -moz-image-region: rect(100px, 740px, 140px, 700px) }
|
||||
|
||||
#runtime-actions {
|
||||
border-top: 1px solid rgba(221,221,221,1);
|
||||
}
|
||||
|
||||
|
||||
#runtime-actions > toolbarbutton {
|
||||
border-top: 1px solid rgba(221,221,221,1);
|
||||
background-color: rgba(233,233,233,1);
|
||||
color: rgba(87,87,87,1);
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
#runtime-actions > toolbarbutton:hover {
|
||||
background-color: rgba(221,221,221,1);
|
||||
}
|
||||
|
||||
#runtime-actions > toolbarbutton:last-child {
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
|
||||
/* Main view */
|
||||
|
||||
#body {
|
||||
background-color: rgb(225, 225, 225);
|
||||
background-image: url('chrome://browser/skin/devtools/app-manager/rocket.svg'), url('chrome://browser/skin/devtools/app-manager/noise.png');
|
||||
background-repeat: no-repeat, repeat;
|
||||
background-size: 35%, auto;
|
||||
background-position: center center, top left;
|
||||
%ifndef XP_MACOSX
|
||||
border-top: 1px solid #AAA;
|
||||
%endif
|
||||
}
|
||||
|
||||
.devtools-horizontal-splitter {
|
||||
-moz-appearance: none;
|
||||
background-image: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid rgba(118, 121, 125, .5);
|
||||
min-height: 3px;
|
||||
height: 3px;
|
||||
margin-top: -3px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
/* Logs */
|
||||
|
||||
#logs-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#logs {
|
||||
overflow: auto;
|
||||
padding: 0 6px;
|
||||
margin: 0;
|
||||
line-height: 12px;
|
||||
font-size: 8px;
|
||||
background-color: #1F1F1F;
|
||||
color: #8fa1b2;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#logs.expand {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
#logs:not(.expand) > .log {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toggle-logs {
|
||||
height: 24px;
|
||||
border-width: 0;
|
||||
background-color: #0881C7;
|
||||
color: white;
|
||||
margin: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#logs > p { margin: 0; }
|
||||
#logs > .warning { color: #d99b28; }
|
||||
#logs > .error { color: #eb5368; }
|
||||
#logs > .success { color: #70bf53; }
|
6
browser/devtools/webide/webide-prefs.js
Normal file
@ -0,0 +1,6 @@
|
||||
# -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
# 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/.
|
||||
|
||||
pref("devtools.webide.templatesURL", "http://fixme/");
|
@ -53,7 +53,7 @@
|
||||
- for editing commands in text inputs. -->
|
||||
<!ENTITY btnPageNet.accesskeyMacOSX "t">
|
||||
<!ENTITY btnPageCSS.label "CSS">
|
||||
<!ENTITY btnPageCSS.tooltip "Log CSS errors and warnings">
|
||||
<!ENTITY btnPageCSS.tooltip2 "Log CSS errors and warnings">
|
||||
<!ENTITY btnPageCSS.accesskey "C">
|
||||
<!ENTITY btnPageJS.label "JS">
|
||||
<!ENTITY btnPageJS.tooltip "Log JavaScript exceptions">
|
||||
|
@ -27,3 +27,8 @@
|
||||
<!ENTITY chooseLanguage.label "Choose your preferred language for displaying pages">
|
||||
<!ENTITY chooseButton.label "Choose…">
|
||||
<!ENTITY chooseButton.accesskey "o">
|
||||
|
||||
<!ENTITY translateWebPages.label "Translate web content">
|
||||
<!ENTITY translateWebPages.accesskey "T">
|
||||
<!ENTITY translateExceptions.label "Exceptions…">
|
||||
<!ENTITY translateExceptions.accesskey "x">
|
||||
|
@ -0,0 +1,24 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
<!ENTITY window.title "Exceptions - Translation">
|
||||
<!ENTITY window.width "36em">
|
||||
<!ENTITY windowClose.key "w">
|
||||
|
||||
<!ENTITY noTranslationForLanguages.label "Translation will not be offered for the following languages:">
|
||||
<!ENTITY treehead.languageName.label "Languages">
|
||||
<!ENTITY removeLanguage.label "Remove Language">
|
||||
<!ENTITY removeLanguage.accesskey "R">
|
||||
<!ENTITY removeAllLanguages.label "Remove All Languages">
|
||||
<!ENTITY removeAllLanguages.accesskey "e">
|
||||
|
||||
<!ENTITY noTranslationForSites.label "Translation will not be offered for the following sites:">
|
||||
<!ENTITY treehead.siteName.label "Sites">
|
||||
<!ENTITY removeSite.label "Remove Site">
|
||||
<!ENTITY removeSite.accesskey "S">
|
||||
<!ENTITY removeAllSites.label "Remove All Sites">
|
||||
<!ENTITY removeAllSites.accesskey "i">
|
||||
|
||||
<!ENTITY button.close.label "Close">
|
||||
<!ENTITY button.close.accesskey "C">
|
@ -45,3 +45,12 @@
|
||||
<!ENTITY translation.tryAgain.button "Try Again">
|
||||
|
||||
<!ENTITY translation.options.menu "Options">
|
||||
<!-- LOCALIZATION NOTE (translation.options.neverForSite.accesskey,
|
||||
- translation.options.preferences.accesskey):
|
||||
- The accesskey values used here should not clash with the value used for
|
||||
- translation.options.neverForLanguage.accesskey in translation.properties
|
||||
-->
|
||||
<!ENTITY translation.options.neverForSite.label "Never translate this site">
|
||||
<!ENTITY translation.options.neverForSite.accesskey "e">
|
||||
<!ENTITY translation.options.preferences.label "Translation preferences">
|
||||
<!ENTITY translation.options.preferences.accesskey "T">
|
||||
|
12
browser/locales/en-US/chrome/browser/translation.properties
Normal file
@ -0,0 +1,12 @@
|
||||
# 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/.
|
||||
|
||||
# LOCALIZATION NOTE (translation.options.neverForLanguage.label):
|
||||
# %S is a language name coming from the global/languageNames.properties file.
|
||||
translation.options.neverForLanguage.label=Never translate %S
|
||||
|
||||
# LOCALIZATION NOTE (translation.options.neverForLanguage.accesskey):
|
||||
# The accesskey value used here should not clash with the values used for
|
||||
# translation.options.*.accesskey in translation.dtd
|
||||
translation.options.neverForLanguage.accesskey=N
|
@ -82,6 +82,7 @@
|
||||
locale/browser/tabview.properties (%chrome/browser/tabview.properties)
|
||||
locale/browser/taskbar.properties (%chrome/browser/taskbar.properties)
|
||||
locale/browser/translation.dtd (%chrome/browser/translation.dtd)
|
||||
locale/browser/translation.properties (%chrome/browser/translation.properties)
|
||||
locale/browser/downloads/downloads.dtd (%chrome/browser/downloads/downloads.dtd)
|
||||
locale/browser/downloads/downloads.properties (%chrome/browser/downloads/downloads.properties)
|
||||
locale/browser/places/places.dtd (%chrome/browser/places/places.dtd)
|
||||
@ -120,6 +121,7 @@
|
||||
locale/browser/preferences/sync.dtd (%chrome/browser/preferences/sync.dtd)
|
||||
#endif
|
||||
locale/browser/preferences/tabs.dtd (%chrome/browser/preferences/tabs.dtd)
|
||||
locale/browser/preferences/translation.dtd (%chrome/browser/preferences/translation.dtd)
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
locale/browser/syncBrand.dtd (%chrome/browser/syncBrand.dtd)
|
||||
locale/browser/syncSetup.dtd (%chrome/browser/syncSetup.dtd)
|
||||
|
@ -158,8 +158,8 @@ browser.jar:
|
||||
skin/classic/browser/social/chat-icons.png (social/chat-icons.png)
|
||||
skin/classic/browser/social/gear_default.png (../shared/social/gear_default.png)
|
||||
skin/classic/browser/social/gear_clicked.png (../shared/social/gear_clicked.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (../shared/tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/tab.png (tabbrowser/tab.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-background-end.png (tabbrowser/tab-background-end.png)
|
||||
|
BIN
browser/themes/linux/tabbrowser/connecting.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
browser/themes/linux/tabbrowser/loading.png
Normal file
After Width: | Height: | Size: 14 KiB |
@ -2897,10 +2897,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.tab-close-button > .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.tab-close-button.close-icon:not([selected=true]):not(:hover):-moz-lwtheme-brighttext {
|
||||
-moz-image-region: rect(0, 128px, 32px, 96px);
|
||||
}
|
||||
@ -4342,10 +4338,6 @@ window > chatbox {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.customization-tipPanel-closeBox > .close-icon > .toolbarbutton-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.customization-tipPanel-infoBox {
|
||||
background-image: url(chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png);
|
||||
background-size: 25px 25px;
|
||||
|
@ -256,10 +256,10 @@ browser.jar:
|
||||
skin/classic/browser/tabbrowser/newtab@2x.png (tabbrowser/newtab@2x.png)
|
||||
skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
|
||||
skin/classic/browser/tabbrowser/newtab-inverted@2x.png (tabbrowser/newtab-inverted@2x.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/connecting@2x.png (../shared/tabbrowser/connecting@2x.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (../shared/tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/loading@2x.png (../shared/tabbrowser/loading@2x.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/connecting@2x.png (tabbrowser/connecting@2x.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/loading@2x.png (tabbrowser/loading@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
|
||||
skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png)
|
||||
|
BIN
browser/themes/osx/tabbrowser/connecting.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
browser/themes/osx/tabbrowser/connecting@2x.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
browser/themes/osx/tabbrowser/loading.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
browser/themes/osx/tabbrowser/loading@2x.png
Normal file
After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -926,20 +926,23 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
||||
background-clip: padding-box !important;
|
||||
background-color: hsla(210,25%,98%,.08) !important;
|
||||
padding: 6px !important;
|
||||
border-color: hsla(210,4%,10%,.25) !important;
|
||||
transition-property: background-color, border-color !important;
|
||||
border-style: none !important;
|
||||
box-shadow: 0 1px 0 0 hsla(210,4%,10%,.25),
|
||||
0 0 0 1px hsla(210,4%,10%,.25);
|
||||
transition-property: background-color, box-shadow !important;
|
||||
transition-duration: 250ms !important;
|
||||
}
|
||||
|
||||
#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
|
||||
background-color: hsla(210,4%,10%,.08) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
#back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
|
||||
#back-button[open="true"] > .toolbarbutton-icon {
|
||||
background-color: hsla(210,4%,10%,.12) !important;
|
||||
box-shadow: 0 1px 0 0 hsla(210,80%,20%,.1) inset !important;
|
||||
box-shadow: 0 1px 0 0 hsla(210,4%,10%,.25),
|
||||
0 0 0 1px hsla(210,4%,10%,.25),
|
||||
0 1px 0 0 hsla(210,80%,20%,.1) inset !important;
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@ -947,13 +950,11 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
||||
(-moz-os-version: windows-win7) {
|
||||
%endif
|
||||
#back-button > .toolbarbutton-icon {
|
||||
border: none !important;
|
||||
background-image: linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1)) !important;
|
||||
box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(0,0%,100%,.3) inset,
|
||||
0 0 0 1px hsla(210,54%,20%,.25),
|
||||
0 1px 0 hsla(210,54%,20%,.35) !important;
|
||||
transition-property: background-color, box-shadow !important;
|
||||
}
|
||||
|
||||
#back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
|
||||
|
@ -183,8 +183,8 @@ browser.jar:
|
||||
skin/classic/browser/social/gear_clicked.png (../shared/social/gear_clicked.png)
|
||||
skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab.png)
|
||||
skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (../shared/tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
|
||||
skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png)
|
||||
skin/classic/browser/tabbrowser/tab.png (tabbrowser/tab.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
|
||||
@ -555,8 +555,8 @@ browser.jar:
|
||||
skin/classic/aero/browser/social/gear_clicked.png (../shared/social/gear_clicked.png)
|
||||
skin/classic/aero/browser/tabbrowser/newtab.png (tabbrowser/newtab.png)
|
||||
skin/classic/aero/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png)
|
||||
skin/classic/aero/browser/tabbrowser/connecting.png (../shared/tabbrowser/connecting.png)
|
||||
skin/classic/aero/browser/tabbrowser/loading.png (../shared/tabbrowser/loading.png)
|
||||
skin/classic/aero/browser/tabbrowser/connecting.png (tabbrowser/connecting.png)
|
||||
skin/classic/aero/browser/tabbrowser/loading.png (tabbrowser/loading.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab.png (tabbrowser/tab.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-active-middle.png (tabbrowser/tab-active-middle.png)
|
||||
skin/classic/aero/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
|
||||
|
BIN
browser/themes/windows/tabbrowser/connecting.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
browser/themes/windows/tabbrowser/loading.png
Normal file
After Width: | Height: | Size: 10 KiB |
13
configure.in
@ -7640,6 +7640,19 @@ if test "$MOZ_CHROME_FILE_FORMAT" != "jar" &&
|
||||
AC_MSG_ERROR([--enable-chrome-format must be set to either jar, flat, or omni])
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Enable Support for devtools webide
|
||||
dnl ========================================================
|
||||
MOZ_ARG_ENABLE_BOOL(devtools-webide,
|
||||
[ --enable-devtools-webide Set compile flags necessary for compiling devtools webide ],
|
||||
MOZ_DEVTOOLS_WEBIDE=1,
|
||||
MOZ_DEVTOOLS_WEBIDE= )
|
||||
|
||||
if test -n "$MOZ_DEVTOOLS_WEBIDE"; then
|
||||
AC_DEFINE(MOZ_DEVTOOLS_WEBIDE)
|
||||
fi
|
||||
AC_SUBST(MOZ_DEVTOOLS_WEBIDE)
|
||||
|
||||
dnl =========================================================
|
||||
dnl Omnijar packaging (bug 552121)
|
||||
dnl =========================================================
|
||||
|
@ -89,7 +89,52 @@ function runTests() {
|
||||
}
|
||||
|
||||
const NDEF = {
|
||||
TNF_WELL_KNOWN: 1
|
||||
TNF_WELL_KNOWN: 1,
|
||||
|
||||
// compares two NDEF messages
|
||||
compare: function(ndef1, ndef2) {
|
||||
isnot(ndef1, null, "LHS message is not null");
|
||||
isnot(ndef2, null, "RHS message is not null");
|
||||
is(ndef1.length, ndef2.length,
|
||||
"NDEF messages have the same number of records");
|
||||
ndef1.forEach(function(record1, index) {
|
||||
let record2 = this[index];
|
||||
is(record1.tnf, record2.tnf, "test for equal TNF fields");
|
||||
let fields = ["type", "id", "payload"];
|
||||
fields.forEach(function(value) {
|
||||
let field1 = record1[value];
|
||||
let field2 = record2[value];
|
||||
is(field1.length, field2.length,
|
||||
value + " fields have the same length");
|
||||
let eq = true;
|
||||
for (let i = 0; eq && i < field1.length; ++i) {
|
||||
eq = (field1[i] === field2[i]);
|
||||
}
|
||||
ok(eq, value + " fields contain the same data");
|
||||
});
|
||||
}, ndef2);
|
||||
},
|
||||
|
||||
// parses an emulator output string into an NDEF message
|
||||
parseString: function(str) {
|
||||
// make it an object
|
||||
let arr = null;
|
||||
try {
|
||||
arr = JSON.parse(str);
|
||||
} catch (e) {
|
||||
ok(false, "Parser error: " + e.message);
|
||||
return null;
|
||||
}
|
||||
// and build NDEF array
|
||||
let ndef = arr.map(function(value) {
|
||||
let type = new Uint8Array(NfcUtils.fromUTF8(this.atob(value.type)));
|
||||
let id = new Uint8Array(NfcUtils.fromUTF8(this.atob(value.id)));
|
||||
let payload =
|
||||
new Uint8Array(NfcUtils.fromUTF8(this.atob(value.payload)));
|
||||
return new MozNDEFRecord(value.tnf, type, id, payload);
|
||||
}, window);
|
||||
return ndef;
|
||||
}
|
||||
};
|
||||
|
||||
var NfcUtils = {
|
||||
|
@ -6,46 +6,6 @@ MARIONETTE_HEAD_JS = "head.js";
|
||||
|
||||
let url = "https://www.example.com";
|
||||
|
||||
function compareNDEFs(ndef1, ndef2) {
|
||||
is(ndef1.length, ndef2.length,
|
||||
"NDEF messages have the same number of records");
|
||||
ndef1.forEach(function(record1, index) {
|
||||
let record2 = this[index];
|
||||
is(record1.tnf, record2.tnf, "test for equal TNF fields");
|
||||
let fields = ["type", "id", "payload"];
|
||||
fields.forEach(function(value) {
|
||||
let field1 = record1[value];
|
||||
let field2 = record2[value];
|
||||
is(field1.length, field2.length,
|
||||
value + " fields have the same length");
|
||||
let eq = true;
|
||||
for (let i = 0; eq && i < field1.length; ++i) {
|
||||
eq = (field1[i] === field2[i]);
|
||||
}
|
||||
ok(eq, value + " fields contain the same data");
|
||||
});
|
||||
}, ndef2);
|
||||
}
|
||||
|
||||
function parseNDEFString(str) {
|
||||
/* make it an object */
|
||||
let arr = null;
|
||||
try {
|
||||
arr = JSON.parse(str);
|
||||
} catch (e) {
|
||||
ok(false, "Parser error: " + e.message);
|
||||
return null;
|
||||
}
|
||||
/* and build NDEF array */
|
||||
let ndef = arr.map(function(value) {
|
||||
let type = new Uint8Array(NfcUtils.fromUTF8(this.atob(value.type)));
|
||||
let id = new Uint8Array(NfcUtils.fromUTF8(this.atob(value.id)));
|
||||
let payload = new Uint8Array(NfcUtils.fromUTF8(this.atob(value.payload)));
|
||||
return new MozNDEFRecord(value.tnf, type, id, payload);
|
||||
}, window);
|
||||
return ndef;
|
||||
}
|
||||
|
||||
function sendNDEF(techType, sessionToken) {
|
||||
let tnf = NDEF.TNF_WELL_KNOWN;
|
||||
let type = new Uint8Array(NfcUtils.fromUTF8("U"));
|
||||
@ -62,8 +22,7 @@ function sendNDEF(techType, sessionToken) {
|
||||
log("Executing \'" + cmd + "\'");
|
||||
emulator.run(cmd, function(result) {
|
||||
is(result.pop(), "OK", "check SNEP PUT result");
|
||||
let ndef2 = parseNDEFString(result.pop());
|
||||
compareNDEFs(ndef, ndef2);
|
||||
NDEF.compare(ndef, NDEF.parseString(result.pop()));
|
||||
toggleNFC(false, runNextTest);
|
||||
});
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
package org.mozilla.gecko.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.db.BrowserContract.ExpirePriority;
|
||||
@ -160,6 +161,13 @@ public class BrowserDB {
|
||||
return sDb.filter(cr, constraint, limit);
|
||||
}
|
||||
|
||||
private static void appendUrlsFromCursor(List<String> urls, Cursor c) {
|
||||
c.moveToPosition(-1);
|
||||
while (c.moveToNext()) {
|
||||
urls.add(c.getString(c.getColumnIndex(URLColumns.URL)));
|
||||
};
|
||||
}
|
||||
|
||||
public static Cursor getTopSites(ContentResolver cr, int minLimit, int maxLimit) {
|
||||
// Note this is not a single query anymore, but actually returns a mixture
|
||||
// of two queries, one for topSites and one for pinned sites.
|
||||
@ -167,12 +175,17 @@ public class BrowserDB {
|
||||
|
||||
int pinnedCount = pinnedSites.getCount();
|
||||
Cursor topSites = sDb.getTopSites(cr, maxLimit - pinnedCount);
|
||||
int topCount = topSites.getCount();
|
||||
|
||||
Cursor suggestedSites = null;
|
||||
if (sSuggestedSites != null) {
|
||||
final int count = minLimit - pinnedCount - topSites.getCount();
|
||||
final int count = minLimit - pinnedCount - topCount;
|
||||
if (count > 0) {
|
||||
suggestedSites = sSuggestedSites.get(count);
|
||||
final List<String> excludeUrls = new ArrayList<String>(pinnedCount + topCount);
|
||||
appendUrlsFromCursor(excludeUrls, pinnedSites);
|
||||
appendUrlsFromCursor(excludeUrls, topSites);
|
||||
|
||||
suggestedSites = sSuggestedSites.get(count, excludeUrls);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,6 +156,16 @@ public class SuggestedSites {
|
||||
* @param limit maximum number of suggested sites.
|
||||
*/
|
||||
public Cursor get(int limit) {
|
||||
return get(limit, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Cursor} with the list of suggested websites.
|
||||
*
|
||||
* @param limit maximum number of suggested sites.
|
||||
* @param excludeUrls list of URLs to be excluded from the list.
|
||||
*/
|
||||
public Cursor get(int limit, List<String> excludeUrls) {
|
||||
List<Site> sites = cachedSites.get();
|
||||
if (sites == null) {
|
||||
Log.d(LOGTAG, "No cached sites, refreshing.");
|
||||
@ -177,6 +187,10 @@ public class SuggestedSites {
|
||||
for (int i = 0; i < count; i++) {
|
||||
final Site site = sites.get(i);
|
||||
|
||||
if (excludeUrls != null && excludeUrls.contains(site.url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final RowBuilder row = cursor.newRow();
|
||||
row.add(-1);
|
||||
row.add(site.url);
|
||||
|
@ -157,25 +157,6 @@ public class TopSitesCursorWrapper implements Cursor {
|
||||
return (c != null && !c.isBeforeFirst() && !c.isAfterLast());
|
||||
}
|
||||
|
||||
private void updateRowState() {
|
||||
if (cursorHasValidPosition(pinnedCursor)) {
|
||||
currentRowType = RowType.PINNED;
|
||||
currentCursor = pinnedCursor;
|
||||
} else if (cursorHasValidPosition(topCursor)) {
|
||||
currentRowType = RowType.TOP;
|
||||
currentCursor = topCursor;
|
||||
} else if (cursorHasValidPosition(suggestedCursor)) {
|
||||
currentRowType = RowType.SUGGESTED;
|
||||
currentCursor = suggestedCursor;
|
||||
} else if (currentPosition >= 0 && currentPosition < minSize) {
|
||||
currentRowType = RowType.BLANK;
|
||||
currentCursor = null;
|
||||
} else {
|
||||
currentRowType = RowType.UNKNOWN;
|
||||
currentCursor = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePinnedBefore(int position) {
|
||||
pinnedBefore = 0;
|
||||
for (int i = 0; i < position; i++) {
|
||||
@ -185,40 +166,51 @@ public class TopSitesCursorWrapper implements Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTopCursorPosition(int position) {
|
||||
// Move the real cursor as if we were stepping through it to this position.
|
||||
// Account for pinned sites, and be careful to update its position to the
|
||||
// minimum or maximum position, even if we're moving beyond its bounds.
|
||||
final int actualPosition = position - pinnedBefore;
|
||||
|
||||
if (actualPosition <= -1) {
|
||||
topCursor.moveToPosition(-1);
|
||||
} else if (actualPosition >= topCursor.getCount()) {
|
||||
topCursor.moveToPosition(topCursor.getCount());
|
||||
} else {
|
||||
topCursor.moveToPosition(actualPosition);
|
||||
}
|
||||
private void setCurrentCursor(Cursor cursor, int position, RowType rowType) {
|
||||
cursor.moveToPosition(position);
|
||||
currentRowType = rowType;
|
||||
currentCursor = cursor;
|
||||
}
|
||||
|
||||
private void updatePinnedCursorPosition(int position) {
|
||||
private boolean updateTopCursorPosition(int position) {
|
||||
final int index = position - pinnedBefore;
|
||||
if (index >= 0 && index < topCursor.getCount()) {
|
||||
setCurrentCursor(topCursor, index, RowType.TOP);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean updatePinnedCursorPosition(int position) {
|
||||
if (position >= minSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pinnedPositions.get(position)) {
|
||||
pinnedCursor.moveToPosition(pinnedPositions.indexOfKey(position));
|
||||
} else {
|
||||
pinnedCursor.moveToPosition(-1);
|
||||
setCurrentCursor(pinnedCursor, pinnedPositions.indexOfKey(position), RowType.PINNED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateSuggestedCursorPosition(int position) {
|
||||
private boolean updateSuggestedCursorPosition(int position) {
|
||||
if (suggestedCursor == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
final int index = Math.max(-1, position - pinnedBefore - topCursor.getCount());
|
||||
if (index < suggestedCursor.getCount()) {
|
||||
suggestedCursor.moveToPosition(index);
|
||||
} else {
|
||||
suggestedCursor.moveToPosition(-1);
|
||||
if (position >= minSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int index = position - pinnedBefore - topCursor.getCount();
|
||||
if (index >= 0 && index < suggestedCursor.getCount()) {
|
||||
setCurrentCursor(suggestedCursor, index, RowType.SUGGESTED);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void assertValidColumnIndex(int columnIndex) {
|
||||
@ -318,12 +310,19 @@ public class TopSitesCursorWrapper implements Cursor {
|
||||
@Override
|
||||
public boolean moveToPosition(int position) {
|
||||
currentPosition = position;
|
||||
|
||||
updatePinnedBefore(position);
|
||||
updatePinnedCursorPosition(position);
|
||||
updateTopCursorPosition(position);
|
||||
updateSuggestedCursorPosition(position);
|
||||
updateRowState();
|
||||
|
||||
if (!updatePinnedCursorPosition(position) &&
|
||||
!updateTopCursorPosition(position) &&
|
||||
!updateSuggestedCursorPosition(position)) {
|
||||
|
||||
if (position >= 0 && position < minSize) {
|
||||
currentRowType = RowType.BLANK;
|
||||
} else {
|
||||
currentRowType = RowType.UNKNOWN;
|
||||
}
|
||||
currentCursor = null;
|
||||
}
|
||||
|
||||
return cursorHasValidPosition(this);
|
||||
}
|
||||
|
@ -63,6 +63,9 @@ public class DynamicPanel extends HomeFragment {
|
||||
// Dataset ID to be used by the loader
|
||||
private static final String DATASET_REQUEST = "dataset_request";
|
||||
|
||||
// Max number of items to display in the panel
|
||||
private static final int RESULT_LIMIT = 100;
|
||||
|
||||
// The main view for this fragment. This contains the PanelLayout and PanelAuthLayout.
|
||||
private FrameLayout mView;
|
||||
|
||||
@ -381,6 +384,8 @@ public class DynamicPanel extends HomeFragment {
|
||||
final Uri queryUri = HomeItems.CONTENT_URI.buildUpon()
|
||||
.appendQueryParameter(BrowserContract.PARAM_DATASET_ID,
|
||||
mRequest.getDatasetId())
|
||||
.appendQueryParameter(BrowserContract.PARAM_LIMIT,
|
||||
String.valueOf(RESULT_LIMIT))
|
||||
.build();
|
||||
|
||||
// XXX: You can use HomeItems.CONTENT_FAKE_URI for development
|
||||
|
@ -78,9 +78,6 @@ class PanelItemView extends LinearLayout {
|
||||
private ArticleItemView(Context context) {
|
||||
super(context, R.layout.panel_article_item);
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
final int padding = getResources().getDimensionPixelSize(R.dimen.article_item_view_padding);
|
||||
setPadding(0, padding, 0, padding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,16 +6,15 @@
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<ImageView android:id="@+id/image"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_marginLeft="10dip"
|
||||
android:layout_width="@dimen/panel_article_item_height"
|
||||
android:layout_height="@dimen/panel_article_item_height"
|
||||
android:scaleType="centerCrop"/>
|
||||
|
||||
<LinearLayout android:id="@+id/title_desc_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="10dip"
|
||||
android:paddingRight="10dip"
|
||||
android:layout_height="@dimen/panel_article_item_height"
|
||||
android:paddingLeft="15dip"
|
||||
android:paddingRight="15dip"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
@ -28,6 +27,7 @@
|
||||
style="@style/Widget.PanelItemView.Description"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:maxLength="1024"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -108,6 +108,6 @@
|
||||
<!-- PanelGridView dimensions -->
|
||||
<dimen name="panel_grid_view_column_width">150dp</dimen>
|
||||
|
||||
<!-- ArticleItemView dimensions -->
|
||||
<dimen name="article_item_view_padding">15dp</dimen>
|
||||
<!-- PanelItemView dimensions -->
|
||||
<dimen name="panel_article_item_height">95dp</dimen>
|
||||
</resources>
|
||||
|
@ -364,7 +364,7 @@
|
||||
<style name="TextAppearance.Widget.Home.ItemTitle" parent="TextAppearance.Medium"/>
|
||||
|
||||
<style name="TextAppearance.Widget.Home.ItemDescription" parent="TextAppearance.Micro">
|
||||
<item name="android:textColor">?android:attr/textColorSecondary</item>
|
||||
<item name="android:textColor">#AFB1B3</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Widget.HomeBanner" parent="TextAppearance.Small">
|
||||
|
@ -10,26 +10,18 @@ import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.NativeEventListener;
|
||||
import org.mozilla.gecko.util.NativeJSObject;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
@ -37,12 +29,11 @@ import android.widget.LinearLayout;
|
||||
import java.util.UUID;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PageActionLayout extends LinearLayout implements GeckoEventListener,
|
||||
public class PageActionLayout extends LinearLayout implements NativeEventListener,
|
||||
View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
private final String LOGTAG = "GeckoPageActionLayout";
|
||||
private final String MENU_BUTTON_KEY = "MENU_BUTTON_KEY";
|
||||
private final int DEFAULT_PAGE_ACTIONS_SHOWN = 2;
|
||||
private static final String MENU_BUTTON_KEY = "MENU_BUTTON_KEY";
|
||||
private static final int DEFAULT_PAGE_ACTIONS_SHOWN = 2;
|
||||
|
||||
private ArrayList<PageAction> mPageActionList;
|
||||
private GeckoPopupMenu mPageActionsMenu;
|
||||
@ -83,33 +74,29 @@ public class PageActionLayout extends LinearLayout implements GeckoEventListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
try {
|
||||
if (event.equals("PageActions:Add")) {
|
||||
final String id = message.getString("id");
|
||||
final String title = message.getString("title");
|
||||
final String imageURL = message.optString("icon");
|
||||
final boolean mImportant = message.getBoolean("important");
|
||||
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
|
||||
if (event.equals("PageActions:Add")) {
|
||||
final String id = message.getString("id");
|
||||
final String title = message.getString("title");
|
||||
final String imageURL = message.optString("icon", null);
|
||||
final boolean mImportant = message.getBoolean("important");
|
||||
|
||||
addPageAction(id, title, imageURL, new OnPageActionClickListeners() {
|
||||
@Override
|
||||
public void onClick(String id) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PageActions:Clicked", id));
|
||||
}
|
||||
addPageAction(id, title, imageURL, new OnPageActionClickListeners() {
|
||||
@Override
|
||||
public void onClick(String id) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PageActions:Clicked", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(String id) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PageActions:LongClicked", id));
|
||||
return true;
|
||||
}
|
||||
}, mImportant);
|
||||
} else if (event.equals("PageActions:Remove")) {
|
||||
final String id = message.getString("id");
|
||||
@Override
|
||||
public boolean onLongClick(String id) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("PageActions:LongClicked", id));
|
||||
return true;
|
||||
}
|
||||
}, mImportant);
|
||||
} else if (event.equals("PageActions:Remove")) {
|
||||
final String id = message.getString("id");
|
||||
|
||||
removePageAction(id);
|
||||
}
|
||||
} catch(JSONException ex) {
|
||||
Log.e(LOGTAG, "Error deocding", ex);
|
||||
removePageAction(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
*/
|
||||
const SCHEMA_VERSION = 2;
|
||||
|
||||
// The maximum number of items you can attempt to save at once.
|
||||
const MAX_SAVE_COUNT = 100;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DB_PATH", function() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
|
||||
});
|
||||
@ -281,21 +284,65 @@ function validateItem(datasetId, item) {
|
||||
}
|
||||
}
|
||||
|
||||
var gRefreshTimers = {};
|
||||
|
||||
/**
|
||||
* Sends a message to Java to refresh the given dataset. Delays sending
|
||||
* messages to avoid successive refreshes, which can result in flashing views.
|
||||
*/
|
||||
function refreshDataset(datasetId) {
|
||||
// Bail if there's already a refresh timer waiting to fire
|
||||
if (gRefreshTimers[datasetId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.initWithCallback(function(timer) {
|
||||
delete gRefreshTimers[datasetId];
|
||||
|
||||
sendMessageToJava({
|
||||
type: "HomePanels:RefreshDataset",
|
||||
datasetId: datasetId
|
||||
});
|
||||
}, 100, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
|
||||
gRefreshTimers[datasetId] = timer;
|
||||
}
|
||||
|
||||
HomeStorage.prototype = {
|
||||
/**
|
||||
* Saves data rows to the DB.
|
||||
*
|
||||
* @param data
|
||||
* (array) JSON array of row items
|
||||
* An array of JS objects represnting row items to save.
|
||||
* Each object may have the following properties:
|
||||
* - url (string)
|
||||
* - title (string)
|
||||
* - description (string)
|
||||
* - image_url (string)
|
||||
* - filter (string)
|
||||
* @param options
|
||||
* A JS object holding additional cofiguration properties.
|
||||
* The following properties are currently supported:
|
||||
* - replace (boolean): Whether or not to replace existing items.
|
||||
*
|
||||
* @return Promise
|
||||
* @resolves When the operation has completed.
|
||||
*/
|
||||
save: function(data) {
|
||||
save: function(data, options) {
|
||||
if (data && data.length > MAX_SAVE_COUNT) {
|
||||
throw "save failed for dataset = " + this.datasetId +
|
||||
": you cannot save more than " + MAX_SAVE_COUNT + " items at once";
|
||||
}
|
||||
|
||||
return Task.spawn(function save_task() {
|
||||
let db = yield getDatabaseConnection();
|
||||
try {
|
||||
yield db.executeTransaction(function save_transaction() {
|
||||
if (options && options.replace) {
|
||||
yield db.executeCached(SQL.deleteFromDataset, { dataset_id: this.datasetId });
|
||||
}
|
||||
|
||||
// Insert data into DB.
|
||||
for (let item of data) {
|
||||
validateItem(this.datasetId, item);
|
||||
@ -317,10 +364,7 @@ HomeStorage.prototype = {
|
||||
yield db.close();
|
||||
}
|
||||
|
||||
sendMessageToJava({
|
||||
type: "HomePanels:RefreshDataset",
|
||||
datasetId: this.datasetId,
|
||||
});
|
||||
refreshDataset(this.datasetId);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
@ -340,10 +384,7 @@ HomeStorage.prototype = {
|
||||
yield db.close();
|
||||
}
|
||||
|
||||
sendMessageToJava({
|
||||
type: "HomePanels:RefreshDataset",
|
||||
datasetId: this.datasetId,
|
||||
});
|
||||
refreshDataset(this.datasetId);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
@ -11,6 +11,8 @@ import android.test.mock.MockResources;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -140,4 +142,29 @@ public class TestSuggestedSites extends BrowserTestCase {
|
||||
|
||||
c.close();
|
||||
}
|
||||
|
||||
public void testExcludeUrls() {
|
||||
resources.setSuggestedSitesResource(generateSites(6));
|
||||
|
||||
List<String> excludedUrls = new ArrayList<String>(3);
|
||||
excludedUrls.add("url1");
|
||||
excludedUrls.add("url3");
|
||||
excludedUrls.add("url5");
|
||||
|
||||
List<String> includedUrls = new ArrayList<String>(3);
|
||||
includedUrls.add("url0");
|
||||
includedUrls.add("url2");
|
||||
includedUrls.add("url4");
|
||||
|
||||
Cursor c = new SuggestedSites(context).get(DEFAULT_LIMIT, excludedUrls);
|
||||
|
||||
c.moveToPosition(-1);
|
||||
while (c.moveToNext()) {
|
||||
String url = c.getString(c.getColumnIndexOrThrow(BrowserContract.SuggestedSites.URL));
|
||||
assertFalse(excludedUrls.contains(url));
|
||||
assertTrue(includedUrls.contains(url));
|
||||
}
|
||||
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ function test()
|
||||
const kPrefName_AutoScroll = "general.autoScroll";
|
||||
Services.prefs.setBoolPref(kPrefName_AutoScroll, false);
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
var doc;
|
||||
|
||||
function startLoad(dataUri) {
|
||||
@ -27,9 +25,6 @@ function test()
|
||||
if (Services.prefs.prefHasUserValue(kPrefName_AutoScroll))
|
||||
Services.prefs.clearUserPref(kPrefName_AutoScroll);
|
||||
|
||||
// cleaning-up
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
// waitForFocus() fixes a failure in the next test if the latter runs too soon.
|
||||
waitForFocus(finish);
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ function test()
|
||||
const kPrefName_AutoScroll = "general.autoScroll";
|
||||
Services.prefs.setBoolPref(kPrefName_AutoScroll, true);
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
|
||||
const expectScrollNone = 0;
|
||||
const expectScrollVert = 1;
|
||||
const expectScrollHori = 2;
|
||||
@ -126,9 +124,6 @@ function test()
|
||||
if (Services.prefs.prefHasUserValue(kPrefName_AutoScroll))
|
||||
Services.prefs.clearUserPref(kPrefName_AutoScroll);
|
||||
|
||||
// cleaning-up
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
// waitForFocus() fixes a failure in the next test if the latter runs too soon.
|
||||
waitForFocus(finish);
|
||||
}
|
||||
|