author | Wes Kocher <wkocher@mozilla.com> |
Wed, 15 Mar 2017 14:24:58 -0700 | |
changeset 347872 | ff04d410e74b69acfab17ef7e73e7397602d5a68 |
parent 347871 | 1c4d97d1bc61468c82e55a09bab660e52bb2e9b5 (current diff) |
parent 347870 | f44a3f36c9eeea0456dfb80274ed80311a1df249 (diff) |
child 347873 | 0b51e2a9d7810cc12363afe41aef50a089cbca10 |
child 347937 | dd2f2db1f87aa31e98d204a707dcfd6457a6c696 |
child 347948 | dd0376458483b07b48676442a29022591148136b |
push id | 38951 |
push user | kwierso@gmail.com |
push date | Wed, 15 Mar 2017 21:29:40 +0000 |
treeherder | autoland@0b51e2a9d781 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 55.0a1 |
first release with | nightly linux32
ff04d410e74b
/
55.0a1
/
20170316110253
/
files
nightly linux64
ff04d410e74b
/
55.0a1
/
20170316110253
/
files
nightly mac
ff04d410e74b
/
55.0a1
/
20170316030211
/
files
nightly win32
ff04d410e74b
/
55.0a1
/
20170316030211
/
files
nightly win64
ff04d410e74b
/
55.0a1
/
20170316030211
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
55.0a1
/
20170316110253
/
pushlog to previous
nightly linux64
55.0a1
/
20170316110253
/
pushlog to previous
nightly mac
55.0a1
/
20170316030211
/
pushlog to previous
nightly win32
55.0a1
/
20170316030211
/
pushlog to previous
nightly win64
55.0a1
/
20170316030211
/
pushlog to previous
|
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js +++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js @@ -137,16 +137,86 @@ add_task(function* testBadPermissions() await browser.pageAction.show(tab.id); }, }); yield BrowserTestUtils.removeTab(tab2); yield BrowserTestUtils.removeTab(tab1); }); +add_task(function* testMatchDataURI() { + const target = ExtensionTestUtils.loadExtension({ + files: { + "page.html": `<!DOCTYPE html> + <meta charset="utf-8"> + <script src="page.js"></script> + <iframe id="inherited" src="data:text/html;charset=utf-8,inherited"></iframe> + `, + "page.js": function() { + browser.test.onMessage.addListener((msg, url) => { + window.location.href = url; + }); + }, + }, + background() { + browser.tabs.create({active: true, url: browser.runtime.getURL("page.html")}); + }, + }); + + const scripts = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["<all_urls>", "webNavigation"], + }, + background() { + browser.webNavigation.onCompleted.addListener(({url, frameId}) => { + browser.test.log(`Document loading complete: ${url}`); + if (frameId === 0) { + browser.test.sendMessage("tab-ready", url); + } + }); + + browser.test.onMessage.addListener(async msg => { + browser.test.assertRejects( + browser.tabs.executeScript({ + code: "location.href;", + allFrames: true, + }), + /No window matching/, + "Should not execute in `data:` frame"); + + browser.test.sendMessage("done"); + }); + }, + }); + + yield scripts.startup(); + yield target.startup(); + + // Test extension page with a data: iframe. + const page = yield scripts.awaitMessage("tab-ready"); + ok(page.endsWith("page.html"), "Extension page loaded into a tab"); + + scripts.sendMessage("execute"); + yield scripts.awaitMessage("done"); + + // Test extension tab navigated to a data: URI. + const data = "data:text/html;charset=utf-8,also-inherits"; + target.sendMessage("navigate", data); + + const url = yield scripts.awaitMessage("tab-ready"); + is(url, data, "Extension tab navigated to a data: URI"); + + scripts.sendMessage("execute"); + yield scripts.awaitMessage("done"); + + yield BrowserTestUtils.removeTab(gBrowser.selectedTab); + yield scripts.unload(); + yield target.unload(); +}); + add_task(function* testBadURL() { async function background() { let promises = [ new Promise(resolve => { browser.tabs.executeScript({ file: "http://example.com/script.js", }, result => { browser.test.assertEq(undefined, result, "Result value");
--- a/devtools/client/inspector/boxmodel/box-model.js +++ b/devtools/client/inspector/boxmodel/box-model.js @@ -293,16 +293,20 @@ BoxModel.prototype = { if (property.substring(0, 7) == "border-") { let bprop = property.substring(0, property.length - 5) + "style"; let style = session.getProperty(bprop); if (!style || style == "none" || style == "hidden") { properties.push({ name: bprop, value: "solid" }); } } + if (property.substring(0, 9) == "position-") { + properties[0].name = property.substring(9); + } + session.setProperties(properties).catch(e => console.error(e)); }, done: (value, commit) => { editor.elt.parentNode.classList.remove("boxmodel-editing"); if (!commit) { session.revert().then(() => { session.destroy(); }, e => console.error(e));
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js +++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js @@ -78,108 +78,179 @@ module.exports = createClass({ value = "auto"; } else if (layout[property]) { value = parseFloat(layout[property]); } return value; }, + getPositionValue(property) { + let { layout } = this.props.boxModel; + + if (layout.position === "static") { + return "-"; + } + return layout[property] ? parseFloat(layout[property]) : "-"; + }, + onHighlightMouseOver(event) { let region = event.target.getAttribute("data-box"); if (!region) { this.props.onHideBoxModelHighlighter(); } this.props.onShowBoxModelHighlighter({ region, showOnly: region, onlyRegionArea: true, }); }, render() { let { boxModel, onShowBoxModelEditor } = this.props; let { layout } = boxModel; - let { height, width } = layout; + let { height, width, position } = layout; let borderTop = this.getBorderOrPaddingValue("border-top-width"); let borderRight = this.getBorderOrPaddingValue("border-right-width"); let borderBottom = this.getBorderOrPaddingValue("border-bottom-width"); let borderLeft = this.getBorderOrPaddingValue("border-left-width"); let paddingTop = this.getBorderOrPaddingValue("padding-top"); let paddingRight = this.getBorderOrPaddingValue("padding-right"); let paddingBottom = this.getBorderOrPaddingValue("padding-bottom"); let paddingLeft = this.getBorderOrPaddingValue("padding-left"); + let displayPosition = layout.position && layout.position != "static"; + let positionTop = this.getPositionValue("top"); + let positionRight = this.getPositionValue("right"); + let positionBottom = this.getPositionValue("bottom"); + let positionLeft = this.getPositionValue("left"); + let marginTop = this.getMarginValue("margin-top", "top"); let marginRight = this.getMarginValue("margin-right", "right"); let marginBottom = this.getMarginValue("margin-bottom", "bottom"); let marginLeft = this.getMarginValue("margin-left", "left"); height = this.getHeightValue(height); width = this.getWidthValue(width); return dom.div( { className: "boxmodel-main", onMouseOver: this.onHighlightMouseOver, onMouseOut: this.props.onHideBoxModelHighlighter, }, - dom.span( - { - className: "boxmodel-legend", - "data-box": "margin", - title: BOXMODEL_L10N.getStr("boxmodel.margin"), - }, - BOXMODEL_L10N.getStr("boxmodel.margin") - ), + displayPosition ? + dom.span( + { + className: "boxmodel-legend", + "data-box": "position", + title: BOXMODEL_L10N.getFormatStr("boxmodel.position", position), + }, + BOXMODEL_L10N.getFormatStr("boxmodel.position", position) + ) + : + null, dom.div( { - className: "boxmodel-margins", - "data-box": "margin", - title: BOXMODEL_L10N.getStr("boxmodel.margin"), + className: "boxmodel-box" }, dom.span( { className: "boxmodel-legend", - "data-box": "border", - title: BOXMODEL_L10N.getStr("boxmodel.border"), + "data-box": "margin", + title: BOXMODEL_L10N.getStr("boxmodel.margin"), }, - BOXMODEL_L10N.getStr("boxmodel.border") + BOXMODEL_L10N.getStr("boxmodel.margin") ), dom.div( { - className: "boxmodel-borders", - "data-box": "border", - title: BOXMODEL_L10N.getStr("boxmodel.border"), + className: "boxmodel-margins", + "data-box": "margin", + title: BOXMODEL_L10N.getStr("boxmodel.margin"), }, dom.span( { className: "boxmodel-legend", - "data-box": "padding", - title: BOXMODEL_L10N.getStr("boxmodel.padding"), + "data-box": "border", + title: BOXMODEL_L10N.getStr("boxmodel.border"), }, - BOXMODEL_L10N.getStr("boxmodel.padding") + BOXMODEL_L10N.getStr("boxmodel.border") ), dom.div( { - className: "boxmodel-paddings", - "data-box": "padding", - title: BOXMODEL_L10N.getStr("boxmodel.padding"), + className: "boxmodel-borders", + "data-box": "border", + title: BOXMODEL_L10N.getStr("boxmodel.border"), }, - dom.div({ - className: "boxmodel-content", - "data-box": "content", - title: BOXMODEL_L10N.getStr("boxmodel.content"), - }) + dom.span( + { + className: "boxmodel-legend", + "data-box": "padding", + title: BOXMODEL_L10N.getStr("boxmodel.padding"), + }, + BOXMODEL_L10N.getStr("boxmodel.padding") + ), + dom.div( + { + className: "boxmodel-paddings", + "data-box": "padding", + title: BOXMODEL_L10N.getStr("boxmodel.padding"), + }, + dom.div({ + className: "boxmodel-content", + "data-box": "content", + title: BOXMODEL_L10N.getStr("boxmodel.content"), + }) + ) ) ) ), + displayPosition ? + BoxModelEditable({ + box: "position", + direction: "top", + property: "position-top", + textContent: positionTop, + onShowBoxModelEditor, + }) + : + null, + displayPosition ? + BoxModelEditable({ + box: "position", + direction: "right", + property: "position-right", + textContent: positionRight, + onShowBoxModelEditor, + }) + : + null, + displayPosition ? + BoxModelEditable({ + box: "position", + direction: "bottom", + property: "position-bottom", + textContent: positionBottom, + onShowBoxModelEditor, + }) + : + null, + displayPosition ? + BoxModelEditable({ + box: "position", + direction: "left", + property: "position-left", + textContent: positionLeft, + onShowBoxModelEditor, + }) + : + null, BoxModelEditable({ box: "margin", direction: "top", property: "margin-top", textContent: marginTop, onShowBoxModelEditor, }), BoxModelEditable({
--- a/devtools/client/locales/en-US/boxmodel.properties +++ b/devtools/client/locales/en-US/boxmodel.properties @@ -10,16 +10,20 @@ # You want to make that choice consistent across the developer tools. # A good criteria is the language in which you'd find the best # documentation on web development on the web. # LOCALIZATION NOTE (boxmodel.title) This is the title of the box model panel and is # displayed as a label. boxmodel.title=Box Model +# LOCALIZATION NOTE (boxmodel.position) This refers to the position in the box model and +# might be displayed as a label or as a tooltip. +boxmodel.position=position: %S + # LOCALIZATION NOTE (boxmodel.margin) This refers to the margin in the box model and # might be displayed as a label or as a tooltip. boxmodel.margin=margin # LOCALIZATION NOTE (boxmodel.border) This refers to the border in the box model and # might be displayed as a label or as a tooltip. boxmodel.border=border
--- a/devtools/client/themes/boxmodel.css +++ b/devtools/client/themes/boxmodel.css @@ -6,16 +6,17 @@ * This is the stylesheet of the Box Model view implemented in the layout panel. */ .boxmodel-container { /* The view will grow bigger as the window gets resized, until 400px */ max-width: 400px; margin: 0px auto; padding: 0; + overflow: auto; } /* Header */ .boxmodel-header, .boxmodel-info { display: flex; align-items: center; @@ -25,24 +26,28 @@ .layout-geometry-editor::before { background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px; } /* Main: contains the box-model regions */ .boxmodel-main { position: relative; - box-sizing: border-box; - /* The regions are semi-transparent, so the white background is partly - visible */ - background-color: white; color: var(--theme-selection-color); /* Make sure there is some space between the window's edges and the regions */ margin: 14px 14px 4px 14px; width: calc(100% - 2 * 14px); + min-width: 240px; +} + +.boxmodel-box { + margin: 25px; + /* The regions are semi-transparent, so the white background is partly + visible */ + background-color: white; } .boxmodel-margin, .boxmodel-size { color: var(--theme-highlight-blue); } /* Regions are 3 nested elements with wide borders and outlines */ @@ -79,22 +84,16 @@ border-color: #6a5acd; } .boxmodel-content { background-color: #87ceeb; } .theme-firebug .boxmodel-main, -.theme-firebug .boxmodel-borders, -.theme-firebug .boxmodel-content { - border-style: solid; -} - -.theme-firebug .boxmodel-main, .theme-firebug .boxmodel-header { font-family: var(--proportional-font-family); } .theme-firebug .boxmodel-main { color: var(--theme-body-color); font-size: var(--theme-toolbar-font-size); } @@ -146,83 +145,145 @@ top: 1px; } .boxmodel-margin.boxmodel-bottom { bottom: 2px; } .boxmodel-size, +.boxmodel-position.boxmodel-left, +.boxmodel-position.boxmodel-right, .boxmodel-margin.boxmodel-left, .boxmodel-margin.boxmodel-right, .boxmodel-border.boxmodel-left, .boxmodel-border.boxmodel-right, .boxmodel-padding.boxmodel-left, .boxmodel-padding.boxmodel-right { top: 22px; line-height: 80px; } .boxmodel-size { width: calc(100% - 2px); } +.boxmodel-position.boxmodel-top, +.boxmodel-position.boxmodel-bottom, +.boxmodel-position.boxmodel-left, +.boxmodel-position.boxmodel-right, .boxmodel-margin.boxmodel-right, .boxmodel-margin.boxmodel-left, .boxmodel-border.boxmodel-left, .boxmodel-border.boxmodel-right, .boxmodel-padding.boxmodel-right, .boxmodel-padding.boxmodel-left { width: 21px; } .boxmodel-padding.boxmodel-left { - left: 35px; + left: 60px; } .boxmodel-padding.boxmodel-right { - right: 35px; + right: 60px; } .boxmodel-border.boxmodel-left { - left: 16px; + left: 41px; } .boxmodel-border.boxmodel-right { - right: 17px; + right: 42px; } .boxmodel-margin.boxmodel-right { - right: 0; + right: 25px; } .boxmodel-margin.boxmodel-left { - left: 0; + left: 25px; } .boxmodel-rotate.boxmodel-left:not(.boxmodel-editing) { transform: rotate(-90deg); } .boxmodel-rotate.boxmodel-right:not(.boxmodel-editing) { transform: rotate(90deg); } +.boxmodel-rotate.boxmodel-left.boxmodel-position:not(.boxmodel-editing) { + border-top: none; + border-right: 1px solid var(--theme-highlight-purple); + width: auto; + height: 30px; +} + +.boxmodel-rotate.boxmodel-right.boxmodel-position:not(.boxmodel-editing) { + border-top: none; + border-left: 1px solid var(--theme-highlight-purple); + width: auto; + height: 30px; +} + +/* Box Model Positioning: contains top, right, bottom, left */ + +.boxmodel-position { + color: var(--theme-highlight-purple); +} + +.boxmodel-position.boxmodel-top, +.boxmodel-position.boxmodel-bottom { + border-left: 1px solid var(--theme-highlight-purple); + left: 49.5%; + padding-left: 1px; +} + +.boxmodel-position.boxmodel-right, +.boxmodel-position.boxmodel-left { + border-top: 1px solid var(--theme-highlight-purple); + line-height: 15px; + top: 49.5%; + width: 30px; +} + +.boxmodel-position.boxmodel-top { + top: -18px; +} + +.boxmodel-position.boxmodel-right { + right: -9px; +} + +.boxmodel-position.boxmodel-bottom { + bottom: -18px; +} + +.boxmodel-position.boxmodel-left { + left: -9px; +} + /* Legend: displayed inside regions */ .boxmodel-legend { position: absolute; margin: 2px 6px; z-index: 1; } .boxmodel-legend[data-box="margin"] { color: var(--theme-highlight-blue); } +.boxmodel-legend[data-box="position"] { + color: var(--theme-highlight-purple); + margin: -18px -9px; +} + /* Editable fields */ .boxmodel-editable { border: 1px dashed transparent; -moz-user-select: none; } .boxmodel-editable:hover {
--- a/devtools/client/themes/layout.css +++ b/devtools/client/themes/layout.css @@ -1,16 +1,17 @@ /* 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/. */ #layout-container { height: 100%; width: 100%; overflow: auto; + min-width: 200px; } /** * Common styles for shared components */ .grid-container { display: flex;
--- a/devtools/server/actors/styles.js +++ b/devtools/server/actors/styles.js @@ -766,16 +766,20 @@ var PageStyleActor = protocol.ActorClass let clientRect = node.rawNode.getBoundingClientRect(); layout.width = parseFloat(clientRect.width.toPrecision(6)); layout.height = parseFloat(clientRect.height.toPrecision(6)); // We compute and update the values of margins & co. let style = CssLogic.getComputedStyle(node.rawNode); for (let prop of [ "position", + "top", + "right", + "bottom", + "left", "margin-top", "margin-right", "margin-bottom", "margin-left", "padding-top", "padding-right", "padding-bottom", "padding-left",
--- a/dom/base/IntlUtils.cpp +++ b/dom/base/IntlUtils.cpp @@ -81,10 +81,57 @@ IntlUtils::GetDisplayNames(const Sequenc // Return the result as DisplayNameResult. JSAutoCompartment ac(cx, &retVal.toObject()); if (!aResult.Init(cx, retVal)) { aError.Throw(NS_ERROR_FAILURE); } } +void +IntlUtils::GetLocaleInfo(const Sequence<nsString>& aLocales, + LocaleInfo& aResult, ErrorResult& aError) +{ + MOZ_ASSERT(nsContentUtils::IsCallerChrome() || + nsContentUtils::IsCallerContentXBL()); + + nsCOMPtr<mozIMozIntl> mozIntl = do_GetService("@mozilla.org/mozintl;1"); + if (!mozIntl) { + aError.Throw(NS_ERROR_UNEXPECTED); + return; + } + + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::PrivilegedJunkScope())) { + aError.Throw(NS_ERROR_FAILURE); + return; + } + JSContext* cx = jsapi.cx(); + + // Prepare parameter for getLocaleInfo(). + JS::Rooted<JS::Value> locales(cx); + if (!ToJSValue(cx, aLocales, &locales)) { + aError.StealExceptionFromJSContext(cx); + return; + } + + // Now call the method. + JS::Rooted<JS::Value> retVal(cx); + nsresult rv = mozIntl->GetLocaleInfo(locales, &retVal); + if (NS_FAILED(rv)) { + aError.Throw(rv); + return; + } + + if (!retVal.isObject()) { + aError.Throw(NS_ERROR_FAILURE); + return; + } + + // Return the result as LocaleInfo. + JSAutoCompartment ac(cx, &retVal.toObject()); + if (!aResult.Init(cx, retVal)) { + aError.Throw(NS_ERROR_FAILURE); + } +} + } // dom namespace } // mozilla namespace
--- a/dom/base/IntlUtils.h +++ b/dom/base/IntlUtils.h @@ -31,16 +31,21 @@ public: WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; void GetDisplayNames(const Sequence<nsString>& aLocales, const mozilla::dom::DisplayNameOptions& aOptions, mozilla::dom::DisplayNameResult& aResult, mozilla::ErrorResult& aError); + void + GetLocaleInfo(const Sequence<nsString>& aLocales, + mozilla::dom::LocaleInfo& aResult, + mozilla::ErrorResult& aError); + private: ~IntlUtils(); nsCOMPtr<nsPIDOMWindowInner> mWindow; }; } // namespace dom } // namespace mozilla
--- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -1139,17 +1139,17 @@ nsFocusManager::EnsureCurrentWidgetFocus } } } bool ActivateOrDeactivateChild(TabParent* aParent, void* aArg) { bool active = static_cast<bool>(aArg); - Unused << aParent->Manager()->AsContentParent()->SendParentActivated(aParent, active); + Unused << aParent->Manager()->SendParentActivated(aParent, active); return false; } void nsFocusManager::ActivateOrDeactivate(nsPIDOMWindowOuter* aWindow, bool aActive) { if (!aWindow) { return;
--- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -199,10 +199,31 @@ ContentBridgeChild::AllocPFileDescriptor } bool ContentBridgeChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) { return nsIContentChild::DeallocPFileDescriptorSetChild(aActor); } +mozilla::ipc::IPCResult +ContentBridgeChild::RecvActivate(PBrowserChild* aTab) +{ + TabChild* tab = static_cast<TabChild*>(aTab); + return tab->RecvActivate(); +} + +mozilla::ipc::IPCResult +ContentBridgeChild::RecvDeactivate(PBrowserChild* aTab) +{ + TabChild* tab = static_cast<TabChild*>(aTab); + return tab->RecvDeactivate(); +} + +mozilla::ipc::IPCResult +ContentBridgeChild::RecvParentActivated(PBrowserChild* aTab, const bool& aActivated) +{ + TabChild* tab = static_cast<TabChild*>(aTab); + return tab->RecvParentActivated(aActivated); +} + } // namespace dom } // namespace mozilla
--- a/dom/ipc/ContentBridgeChild.h +++ b/dom/ipc/ContentBridgeChild.h @@ -46,16 +46,23 @@ public: const bool& aIsForBrowser) override; virtual mozilla::ipc::PFileDescriptorSetChild* SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) override; virtual mozilla::ipc::PChildToParentStreamChild* SendPChildToParentStreamConstructor(mozilla::ipc::PChildToParentStreamChild*) override; + virtual mozilla::ipc::IPCResult RecvActivate(PBrowserChild* aTab) override; + + virtual mozilla::ipc::IPCResult RecvDeactivate(PBrowserChild* aTab) override; + + virtual mozilla::ipc::IPCResult RecvParentActivated(PBrowserChild* aTab, + const bool& aActivated) override; + FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild) protected: virtual ~ContentBridgeChild(); virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId, const IPCTabContext& aContext, const uint32_t& aChromeFlags,
--- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -64,16 +64,32 @@ public: { // XXX: do we need this for ContentBridgeParent? return -1; } virtual mozilla::ipc::PParentToChildStreamParent* SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) override; + virtual bool SendActivate(PBrowserParent* aTab) override + { + return PContentBridgeParent::SendActivate(aTab); + } + + virtual bool SendDeactivate(PBrowserParent* aTab) override + { + return PContentBridgeParent::SendDeactivate(aTab); + } + + virtual bool SendParentActivated(PBrowserParent* aTab, + const bool& aActivated) override + { + return PContentBridgeParent::SendParentActivated(aTab, aActivated); + } + protected: virtual ~ContentBridgeParent(); void SetChildID(ContentParentId aId) { mChildID = aId; }
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1961,51 +1961,55 @@ ContentParent::LaunchSubprocess(ProcessP std::vector<std::string> extraArgs; extraArgs.push_back("-childID"); char idStr[21]; SprintfLiteral(idStr, "%" PRId64, static_cast<uint64_t>(mChildID)); extraArgs.push_back(idStr); extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser"); - std::stringstream boolPrefs; - std::stringstream intPrefs; - std::stringstream stringPrefs; + char boolBuf[1024]; + char intBuf[1024]; + char strBuf[1024]; + nsFixedCString boolPrefs(boolBuf, 1024, 0); + nsFixedCString intPrefs(intBuf, 1024, 0); + nsFixedCString stringPrefs(strBuf, 1024, 0); size_t prefsLen; ContentPrefs::GetContentPrefs(&prefsLen); for (unsigned int i = 0; i < prefsLen; i++) { MOZ_ASSERT(i == 0 || strcmp(ContentPrefs::GetContentPref(i), ContentPrefs::GetContentPref(i - 1)) > 0); switch (Preferences::GetType(ContentPrefs::GetContentPref(i))) { case nsIPrefBranch::PREF_INT: - intPrefs << i << ':' << Preferences::GetInt(ContentPrefs::GetContentPref(i)) << '|'; + intPrefs.Append(nsPrintfCString("%u:%d|", i, Preferences::GetInt(ContentPrefs::GetContentPref(i)))); break; case nsIPrefBranch::PREF_BOOL: - boolPrefs << i << ':' << Preferences::GetBool(ContentPrefs::GetContentPref(i)) << '|'; + boolPrefs.Append(nsPrintfCString("%u:%d|", i, Preferences::GetBool(ContentPrefs::GetContentPref(i)))); break; case nsIPrefBranch::PREF_STRING: { - std::string value(Preferences::GetCString(ContentPrefs::GetContentPref(i)).get()); - stringPrefs << i << ':' << value.length() << ':' << value << '|'; + nsAdoptingCString value(Preferences::GetCString(ContentPrefs::GetContentPref(i))); + stringPrefs.Append(nsPrintfCString("%u:%d;%s|", i, value.Length(), value.get())); + } break; case nsIPrefBranch::PREF_INVALID: break; default: printf("preference type: %x\n", Preferences::GetType(ContentPrefs::GetContentPref(i))); MOZ_CRASH(); } } extraArgs.push_back("-intPrefs"); - extraArgs.push_back(intPrefs.str()); + extraArgs.push_back(intPrefs.get()); extraArgs.push_back("-boolPrefs"); - extraArgs.push_back(boolPrefs.str()); + extraArgs.push_back(boolPrefs.get()); extraArgs.push_back("-stringPrefs"); - extraArgs.push_back(stringPrefs.str()); + extraArgs.push_back(stringPrefs.get()); if (gSafeMode) { extraArgs.push_back("-safeMode"); } if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) { MarkAsDead(); return false;
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -606,16 +606,33 @@ public: AllocPURLClassifierParent(const Principal& aPrincipal, const bool& aUseTrackingProtection, bool* aSuccess) override; virtual mozilla::ipc::IPCResult RecvPURLClassifierConstructor(PURLClassifierParent* aActor, const Principal& aPrincipal, const bool& aUseTrackingProtection, bool* aSuccess) override; + + virtual bool SendActivate(PBrowserParent* aTab) override + { + return PContentParent::SendActivate(aTab); + } + + virtual bool SendDeactivate(PBrowserParent* aTab) override + { + return PContentParent::SendDeactivate(aTab); + } + + virtual bool SendParentActivated(PBrowserParent* aTab, + const bool& aActivated) override + { + return PContentParent::SendParentActivated(aTab, aActivated); + } + virtual bool DeallocPURLClassifierParent(PURLClassifierParent* aActor) override; virtual mozilla::ipc::IPCResult RecvClassifyLocal(const URIParams& aURI, const nsCString& aTables, nsresult* aRv, nsTArray<nsCString>* aResults) override;
--- a/dom/ipc/ContentProcess.cpp +++ b/dom/ipc/ContentProcess.cpp @@ -155,49 +155,56 @@ ContentProcess::Init(int aArgc, char* aA } isForBrowser = strcmp(aArgv[idx], "-notForBrowser"); foundIsForBrowser = true; } else if (!strcmp(aArgv[idx], "-intPrefs")) { SET_PREF_PHASE(BEGIN_INIT_PREFS); char* str = aArgv[idx + 1]; while (*str) { int32_t index = strtol(str, &str, 10); + MOZ_ASSERT(str[0] == ':'); str++; MaybePrefValue value(PrefValue(static_cast<int32_t>(strtol(str, &str, 10)))); + MOZ_ASSERT(str[0] == '|'); str++; PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue()); prefsArray.AppendElement(pref); } SET_PREF_PHASE(END_INIT_PREFS); foundIntPrefs = true; } else if (!strcmp(aArgv[idx], "-boolPrefs")) { SET_PREF_PHASE(BEGIN_INIT_PREFS); char* str = aArgv[idx + 1]; while (*str) { int32_t index = strtol(str, &str, 10); + MOZ_ASSERT(str[0] == ':'); str++; MaybePrefValue value(PrefValue(!!strtol(str, &str, 10))); + MOZ_ASSERT(str[0] == '|'); str++; PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue()); prefsArray.AppendElement(pref); } SET_PREF_PHASE(END_INIT_PREFS); foundBoolPrefs = true; } else if (!strcmp(aArgv[idx], "-stringPrefs")) { SET_PREF_PHASE(BEGIN_INIT_PREFS); char* str = aArgv[idx + 1]; while (*str) { int32_t index = strtol(str, &str, 10); + MOZ_ASSERT(str[0] == ':'); str++; int32_t length = strtol(str, &str, 10); + MOZ_ASSERT(str[0] == ';'); str++; MaybePrefValue value(PrefValue(nsCString(str, length))); PrefSetting pref(nsCString(ContentPrefs::GetContentPref(index)), value, MaybePrefValue()); prefsArray.AppendElement(pref); str += length + 1; + MOZ_ASSERT(*(str - 1) == '|'); } SET_PREF_PHASE(END_INIT_PREFS); foundStringPrefs = true; } else if (!strcmp(aArgv[idx], "-safeMode")) { gSafeMode = true; }
--- a/dom/ipc/PContentBridge.ipdl +++ b/dom/ipc/PContentBridge.ipdl @@ -40,16 +40,26 @@ nested(upto inside_cpow) sync protocol P manages PFileDescriptorSet; manages PJavaScript; manages PChildToParentStream; manages PParentToChildStream; child: async PParentToChildStream(); +child: + /** + * Sending an activate message moves focus to the child. + */ + async Activate(PBrowser aTab); + + async Deactivate(PBrowser aTab); + + async ParentActivated(PBrowser aTab, bool aActivated); + parent: sync SyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows, Principal aPrincipal) returns (StructuredCloneData[] retval); async PJavaScript(); async PChildToParentStream();
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -353,16 +353,18 @@ public: const LayoutDeviceIntPoint& aChromeDisp) override; virtual mozilla::ipc::IPCResult RecvSizeModeChanged(const nsSizeMode& aSizeMode) override; mozilla::ipc::IPCResult RecvActivate(); mozilla::ipc::IPCResult RecvDeactivate(); + mozilla::ipc::IPCResult RecvParentActivated(const bool& aActivated); + virtual mozilla::ipc::IPCResult RecvMouseEvent(const nsString& aType, const float& aX, const float& aY, const int32_t& aButton, const int32_t& aClickCount, const int32_t& aModifiers, const bool& aIgnoreRootScrollFrame) override; @@ -687,18 +689,16 @@ protected: virtual mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation) override; virtual mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint() override; virtual mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled) override; - mozilla::ipc::IPCResult RecvParentActivated(const bool& aActivated); - virtual mozilla::ipc::IPCResult RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators, const UIStateChangeType& aShowFocusRings) override; virtual mozilla::ipc::IPCResult RecvStopIMEStateManagement() override; virtual mozilla::ipc::IPCResult RecvMenuKeyboardListenerInstalled( const bool& aInstalled) override;
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -854,25 +854,25 @@ TabParent::HandleAccessKey(const WidgetK Unused << SendHandleAccessKey(aEvent, aCharCodes, aModifierMask); } } void TabParent::Activate() { if (!mIsDestroyed) { - Unused << Manager()->AsContentParent()->SendActivate(this); + Unused << Manager()->SendActivate(this); } } void TabParent::Deactivate() { if (!mIsDestroyed) { - Unused << Manager()->AsContentParent()->SendDeactivate(this); + Unused << Manager()->SendDeactivate(this); } } NS_IMETHODIMP TabParent::Init(mozIDOMWindowProxy *window) { return NS_OK; }
--- a/dom/ipc/nsIContentParent.h +++ b/dom/ipc/nsIContentParent.h @@ -86,16 +86,23 @@ public: ContentParent* AsContentParent(); virtual bool IsContentBridgeParent() const { return false; } ContentBridgeParent* AsContentBridgeParent(); nsFrameMessageManager* GetMessageManager() const { return mMessageManager; } + virtual bool SendActivate(PBrowserParent* aTab) = 0; + + virtual bool SendDeactivate(PBrowserParent* aTab) = 0; + + virtual bool SendParentActivated(PBrowserParent* aTab, + const bool& aActivated) = 0; + virtual int32_t Pid() const = 0; virtual mozilla::ipc::PParentToChildStreamParent* SendPParentToChildStreamConstructor(mozilla::ipc::PParentToChildStreamParent*) = 0; protected: // methods bool CanOpenBrowser(const IPCTabContext& aContext);
--- a/dom/manifest/Manifest.jsm +++ b/dom/manifest/Manifest.jsm @@ -85,18 +85,25 @@ class Manifest { installed: true, manifest: manifestData }; Manifests.manifestInstalled(this); this._store.saveSoon(); } async icon(expectedSize) { - return await ManifestIcons + if ('cached_icon' in this._store.data) { + return this._store.data.cached_icon; + } + const icon = await ManifestIcons .browserFetchIcon(this._browser, this._store.data.manifest, expectedSize); + // Cache the icon so future requests do not go over the network + this._store.data.cached_icon = icon; + this._store.saveSoon(); + return icon; } get scope() { const scope = this._store.data.manifest.scope || this._store.data.manifest.start_url; return stripQuery(scope); } @@ -111,16 +118,20 @@ class Manifest { get installed() { return this._store.data && this._store.data.installed || false; } get start_url() { return this._store.data.manifest.start_url; } + + get path() { + return this._path; + } } /* * Manifests maintains the list of installed manifests */ var Manifests = { async initialise () {
--- a/dom/manifest/ManifestIcons.jsm +++ b/dom/manifest/ManifestIcons.jsm @@ -58,17 +58,17 @@ async function getIcon(aWindow, icons, e let index = icons.findIndex(icon => icon.size >= expectedSize); if (index === -1) { index = icons.length - 1; } return fetchIcon(aWindow, icons[index].src).catch(err => { // Remove all icons with the failed source, the same source // may have been used for multiple sizes - icons = icons.filter(x => x.src === icons[index].src); + icons = icons.filter(x => x.src !== icons[index].src); return getIcon(aWindow, icons, expectedSize); }); } function fetchIcon(aWindow, src) { const manifestURL = new aWindow.URL(src); const request = new aWindow.Request(manifestURL, {mode: "cors"}); request.overrideContentPolicyType(Ci.nsIContentPolicy.TYPE_WEB_MANIFEST);
--- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -60,16 +60,17 @@ skip-if = os == 'linux' && !debug # bug [test_focused_link_scroll.xul] [test_fullscreen.xul] tags = fullscreen # disabled on linux for timeouts--bug-867745 skip-if = os == 'linux' [test_geolocation.xul] [test_indexedSetter.html] [test_intlUtils_getDisplayNames.html] +[test_intlUtils_getLocaleInfo.html] [test_moving_nodeList.xul] [test_moving_xhr.xul] [test_MozDomFullscreen_event.xul] tags = fullscreen # disabled on OS X for intermittent failures--bug-798848 skip-if = toolkit == 'cocoa' [test_nodesFromRect.html] [test_parsingMode.html]
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/chrome/test_intlUtils_getLocaleInfo.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1346084 +--> +<head> + <title>Test for Bug 1346084 </title> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1346084 ">Mozilla Bug 1346084</a> +<p id="display"></p> +<div id="content" style="display: none"> +<script> + +const Cc = Components.classes; +const Ci = Components.interfaces; +const localeService = + Cc["@mozilla.org/intl/localeservice;1"].getService(Ci.mozILocaleService); +const mozIntl = Cc["@mozilla.org/mozintl;1"].getService(Ci.mozIMozIntl); + +let appLocale = localeService.getAppLocalesAsBCP47()[0]; + +let testData = [ + { + locales: ["en-US"], + expected: { + locale: "en-US", + direction: "ltr", + } + }, + { + locales: ["fr"], + expected: { + locale: "fr", + direction: "ltr", + } + }, + { + locales: ["ar"], + expected: { + locale: "ar", + direction: "rtl", + } + }, + // IntlUtils uses current app locales if locales is not provided. + { + locales: [], + expected: { + locale: appLocale, + direction: mozIntl.getLocaleInfo(appLocale).direction, + } + }, +]; + +let intlUtils = window.intlUtils; +ok(intlUtils, "window.intlUtils should exist"); + +for (let { locales, expected } of testData) { + let result = intlUtils.getLocaleInfo(locales); + + is(result.locale, expected.locale, "locale is " + expected.locale); + is(result.direction, expected.direction, "direction is " + expected.direction); +} + +</script> +</body> +</html>
--- a/dom/webidl/IntlUtils.webidl +++ b/dom/webidl/IntlUtils.webidl @@ -8,16 +8,21 @@ dictionary DisplayNameOptions { }; dictionary DisplayNameResult { DOMString locale; DOMString style; record<DOMString, DOMString> values; }; +dictionary LocaleInfo { + DOMString locale; + DOMString direction; +}; + /** * The IntlUtils interface provides helper functions for localization. */ [NoInterfaceObject] interface IntlUtils { /** * Helper function to retrieve the localized values for a list of requested * keys. @@ -42,9 +47,27 @@ interface IntlUtils { * values: * a key-value pair list of requested keys and corresponding translated * values * */ [Throws] DisplayNameResult getDisplayNames(sequence<DOMString> locales, optional DisplayNameOptions options); + + /** + * Helper function to retrieve useful information about a locale. + * + * The function takes one argument - locales which is a list of locale + * strings. + * + * It returns an object with properties: + * + * locale: + * a negotiated locale string + * + * direction: + * text direction, "ltr" or "rtl" + * + */ + [Throws] + LocaleInfo getLocaleInfo(sequence<DOMString> locales); };
--- a/gfx/skia/skia/src/gpu/SkGpuDevice.cpp +++ b/gfx/skia/skia/src/gpu/SkGpuDevice.cpp @@ -1029,17 +1029,17 @@ void SkGpuDevice::drawBitmapTile(const S sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(bitmap.colorSpace(), fDrawContext->getColorSpace()); SkScalar iw = 1.f / texture->width(); SkScalar ih = 1.f / texture->height(); SkMatrix texMatrix; // Compute a matrix that maps the rect we will draw to the src rect. - texMatrix.setRectToRect(dstRect, srcRect, SkMatrix::kStart_ScaleToFit); + texMatrix.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit); texMatrix.postScale(iw, ih); // Construct a GrPaint by setting the bitmap texture as the first effect and then configuring // the rest from the SkPaint. sk_sp<GrFragmentProcessor> fp; if (needsTextureDomain && (SkCanvas::kStrict_SrcRectConstraint == constraint)) { // Use a constrained texture domain to avoid color bleeding
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1342882.js @@ -0,0 +1,3 @@ +// |jit-test| error: ReferenceError + +for (let [k, map = send.log += "" + map] of map) {}
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -1212,20 +1212,36 @@ GetPropIRGenerator::tryAttachStringLengt bool GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId) { MOZ_ASSERT(idVal_.isInt32()); if (!val_.isString()) return false; + int32_t index = idVal_.toInt32(); + if (index < 0) + return false; + JSString* str = val_.toString(); - int32_t index = idVal_.toInt32(); - if (size_t(index) >= str->length() || - !str->isLinear() || + if (size_t(index) >= str->length()) + return false; + + // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases. + if (str->isRope()) { + JSRope* rope = &str->asRope(); + + // Make sure the left side contains the index. + if (size_t(index) >= rope->leftChild()->length()) + return false; + + str = rope->leftChild(); + } + + if (!str->isLinear() || str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT) { return false; } StringOperandId strId = writer.guardIsString(valId); Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId); writer.loadStringCharResult(strId, int32IndexId);
--- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -1753,22 +1753,20 @@ CacheIRCompiler::emitLoadStringCharResul Register index = allocator.useRegister(masm, reader.int32OperandId()); AutoScratchRegisterMaybeOutput scratch1(allocator, masm, output); AutoScratchRegister scratch2(allocator, masm); FailurePath* failure; if (!addFailurePath(&failure)) return false; - masm.branchIfRope(str, failure->label()); - // Bounds check, load string char. masm.branch32(Assembler::BelowOrEqual, Address(str, JSString::offsetOfLength()), index, failure->label()); - masm.loadStringChar(str, index, scratch1); + masm.loadStringChar(str, index, scratch1, failure->label()); // Load StaticString for this char. masm.branch32(Assembler::AboveOrEqual, scratch1, Imm32(StaticStrings::UNIT_STATIC_LIMIT), failure->label()); masm.movePtr(ImmPtr(&cx_->staticStrings().unitStaticTable), scratch2); masm.loadPtr(BaseIndex(scratch2, scratch1, ScalePointer), scratch2); EmitStoreResult(masm, scratch2, JSVAL_TYPE_STRING, output);
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -7961,20 +7961,17 @@ static const VMFunction CharCodeAtInfo = void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) { Register str = ToRegister(lir->str()); Register index = ToRegister(lir->index()); Register output = ToRegister(lir->output()); OutOfLineCode* ool = oolCallVM(CharCodeAtInfo, lir, ArgList(str, index), StoreRegisterTo(output)); - - masm.branchIfRope(str, ool->entry()); - masm.loadStringChar(str, index, output); - + masm.loadStringChar(str, index, output, ool->entry()); masm.bind(ool->rejoin()); } typedef JSFlatString* (*StringFromCharCodeFn)(JSContext*, int32_t); static const VMFunction StringFromCharCodeInfo = FunctionInfo<StringFromCharCodeFn>(jit::StringFromCharCode, "StringFromCharCode"); void
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3223,23 +3223,25 @@ IonBuilder::binaryArithTryConcat(bool* e // Make sure one of the inputs is a string. if (left->type() != MIRType::String && right->type() != MIRType::String) { trackOptimizationOutcome(TrackedOutcome::OperandNotString); return Ok(); } // The non-string input (if present) should be atleast easily coercible to string. if (right->type() != MIRType::String && - (right->mightBeType(MIRType::Symbol) || right->mightBeType(MIRType::Object))) + (right->mightBeType(MIRType::Symbol) || right->mightBeType(MIRType::Object) || + right->mightBeMagicType())) { trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString); return Ok(); } if (left->type() != MIRType::String && - (left->mightBeType(MIRType::Symbol) || left->mightBeType(MIRType::Object))) + (left->mightBeType(MIRType::Symbol) || left->mightBeType(MIRType::Object) || + left->mightBeMagicType())) { trackOptimizationOutcome(TrackedOutcome::OperandNotEasilyCoercibleToString); return Ok(); } MConcat* ins = MConcat::New(alloc(), left, right); current->add(ins); current->push(ins);
--- a/js/src/jit/MacroAssembler-inl.h +++ b/js/src/jit/MacroAssembler-inl.h @@ -391,16 +391,24 @@ void MacroAssembler::branchIfRope(Register str, Label* label) { Address flags(str, JSString::offsetOfFlags()); static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0"); branchTest32(Assembler::Zero, flags, Imm32(JSString::TYPE_FLAGS_MASK), label); } void +MacroAssembler::branchIfNotRope(Register str, Label* label) +{ + Address flags(str, JSString::offsetOfFlags()); + static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0"); + branchTest32(Assembler::NonZero, flags, Imm32(JSString::TYPE_FLAGS_MASK), label); +} + +void MacroAssembler::branchLatin1String(Register string, Label* label) { branchTest32(Assembler::NonZero, Address(string, JSString::offsetOfFlags()), Imm32(JSString::LATIN1_CHARS_BIT), label); } void MacroAssembler::branchTwoByteString(Register string, Label* label)
--- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1378,22 +1378,39 @@ MacroAssembler::loadStringChars(Register bind(&isInline); computeEffectiveAddress(Address(str, JSInlineString::offsetOfInlineStorage()), dest); bind(&done); } void -MacroAssembler::loadStringChar(Register str, Register index, Register output) +MacroAssembler::loadStringChar(Register str, Register index, Register output, Label* fail) { MOZ_ASSERT(str != output); MOZ_ASSERT(index != output); - loadStringChars(str, output); + movePtr(str, output); + + // This follows JSString::getChar. + Label notRope; + branchIfNotRope(str, ¬Rope); + + // Load leftChild. + loadPtr(Address(str, JSRope::offsetOfLeft()), output); + + // Check if the index is contained in the leftChild. + // Todo: Handle index in the rightChild. + branch32(Assembler::BelowOrEqual, Address(output, JSString::offsetOfLength()), index, fail); + + // If the left side is another rope, give up. + branchIfRope(output, fail); + + bind(¬Rope); + loadStringChars(output, output); Label isLatin1, done; branchLatin1String(str, &isLatin1); load16ZeroExtend(BaseIndex(output, index, TimesTwo), output); jump(&done); bind(&isLatin1); load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
--- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1108,16 +1108,17 @@ class MacroAssembler : public MacroAssem // Branches to |label| if |reg| is false. |reg| should be a C++ bool. template <class L> inline void branchIfFalseBool(Register reg, L label); // Branches to |label| if |reg| is true. |reg| should be a C++ bool. inline void branchIfTrueBool(Register reg, Label* label); inline void branchIfRope(Register str, Label* label); + inline void branchIfNotRope(Register str, Label* label); inline void branchLatin1String(Register string, Label* label); inline void branchTwoByteString(Register string, Label* label); inline void branchIfFunctionHasNoScript(Register fun, Label* label); inline void branchIfInterpreted(Register fun, Label* label); inline void branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun, @@ -1488,17 +1489,17 @@ class MacroAssembler : public MacroAssem loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest); } void loadStringLength(Register str, Register dest) { load32(Address(str, JSString::offsetOfLength()), dest); } void loadStringChars(Register str, Register dest); - void loadStringChar(Register str, Register index, Register output); + void loadStringChar(Register str, Register index, Register output, Label* fail); void loadJSContext(Register dest); void loadJitActivation(Register dest) { loadJSContext(dest); loadPtr(Address(dest, offsetof(JSContext, activation_)), dest); } void loadWasmActivationFromTls(Register dest) { loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, cx)), dest);
--- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1455,33 +1455,34 @@ js::StringFindPattern(JSLinearString* te // When an algorithm does not need a string represented as a single linear // array of characters, this range utility may be used to traverse the string a // sequence of linear arrays of characters. This avoids flattening ropes. class StringSegmentRange { // If malloc() shows up in any profiles from this vector, we can add a new // StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx. - Rooted<StringVector> stack; + using StackVector = JS::GCVector<JSString*, 16>; + Rooted<StackVector> stack; RootedLinearString cur; bool settle(JSString* str) { while (str->isRope()) { JSRope& rope = str->asRope(); if (!stack.append(rope.rightChild())) return false; str = rope.leftChild(); } cur = &str->asLinear(); return true; } public: explicit StringSegmentRange(JSContext* cx) - : stack(cx, StringVector(cx)), cur(cx) + : stack(cx, StackVector(cx)), cur(cx) {} MOZ_MUST_USE bool init(JSString* str) { MOZ_ASSERT(stack.empty()); return settle(str); } bool empty() const {
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -63,16 +63,17 @@ #include "gfxPrefs.h" #include "gfxTypes.h" #include "nsTArray.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "nsICanvasRenderingContextInternal.h" #include "gfxPlatform.h" #include <algorithm> #include <limits> +#include "mozilla/dom/AnonymousContent.h" #include "mozilla/dom/HTMLVideoElement.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/DOMRect.h" #include "mozilla/dom/DOMStringList.h" #include "mozilla/dom/KeyframeEffectReadOnly.h" #include "mozilla/layers/APZCCallbackHelper.h" #include "imgIRequest.h" #include "nsIImageLoadingContent.h" @@ -1152,25 +1153,65 @@ GetDisplayPortFromMarginsData(nsIContent // Make sure the displayport remains within the scrollable rect. result = result.MoveInsideAndClamp(expandedScrollableRect - scrollPos); return result; } static bool -ShouldDisableApzForElement(nsIContent* aContent) -{ - if (gfxPrefs::APZDisableForScrollLinkedEffects() && aContent) { - nsIDocument* doc = aContent->GetComposedDoc(); - return (doc && doc->HasScrollLinkedEffect()); +HasVisibleAnonymousContents(nsIDocument* aDoc) +{ + for (RefPtr<AnonymousContent>& ac : aDoc->GetAnonymousContents()) { + Element* elem = ac->GetContentNode(); + // We check to see if the anonymous content node has a frame. If it doesn't, + // that means that's not visible to the user because e.g. it's display:none. + // For now we assume that if it has a frame, it is visible. We might be able + // to refine this further by adding complexity if it turns out this condition + // results in a lot of false positives. + if (elem && elem->GetPrimaryFrame()) { + return true; + } } return false; } +bool +nsLayoutUtils::ShouldDisableApzForElement(nsIContent* aContent) +{ + if (!aContent) { + return false; + } + + nsIDocument* doc = aContent->GetComposedDoc(); + nsIPresShell* rootShell = APZCCallbackHelper::GetRootContentDocumentPresShellForContent(aContent); + if (rootShell) { + if (nsIDocument* rootDoc = rootShell->GetDocument()) { + nsIContent* rootContent = rootShell->GetRootScrollFrame() + ? rootShell->GetRootScrollFrame()->GetContent() + : rootDoc->GetDocumentElement(); + // For the AccessibleCaret: disable APZ on any scrollable subframes that + // are not the root scrollframe of a document, if the document has any + // visible anonymous contents. + // If we find this is triggering in too many scenarios then we might + // want to tighten this check further. The main use cases for which we want + // to disable APZ as of this writing are listed in bug 1316318. + if (aContent != rootContent && HasVisibleAnonymousContents(rootDoc)) { + return true; + } + } + } + + if (!doc) { + return false; + } + return gfxPrefs::APZDisableForScrollLinkedEffects() && + doc->HasScrollLinkedEffect(); +} + static bool GetDisplayPortData(nsIContent* aContent, DisplayPortPropertyData** aOutRectData, DisplayPortMarginsPropertyData** aOutMarginsData) { MOZ_ASSERT(aOutRectData && aOutMarginsData); *aOutRectData = @@ -1225,17 +1266,18 @@ GetDisplayPortImpl(nsIContent* aContent, // We have displayport data, but the caller doesn't want the actual // rect, so we don't need to actually compute it. return true; } nsRect result; if (rectData) { result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier); - } else if (APZCCallbackHelper::IsDisplayportSuppressed() || ShouldDisableApzForElement(aContent)) { + } else if (APZCCallbackHelper::IsDisplayportSuppressed() || + nsLayoutUtils::ShouldDisableApzForElement(aContent)) { DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1); result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier); } else { result = GetDisplayPortFromMarginsData(aContent, marginsData, aMultiplier); } if (!gfxPrefs::LayersTilesEnabled()) { // Either we should have gotten a valid rect directly from the displayport
--- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -2680,16 +2680,24 @@ public: /** * Returns the current APZ Resolution Scale. When Java Pan/Zoom is * enabled in Fennec it will always return 1.0. */ static float GetCurrentAPZResolutionScale(nsIPresShell* aShell); /** + * Returns true if we need to disable async scrolling for this particular + * element. Note that this does a partial disabling - the displayport still + * exists but uses a very small margin, and the compositor doesn't apply the + * async transform. However, the content may still be layerized. + */ + static bool ShouldDisableApzForElement(nsIContent* aContent); + + /** * Log a key/value pair for APZ testing during a paint. * @param aManager The data will be written to the APZTestData associated * with this layer manager. * @param aScrollId Identifies the scroll frame to which the data pertains. * @param aKey The key under which to log the data. * @param aValue The value of the data to be logged. */ static void LogTestDataForPaint(mozilla::layers::LayerManager* aManager,
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -2813,17 +2813,19 @@ ScrollFrameHelper::ScrollToImpl(nsPoint mAllowScrollOriginDowngrade = false; } mLastSmoothScrollOrigin = nullptr; mScrollGeneration = ++sScrollGenerationCounter; ScrollVisual(); bool schedulePaint = true; - if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && gfxPrefs::APZPaintSkipping()) { + if (nsLayoutUtils::AsyncPanZoomEnabled(mOuter) && + !nsLayoutUtils::ShouldDisableApzForElement(content) && + gfxPrefs::APZPaintSkipping()) { // If APZ is enabled with paint-skipping, there are certain conditions in // which we can skip paints: // 1) If APZ triggered this scroll, and the tile-aligned displayport is // unchanged. // 2) If non-APZ triggered this scroll, but we can handle it by just asking // APZ to update the scroll position. Again we make this conditional on // the tile-aligned displayport being unchanged. // We do the displayport check first since it's common to all scenarios,
--- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -49,16 +49,17 @@ #endif <!-- This activity handles all incoming Intents and dispatches them to other activities. --> <activity android:name="org.mozilla.gecko.LauncherActivity" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:relinquishTaskIdentity="true" android:taskAffinity="" + android:exported="true" android:excludeFromRecents="true" /> <!-- Fennec is shipped as the Android package named org.mozilla.{fennec,firefox,firefox_beta}. The internal Java package hierarchy inside the Android package used to have an org.mozilla.{fennec,firefox,firefox_beta} subtree *and* an org.mozilla.gecko subtree; it now only has org.mozilla.gecko. --> <activity android:name="@MOZ_ANDROID_BROWSER_INTENT_CLASS@" @@ -311,16 +312,18 @@ </intent-filter> </activity> #ifdef MOZ_ANDROID_CUSTOM_TABS <activity android:name="org.mozilla.gecko.customtabs.CustomTabsActivity" android:theme="@style/GeckoCustomTabs" /> #endif + <activity android:name="org.mozilla.gecko.webapps.WebAppActivity" + android:theme="@style/Theme.AppCompat.NoActionBar" /> <!-- Service to handle requests from overlays. --> <service android:name="org.mozilla.gecko.overlays.service.OverlayActionService" /> <!-- Ensure that passwords provider runs in its own process. (Bug 718760.) Process name is per-application to avoid loading CPs from multiple Fennec versions into the same process. (Bug 749727.)
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java @@ -2032,20 +2032,21 @@ public class BrowserApp extends GeckoApp Telemetry.addToHistogram("FENNEC_ORBOT_INSTALLED", ContextUtils.isPackageInstalled(getContext(), "org.torproject.android") ? 1 : 0); break; case "Website:AppInstalled": final String name = message.getString("name"); final String startUrl = message.getString("start_url"); + final String manifestPath = message.getString("manifest_path"); final Bitmap icon = FaviconDecoder .decodeDataURI(getContext(), message.getString("icon")) .getBestBitmap(GeckoAppShell.getPreferredIconSize()); - createShortcut(name, startUrl, icon); + createAppShortcut(name, startUrl, manifestPath, icon); break; case "Updater:Launch": /** * Launch UI that lets the user update Firefox. * * This depends on the current channel: Release and Beta both direct to * the Google Play Store. If updating is enabled, Aurora, Nightly, and
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -35,22 +35,24 @@ import org.mozilla.gecko.prompts.PromptS import org.mozilla.gecko.restrictions.Restrictions; import org.mozilla.gecko.tabqueue.TabQueueHelper; import org.mozilla.gecko.text.FloatingToolbarTextSelection; import org.mozilla.gecko.text.TextSelection; import org.mozilla.gecko.updater.UpdateServiceHelper; import org.mozilla.gecko.util.ActivityResultHandler; import org.mozilla.gecko.util.ActivityUtils; import org.mozilla.gecko.util.BundleEventListener; +import org.mozilla.gecko.util.ColorUtil; import org.mozilla.gecko.util.EventCallback; import org.mozilla.gecko.util.FileUtils; import org.mozilla.gecko.util.GeckoBundle; import org.mozilla.gecko.util.HardwareUtils; import org.mozilla.gecko.util.PrefUtils; import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.webapps.WebAppActivity; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -140,16 +142,17 @@ public abstract class GeckoApp Tabs.OnTabsChangedListener, ViewTreeObserver.OnGlobalLayoutListener { private static final String LOGTAG = "GeckoApp"; private static final long ONE_DAY_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.DAYS); public static final String ACTION_ALERT_CALLBACK = "org.mozilla.gecko.ALERT_CALLBACK"; public static final String ACTION_HOMESCREEN_SHORTCUT = "org.mozilla.gecko.BOOKMARK"; + public static final String ACTION_WEBAPP = "org.mozilla.gecko.WEBAPP"; public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG"; public static final String ACTION_LAUNCH_SETTINGS = "org.mozilla.gecko.SETTINGS"; public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD"; public static final String ACTION_INIT_PW = "org.mozilla.gecko.INIT_PW"; public static final String ACTION_SWITCH_TAB = "org.mozilla.gecko.SWITCH_TAB"; public static final String INTENT_REGISTER_STUMBLER_LISTENER = "org.mozilla.gecko.STUMBLER_REGISTER_LOCAL_LISTENER"; @@ -2017,23 +2020,35 @@ public abstract class GeckoApp @Override public void onIconResponse(IconResponse response) { createShortcut(title, url, response.getBitmap()); } }); } public void createShortcut(final String aTitle, final String aURI, final Bitmap aIcon) { - // The intent to be launched by the shortcut. Intent shortcutIntent = new Intent(); shortcutIntent.setAction(GeckoApp.ACTION_HOMESCREEN_SHORTCUT); shortcutIntent.setData(Uri.parse(aURI)); shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); - + createHomescreenIcon(shortcutIntent, aTitle, aURI, aIcon); + } + + public void createAppShortcut(final String aTitle, final String aURI, final String manifestPath, final Bitmap aIcon) { + Intent shortcutIntent = new Intent(); + shortcutIntent.setAction(GeckoApp.ACTION_WEBAPP); + shortcutIntent.setData(Uri.parse(aURI)); + shortcutIntent.putExtra("MANIFEST_PATH", manifestPath); + shortcutIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, LauncherActivity.class.getName()); + createHomescreenIcon(shortcutIntent, aTitle, aURI, aIcon); + } + + public void createHomescreenIcon(final Intent shortcutIntent, final String aTitle, + final String aURI, final Bitmap aIcon) { Intent intent = new Intent(); intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, getLauncherIcon(aIcon, GeckoAppShell.getPreferredIconSize())); if (aTitle != null) { intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle); } else { intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aURI);
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java +++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java @@ -6,16 +6,17 @@ package org.mozilla.gecko; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.customtabs.CustomTabsIntent; +import org.mozilla.gecko.webapps.WebAppActivity; import org.mozilla.gecko.customtabs.CustomTabsActivity; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.mozglue.SafeIntent; import org.mozilla.gecko.preferences.GeckoPreferences; import org.mozilla.gecko.tabqueue.TabQueueHelper; import org.mozilla.gecko.tabqueue.TabQueueService; /** @@ -25,18 +26,22 @@ public class LauncherActivity extends Ac @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeckoAppShell.ensureCrashHandling(); final SafeIntent safeIntent = new SafeIntent(getIntent()); + // Is this web app? + if (isWebAppIntent(safeIntent)) { + dispatchWebAppIntent(); + // If it's not a view intent, it won't be a custom tabs intent either. Just launch! - if (!isViewIntentWithURL(safeIntent)) { + } else if (!isViewIntentWithURL(safeIntent)) { dispatchNormalIntent(); // Is this a custom tabs intent, and are custom tabs enabled? } else if (AppConstants.MOZ_ANDROID_CUSTOM_TABS && isCustomTabsIntent(safeIntent) && isCustomTabsEnabled()) { dispatchCustomTabsIntent(); // Can we dispatch this VIEW action intent to the tab queue service? @@ -78,16 +83,25 @@ public class LauncherActivity extends Ac Intent intent = new Intent(getIntent()); intent.setClassName(getApplicationContext(), CustomTabsActivity.class.getName()); filterFlags(intent); startActivity(intent); } + private void dispatchWebAppIntent() { + Intent intent = new Intent(getIntent()); + intent.setClassName(getApplicationContext(), WebAppActivity.class.getName()); + + filterFlags(intent); + + startActivity(intent); + } + private static void filterFlags(Intent intent) { // Explicitly remove the new task and clear task flags (Our browser activity is a single // task activity and we never want to start a second task here). See bug 1280112. intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_CLEAR_TASK); // LauncherActivity is started with the "exclude from recents" flag (set in manifest). We do // not want to propagate this flag from the launcher activity to the browser. @@ -99,12 +113,16 @@ public class LauncherActivity extends Ac && safeIntent.getDataString() != null; } private static boolean isCustomTabsIntent(@NonNull final SafeIntent safeIntent) { return isViewIntentWithURL(safeIntent) && safeIntent.hasExtra(CustomTabsIntent.EXTRA_SESSION); } + private static boolean isWebAppIntent(@NonNull final SafeIntent safeIntent) { + return GeckoApp.ACTION_WEBAPP.equals(safeIntent.getAction()); + } + private boolean isCustomTabsEnabled() { return GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_CUSTOM_TABS, false); } }
--- a/mobile/android/base/java/org/mozilla/gecko/search/SearchEngineManager.java +++ b/mobile/android/base/java/org/mozilla/gecko/search/SearchEngineManager.java @@ -17,16 +17,17 @@ import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.GeckoSharedPrefs; import org.mozilla.gecko.Locales; import org.mozilla.gecko.R; import org.mozilla.gecko.distribution.Distribution; import org.mozilla.gecko.util.FileUtils; import org.mozilla.gecko.util.GeckoJarReader; import org.mozilla.gecko.util.HardwareUtils; +import org.mozilla.gecko.util.IOUtils; import org.mozilla.gecko.util.RawResource; import org.mozilla.gecko.util.StringUtils; import org.mozilla.gecko.util.ThreadUtils; import org.xmlpull.v1.XmlPullParserException; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; @@ -37,16 +38,18 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.lang.ref.WeakReference; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Locale; +import org.json.JSONObject; +import org.json.JSONArray; /** * This class is not thread-safe, except where otherwise noted. * * This class contains a reference to {@link Context} - DO NOT LEAK! */ public class SearchEngineManager implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String LOG_TAG = "GeckoSearchEngineManager"; @@ -61,16 +64,19 @@ public class SearchEngineManager impleme private static final String PREF_DEFAULT_ENGINE_KEY = "search.engines.defaultname"; // Key for shared preference that stores search region. private static final String PREF_REGION_KEY = "search.region"; // URL for the geo-ip location service. Keep in sync with "browser.search.geoip.url" perference in Gecko. private static final String GEOIP_LOCATION_URL = "https://location.services.mozilla.com/v1/country?key=" + AppConstants.MOZ_MOZILLA_API_KEY; + // The API we're using requires a file size, so set an arbitrarily large one + public static final int MAX_LISTJSON_SIZE = 8192; + // This should go through GeckoInterface to get the UA, but the search activity // doesn't use a GeckoView yet. Until it does, get the UA directly. private static final String USER_AGENT = HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET : AppConstants.USER_AGENT_FENNEC_MOBILE; private final Context context; private final Distribution distribution; @Nullable private volatile SearchEngineCallback changeCallback; @@ -554,52 +560,61 @@ public class SearchEngineManager impleme } return createEngineFromFileList(files.toArray(new File[files.size()]), name); } /** * Creates a SearchEngine instance for a search plugin shipped in the locale. * - * This method reads the list of search plugin file names from list.txt, then + * This method reads the list of search plugin file names from list.json, then * iterates through the files, creating SearchEngine instances until it finds one * with the right name. Unfortunately, we need to do this because there is no * other way to map the search engine "name" to the file for the search plugin. * * @param name Search engine name. * @return SearchEngine instance for name. */ private SearchEngine createEngineFromLocale(String name) { - final InputStream in = getInputStreamFromSearchPluginsJar("list.txt"); + final InputStream in = getInputStreamFromSearchPluginsJar("list.json"); if (in == null) { return null; } - final BufferedReader br = getBufferedReader(in); - + JSONObject json; + try { + json = new JSONObject(FileUtils.readStringFromInputStreamAndCloseStream(in, MAX_LISTJSON_SIZE)); + } catch (IOException e) { + Log.e(LOG_TAG, "Error reading list.json", e); + return null; + } catch (JSONException e) { + Log.e(LOG_TAG, "Error parsing list.json", e); + return null; + } finally { + IOUtils.safeStreamClose(in); + } try { - String identifier; - while ((identifier = br.readLine()) != null) { - final InputStream pluginIn = getInputStreamFromSearchPluginsJar(identifier + ".xml"); - // pluginIn can be null if the xml file doesn't exist which - // can happen with :hidden plugins + String region = GeckoSharedPrefs.forApp(context).getString(PREF_REGION_KEY, null); + + JSONArray engines; + if (json.has(region)) { + engines = json.getJSONObject(region).getJSONArray("visibleDefaultEngines"); + } else { + engines = json.getJSONObject("default").getJSONArray("visibleDefaultEngines"); + } + for (int i = 0; i < engines.length(); i++) { + final InputStream pluginIn = getInputStreamFromSearchPluginsJar(engines.getString(i) + ".xml"); if (pluginIn != null) { - final SearchEngine engine = createEngineFromInputStream(identifier, pluginIn); - if (engine != null && engine.getName().equals(name)) { - return engine; - } + final SearchEngine engine = createEngineFromInputStream(engines.getString(i), pluginIn); + if (engine != null && engine.getName().equals(name)) { + return engine; + } } } } catch (Throwable e) { Log.e(LOG_TAG, "Error creating shipped search engine from name: " + name, e); - } finally { - try { - br.close(); - } catch (IOException e) { - // Ignore. - } } return null; } /** * Creates a SearchEngine instance for a search plugin in the profile directory. * * This method iterates through the profile searchplugins directory, creating
--- a/mobile/android/base/java/org/mozilla/gecko/util/ColorUtil.java +++ b/mobile/android/base/java/org/mozilla/gecko/util/ColorUtil.java @@ -24,16 +24,29 @@ public class ColorUtil { // 186 chosen rather than the seemingly obvious 128 because of gamma. if (greyValue < 186) { return Color.WHITE; } else { return Color.BLACK; } } + public static Integer parseStringColor(final String color) { + try { + if (color.length() < 7) { + return null; + } + return Color.argb(255, + Integer.valueOf(color.substring(1, 3), 16), + Integer.valueOf(color.substring(3, 5), 16), + Integer.valueOf(color.substring(5, 7), 16)); + } catch (NumberFormatException e) { } + return null; + } + private static int darkenColor(final int color, final double fraction) { return (int) Math.max(color - (color * fraction), 0); } private static int grayscaleFromRGB(final int color) { final int red = Color.red(color); final int green = Color.green(color); final int blue = Color.blue(color);
new file mode 100644 --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java @@ -0,0 +1,112 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko.webapps; + +import java.io.File; +import java.io.IOException; + +import android.app.ActivityManager; +import android.graphics.Bitmap; +import android.os.Build; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; +import android.util.Log; + +import org.json.JSONObject; +import org.json.JSONException; + +import org.mozilla.gecko.AppConstants; +import org.mozilla.gecko.EventDispatcher; +import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.GeckoProfile; +import org.mozilla.gecko.icons.decoders.FaviconDecoder; +import org.mozilla.gecko.R; +import org.mozilla.gecko.util.ColorUtil; +import org.mozilla.gecko.util.EventCallback; +import org.mozilla.gecko.util.FileUtils; +import org.mozilla.gecko.util.GeckoBundle; + +public class WebAppActivity extends GeckoApp { + + public static final String INTENT_KEY = "IS_A_WEBAPP"; + public static final String MANIFEST_PATH = "MANIFEST_PATH"; + + private static final String LOGTAG = "WebAppActivity"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String manifestPath = getIntent().getStringExtra(WebAppActivity.MANIFEST_PATH); + if (manifestPath != null) { + updateFromManifest(manifestPath); + } + } + + @Override + public int getLayout() { + return R.layout.webapp_activity; + } + + private void updateFromManifest(String manifestPath) { + try { + final File manifestFile = new File(manifestPath); + final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile); + final JSONObject manifestField = (JSONObject) manifest.get("manifest"); + final Integer color = readColorFromManifest(manifestField); + final String name = readNameFromManifest(manifestField); + final Bitmap icon = readIconFromManifest(manifest); + final ActivityManager.TaskDescription taskDescription = (color == null) + ? new ActivityManager.TaskDescription(name, icon) + : new ActivityManager.TaskDescription(name, icon, color); + + updateStatusBarColor(color); + setTaskDescription(taskDescription); + + } catch (IOException | JSONException e) { + Log.e(LOGTAG, "Failed to read manifest", e); + } + } + + private void updateStatusBarColor(final Integer themeColor) { + if (themeColor != null && !AppConstants.Versions.preLollipop) { + final Window window = getWindow(); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(ColorUtil.darken(themeColor, 0.25)); + } + } + + private Integer readColorFromManifest(JSONObject manifest) throws JSONException { + final String colorStr = (String) manifest.get("theme_color"); + if (colorStr != null) { + return ColorUtil.parseStringColor(colorStr); + } + return null; + } + + private String readNameFromManifest(JSONObject manifest) throws JSONException { + String name = (String) manifest.get("name"); + if (name == null) { + name = (String) manifest.get("short_name"); + } + if (name == null) { + name = (String) manifest.get("start_url"); + } + return name; + } + + private Bitmap readIconFromManifest(JSONObject manifest) throws JSONException { + final String iconStr = (String) manifest.get("cached_icon"); + if (iconStr != null) { + return FaviconDecoder + .decodeDataURI(getContext(), iconStr) + .getBestBitmap(GeckoAppShell.getPreferredIconSize()); + } + return null; + } +}
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -784,16 +784,17 @@ gbjar.sources += ['java/org/mozilla/geck 'updater/UpdateService.java', 'updater/UpdateServiceHelper.java', 'util/ColorUtil.java', 'util/DrawableUtil.java', 'util/JavaUtil.java', 'util/ResourceDrawableUtils.java', 'util/TouchTargetUtil.java', 'util/ViewUtil.java', + 'webapps/WebAppActivity.java', 'widget/ActivityChooserModel.java', 'widget/AllCapsTextView.java', 'widget/AnchoredPopup.java', 'widget/AnimatedHeightLayout.java', 'widget/BasicColorPicker.java', 'widget/CheckableLinearLayout.java', 'widget/ClickableWhenDisabledEditText.java', 'widget/ContentSecurityDoorHanger.java',
new file mode 100644 --- /dev/null +++ b/mobile/android/base/resources/layout/webapp_activity.xml @@ -0,0 +1,42 @@ +<?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/. --> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/root_layout" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- + This layout is quite complex because GeckoApp accesses all view groups + in this tree. In a perfect world this should just include a GeckoView. + --> + + <view class="org.mozilla.gecko.GeckoApp$MainLayout" + android:id="@+id/main_layout" + android:layout_width="match_parent" + android:layout_below="@+id/toolbar" + android:layout_height="match_parent" + android:background="@android:color/transparent"> + + <RelativeLayout android:id="@+id/gecko_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_below="@+id/tablet_tab_strip" + android:layout_above="@+id/find_in_page"> + + <fragment class="org.mozilla.gecko.GeckoViewFragment" + android:id="@+id/layer_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scrollbars="none"/> + + </RelativeLayout> + + </view> + +</RelativeLayout>
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2203,17 +2203,18 @@ async function installManifest(browser, try { const manifest = await Manifests.getManifest(browser, manifestUrl); await manifest.install(); const icon = await manifest.icon(iconSize); GlobalEventDispatcher.sendRequest({ type: "Website:AppInstalled", icon, name: manifest.name, - start_url: manifest.start_url + start_url: manifest.start_url, + manifest_path: manifest.path }); } catch (err) { Cu.reportError("Failed to install: " + err.message); } } var NativeWindow = { init: function() {
--- a/mobile/locales/Makefile.in +++ b/mobile/locales/Makefile.in @@ -41,18 +41,17 @@ SUBMAKEFILES += \ # gmake -C $obj/mobile/locales ########################################################################### search-jar-default: search-jar ########################################################################### ## Searchlist plugin config plugin-file-array = \ - $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ - $(srcdir)/en-US/searchplugins/list.txt \ + $(srcdir)/search/list.json \ $(NULL) ########################################################################### plugin_file = $(firstword $(plugin-file-array)) plugin-file-ts = $(tgt-gendir)/$(subst $(topsrcdir)/,$(NULL),$(plugin_file)).ts GARBAGE += $(plugin-file-ts) # --------------------------------------------------------------------------- @@ -75,32 +74,32 @@ plugin-file-ts-preqs = \ search-jar-common = tmp-search.jar.mn search-jar = $(tgt-gendir)/$(search-jar-common) search-jar-ts = $(search-jar).ts GARBAGE += $(search-jar) $(search-jar-ts) $(search-jar-common) # --------------------------------------------------------------------------- # search-jar contains a list of providers for the search plugin ########################################################################### -SEARCH_PLUGINS = $(shell cat $(plugin_file)) -SEARCH_PLUGINS := $(subst :hidden,,$(SEARCH_PLUGINS)) +SEARCH_PLUGINS := $(shell $(call py_action,output_searchplugins_list,$(srcdir)/search/list.json $(AB_CD))) $(call errorIfEmpty,SEARCH_PLUGINS) search-jar-preqs = \ $(plugin-file-ts) \ $(if $(IS_LANGUAGE_REPACK),FORCE) \ $(NULL) .PHONY: search-jar search-jar: $(search-jar) $(search-jar): $(search-jar-preqs) @echo '\nGenerating: search-jar' printf '$(AB_CD).jar:' > $@ ln -fn $@ . printf '$(foreach plugin,$(SEARCH_PLUGINS),$(subst __PLUGIN_SUBST__,$(plugin), \n locale/$(AB_CD)/browser/searchplugins/__PLUGIN_SUBST__.xml (__PLUGIN_SUBST__.xml)))' >> $@ + printf '\n locale/$(AB_CD)/browser/searchplugins/list.json (list.json)' >> $@ @echo >> $@ ################### search-dir-deps = \ $(plugin-file) \ $(dir-chrome) \ $(NULL) @@ -110,20 +109,22 @@ search-preqs =\ $(search-jar) \ $(search-dir-deps) \ $(if $(IS_LANGUAGE_REPACK),FORCE) \ $(GLOBAL_DEPS) \ $(NULL) .PHONY: searchplugins searchplugins: $(search-preqs) + $(call py_action,generate_searchjson,$(srcdir)/search/list.json $(AB_CD) $(tgt-gendir)/list.json) $(call py_action,jar_maker,\ $(QUIET) -d $(FINAL_TARGET) \ -s $(topsrcdir)/$(relativesrcdir)/en-US/searchplugins \ -s $(LOCALE_SRCDIR)/searchplugins \ + -s $(tgt-gendir) \ $(MAKE_JARS_FLAGS) $(search-jar)) $(TOUCH) $@ include $(topsrcdir)/config/rules.mk ############# libs-preqs =\
--- a/mobile/locales/jar.mn +++ b/mobile/locales/jar.mn @@ -2,17 +2,16 @@ # 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/. @AB_CD@.jar: % locale browser @AB_CD@ %locale/@AB_CD@/browser/ locale/@AB_CD@/browser/region.properties (%chrome/region.properties) - locale/@AB_CD@/browser/searchplugins/list.txt (%searchplugins/list.txt) # Fennec-specific overrides of generic strings locale/@AB_CD@/browser/netError.dtd (%overrides/netError.dtd) % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd locale/@AB_CD@/browser/appstrings.properties (%overrides/appstrings.properties) % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties locale/@AB_CD@/browser/passwordmgr.properties (%overrides/passwordmgr.properties) % override chrome://passwordmgr/locale/passwordmgr.properties chrome://browser/locale/passwordmgr.properties
new file mode 100644 --- /dev/null +++ b/mobile/locales/search/list.json @@ -0,0 +1,696 @@ +{ + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + ] + }, + "regionOverrides": { + "US": { + "google": "google-nocodes" + }, + "CA": { + "google": "google-nocodes" + }, + "KZ": { + "google": "google-nocodes" + }, + "BY": { + "google": "google-nocodes" + }, + "RU": { + "google": "google-nocodes" + }, + "TR": { + "google": "google-nocodes" + }, + "UA": { + "google": "google-nocodes" + }, + "CN": { + "google": "google-nocodes" + }, + "TW": { + "google": "google-nocodes" + }, + "HK": { + "google": "google-nocodes" + } + }, + "locales": { + "an": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-es", "bing", "twitter", "wikipedia-an" + ] + } + }, + "ar": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-ar" + ] + } + }, + "as": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "wikipedia-as" + ] + } + }, + "ast": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-es", "bing", "amazondotcom", "twitter", "wikipedia-ast" + ] + } + }, + "az": { + "default": { + "visibleDefaultEngines": [ + "google", "bing", "amazondotcom", "azerdict", "duckduckgo", "wikipedia-az" + ] + } + }, + "be": { + "default": { + "visibleDefaultEngines": [ + "google", "be.wikipedia.org", "bing", "yahoo", "yandex.by" + ] + } + }, + "bg": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "wikipedia-bg" + ] + } + }, + "bn-IN": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazondotcom", "rediff", "wikipedia-bn" + ] + } + }, + "br": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-france", "bing", "twitter", "wikipedia-br" + ] + } + }, + "ca": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-ca", "diec2" + ] + } + }, + "cak": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-espanol", "amazondotcom", "duckduckgo", "wikipedia-es" + ] + } + }, + "cs": { + "default": { + "visibleDefaultEngines": [ + "google", "duckduckgo", "heureka-cz", "mapy-cz", "seznam-cz", "twitter", "wikipedia-cz" + ] + } + }, + "cy": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-en-GB", "bing", "amazon-en-GB", "duckduckgo", "wikipedia-cy" + ] + } + }, + "da": { + "default": { + "visibleDefaultEngines": [ + "google", "amazon-co-uk", "twitter", "wikipedia-da" + ] + } + }, + "de": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-de", "bing", "amazon-de", "duckduckgo", "qwant", "twitter", "wikipedia-de" + ] + } + }, + "dsb": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-de", "bing", "amazon-de", "twitter", "wikipedia-dsb" + ] + } + }, + "el": { + "default": { + "visibleDefaultEngines": [ + "google", "skroutz", "twitter", "wikipedia-el" + ] + } + }, + "en-GB": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-en-GB", "bing", "amazon-en-GB", "duckduckgo", "qwant", "twitter", "wikipedia" + ] + } + }, + "en-US": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + ] + }, + "US": { + "visibleDefaultEngines": [ + "yahoo", "google-nocodes", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + ] + }, + "CA": { + "visibleDefaultEngines": [ + "google-nocodes", "yahoo", "bing", "amazondotcom", "duckduckgo", "twitter", "wikipedia" + ] + } + }, + "en-ZA": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia" + ] + } + }, + "eo": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "duckduckgo", "reta-vortaro", "twitter", "wikipedia-eo" + ] + } + }, + "es-AR": { + "default": { + "visibleDefaultEngines": [ + "google", "mercadolibre-ar", "twitter", "wikipedia-es" + ] + } + }, + "es-CL": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-cl", "bing", "drae", "mercadolibre-cl", "twitter", "wikipedia-es" + ] + } + }, + "es-ES": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-es", "bing", "duckduckgo", "twitter", "wikipedia-es" + ] + } + }, + "es-MX": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-mx", "bing", "amazondotcom", "duckduckgo", "mercadolibre-mx", "twitter", "wikipedia-es" + ] + } + }, + "et": { + "default": { + "visibleDefaultEngines": [ + "google", "amazon-co-uk", "twitter", "wikipedia-et" + ] + } + }, + "eu": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-es", "bing", "elebila", "wikipedia-eu" + ] + } + }, + "fa": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "wikipedia-fa" + ] + } + }, + "ff": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-france", "bing", "amazon-france", "wikipedia-fr" + ] + } + }, + "fi": { + "default": { + "visibleDefaultEngines": [ + "google", "amazondotcom", "twitter", "wikipedia-fi", "yahoo-fi" + ] + } + }, + "fr": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-france", "bing", "duckduckgo", "qwant", "twitter", "wikipedia-fr" + ] + } + }, + "fy-NL": { + "default": { + "visibleDefaultEngines": [ + "google", "wikipedia-fy-NL", "bolcom-fy-NL" + ] + } + }, + "ga-IE": { + "default": { + "visibleDefaultEngines": [ + "google", "amazon-en-GB", "tearma", "twitter", "wikipedia-ga-IE" + ] + } + }, + "gd": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-en-GB", "bing", "duckduckgo", "faclair-beag", "wikipedia-gd" + ] + } + }, + "gl": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-es", "bing", "amazondotcom", "twitter", "wikipedia-gl" + ] + } + }, + "gn": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-espanol", "bing", "amazondotcom", "twitter", "wikipedia-gn" + ] + } + }, + "gu-IN": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "wikipedia-gu" + ] + } + }, + "he": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-he" + ] + } + }, + "hi-IN": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "twitter", "wikipedia-hi" + ] + } + }, + "hr": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazon-en-GB", "duckduckgo", "twitter", "wikipedia-hr" + ] + } + }, + "hsb": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-de", "bing", "amazon-de", "twitter", "wikipedia-hsb" + ] + } + }, + "hu": { + "default": { + "visibleDefaultEngines": [ + "google", "sztaki-en-hu", "vatera", "twitter", "wikipedia-hu" + ] + } + }, + "hy-AM": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "list-am", "wikipedia-hy-AM" + ] + } + }, + "id": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-id", "bing", "twitter", "wikipedia-id" + ] + } + }, + "is": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "leit-is", "wikipedia-is" + ] + } + }, + "it": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-it" + ] + } + }, + "ja": { + "default": { + "visibleDefaultEngines": [ + "google", "amazon-jp", "bing", "twitter-ja", "wikipedia-ja", "yahoo-jp" + ] + } + }, + "ka": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "duckduckgo", "wikipedia-ka" + ] + } + }, + "kab": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-france", "bing", "wikipedia-kab" + ] + } + }, + "kk": { + "default": { + "visibleDefaultEngines": [ + "yandex", "google", "yahoo", "bing", "twitter", "wikipedia-kk" + ] + } + }, + "km": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-km" + ] + } + }, + "kn": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "twitter", "wikipedia-kn", "wiktionary-kn" + ] + } + }, + "ko": { + "default": { + "visibleDefaultEngines": [ + "google", "danawa-kr", "twitter", "daum-kr", "naver-kr" + ] + } + }, + "lij": { + "default": { + "visibleDefaultEngines": [ + "google", "amazondotcom", "twitter", "wikipedia" + ] + } + }, + "lo": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "twitter", "wikipedia-lo" + ] + } + }, + "lt": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-lt" + ] + } + }, + "lv": { + "default": { + "visibleDefaultEngines": [ + "google", "salidzinilv", "sslv", "twitter", "wikipedia-lv" + ] + } + }, + "mai": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "twitter", "wikipedia-hi" + ] + } + }, + "ml": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "duckduckgo", "twitter", "wikipedia-ml" + ] + } + }, + "mr": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "rediff", "wikipedia-mr" + ] + } + }, + "ms": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-ms" + ] + } + }, + "my": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-my" + ] + } + }, + "nb-NO": { + "default": { + "visibleDefaultEngines": [ + "google", "duckduckgo", "gulesider-mobile-NO", "twitter", "wikipedia-NO" + ] + } + }, + "ne-NP": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "twitter", "wikipedia-ne" + ] + } + }, + "nl": { + "default": { + "visibleDefaultEngines": [ + "google", "wikipedia-nl", "bolcom-nl" + ] + } + }, + "nn-NO": { + "default": { + "visibleDefaultEngines": [ + "google", "duckduckgo", "gulesider-mobile-NO", "twitter", "wikipedia-NN" + ] + } + }, + "or": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "wikipedia-or", "wiktionary-or" + ] + } + }, + "pa-IN": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "wikipedia-pa" + ] + } + }, + "pl": { + "default": { + "visibleDefaultEngines": [ + "google", "bing", "duckduckgo", "twitter", "wikipedia-pl" + ] + } + }, + "pt-BR": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-br", "bing", "twitter", "wikipedia-br" + ] + } + }, + "pt-PT": { + "default": { + "visibleDefaultEngines": [ + "google", "wikipedia-ptpt" + ] + } + }, + "rm": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-ch", "bing", "duckduckgo", "leo_ende_de", "pledarigrond", "wikipedia-rm" + ] + } + }, + "ro": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-ro" + ] + } + }, + "ru": { + "default": { + "visibleDefaultEngines": [ + "google", "yandex-ru", "twitter", "wikipedia-ru" + ] + } + }, + "sk": { + "default": { + "visibleDefaultEngines": [ + "google", "azet-sk", "slovnik-sk", "twitter", "wikipedia-sk" + ] + } + }, + "sl": { + "default": { + "visibleDefaultEngines": [ + "google", "ceneje", "odpiralni", "twitter", "wikipedia-sl" + ] + } + }, + "son": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-france", "bing", "amazon-fr", "twitter", "wikipedia-fr" + ] + } + }, + "sq": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazon-en-GB", "twitter", "wikipedia-sq" + ] + } + }, + "sr": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "duckduckgo", "twitter", "wikipedia-sr" + ] + } + }, + "sv-SE": { + "default": { + "visibleDefaultEngines": [ + "google", "prisjakt-sv-SE", "twitter", "wikipedia-sv-SE" + ] + } + }, + "ta": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "duckduckgo", "wikipedia-ta", "wiktionary-ta" + ] + } + }, + "te": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "wikipedia-te", "wiktionary-te" + ] + } + }, + "th": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-th" + ] + } + }, + "tr": { + "default": { + "visibleDefaultEngines": [ + "yandex-tr", "google", "twitter", "wikipedia-tr" + ] + } + }, + "trs": { + "default": { + "searchDefault": "Google", + "visibleDefaultEngines": [ + "amazondotcom", "bing", "google", "twitter", "wikipedia-es", "yahoo-espanol" + ] + } + }, + "uk": { + "default": { + "visibleDefaultEngines": [ + "google", "twitter", "wikipedia-uk", "yandex-market" + ] + } + }, + "ur": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo-in", "bing", "amazon-in", "duckduckgo", "twitter", "wikipedia-ur" + ] + } + }, + "uz": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "amazondotcom", "twitter", "wikipedia-uz" + ] + } + }, + "vi": { + "default": { + "visibleDefaultEngines": [ + "google", "amazondotcom", "twitter", "wikipedia-vi" + ] + } + }, + "xh": { + "default": { + "visibleDefaultEngines": [ + "google", "yahoo", "bing", "twitter", "wikipedia" + ] + } + }, + "zh-CN": { + "default": { + "visibleDefaultEngines": [ + "google", "baidu", "bing", "taobao", "wikipedia-zh-CN" + ] + } + }, + "zh-TW": { + "default": { + "visibleDefaultEngines": [ + "google", "bing", "duckduckgo", "wikipedia-zh-TW" + ] + } + } + } +}
--- a/mozglue/android/APKOpen.cpp +++ b/mozglue/android/APKOpen.cpp @@ -472,10 +472,14 @@ ChildProcessInit(int argc, char* argv[]) XREChildData childData; return NS_FAILED(gBootstrap->XRE_InitChildProcess(argc, argv, &childData)); } extern "C" APKOPEN_EXPORT jboolean MOZ_JNICALL Java_org_mozilla_gecko_mozglue_GeckoLoader_neonCompatible(JNIEnv *jenv, jclass jc) { +#ifdef __ARM_EABI__ return mozilla::supports_neon(); +#else + return true; +#endif // __ARM_EABI__ }
--- a/testing/web-platform/tests/webvtt/webvtt-file-format-parsing/webvtt-file-parsing/support/newlines.vtt +++ b/testing/web-platform/tests/webvtt/webvtt-file-format-parsing/webvtt-file-parsing/support/newlines.vtt @@ -4,9 +4,8 @@ lf 00:00:00.000 --> 00:00:01.000 {"id":"lf"} crlf 00:00:00.000 --> 00:00:01.000 {"id":"crlf"} lfcr 00:00:00.000 --> 00:00:01.000 {"id":"lfcr"} - \ No newline at end of file
--- a/toolkit/components/extensions/test/mochitest/chrome.ini +++ b/toolkit/components/extensions/test/mochitest/chrome.ini @@ -13,16 +13,17 @@ support-files = redirect_auto.sjs tags = webextensions in-process-webextensions [test_chrome_ext_background_debug_global.html] skip-if = (os == 'android') # android doesn't have devtools [test_chrome_ext_background_page.html] skip-if = (toolkit == 'android') # android doesn't have devtools [test_chrome_ext_eventpage_warning.html] +[test_chrome_ext_contentscript_data_uri.html] [test_chrome_ext_contentscript_unrecognizedprop_warning.html] [test_chrome_ext_hybrid_addons.html] [test_chrome_ext_trustworthy_origin.html] [test_chrome_ext_webnavigation_resolved_urls.html] [test_chrome_native_messaging_paths.html] skip-if = os != "mac" && os != "linux" [test_ext_cookies_expiry.html] [test_ext_cookies_permissions_bad.html]
new file mode 100644 --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_contentscript_data_uri.html @@ -0,0 +1,98 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test content script matching a data: URI</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script src="head.js"></script> + <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> +</head> +<body> + +<script> +"use strict"; + +add_task(function* test_contentscript_data_uri() { + const target = ExtensionTestUtils.loadExtension({ + files: { + "page.html": `<!DOCTYPE html> + <meta charset="utf-8"> + <iframe id="inherited" src="data:text/html;charset=utf-8,inherited"></iframe> + `, + }, + background() { + browser.test.sendMessage("page", browser.runtime.getURL("page.html")); + }, + }); + + const scripts = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["webNavigation"], + content_scripts: [{ + all_frames: true, + matches: ["<all_urls>"], + run_at: "document_start", + css: ["all_urls.css"], + js: ["all_urls.js"], + }], + }, + files: { + "all_urls.css": ` + body { background: yellow; } + `, + "all_urls.js": function() { + document.body.style.color = "red"; + browser.test.assertTrue(location.protocol !== "data:", + `Matched document not a data URI: ${location.href}`); + }, + }, + background() { + browser.webNavigation.onCompleted.addListener(({url, frameId}) => { + browser.test.log(`Document loading complete: ${url}`); + if (frameId === 0) { + browser.test.sendMessage("tab-ready", url); + } + }); + }, + }); + + yield target.startup(); + yield scripts.startup(); + + // Test extension page with a data: iframe. + const page = yield target.awaitMessage("page"); + const win = window.open(page); + + yield scripts.awaitMessage("tab-ready"); + is(win.location.href, page, "Extension page loaded into a tab"); + is(win.document.readyState, "complete", "Page finished loading"); + + const iframe = win.document.getElementById("inherited").contentWindow; + is(iframe.document.readyState, "complete", "iframe finished loading"); + + const style1 = iframe.getComputedStyle(iframe.document.body); + is(style1.color, "rgb(0, 0, 0)", "iframe text color is unmodified"); + is(style1.backgroundColor, "rgba(0, 0, 0, 0)", "iframe background unmodified"); + + // Test extension tab navigated to a data: URI. + const data = "data:text/html;charset=utf-8,also-inherits"; + win.location.href = data; + + yield scripts.awaitMessage("tab-ready"); + is(win.location.href, data, "Extension tab navigated to a data: URI"); + is(win.document.readyState, "complete", "Tab finished loading"); + + const style2 = win.getComputedStyle(win.document.body); + is(style2.color, "rgb(0, 0, 0)", "Tab text color is unmodified"); + is(style2.backgroundColor, "rgba(0, 0, 0, 0)", "Tab background unmodified"); + + win.close(); + yield target.unload(); + yield scripts.unload(); +}); + +</script> + +</body> +</html>
--- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -147,16 +147,26 @@ var snapshotFormatters = { $.new("td", extension.name), $.new("td", extension.version), $.new("td", extension.isActive), $.new("td", extension.id), ]); })); }, + features: function features(data) { + $.append($("features-tbody"), data.map(function(feature) { + return $.new("tr", [ + $.new("td", feature.name), + $.new("td", feature.version), + $.new("td", feature.id), + ]); + })); + }, + experiments: function experiments(data) { $.append($("experiments-tbody"), data.map(function(experiment) { return $.new("tr", [ $.new("td", experiment.name), $.new("td", experiment.id), $.new("td", experiment.description), $.new("td", experiment.active), $.new("td", experiment.endDate),
--- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -295,16 +295,40 @@ <a href="about:crashes" id="crashes-allReportsWithPending" class="block">&aboutSupport.crashes.allReports;</a> </p> <p id="crashes-noConfig" class="hidden no-copy">&aboutSupport.crashes.noConfig;</p> #endif <!-- - - - - - - - - - - - - - - - - - - - - --> <h2 class="major-section"> + &aboutSupport.featuresTitle; + </h2> + + <table> + <thead> + <tr> + <th> + &aboutSupport.featureName; + </th> + <th> + &aboutSupport.featureVersion; + </th> + <th> + &aboutSupport.featureId; + </th> + </tr> + </thead> + <tbody id="features-tbody"> + </tbody> + </table> + + <!-- - - - - - - - - - - - - - - - - - - - - --> + + <h2 class="major-section"> &aboutSupport.extensionsTitle; </h2> <table> <thead> <tr> <th> &aboutSupport.extensionName;
--- a/toolkit/locales/en-US/chrome/global/aboutSupport.dtd +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.dtd @@ -20,16 +20,21 @@ This is likely the same like id.heading <!ENTITY aboutSupport.crashes.noConfig "This application has not been configured to display crash reports."> <!ENTITY aboutSupport.extensionsTitle "Extensions"> <!ENTITY aboutSupport.extensionName "Name"> <!ENTITY aboutSupport.extensionEnabled "Enabled"> <!ENTITY aboutSupport.extensionVersion "Version"> <!ENTITY aboutSupport.extensionId "ID"> +<!ENTITY aboutSupport.featuresTitle "&brandShortName; Features"> +<!ENTITY aboutSupport.featureName "Name"> +<!ENTITY aboutSupport.featureVersion "Version"> +<!ENTITY aboutSupport.featureId "ID"> + <!ENTITY aboutSupport.experimentsTitle "Experimental Features"> <!ENTITY aboutSupport.experimentName "Name"> <!ENTITY aboutSupport.experimentId "ID"> <!ENTITY aboutSupport.experimentDescription "Description"> <!ENTITY aboutSupport.experimentActive "Active"> <!ENTITY aboutSupport.experimentEndDate "End Date"> <!ENTITY aboutSupport.experimentHomepage "Homepage"> <!ENTITY aboutSupport.experimentBranch "Branch">
--- a/toolkit/modules/Troubleshoot.jsm +++ b/toolkit/modules/Troubleshoot.jsm @@ -237,16 +237,17 @@ var dataProviders = { const keyMozilla = Services.urlFormatter.formatURL("%MOZILLA_API_KEY%").trim(); data.keyMozillaFound = keyMozilla != "no-mozilla-api-key" && keyMozilla.length > 0; done(data); }, extensions: function extensions(done) { AddonManager.getAddonsByTypes(["extension"], function(extensions) { + extensions = extensions.filter(e => !e.isSystem); extensions.sort(function(a, b) { if (a.isActive != b.isActive) return b.isActive ? 1 : -1; // In some unfortunate cases addon names can be null. let aname = a.name || null; let bname = b.name || null; let lc = aname.localeCompare(bname); @@ -261,16 +262,40 @@ var dataProviders = { return props.reduce(function(extData, prop) { extData[prop] = ext[prop]; return extData; }, {}); })); }); }, + features: function features(done) { + AddonManager.getAddonsByTypes(["extension"], function(features) { + features = features.filter(f => f.isSystem); + features.sort(function(a, b) { + // In some unfortunate cases addon names can be null. + let aname = a.name || null; + let bname = b.name || null; + let lc = aname.localeCompare(bname); + if (lc != 0) + return lc; + if (a.version != b.version) + return a.version > b.version ? 1 : -1; + return 0; + }); + let props = ["name", "version", "id"]; + done(features.map(function(f) { + return props.reduce(function(fData, prop) { + fData[prop] = f[prop]; + return fData; + }, {}); + })); + }); + }, + experiments: function experiments(done) { if (Experiments === undefined) { done([]); return; } // getExperiments promises experiment history Experiments.instance().getExperiments().then(
--- a/toolkit/modules/tests/browser/browser_Troubleshoot.js +++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js @@ -200,16 +200,37 @@ const SNAPSHOT_SCHEMA = { }, isActive: { required: true, type: "boolean", }, }, }, }, + features: { + required: true, + type: "array", + items: { + type: "object", + properties: { + name: { + required: true, + type: "string", + }, + version: { + required: true, + type: "string", + }, + id: { + required: true, + type: "string", + }, + }, + }, + }, modifiedPreferences: { required: true, type: "object", }, lockedPreferences: { required: true, type: "object", },