зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset aebbb469f1d5 (bug 1539144) for ESlint failure at background.js:2:1 and checkstyle bustage. CLOSED TREE
This commit is contained in:
Родитель
a23cc83a46
Коммит
8594594498
|
@ -333,14 +333,7 @@ this.tabs = class extends ExtensionAPI {
|
|||
|
||||
tabListener.initTabReady();
|
||||
options.triggeringPrincipal = principal;
|
||||
|
||||
let nativeTab;
|
||||
if (Services.androidBridge.isFennec) {
|
||||
nativeTab = BrowserApp.addTab(url, options);
|
||||
} else {
|
||||
options.extensionId = context.extension.id;
|
||||
nativeTab = await BrowserApp.createNewTab(url, options);
|
||||
}
|
||||
let nativeTab = BrowserApp.addTab(url, options);
|
||||
|
||||
if (createProperties.url) {
|
||||
tabListener.initializingTabs.add(nativeTab);
|
||||
|
|
|
@ -32,10 +32,6 @@ const BrowserStatusFilter = Components.Constructor(
|
|||
"addProgressListener"
|
||||
);
|
||||
|
||||
const WINDOW_TYPE = Services.androidBridge.isFennec
|
||||
? "navigator:browser"
|
||||
: "navigator:geckoview";
|
||||
|
||||
let tabTracker;
|
||||
let windowTracker;
|
||||
|
||||
|
@ -186,12 +182,10 @@ class WindowTracker extends WindowTrackerBase {
|
|||
}
|
||||
|
||||
get topWindow() {
|
||||
return Services.wm.getMostRecentWindow(WINDOW_TYPE);
|
||||
}
|
||||
|
||||
isBrowserWindow(window) {
|
||||
let { documentElement } = window.document;
|
||||
return documentElement.getAttribute("windowtype") === WINDOW_TYPE;
|
||||
return (
|
||||
Services.wm.getMostRecentWindow("navigator:browser") ||
|
||||
Services.wm.getMostRecentWindow("navigator:geckoview")
|
||||
);
|
||||
}
|
||||
|
||||
addProgressListener(window, listener) {
|
||||
|
|
|
@ -56,9 +56,6 @@ import java.util.AbstractSequentialList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
import org.mozilla.geckoview.AllowOrDeny;
|
||||
import org.mozilla.geckoview.CompositorController;
|
||||
import org.mozilla.geckoview.ContentBlocking;
|
||||
|
@ -82,8 +79,6 @@ import org.mozilla.geckoview.SessionFinder;
|
|||
import org.mozilla.geckoview.SessionTextInput;
|
||||
import org.mozilla.geckoview.StorageController;
|
||||
import org.mozilla.geckoview.WebExtension;
|
||||
import org.mozilla.geckoview.WebExtensionController;
|
||||
import org.mozilla.geckoview.WebExtensionEventDispatcher;
|
||||
import org.mozilla.geckoview.WebMessage;
|
||||
import org.mozilla.geckoview.WebRequest;
|
||||
import org.mozilla.geckoview.WebRequestError;
|
||||
|
@ -289,7 +284,6 @@ package org.mozilla.geckoview {
|
|||
method @AnyThread @NonNull public GeckoRuntimeSettings getSettings();
|
||||
method @UiThread @NonNull public StorageController getStorageController();
|
||||
method @UiThread @NonNull public RuntimeTelemetry getTelemetry();
|
||||
method @UiThread @NonNull public WebExtensionController getWebExtensionController();
|
||||
method @UiThread public void orientationChanged();
|
||||
method @UiThread public void orientationChanged(int);
|
||||
method @AnyThread public void readFromParcel(@NonNull Parcel);
|
||||
|
@ -1108,16 +1102,6 @@ package org.mozilla.geckoview {
|
|||
method default public void onPortMessage(@NonNull Object, @NonNull WebExtension.Port);
|
||||
}
|
||||
|
||||
public class WebExtensionController implements BundleEventListener {
|
||||
ctor protected WebExtensionController(GeckoRuntime, WebExtensionEventDispatcher);
|
||||
method @UiThread @Nullable public WebExtensionController.TabDelegate getTabDelegate();
|
||||
method @UiThread public void setTabDelegate(@Nullable WebExtensionController.TabDelegate);
|
||||
}
|
||||
|
||||
public static interface WebExtensionController.TabDelegate {
|
||||
method @UiThread @Nullable default public GeckoResult<GeckoSession> onNewTab(@Nullable WebExtension, @Nullable String);
|
||||
}
|
||||
|
||||
@AnyThread public abstract class WebMessage {
|
||||
ctor protected WebMessage(@NonNull WebMessage.Builder);
|
||||
field @NonNull public final Map<String,String> headers;
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
browser.tabs.create({ url: "https://www.mozilla.org/en-US/" });
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "messaging",
|
||||
"version": "1.0",
|
||||
"description": "Test browser tabs api",
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"permissions": [
|
||||
"tabs"
|
||||
]
|
||||
}
|
|
@ -14,8 +14,6 @@ import org.mozilla.geckoview.GeckoSessionSettings;
|
|||
import org.mozilla.geckoview.GeckoView;
|
||||
import org.mozilla.geckoview.GeckoRuntime;
|
||||
import org.mozilla.geckoview.GeckoRuntimeSettings;
|
||||
import org.mozilla.geckoview.WebExtension;
|
||||
import org.mozilla.geckoview.WebExtensionController;
|
||||
import org.mozilla.geckoview.WebRequestError;
|
||||
|
||||
import android.app.Activity;
|
||||
|
@ -211,13 +209,6 @@ public class TestRunnerActivity extends Activity {
|
|||
.crashHandler(TestCrashHandler.class);
|
||||
|
||||
sRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
|
||||
|
||||
sRuntime.getWebExtensionController().setTabDelegate(new WebExtensionController.TabDelegate() {
|
||||
@Override
|
||||
public GeckoResult<GeckoSession> onNewTab(WebExtension source, String uri) {
|
||||
return GeckoResult.fromValue(createSession());
|
||||
}
|
||||
});
|
||||
sRuntime.setDelegate(() -> {
|
||||
mKillProcessOnDestroy = true;
|
||||
finish();
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.UUID
|
|||
class WebExtensionTest : BaseSessionTest() {
|
||||
companion object {
|
||||
val TEST_ENDPOINT: String = "http://localhost:4243"
|
||||
val TABS_BACKGROUND: String = "resource://android/assets/web_extensions/tabs/"
|
||||
val MESSAGING_BACKGROUND: String = "resource://android/assets/web_extensions/messaging/"
|
||||
val MESSAGING_CONTENT: String = "resource://android/assets/web_extensions/messaging-content/"
|
||||
}
|
||||
|
@ -117,31 +116,6 @@ class WebExtensionTest : BaseSessionTest() {
|
|||
sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(messaging))
|
||||
}
|
||||
|
||||
// This test
|
||||
// - Listen for a new tab request from a web extension
|
||||
// - Registers a web extension
|
||||
@Test
|
||||
fun testBrowserTabsCreate() {
|
||||
val tabsCreateResult = GeckoResult<Void>()
|
||||
var tabsExtension : WebExtension? = null
|
||||
|
||||
val tabDelegate = object : WebExtensionController.TabDelegate {
|
||||
override fun onNewTab(source: WebExtension?, uri: String?): GeckoResult<GeckoSession> {
|
||||
Assert.assertEquals(uri, "https://www.mozilla.org/en-US/")
|
||||
Assert.assertEquals(tabsExtension, source)
|
||||
tabsCreateResult.complete(null)
|
||||
return GeckoResult.fromValue(GeckoSession(sessionRule.session.settings))
|
||||
}
|
||||
}
|
||||
sessionRule.runtime.webExtensionController.tabDelegate = tabDelegate
|
||||
tabsExtension = WebExtension(TABS_BACKGROUND)
|
||||
|
||||
sessionRule.waitForResult(sessionRule.runtime.registerWebExtension(tabsExtension))
|
||||
sessionRule.waitForResult(tabsCreateResult)
|
||||
|
||||
sessionRule.waitForResult(sessionRule.runtime.unregisterWebExtension(tabsExtension))
|
||||
}
|
||||
|
||||
private fun createWebExtension(background: Boolean,
|
||||
messageDelegate: WebExtension.MessageDelegate): WebExtension {
|
||||
val webExtension: WebExtension
|
||||
|
|
|
@ -161,12 +161,6 @@ public final class GeckoRuntime implements Parcelable {
|
|||
private RuntimeTelemetry mTelemetry;
|
||||
private WebExtensionEventDispatcher mWebExtensionDispatcher;
|
||||
private StorageController mStorageController;
|
||||
private WebExtensionController mWebExtensionController;
|
||||
|
||||
public GeckoRuntime() {
|
||||
mWebExtensionDispatcher = new WebExtensionEventDispatcher();
|
||||
mWebExtensionController = new WebExtensionController(this, mWebExtensionDispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach the runtime to the given context.
|
||||
|
@ -259,6 +253,8 @@ public final class GeckoRuntime implements Parcelable {
|
|||
GeckoAppShell.setCrashHandlerService(settings.getCrashHandler());
|
||||
GeckoFontScaleListener.getInstance().attachToContext(context, settings);
|
||||
|
||||
mWebExtensionDispatcher = new WebExtensionEventDispatcher();
|
||||
|
||||
final GeckoThread.InitInfo info = new GeckoThread.InitInfo();
|
||||
info.args = settings.getArguments();
|
||||
info.extras = settings.getExtras();
|
||||
|
@ -333,16 +329,6 @@ public final class GeckoRuntime implements Parcelable {
|
|||
return create(context, new GeckoRuntimeSettings());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a WebExtensionController for this GeckoRuntime.
|
||||
*
|
||||
* @return an instance of {@link WebExtensionController}.
|
||||
*/
|
||||
@UiThread
|
||||
public @NonNull WebExtensionController getWebExtensionController() {
|
||||
return mWebExtensionController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link WebExtension} that will be run with this GeckoRuntime.
|
||||
*
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
package org.mozilla.geckoview;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.util.BundleEventListener;
|
||||
import org.mozilla.gecko.util.EventCallback;
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
|
||||
public class WebExtensionController implements BundleEventListener {
|
||||
public interface TabDelegate {
|
||||
/**
|
||||
* Called when tabs.create is invoked, this method returns a *newly-created* session
|
||||
* that GeckoView will use to load the requested page on. If the returned value
|
||||
* is null the page will not be opened.
|
||||
*
|
||||
* @param source An instance of {@link WebExtension} or null if extension was not registered
|
||||
* with GeckoRuntime.registerWebextension
|
||||
*/
|
||||
@UiThread
|
||||
@Nullable
|
||||
default GeckoResult<GeckoSession> onNewTab(@Nullable WebExtension source, @Nullable String uri) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private GeckoRuntime mRuntime;
|
||||
private WebExtensionEventDispatcher mDispatcher;
|
||||
private TabDelegate mTabDelegate;
|
||||
|
||||
protected WebExtensionController(GeckoRuntime runtime, WebExtensionEventDispatcher dispatcher) {
|
||||
mRuntime = runtime;
|
||||
mDispatcher = dispatcher;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setTabDelegate(final @Nullable TabDelegate delegate) {
|
||||
if (delegate == null) {
|
||||
if (mTabDelegate != null) {
|
||||
EventDispatcher.getInstance().unregisterUiThreadListener(
|
||||
this,
|
||||
"GeckoView:WebExtension:NewTab"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (mTabDelegate == null) {
|
||||
EventDispatcher.getInstance().registerUiThreadListener(
|
||||
this,
|
||||
"GeckoView:WebExtension:NewTab"
|
||||
);
|
||||
}
|
||||
}
|
||||
mTabDelegate = delegate;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public @Nullable TabDelegate getTabDelegate() {
|
||||
return mTabDelegate;
|
||||
}
|
||||
|
||||
private void newTab(final GeckoBundle message, final EventCallback callback) {
|
||||
if (mTabDelegate == null) {
|
||||
callback.sendSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
WebExtension extension = mDispatcher.getWebExtension(message.getString("extensionId"));
|
||||
|
||||
final GeckoResult<GeckoSession> result = mTabDelegate.onNewTab(extension, message.getString("uri"));
|
||||
|
||||
if (result == null) {
|
||||
callback.sendSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
result.accept(session -> {
|
||||
if (session == null) {
|
||||
callback.sendSuccess(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.isOpen()) {
|
||||
throw new IllegalArgumentException("Must use an unopened GeckoSession instance");
|
||||
}
|
||||
|
||||
session.open(mRuntime);
|
||||
|
||||
callback.sendSuccess(session.getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(final String event, final GeckoBundle message,
|
||||
final EventCallback callback) {
|
||||
if ("GeckoView:WebExtension:NewTab".equals(event)) {
|
||||
newTab(message, callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.mozilla.geckoview;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
@ -52,10 +51,6 @@ import java.util.Map;
|
|||
}
|
||||
}
|
||||
|
||||
public @Nullable WebExtension getWebExtension(final String id) {
|
||||
return mExtensions.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(final String event, final GeckoBundle message,
|
||||
final EventCallback callback) {
|
||||
|
|
|
@ -30,11 +30,6 @@ exclude: true
|
|||
[69.3]: ../WebExtension.html
|
||||
[69.4]: ../WebExtension.MessageDelegate.html
|
||||
|
||||
- Added `WebExtensionController` and `TabDelegate` to handle
|
||||
[`browser.tabs.create`][69.6] calls by WebExtensions.
|
||||
|
||||
[69.6]: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create
|
||||
|
||||
## v68
|
||||
- Added [`GeckoRuntime#configurationChanged`][68.1] to notify the device
|
||||
configuration has changed.
|
||||
|
@ -347,4 +342,4 @@ exclude: true
|
|||
[65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
|
||||
[65.25]: ../GeckoResult.html
|
||||
|
||||
[api-version]: 3d726407275d906a54c1fc86eb92f2c3bfaaa3d0
|
||||
[api-version]: 95d443eb3946aeb5b278a258ab5a527d3281edda
|
||||
|
|
|
@ -16,8 +16,6 @@ import org.mozilla.geckoview.GeckoRuntimeSettings;
|
|||
import org.mozilla.geckoview.GeckoSession;
|
||||
import org.mozilla.geckoview.GeckoSessionSettings;
|
||||
import org.mozilla.geckoview.GeckoView;
|
||||
import org.mozilla.geckoview.WebExtension;
|
||||
import org.mozilla.geckoview.WebExtensionController;
|
||||
import org.mozilla.geckoview.WebRequestError;
|
||||
|
||||
import android.Manifest;
|
||||
|
@ -155,15 +153,6 @@ public class GeckoViewActivity extends AppCompatActivity {
|
|||
.crashHandler(ExampleCrashHandler.class);
|
||||
|
||||
sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
|
||||
|
||||
sGeckoRuntime.getWebExtensionController().setTabDelegate(new WebExtensionController.TabDelegate() {
|
||||
@Override
|
||||
public GeckoResult<GeckoSession> onNewTab(WebExtension source, String uri) {
|
||||
final TabSession newSession = createSession();
|
||||
mToolbarView.updateTabCount();
|
||||
return GeckoResult.fromValue(newSession);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(savedInstanceState == null) {
|
||||
|
|
|
@ -9,14 +9,6 @@ var EXPORTED_SYMBOLS = ["GeckoViewTab"];
|
|||
const { GeckoViewModule } = ChromeUtils.import(
|
||||
"resource://gre/modules/GeckoViewModule.jsm"
|
||||
);
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
EventDispatcher: "resource://gre/modules/Messaging.jsm",
|
||||
Services: "resource://gre/modules/Services.jsm",
|
||||
});
|
||||
|
||||
// Based on the "Tab" prototype from mobile/android/chrome/content/browser.js
|
||||
class Tab {
|
||||
|
@ -31,135 +23,46 @@ class Tab {
|
|||
}
|
||||
|
||||
// Stub BrowserApp implementation for WebExtensions support.
|
||||
class BrowserAppShim {
|
||||
constructor(window) {
|
||||
// Because of bug 1410749, we can't use 0, though, and just to be safe
|
||||
// we choose a value that is unlikely to overlap with Fennec's tab IDs.
|
||||
const tabId = 10000 + window.windowUtils.outerWindowID;
|
||||
this.selectedBrowser = window.browser;
|
||||
this.selectedTab = new Tab(tabId, this.selectedBrowser);
|
||||
this.tabs = [this.selectedTab];
|
||||
}
|
||||
|
||||
closeTab(aTab) {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
getTabForId(aId) {
|
||||
return this.selectedTab;
|
||||
}
|
||||
|
||||
getTabForBrowser(aBrowser) {
|
||||
return this.selectedTab;
|
||||
}
|
||||
|
||||
getTabForWindow(aWindow) {
|
||||
return this.selectedTab;
|
||||
}
|
||||
|
||||
getTabForDocument(aDocument) {
|
||||
return this.selectedTab;
|
||||
}
|
||||
|
||||
getBrowserForOuterWindowID(aID) {
|
||||
return this.selectedBrowser;
|
||||
}
|
||||
|
||||
getBrowserForDocument(aDocument) {
|
||||
return this.selectedBrowser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the GeckoView App to create a new tab (GeckoSession).
|
||||
*
|
||||
* @param {string} url The url to load in the newly created tab
|
||||
* @param {object} options
|
||||
* @param {nsIPrincipal} options.triggeringPrincipal
|
||||
* @param {boolean} [options.disallowInheritPrincipal]
|
||||
* @param {string} options.extensionId
|
||||
*
|
||||
* @returns {Promise<Tab>}
|
||||
* A promise resolved to the newly created tab.
|
||||
* @throws {Error}
|
||||
* Throws an error if the GeckoView app doesn't support tabs.create or fails to handle the request.
|
||||
*/
|
||||
async createNewTab(url, options) {
|
||||
url = url || "about:blank";
|
||||
|
||||
if (!options.extensionId) {
|
||||
throw new Error("options.extensionId missing");
|
||||
}
|
||||
|
||||
const message = {
|
||||
type: "GeckoView:WebExtension:NewTab",
|
||||
uri: url,
|
||||
extensionId: options.extensionId,
|
||||
};
|
||||
|
||||
const sessionId = await EventDispatcher.instance.sendRequestForResult(
|
||||
message
|
||||
);
|
||||
|
||||
if (!sessionId) {
|
||||
throw new Error("Cannot create new tab");
|
||||
}
|
||||
|
||||
let window;
|
||||
const browser = await new Promise(resolve => {
|
||||
const handler = {
|
||||
observe(aSubject, aTopic, aData) {
|
||||
if (
|
||||
aTopic === "geckoview-window-created" &&
|
||||
aSubject.name === sessionId
|
||||
) {
|
||||
Services.obs.removeObserver(handler, "geckoview-window-created");
|
||||
window = aSubject;
|
||||
resolve(window.browser);
|
||||
}
|
||||
},
|
||||
};
|
||||
Services.obs.addObserver(handler, "geckoview-window-created");
|
||||
});
|
||||
|
||||
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
|
||||
if (options.disallowInheritPrincipal) {
|
||||
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
|
||||
browser.loadURI(url, {
|
||||
flags,
|
||||
triggeringPrincipal: options.triggeringPrincipal,
|
||||
});
|
||||
|
||||
return BrowserAppShim.getBrowserApp(window).selectedTab;
|
||||
}
|
||||
|
||||
// ext-tabs calls tabListener.initTabReady(); which rely on deck when initializing ProgressListeners.
|
||||
// Deck will be removed by https://phabricator.services.mozilla.com/D36575.
|
||||
get deck() {
|
||||
return {
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
};
|
||||
}
|
||||
|
||||
static getBrowserApp(window) {
|
||||
let { BrowserApp } = window;
|
||||
|
||||
if (!BrowserApp) {
|
||||
BrowserApp = window.gBrowser = window.BrowserApp = new BrowserAppShim(
|
||||
window
|
||||
);
|
||||
}
|
||||
|
||||
return BrowserApp;
|
||||
}
|
||||
}
|
||||
|
||||
class GeckoViewTab extends GeckoViewModule {
|
||||
onInit() {
|
||||
BrowserAppShim.getBrowserApp(this.window);
|
||||
// Because of bug 1410749, we can't use 0, though, and just to be safe
|
||||
// we choose a value that is unlikely to overlap with Fennec's tab IDs.
|
||||
const tabId = 10000 + this.browser.ownerGlobal.windowUtils.outerWindowID;
|
||||
const tab = new Tab(tabId, this.browser);
|
||||
|
||||
this.window.gBrowser = this.window.BrowserApp = {
|
||||
selectedBrowser: this.browser,
|
||||
tabs: [tab],
|
||||
selectedTab: tab,
|
||||
|
||||
closeTab: function(aTab) {
|
||||
// not implemented
|
||||
},
|
||||
|
||||
getTabForId: function(aId) {
|
||||
return this.selectedTab;
|
||||
},
|
||||
|
||||
getTabForBrowser: function(aBrowser) {
|
||||
return this.selectedTab;
|
||||
},
|
||||
|
||||
getTabForWindow: function(aWindow) {
|
||||
return this.selectedTab;
|
||||
},
|
||||
|
||||
getTabForDocument: function(aDocument) {
|
||||
return this.selectedTab;
|
||||
},
|
||||
|
||||
getBrowserForOuterWindowID: function(aID) {
|
||||
return this.browser;
|
||||
},
|
||||
|
||||
getBrowserForDocument: function(aDocument) {
|
||||
return this.selectedBrowser;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче