document.title not correctly set with JavaScript in omnibox |
||||||||||
Issue descriptionUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 Steps to reproduce the problem: 1. in the bar load javascript:document.title="hey"; What is the expected behavior? the title changes What went wrong? the title doesn't changes in the tab. If in console you do console.log(document.title) it gives undefined Did this work before? Yes Don't know Chrome version: 64.0.3282.186 Channel: n/a OS Version: OS X 10.13.3 Flash Version: if you do 1. in the bar load javascript:document.title="hey";console.log(document.title); title changes correctly. Accessing the variable, actually sets it.
,
Mar 16 2018
Bisect info: 516130 (good) - 516143 (bad) https://chromium.googlesource.com/chromium/src/+log/451942a4..f0747fdc?pretty=fuller Suspecting r516133 = 5b4a0cb6c9f73c4c27bddd6909706452cf2cd47e = https://crrev.com/c/590273 by avi@chromium.org "Clear the page title on reload." Landed in 64.0.3268.0
,
Mar 19 2018
,
Mar 19 2018
Thanks for filing the issue!
Checked the issue on reported chrome version 64.0.3282.186, M60(60.0.3072.0) and latest canary 67.0.3375.0 using Mac 10.13.1 with the below mentioned steps.
1. Launched chrome.
2. Opened DevTools->Console
3. We have run javascript:document.title="hey";
console.log(document.title) and
javascript:document.title="hey";console.log(document.title);
We observed similar results on all the above mentioned three chrome versions. Attaching the screen shots for reference.
@fgalassi: Could you please have a look at those screenshots provided and let us know if anything missed. As we are not very sure about expected and actual results, providing with screenshots of them would help us in triaging the issue in a better way.
Any further inputs may be helpful..!
,
Mar 19 2018
javascript:document.title="hey" is a URL. It shouldn't go in the console, but in the url bar, then press return to load it.
,
Mar 19 2018
Thank you for providing more feedback. Adding the requester to the cc list. For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
,
Mar 20 2018
Able to reproduce the issue on reported chrome version 64.0.3282.186 and on the latest canary 67.0.3375.0 using Mac 10.13.1, Windows 10 and Ubuntu 14.04. Bisect Info: ============== Last Good Build: 64.0.3267.0 First Bad Build: 64.0.3268.0 As per comment#2 suspecting the same. Change log: https://chromium.googlesource.com/chromium/src/+log/451942a4..f0747fdc?pretty=fuller Suspecting: https://chromium.googlesource.com/chromium/src/+/5b4a0cb6c9f73c4c27bddd6909706452cf2cd47e Review URL: https://chromium-review.googlesource.com/590273 @Avi Drissman: Please help in assigning it to the right owner if this is not related to youe change. Note: Adding Milestone M-65 and RB-stable as this seems to be a recent regression. Please change if not applicable. Thanks!
,
Mar 20 2018
,
Mar 20 2018
,
Mar 20 2018
This is regressed in M64, not a blocker for M65. Lets target fix for M66.
,
Mar 20 2018
Here's what we know.
Case 1: Executing
> javascript:document.title = "hi"
from the omnibox does not change the page title, but changes the page's content to the string "hi".
Case 2: Executing
> javascript:document.title = "hi";console.log(document.title)
from the omnibox correctly changes the page's title and leaves the page's content alone.
Inside of WebLocalFrameImpl::LoadJavaScriptURL() is:
String script = DecodeURLEscapeSequences(
static_cast<const KURL&>(url).GetString().Substring(
strlen("javascript:")));
std::unique_ptr<UserGestureIndicator> gesture_indicator =
Frame::NotifyUserActivation(GetFrame(), UserGestureToken::kNewGesture);
v8::HandleScope handle_scope(ToIsolate(GetFrame()));
v8::Local<v8::Value> result =
GetFrame()->GetScriptController().ExecuteScriptInMainWorldAndReturnValue(
ScriptSourceCode(script, ScriptSourceLocationType::kJavascriptUrl));
if (result.IsEmpty() || !result->IsString()) ** early return **
return;
String script_result = ToCoreString(v8::Local<v8::String>::Cast(result));
if (!GetFrame()->GetNavigationScheduler().LocationChangePending()) {
GetFrame()->Loader().ReplaceDocumentWhileExecutingJavaScriptURL(
script_result, owner_document);
}
Case 2 executes as expected. The |result| variable is empty so the no result early return (marked) is taken.
Case 1 fails. The result is just "hi", which ends up being loaded as the document contents.
,
Mar 20 2018
I have investigated; this bug seems to stem from a misunderstanding. Background ========== Imagine you have, in a page, an iframe: <iframe src="javascript:【... JavaScript code that generates HTML...】"> The behavior here is that the JavaScript code specified as the page URL will be executed, and if it returns a string as a result, that result string will be loaded *replacing* the contents of the page. I will grant you that this replacement of the contents of the page is weird magical behavior, but it's what Chrome and other browsers have done for years. That same core functionality is used when you type a JavaScript URL into the omnibox. To see this, type "javascript:'<b>hello'" into the omnibox while viewing any page. You'll see a bold word "hello" replacing the contents of the page. (For further reference/investigation, take a look at WebLocalFrameImpl::LoadJavaScriptURL()). The CL in question ================== The CL in question made a change to Blink guaranteeing that, during page load, at least one "title was set" event is sent to the upper levels of Chrome. This solved bug 96041 . Before this change, if a page had no <title> tag, yet had a title set via JavaScript, reloading the page would fail to reset the title of the page to be blank. After this change, at the end of the page loading, Blink will send a "title was set" event to Chrome, saying that a blank title was set. How the two interact ==================== Many people think that when you use a javascript: URL in the omnibox, that just sends the script contents of that URL to be executed in the context of the currently-loaded page. That is a mistaken understanding of what happens. Yes, the script contents of that URL is executed in the context of the page, but then, *if the execution of the script contents of that URL results in a string, that string is loaded to replace the contents of the page*. This behavior explains why the CL in question "regresses" the display of the new title in the tab. If we look at the script contents of the URL in question: > javascript:document.title = "hey" ^^^^^^^^^^^^^^^^^^^^^^ we'll notice two things of importance. First, we'll notice that it returns a string, and second, we'll notice that the string that it returns has no <title> tag. (For reference to the first point, see https://www.safaribooksonline.com/library/view/javascript-the-definitive/9781449393854/ch04s11.html : "The value of an assignment expression is the value of the right-side operand.") Before the CL in question, what would happen is that script contents of the URL would be executed. That would set the title in the tab. Then, because the script contents of the URL returns a string, that string would be loaded to replace the contents of the page. Even though that string had no <title> tag, the title of the page would not be reset. After the CL in question, things are only slightly different. The script contents of the URL are executed, setting the title in the tab. The script contents of the URL returns a string, so that string is loaded to replace the contents of the page. However, now Blink notices that it's loading a page that has no <title> tag, so it resets the title as displayed in the tab. This also explains the second part of the bug report: > if you do > 1. in the bar load javascript:document.title="hey";console.log(document.title); > title changes correctly. Accessing the variable, actually sets it. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This statement is incorrect. The change in the behavior is unrelated to whether or not the variable is accessed. What's happening here is that adding a "console.log" statement to the javascript: URL changes what the script contents of the URL returns. If you load: > javascript:document.title = "hey" what happens is: 1. this is executed in the context of the page, which sets the title 2. this returns the result value of "hey" 3. because "hey" is a string, this string is loaded to replace the contents of the page 4. a page is loaded that has no <title> tag, so the title is cleared However, if you load: > javascript:document.title = "hey";console.log(document.title); what happens is: 1. this is executed in the context of the page, which sets the title 2. this returns the result value of |undefined| 3. because |undefined| is a not a string, nothing further happens and the set string remains as the document's title. Any value that isn't a string can be used in this way: > javascript:document.title = "hey";undefined > javascript:document.title = "hey";true > javascript:document.title = "hey";false > javascript:document.title = "hey";42 all resulting in just a title change but not a document change. Conclusions =========== The lesson learned here for bookmarklets (and other things that use javascript: URLs) is to keep in mind that if a string value is returned from the script contents of the URL, that string value *will be loaded to replace the contents of the page*. If this is not intended behavior, be very careful to always return |undefined| or the like to avoid this.
,
Mar 21 2018
I agree on the won't fix. The behavior of replacing the page content is a bit weird but it's even part of the standard (https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigating-across-documents%3Ajavascript-protocol). Bookmarklets should all have void(0) at the end.
,
Mar 21 2018
Oh, and thanks!
,
Mar 21 2018
Thank you for looking up the standard! I didn't know that, though now that I think about it, of course it's in there. Always happy to help.
,
Mar 22 2018
Nothing secret here.
,
Mar 22 2018
Instead of having a `void 0` suffix, bookmarklets could also wrap their code in an IIFE or a similar construct, i.e.:
javascript:(() => { /* code goes here */ })();
,
Mar 23 2018
In practice most bookmarklets do that (though without arrow functions). I can't find any in my collection of 11 that end in void 0. Ending with a string output and needing to void would also usually be indicative of leaking globals. |
||||||||||
►
Sign in to add a comment |
||||||||||
Comment 1 by mbonadei@chromium.org
, Mar 16 2018