closes #522: rel=noopener M1222516 M1358469 M1419960
This commit is contained in:
parent
46bf256a1f
commit
6abcdc2cc2
|
@ -138,6 +138,8 @@ ImageAccessible::DoAction(uint8_t aIndex)
|
|||
|
||||
nsCOMPtr<nsPIDOMWindow> tmp;
|
||||
return NS_SUCCEEDED(piWindow->Open(spec, EmptyString(), EmptyString(),
|
||||
/* aLoadInfo = */ nullptr,
|
||||
/* aForceNoOpener = */ false,
|
||||
getter_AddRefs(tmp)));
|
||||
}
|
||||
|
||||
|
|
|
@ -9592,9 +9592,20 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
if (aWindowTarget && *aWindowTarget) {
|
||||
// Locate the target DocShell.
|
||||
nsCOMPtr<nsIDocShellTreeItem> targetItem;
|
||||
rv = FindItemWithName(aWindowTarget, nullptr, this,
|
||||
getter_AddRefs(targetItem));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsDependentString name(aWindowTarget);
|
||||
// Only _self, _parent, and _top are supported in noopener case. But we
|
||||
// have to be careful to not apply that to the noreferrer case. See bug
|
||||
// 1358469.
|
||||
bool allowNamedTarget = !(aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) ||
|
||||
(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
|
||||
if (allowNamedTarget ||
|
||||
name.LowerCaseEqualsLiteral("_self") ||
|
||||
name.LowerCaseEqualsLiteral("_parent") ||
|
||||
name.LowerCaseEqualsLiteral("_top")) {
|
||||
rv = FindItemWithName(aWindowTarget, nullptr, this,
|
||||
getter_AddRefs(targetItem));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
targetDocShell = do_QueryInterface(targetItem);
|
||||
// If the targetDocShell doesn't exist, then this is a new docShell
|
||||
|
@ -9737,11 +9748,60 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsDependentString name(aWindowTarget);
|
||||
nsCOMPtr<nsIDOMWindow> newWin;
|
||||
nsAutoCString spec;
|
||||
if (aURI) {
|
||||
aURI->GetSpec(spec);
|
||||
}
|
||||
// If we are a noopener load, we just hand the whole thing over to our
|
||||
// window.
|
||||
if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
|
||||
// Various asserts that we know to hold because NO_OPENER loads can only
|
||||
// happen for links.
|
||||
MOZ_ASSERT(!aLoadReplace);
|
||||
/* MOZ_ASSERT(aPrincipalToInherit == aTriggeringPrincipal); */
|
||||
MOZ_ASSERT(aFlags == INTERNAL_LOAD_FLAGS_NO_OPENER ||
|
||||
aFlags == (INTERNAL_LOAD_FLAGS_NO_OPENER |
|
||||
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
|
||||
MOZ_ASSERT(!aPostData);
|
||||
MOZ_ASSERT(!aHeadersData);
|
||||
MOZ_ASSERT(aLoadType == LOAD_LINK);
|
||||
MOZ_ASSERT(!aSHEntry);
|
||||
MOZ_ASSERT(aFirstParty); // Windowwatcher will assume this.
|
||||
|
||||
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
||||
rv = CreateLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set up our loadinfo so it will do the load as much like we would have
|
||||
// as possible.
|
||||
loadInfo->SetReferrer(aReferrer);
|
||||
loadInfo->SetReferrerPolicy(aReferrerPolicy);
|
||||
loadInfo->SetSendReferrer(!(aFlags &
|
||||
INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER));
|
||||
loadInfo->SetOriginalURI(aOriginalURI);
|
||||
loadInfo->SetLoadReplace(aLoadReplace);
|
||||
loadInfo->SetOwner(loadingPrincipal); // SetTriggeringPrincipal
|
||||
loadInfo->SetInheritOwner( /* SetInheritPrincipal _INHERIT_PRINCIPAL */
|
||||
!!(aFlags & INTERNAL_LOAD_FLAGS_INHERIT_OWNER));
|
||||
// Explicit principal because we do not want any guesses as to what the
|
||||
// principal to inherit is: it should be aTriggeringPrincipal.
|
||||
loadInfo->SetOwnerIsExplicit(true); // SetPrincipalIsExplicit(true);
|
||||
loadInfo->SetLoadType(ConvertLoadTypeToDocShellLoadInfo(LOAD_LINK));
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> newWin;
|
||||
rv = win->Open(NS_ConvertUTF8toUTF16(spec),
|
||||
name, // window name
|
||||
EmptyString(), // Features
|
||||
loadInfo,
|
||||
true, // aForceNoOpener
|
||||
getter_AddRefs(newWin));
|
||||
MOZ_ASSERT(!newWin);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> newWin;
|
||||
rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
|
||||
name, // window name
|
||||
EmptyString(), // Features
|
||||
|
@ -9756,11 +9816,6 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
if (!newDoc || newDoc->IsInitialDocument()) {
|
||||
isNewWindow = true;
|
||||
aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
|
||||
|
||||
// set opener object to null for noreferrer
|
||||
if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
|
||||
piNewWin->SetOpenerWindow(nullptr, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13497,11 +13552,16 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent,
|
|||
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, referrer);
|
||||
nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(referrer);
|
||||
while (tok.hasMoreTokens()) {
|
||||
if (tok.nextToken().LowerCaseEqualsLiteral("noreferrer")) {
|
||||
const nsAString& token = tok.nextToken();
|
||||
if (token.LowerCaseEqualsLiteral("noreferrer")) {
|
||||
flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
|
||||
INTERNAL_LOAD_FLAGS_NO_OPENER;
|
||||
// We now have all the flags we could possibly have, so just stop.
|
||||
break;
|
||||
}
|
||||
if (token.LowerCaseEqualsLiteral("noopener")) {
|
||||
flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7677,9 +7677,11 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
|
|||
|
||||
nsresult
|
||||
nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
|
||||
const nsAString& aOptions, nsPIDOMWindow **_retval)
|
||||
const nsAString& aOptions, nsIDocShellLoadInfo* aLoadInfo,
|
||||
bool aForceNoOpener, nsPIDOMWindow **_retval)
|
||||
{
|
||||
FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, _retval),
|
||||
FORWARD_TO_OUTER(Open, (aUrl, aName, aOptions, aLoadInfo, aForceNoOpener,
|
||||
_retval),
|
||||
NS_ERROR_NOT_INITIALIZED);
|
||||
nsCOMPtr<nsIDOMWindow> window;
|
||||
nsresult rv = OpenInternal(aUrl, aName, aOptions,
|
||||
|
@ -7689,6 +7691,8 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
|
|||
false, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
nullptr, nullptr, // No args
|
||||
aLoadInfo,
|
||||
aForceNoOpener,
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nullptr, // aJSCallerContext
|
||||
getter_AddRefs(window));
|
||||
|
@ -7710,6 +7714,8 @@ nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
|
|||
true, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
nullptr, nullptr, // No args
|
||||
nullptr, // aLoadInfo
|
||||
false, // aForceNoOpener
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
|
||||
_retval);
|
||||
|
@ -7730,6 +7736,8 @@ nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
|
|||
false, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
nullptr, aExtraArgument, // Arguments
|
||||
nullptr, // aLoadInfo
|
||||
false, // aForceNoOpener
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nullptr, // aJSCallerContext
|
||||
_retval);
|
||||
|
@ -7750,6 +7758,8 @@ nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
|
|||
false, // aDoJSFixups
|
||||
false, // aNavigate
|
||||
nullptr, nullptr, // No args
|
||||
nullptr, // aLoadInfo
|
||||
false, // aForceNoOpener
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nullptr, // aJSCallerContext
|
||||
_retval);
|
||||
|
@ -7780,6 +7790,8 @@ nsGlobalWindow::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
|
|||
false, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
argvArray, nullptr, // Arguments
|
||||
nullptr, // aLoadInfo
|
||||
false, // aForceNoOpener
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
aCx, // aJSCallerContext
|
||||
getter_AddRefs(dialog));
|
||||
|
@ -8860,6 +8872,8 @@ nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgumen
|
|||
true, // aDoJSFixups
|
||||
true, // aNavigate
|
||||
nullptr, argHolder, // args
|
||||
nullptr, // aLoadInfo
|
||||
false, // aForceNoOpener
|
||||
GetPrincipal(), // aCalleePrincipal
|
||||
nullptr, // aJSCallerContext
|
||||
getter_AddRefs(dlgWin));
|
||||
|
@ -11294,6 +11308,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
bool aDoJSFixups, bool aNavigate,
|
||||
nsIArray *argv,
|
||||
nsISupports *aExtraArgument,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
bool aForceNoOpener,
|
||||
nsIPrincipal *aCalleePrincipal,
|
||||
JSContext *aJSCallerContext,
|
||||
nsIDOMWindow **aReturn)
|
||||
|
@ -11335,16 +11351,24 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
nsIPrincipal::APP_STATUS_INSTALLED;
|
||||
}
|
||||
|
||||
bool forceNoOpener = false;
|
||||
nsAutoCString options;
|
||||
bool forceNoOpener = aForceNoOpener;
|
||||
// Unlike other window flags, "noopener" comes from splitting on commas with
|
||||
// HTML whitespace trimming...
|
||||
nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
|
||||
aOptions, ',');
|
||||
while (tok.hasMoreTokens()) {
|
||||
if (tok.nextToken().EqualsLiteral("noopener")) {
|
||||
auto nextTok = tok.nextToken();
|
||||
if (nextTok.EqualsLiteral("noopener")) {
|
||||
forceNoOpener = true;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
// Want to create a copy of the options without 'noopener' because having
|
||||
// 'noopener' in the options affects other window features.
|
||||
if (!options.IsEmpty()) {
|
||||
options.Append(',');
|
||||
}
|
||||
AppendUTF16toUTF8(nextTok, options);
|
||||
}
|
||||
|
||||
// XXXbz When this gets fixed to not use LegacyIsCallerNativeCode()
|
||||
|
@ -11406,10 +11430,9 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
||||
NS_ENSURE_TRUE(wwatch, rv);
|
||||
|
||||
NS_ConvertUTF16toUTF8 options(aOptions);
|
||||
NS_ConvertUTF16toUTF8 name(aName);
|
||||
|
||||
const char *options_ptr = aOptions.IsEmpty() ? nullptr : options.get();
|
||||
const char *options_ptr = options.IsEmpty() ? nullptr : options.get();
|
||||
const char *name_ptr = aName.IsEmpty() ? nullptr : name.get();
|
||||
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
|
||||
|
@ -11436,6 +11459,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
aDialog, aNavigate, nullptr, argv,
|
||||
isPopupSpamWindow,
|
||||
forceNoOpener,
|
||||
aLoadInfo,
|
||||
getter_AddRefs(domReturn));
|
||||
} else {
|
||||
// Force a system caller here so that the window watcher won't screw us
|
||||
|
@ -11457,6 +11481,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
|
|||
aDialog, aNavigate, nullptr, aExtraArgument,
|
||||
isPopupSpamWindow,
|
||||
forceNoOpener,
|
||||
aLoadInfo,
|
||||
getter_AddRefs(domReturn));
|
||||
|
||||
}
|
||||
|
|
|
@ -901,7 +901,10 @@ public:
|
|||
const nsAString& aOptions,
|
||||
mozilla::ErrorResult& aError);
|
||||
nsresult Open(const nsAString& aUrl, const nsAString& aName,
|
||||
const nsAString& aOptions, nsPIDOMWindow **_retval) override;
|
||||
const nsAString& aOptions,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
bool aForceNoOpener,
|
||||
nsPIDOMWindow **_retval) override;
|
||||
mozilla::dom::Navigator* GetNavigator(mozilla::ErrorResult& aError);
|
||||
nsIDOMNavigator* GetNavigator() override;
|
||||
nsIDOMOfflineResourceList* GetApplicationCache(mozilla::ErrorResult& aError);
|
||||
|
@ -1380,15 +1383,21 @@ private:
|
|||
* three args, if present, will be aUrl, aName, and aOptions. So this
|
||||
* param only matters if there are more than 3 arguments.
|
||||
*
|
||||
* @param argc The number of arguments in argv.
|
||||
*
|
||||
* @param aExtraArgument Another way to pass arguments in. This is mutually
|
||||
* exclusive with the argv/argc approach.
|
||||
* exclusive with the argv approach.
|
||||
*
|
||||
* @param aLoadInfo to be passed on along to the windowwatcher.
|
||||
*
|
||||
* @param aForceNoOpener if true, will act as if "noopener" were passed in
|
||||
* aOptions, but without affecting any other window
|
||||
* features.
|
||||
*
|
||||
* @param aJSCallerContext The calling script's context. This must be null
|
||||
* when aCalledNoScript is true.
|
||||
*
|
||||
* @param aReturn [out] The window that was opened, if any.
|
||||
* @param aReturn [out] The window that was opened, if any. Will be null if
|
||||
* aForceNoOpener is true of if aOptions contains
|
||||
* "noopener".
|
||||
*
|
||||
* Outer windows only.
|
||||
*/
|
||||
|
@ -1402,6 +1411,8 @@ private:
|
|||
bool aNavigate,
|
||||
nsIArray *argv,
|
||||
nsISupports *aExtraArgument,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
bool aForceNoOpener,
|
||||
nsIPrincipal *aCalleePrincipal,
|
||||
JSContext *aJSCallerContext,
|
||||
nsIDOMWindow **aReturn);
|
||||
|
|
|
@ -25,6 +25,7 @@ class nsIArray;
|
|||
class nsIContent;
|
||||
class nsICSSDeclaration;
|
||||
class nsIDocShell;
|
||||
class nsIDocShellLoadInfo;
|
||||
class nsIDocument;
|
||||
class nsIIdleObserver;
|
||||
class nsIScriptTimeoutHandler;
|
||||
|
@ -795,8 +796,14 @@ public:
|
|||
virtual already_AddRefed<nsISelection> GetSelection() = 0;
|
||||
virtual already_AddRefed<nsPIDOMWindow> GetOpener() = 0;
|
||||
virtual already_AddRefed<nsIDOMWindowCollection> GetFrames() = 0;
|
||||
// aLoadInfo will be passed on through to the windowwatcher.
|
||||
// aForceNoOpener will act just like a "noopener" feature in aOptions except
|
||||
// will not affect any other window features.
|
||||
virtual nsresult Open(const nsAString& aUrl, const nsAString& aName,
|
||||
const nsAString& aOptions, nsPIDOMWindow **_retval) = 0;
|
||||
const nsAString& aOptions,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
bool aForceNoOpener,
|
||||
nsPIDOMWindow **_retval) = 0;
|
||||
virtual nsresult OpenDialog(const nsAString& aUrl, const nsAString& aName,
|
||||
const nsAString& aOptions,
|
||||
nsISupports* aExtraArgument, nsIDOMWindow** _retval) = 0;
|
||||
|
|
|
@ -42,6 +42,7 @@ ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
|
|||
// static
|
||||
const DOMTokenListSupportedToken HTMLAnchorElement::sSupportedRelValues[] = {
|
||||
"noreferrer",
|
||||
"noopener",
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
|
|
@ -614,6 +614,7 @@ private:
|
|||
// opener anyway, and we _do_ want the returned
|
||||
// window.
|
||||
/* aForceNoOpener = */ false,
|
||||
/* aLoadInfo = */ nullptr,
|
||||
getter_AddRefs(newWindow));
|
||||
nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(newWindow);
|
||||
pwindow.forget(aWindow);
|
||||
|
|
|
@ -16,8 +16,9 @@ interface nsIWebBrowserChrome;
|
|||
interface nsIDocShellTreeItem;
|
||||
interface nsIArray;
|
||||
interface nsITabParent;
|
||||
interface nsIDocShellLoadInfo;
|
||||
|
||||
[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d2)]
|
||||
[uuid(0f2d9d75-c46b-4114-802e-83b4655e61d3)]
|
||||
|
||||
interface nsPIWindowWatcher : nsISupports
|
||||
{
|
||||
|
@ -62,6 +63,12 @@ interface nsPIWindowWatcher : nsISupports
|
|||
looking for existing windows with the given name,
|
||||
not setting an opener on the newly opened window,
|
||||
and returning null from this method.
|
||||
@param aLoadInfo if aNavigate is true, this allows the caller to pass in
|
||||
an nsIDocShellLoadInfo to use for the navigation.
|
||||
Callers can pass in null if they want the windowwatcher
|
||||
to just construct a loadinfo itself. If aNavigate is
|
||||
false, this argument is ignored.
|
||||
|
||||
@return the new window
|
||||
|
||||
@note This method may examine the JS context stack for purposes of
|
||||
|
@ -78,7 +85,8 @@ interface nsPIWindowWatcher : nsISupports
|
|||
in boolean aNavigate, in nsITabParent aOpeningTab,
|
||||
in nsISupports aArgs,
|
||||
in boolean aIsPopupSpam,
|
||||
in boolean aForceNoOpener);
|
||||
in boolean aForceNoOpener,
|
||||
in nsIDocShellLoadInfo aLoadInfo);
|
||||
|
||||
/**
|
||||
* Find a named docshell tree item amongst all windows registered
|
||||
|
|
|
@ -370,6 +370,7 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow* aParent,
|
|||
/* navigate = */ true, nullptr, argv,
|
||||
/* aIsPopupSpam = */ false,
|
||||
/* aForceNoOpener = */ false,
|
||||
/* aLoadInfo = */ nullptr,
|
||||
aResult);
|
||||
}
|
||||
|
||||
|
@ -430,6 +431,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
|
|||
nsISupports* aArguments,
|
||||
bool aIsPopupSpam,
|
||||
bool aForceNoOpener,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
nsIDOMWindow** aResult)
|
||||
{
|
||||
nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
|
||||
|
@ -452,6 +454,7 @@ nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent,
|
|||
aNavigate, aOpeningTab, argv,
|
||||
aIsPopupSpam,
|
||||
aForceNoOpener,
|
||||
aLoadInfo,
|
||||
aResult);
|
||||
}
|
||||
|
||||
|
@ -467,6 +470,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
|
|||
nsIArray* aArgv,
|
||||
bool aIsPopupSpam,
|
||||
bool aForceNoOpener,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
nsIDOMWindow** aResult)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -967,8 +971,8 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent,
|
|||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
||||
if (uriToLoad && aNavigate) {
|
||||
nsCOMPtr<nsIDocShellLoadInfo> loadInfo = aLoadInfo;
|
||||
if (uriToLoad && aNavigate && !loadInfo) {
|
||||
newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
|
||||
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ protected:
|
|||
nsIArray* aArgv,
|
||||
bool aIsPopupSpam,
|
||||
bool aForceNoOpener,
|
||||
nsIDocShellLoadInfo* aLoadInfo,
|
||||
nsIDOMWindow** aResult);
|
||||
|
||||
static nsresult URIfromURL(const char* aURL,
|
||||
|
|
Loading…
Reference in New Issue