mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c. a=merge
This commit is contained in:
commit
d89a05d20b
@ -13,6 +13,8 @@ Cu.import("resource://gre/modules/WindowsPrefSync.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
|
||||
"resource:///modules/BrowserUITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
"resource:///modules/E10SUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
@ -166,6 +168,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
|
||||
"resource:///modules/sessionstore/SessionStore.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TabState",
|
||||
"resource:///modules/sessionstore/TabState.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
|
||||
"resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
@ -766,6 +771,36 @@ function gKeywordURIFixup({ target: browser, data: fixupInfo }) {
|
||||
gDNSService.asyncResolve(hostName, 0, onLookupComplete, Services.tm.mainThread);
|
||||
}
|
||||
|
||||
// Called when a docshell has attempted to load a page in an incorrect process.
|
||||
// This function is responsible for loading the page in the correct process.
|
||||
function RedirectLoad({ target: browser, data }) {
|
||||
let tab = gBrowser._getTabForBrowser(browser);
|
||||
// Flush the tab state before getting it
|
||||
TabState.flush(browser);
|
||||
let tabState = JSON.parse(SessionStore.getTabState(tab));
|
||||
|
||||
if (data.historyIndex < 0) {
|
||||
// Add a pseudo-history state for the new url to load
|
||||
let newEntry = {
|
||||
url: data.uri,
|
||||
referrer: data.referrer,
|
||||
};
|
||||
|
||||
tabState.entries = tabState.entries.slice(0, tabState.index);
|
||||
tabState.entries.push(newEntry);
|
||||
tabState.index++;
|
||||
tabState.userTypedValue = null;
|
||||
}
|
||||
else {
|
||||
// Update the history state to point to the requested index
|
||||
tabState.index = data.historyIndex + 1;
|
||||
}
|
||||
|
||||
// SessionStore takes care of setting the browser remoteness before restoring
|
||||
// history into it.
|
||||
SessionStore.setTabState(tab, JSON.stringify(tabState));
|
||||
}
|
||||
|
||||
var gBrowserInit = {
|
||||
delayedStartupFinished: false,
|
||||
|
||||
@ -1064,6 +1099,7 @@ var gBrowserInit = {
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
|
||||
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
|
||||
window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
|
||||
window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
|
||||
|
||||
BrowserOffline.init();
|
||||
OfflineApps.init();
|
||||
@ -1372,6 +1408,7 @@ var gBrowserInit = {
|
||||
Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
|
||||
Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
|
||||
window.messageManager.removeMessageListener("Browser:URIFixup", gKeywordURIFixup);
|
||||
window.messageManager.removeMessageListener("Browser:LoadURI", RedirectLoad);
|
||||
|
||||
try {
|
||||
gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
|
||||
@ -3551,6 +3588,28 @@ var XULBrowserWindow = {
|
||||
return target;
|
||||
},
|
||||
|
||||
// Check whether this URI should load in the current process
|
||||
shouldLoadURI: function(aDocShell, aURI, aReferrer) {
|
||||
if (!gMultiProcessBrowser)
|
||||
return true;
|
||||
|
||||
let browser = aDocShell.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
|
||||
// Ignore loads that aren't in the main tabbrowser
|
||||
if (browser.localName != "browser" || browser.getTabBrowser() != gBrowser)
|
||||
return true;
|
||||
|
||||
if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
|
||||
E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
onProgressChange: function (aWebProgress, aRequest,
|
||||
aCurSelfProgress, aMaxSelfProgress,
|
||||
aCurTotalProgress, aMaxTotalProgress) {
|
||||
|
@ -9,6 +9,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/ContentWebRTC.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
"resource:///modules/E10SUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler",
|
||||
@ -683,6 +685,16 @@ let WebBrowserChrome = {
|
||||
onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
|
||||
return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab);
|
||||
},
|
||||
|
||||
// Check whether this URI should load in the current process
|
||||
shouldLoadURI: function(aDocShell, aURI, aReferrer) {
|
||||
if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
|
||||
E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
|
||||
|
@ -142,6 +142,9 @@ let gPage = {
|
||||
*/
|
||||
handleEvent: function Page_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "load":
|
||||
this.onPageVisibleAndLoaded();
|
||||
break;
|
||||
case "unload":
|
||||
gAllPages.unregister(this);
|
||||
break;
|
||||
@ -183,39 +186,41 @@ let gPage = {
|
||||
}
|
||||
}
|
||||
|
||||
// Allow the document to reflow so the page has sizing info
|
||||
let i = 0;
|
||||
let checkSizing = _ => setTimeout(_ => {
|
||||
if (document.documentElement.clientWidth == 0) {
|
||||
checkSizing();
|
||||
}
|
||||
else {
|
||||
this.onPageFirstSized();
|
||||
}
|
||||
});
|
||||
checkSizing();
|
||||
if (document.readyState == "complete") {
|
||||
this.onPageVisibleAndLoaded();
|
||||
} else {
|
||||
addEventListener("load", this);
|
||||
}
|
||||
},
|
||||
|
||||
onPageFirstSized: function() {
|
||||
// Work backwards to find the first visible site from the end
|
||||
let {sites} = gGrid;
|
||||
let lastIndex = sites.length;
|
||||
while (lastIndex-- > 0) {
|
||||
let site = sites[lastIndex];
|
||||
if (site) {
|
||||
let {node} = site;
|
||||
let rect = node.getBoundingClientRect();
|
||||
let target = document.elementFromPoint(rect.x + rect.width / 2,
|
||||
rect.y + rect.height / 2);
|
||||
if (node.contains(target)) {
|
||||
break;
|
||||
onPageVisibleAndLoaded() {
|
||||
// Send the index of the last visible tile.
|
||||
this.reportLastVisibleTileIndex();
|
||||
|
||||
// Show the panel now that anchors are sized
|
||||
gIntro.showIfNecessary();
|
||||
},
|
||||
|
||||
reportLastVisibleTileIndex() {
|
||||
let cwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
let rect = cwu.getBoundsWithoutFlushing(gGrid.node);
|
||||
let nodes = cwu.nodesFromRect(rect.left, rect.top, 0, rect.width,
|
||||
rect.height, 0, true, false);
|
||||
|
||||
let i = -1;
|
||||
let lastIndex = -1;
|
||||
let sites = gGrid.sites;
|
||||
|
||||
for (let node of nodes) {
|
||||
if (node.classList && node.classList.contains("newtab-cell")) {
|
||||
if (sites[++i]) {
|
||||
lastIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DirectoryLinksProvider.reportSitesAction(gGrid.sites, "view", lastIndex);
|
||||
|
||||
// Show the panel now that anchors are sized
|
||||
gIntro.showIfNecessary();
|
||||
DirectoryLinksProvider.reportSitesAction(sites, "view", lastIndex);
|
||||
}
|
||||
};
|
||||
|
@ -9,11 +9,6 @@
|
||||
%tabBrowserDTD;
|
||||
]>
|
||||
|
||||
# MAKE_E10S_WORK surrounds code needed to have the front-end try to be smart
|
||||
# about using non-remote browsers for loading certain URIs when remote tabs
|
||||
# (browser.tabs.remote) are enabled.
|
||||
#define MAKE_E10S_WORK 1
|
||||
|
||||
<bindings id="tabBrowserBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
@ -1474,49 +1469,18 @@
|
||||
</body>
|
||||
</method>
|
||||
|
||||
#ifdef MAKE_E10S_WORK
|
||||
<method name="updateBrowserRemotenessByURL">
|
||||
<parameter name="aBrowser"/>
|
||||
<parameter name="aURL"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let shouldBeRemote = this._shouldBrowserBeRemote(aURL);
|
||||
let shouldBeRemote = gMultiProcessBrowser &&
|
||||
E10SUtils.shouldBrowserBeRemote(aURL);
|
||||
return this.updateBrowserRemoteness(aBrowser, shouldBeRemote);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Returns true if we want to load the content for this URL in a
|
||||
remote process. Eventually this should just check whether aURL
|
||||
is unprivileged. Right now, though, we would like to load
|
||||
some unprivileged URLs (like about:neterror) in the main
|
||||
process since they interact with chrome code through
|
||||
BrowserOnClick.
|
||||
-->
|
||||
<method name="_shouldBrowserBeRemote">
|
||||
<parameter name="aURL"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (!gMultiProcessBrowser)
|
||||
return false;
|
||||
|
||||
// loadURI in browser.xml treats null as about:blank
|
||||
if (!aURL)
|
||||
aURL = "about:blank";
|
||||
|
||||
if (aURL.startsWith("about:") &&
|
||||
aURL.toLowerCase() != "about:home" &&
|
||||
aURL.toLowerCase() != "about:blank") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
#endif
|
||||
|
||||
<method name="addTab">
|
||||
<parameter name="aURI"/>
|
||||
<parameter name="aReferrerURI"/>
|
||||
@ -1558,11 +1522,7 @@
|
||||
t.setAttribute("onerror", "this.removeAttribute('image');");
|
||||
t.className = "tabbrowser-tab";
|
||||
|
||||
#ifdef MAKE_E10S_WORK
|
||||
let remote = this._shouldBrowserBeRemote(aURI);
|
||||
#else
|
||||
let remote = gMultiProcessBrowser;
|
||||
#endif
|
||||
let remote = gMultiProcessBrowser && E10SUtils.shouldBrowserBeRemote(aURI);
|
||||
if (remote)
|
||||
t.setAttribute("remote", "true");
|
||||
|
||||
@ -2790,18 +2750,7 @@
|
||||
<parameter name="aCharset"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
#ifdef MAKE_E10S_WORK
|
||||
this.updateBrowserRemotenessByURL(this.mCurrentBrowser, aURI);
|
||||
try {
|
||||
#endif
|
||||
return this.mCurrentBrowser.loadURI(aURI, aReferrerURI, aCharset);
|
||||
#ifdef MAKE_E10S_WORK
|
||||
} catch (e) {
|
||||
let url = this.mCurrentBrowser.currentURI.spec;
|
||||
this.updateBrowserRemotenessByURL(this.mCurrentBrowser, url);
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
@ -2815,18 +2764,7 @@
|
||||
<parameter name="aPostData"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
#ifdef MAKE_E10S_WORK
|
||||
this.updateBrowserRemotenessByURL(this.mCurrentBrowser, aURI);
|
||||
try {
|
||||
#endif
|
||||
return this.mCurrentBrowser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);
|
||||
#ifdef MAKE_E10S_WORK
|
||||
} catch (e) {
|
||||
let url = this.mCurrentBrowser.currentURI.spec;
|
||||
this.updateBrowserRemotenessByURL(this.mCurrentBrowser, url);
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -483,4 +483,6 @@ skip-if = e10s
|
||||
[browser_addCertException.js]
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (content.document.getElementById)
|
||||
[browser_bug1045809.js]
|
||||
skip-if = e10s
|
||||
skip-if = e10s # Bug 1068360 - [e10s] Mixed content blocker doorhanger doesn't work
|
||||
[browser_e10s_switchbrowser.js]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const URL = "about:robots";
|
||||
const URL = "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
|
||||
|
||||
function test() {
|
||||
let win;
|
||||
|
104
browser/base/content/test/general/browser_e10s_switchbrowser.js
Normal file
104
browser/base/content/test/general/browser_e10s_switchbrowser.js
Normal file
@ -0,0 +1,104 @@
|
||||
const DUMMY_PATH = "browser/browser/base/content/test/general/dummy_page.html";
|
||||
|
||||
const gExpectedHistory = {
|
||||
index: -1,
|
||||
entries: []
|
||||
};
|
||||
|
||||
function check_history() {
|
||||
let webNav = gBrowser.webNavigation;
|
||||
let sessionHistory = webNav.sessionHistory;
|
||||
|
||||
let count = sessionHistory.count;
|
||||
is(count, gExpectedHistory.entries.length, "Should have the right number of history entries");
|
||||
is(sessionHistory.index, gExpectedHistory.index, "Should have the right history index");
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
let entry = sessionHistory.getEntryAtIndex(i, false);
|
||||
is(entry.URI.spec, gExpectedHistory.entries[i].uri, "Should have the right URI");
|
||||
is(entry.title, gExpectedHistory.entries[i].title, "Should have the right title");
|
||||
}
|
||||
}
|
||||
|
||||
// Waits for a load and updates the known history
|
||||
let waitForLoad = Task.async(function*(uri) {
|
||||
info("Loading " + uri);
|
||||
gBrowser.loadURI(uri);
|
||||
|
||||
yield waitForDocLoadComplete();
|
||||
gExpectedHistory.index++;
|
||||
gExpectedHistory.entries.push({
|
||||
uri: gBrowser.currentURI.spec,
|
||||
title: gBrowser.contentTitle
|
||||
});
|
||||
});
|
||||
|
||||
let back = Task.async(function*() {
|
||||
info("Going back");
|
||||
gBrowser.goBack();
|
||||
yield waitForDocLoadComplete();
|
||||
gExpectedHistory.index--;
|
||||
});
|
||||
|
||||
let forward = Task.async(function*() {
|
||||
info("Going forward");
|
||||
gBrowser.goForward();
|
||||
yield waitForDocLoadComplete();
|
||||
gExpectedHistory.index++;
|
||||
});
|
||||
|
||||
// Tests that navigating from a page that should be in the remote process and
|
||||
// a page that should be in the main process works and retains history
|
||||
add_task(function*() {
|
||||
SimpleTest.requestCompleteLog();
|
||||
|
||||
let remoting = Services.prefs.getBoolPref("browser.tabs.remote.autostart");
|
||||
let expectedRemote = remoting ? "true" : "";
|
||||
|
||||
info("1");
|
||||
// Create a tab and load a remote page in it
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
|
||||
yield waitForLoad("http://example.org/" + DUMMY_PATH);
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
|
||||
|
||||
info("2");
|
||||
// Load another page
|
||||
yield waitForLoad("http://example.com/" + DUMMY_PATH);
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("3");
|
||||
// Load a non-remote page
|
||||
yield waitForLoad("about:robots");
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("4");
|
||||
// Load a remote page
|
||||
yield waitForLoad("http://example.org/" + DUMMY_PATH);
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("5");
|
||||
yield back();
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("6");
|
||||
yield back();
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("7");
|
||||
yield forward();
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), "", "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("8");
|
||||
yield forward();
|
||||
is(gBrowser.selectedTab.getAttribute("remote"), expectedRemote, "Remote attribute should be correct");
|
||||
check_history();
|
||||
|
||||
info("9");
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
@ -23,9 +23,6 @@ const EXPECTED_REFLOWS = [
|
||||
"openUILinkIn@chrome://browser/content/utilityOverlay.js|" +
|
||||
"BrowserOpenTab@chrome://browser/content/browser.js|",
|
||||
|
||||
// unpreloaded newtab pages explicitly waits for reflows for sizing
|
||||
"gPage.onPageFirstVisible/checkSizing/<@chrome://browser/content/newtab/newTab.js|",
|
||||
|
||||
// accessing element.scrollPosition in _fillTrailingGap() flushes layout
|
||||
"get_scrollPosition@chrome://global/content/bindings/scrollbox.xml|" +
|
||||
"_fillTrailingGap@chrome://browser/content/tabbrowser.xml|" +
|
||||
|
@ -454,12 +454,15 @@ function waitForDocLoadComplete(aBrowser=gBrowser) {
|
||||
let deferred = Promise.defer();
|
||||
let progressListener = {
|
||||
onStateChange: function (webProgress, req, flags, status) {
|
||||
let docStart = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
|
||||
Ci.nsIWebProgressListener.STATE_STOP;
|
||||
info("Saw state " + flags.toString(16));
|
||||
if ((flags & docStart) == docStart) {
|
||||
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
|
||||
Ci.nsIWebProgressListener.STATE_STOP;
|
||||
info("Saw state " + flags.toString(16) + " and status " + status.toString(16));
|
||||
|
||||
// When a load needs to be retargetted to a new process it is cancelled
|
||||
// with NS_BINDING_ABORTED so ignore that case
|
||||
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
|
||||
aBrowser.removeProgressListener(progressListener);
|
||||
info("Browser loaded");
|
||||
info("Browser loaded " + aBrowser.contentWindow.location);
|
||||
deferred.resolve();
|
||||
}
|
||||
},
|
||||
|
@ -28,6 +28,9 @@ skip-if = os == "mac" # Intermittent failures, bug 898317
|
||||
[browser_newtab_intro.js]
|
||||
[browser_newtab_perwindow_private_browsing.js]
|
||||
[browser_newtab_reportLinkAction.js]
|
||||
[browser_newtab_reflow_load.js]
|
||||
support-files =
|
||||
content-reflows.js
|
||||
[browser_newtab_reset.js]
|
||||
[browser_newtab_search.js]
|
||||
support-files =
|
||||
|
@ -0,0 +1,38 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const FRAME_SCRIPT = getRootDirectory(gTestPath) + "content-reflows.js";
|
||||
const ADDITIONAL_WAIT_MS = 2000;
|
||||
|
||||
/*
|
||||
* Ensure that loading about:newtab doesn't cause uninterruptible reflows.
|
||||
*/
|
||||
function runTests() {
|
||||
gBrowser.selectedTab = gBrowser.addTab("about:blank", {animate: false});
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
yield whenBrowserLoaded(browser);
|
||||
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript(FRAME_SCRIPT, true);
|
||||
mm.addMessageListener("newtab-reflow", ({data: stack}) => {
|
||||
ok(false, `unexpected uninterruptible reflow ${stack}`);
|
||||
});
|
||||
|
||||
browser.loadURI("about:newtab");
|
||||
yield whenBrowserLoaded(browser);
|
||||
|
||||
// Wait some more to catch sync reflows after the page has loaded.
|
||||
yield setTimeout(TestRunner.next, ADDITIONAL_WAIT_MS);
|
||||
|
||||
// Clean up.
|
||||
gBrowser.removeCurrentTab({animate: false});
|
||||
}
|
||||
|
||||
function whenBrowserLoaded(browser) {
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
executeSoon(TestRunner.next);
|
||||
}, true);
|
||||
}
|
26
browser/base/content/test/newtab/content-reflows.js
Normal file
26
browser/base/content/test/newtab/content-reflows.js
Normal file
@ -0,0 +1,26 @@
|
||||
/* 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/. */
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
docShell.addWeakReflowObserver({
|
||||
reflow() {
|
||||
// Gather information about the current code path.
|
||||
let path = (new Error().stack).split("\n").slice(1).join("\n");
|
||||
if (path) {
|
||||
sendSyncMessage("newtab-reflow", path);
|
||||
}
|
||||
},
|
||||
|
||||
reflowInterruptible() {
|
||||
// We're not interested in interruptible reflows.
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
});
|
||||
})();
|
@ -235,12 +235,12 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
min-height: 264px;
|
||||
min-height: 230px;
|
||||
}
|
||||
|
||||
.incoming-call-action-group {
|
||||
display: flex;
|
||||
padding: 2.5em 0;
|
||||
padding: 2.5em 0 0 0;
|
||||
width: 100%;
|
||||
justify-content: space-around;
|
||||
}
|
||||
@ -363,13 +363,16 @@
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.feedback p {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.feedback h3 {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
margin-bottom: 14px;
|
||||
margin-top: 14px;
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
|
||||
.feedback .faces {
|
||||
|
@ -366,12 +366,15 @@ loop.shared.models = (function(l10n) {
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a l10n rror notification to the stack and renders it.
|
||||
* Adds a l10n error notification to the stack and renders it.
|
||||
*
|
||||
* @param {String} messageId L10n message id
|
||||
* @param {Object} [l10nProps] An object with variables to be interpolated
|
||||
* into the translation. All members' values must be
|
||||
* strings or numbers.
|
||||
*/
|
||||
errorL10n: function(messageId) {
|
||||
this.error(l10n.get(messageId));
|
||||
errorL10n: function(messageId, l10nProps) {
|
||||
this.error(l10n.get(messageId, l10nProps));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -58,6 +58,15 @@ loop.shared.utils = (function() {
|
||||
return platform.indexOf("Firefox") !== -1;
|
||||
},
|
||||
|
||||
isFirefoxOS: function(platform) {
|
||||
// So far WebActivities are exposed only in FxOS, but they may be
|
||||
// exposed in Firefox Desktop soon, so we check for its existence
|
||||
// and also check if the UA belongs to a mobile platform.
|
||||
// XXX WebActivities are also exposed in WebRT on Firefox for Android,
|
||||
// so we need a better check. Bug 1065403.
|
||||
return !!window.MozActivity && /mobi/i.test(platform);
|
||||
},
|
||||
|
||||
isIOS: function(platform) {
|
||||
return this._iOSRegex.test(platform);
|
||||
},
|
||||
|
@ -73,3 +73,6 @@ config:
|
||||
@echo "loop.config.serverUrl = '`echo $(LOOP_SERVER_URL)`';" >> content/config.js
|
||||
@echo "loop.config.feedbackApiUrl = '`echo $(LOOP_FEEDBACK_API_URL)`';" >> content/config.js
|
||||
@echo "loop.config.feedbackProductName = '`echo $(LOOP_FEEDBACK_PRODUCT_NAME)`';" >> content/config.js
|
||||
@echo "loop.config.fxosApp = loop.config.fxosApp || {};" >> content/config.js
|
||||
@echo "loop.config.fxosApp.name = 'Loop';" >> content/config.js
|
||||
@echo "loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';" >> content/config.js
|
||||
|
@ -4,7 +4,7 @@
|
||||
* 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/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
/* global loop:true, React, MozActivity */
|
||||
/* jshint newcap:false, maxlen:false */
|
||||
|
||||
var loop = loop || {};
|
||||
@ -122,6 +122,95 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The Firefox Marketplace exposes a web page that contains a postMesssage
|
||||
* based API that wraps a small set of functionality from the WebApps API
|
||||
* that allow us to request the installation of apps given their manifest
|
||||
* URL. We will be embedding the content of this web page within an hidden
|
||||
* iframe in case that we need to request the installation of the FxOS Loop
|
||||
* client.
|
||||
*/
|
||||
var FxOSHiddenMarketplace = React.createClass({displayName: 'FxOSHiddenMarketplace',
|
||||
render: function() {
|
||||
return React.DOM.iframe({id: "marketplace", src: this.props.marketplaceSrc, hidden: true});
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
// This happens only once when we change the 'src' property of the iframe.
|
||||
if (this.props.onMarketplaceMessage) {
|
||||
// The reason for listening on the global window instead of on the
|
||||
// iframe content window is because the Marketplace is doing a
|
||||
// window.top.postMessage.
|
||||
window.addEventListener("message", this.props.onMarketplaceMessage);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var FxOSConversationModel = Backbone.Model.extend({
|
||||
setupOutgoingCall: function() {
|
||||
// The FxOS Loop client exposes a "loop-call" activity. If we get the
|
||||
// activity onerror callback it means that there is no "loop-call"
|
||||
// activity handler available and so no FxOS Loop client installed.
|
||||
var request = new MozActivity({
|
||||
name: "loop-call",
|
||||
data: {
|
||||
type: "loop/token",
|
||||
token: this.get("loopToken"),
|
||||
callerId: this.get("callerId"),
|
||||
callType: this.get("callType")
|
||||
}
|
||||
});
|
||||
|
||||
request.onsuccess = function() {};
|
||||
|
||||
request.onerror = (function(event) {
|
||||
if (event.target.error.name !== "NO_PROVIDER") {
|
||||
console.error ("Unexpected " + event.target.error.name);
|
||||
this.trigger("session:error", "fxos_app_needed", {
|
||||
fxosAppName: loop.config.fxosApp.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.trigger("fxos:app-needed");
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
onMarketplaceMessage: function(event) {
|
||||
var message = event.data;
|
||||
switch (message.name) {
|
||||
case "loaded":
|
||||
var marketplace = window.document.getElementById("marketplace");
|
||||
// Once we have it loaded, we request the installation of the FxOS
|
||||
// Loop client app. We will be receiving the result of this action
|
||||
// via postMessage from the child iframe.
|
||||
marketplace.contentWindow.postMessage({
|
||||
"name": "install-package",
|
||||
"data": {
|
||||
"product": {
|
||||
"name": loop.config.fxosApp.name,
|
||||
"manifest_url": loop.config.fxosApp.manifestUrl,
|
||||
"is_packaged": true
|
||||
}
|
||||
}
|
||||
}, "*");
|
||||
break;
|
||||
case "install-package":
|
||||
window.removeEventListener("message", this.onMarketplaceMessage);
|
||||
if (message.error) {
|
||||
console.error(message.error.error);
|
||||
this.trigger("session:error", "fxos_app_needed", {
|
||||
fxosAppName: loop.config.fxosApp.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
// We installed the FxOS app \o/, so we can continue with the call
|
||||
// process.
|
||||
this.setupOutgoingCall();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ConversationHeader = React.createClass({displayName: 'ConversationHeader',
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
@ -233,8 +322,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
var StartConversationView = React.createClass({displayName: 'StartConversationView',
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
model: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
@ -257,15 +348,29 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
this.props.model.listenTo(this.props.model, "session:error",
|
||||
this._onSessionError);
|
||||
this.props.model.listenTo(this.props.model, "fxos:app-needed",
|
||||
this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
_onSessionError: function(error) {
|
||||
console.error(error);
|
||||
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||
_onSessionError: function(error, l10nProps) {
|
||||
var errorL10n = error || "unable_retrieve_call_info";
|
||||
this.props.notifications.errorL10n(errorL10n, l10nProps);
|
||||
console.error(errorL10n);
|
||||
},
|
||||
|
||||
_onFxOSAppNeeded: function() {
|
||||
this.setState({
|
||||
marketplaceSrc: loop.config.marketplaceUrl
|
||||
});
|
||||
this.setState({
|
||||
onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
|
||||
this.props.model
|
||||
)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiates the call.
|
||||
* Takes in a call type parameter "audio" or "audio-video" and returns
|
||||
@ -330,6 +435,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
"terms-service": true,
|
||||
hide: (localStorage.getItem("has-seen-tos") === "true")
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.state.disableCallButton
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "container"},
|
||||
@ -360,7 +469,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
React.DOM.span({className: "standalone-call-btn-video-icon"})
|
||||
),
|
||||
|
||||
React.DOM.div({className: "btn-chevron",
|
||||
React.DOM.div({className: chevronClasses,
|
||||
onClick: this._toggleCallOptionsMenu}
|
||||
)
|
||||
|
||||
@ -388,6 +497,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
dangerouslySetInnerHTML: {__html: tosHTML}})
|
||||
),
|
||||
|
||||
FxOSHiddenMarketplace({
|
||||
marketplaceSrc: this.state.marketplaceSrc,
|
||||
onMarketplaceMessage: this.state.onMarketplaceMessage}),
|
||||
|
||||
ConversationFooter(null)
|
||||
)
|
||||
);
|
||||
@ -434,8 +547,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var OutgoingConversationView = React.createClass({displayName: 'OutgoingConversationView',
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
helper: React.PropTypes.instanceOf(sharedUtils.Helper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
@ -689,8 +804,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var WebappRootView = React.createClass({displayName: 'WebappRootView',
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
helper: React.PropTypes.instanceOf(sharedUtils.Helper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
@ -736,9 +853,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
baseServerUrl: loop.config.serverUrl
|
||||
});
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
var conversation
|
||||
if (helper.isFirefoxOS(navigator.userAgent)) {
|
||||
conversation = new FxOSConversationModel();
|
||||
} else {
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
}
|
||||
|
||||
var feedbackApiClient = new loop.FeedbackAPIClient(
|
||||
loop.config.feedbackApiUrl, {
|
||||
product: loop.config.feedbackProductName,
|
||||
@ -777,6 +900,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
UnsupportedDeviceView: UnsupportedDeviceView,
|
||||
init: init,
|
||||
PromoteFirefoxView: PromoteFirefoxView,
|
||||
WebappRootView: WebappRootView
|
||||
WebappRootView: WebappRootView,
|
||||
FxOSConversationModel: FxOSConversationModel
|
||||
};
|
||||
})(jQuery, _, window.OT, navigator.mozL10n);
|
||||
|
@ -4,7 +4,7 @@
|
||||
* 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/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
/* global loop:true, React, MozActivity */
|
||||
/* jshint newcap:false, maxlen:false */
|
||||
|
||||
var loop = loop || {};
|
||||
@ -122,6 +122,95 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The Firefox Marketplace exposes a web page that contains a postMesssage
|
||||
* based API that wraps a small set of functionality from the WebApps API
|
||||
* that allow us to request the installation of apps given their manifest
|
||||
* URL. We will be embedding the content of this web page within an hidden
|
||||
* iframe in case that we need to request the installation of the FxOS Loop
|
||||
* client.
|
||||
*/
|
||||
var FxOSHiddenMarketplace = React.createClass({
|
||||
render: function() {
|
||||
return <iframe id="marketplace" src={this.props.marketplaceSrc} hidden/>;
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
// This happens only once when we change the 'src' property of the iframe.
|
||||
if (this.props.onMarketplaceMessage) {
|
||||
// The reason for listening on the global window instead of on the
|
||||
// iframe content window is because the Marketplace is doing a
|
||||
// window.top.postMessage.
|
||||
window.addEventListener("message", this.props.onMarketplaceMessage);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var FxOSConversationModel = Backbone.Model.extend({
|
||||
setupOutgoingCall: function() {
|
||||
// The FxOS Loop client exposes a "loop-call" activity. If we get the
|
||||
// activity onerror callback it means that there is no "loop-call"
|
||||
// activity handler available and so no FxOS Loop client installed.
|
||||
var request = new MozActivity({
|
||||
name: "loop-call",
|
||||
data: {
|
||||
type: "loop/token",
|
||||
token: this.get("loopToken"),
|
||||
callerId: this.get("callerId"),
|
||||
callType: this.get("callType")
|
||||
}
|
||||
});
|
||||
|
||||
request.onsuccess = function() {};
|
||||
|
||||
request.onerror = (function(event) {
|
||||
if (event.target.error.name !== "NO_PROVIDER") {
|
||||
console.error ("Unexpected " + event.target.error.name);
|
||||
this.trigger("session:error", "fxos_app_needed", {
|
||||
fxosAppName: loop.config.fxosApp.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.trigger("fxos:app-needed");
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
onMarketplaceMessage: function(event) {
|
||||
var message = event.data;
|
||||
switch (message.name) {
|
||||
case "loaded":
|
||||
var marketplace = window.document.getElementById("marketplace");
|
||||
// Once we have it loaded, we request the installation of the FxOS
|
||||
// Loop client app. We will be receiving the result of this action
|
||||
// via postMessage from the child iframe.
|
||||
marketplace.contentWindow.postMessage({
|
||||
"name": "install-package",
|
||||
"data": {
|
||||
"product": {
|
||||
"name": loop.config.fxosApp.name,
|
||||
"manifest_url": loop.config.fxosApp.manifestUrl,
|
||||
"is_packaged": true
|
||||
}
|
||||
}
|
||||
}, "*");
|
||||
break;
|
||||
case "install-package":
|
||||
window.removeEventListener("message", this.onMarketplaceMessage);
|
||||
if (message.error) {
|
||||
console.error(message.error.error);
|
||||
this.trigger("session:error", "fxos_app_needed", {
|
||||
fxosAppName: loop.config.fxosApp.name
|
||||
});
|
||||
return;
|
||||
}
|
||||
// We installed the FxOS app \o/, so we can continue with the call
|
||||
// process.
|
||||
this.setupOutgoingCall();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ConversationHeader = React.createClass({
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
@ -233,8 +322,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
var StartConversationView = React.createClass({
|
||||
propTypes: {
|
||||
model: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
model: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
@ -257,15 +348,29 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
this.props.model.listenTo(this.props.model, "session:error",
|
||||
this._onSessionError);
|
||||
this.props.model.listenTo(this.props.model, "fxos:app-needed",
|
||||
this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
_onSessionError: function(error) {
|
||||
console.error(error);
|
||||
this.props.notifications.errorL10n("unable_retrieve_call_info");
|
||||
_onSessionError: function(error, l10nProps) {
|
||||
var errorL10n = error || "unable_retrieve_call_info";
|
||||
this.props.notifications.errorL10n(errorL10n, l10nProps);
|
||||
console.error(errorL10n);
|
||||
},
|
||||
|
||||
_onFxOSAppNeeded: function() {
|
||||
this.setState({
|
||||
marketplaceSrc: loop.config.marketplaceUrl
|
||||
});
|
||||
this.setState({
|
||||
onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
|
||||
this.props.model
|
||||
)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiates the call.
|
||||
* Takes in a call type parameter "audio" or "audio-video" and returns
|
||||
@ -330,6 +435,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
"terms-service": true,
|
||||
hide: (localStorage.getItem("has-seen-tos") === "true")
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.state.disableCallButton
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
@ -360,7 +469,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
<span className="standalone-call-btn-video-icon"></span>
|
||||
</button>
|
||||
|
||||
<div className="btn-chevron"
|
||||
<div className={chevronClasses}
|
||||
onClick={this._toggleCallOptionsMenu}>
|
||||
</div>
|
||||
|
||||
@ -388,6 +497,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
dangerouslySetInnerHTML={{__html: tosHTML}}></p>
|
||||
</div>
|
||||
|
||||
<FxOSHiddenMarketplace
|
||||
marketplaceSrc={this.state.marketplaceSrc}
|
||||
onMarketplaceMessage= {this.state.onMarketplaceMessage} />
|
||||
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
);
|
||||
@ -434,8 +547,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var OutgoingConversationView = React.createClass({
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
helper: React.PropTypes.instanceOf(sharedUtils.Helper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
@ -689,8 +804,10 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
var WebappRootView = React.createClass({
|
||||
propTypes: {
|
||||
client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
helper: React.PropTypes.instanceOf(sharedUtils.Helper).isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
@ -736,9 +853,15 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
baseServerUrl: loop.config.serverUrl
|
||||
});
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
var conversation
|
||||
if (helper.isFirefoxOS(navigator.userAgent)) {
|
||||
conversation = new FxOSConversationModel();
|
||||
} else {
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: OT
|
||||
});
|
||||
}
|
||||
|
||||
var feedbackApiClient = new loop.FeedbackAPIClient(
|
||||
loop.config.feedbackApiUrl, {
|
||||
product: loop.config.feedbackProductName,
|
||||
@ -777,6 +900,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
UnsupportedDeviceView: UnsupportedDeviceView,
|
||||
init: init,
|
||||
PromoteFirefoxView: PromoteFirefoxView,
|
||||
WebappRootView: WebappRootView
|
||||
WebappRootView: WebappRootView,
|
||||
FxOSConversationModel: FxOSConversationModel
|
||||
};
|
||||
})(jQuery, _, window.OT, navigator.mozL10n);
|
||||
|
@ -46,6 +46,7 @@ clientShortname=WebRTC!
|
||||
call_url_creation_date_label=(from {{call_url_creation_date}})
|
||||
call_progress_connecting_description=Connecting…
|
||||
call_progress_ringing_description=Ringing…
|
||||
fxos_app_needed=Please install the {{fxosAppName}} app from the Firefox Marketplace.
|
||||
|
||||
feedback_call_experience_heading2=How was your conversation?
|
||||
feedback_what_makes_you_sad=What makes you sad?
|
||||
|
@ -21,6 +21,12 @@ function getConfigFile(req, res) {
|
||||
"loop.config.serverUrl = 'http://localhost:" + loopServerPort + "';",
|
||||
"loop.config.feedbackApiUrl = '" + feedbackApiUrl + "';",
|
||||
"loop.config.feedbackProductName = '" + feedbackProductName + "';",
|
||||
// XXX Update with the real marketplace url once the FxOS Loop app is
|
||||
// uploaded to the marketplace bug 1053424
|
||||
"loop.config.marketplaceUrl = 'http://fake-market.herokuapp.com/iframe-install.html'",
|
||||
"loop.config.fxosApp = loop.config.fxosApp || {};",
|
||||
"loop.config.fxosApp.name = 'Loop';",
|
||||
"loop.config.fxosApp.manifestUrl = 'http://fake-market.herokuapp.com/apps/packagedApp/manifest.webapp';"
|
||||
].join("\n"));
|
||||
}
|
||||
|
||||
|
@ -355,8 +355,8 @@ describe("loop.shared.models", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
collection = new sharedModels.NotificationCollection();
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return "translated:" + x;
|
||||
sandbox.stub(l10n, "get", function(x, y) {
|
||||
return "translated:" + x + (y ? ':' + y : '');
|
||||
});
|
||||
notifData = {level: "error", message: "plop"};
|
||||
testNotif = new sharedModels.NotificationModel(notifData);
|
||||
@ -400,6 +400,15 @@ describe("loop.shared.models", function() {
|
||||
expect(collection.at(0).get("level")).eql("error");
|
||||
expect(collection.at(0).get("message")).eql("translated:fakeId");
|
||||
});
|
||||
|
||||
it("should notify an error using a l10n string id + l10n properties",
|
||||
function() {
|
||||
collection.errorL10n("fakeId", "fakeProp");
|
||||
|
||||
expect(collection).to.have.length.of(1);
|
||||
expect(collection.at(0).get("level")).eql("error");
|
||||
expect(collection.at(0).get("message")).eql("translated:fakeId:fakeProp");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -53,6 +53,39 @@ describe("loop.shared.utils", function() {
|
||||
expect(helper.isFirefox("Opera")).eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#isFirefoxOS", function() {
|
||||
describe("without mozActivities", function() {
|
||||
it("shouldn't detect FirefoxOS on mobile platform", function() {
|
||||
expect(helper.isFirefoxOS("mobi")).eql(false);
|
||||
});
|
||||
|
||||
it("shouldn't detect FirefoxOS on non mobile platform", function() {
|
||||
expect(helper.isFirefoxOS("whatever")).eql(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with mozActivities", function() {
|
||||
var realMozActivity;
|
||||
|
||||
before(function() {
|
||||
realMozActivity = window.MozActivity;
|
||||
window.MozActivity = {};
|
||||
});
|
||||
|
||||
after(function() {
|
||||
window.MozActivity = realMozActivity;
|
||||
});
|
||||
|
||||
it("should detect FirefoxOS on mobile platform", function() {
|
||||
expect(helper.isFirefoxOS("mobi")).eql(true);
|
||||
});
|
||||
|
||||
it("shouldn't detect FirefoxOS on non mobile platform", function() {
|
||||
expect(helper.isFirefoxOS("whatever")).eql(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getBoolPreference", function() {
|
||||
|
@ -20,6 +20,9 @@ describe("loop.shared.views", function() {
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
sandbox.useFakeTimers(); // exposes sandbox.clock as a fake timer
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return "translated:" + x;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@ -424,9 +427,6 @@ describe("loop.shared.views", function() {
|
||||
var comp, fakeFeedbackApiClient;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return x;
|
||||
});
|
||||
fakeFeedbackApiClient = {send: sandbox.stub()};
|
||||
comp = TestUtils.renderIntoDocument(sharedViews.FeedbackView({
|
||||
feedbackApiClient: fakeFeedbackApiClient
|
||||
@ -601,9 +601,6 @@ describe("loop.shared.views", function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return "translated:" + x;
|
||||
});
|
||||
coll = new sharedModels.NotificationCollection();
|
||||
view = mountTestComponent({notifications: coll});
|
||||
testNotif = {level: "warning", message: "foo"};
|
||||
|
@ -384,7 +384,7 @@ describe("loop.webapp", function() {
|
||||
sinon.assert.notCalled(ocView._websocket.mediaUp);
|
||||
});
|
||||
|
||||
it("should notify the websocket that media is up if both streams" +
|
||||
it("should notify tloadhe websocket that media is up if both streams" +
|
||||
"are connected", function() {
|
||||
conversation.set("publishedStream", true);
|
||||
conversation.set("subscribedStream", true);
|
||||
@ -691,7 +691,8 @@ describe("loop.webapp", function() {
|
||||
sdk: {}
|
||||
});
|
||||
|
||||
sandbox.spy(conversation, "listenTo");
|
||||
conversation.onMarketplaceMessage = function() {};
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
requestCallUrlInfo = sandbox.stub();
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
@ -701,6 +702,8 @@ describe("loop.webapp", function() {
|
||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||
})
|
||||
);
|
||||
|
||||
loop.config.marketplaceUrl = "http://market/";
|
||||
});
|
||||
|
||||
it("should call requestCallUrlInfo", function() {
|
||||
@ -710,21 +713,47 @@ describe("loop.webapp", function() {
|
||||
sinon.match.func);
|
||||
});
|
||||
|
||||
it("should listen for session:error events", function() {
|
||||
sinon.assert.calledOnce(conversation.listenTo);
|
||||
sinon.assert.calledWithExactly(conversation.listenTo, conversation,
|
||||
"session:error", sinon.match.func);
|
||||
});
|
||||
|
||||
it("should trigger a notication when a session:error model event is " +
|
||||
" received", function() {
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
conversation.trigger("session:error", "tech error");
|
||||
it("should add a notification when a session:error model event is " +
|
||||
" received without an argument", function() {
|
||||
conversation.trigger("session:error");
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||
"unable_retrieve_call_info");
|
||||
sinon.match.string, undefined);
|
||||
});
|
||||
|
||||
it("should add a notification with the custom message id when a " +
|
||||
"session:error event is fired with an argument", function() {
|
||||
conversation.trigger("session:error", "tech_error");
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||
"tech_error", undefined);
|
||||
});
|
||||
|
||||
it("should add a notification with the custom message id when a " +
|
||||
"session:error event is fired with an argument and parameters",
|
||||
function() {
|
||||
conversation.trigger("session:error", "tech_error", {param: "value"});
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||
"tech_error", { param: "value" });
|
||||
});
|
||||
|
||||
it("should set marketplace hidden iframe src when fxos:app-needed is " +
|
||||
"triggered", function(done) {
|
||||
var marketplace = view.getDOMNode().querySelector("#marketplace");
|
||||
expect(marketplace.src).to.be.equal("");
|
||||
|
||||
conversation.trigger("fxos:app-needed");
|
||||
|
||||
view.forceUpdate(function() {
|
||||
expect(marketplace.src).to.be.equal(loop.config.marketplaceUrl);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#render", function() {
|
||||
@ -826,4 +855,198 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Firefox OS", function() {
|
||||
var conversation, client;
|
||||
|
||||
before(function() {
|
||||
client = new loop.StandaloneClient({
|
||||
baseServerUrl: "http://fake.example.com"
|
||||
});
|
||||
sandbox.stub(client, "requestCallInfo");
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: {},
|
||||
pendingCallTimeout: 1000
|
||||
});
|
||||
});
|
||||
|
||||
describe("Setup call", function() {
|
||||
var conversation, setupOutgoingCall, view, requestCallUrlInfo;
|
||||
|
||||
beforeEach(function() {
|
||||
conversation = new loop.webapp.FxOSConversationModel({
|
||||
loopToken: "fakeToken"
|
||||
});
|
||||
setupOutgoingCall = sandbox.stub(conversation, "setupOutgoingCall");
|
||||
|
||||
var standaloneClientStub = {
|
||||
requestCallUrlInfo: function(token, cb) {
|
||||
cb(null, {urlCreationDate: 0});
|
||||
},
|
||||
settings: {baseServerUrl: loop.webapp.baseServerUrl}
|
||||
};
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: standaloneClientStub
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("should start the conversation establishment process", function() {
|
||||
var button = view.getDOMNode().querySelector("button");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(setupOutgoingCall);
|
||||
});
|
||||
});
|
||||
|
||||
describe("FxOSConversationModel", function() {
|
||||
var model, realMozActivity;
|
||||
|
||||
before(function() {
|
||||
model = new loop.webapp.FxOSConversationModel({
|
||||
loopToken: "fakeToken",
|
||||
callerId: "callerId",
|
||||
callType: "callType"
|
||||
});
|
||||
|
||||
realMozActivity = window.MozActivity;
|
||||
|
||||
loop.config.fxosApp = {
|
||||
name: "Firefox Hello"
|
||||
};
|
||||
});
|
||||
|
||||
after(function() {
|
||||
window.MozActivity = realMozActivity;
|
||||
});
|
||||
|
||||
describe("setupOutgoingCall", function() {
|
||||
var _activityProps, _onerror, trigger;
|
||||
|
||||
function fireError(errorName) {
|
||||
_onerror({
|
||||
target: {
|
||||
error: {
|
||||
name: errorName
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
before(function() {
|
||||
window.MozActivity = function(activityProps) {
|
||||
_activityProps = activityProps;
|
||||
return {
|
||||
set onerror(callback) {
|
||||
_onerror = callback;
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
after(function() {
|
||||
window.MozActivity = realMozActivity;
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
trigger = sandbox.stub(model, "trigger");
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
trigger.restore();
|
||||
});
|
||||
|
||||
it("Activity properties", function() {
|
||||
expect(_activityProps).to.not.exist;
|
||||
model.setupOutgoingCall();
|
||||
expect(_activityProps).to.exist;
|
||||
expect(_activityProps).eql({
|
||||
name: "loop-call",
|
||||
data: {
|
||||
type: "loop/token",
|
||||
token: "fakeToken",
|
||||
callerId: "callerId",
|
||||
callType: "callType"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("NO_PROVIDER activity error should trigger fxos:app-needed",
|
||||
function() {
|
||||
sinon.assert.notCalled(trigger);
|
||||
model.setupOutgoingCall();
|
||||
fireError("NO_PROVIDER");
|
||||
sinon.assert.calledOnce(trigger);
|
||||
sinon.assert.calledWithExactly(trigger, "fxos:app-needed");
|
||||
}
|
||||
);
|
||||
|
||||
it("Other activity error should trigger session:error",
|
||||
function() {
|
||||
sinon.assert.notCalled(trigger);
|
||||
model.setupOutgoingCall();
|
||||
fireError("whatever");
|
||||
sinon.assert.calledOnce(trigger);
|
||||
sinon.assert.calledWithExactly(trigger, "session:error",
|
||||
"fxos_app_needed", { fxosAppName: loop.config.fxosApp.name });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe("onMarketplaceMessage", function() {
|
||||
var view, setupOutgoingCall, trigger;
|
||||
|
||||
before(function() {
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
model: model,
|
||||
notifications: notifications,
|
||||
client: {requestCallUrlInfo: sandbox.stub()}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
setupOutgoingCall = sandbox.stub(model, "setupOutgoingCall");
|
||||
trigger = sandbox.stub(model, "trigger");
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
setupOutgoingCall.restore();
|
||||
trigger.restore();
|
||||
});
|
||||
|
||||
it("We should call trigger a FxOS outgoing call if we get " +
|
||||
"install-package message without error", function() {
|
||||
sinon.assert.notCalled(setupOutgoingCall);
|
||||
model.onMarketplaceMessage({
|
||||
data: {
|
||||
name: "install-package"
|
||||
}
|
||||
});
|
||||
sinon.assert.calledOnce(setupOutgoingCall);
|
||||
});
|
||||
|
||||
it("We should trigger a session:error event if we get " +
|
||||
"install-package message with an error", function() {
|
||||
sinon.assert.notCalled(trigger);
|
||||
sinon.assert.notCalled(setupOutgoingCall);
|
||||
model.onMarketplaceMessage({
|
||||
data: {
|
||||
name: "install-package",
|
||||
error: "error"
|
||||
}
|
||||
});
|
||||
sinon.assert.notCalled(setupOutgoingCall);
|
||||
sinon.assert.calledOnce(trigger);
|
||||
sinon.assert.calledWithExactly(trigger, "session:error",
|
||||
"fxos_app_needed", { fxosAppName: loop.config.fxosApp.name });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -149,14 +149,14 @@
|
||||
),
|
||||
|
||||
Section({name: "IncomingCallView"},
|
||||
Example({summary: "Default / incoming video call", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Default / incoming video call", dashed: "true", style: {width: "260px", height: "254px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
IncomingCallView({model: mockConversationModel,
|
||||
video: true})
|
||||
)
|
||||
),
|
||||
|
||||
Example({summary: "Default / incoming audio only call", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Default / incoming audio only call", dashed: "true", style: {width: "260px", height: "254px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
IncomingCallView({model: mockConversationModel,
|
||||
video: false})
|
||||
@ -165,7 +165,7 @@
|
||||
),
|
||||
|
||||
Section({name: "IncomingCallView-ActiveState"},
|
||||
Example({summary: "Default", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Default", dashed: "true", style: {width: "260px", height: "254px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
IncomingCallView({model: mockConversationModel,
|
||||
showDeclineMenu: true,
|
||||
@ -319,13 +319,13 @@
|
||||
React.DOM.strong(null, "Note:"), " For the useable demo, you can access submitted data at ",
|
||||
React.DOM.a({href: "https://input.allizom.org/"}, "input.allizom.org"), "."
|
||||
),
|
||||
Example({summary: "Default (useable demo)", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Default (useable demo)", dashed: "true", style: {width: "260px"}},
|
||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient})
|
||||
),
|
||||
Example({summary: "Detailed form", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Detailed form", dashed: "true", style: {width: "260px"}},
|
||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient, step: "form"})
|
||||
),
|
||||
Example({summary: "Thank you!", dashed: "true", style: {width: "280px"}},
|
||||
Example({summary: "Thank you!", dashed: "true", style: {width: "260px"}},
|
||||
FeedbackView({feedbackApiClient: stageFeedbackApiClient, step: "finished"})
|
||||
)
|
||||
),
|
||||
|
@ -149,14 +149,14 @@
|
||||
</Section>
|
||||
|
||||
<Section name="IncomingCallView">
|
||||
<Example summary="Default / incoming video call" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Default / incoming video call" dashed="true" style={{width: "260px", height: "254px"}}>
|
||||
<div className="fx-embedded">
|
||||
<IncomingCallView model={mockConversationModel}
|
||||
video={true} />
|
||||
</div>
|
||||
</Example>
|
||||
|
||||
<Example summary="Default / incoming audio only call" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Default / incoming audio only call" dashed="true" style={{width: "260px", height: "254px"}}>
|
||||
<div className="fx-embedded">
|
||||
<IncomingCallView model={mockConversationModel}
|
||||
video={false} />
|
||||
@ -165,7 +165,7 @@
|
||||
</Section>
|
||||
|
||||
<Section name="IncomingCallView-ActiveState">
|
||||
<Example summary="Default" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Default" dashed="true" style={{width: "260px", height: "254px"}}>
|
||||
<div className="fx-embedded" >
|
||||
<IncomingCallView model={mockConversationModel}
|
||||
showDeclineMenu={true}
|
||||
@ -319,13 +319,13 @@
|
||||
<strong>Note:</strong> For the useable demo, you can access submitted data at
|
||||
<a href="https://input.allizom.org/">input.allizom.org</a>.
|
||||
</p>
|
||||
<Example summary="Default (useable demo)" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Default (useable demo)" dashed="true" style={{width: "260px"}}>
|
||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} />
|
||||
</Example>
|
||||
<Example summary="Detailed form" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Detailed form" dashed="true" style={{width: "260px"}}>
|
||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} step="form" />
|
||||
</Example>
|
||||
<Example summary="Thank you!" dashed="true" style={{width: "280px"}}>
|
||||
<Example summary="Thank you!" dashed="true" style={{width: "260px"}}>
|
||||
<FeedbackView feedbackApiClient={stageFeedbackApiClient} step="finished" />
|
||||
</Example>
|
||||
</Section>
|
||||
|
@ -1,5 +1,4 @@
|
||||
[DEFAULT]
|
||||
skip-if = e10s # Bug 1058879 - canvas debugger tests disabled with e10s
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
doc_simple-canvas.html
|
||||
@ -20,6 +19,7 @@ support-files =
|
||||
[browser_canvas-actor-test-08.js]
|
||||
[browser_canvas-actor-test-09.js]
|
||||
[browser_canvas-actor-test-10.js]
|
||||
skip-if = e10s # Bug 1058879 - canvas debugger tests disabled with e10s
|
||||
[browser_canvas-frontend-call-highlight.js]
|
||||
[browser_canvas-frontend-call-list.js]
|
||||
[browser_canvas-frontend-call-search.js]
|
||||
|
@ -7,10 +7,9 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
ok(target, "Should have a target available.");
|
||||
ok(debuggee, "Should have a debuggee available.");
|
||||
ok(front, "Should have a protocol front available.");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCallWatcherBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_TRANSPARENT_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_CANVAS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(SIMPLE_BITMASKS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(SIMPLE_BITMASKS_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(WEBGL_ENUM_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_ENUM_URL);
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -7,7 +7,9 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, front] = yield initCanavsDebuggerBackend(WEBGL_BINDINGS_URL);
|
||||
let { target, front } = yield initCanvasDebuggerBackend(WEBGL_BINDINGS_URL);
|
||||
// XXX - use of |debuggee| here is incompatible with e10s - bug 1058879.
|
||||
let debuggee = target.window.wrappedJSObject
|
||||
|
||||
let navigated = once(target, "navigate");
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
let searchbox = $("#calls-searchbox");
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, EVENTS, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { $ } = panel.panelWin;
|
||||
|
||||
is($("#snapshots-pane").hasAttribute("hidden"), false,
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, EVENTS, L10N, $, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, EVENTS, $, SnapshotsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS } = panel.panelWin;
|
||||
|
||||
let reset = once(window, EVENTS.UI_RESET);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
is(SnapshotsListView.itemCount, 0,
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
function ifTestingSupported() {
|
||||
let [target, debuggee, panel] = yield initCanavsDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
|
||||
let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
|
||||
|
||||
yield reload(target);
|
||||
|
@ -185,45 +185,42 @@ function initCallWatcherBackend(aUrl) {
|
||||
return Task.spawn(function*() {
|
||||
let tab = yield addTab(aUrl);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
let front = new CallWatcherFront(target.client, target.form);
|
||||
return [target, debuggee, front];
|
||||
return { target, front };
|
||||
});
|
||||
}
|
||||
|
||||
function initCanavsDebuggerBackend(aUrl) {
|
||||
function initCanvasDebuggerBackend(aUrl) {
|
||||
info("Initializing a canvas debugger front.");
|
||||
initServer();
|
||||
|
||||
return Task.spawn(function*() {
|
||||
let tab = yield addTab(aUrl);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
let front = new CanvasFront(target.client, target.form);
|
||||
return [target, debuggee, front];
|
||||
return { target, front };
|
||||
});
|
||||
}
|
||||
|
||||
function initCanavsDebuggerFrontend(aUrl) {
|
||||
function initCanvasDebuggerFrontend(aUrl) {
|
||||
info("Initializing a canvas debugger pane.");
|
||||
|
||||
return Task.spawn(function*() {
|
||||
let tab = yield addTab(aUrl);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
let debuggee = target.window.wrappedJSObject;
|
||||
|
||||
yield target.makeRemote();
|
||||
|
||||
Services.prefs.setBoolPref("devtools.canvasdebugger.enabled", true);
|
||||
let toolbox = yield gDevTools.showToolbox(target, "canvasdebugger");
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
return [target, debuggee, panel];
|
||||
return { target, panel };
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -633,6 +633,7 @@ exports.AppManager = AppManager = {
|
||||
discovery.on("devtools-device-added", this._updateWiFiRuntimes);
|
||||
discovery.on("devtools-device-updated", this._updateWiFiRuntimes);
|
||||
discovery.on("devtools-device-removed", this._updateWiFiRuntimes);
|
||||
this._updateWiFiRuntimes();
|
||||
},
|
||||
untrackWiFiRuntimes: function() {
|
||||
if (!this.isWiFiScanningEnabled) {
|
||||
|
59
browser/modules/E10SUtils.jsm
Normal file
59
browser/modules/E10SUtils.jsm
Normal file
@ -0,0 +1,59 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["E10SUtils"];
|
||||
|
||||
const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.E10SUtils = {
|
||||
shouldBrowserBeRemote: function(aURL) {
|
||||
// loadURI in browser.xml treats null as about:blank
|
||||
if (!aURL)
|
||||
aURL = "about:blank";
|
||||
|
||||
if (aURL.startsWith("about:") &&
|
||||
aURL.toLowerCase() != "about:home" &&
|
||||
aURL.toLowerCase() != "about:blank" &&
|
||||
!aURL.toLowerCase().startsWith("about:neterror")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
shouldLoadURI: function(aDocShell, aURI, aReferrer) {
|
||||
// about:blank is the initial document and can load anywhere
|
||||
if (aURI.spec == "about:blank")
|
||||
return true;
|
||||
|
||||
// Inner frames should always load in the current process
|
||||
if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
|
||||
return true;
|
||||
|
||||
// If the URI can be loaded in the current process then continue
|
||||
let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
if (this.shouldBrowserBeRemote(aURI.spec) == isRemote)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
redirectLoad: function(aDocShell, aURI, aReferrer) {
|
||||
// Retarget the load to the correct process
|
||||
let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
let sessionHistory = aDocShell.getInterface(Ci.nsIWebNavigation).sessionHistory;
|
||||
|
||||
messageManager.sendAsyncMessage("Browser:LoadURI", {
|
||||
uri: aURI.spec,
|
||||
referrer: aReferrer ? aReferrer.spec : null,
|
||||
historyIndex: sessionHistory.requestedIndex,
|
||||
});
|
||||
return false;
|
||||
},
|
||||
};
|
@ -96,7 +96,6 @@ this.TabCrashReporter = {
|
||||
return;
|
||||
|
||||
let url = browser.currentURI.spec;
|
||||
browser.getTabBrowser().updateBrowserRemotenessByURL(browser, url);
|
||||
browser.loadURIWithFlags(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
|
||||
},
|
||||
|
||||
|
@ -17,6 +17,7 @@ EXTRA_JS_MODULES += [
|
||||
'ContentSearch.jsm',
|
||||
'ContentWebRTC.jsm',
|
||||
'CustomizationTabPreloader.jsm',
|
||||
'E10SUtils.jsm',
|
||||
'Feeds.jsm',
|
||||
'FormSubmitObserver.jsm',
|
||||
'FormValidationHandler.jsm',
|
||||
|
@ -9838,7 +9838,18 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check if the webbrowser chrome wants the load to proceed; this can be
|
||||
// used to cancel attempts to load URIs in the wrong process.
|
||||
nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
|
||||
if (browserChrome3) {
|
||||
bool shouldLoad;
|
||||
rv = browserChrome3->ShouldLoadURI(this, aURI, aReferrer, &shouldLoad);
|
||||
if (NS_SUCCEEDED(rv) && !shouldLoad) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// mContentViewer->PermitUnload can destroy |this| docShell, which
|
||||
// causes the next call of CanSavePresentation to crash.
|
||||
// Hold onto |this| until we return, to prevent a crash from happening.
|
||||
|
@ -80,7 +80,6 @@
|
||||
#include "UnitTransforms.h"
|
||||
#include "ClientLayerManager.h"
|
||||
#include "LayersLogging.h"
|
||||
#include "nsIWebBrowserChrome3.h"
|
||||
|
||||
#include "nsColorPickerProxy.h"
|
||||
|
||||
|
@ -6,10 +6,13 @@
|
||||
#include "nsIURI.idl"
|
||||
#include "nsIDOMNode.idl"
|
||||
|
||||
interface nsIDocShell;
|
||||
interface nsIInputStream;
|
||||
|
||||
/**
|
||||
* nsIWebBrowserChrome3 is an extension to nsIWebBrowserChrome2.
|
||||
*/
|
||||
[scriptable, uuid(7f2aa813-b250-4e46-afeb-97b1e91bc9a5)]
|
||||
[scriptable, uuid(9e6c2372-5d9d-4ce8-ab9e-c5df1494dc84)]
|
||||
interface nsIWebBrowserChrome3 : nsIWebBrowserChrome2
|
||||
{
|
||||
/**
|
||||
@ -30,4 +33,18 @@ interface nsIWebBrowserChrome3 : nsIWebBrowserChrome2
|
||||
in nsIURI linkURI,
|
||||
in nsIDOMNode linkNode,
|
||||
in boolean isAppTab);
|
||||
|
||||
/**
|
||||
* Determines whether a load should continue.
|
||||
*
|
||||
* @param aDocShell
|
||||
* The docshell performing the load.
|
||||
* @param aURI
|
||||
* The URI being loaded.
|
||||
* @param aReferrer
|
||||
* The referrer of the load.
|
||||
*/
|
||||
bool shouldLoadURI(in nsIDocShell aDocShell,
|
||||
in nsIURI aURI,
|
||||
in nsIURI aReferrer);
|
||||
};
|
||||
|
@ -28,6 +28,8 @@ import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.search.providers.SearchEngine;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class PostSearchFragment extends Fragment {
|
||||
|
||||
private static final String LOG_TAG = "PostSearchFragment";
|
||||
@ -89,25 +91,38 @@ public class PostSearchFragment extends Fragment {
|
||||
public void onPageStarted(WebView view, final String url, Bitmap favicon) {
|
||||
// Reset the error state.
|
||||
networkError = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
// We keep URLs in the webview that are either about:blank or a search engine result page.
|
||||
if (TextUtils.equals(url, Constants.ABOUT_BLANK) || engine.isSearchResultsPage(url)) {
|
||||
// Keeping the URL in the webview is a noop.
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
webview.stopLoading();
|
||||
try {
|
||||
// If the url URI does not have an intent scheme, the intent data will be the entire
|
||||
// URI and its action will be ACTION_VIEW.
|
||||
final Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
|
||||
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL,
|
||||
TelemetryContract.Method.CONTENT, "search-result");
|
||||
// If the intent URI didn't specify a package, open this in Fennec.
|
||||
if (i.getPackage() == null) {
|
||||
i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL,
|
||||
TelemetryContract.Method.CONTENT, "search-result");
|
||||
} else {
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH,
|
||||
TelemetryContract.Method.INTENT, "search-result");
|
||||
}
|
||||
|
||||
final Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
startActivity(i);
|
||||
return true;
|
||||
} catch (URISyntaxException e) {
|
||||
Log.e(LOG_TAG, "Error parsing intent URI", e);
|
||||
}
|
||||
|
||||
// This sends the URL directly to fennec, rather than to Android.
|
||||
i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
|
||||
i.setData(Uri.parse(url));
|
||||
startActivity(i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.search.autocomplete;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
@ -127,19 +128,23 @@ public class SearchBar extends FrameLayout {
|
||||
}
|
||||
|
||||
public void setEngine(SearchEngine engine) {
|
||||
int color = engine.getColor();
|
||||
if (color == Color.TRANSPARENT) {
|
||||
// Fall back to default orange if the search engine doesn't specify a color.
|
||||
color = getResources().getColor(R.color.highlight_orange);
|
||||
}
|
||||
// Update the focused background color.
|
||||
focusedBackground.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
|
||||
|
||||
final String iconURL = engine.getIconURL();
|
||||
final BitmapDrawable d = new BitmapDrawable(getResources(), BitmapUtils.getBitmapFromDataURI(iconURL));
|
||||
final Bitmap bitmap = BitmapUtils.getBitmapFromDataURI(iconURL);
|
||||
final BitmapDrawable d = new BitmapDrawable(getResources(), bitmap);
|
||||
engineIcon.setImageDrawable(d);
|
||||
engineIcon.setContentDescription(engine.getName());
|
||||
|
||||
// Update the focused background color.
|
||||
int color = BitmapUtils.getDominantColor(bitmap);
|
||||
|
||||
// BitmapUtils#getDominantColor ignores black and white pixels, but it will
|
||||
// return white if no dominant color was found. We don't want to create a
|
||||
// white underline for the search bar, so we default to black instead.
|
||||
if (color == Color.WHITE) {
|
||||
color = Color.BLACK;
|
||||
}
|
||||
focusedBackground.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
|
||||
|
||||
editText.setHint(getResources().getString(R.string.search_bar_hint, engine.getName()));
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package org.mozilla.search.providers;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
@ -195,14 +194,6 @@ public class SearchEngine {
|
||||
return iconURL;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
// TOOD: Add brand colors to search plugin XML.
|
||||
if (identifier.equals("yahoo")) {
|
||||
return 0xFF500095;
|
||||
}
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a particular url belongs to this search engine. If not,
|
||||
* the url will be sent to Fennec.
|
||||
|
@ -445,6 +445,23 @@ NS_IMETHODIMP nsContentTreeOwner::OnBeforeLinkTraversal(const nsAString &origina
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsContentTreeOwner::ShouldLoadURI(nsIDocShell *aDocShell,
|
||||
nsIURI *aURI,
|
||||
nsIURI *aReferrer,
|
||||
bool *_retval)
|
||||
{
|
||||
NS_ENSURE_STATE(mXULWindow);
|
||||
|
||||
nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
|
||||
mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
|
||||
|
||||
if (xulBrowserWindow)
|
||||
return xulBrowserWindow->ShouldLoadURI(aDocShell, aURI, aReferrer, _retval);
|
||||
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// nsContentTreeOwner::nsIWebBrowserChrome2
|
||||
//*****************************************************************************
|
||||
|
@ -10,13 +10,15 @@
|
||||
|
||||
interface nsIRequest;
|
||||
interface nsIDOMElement;
|
||||
interface nsIInputStream;
|
||||
interface nsIDocShell;
|
||||
|
||||
/**
|
||||
* The nsIXULBrowserWindow supplies the methods that may be called from the
|
||||
* internals of the browser area to tell the containing xul window to update
|
||||
* its ui.
|
||||
*/
|
||||
[scriptable, uuid(e4ee85a0-645d-11e3-949a-0800200c9a66)]
|
||||
[scriptable, uuid(162d3378-a7d5-410c-8635-fe80e32020fc)]
|
||||
interface nsIXULBrowserWindow : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -38,6 +40,19 @@ interface nsIXULBrowserWindow : nsISupports
|
||||
in nsIDOMNode linkNode,
|
||||
in boolean isAppTab);
|
||||
|
||||
/**
|
||||
* Determines whether a load should continue.
|
||||
*
|
||||
* @param aDocShell
|
||||
* The docshell performing the load.
|
||||
* @param aURI
|
||||
* The URI being loaded.
|
||||
* @param aReferrer
|
||||
* The referrer of the load.
|
||||
*/
|
||||
bool shouldLoadURI(in nsIDocShell aDocShell,
|
||||
in nsIURI aURI,
|
||||
in nsIURI aReferrer);
|
||||
/**
|
||||
* Show/hide a tooltip (when the user mouses over a link, say).
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user